diff --git a/reactos/Makefile b/reactos/Makefile index dd66e4b0823..d702845c1a5 100644 --- a/reactos/Makefile +++ b/reactos/Makefile @@ -79,6 +79,7 @@ ifeq ($(HALFVERBOSEECHO),yes) ECHO_AR =@echo [AR] $@ ECHO_WINEBLD =@echo [WINEBLD] $@ ECHO_WRC =@echo [WRC] $@ + ECHO_WIDL =@echo [WIDL] $@ ECHO_BIN2RES =@echo [BIN2RES] $< ECHO_DLLTOOL =@echo [DLLTOOL] $@ ECHO_LD =@echo [LD] $@ @@ -105,6 +106,7 @@ else ECHO_AR = ECHO_WINEBLD = ECHO_WRC = + ECHO_WIDL = ECHO_BIN2RES = ECHO_DLLTOOL = ECHO_LD = diff --git a/reactos/ReactOS.xml b/reactos/ReactOS.xml index 284e19b2c21..12437ee5187 100644 --- a/reactos/ReactOS.xml +++ b/reactos/ReactOS.xml @@ -38,6 +38,9 @@ + + + diff --git a/reactos/apps/testsets/loadlib/makefile b/reactos/apps/testsets/loadlib/makefile index f3e22888ad1..1a256b21113 100644 --- a/reactos/apps/testsets/loadlib/makefile +++ b/reactos/apps/testsets/loadlib/makefile @@ -11,7 +11,7 @@ TARGET_APPTYPE = console TARGET_NAME = loadlib -TARGET_CFLAGS = -Wall -Werror -D_USE_W32API -DUNICODE -D_UNICODE +TARGET_CFLAGS = -Wall -Werror -D__USE_W32API -DUNICODE -D_UNICODE TARGET_SDKLIBS = kernel32.a ntdll.a diff --git a/reactos/apps/utils/Makefile b/reactos/apps/utils/Makefile index 86e8921fa18..8ce2f875d2b 100644 --- a/reactos/apps/utils/Makefile +++ b/reactos/apps/utils/Makefile @@ -9,7 +9,7 @@ include $(PATH_TO_TOP)/rules.mak # Console system utilities # cabman cat net objdir partinfo pice ps sc stats -UTIL_APPS = cat objdir partinfo pnpdump sc shutdown stats tickcount consw ps +UTIL_APPS = cat objdir pnpdump sc shutdown stats tickcount ps UTIL_NET_APPS = arp finger ftp ipconfig netstat ping route telnet whois diff --git a/reactos/apps/utils/consw/makefile b/reactos/apps/utils/binpatch/Makefile similarity index 56% rename from reactos/apps/utils/consw/makefile rename to reactos/apps/utils/binpatch/Makefile index 2009ee05391..4ff7b6bc553 100644 --- a/reactos/apps/utils/consw/makefile +++ b/reactos/apps/utils/binpatch/Makefile @@ -1,23 +1,21 @@ -# $Id$ - -PATH_TO_TOP = ../../.. - -TARGET_NORC = yes - -TARGET_TYPE = program - -TARGET_APPTYPE = console - -TARGET_NAME = consw - -TARGET_SDKLIBS = ntdll.a kernel32.a - -TARGET_OBJECTS = $(TARGET_NAME).o - -TARGET_CFLAGS = -Wall -Werror - -include $(PATH_TO_TOP)/rules.mak - -include $(TOOLS_PATH)/helper.mk - -# EOF +PATH_TO_TOP = ../../.. + +TARGET_NORC = yes + +TARGET_TYPE = program + +TARGET_APPTYPE = console + +TARGET_NAME = binpatch + +TARGET_SDKLIBS = + +TARGET_OBJECTS = patch.o + +TARGET_CFLAGS += -Wall -Werror + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + +# EOF diff --git a/reactos/apps/utils/binpatch/patch.c b/reactos/apps/utils/binpatch/patch.c new file mode 100644 index 00000000000..93ae7488501 --- /dev/null +++ b/reactos/apps/utils/binpatch/patch.c @@ -0,0 +1,615 @@ +#include +#include +#include +#include +#include +#include + +/** DEFINES *******************************************************************/ + +#define PATCH_BUFFER_SIZE 4096 /* Maximum size of a patch */ +#define PATCH_BUFFER_MAGIC "\xde\xad\xbe\xef MaGiC MaRk " +#define SIZEOF_PATCH_BUFFER_MAGIC (sizeof (PATCH_BUFFER_MAGIC) - 1) + +/** TYPES *********************************************************************/ + +typedef struct _PatchedByte +{ + int offset; /*!< File offset of the patched byte. */ + unsigned char expected; /*!< Expected (original) value of the byte. */ + unsigned char patched; /*!< Patched (new) value for the byte. */ +} PatchedByte; + +typedef struct _PatchedFile +{ + const char *name; /*!< Name of the file to be patched. */ + int fileSize; /*!< Size of the file in bytes. */ + int patchCount; /*!< Number of patches for the file. */ + PatchedByte *patches; /*!< Patches for the file. */ +} PatchedFile; + +typedef struct _Patch +{ + const char *name; /*!< Name of the patch. */ + int fileCount; /*!< Number of files in the patch. */ + PatchedFile *files; /*!< Files for the patch. */ +} Patch; + +/** FUNCTION PROTOTYPES *******************************************************/ + +static void printUsage(); + +/** GLOBALS *******************************************************************/ + +static Patch m_patch = { NULL, 0, NULL }; +static int m_argc = 0; +static char **m_argv = NULL; + +/* patch buffer where we put the patch info into */ +static unsigned char m_patchBuffer[SIZEOF_PATCH_BUFFER_MAGIC + PATCH_BUFFER_SIZE] = + PATCH_BUFFER_MAGIC; + +/** HELPER FUNCTIONS **********************************************************/ + +static void * +loadFile(const char *fileName, int *fileSize_) +{ + FILE *f; + struct stat sb; + int fileSize; + void *p; + + /* Open the file */ + f = fopen(fileName, "rb"); + if (f == NULL) + { + printf("Couldn't open file %s for reading!\n", fileName); + return NULL; + } + + /* Get file size */ + if (fstat(fileno(f), &sb) < 0) + { + fclose(f); + printf("Couldn't get size of file %s!\n", fileName); + return NULL; + } + fileSize = sb.st_size; + + /* Load file */ + p = malloc(fileSize); + if (p == NULL) + { + fclose(f); + printf("Couldn't allocate %d bytes for file %s!\n", fileSize, fileName); + return NULL; + } + + if (fread(p, fileSize, 1, f) != 1) + { + fclose(f); + free(p); + printf("Couldn't read file %s into memory!\n", fileName); + return NULL; + } + + /* Close file */ + fclose(f); + + *fileSize_ = fileSize; + return p; +} + + +static int +saveFile(const char *fileName, void *file, int fileSize) +{ + FILE *f; + + /* Open the file */ + f = fopen(fileName, "wb"); + if (f == NULL) + { + printf("Couldn't open file %s for writing!\n", fileName); + return -1; + } + + /* Write file */ + if (fwrite(file, fileSize, 1, f) != 1) + { + fclose(f); + printf("Couldn't write file %s!\n", fileName); + return -1; + } + + /* Close file */ + fclose(f); + return 0; +} + + +static int +compareFiles( + PatchedFile *patchedFile, + const char *originalFileName) +{ + const char *patchedFileName = patchedFile->name; + unsigned char *origChunk, *patchedChunk; + int origSize, patchedSize, i, patchCount; + PatchedByte *patches = NULL; + int patchesArrayCount = 0; + + /* Load both files */ + origChunk = loadFile(originalFileName, &origSize); + if (origChunk == NULL) + return -1; + patchedChunk = loadFile(patchedFileName, &patchedSize); + if (patchedChunk == NULL) + { + free(origChunk); + return -1; + } + if (origSize != patchedSize) + { + free(origChunk); + free(patchedChunk); + printf("File size of %s and %s differs (%d != %d)\n", + originalFileName, patchedFileName, + origSize, patchedSize); + return -1; + } + + /* Compare the files and record any differences */ + printf("Comparing %s to %s", originalFileName, patchedFileName); + for (i = 0, patchCount = 0; i < origSize; i++) + { + if (origChunk[i] != patchedChunk[i]) + { + patchCount++; + + /* Resize patches array if needed */ + if (patchesArrayCount < patchCount) + { + PatchedByte *newPatches; + newPatches = realloc(patches, patchCount * sizeof (PatchedByte)); + if (newPatches == NULL) + { + if (patches != NULL) + free(patches); + free(origChunk); + free(patchedChunk); + printf("\nOut of memory (tried to allocated %d bytes)\n", + patchCount * sizeof (PatchedByte)); + return -1; + } + patches = newPatches; + } + + /* Fill in patch info */ + patches[patchCount - 1].offset = i; + patches[patchCount - 1].expected = origChunk[i]; + patches[patchCount - 1].patched = patchedChunk[i]; + } + if ((i % (origSize / 40)) == 0) + printf("."); + } + printf(" %d changed bytes found.\n", patchCount); + + /* Unload the files */ + free(origChunk); + free(patchedChunk); + + /* Save patch info */ + patchedFile->fileSize = patchedSize; + patchedFile->patchCount = patchCount; + patchedFile->patches = patches; + + return 0; +} + + +static int +outputPatch(const char *outputFileName) +{ + unsigned char *patchExe, *patchBuffer; + int i, size, patchExeSize, patchSize, stringSize, stringOffset, patchOffset; + Patch *patch; + PatchedFile *files; + + printf("Putting patch into %s...\n", outputFileName); + + /* Calculate size of the patch */ + patchSize = sizeof (Patch) + sizeof (PatchedFile) * m_patch.fileCount; + stringSize = strlen(m_patch.name) + 1; + for (i = 0; i < m_patch.fileCount; i++) + { + stringSize += strlen(m_patch.files[i].name) + 1; + patchSize += sizeof (PatchedByte) * m_patch.files[i].patchCount; + } + if ((stringSize + patchSize) > PATCH_BUFFER_SIZE) + { + printf("Patch is too big - %d bytes maximum, %d bytes needed\n", + PATCH_BUFFER_SIZE, stringSize + patchSize); + return -1; + } + + /* Load patch.exe file into memory... */ + patchExe = loadFile(m_argv[0], &patchExeSize); + if (patchExe == NULL) + { + return -1; + } + + /* Try to find the magic mark for the patch buffer */ + for (i = 0; i < (patchExeSize - SIZEOF_PATCH_BUFFER_MAGIC); i++) + { + if (memcmp(patchExe + i, m_patchBuffer, SIZEOF_PATCH_BUFFER_MAGIC) == 0) + { + patchBuffer = patchExe + i + SIZEOF_PATCH_BUFFER_MAGIC; + + break; + } + } + if (!(i < (patchExeSize - SIZEOF_PATCH_BUFFER_MAGIC))) + { + free(patchExe); + printf("Couldn't find patch buffer magic in file %s - this shouldn't happen!!!\n", m_argv[0]); + return -1; + } + + /* Pack patch together and replace string pointers by offsets */ + patch = (Patch *)patchBuffer; + files = (PatchedFile *)(patchBuffer + sizeof (Patch)); + patchOffset = sizeof (Patch) + sizeof (PatchedFile) * m_patch.fileCount; + stringOffset = patchSize; + + patch->fileCount = m_patch.fileCount; + patch->files = (PatchedFile *)sizeof (Patch); + + patch->name = (const char *)stringOffset; + strcpy(patchBuffer + stringOffset, m_patch.name); + stringOffset += strlen(m_patch.name) + 1; + + for (i = 0; i < m_patch.fileCount; i++) + { + files[i].fileSize = m_patch.files[i].fileSize; + files[i].patchCount = m_patch.files[i].patchCount; + + files[i].name = (const char *)stringOffset; + strcpy(patchBuffer + stringOffset, m_patch.files[i].name); + stringOffset += strlen(m_patch.files[i].name) + 1; + + size = files[i].patchCount * sizeof (PatchedByte); + files[i].patches = (PatchedByte *)patchOffset; + memcpy(patchBuffer + patchOffset, m_patch.files[i].patches, size); + patchOffset += size; + } + size = patchSize + stringSize; + memset(patchBuffer + size, 0, PATCH_BUFFER_SIZE - size); + + /* Save file */ + if (saveFile(outputFileName, patchExe, patchExeSize) < 0) + { + free(patchExe); + return -1; + } + free(patchExe); + + printf("Patch saved!\n"); + return 0; +} + + +static int +loadPatch() +{ + char *p; + Patch *patch; + int i; + + p = m_patchBuffer + SIZEOF_PATCH_BUFFER_MAGIC; + patch = (Patch *)p; + + if (patch->name == NULL) + { + return -1; + } + + m_patch.name = p + (int)patch->name; + m_patch.fileCount = patch->fileCount; + m_patch.files = (PatchedFile *)(p + (int)patch->files); + + for (i = 0; i < m_patch.fileCount; i++) + { + m_patch.files[i].name = p + (int)m_patch.files[i].name; + m_patch.files[i].patches = (PatchedByte *)(p + (int)m_patch.files[i].patches); + } + + printf("Patch %s loaded...\n", m_patch.name); + return 0; +} + + +/** MAIN FUNCTIONS ************************************************************/ + +static int +createPatch() +{ + int i, status; + const char *outputFileName; + + /* Check argument count */ + if (m_argc < 6 || (m_argc % 2) != 0) + { + printUsage(); + return -1; + } + + outputFileName = m_argv[3]; + m_patch.name = m_argv[2]; + + /* Allocate PatchedFiles array */ + m_patch.fileCount = (m_argc - 4) / 2; + m_patch.files = malloc(m_patch.fileCount * sizeof (PatchedFile)); + if (m_patch.files == NULL) + { + printf("Out of memory!\n"); + return -1; + } + memset(m_patch.files, 0, m_patch.fileCount * sizeof (PatchedFile)); + + /* Compare original to patched files and fill m_patch.files array */ + for (i = 0; i < m_patch.fileCount; i++) + { + m_patch.files[i].name = m_argv[4 + (i * 2) + 1]; + status = compareFiles(m_patch.files + i, m_argv[4 + (i * 2) + 0]); + if (status < 0) + { + for (i = 0; i < m_patch.fileCount; i++) + { + if (m_patch.files[i].patches != NULL) + free(m_patch.files[i].patches); + } + free(m_patch.files); + m_patch.files = NULL; + m_patch.fileCount = 0; + return status; + } + } + + /* Output patch */ + return outputPatch(outputFileName); +} + + +static int +applyPatch() +{ + int c, i, j, fileSize, makeBackup; + unsigned char *file; + char *p; + const char *fileName; + char buffer[MAX_PATH]; + + + if (m_argc > 1 && strcmp(m_argv[1], "-d") != 0) + { + printUsage(); + return -1; + } + + /* Load patch */ + if (loadPatch() < 0) + { + printf("This executable doesn't contain a patch, use -c to create one.\n"); + return -1; + } + + if (m_argc > 1) + { + /* Dump patch */ + printf("Patch name: %s\n", m_patch.name); + printf("File count: %d\n", m_patch.fileCount); + for (i = 0; i < m_patch.fileCount; i++) + { + printf("----------------------\n" + "File name: %s\n" + "File size: %d bytes\n", + m_patch.files[i].name, m_patch.files[i].fileSize); + printf("Patch count: %d\n", m_patch.files[i].patchCount); + for (j = 0; j < m_patch.files[i].patchCount; j++) + { + printf(" Offset 0x%x 0x%02x -> 0x%02x\n", + m_patch.files[i].patches[j].offset, + m_patch.files[i].patches[j].expected, + m_patch.files[i].patches[j].patched); + } + } + } + else + { + /* Apply patch */ + printf("Applying patch...\n"); + for (i = 0; i < m_patch.fileCount; i++) + { + /* Load original file */ + fileName = m_patch.files[i].name; +applyPatch_retry_file: + file = loadFile(fileName, &fileSize); + if (file == NULL) + { + printf("File %s not found! ", fileName); +applyPatch_file_open_error: + printf("(S)kip, (R)etry, (A)bort, (M)anually enter filename"); + do + { + c = getch(); + } + while (c != 's' && c != 'r' && c != 'a' && c != 'm'); + printf("\n"); + if (c == 's') + { + continue; + } + else if (c == 'r') + { + goto applyPatch_retry_file; + } + else if (c == 'a') + { + return 0; + } + else if (c == 'm') + { + if (fgets(buffer, sizeof (buffer), stdin) == NULL) + { + printf("fgets() failed!\n"); + return -1; + } + p = strchr(buffer, '\r'); + if (p != NULL) + *p = '\0'; + p = strchr(buffer, '\n'); + if (p != NULL) + *p = '\0'; + + fileName = buffer; + goto applyPatch_retry_file; + } + } + + /* Check file size */ + if (fileSize != m_patch.files[i].fileSize) + { + free(file); + printf("File %s has unexpected filesize of %d bytes (%d bytes expected)\n", + fileName, fileSize, m_patch.files[i].fileSize); + if (fileName != m_patch.files[i].name) /* manually entered filename */ + { + goto applyPatch_file_open_error; + } + return -1; + } + + /* Ask for backup */ + printf("Do you want to make a backup of %s? (Y)es, (N)o, (A)bort", fileName); + do + { + c = getch(); + } + while (c != 'y' && c != 'n' && c != 'a'); + printf("\n"); + if (c == 'y') + { + char buffer[MAX_PATH]; + snprintf(buffer, MAX_PATH, "%s.bak", fileName); + buffer[MAX_PATH-1] = '\0'; + makeBackup = 1; + if (access(buffer, 0) >= 0) /* file exists */ + { + printf("File %s already exists, overwrite? (Y)es, (N)o, (A)bort", buffer); + do + { + c = getch(); + } + while (c != 'y' && c != 'n' && c != 'a'); + printf("\n"); + if (c == 'n') + makeBackup = 0; + else if (c == 'a') + { + free(file); + return 0; + } + } + if (makeBackup && saveFile(buffer, file, fileSize) < 0) + { + free(file); + return -1; + } + } + else if (c == 'a') + { + free(file); + return 0; + } + + /* Patch file */ + for (j = 0; j < m_patch.files[i].patchCount; j++) + { + int offset = m_patch.files[i].patches[j].offset; + if (file[offset] != m_patch.files[i].patches[j].expected) + { + printf("Unexpected value in file %s at offset 0x%x: expected = 0x%02x, found = 0x%02x\n", + fileName, offset, m_patch.files[i].patches[j].expected, file[offset]); + free(file); + return -1; + } + file[offset] = m_patch.files[i].patches[j].patched; + } + + /* Save file */ + if (saveFile(fileName, file, fileSize) < 0) + { + free(file); + return -1; + } + free(file); + } + + printf("Patch applied sucessfully!\n"); + } + + return 0; +} + + +static void +printUsage() +{ + printf("Usage:\n" + "%s -c - Create patch\n" + "%s -d - Dump patch\n" + "%s - Apply patch\n" + "\n" + "A patch can be created like this:\n" + "%s -c \"patch name\" output.exe file1.orig file1.patched[ file2.orig file2.patched[ ...]]\n", + m_argv[0], m_argv[0], m_argv[0], m_argv[0]); +} + + +int +main( + int argc, + char *argv[]) +{ + m_argc = argc; + m_argv = argv; + + if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) + { + printUsage(); + return 0; + } + else if (argc >= 2 && argv[1][0] == '-') + { + if (strcmp(argv[1], "-c") == 0) + { + return createPatch(); + } + else if (strcmp(argv[1], "-d") == 0) + { + return applyPatch(); + } + else + { + printf("Unknown option: %s\n" + "Use -h for help.\n", + argv[1]); + return -1; + } + } + + return applyPatch(); +} + diff --git a/reactos/apps/utils/consw/consw.c b/reactos/apps/utils/consw/consw.c deleted file mode 100644 index 98c0d36810b..00000000000 --- a/reactos/apps/utils/consw/consw.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $Id$ - * - * DESCRIPTION: Console mode switcher - * PROGRAMMER: Art Yerkes - * REVISIONS - * 2003-07-26 (arty) - */ - -#include -#include -#include -#include -#include - -void STDCALL SetConsoleHardwareState( HANDLE conhandle, - DWORD flags, - DWORD state ); - -int main(int argc, char* argv[]) -{ - if( argc > 1 ) { - SetConsoleHardwareState( GetStdHandle( STD_INPUT_HANDLE ), - 0, - !strcmp( argv[1], "hw" ) ); - } - return 0; -} - - -/* EOF */ diff --git a/reactos/apps/utils/consw/runprg.bat b/reactos/apps/utils/consw/runprg.bat deleted file mode 100644 index 989cf858ddc..00000000000 --- a/reactos/apps/utils/consw/runprg.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off -rem Turn off cosole, run a program, turn console on -\reactos\bin\consw sw -"%1" "%2" "%3" "%4" "%5" "%6" "%7" "%8" "%9" -\reactos\bin\consw hw diff --git a/reactos/apps/utils/net/ftp/makefile b/reactos/apps/utils/net/ftp/makefile index a32c246c310..ab481c2212a 100644 --- a/reactos/apps/utils/net/ftp/makefile +++ b/reactos/apps/utils/net/ftp/makefile @@ -8,6 +8,8 @@ TARGET_NAME = ftp TARGET_INSTALLDIR = system32 +TARGET_CFLAGS = -D__USE_W32API + TARGET_SDKLIBS = ws2_32.a iphlpapi.a # ntdll.a diff --git a/reactos/apps/utils/net/ping/makefile b/reactos/apps/utils/net/ping/makefile index 4e7a078d071..3459fc53d65 100644 --- a/reactos/apps/utils/net/ping/makefile +++ b/reactos/apps/utils/net/ping/makefile @@ -8,7 +8,7 @@ TARGET_NAME = ping TARGET_INSTALLDIR = system32 -TARGET_CFLAGS = -D__USE_W32_SOCKETS +TARGET_CFLAGS = -D__USE_W32API -D__USE_W32_SOCKETS TARGET_SDKLIBS = ws2_32.a diff --git a/reactos/apps/utils/net/ping/ping.xml b/reactos/apps/utils/net/ping/ping.xml index 2e6a5010f40..c604abd4f7b 100644 --- a/reactos/apps/utils/net/ping/ping.xml +++ b/reactos/apps/utils/net/ping/ping.xml @@ -1,5 +1,6 @@ . + 0x600 0x501 diff --git a/reactos/apps/utils/net/route/makefile b/reactos/apps/utils/net/route/makefile index 28c948f1184..e7c5e4da2a5 100644 --- a/reactos/apps/utils/net/route/makefile +++ b/reactos/apps/utils/net/route/makefile @@ -12,7 +12,7 @@ TARGET_SDKLIBS = ws2_32.a iphlpapi.a ntdll.a TARGET_OBJECTS = $(TARGET_NAME).o -TARGET_GCCLIBS = +TARGET_CFLAGS = -D__USE_W32API include $(PATH_TO_TOP)/rules.mak diff --git a/reactos/apps/utils/net/route/route.xml b/reactos/apps/utils/net/route/route.xml index c1a435eb7e6..0760122062e 100644 --- a/reactos/apps/utils/net/route/route.xml +++ b/reactos/apps/utils/net/route/route.xml @@ -1,5 +1,6 @@ . + kernel32 ws2_32 iphlpapi diff --git a/reactos/apps/utils/partinfo/makefile b/reactos/apps/utils/partinfo/makefile index 89789080564..483df1eeea4 100644 --- a/reactos/apps/utils/partinfo/makefile +++ b/reactos/apps/utils/partinfo/makefile @@ -10,7 +10,7 @@ TARGET_APPTYPE = console TARGET_NAME = partinfo -TARGET_CFLAGS = -Wall -Werror -Wno-format +TARGET_CFLAGS = -D__USE_W32API -Wall -Werror -Wno-format TARGET_SDKLIBS = ntdll.a kernel32.a diff --git a/reactos/apps/utils/partinfo/partinfo.c b/reactos/apps/utils/partinfo/partinfo.c index 844fe8f83d8..252e465974e 100644 --- a/reactos/apps/utils/partinfo/partinfo.c +++ b/reactos/apps/utils/partinfo/partinfo.c @@ -8,9 +8,11 @@ #include #include +#include + //#define DUMP_DATA #define DUMP_SIZE_INFO - +#define UNICODE #ifdef DUMP_DATA void HexDump(char *buffer, ULONG size) diff --git a/reactos/apps/utils/pnpdump/makefile b/reactos/apps/utils/pnpdump/makefile index 240453823ed..5cecb16c0de 100644 --- a/reactos/apps/utils/pnpdump/makefile +++ b/reactos/apps/utils/pnpdump/makefile @@ -10,7 +10,7 @@ TARGET_APPTYPE = console TARGET_NAME = pnpdump -TARGET_CFLAGS = -Wall -Werror +TARGET_CFLAGS = -D__USE_W32API -Wall -Werror TARGET_SDKLIBS = ntdll.a kernel32.a diff --git a/reactos/apps/utils/pnpdump/pnpdump.c b/reactos/apps/utils/pnpdump/pnpdump.c index b02bc2bb1d6..4557e69ffe8 100644 --- a/reactos/apps/utils/pnpdump/pnpdump.c +++ b/reactos/apps/utils/pnpdump/pnpdump.c @@ -8,6 +8,8 @@ #include #include +#include + #include typedef struct _CM_PNP_BIOS_DEVICE_NODE diff --git a/reactos/apps/utils/ps/makefile b/reactos/apps/utils/ps/makefile index b86b573654f..46c3601ee7c 100644 --- a/reactos/apps/utils/ps/makefile +++ b/reactos/apps/utils/ps/makefile @@ -10,7 +10,7 @@ TARGET_APPTYPE = console TARGET_NAME = ps -TARGET_CFLAGS = -DANONYMOUSUNIONS -Werror -Wall +TARGET_CFLAGS = -D__USE_W32API -DANONYMOUSUNIONS -Werror -Wall TARGET_SDKLIBS = ntdll.a kernel32.a user32.a diff --git a/reactos/apps/utils/sc/makefile b/reactos/apps/utils/sc/makefile index ba118f3b3fd..0c7f62900c8 100644 --- a/reactos/apps/utils/sc/makefile +++ b/reactos/apps/utils/sc/makefile @@ -13,7 +13,7 @@ TARGET_INSTALLDIR = system32 TARGET_NAME = sc -TARGET_CFLAGS = -DDBG -Werror -Wall +TARGET_CFLAGS = -D__USE_W32API -DDBG -Werror -Wall TARGET_SDKLIBS = kernel32.a ntdll.a advapi32.a diff --git a/reactos/apps/utils/winetest/config.h b/reactos/apps/utils/winetest/config.h new file mode 100644 index 00000000000..949acf8a401 --- /dev/null +++ b/reactos/apps/utils/winetest/config.h @@ -0,0 +1,949 @@ +/* include/config.h. Generated by configure. */ +/* include/config.h.in. Generated from configure.ac by autoheader. */ + +#define __WINE_CONFIG_H + +/* Specifies the compiler flag that forces a short wchar_t */ +#define CC_FLAG_SHORT_WCHAR "-fshort-wchar" + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +/* #undef CRAY_STACKSEG_END */ + +/* Define to 1 if using `alloca.c'. */ +/* #undef C_ALLOCA */ + +/* Define to 1 if you have `alloca', as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +/* #undef HAVE_ALLOCA_H */ + +/* Define if you have ALSA 1.x including devel headers */ +/* #undef HAVE_ALSA */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ALSA_ASOUNDLIB_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_INET_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_NAMESER_H */ + +/* Define if you have ARTS sound server */ +/* #undef HAVE_ARTS */ + +/* Define if the assembler keyword .size is accepted */ +/* #undef HAVE_ASM_DOT_SIZE */ + +/* Define to 1 if you have the diff --git a/reactos/lib/richedit/richedit.c b/reactos/lib/richedit/richedit.c index 648c7be9257..e0d56167ea3 100644 --- a/reactos/lib/richedit/richedit.c +++ b/reactos/lib/richedit/richedit.c @@ -1,11 +1,10 @@ /* * RichEdit32 functions * - * This module is a simple wrapper for the edit controls. - * At the point, it is good only for application who use the RICHEDIT - * control to display RTF text. + * This module is a simple wrapper for the RichEdit 2.0 control * * Copyright 2000 by Jean-Claude Batista + * Copyright 2005 Mike McCormack * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,728 +28,29 @@ #include "wingdi.h" #include "winreg.h" #include "winerror.h" -#include "riched32.h" +#include "winuser.h" #include "richedit.h" -#define NO_SHLWAPI_STREAM #include "shlwapi.h" -#include "rtf.h" -#include "rtf2text.h" #include "wine/debug.h" -#define ID_EDIT 1 - WINE_DEFAULT_DEBUG_CHANNEL(richedit); -HANDLE RICHED32_hHeap = NULL; -/* LPSTR RICHED32_aSubclass = NULL; */ -static WNDPROC lpfnEditWndProc = NULL; -static INT RTFInfoOffset = 0; +/* Window procedure of the RichEdit 1.0 control in riched20.dll */ +extern LRESULT WINAPI RichEdit10ANSIWndProc(HWND, UINT, WPARAM, LPARAM); -#define TRACE_EDIT_MSG32(str) \ - TRACE(\ - "32 bit : " str ": hwnd=%p, wParam=%08x, lParam=%08x\n"\ - , \ - hwnd, (UINT)wParam, (UINT)lParam) - -LPVOID* WINAPI CreateIRichEditOle(); -VOID RICHEDIT_InitEditControlInfo(void); - -/*********************************************************************** - * DllMain [Internal] Initializes the internal 'RICHED32.DLL'. - * - * PARAMS - * hinstDLL [I] handle to the DLL's instance - * fdwReason [I] - * lpvReserved [I] reserved, must be NULL - * - * RETURNS - * Success: TRUE - * Failure: FALSE - */ - -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) -{ - TRACE("\n"); - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - DisableThreadLibraryCalls(hinstDLL); - /* create private heap */ - RICHED32_hHeap = HeapCreate (0, 0x10000, 0); - /* Retrieve edit control class info */ - RICHEDIT_InitEditControlInfo(); - /* register the Rich Edit class */ - RICHED32_Register (); - break; - - case DLL_PROCESS_DETACH: - /* unregister all common control classes */ - RICHED32_Unregister (); - HeapDestroy (RICHED32_hHeap); - RICHED32_hHeap = NULL; - break; - } - return TRUE; -} - -/* Support routines for window procedure */ - INT RICHEDIT_GetTextRange(HWND hwnd,TEXTRANGEA *tr); - INT RICHEDIT_GetSelText(HWND hwnd,LPSTR lpstrBuffer); - -typedef struct _RTFControl_info -{ - HWND hwndParent; - char* rtfBuffer; - RTF_Info *parser; -} RTFControl_Info; - -/* - * - * DESCRIPTION: - * Window procedure of the RichEdit control. - * - */ -static LRESULT WINAPI RICHED32_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, - LPARAM lParam) -{ - LONG newstyle = 0; - LONG style = 0; - RTFControl_Info *info; - CHARRANGE *cr; - - info = (RTFControl_Info *) GetWindowLongW( hwnd, RTFInfoOffset ); - - TRACE("uMsg: 0x%x hwnd: %p\n",uMsg,hwnd); - - switch (uMsg) - { - - case WM_CREATE: - TRACE_EDIT_MSG32("WM_CREATE Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - - case WM_NCCREATE : - TRACE_EDIT_MSG32("WM_NCCREATE"); - - info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, - sizeof (RTFControl_Info)); - info->parser = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, - sizeof (RTF_Info)); - SetWindowLongW( hwnd, RTFInfoOffset, (LONG)info ); - - /* remove SCROLLBARS from the current window style */ - info->hwndParent = ((LPCREATESTRUCTA) lParam)->hwndParent; - - newstyle = style = ((LPCREATESTRUCTA) lParam)->style; - newstyle &= ~WS_HSCROLL; - newstyle &= ~WS_VSCROLL; - newstyle &= ~ES_AUTOHSCROLL; - newstyle &= ~ES_AUTOVSCROLL; - SetWindowLongA(hwnd,GWL_STYLE, newstyle); - - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - - case WM_SETFOCUS : - TRACE_EDIT_MSG32("WM_SETFOCUS"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - - case WM_SIZE : - TRACE_EDIT_MSG32("WM_SIZE"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - - case WM_COMMAND : - TRACE_EDIT_MSG32("WM_COMMAND"); - switch(HIWORD(wParam)) { - case EN_CHANGE: - case EN_HSCROLL: - case EN_KILLFOCUS: - case EN_SETFOCUS: - case EN_UPDATE: - case EN_VSCROLL: - return SendMessageA(info->hwndParent, WM_COMMAND, - wParam, (LPARAM)(hwnd)); - - case EN_ERRSPACE: - case EN_MAXTEXT: - MessageBoxA (hwnd, "RichEdit control out of space.", - "ERROR", MB_OK | MB_ICONSTOP) ; - return 0 ; - } - - case EM_STREAMIN: - TRACE_EDIT_MSG32("EM_STREAMIN"); - - /* setup the RTF parser */ - RTFSetEditStream(info->parser,( EDITSTREAM*)lParam); - info->parser->rtfFormat = wParam&(SF_TEXT|SF_RTF); - info->parser->hwndEdit = hwnd; - WriterInit(info->parser); - RTFInit (info->parser); - BeginFile(info->parser); - - /* do the parsing */ - RTFRead (info->parser); - RTFFlushOutputBuffer( info->parser ); - - /* put the cursor at the top */ - SendMessageA( hwnd, EM_SETSEL, 0, 0 ); - - return 0; - -/* Messages specific to Richedit controls */ - - case EM_AUTOURLDETECT: - TRACE_EDIT_MSG32("EM_AUTOURLDETECT Ignored"); - return 0; - - case EM_CANPASTE: - TRACE_EDIT_MSG32("EM_CANPASTE Ignored"); - return 0; - - case EM_CANREDO: - TRACE_EDIT_MSG32("EM_CANREDO Ignored"); - return 0; - - case EM_DISPLAYBAND: - TRACE_EDIT_MSG32("EM_DISPLAYBAND Ignored"); - return 0; - - case EM_EXGETSEL: - TRACE_EDIT_MSG32("EM_EXGETSEL -> EM_GETSEL"); - cr = (VOID *) lParam; - CallWindowProcA(lpfnEditWndProc, hwnd, EM_GETSEL, (INT)&cr->cpMin, (INT)&cr->cpMax); - TRACE("cpMin: 0x%x cpMax: 0x%x\n",(INT)cr->cpMin,(INT)cr->cpMax); - return 0; - - case EM_EXLIMITTEXT: - { - DWORD limit = lParam; - TRACE_EDIT_MSG32("EM_EXLIMITTEXT"); - if (limit > 65534) - { - limit = 0xFFFFFFFF; - } - return CallWindowProcA(lpfnEditWndProc, hwnd, EM_SETLIMITTEXT, limit, 0); - } - - case EM_EXLINEFROMCHAR: - TRACE_EDIT_MSG32("EM_EXLINEFROMCHAR -> LINEFROMCHAR"); - return CallWindowProcA(lpfnEditWndProc, hwnd, EM_LINEFROMCHAR, lParam, wParam); - - case EM_EXSETSEL: - TRACE_EDIT_MSG32("EM_EXSETSEL -> EM_SETSEL"); - cr = (VOID *) lParam; - return CallWindowProcA(lpfnEditWndProc, hwnd, EM_SETSEL, cr->cpMin, cr->cpMax); - - case EM_FINDTEXT: - TRACE_EDIT_MSG32("EM_FINDTEXT Ignored"); - return 0; - - case EM_FINDTEXTEX: - TRACE_EDIT_MSG32("EM_FINDTEXTEX Ignored"); - return 0; - - case EM_FINDTEXTEXW: - TRACE_EDIT_MSG32("EM_FINDTEXTEXW Ignored"); - return 0; - - case EM_FINDTEXTW: - TRACE_EDIT_MSG32("EM_FINDTEXTW Ignored"); - return 0; - - case EM_FINDWORDBREAK: - TRACE_EDIT_MSG32("EM_FINDWORDBREAK Ignored"); - return 0; - - case EM_FORMATRANGE: - TRACE_EDIT_MSG32("EM_FORMATRANGE Ignored"); - return 0; - - case EM_GETAUTOURLDETECT: - TRACE_EDIT_MSG32("EM_GETAUTOURLDETECT Ignored"); - return 0; - - case EM_GETBIDIOPTIONS: - TRACE_EDIT_MSG32("EM_GETBIDIOPTIONS Ignored"); - return 0; - - case EM_GETCHARFORMAT: - TRACE_EDIT_MSG32("EM_GETCHARFORMAT Ignored"); - return 0; - - case EM_GETEDITSTYLE: - TRACE_EDIT_MSG32("EM_GETEDITSTYLE Ignored"); - return 0; - - case EM_GETEVENTMASK: - TRACE_EDIT_MSG32("EM_GETEVENTMASK Ignored"); - return 0; - - case EM_GETIMECOLOR: - TRACE_EDIT_MSG32("EM_GETIMECOLOR Ignored"); - return 0; - - case EM_GETIMECOMPMODE: - TRACE_EDIT_MSG32("EM_GETIMECOMPMODE Ignored"); - return 0; - - case EM_GETIMEOPTIONS: - TRACE_EDIT_MSG32("EM_GETIMEOPTIONS Ignored"); - return 0; - - case EM_GETLANGOPTIONS: - TRACE_EDIT_MSG32("STUB: EM_GETLANGOPTIONS"); - return 0; - - case EM_GETOLEINTERFACE: - TRACE_EDIT_MSG32("EM_GETOLEINTERFACE Ignored"); - return 0; - - case EM_GETOPTIONS: - TRACE_EDIT_MSG32("EM_GETOPTIONS Ignored"); - return 0; - - case EM_GETPARAFORMAT: - TRACE_EDIT_MSG32("EM_GETPARAFORMAT Ignored"); - return 0; - - case EM_GETPUNCTUATION: - TRACE_EDIT_MSG32("EM_GETPUNCTUATION Ignored"); - return 0; - - case EM_GETREDONAME: - TRACE_EDIT_MSG32("EM_GETREDONAME Ignored"); - return 0; - - case EM_GETSCROLLPOS: - TRACE_EDIT_MSG32("EM_GETSCROLLPOS Ignored"); - return 0; - - case EM_GETSELTEXT: - TRACE_EDIT_MSG32("EM_GETSELTEXT"); - return RICHEDIT_GetSelText(hwnd,(void *)lParam); - - case EM_GETTEXTEX: - TRACE_EDIT_MSG32("EM_GETTEXTEX Ignored"); - return 0; - - case EM_GETTEXTLENGTHEX: - TRACE_EDIT_MSG32("EM_GETTEXTLENGTHEX Ignored"); - return 0; - - case EM_GETTEXTMODE: - TRACE_EDIT_MSG32("EM_GETTEXTMODE Ignored"); - return 0; - - case EM_GETTEXTRANGE: - TRACE_EDIT_MSG32("EM_GETTEXTRANGE"); - return RICHEDIT_GetTextRange(hwnd,(TEXTRANGEA *)lParam); - - case EM_GETTYPOGRAPHYOPTIONS: - TRACE_EDIT_MSG32("EM_GETTYPOGRAPHYOPTIONS Ignored"); - return 0; - - case EM_GETUNDONAME: - TRACE_EDIT_MSG32("EM_GETUNDONAME Ignored"); - return 0; - - case EM_GETWORDBREAKPROCEX: - TRACE_EDIT_MSG32("EM_GETWORDBREAKPROCEX Ignored"); - return 0; - - case EM_GETWORDWRAPMODE: - TRACE_EDIT_MSG32("EM_GETWORDWRAPMODE Ignored"); - return 0; - - case EM_GETZOOM: - TRACE_EDIT_MSG32("EM_GETZOOM Ignored"); - return 0; - - case EM_HIDESELECTION: - TRACE_EDIT_MSG32("EM_HIDESELECTION Ignored"); - return 0; - - case EM_PASTESPECIAL: - TRACE_EDIT_MSG32("EM_PASTESPECIAL Ignored"); - return 0; - - case EM_RECONVERSION: - TRACE_EDIT_MSG32("EM_RECONVERSION Ignored"); - return 0; - - case EM_REDO: - TRACE_EDIT_MSG32("EM_REDO Ignored"); - return 0; - - case EM_REQUESTRESIZE: - TRACE_EDIT_MSG32("EM_REQUESTRESIZE Ignored"); - return 0; - - case EM_SELECTIONTYPE: - TRACE_EDIT_MSG32("EM_SELECTIONTYPE Ignored"); - return 0; - - case EM_SETBIDIOPTIONS: - TRACE_EDIT_MSG32("EM_SETBIDIOPTIONS Ignored"); - return 0; - - case EM_SETBKGNDCOLOR: - TRACE_EDIT_MSG32("EM_SETBKGNDCOLOR Ignored"); - return 0; - - case EM_SETCHARFORMAT: - TRACE_EDIT_MSG32("EM_SETCHARFORMAT Ignored"); - return 0; - - case EM_SETEDITSTYLE: - TRACE_EDIT_MSG32("EM_SETEDITSTYLE Ignored"); - return 0; - - case EM_SETEVENTMASK: - TRACE_EDIT_MSG32("EM_SETEVENTMASK Ignored"); - return 0; - - case EM_SETFONTSIZE: - TRACE_EDIT_MSG32("EM_SETFONTSIZE Ignored"); - return 0; - - case EM_SETIMECOLOR: - TRACE_EDIT_MSG32("EM_SETIMECOLO Ignored"); - return 0; - - case EM_SETIMEOPTIONS: - TRACE_EDIT_MSG32("EM_SETIMEOPTIONS Ignored"); - return 0; - - case EM_SETLANGOPTIONS: - TRACE_EDIT_MSG32("EM_SETLANGOPTIONS Ignored"); - return 0; - - case EM_SETOLECALLBACK: - TRACE_EDIT_MSG32("EM_SETOLECALLBACK Ignored"); - return 0; - - case EM_SETOPTIONS: - TRACE_EDIT_MSG32("EM_SETOPTIONS Ignored"); - return 0; - - case EM_SETPALETTE: - TRACE_EDIT_MSG32("EM_SETPALETTE Ignored"); - return 0; - - case EM_SETPARAFORMAT: - TRACE_EDIT_MSG32("EM_SETPARAFORMAT Ignored"); - return 0; - - case EM_SETPUNCTUATION: - TRACE_EDIT_MSG32("EM_SETPUNCTUATION Ignored"); - return 0; - - case EM_SETSCROLLPOS: - TRACE_EDIT_MSG32("EM_SETSCROLLPOS Ignored"); - return 0; - - case EM_SETTARGETDEVICE: - TRACE_EDIT_MSG32("EM_SETTARGETDEVICE Ignored"); - return 0; - - case EM_SETTEXTEX: - TRACE_EDIT_MSG32("EM_SETTEXTEX Ignored"); - return 0; - - case EM_SETTEXTMODE: - TRACE_EDIT_MSG32("EM_SETTEXTMODE Ignored"); - return 0; - - case EM_SETTYPOGRAPHYOPTIONS: - TRACE_EDIT_MSG32("EM_SETTYPOGRAPHYOPTIONS Ignored"); - return 0; - - case EM_SETUNDOLIMIT: - TRACE_EDIT_MSG32("EM_SETUNDOLIMIT Ignored"); - return 0; - - case EM_SETWORDBREAKPROCEX: - TRACE_EDIT_MSG32("EM_SETWORDBREAKPROCEX Ignored"); - return 0; - - case EM_SETWORDWRAPMODE: - TRACE_EDIT_MSG32("EM_SETWORDWRAPMODE Ignored"); - return 0; - - case EM_SETZOOM: - TRACE_EDIT_MSG32("EM_SETZOOM Ignored"); - return 0; - - case EM_SHOWSCROLLBAR: - TRACE_EDIT_MSG32("EM_SHOWSCROLLBAR Ignored"); - return 0; - - case EM_STOPGROUPTYPING: - TRACE_EDIT_MSG32("EM_STOPGROUPTYPING Ignored"); - return 0; - - case EM_STREAMOUT: - TRACE_EDIT_MSG32("EM_STREAMOUT Ignored"); - return 0; - -/* Messages dispatched to the edit control */ - case EM_CANUNDO: - TRACE_EDIT_MSG32("EM_CANUNDO Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_CHARFROMPOS: - TRACE_EDIT_MSG32("EM_CHARFROMPOS Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_EMPTYUNDOBUFFER: - TRACE_EDIT_MSG32("EM_EMPTYUNDOBUFFER Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_FMTLINES: - TRACE_EDIT_MSG32("EM_FMTLINES Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETFIRSTVISIBLELINE: - TRACE_EDIT_MSG32("EM_GETFIRSTVISIBLELINE Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETHANDLE: - TRACE_EDIT_MSG32("EM_GETHANDLE Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - /* case EM_GETIMESTATUS:*/ - case EM_GETLIMITTEXT: - TRACE_EDIT_MSG32("EM_GETLIMITTEXT Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETLINE: - TRACE_EDIT_MSG32("EM_GETLINE Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETLINECOUNT: - TRACE_EDIT_MSG32("EM_GETLINECOUNT Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETMARGINS: - TRACE_EDIT_MSG32("EM_GETMARGINS Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETMODIFY: - TRACE_EDIT_MSG32("EM_GETMODIFY Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETPASSWORDCHAR: - TRACE_EDIT_MSG32("EM_GETPASSWORDCHAR Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETRECT: - TRACE_EDIT_MSG32("EM_GETRECT Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETSEL: - TRACE_EDIT_MSG32("EM_GETSEL Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETTHUMB: - TRACE_EDIT_MSG32("EM_GETTHUMB Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_GETWORDBREAKPROC: - TRACE_EDIT_MSG32("EM_GETWORDBREAKPROC Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_LINEFROMCHAR: - TRACE_EDIT_MSG32("EM_LINEFROMCHAR Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_LINEINDEX: - TRACE_EDIT_MSG32("EM_LINEINDEX Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_LINELENGTH: - TRACE_EDIT_MSG32("EM_LINELENGTH Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_LINESCROLL: - TRACE_EDIT_MSG32("EM_LINESCROLL Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_POSFROMCHAR: - TRACE_EDIT_MSG32("EM_POSFROMCHAR Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_REPLACESEL: - TRACE_EDIT_MSG32("case EM_REPLACESEL Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SCROLL: - TRACE_EDIT_MSG32("case EM_SCROLL Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SCROLLCARET: - case WM_USER+49: /* EM_SCROLLCARET too */ - TRACE_EDIT_MSG32("EM_SCROLLCARET Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, EM_SCROLLCARET, wParam, lParam); - case EM_SETHANDLE: - TRACE_EDIT_MSG32("EM_SETHANDLE Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - /* case EM_SETIMESTATUS:*/ - case EM_SETLIMITTEXT: - TRACE_EDIT_MSG32("EM_SETLIMITTEXT Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SETMARGINS: - TRACE_EDIT_MSG32("case EM_SETMARGINS Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SETMODIFY: - TRACE_EDIT_MSG32("EM_SETMODIFY Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SETPASSWORDCHAR: - TRACE_EDIT_MSG32("EM_SETPASSWORDCHAR Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SETREADONLY: - TRACE_EDIT_MSG32("EM_SETREADONLY Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SETRECT: - TRACE_EDIT_MSG32("EM_SETRECT Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SETRECTNP: - TRACE_EDIT_MSG32("EM_SETRECTNP Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SETSEL: - TRACE_EDIT_MSG32("EM_SETSEL Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SETTABSTOPS: - TRACE_EDIT_MSG32("EM_SETTABSTOPS Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_SETWORDBREAKPROC: - TRACE_EDIT_MSG32("EM_SETWORDBREAKPROC Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case EM_UNDO: - TRACE_EDIT_MSG32("EM_UNDO Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - - case WM_STYLECHANGING: - TRACE_EDIT_MSG32("WM_STYLECHANGING Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_STYLECHANGED: - TRACE_EDIT_MSG32("WM_STYLECHANGED Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_GETTEXT: - TRACE_EDIT_MSG32("WM_GETTEXT Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_GETTEXTLENGTH: - TRACE_EDIT_MSG32("WM_GETTEXTLENGTH Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_SETTEXT: - TRACE_EDIT_MSG32("WM_SETTEXT Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_CUT: - TRACE_EDIT_MSG32("WM_CUT Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_COPY: - TRACE_EDIT_MSG32("WM_COPY Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_PASTE: - TRACE_EDIT_MSG32("WM_PASTE Passed to edit control"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - - /* Messages passed to default handler. */ - case WM_NCCALCSIZE: - TRACE_EDIT_MSG32("WM_NCCALCSIZE Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_NCPAINT: - TRACE_EDIT_MSG32("WM_NCPAINT Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_PAINT: - TRACE_EDIT_MSG32("WM_PAINT Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_ERASEBKGND: - TRACE_EDIT_MSG32("WM_ERASEBKGND Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_KILLFOCUS: - TRACE_EDIT_MSG32("WM_KILLFOCUS Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_DESTROY: - TRACE_EDIT_MSG32("WM_DESTROY Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_CHILDACTIVATE: - TRACE_EDIT_MSG32("WM_CHILDACTIVATE Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - - case WM_WINDOWPOSCHANGING: - TRACE_EDIT_MSG32("WM_WINDOWPOSCHANGING Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_WINDOWPOSCHANGED: - TRACE_EDIT_MSG32("WM_WINDOWPOSCHANGED Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); -/* case WM_INITIALUPDATE: - TRACE_EDIT_MSG32("WM_INITIALUPDATE Passed to default"); - return DefWindowProcA( hwnd,uMsg,wParam,lParam); */ - case WM_CTLCOLOREDIT: - TRACE_EDIT_MSG32("WM_CTLCOLOREDIT Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_SETCURSOR: - TRACE_EDIT_MSG32("WM_SETCURSOR Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_MOVE: - TRACE_EDIT_MSG32("WM_MOVE Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_SHOWWINDOW: - TRACE_EDIT_MSG32("WM_SHOWWINDOW Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_PARENTNOTIFY: - TRACE_EDIT_MSG32("WM_PARENTNOTIFY Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_SETREDRAW: - TRACE_EDIT_MSG32("WM_SETREDRAW Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_NCDESTROY: - { - TRACE_EDIT_MSG32("WM_NCDESTROY Passed to default"); - HeapFree( GetProcessHeap(), 0, info->parser ); - HeapFree( GetProcessHeap(), 0, info ); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - } - - case WM_NCHITTEST: - TRACE_EDIT_MSG32("WM_NCHITTEST Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_CTLCOLORSTATIC: - TRACE_EDIT_MSG32("WM_CTLCOLORSTATIC Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_NCMOUSEMOVE: - TRACE_EDIT_MSG32("WM_NCMOUSEMOVE Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_CLEAR: - TRACE_EDIT_MSG32("WM_CLEAR Passed to default"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - /* - * used by IE in the EULA box - */ - case WM_ALTTABACTIVE: - TRACE_EDIT_MSG32("WM_ALTTABACTIVE"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_GETDLGCODE: - TRACE_EDIT_MSG32("WM_GETDLGCODE"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - case WM_SETFONT: - TRACE_EDIT_MSG32("WM_SETFONT"); - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); - - } - - if ((uMsg >= WM_USER) && (uMsg < WM_APP)) { - FIXME("Unknown message 0x%x Passed to default hwnd=%p, wParam=%08x, lParam=%08x\n", - uMsg, hwnd, (UINT)wParam, (UINT)lParam); - } - - return CallWindowProcA(lpfnEditWndProc, hwnd, uMsg, wParam, lParam); -} /*********************************************************************** * DllGetVersion [RICHED32.2] * - * Retrieves version information of the 'RICHED32.DLL' - * - * PARAMS - * pdvi [O] pointer to version information structure. - * - * RETURNS - * Success: S_OK - * Failure: E_INVALIDARG - * - * NOTES - * Returns version of a comctl32.dll from IE4.01 SP1. + * Retrieves version information */ - -HRESULT WINAPI -RICHED32_DllGetVersion (DLLVERSIONINFO *pdvi) +HRESULT WINAPI RICHED32_DllGetVersion (DLLVERSIONINFO *pdvi) { TRACE("\n"); - if (pdvi->cbSize != sizeof(DLLVERSIONINFO)) { - + if (pdvi->cbSize != sizeof(DLLVERSIONINFO)) return E_INVALIDARG; - } pdvi->dwMajorVersion = 4; pdvi->dwMinorVersion = 0; @@ -760,99 +60,47 @@ RICHED32_DllGetVersion (DLLVERSIONINFO *pdvi) return S_OK; } -/*** - * DESCRIPTION: - * Registers the window class. - * - * PARAMETER(S): - * None - * - * RETURN: - * None - */ -VOID RICHED32_Register(void) -{ - WNDCLASSA wndClass; - - TRACE("\n"); - - ZeroMemory(&wndClass, sizeof(WNDCLASSA)); - wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; - wndClass.lpfnWndProc = RICHED32_WindowProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = RTFInfoOffset + sizeof(RTFControl_Info*); - wndClass.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); - wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wndClass.lpszClassName = RICHEDIT_CLASS10A; /* WC_RICHED32A; */ - - RegisterClassA (&wndClass); -} - -/*** - * DESCRIPTION: - * Unregisters the window class. - * - * PARAMETER(S): - * None - * - * RETURN: - * None - */ -VOID RICHED32_Unregister(void) +/* Unregisters the window class. */ +static BOOL RICHED32_Unregister(void) { TRACE("\n"); UnregisterClassA(RICHEDIT_CLASS10A, NULL); + return TRUE; } -/*** - * DESCRIPTION: - * Initialize edit control class info - */ -VOID RICHEDIT_InitEditControlInfo(void) +/* Registers the window class. */ +static BOOL RICHED32_Register(void) { - WNDCLASSA wcEdit; + WNDCLASSA wndClass; - if (GetClassInfoA(0, "edit", &wcEdit)) + ZeroMemory(&wndClass, sizeof(WNDCLASSA)); + wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; + wndClass.lpfnWndProc = RichEdit10ANSIWndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 4; + wndClass.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wndClass.lpszClassName = RICHEDIT_CLASS10A; /* WC_RICHED32A; */ + + RegisterClassA(&wndClass); + + return TRUE; +} + +/* Initialization function */ +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + TRACE("\n"); + switch (fdwReason) { - lpfnEditWndProc = wcEdit.lpfnWndProc; - RTFInfoOffset = wcEdit.cbWndExtra; + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + return RICHED32_Register(); + + case DLL_PROCESS_DETACH: + return RICHED32_Unregister(); } - else - ERR("Failed to retrieve edit control class info\n"); -} - - -INT RICHEDIT_GetTextRange(HWND hwnd,TEXTRANGEA *tr) -{ - UINT alloc_size, text_size, range_size; - char *text; - - TRACE("start: 0x%x stop: 0x%x\n",(INT)tr->chrg.cpMin,(INT)tr->chrg.cpMax); - - if (!(alloc_size = SendMessageA(hwnd,WM_GETTEXTLENGTH,0,0))) return FALSE; - if (!(text = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (alloc_size+1)))) - return FALSE; - text_size = SendMessageA(hwnd,WM_GETTEXT,alloc_size,(INT)text); - - if (text_size > tr->chrg.cpMin) - { - range_size = (text_size> tr->chrg.cpMax) ? (tr->chrg.cpMax - tr->chrg.cpMin) : (text_size - tr->chrg.cpMin); - TRACE("EditText: %.30s ...\n",text+tr->chrg.cpMin); - memcpy(tr->lpstrText,text+tr->chrg.cpMin,range_size); - } - else range_size = 0; - HeapFree(GetProcessHeap(), 0, text); - - return range_size; -} - -INT RICHEDIT_GetSelText(HWND hwnd,LPSTR lpstrBuffer) -{ - TEXTRANGEA textrange; - - textrange.lpstrText = lpstrBuffer; - SendMessageA(hwnd,EM_GETSEL,(INT)&textrange.chrg.cpMin,(INT)&textrange.chrg.cpMax); - return RICHEDIT_GetTextRange(hwnd,&textrange); + return TRUE; } diff --git a/reactos/lib/rosrtl/misc/intrlck.c b/reactos/lib/rosrtl/misc/intrlck.c index 780176dcd3f..4d01c1100e9 100644 --- a/reactos/lib/rosrtl/misc/intrlck.c +++ b/reactos/lib/rosrtl/misc/intrlck.c @@ -15,6 +15,29 @@ * Copied from kernel32 */ +/* PowerPC Functions from gatomic.c in glib + * + * GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * g_atomic_*: atomic operations. + * Copyright (C) 2003 Sebastian Wilhelmi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ /************************************************************************ * InterlockedIncrement * @@ -36,6 +59,7 @@ STDCALL InterlockedIncrement(PLONG Addend) { long ret = 0; +#ifdef _M_IX86 __asm__ ( "\tlock\n" /* for SMP systems */ @@ -48,6 +72,10 @@ InterlockedIncrement(PLONG Addend) "2:\n" :"=r" (ret):"r" (Addend), "0" (0): "memory" ); +#elif defined(_M_PPC) + ret = *Addend; + ret = InterlockedExchangeAdd( Addend, ret + 1 ); +#endif return ret; } @@ -67,6 +95,7 @@ STDCALL InterlockedDecrement(LPLONG lpAddend) { long ret; +#ifdef _M_IX86 __asm__ ( "\tlock\n" /* for SMP systems */ @@ -79,6 +108,10 @@ InterlockedDecrement(LPLONG lpAddend) "2:\n" :"=r" (ret):"r" (lpAddend), "0" (0): "memory" ); +#elif defined(_M_PPC) + ret = *lpAddend; + ret = InterlockedExchangeAdd( lpAddend, ret - 1 ); +#endif return ret; @@ -97,14 +130,17 @@ LONG STDCALL InterlockedExchange(LPLONG target, LONG value ) { - long ret; +#ifdef _M_IX86 __asm__ ( /* lock for SMP systems */ "lock\n\txchgl %0,(%1)" :"=r" (ret):"r" (target), "0" (value):"memory" ); +#elif defined(_M_PPC) + do { + ret = *(volatile LONG *)target; + } while( InterlockedCompareExchange( target, value, ret ) != ret ); +#endif return ret; - - } /************************************************************************ @@ -124,8 +160,21 @@ InterlockedCompareExchange( LONG Comperand ) { LONG ret; +#ifdef _M_IX86 __asm__ __volatile__( "lock; cmpxchgl %2,(%1)" : "=a" (ret) : "r" (Destination), "r" (Exchange), "0" (Comperand) : "memory" ); +#elif defined(_M_PPC) + __asm__ __volatile__ ("sync\n" + "1: lwarx %0,0,%1\n" + " subf. %0,%2,%0\n" + " bne 2f\n" + " stwcx. %3,0,%1\n" + " bne- 1b\n" + "2: isync" + : "=&r" (ret) + : "b" (Destination), "r" (Comperand), "r" (Exchange) + : "cr0", "memory"); +#endif return ret; } @@ -145,14 +194,20 @@ InterlockedExchangeAdd( LONG Increment ) { - LONG ret; +#ifdef _M_IX86 __asm__ ( /* lock for SMP systems */ "lock\n\t" "xaddl %0,(%1)" :"=r" (ret) :"r" (Addend), "0" (Increment) :"memory" ); +#elif defined(_M_PPC) + LONG newval; + do { + ret = *(volatile LONG *)Addend; + newval = ret + Increment; + } while (InterlockedCompareExchange(Addend, ret, newval) != ret); +#endif return ret; - } diff --git a/reactos/lib/rpcrt4/ndr_marshall.c b/reactos/lib/rpcrt4/ndr_marshall.c index 33ff8f59ddd..314f7ee7c48 100644 --- a/reactos/lib/rpcrt4/ndr_marshall.c +++ b/reactos/lib/rpcrt4/ndr_marshall.c @@ -45,21 +45,21 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); #define BUFFER_PARANOIA 20 #if defined(__i386__) - #define LITTLE_ENDIAN_UINT32_WRITE(pchar, uint32) \ +# define LITTLE_ENDIAN_UINT32_WRITE(pchar, uint32) \ (*((UINT32 *)(pchar)) = (uint32)) - #define LITTLE_ENDIAN_UINT32_READ(pchar) \ +# define LITTLE_ENDIAN_UINT32_READ(pchar) \ (*((UINT32 *)(pchar))) #else /* these would work for i386 too, but less efficient */ - #define LITTLE_ENDIAN_UINT32_WRITE(pchar, uint32) \ +# define LITTLE_ENDIAN_UINT32_WRITE(pchar, uint32) \ (*(pchar) = LOBYTE(LOWORD(uint32)), \ *((pchar)+1) = HIBYTE(LOWORD(uint32)), \ *((pchar)+2) = LOBYTE(HIWORD(uint32)), \ *((pchar)+3) = HIBYTE(HIWORD(uint32)), \ (uint32)) /* allow as r-value */ - #define LITTLE_ENDIAN_UINT32_READ(pchar) \ +# define LITTLE_ENDIAN_UINT32_READ(pchar) \ (MAKELONG( \ MAKEWORD(*(pchar), *((pchar)+1)), \ MAKEWORD(*((pchar)+2), *((pchar)+3)))) @@ -78,14 +78,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); MAKEWORD(*((pchar)+1), *(pchar)))) #ifdef NDR_LOCAL_IS_BIG_ENDIAN - #define NDR_LOCAL_UINT32_WRITE(pchar, uint32) \ +# define NDR_LOCAL_UINT32_WRITE(pchar, uint32) \ BIG_ENDIAN_UINT32_WRITE(pchar, uint32) - #define NDR_LOCAL_UINT32_READ(pchar) \ +# define NDR_LOCAL_UINT32_READ(pchar) \ BIG_ENDIAN_UINT32_READ(pchar) #else - #define NDR_LOCAL_UINT32_WRITE(pchar, uint32) \ +# define NDR_LOCAL_UINT32_WRITE(pchar, uint32) \ LITTLE_ENDIAN_UINT32_WRITE(pchar, uint32) - #define NDR_LOCAL_UINT32_READ(pchar) \ +# define NDR_LOCAL_UINT32_READ(pchar) \ LITTLE_ENDIAN_UINT32_READ(pchar) #endif @@ -388,7 +388,12 @@ unsigned char *WINAPI NdrConformantStringMarshall(MIDL_STUB_MESSAGE *pStubMsg, TRACE("(pStubMsg == ^%p, pszMessage == ^%p, pFormat == ^%p)\n", pStubMsg, pszMessage, pFormat); assert(pFormat); - if (*pFormat == RPC_FC_C_CSTRING) { + if (pszMessage == NULL) { + TRACE("string=%s\n", debugstr_a(pszMessage)); + len = 0; + esize = 0; + } + else if (*pFormat == RPC_FC_C_CSTRING) { TRACE("string=%s\n", debugstr_a(pszMessage)); len = strlen(pszMessage)+1; esize = 1; @@ -416,8 +421,10 @@ unsigned char *WINAPI NdrConformantStringMarshall(MIDL_STUB_MESSAGE *pStubMsg, c += 8; /* offset: 0 */ NDR_LOCAL_UINT32_WRITE(c, len); /* actual length: (same) */ c += 4; - memcpy(c, pszMessage, len*esize); /* the string itself */ - c += len*esize; + if (len != 0) { + memcpy(c, pszMessage, len*esize); /* the string itself */ + c += len*esize; + } pStubMsg->Buffer = c; STD_OVERFLOW_CHECK(pStubMsg); @@ -435,7 +442,12 @@ void WINAPI NdrConformantStringBufferSize(PMIDL_STUB_MESSAGE pStubMsg, TRACE("(pStubMsg == ^%p, pMemory == ^%p, pFormat == ^%p)\n", pStubMsg, pMemory, pFormat); assert(pFormat); - if (*pFormat == RPC_FC_C_CSTRING) { + if (pMemory == NULL) { + /* we need 12 octets for the [maxlen, offset, len] DWORDS */ + TRACE("string=NULL\n"); + pStubMsg->BufferLength += 12 + BUFFER_PARANOIA; + } + else if (*pFormat == RPC_FC_C_CSTRING) { /* we need 12 octets for the [maxlen, offset, len] DWORDS, + 1 octet for '\0' */ TRACE("string=%s\n", debugstr_a(pMemory)); pStubMsg->BufferLength += strlen(pMemory) + 13 + BUFFER_PARANOIA; @@ -528,6 +540,11 @@ unsigned char *WINAPI NdrConformantStringUnmarshall( PMIDL_STUB_MESSAGE pStubMsg /* for clients, memory should be provided by caller */ } + if (len == 0) { + *ppMemory = NULL; + return NULL; + } + pMem = *ppMemory + ofs*esize; if (pMem != pStubMsg->Buffer) @@ -572,6 +589,8 @@ void WINAPI PointerMarshall(PMIDL_STUB_MESSAGE pStubMsg, switch (type) { case RPC_FC_RP: /* ref pointer (always non-null) */ break; + case RPC_FC_UP: /* unique pointer */ + break; default: FIXME("unhandled ptr type=%02x\n", type); } @@ -609,6 +628,8 @@ void WINAPI PointerUnmarshall(PMIDL_STUB_MESSAGE pStubMsg, switch (type) { case RPC_FC_RP: /* ref pointer (always non-null) */ break; + case RPC_FC_UP: /* unique pointer */ + break; default: FIXME("unhandled ptr type=%02x\n", type); } @@ -645,6 +666,8 @@ void WINAPI PointerBufferSize(PMIDL_STUB_MESSAGE pStubMsg, switch (type) { case RPC_FC_RP: /* ref pointer (always non-null) */ break; + case RPC_FC_UP: /* unique pointer */ + break; default: FIXME("unhandled ptr type=%02x\n", type); } diff --git a/reactos/lib/rpcrt4/ndr_midl.c b/reactos/lib/rpcrt4/ndr_midl.c index 76f972b69f4..f2929b8937c 100644 --- a/reactos/lib/rpcrt4/ndr_midl.c +++ b/reactos/lib/rpcrt4/ndr_midl.c @@ -233,12 +233,12 @@ unsigned char* WINAPI NdrServerInitializeNew( PRPC_MESSAGE pRpcMsg, PMIDL_STUB_M unsigned char *WINAPI NdrGetBuffer(MIDL_STUB_MESSAGE *stubmsg, unsigned long buflen, RPC_BINDING_HANDLE handle) { TRACE("(stubmsg == ^%p, buflen == %lu, handle == %p): wild guess.\n", stubmsg, buflen, handle); - + assert( stubmsg && stubmsg->RpcMsg ); /* I guess this is our chance to put the binding handle into the RPC_MESSAGE */ stubmsg->RpcMsg->Handle = handle; - + stubmsg->RpcMsg->BufferLength = buflen; if (I_RpcGetBuffer(stubmsg->RpcMsg) != S_OK) return NULL; @@ -248,6 +248,7 @@ unsigned char *WINAPI NdrGetBuffer(MIDL_STUB_MESSAGE *stubmsg, unsigned long buf stubmsg->BufferEnd = stubmsg->Buffer + stubmsg->BufferLength; return (stubmsg->Buffer = (unsigned char *)stubmsg->RpcMsg->Buffer); } + /*********************************************************************** * NdrFreeBuffer [RPCRT4.@] */ @@ -262,28 +263,34 @@ void WINAPI NdrFreeBuffer(MIDL_STUB_MESSAGE *pStubMsg) /************************************************************************ * NdrSendReceive [RPCRT4.@] */ -unsigned char *WINAPI NdrSendReceive( MIDL_STUB_MESSAGE *stubmsg, unsigned char *buffer ) +unsigned char *WINAPI NdrSendReceive( MIDL_STUB_MESSAGE *pStubMsg, unsigned char *buffer ) { - TRACE("(stubmsg == ^%p, buffer == ^%p)\n", stubmsg, buffer); + TRACE("(pStubMsg == ^%p, buffer == ^%p)\n", pStubMsg, buffer); /* FIXME: how to handle errors? (raise exception?) */ - if (!stubmsg) { + if (!pStubMsg) { ERR("NULL stub message. No action taken.\n"); return NULL; } - if (!stubmsg->RpcMsg) { + if (!pStubMsg->RpcMsg) { ERR("RPC Message not present in stub message. No action taken.\n"); return NULL; } /* FIXME: Seems wrong. Where should this really come from, and when? */ - stubmsg->RpcMsg->DataRepresentation = NDR_LOCAL_DATA_REPRESENTATION; + pStubMsg->RpcMsg->DataRepresentation = NDR_LOCAL_DATA_REPRESENTATION; - if (I_RpcSendReceive(stubmsg->RpcMsg) != RPC_S_OK) { + if (I_RpcSendReceive(pStubMsg->RpcMsg) != RPC_S_OK) { WARN("I_RpcSendReceive did not return success.\n"); /* FIXME: raise exception? */ + return NULL; } + pStubMsg->BufferLength = pStubMsg->RpcMsg->BufferLength; + pStubMsg->BufferStart = pStubMsg->RpcMsg->Buffer; + pStubMsg->BufferEnd = pStubMsg->BufferStart + pStubMsg->BufferLength; + pStubMsg->Buffer = pStubMsg->BufferStart; + /* FIXME: is this the right return value? */ return NULL; } diff --git a/reactos/lib/rpcrt4/rpc_binding.c b/reactos/lib/rpcrt4/rpc_binding.c index 82e1690ea5f..849b8f0f825 100644 --- a/reactos/lib/rpcrt4/rpc_binding.c +++ b/reactos/lib/rpcrt4/rpc_binding.c @@ -147,11 +147,12 @@ RPC_STATUS RPCRT4_OpenConnection(RpcConnection* Connection) RPC_MAX_PACKET_SIZE, RPC_MAX_PACKET_SIZE, 5000, NULL); HeapFree(GetProcessHeap(), 0, pname); memset(&Connection->ovl, 0, sizeof(Connection->ovl)); - Connection->ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); - if (!ConnectNamedPipe(Connection->conn, &Connection->ovl)) { + Connection->ovl[0].hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + Connection->ovl[1].hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + if (!ConnectNamedPipe(Connection->conn, &Connection->ovl[0])) { WARN("Couldn't ConnectNamedPipe (error was %ld)\n", GetLastError()); if (GetLastError() == ERROR_PIPE_CONNECTED) { - SetEvent(Connection->ovl.hEvent); + SetEvent(Connection->ovl[0].hEvent); return RPC_S_OK; } else if (GetLastError() == ERROR_IO_PENDING) { return RPC_S_OK; @@ -171,13 +172,16 @@ RPC_STATUS RPCRT4_OpenConnection(RpcConnection* Connection) RPC_MAX_PACKET_SIZE, RPC_MAX_PACKET_SIZE, 5000, NULL); HeapFree(GetProcessHeap(), 0, pname); memset(&Connection->ovl, 0, sizeof(Connection->ovl)); - Connection->ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); - if (!ConnectNamedPipe(Connection->conn, &Connection->ovl)) { - WARN("Couldn't ConnectNamedPipe (error was %ld)\n", GetLastError()); + Connection->ovl[0].hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + Connection->ovl[1].hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + if (!ConnectNamedPipe(Connection->conn, &Connection->ovl[0])) { if (GetLastError() == ERROR_PIPE_CONNECTED) { - SetEvent(Connection->ovl.hEvent); + SetEvent(Connection->ovl[0].hEvent); + return RPC_S_OK; + } else if (GetLastError() == ERROR_IO_PENDING) { return RPC_S_OK; } + WARN("Couldn't ConnectNamedPipe (error was %ld)\n", GetLastError()); return RPC_S_SERVER_UNAVAILABLE; } } @@ -211,7 +215,7 @@ RPC_STATUS RPCRT4_OpenConnection(RpcConnection* Connection) return RPC_S_SERVER_TOO_BUSY; } else { err = GetLastError(); - TRACE("connection failed, error=%lx\n", err); + WARN("connection failed, error=%lx\n", err); HeapFree(GetProcessHeap(), 0, pname); return RPC_S_SERVER_UNAVAILABLE; } @@ -223,7 +227,8 @@ RPC_STATUS RPCRT4_OpenConnection(RpcConnection* Connection) /* pipe is connected; change to message-read mode. */ dwMode = PIPE_READMODE_MESSAGE; SetNamedPipeHandleState(conn, &dwMode, NULL, NULL); - Connection->ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + Connection->ovl[0].hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + Connection->ovl[1].hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); Connection->conn = conn; } /* protseq=ncacn_np: named pipes */ @@ -243,7 +248,7 @@ RPC_STATUS RPCRT4_OpenConnection(RpcConnection* Connection) err = GetLastError(); /* we don't need to handle ERROR_PIPE_BUSY here, * the doc says that it is returned to the app */ - TRACE("connection failed, error=%lx\n", err); + WARN("connection failed, error=%lx\n", err); HeapFree(GetProcessHeap(), 0, pname); if (err == ERROR_PIPE_BUSY) return RPC_S_SERVER_TOO_BUSY; @@ -257,7 +262,8 @@ RPC_STATUS RPCRT4_OpenConnection(RpcConnection* Connection) /* pipe is connected; change to message-read mode. */ dwMode = PIPE_READMODE_MESSAGE; SetNamedPipeHandleState(conn, &dwMode, NULL, NULL); - Connection->ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + Connection->ovl[0].hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + Connection->ovl[1].hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); Connection->conn = conn; } else { ERR("protseq %s not supported\n", Connection->Protseq); @@ -276,9 +282,13 @@ RPC_STATUS RPCRT4_CloseConnection(RpcConnection* Connection) CloseHandle(Connection->conn); Connection->conn = 0; } - if (Connection->ovl.hEvent) { - CloseHandle(Connection->ovl.hEvent); - Connection->ovl.hEvent = 0; + if (Connection->ovl[0].hEvent) { + CloseHandle(Connection->ovl[0].hEvent); + Connection->ovl[0].hEvent = 0; + } + if (Connection->ovl[1].hEvent) { + CloseHandle(Connection->ovl[1].hEvent); + Connection->ovl[1].hEvent = 0; } return RPC_S_OK; } @@ -292,7 +302,8 @@ RPC_STATUS RPCRT4_SpawnConnection(RpcConnection** Connection, RpcConnection* Old /* because of the way named pipes work, we'll transfer the connected pipe * to the child, then reopen the server binding to continue listening */ NewConnection->conn = OldConnection->conn; - NewConnection->ovl = OldConnection->ovl; + NewConnection->ovl[0] = OldConnection->ovl[0]; + NewConnection->ovl[1] = OldConnection->ovl[1]; OldConnection->conn = 0; memset(&OldConnection->ovl, 0, sizeof(OldConnection->ovl)); *Connection = NewConnection; @@ -397,7 +408,7 @@ RPC_STATUS RPCRT4_SetBindingObject(RpcBinding* Binding, UUID* ObjectUuid) RPC_STATUS RPCRT4_MakeBinding(RpcBinding** Binding, RpcConnection* Connection) { RpcBinding* NewBinding; - TRACE("(*RpcBinding == ^%p, Connection == ^%p)\n", *Binding, Connection); + TRACE("(RpcBinding == ^%p, Connection == ^%p)\n", Binding, Connection); RPCRT4_AllocBinding(&NewBinding, Connection->server); NewBinding->Protseq = RPCRT4_strdupA(Connection->Protseq); @@ -1094,3 +1105,22 @@ RPC_STATUS WINAPI RpcNetworkIsProtseqValidW(LPWSTR protseq) { FIXME("Unknown protseq %s - we probably need to implement it one day\n", debugstr_w(protseq)); return RPC_S_PROTSEQ_NOT_SUPPORTED; } + +/*********************************************************************** + * RpcImpersonateClient (RPCRT4.@) + * + * Impersonates the client connected via a binding handle so that security + * checks are done in the context of the client. + * + * PARAMS + * BindingHandle [I] Handle to the binding to the client. + * + * RETURNS + * Success: RPS_S_OK. + * Failure: RPC_STATUS value. + */ +RPC_STATUS WINAPI RpcImpersonateClient(RPC_BINDING_HANDLE BindingHandle) +{ + FIXME("(%p): stub\n", BindingHandle); + return RPC_S_NO_CONTEXT_AVAILABLE; +} diff --git a/reactos/lib/rpcrt4/rpc_binding.h b/reactos/lib/rpcrt4/rpc_binding.h index 0d7daa50000..61f796b41ff 100644 --- a/reactos/lib/rpcrt4/rpc_binding.h +++ b/reactos/lib/rpcrt4/rpc_binding.h @@ -32,7 +32,7 @@ typedef struct _RpcConnection LPSTR NetworkAddr; LPSTR Endpoint; HANDLE conn, thread; - OVERLAPPED ovl; + OVERLAPPED ovl[2]; USHORT MaxTransmissionSize; /* The active interface bound to server. */ RPC_SYNTAX_IDENTIFIER ActiveInterface; diff --git a/reactos/lib/rpcrt4/rpc_message.c b/reactos/lib/rpcrt4/rpc_message.c index d10e56c9ab4..a57c25c79a4 100644 --- a/reactos/lib/rpcrt4/rpc_message.c +++ b/reactos/lib/rpcrt4/rpc_message.c @@ -265,12 +265,12 @@ RPC_STATUS RPCRT4_Send(RpcConnection *Connection, RpcPktHdr *Header, } /* transmit packet header */ - ResetEvent(Connection->ovl.hEvent); - if (!WriteFile(Connection->conn, Header, hdr_size, &count, &Connection->ovl)) { + if (!WriteFile(Connection->conn, Header, hdr_size, &count, &Connection->ovl[1]) && + ERROR_IO_PENDING != GetLastError()) { WARN("WriteFile failed with error %ld\n", GetLastError()); return GetLastError(); } - if (!GetOverlappedResult(Connection->conn, &Connection->ovl, &count, TRUE)) { + if (!GetOverlappedResult(Connection->conn, &Connection->ovl[1], &count, TRUE)) { WARN("GetOverlappedResult failed with error %ld\n", GetLastError()); return GetLastError(); } @@ -282,12 +282,12 @@ RPC_STATUS RPCRT4_Send(RpcConnection *Connection, RpcPktHdr *Header, } /* send the fragment data */ - ResetEvent(Connection->ovl.hEvent); - if (!WriteFile(Connection->conn, buffer_pos, Header->common.frag_len - hdr_size, &count, &Connection->ovl)) { + if (!WriteFile(Connection->conn, buffer_pos, Header->common.frag_len - hdr_size, &count, &Connection->ovl[1]) && + ERROR_IO_PENDING != GetLastError()) { WARN("WriteFile failed with error %ld\n", GetLastError()); return GetLastError(); } - if (!GetOverlappedResult(Connection->conn, &Connection->ovl, &count, TRUE)) { + if (!GetOverlappedResult(Connection->conn, &Connection->ovl[1], &count, TRUE)) { WARN("GetOverlappedResult failed with error %ld\n", GetLastError()); return GetLastError(); } @@ -319,13 +319,13 @@ RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, TRACE("(%p, %p, %p)\n", Connection, Header, pMsg); /* read packet common header */ - ResetEvent(Connection->ovl.hEvent); - if (!ReadFile(Connection->conn, &common_hdr, sizeof(common_hdr), &dwRead, &Connection->ovl)) { + if (!ReadFile(Connection->conn, &common_hdr, sizeof(common_hdr), &dwRead, &Connection->ovl[0]) && + ERROR_IO_PENDING != GetLastError()) { WARN("ReadFile failed with error %ld\n", GetLastError()); status = RPC_S_PROTOCOL_ERROR; goto fail; } - if (!GetOverlappedResult(Connection->conn, &Connection->ovl, &dwRead, TRUE)) { + if (!GetOverlappedResult(Connection->conn, &Connection->ovl[0], &dwRead, TRUE)) { if (GetLastError() != ERROR_MORE_DATA) { WARN("GetOverlappedResult failed with error %ld\n", GetLastError()); status = RPC_S_PROTOCOL_ERROR; @@ -333,6 +333,7 @@ RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, } } if (dwRead != sizeof(common_hdr)) { + WARN("Short read of header, %ld/%d bytes\n", dwRead, sizeof(common_hdr)); status = RPC_S_PROTOCOL_ERROR; goto fail; } @@ -347,6 +348,7 @@ RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, hdr_length = RPCRT4_GetHeaderSize((RpcPktHdr*)&common_hdr); if (hdr_length == 0) { + WARN("header length == 0\n"); status = RPC_S_PROTOCOL_ERROR; goto fail; } @@ -355,14 +357,14 @@ RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, memcpy(*Header, &common_hdr, sizeof(common_hdr)); /* read the rest of packet header */ - ResetEvent(Connection->ovl.hEvent); if (!ReadFile(Connection->conn, &(*Header)->common + 1, - hdr_length - sizeof(common_hdr), &dwRead, &Connection->ovl)) { + hdr_length - sizeof(common_hdr), &dwRead, &Connection->ovl[0]) && + ERROR_IO_PENDING != GetLastError()) { WARN("ReadFile failed with error %ld\n", GetLastError()); status = RPC_S_PROTOCOL_ERROR; goto fail; } - if (!GetOverlappedResult(Connection->conn, &Connection->ovl, &dwRead, TRUE)) { + if (!GetOverlappedResult(Connection->conn, &Connection->ovl[0], &dwRead, TRUE)) { if (GetLastError() != ERROR_MORE_DATA) { WARN("GetOverlappedResult failed with error %ld\n", GetLastError()); status = RPC_S_PROTOCOL_ERROR; @@ -370,6 +372,7 @@ RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, } } if (dwRead != hdr_length - sizeof(common_hdr)) { + WARN("bad header length, %ld/%ld bytes\n", dwRead, hdr_length - sizeof(common_hdr)); status = RPC_S_PROTOCOL_ERROR; goto fail; } @@ -386,6 +389,9 @@ RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, default: pMsg->BufferLength = common_hdr.frag_len - hdr_length; } + + TRACE("buffer length = %u\n", pMsg->BufferLength); + status = I_RpcGetBuffer(pMsg); if (status != RPC_S_OK) goto fail; @@ -403,13 +409,13 @@ RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, } if (data_length == 0) dwRead = 0; else { - ResetEvent(Connection->ovl.hEvent); - if (!ReadFile(Connection->conn, buffer_ptr, data_length, &dwRead, &Connection->ovl)) { + if (!ReadFile(Connection->conn, buffer_ptr, data_length, &dwRead, &Connection->ovl[0]) && + ERROR_IO_PENDING != GetLastError()) { WARN("ReadFile failed with error %ld\n", GetLastError()); status = RPC_S_PROTOCOL_ERROR; goto fail; } - if (!GetOverlappedResult(Connection->conn, &Connection->ovl, &dwRead, TRUE)) { + if (!GetOverlappedResult(Connection->conn, &Connection->ovl[0], &dwRead, TRUE)) { if (GetLastError() != ERROR_MORE_DATA) { WARN("GetOverlappedResult failed with error %ld\n", GetLastError()); status = RPC_S_PROTOCOL_ERROR; @@ -418,12 +424,15 @@ RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, } } if (dwRead != data_length) { + WARN("bad data length, %ld/%ld\n", dwRead, data_length); status = RPC_S_PROTOCOL_ERROR; goto fail; } + /* when there is no more data left, it should be the last packet */ if (buffer_length == pMsg->BufferLength && ((*Header)->common.flags & RPC_FLG_LAST) == 0) { + WARN("no more data left, but not last packet\n"); status = RPC_S_PROTOCOL_ERROR; goto fail; } @@ -433,13 +442,13 @@ RPC_STATUS RPCRT4_Receive(RpcConnection *Connection, RpcPktHdr **Header, TRACE("next header\n"); /* read the header of next packet */ - ResetEvent(Connection->ovl.hEvent); - if (!ReadFile(Connection->conn, *Header, hdr_length, &dwRead, &Connection->ovl)) { + if (!ReadFile(Connection->conn, *Header, hdr_length, &dwRead, &Connection->ovl[0]) && + ERROR_IO_PENDING != GetLastError()) { WARN("ReadFile failed with error %ld\n", GetLastError()); status = GetLastError(); goto fail; } - if (!GetOverlappedResult(Connection->conn, &Connection->ovl, &dwRead, TRUE)) { + if (!GetOverlappedResult(Connection->conn, &Connection->ovl[0], &dwRead, TRUE)) { if (GetLastError() != ERROR_MORE_DATA) { WARN("GetOverlappedResult failed with error %ld\n", GetLastError()); status = RPC_S_PROTOCOL_ERROR; @@ -616,6 +625,7 @@ RPC_STATUS WINAPI I_RpcReceive(PRPC_MESSAGE pMsg) status = RPC_S_CALL_FAILED; /* ? */ goto fail; default: + WARN("bad packet type %d\n", hdr->common.ptype); goto fail; } diff --git a/reactos/lib/rpcrt4/rpc_server.c b/reactos/lib/rpcrt4/rpc_server.c index bb3cb95da82..158033fc7a9 100644 --- a/reactos/lib/rpcrt4/rpc_server.c +++ b/reactos/lib/rpcrt4/rpc_server.c @@ -93,7 +93,14 @@ static CRITICAL_SECTION listen_cs = { &listen_cs_debug, -1, 0, 0, 0, 0 }; static BOOL std_listen; static LONG listen_count = -1; -static HANDLE mgr_event, server_thread; +/* set on change of configuration (e.g. listening on new protseq) */ +static HANDLE mgr_event; +/* mutex for ensuring only one thread can change state at a time */ +static HANDLE mgr_mutex; +/* set when server thread has finished opening connections */ +static HANDLE server_ready_event; +/* thread that waits for connections */ +static HANDLE server_thread; static CRITICAL_SECTION spacket_cs; static CRITICAL_SECTION_DEBUG spacket_cs_debug = @@ -463,6 +470,7 @@ static DWORD CALLBACK RPCRT4_server_thread(LPVOID the_arg) RpcServerProtseq* cps; RpcConnection* conn; RpcConnection* cconn; + BOOL set_ready_event = FALSE; TRACE("(the_arg == ^%p)\n", the_arg); @@ -475,7 +483,7 @@ static DWORD CALLBACK RPCRT4_server_thread(LPVOID the_arg) conn = cps->conn; while (conn) { RPCRT4_OpenConnection(conn); - if (conn->ovl.hEvent) count++; + if (conn->ovl[0].hEvent) count++; conn = conn->Next; } cps = cps->Next; @@ -492,18 +500,29 @@ static DWORD CALLBACK RPCRT4_server_thread(LPVOID the_arg) while (cps) { conn = cps->conn; while (conn) { - if (conn->ovl.hEvent) objs[count++] = conn->ovl.hEvent; + if (conn->ovl[0].hEvent) objs[count++] = conn->ovl[0].hEvent; conn = conn->Next; } cps = cps->Next; } LeaveCriticalSection(&server_cs); + if (set_ready_event) + { + /* signal to function that changed state that we are now sync'ed */ + SetEvent(server_ready_event); + set_ready_event = FALSE; + } + /* start waiting */ res = WaitForMultipleObjects(count, objs, FALSE, INFINITE); if (res == WAIT_OBJECT_0) { - ResetEvent(m_event); - if (!std_listen) break; + if (!std_listen) + { + SetEvent(server_ready_event); + break; + } + set_ready_event = TRUE; } else if (res == WAIT_FAILED) { ERR("wait failed\n"); @@ -517,7 +536,7 @@ static DWORD CALLBACK RPCRT4_server_thread(LPVOID the_arg) while (cps) { conn = cps->conn; while (conn) { - if (conn->ovl.hEvent == b_handle) break; + if (conn->ovl[0].hEvent == b_handle) break; conn = conn->Next; } if (conn) break; @@ -548,13 +567,31 @@ static DWORD CALLBACK RPCRT4_server_thread(LPVOID the_arg) return 0; } +/* tells the server thread that the state has changed and waits for it to + * make the changes */ +static void RPCRT4_sync_with_server_thread(void) +{ + /* make sure we are the only thread sync'ing the server state, otherwise + * there is a race with the server thread setting an older state and setting + * the server_ready_event when the new state hasn't yet been applied */ + WaitForSingleObject(mgr_mutex, INFINITE); + + SetEvent(mgr_event); + /* wait for server thread to make the requested changes before returning */ + WaitForSingleObject(server_ready_event, INFINITE); + + ReleaseMutex(mgr_mutex); +} + static void RPCRT4_start_listen(void) { TRACE("\n"); EnterCriticalSection(&listen_cs); if (! ++listen_count) { - if (!mgr_event) mgr_event = CreateEventW(NULL, TRUE, FALSE, NULL); + if (!mgr_mutex) mgr_mutex = CreateMutexW(NULL, FALSE, NULL); + if (!mgr_event) mgr_event = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!server_ready_event) server_ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); if (!server_sem) server_sem = CreateSemaphoreW(NULL, 0, MAX_THREADS, NULL); if (!worker_tls) worker_tls = TlsAlloc(); std_listen = TRUE; @@ -562,7 +599,7 @@ static void RPCRT4_start_listen(void) LeaveCriticalSection(&listen_cs); } else { LeaveCriticalSection(&listen_cs); - SetEvent(mgr_event); + RPCRT4_sync_with_server_thread(); } } @@ -574,7 +611,7 @@ static void RPCRT4_stop_listen(void) else if (--listen_count == -1) { std_listen = FALSE; LeaveCriticalSection(&listen_cs); - SetEvent(mgr_event); + RPCRT4_sync_with_server_thread(); } else LeaveCriticalSection(&listen_cs); assert(listen_count > -2); @@ -589,7 +626,7 @@ static RPC_STATUS RPCRT4_use_protseq(RpcServerProtseq* ps) protseqs = ps; LeaveCriticalSection(&server_cs); - if (std_listen) SetEvent(mgr_event); + if (std_listen) RPCRT4_sync_with_server_thread(); return RPC_S_OK; } diff --git a/reactos/lib/rpcrt4/rpcrt4.spec b/reactos/lib/rpcrt4/rpcrt4.spec index c2250a8e1e4..b098786153e 100644 --- a/reactos/lib/rpcrt4/rpcrt4.spec +++ b/reactos/lib/rpcrt4/rpcrt4.spec @@ -72,7 +72,7 @@ @ stub RpcGetAsyncCallStatus @ stub RpcIfIdVectorFree @ stub RpcIfInqId -@ stub RpcImpersonateClient +@ stdcall RpcImpersonateClient(ptr) @ stub RpcInitializeAsyncHandle @ stub RpcMgmtBindingInqParameter # win9x @ stub RpcMgmtBindingSetParameter # win9x diff --git a/reactos/lib/rpcrt4/rpcss_np_client.c b/reactos/lib/rpcrt4/rpcss_np_client.c index 62b1843e631..230baae7151 100644 --- a/reactos/lib/rpcrt4/rpcss_np_client.c +++ b/reactos/lib/rpcrt4/rpcss_np_client.c @@ -84,7 +84,9 @@ HANDLE RPCRT4_RpcssNPConnect(void) if (! WaitNamedPipeA(NAME_RPCSS_NAMED_PIPE, MASTER_MUTEX_WAITNAMEDPIPE_TIMEOUT)) { ERR("Named pipe unavailable after waiting. Something is probably wrong.\n"); - return NULL; + CloseHandle(the_pipe); + the_pipe = NULL; + break; } } diff --git a/reactos/lib/rtl/bitmap.c b/reactos/lib/rtl/bitmap.c index c94c22840a3..c98583e0e4b 100644 --- a/reactos/lib/rtl/bitmap.c +++ b/reactos/lib/rtl/bitmap.c @@ -187,8 +187,7 @@ RtlClearBits(PRTL_BITMAP BitMapHeader, if (StartingIndex >= Size || NumberToClear == 0) return; - if (StartingIndex + NumberToClear > Size) - NumberToClear = Size - StartingIndex; + ASSERT(StartingIndex + NumberToClear <= Size); Ptr = (PULONG)BitMapHeader->Buffer + (StartingIndex / 32); while (NumberToClear) @@ -834,8 +833,7 @@ RtlSetBits(PRTL_BITMAP BitMapHeader, if (StartingIndex >= Size || NumberToSet == 0) return; - if (StartingIndex + NumberToSet > Size) - NumberToSet = Size - StartingIndex; + ASSERT(StartingIndex + NumberToSet <= Size); Ptr = (PULONG)BitMapHeader->Buffer + (StartingIndex / 32); while (NumberToSet) diff --git a/reactos/lib/rtl/error.c b/reactos/lib/rtl/error.c index 7765d673616..3e7541bc42b 100644 --- a/reactos/lib/rtl/error.c +++ b/reactos/lib/rtl/error.c @@ -20,7 +20,7 @@ /* INCLUDES *****************************************************************/ #include -#include +#include #define NDEBUG #include diff --git a/reactos/lib/ntdll/rtl/i386/chkstk.s b/reactos/lib/rtl/i386/chkstk.s similarity index 100% rename from reactos/lib/ntdll/rtl/i386/chkstk.s rename to reactos/lib/rtl/i386/chkstk.s diff --git a/reactos/lib/rtl/makefile b/reactos/lib/rtl/makefile index 013dfb8dea1..6987763c93e 100644 --- a/reactos/lib/rtl/makefile +++ b/reactos/lib/rtl/makefile @@ -18,7 +18,10 @@ endif TARGET_OBJECTS = \ acl.o \ + ppb.o \ bit.o \ + thread.o \ + process.o \ bitmap.o \ bootdata.o \ compress.o \ @@ -46,6 +49,7 @@ TARGET_OBJECTS = \ unicode.o \ unicodeprefix.o \ version.o \ + i386/chkstk.o \ i386/exception.o \ i386/except.o diff --git a/reactos/lib/ntdll/rtl/ppb.c b/reactos/lib/rtl/ppb.c similarity index 94% rename from reactos/lib/ntdll/rtl/ppb.c rename to reactos/lib/rtl/ppb.c index d05a9633840..c9232441534 100644 --- a/reactos/lib/ntdll/rtl/ppb.c +++ b/reactos/lib/rtl/ppb.c @@ -12,7 +12,6 @@ /* INCLUDES ****************************************************************/ #include -#include #include #include #include @@ -28,28 +27,12 @@ #define ALIGN(x,align) (((ULONG)(x)+(align)-1UL)&(~((align)-1UL))) +KPROCESSOR_MODE +RtlpGetMode(); + + /* FUNCTIONS ****************************************************************/ -/* - * @implemented - */ -VOID STDCALL -RtlAcquirePebLock(VOID) -{ - PPEB Peb = NtCurrentPeb (); - Peb->FastPebLockRoutine (Peb->FastPebLock); -} - - -/* - * @implemented - */ -VOID STDCALL -RtlReleasePebLock(VOID) -{ - PPEB Peb = NtCurrentPeb (); - Peb->FastPebUnlockRoutine (Peb->FastPebLock); -} static inline VOID RtlpCopyParameterString(PWCHAR *Ptr, @@ -100,7 +83,7 @@ RtlCreateProcessParameters(PRTL_USER_PROCESS_PARAMETERS *ProcessParameters, EmptyString.MaximumLength = sizeof(WCHAR); EmptyString.Buffer = L""; - if (NtCurrentPeb()->ProcessParameters) + if (RtlpGetMode() == UserMode) { if (DllPath == NULL) DllPath = &NtCurrentPeb()->ProcessParameters->DllPath; @@ -152,7 +135,7 @@ RtlCreateProcessParameters(PRTL_USER_PROCESS_PARAMETERS *ProcessParameters, /* Calculate the required block size */ RegionSize = ROUNDUP(Length, PAGE_SIZE); - Status = NtAllocateVirtualMemory(NtCurrentProcess(), + Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&Param, 0, &RegionSize, @@ -253,7 +236,7 @@ RtlDestroyProcessParameters(PRTL_USER_PROCESS_PARAMETERS ProcessParameters) { ULONG RegionSize = 0; - return NtFreeVirtualMemory (NtCurrentProcess (), + return ZwFreeVirtualMemory (NtCurrentProcess (), (PVOID)ProcessParameters, &RegionSize, MEM_RELEASE); diff --git a/reactos/lib/rtl/process.c b/reactos/lib/rtl/process.c new file mode 100644 index 00000000000..ff27c211913 --- /dev/null +++ b/reactos/lib/rtl/process.c @@ -0,0 +1,317 @@ +/* $Id$ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: lib/ntdll/rtl/process.c + * PURPOSE: Process functions + * PROGRAMMER: Ariadne ( ariadne@xs4all.nl) + * UPDATE HISTORY: + * Created 01/11/98 + */ + +/* INCLUDES ****************************************************************/ + +#include +#include +#include +#include +#include + +#define NDEBUG +#include + +/* FUNCTIONS ****************************************************************/ + + +static NTSTATUS +RtlpMapFile(PUNICODE_STRING ImageFileName, + PRTL_USER_PROCESS_PARAMETERS Ppb, + ULONG Attributes, + PHANDLE Section) +{ + HANDLE hFile; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; + NTSTATUS Status; + + hFile = NULL; + + RtlDeNormalizeProcessParams (Ppb); + +// DbgPrint("ImagePathName %x\n", Ppb->ImagePathName.Buffer); + + InitializeObjectAttributes(&ObjectAttributes, + ImageFileName, + Attributes & (OBJ_CASE_INSENSITIVE | OBJ_INHERIT), + NULL, + SecurityDescriptor); + + RtlNormalizeProcessParams (Ppb); + + /* + * Try to open the executable + */ + + Status = ZwOpenFile(&hFile, + SYNCHRONIZE|FILE_EXECUTE|FILE_READ_DATA, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_DELETE|FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE); + + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + Status = ZwCreateSection(Section, + SECTION_ALL_ACCESS, + NULL, + NULL, + PAGE_EXECUTE, + SEC_IMAGE, + hFile); + ZwClose(hFile); + + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + return(STATUS_SUCCESS); +} + +static NTSTATUS KlInitPeb (HANDLE ProcessHandle, + PRTL_USER_PROCESS_PARAMETERS Ppb, + PVOID* ImageBaseAddress) +{ + NTSTATUS Status; + PVOID PpbBase; + ULONG PpbSize; + ULONG BytesWritten; + ULONG Offset; + PVOID EnvPtr = NULL; + ULONG EnvSize = 0; + + /* create the Environment */ + if (Ppb->Environment != NULL) + { + MEMORY_BASIC_INFORMATION MemInfo; + + Status = ZwQueryVirtualMemory (NtCurrentProcess (), + Ppb->Environment, + MemoryBasicInformation, + &MemInfo, + sizeof(MEMORY_BASIC_INFORMATION), + NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + EnvSize = MemInfo.RegionSize; + } + DPRINT("EnvironmentSize %ld\n", EnvSize); + + /* allocate and initialize new environment block */ + if (EnvSize != 0) + { + Status = ZwAllocateVirtualMemory(ProcessHandle, + &EnvPtr, + 0, + &EnvSize, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + ZwWriteVirtualMemory(ProcessHandle, + EnvPtr, + Ppb->Environment, + EnvSize, + &BytesWritten); + } + DPRINT("EnvironmentPointer %p\n", EnvPtr); + + /* create the PPB */ + PpbBase = NULL; + PpbSize = Ppb->AllocationSize; + + Status = ZwAllocateVirtualMemory(ProcessHandle, + &PpbBase, + 0, + &PpbSize, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + DPRINT("Ppb->MaximumLength %x\n", Ppb->AllocationSize); + + /* write process parameters block*/ + RtlDeNormalizeProcessParams (Ppb); + ZwWriteVirtualMemory(ProcessHandle, + PpbBase, + Ppb, + Ppb->AllocationSize, + + &BytesWritten); + RtlNormalizeProcessParams (Ppb); + + /* write pointer to environment */ + Offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Environment); + ZwWriteVirtualMemory(ProcessHandle, + (PVOID)(PpbBase + Offset), + &EnvPtr, + sizeof(EnvPtr), + &BytesWritten); + + /* write pointer to process parameter block */ + Offset = FIELD_OFFSET(PEB, ProcessParameters); + ZwWriteVirtualMemory(ProcessHandle, + (PVOID)(PEB_BASE + Offset), + &PpbBase, + sizeof(PpbBase), + &BytesWritten); + + /* Read image base address. */ + Offset = FIELD_OFFSET(PEB, ImageBaseAddress); + ZwReadVirtualMemory(ProcessHandle, + (PVOID)(PEB_BASE + Offset), + ImageBaseAddress, + sizeof(PVOID), + &BytesWritten); + + return(STATUS_SUCCESS); +} + + +/* + * @implemented + * + * Creates a process and its initial thread. + * + * NOTES: + * - The first thread is created suspended, so it needs a manual resume!!! + * - If ParentProcess is NULL, current process is used + * - ProcessParameters must be normalized + * - Attributes are object attribute flags used when opening the ImageFileName. + * Valid flags are OBJ_INHERIT and OBJ_CASE_INSENSITIVE. + * + * -Gunnar + */ +NTSTATUS STDCALL +RtlCreateUserProcess( + IN PUNICODE_STRING ImageFileName, + IN ULONG Attributes, + IN OUT PRTL_USER_PROCESS_PARAMETERS ProcessParameters, + IN PSECURITY_DESCRIPTOR ProcessSecurityDescriptor OPTIONAL, + IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL, + IN HANDLE ParentProcess OPTIONAL, + IN BOOLEAN InheritHandles, + IN HANDLE DebugPort OPTIONAL, + IN HANDLE ExceptionPort OPTIONAL, + OUT PRTL_PROCESS_INFO ProcessInfo + ) +{ + HANDLE hSection; + NTSTATUS Status; + PROCESS_BASIC_INFORMATION ProcessBasicInfo; + ULONG retlen; + SECTION_IMAGE_INFORMATION Sii; + ULONG ResultLength; + PVOID ImageBaseAddress; + + DPRINT("RtlCreateUserProcess\n"); + + Status = RtlpMapFile(ImageFileName, + ProcessParameters, + Attributes, + &hSection); + if( !NT_SUCCESS( Status ) ) + return Status; + + /* + * Create a new process + */ + if (ParentProcess == NULL) + ParentProcess = NtCurrentProcess(); + + Status = ZwCreateProcess(&(ProcessInfo->ProcessHandle), + PROCESS_ALL_ACCESS, + NULL, + ParentProcess, + InheritHandles, + hSection, + DebugPort, + ExceptionPort); + if (!NT_SUCCESS(Status)) + { + ZwClose(hSection); + return(Status); + } + + /* + * Get some information about the process + */ + ZwQueryInformationProcess(ProcessInfo->ProcessHandle, + ProcessBasicInformation, + &ProcessBasicInfo, + sizeof(ProcessBasicInfo), + &retlen); + DPRINT("ProcessBasicInfo.UniqueProcessId %d\n", + ProcessBasicInfo.UniqueProcessId); + ProcessInfo->ClientId.UniqueProcess = (HANDLE)ProcessBasicInfo.UniqueProcessId; + + /* + * Create Process Environment Block + */ + DPRINT("Creating peb\n"); + KlInitPeb(ProcessInfo->ProcessHandle, + ProcessParameters, + &ImageBaseAddress); + + Status = ZwQuerySection(hSection, + SectionImageInformation, + &Sii, + sizeof(Sii), + &ResultLength); + if (!NT_SUCCESS(Status) || ResultLength != sizeof(Sii)) + { + DPRINT("Failed to get section image information.\n"); + ZwClose(hSection); + return(Status); + } + + DPRINT("Creating thread for process\n"); + Status = RtlCreateUserThread( + ProcessInfo->ProcessHandle, + NULL, + TRUE, /* CreateSuspended? */ + 0, + &Sii.StackReserve, + &Sii.StackCommit, + ImageBaseAddress + (ULONG)Sii.EntryPoint, + (PVOID)PEB_BASE, + &ProcessInfo->ThreadHandle, + &ProcessInfo->ClientId + ); + + ZwClose(hSection); + + if (!NT_SUCCESS(Status)) + { + DPRINT("Failed to create thread\n"); + return(Status); + } + + return(STATUS_SUCCESS); +} + + + +/* EOF */ diff --git a/reactos/lib/rtl/rtl.xml b/reactos/lib/rtl/rtl.xml index 147aa85f906..9a689761b64 100644 --- a/reactos/lib/rtl/rtl.xml +++ b/reactos/lib/rtl/rtl.xml @@ -1,5 +1,6 @@ + chkstk.s except.s exception.c @@ -21,12 +22,15 @@ mem.c network.c nls.c + ppb.c + process.c random.c registry.c sd.c security.c sid.c splaytree.c + thread.c time.c timezone.c unicode.c diff --git a/reactos/lib/rtl/sid.c b/reactos/lib/rtl/sid.c index 4c87664a8c2..1194d3adcb7 100644 --- a/reactos/lib/rtl/sid.c +++ b/reactos/lib/rtl/sid.c @@ -200,7 +200,7 @@ RtlCopySidAndAttributesArray(ULONG Count, RtlCopySid(SidLength, SidArea, Src[i].Sid); - SidArea = SidArea + SidLength; + SidArea = (PVOID)((ULONG_PTR)SidArea + SidLength); } *RemainingSidArea = SidArea; *RemainingSidAreaSize = Length; diff --git a/reactos/lib/ntdll/rtl/thread.c b/reactos/lib/rtl/thread.c similarity index 99% rename from reactos/lib/ntdll/rtl/thread.c rename to reactos/lib/rtl/thread.c index f92aacaab83..50780f010c8 100644 --- a/reactos/lib/ntdll/rtl/thread.c +++ b/reactos/lib/rtl/thread.c @@ -18,7 +18,6 @@ /* INCLUDES *****************************************************************/ -#define NTOS_MODE_USER #include #define NDEBUG diff --git a/reactos/lib/rtl/unicode.c b/reactos/lib/rtl/unicode.c index 9905fc9e6f4..8236047961a 100644 --- a/reactos/lib/rtl/unicode.c +++ b/reactos/lib/rtl/unicode.c @@ -2256,6 +2256,8 @@ RtlCreateUnicodeString( /* * private + * + * Creates a nullterminated UNICODE_STRING */ BOOLEAN FASTCALL @@ -2671,4 +2673,33 @@ RtlpDuplicateUnicodeString( return STATUS_SUCCESS; } + +/* + * @implemented + */ +NTSTATUS STDCALL +RtlValidateUnicodeString(IN ULONG Flags, + IN PUNICODE_STRING UnicodeString) +{ + /* currently no flags are supported! */ + ASSERT(Flags == 0); + + if ((Flags == 0) && + ((UnicodeString == NULL) || + ((UnicodeString->Length != 0) && + (UnicodeString->Buffer != NULL) && + ((UnicodeString->Length % sizeof(WCHAR)) == 0) && + ((UnicodeString->MaximumLength % sizeof(WCHAR)) == 0) && + (UnicodeString->MaximumLength >= UnicodeString->Length)))) + { + /* a NULL pointer as a unicode string is considered to be a valid unicode + string! */ + return STATUS_SUCCESS; + } + else + { + return STATUS_INVALID_PARAMETER; + } +} + /* EOF */ diff --git a/reactos/lib/rtl/version.c b/reactos/lib/rtl/version.c index 5d954b45de2..49920db6bec 100644 --- a/reactos/lib/rtl/version.c +++ b/reactos/lib/rtl/version.c @@ -1,6 +1,10 @@ /* * ReactOS kernel * Copyright (C) 2004 ReactOS Team + * Copyright 1997 Marcus Meissner + * Copyright 1998 Patrik Stridvall + * Copyright 1998, 2003 Andreas Mohr + * Copyright 1997, 2003 Alexandre Julliard * * 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 @@ -36,25 +40,204 @@ /* GLOBALS ******************************************************************/ +NTSTATUS +STDCALL +RtlGetVersion( + OUT PRTL_OSVERSIONINFOW lpVersionInformation + ); /* FUNCTIONS ****************************************************************/ /* -* @unimplemented +* @implemented */ -/* NTSTATUS STDCALL RtlVerifyVersionInfo( IN PRTL_OSVERSIONINFOEXW VersionInfo, IN ULONG TypeMask, - IN ULONGLONG ConditionMask + IN ULONGLONG ConditionMask ) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + RTL_OSVERSIONINFOEXW ver; + NTSTATUS status; + + /* FIXME: + - Check the following special case on Windows (various versions): + o lp->wSuiteMask == 0 and ver.wSuiteMask != 0 and VER_AND/VER_OR + o lp->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXW) + - MSDN talks about some tests being impossible. Check what really happens. + */ + + ver.dwOSVersionInfoSize = sizeof(ver); + if ((status = RtlGetVersion( (PRTL_OSVERSIONINFOW)&ver )) != STATUS_SUCCESS) return status; + + if(!(TypeMask && ConditionMask)) return STATUS_INVALID_PARAMETER; + + if(TypeMask & VER_PRODUCT_TYPE) + switch(ConditionMask >> 7*3 & 0x07) { + case VER_EQUAL: + if(ver.wProductType != VersionInfo->wProductType) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER: + if(ver.wProductType <= VersionInfo->wProductType) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER_EQUAL: + if(ver.wProductType < VersionInfo->wProductType) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS: + if(ver.wProductType >= VersionInfo->wProductType) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS_EQUAL: + if(ver.wProductType > VersionInfo->wProductType) return STATUS_REVISION_MISMATCH; + break; + default: + return STATUS_INVALID_PARAMETER; + } + if(TypeMask & VER_SUITENAME) + switch(ConditionMask >> 6*3 & 0x07) + { + case VER_AND: + if((VersionInfo->wSuiteMask & ver.wSuiteMask) != VersionInfo->wSuiteMask) + return STATUS_REVISION_MISMATCH; + break; + case VER_OR: + if(!(VersionInfo->wSuiteMask & ver.wSuiteMask) && VersionInfo->wSuiteMask) + return STATUS_REVISION_MISMATCH; + break; + default: + return STATUS_INVALID_PARAMETER; + } + if(TypeMask & VER_PLATFORMID) + switch(ConditionMask >> 3*3 & 0x07) + { + case VER_EQUAL: + if(ver.dwPlatformId != VersionInfo->dwPlatformId) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER: + if(ver.dwPlatformId <= VersionInfo->dwPlatformId) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER_EQUAL: + if(ver.dwPlatformId < VersionInfo->dwPlatformId) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS: + if(ver.dwPlatformId >= VersionInfo->dwPlatformId) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS_EQUAL: + if(ver.dwPlatformId > VersionInfo->dwPlatformId) return STATUS_REVISION_MISMATCH; + break; + default: + return STATUS_INVALID_PARAMETER; + } + if(TypeMask & VER_BUILDNUMBER) + switch(ConditionMask >> 2*3 & 0x07) + { + case VER_EQUAL: + if(ver.dwBuildNumber != VersionInfo->dwBuildNumber) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER: + if(ver.dwBuildNumber <= VersionInfo->dwBuildNumber) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER_EQUAL: + if(ver.dwBuildNumber < VersionInfo->dwBuildNumber) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS: + if(ver.dwBuildNumber >= VersionInfo->dwBuildNumber) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS_EQUAL: + if(ver.dwBuildNumber > VersionInfo->dwBuildNumber) return STATUS_REVISION_MISMATCH; + break; + default: + return STATUS_INVALID_PARAMETER; + } + if(TypeMask & VER_MAJORVERSION) + switch(ConditionMask >> 1*3 & 0x07) + { + case VER_EQUAL: + if(ver.dwMajorVersion != VersionInfo->dwMajorVersion) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER: + if(ver.dwMajorVersion <= VersionInfo->dwMajorVersion) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER_EQUAL: + if(ver.dwMajorVersion < VersionInfo->dwMajorVersion) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS: + if(ver.dwMajorVersion >= VersionInfo->dwMajorVersion) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS_EQUAL: + if(ver.dwMajorVersion > VersionInfo->dwMajorVersion) return STATUS_REVISION_MISMATCH; + break; + default: + return STATUS_INVALID_PARAMETER; + } + if(TypeMask & VER_MINORVERSION) + switch(ConditionMask >> 0*3 & 0x07) + { + case VER_EQUAL: + if(ver.dwMinorVersion != VersionInfo->dwMinorVersion) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER: + if(ver.dwMinorVersion <= VersionInfo->dwMinorVersion) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER_EQUAL: + if(ver.dwMinorVersion < VersionInfo->dwMinorVersion) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS: + if(ver.dwMinorVersion >= VersionInfo->dwMinorVersion) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS_EQUAL: + if(ver.dwMinorVersion > VersionInfo->dwMinorVersion) return STATUS_REVISION_MISMATCH; + break; + default: + return STATUS_INVALID_PARAMETER; + } + if(TypeMask & VER_SERVICEPACKMAJOR) + switch(ConditionMask >> 5*3 & 0x07) + { + case VER_EQUAL: + if(ver.wServicePackMajor != VersionInfo->wServicePackMajor) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER: + if(ver.wServicePackMajor <= VersionInfo->wServicePackMajor) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER_EQUAL: + if(ver.wServicePackMajor < VersionInfo->wServicePackMajor) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS: + if(ver.wServicePackMajor >= VersionInfo->wServicePackMajor) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS_EQUAL: + if(ver.wServicePackMajor > VersionInfo->wServicePackMajor) return STATUS_REVISION_MISMATCH; + break; + default: + return STATUS_INVALID_PARAMETER; + } + if(TypeMask & VER_SERVICEPACKMINOR) + switch(ConditionMask >> 4*3 & 0x07) + { + case VER_EQUAL: + if(ver.wServicePackMinor != VersionInfo->wServicePackMinor) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER: + if(ver.wServicePackMinor <= VersionInfo->wServicePackMinor) return STATUS_REVISION_MISMATCH; + break; + case VER_GREATER_EQUAL: + if(ver.wServicePackMinor < VersionInfo->wServicePackMinor) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS: + if(ver.wServicePackMinor >= VersionInfo->wServicePackMinor) return STATUS_REVISION_MISMATCH; + break; + case VER_LESS_EQUAL: + if(ver.wServicePackMinor > VersionInfo->wServicePackMinor) return STATUS_REVISION_MISMATCH; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; } -*/ + /* Header hell made me do it, don't blame me. Please move these somewhere more diff --git a/reactos/lib/samlib/dllmain.c b/reactos/lib/samlib/dllmain.c index de1b31f30f7..22b194a784c 100644 --- a/reactos/lib/samlib/dllmain.c +++ b/reactos/lib/samlib/dllmain.c @@ -27,8 +27,11 @@ /* INCLUDES *****************************************************************/ -#include #include +#include + +#include +#include #include "debug.h" diff --git a/reactos/lib/samlib/samlib.c b/reactos/lib/samlib/samlib.c index c71ee02e828..1839ada4749 100644 --- a/reactos/lib/samlib/samlib.c +++ b/reactos/lib/samlib/samlib.c @@ -28,6 +28,7 @@ /* INCLUDES *****************************************************************/ #include +#include #include #include diff --git a/reactos/lib/serialui/Makefile b/reactos/lib/serialui/Makefile new file mode 100644 index 00000000000..829ba3edd62 --- /dev/null +++ b/reactos/lib/serialui/Makefile @@ -0,0 +1,36 @@ + +PATH_TO_TOP = ../.. + +TARGET_TYPE = dynlink + +TARGET_NAME = serialui + +TARGET_BASE = $(TARGET_BASE_LIB_SERIALUI) + +TARGET_CFLAGS = \ + -I./include \ + -Wall \ + -Werror \ + -fno-builtin \ + -DUNICODE \ + -DLE \ + -DDBG \ + -D__USE_W32API + +TARGET_LFLAGS = -nostartfiles -nostdlib + +TARGET_SDKLIBS = kernel32.a user32.a shlwapi.a + +TARGET_OBJECTS = serialui.o + +DEP_OBJECTS := $(TARGET_OBJECTS) + +TARGET_CLEAN = $(DEP_FILES) + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + +include $(TOOLS_PATH)/depend.mk + +# EOF diff --git a/reactos/lib/serialui/resource.h b/reactos/lib/serialui/resource.h new file mode 100644 index 00000000000..05402aa02a5 --- /dev/null +++ b/reactos/lib/serialui/resource.h @@ -0,0 +1,32 @@ +#define IDS_EVENPARITY 2001 +#define IDS_MARKPARITY 2002 +#define IDS_NOPARITY 2003 +#define IDS_ODDPARITY 2004 +#define IDS_SPACEPARITY 2005 + +#define IDS_ONESTOPBIT 2006 +#define IDS_ONE5STOPBITS 2007 +#define IDS_TWOSTOPBITS 2008 + +#define IDS_FC_NO 2009 +#define IDS_FC_CTSRTS 2010 +#define IDS_FC_XONXOFF 2011 + +#define IDS_TITLE 2012 + +#define IDD_COMMDLG 1000 +#define IDC_GRP1 1001 +#define IDC_OKBTN 1002 +#define IDC_CANCELBTN 1003 +#define IDC_BTN 1004 +#define IDC_STC1 1005 +#define IDC_STC2 1006 +#define IDC_STC3 1007 +#define IDC_STC4 1008 +#define IDC_STC5 1009 +#define IDC_BAUDRATE 1010 +#define IDC_BYTESIZE 1011 +#define IDC_PARITY 1012 +#define IDC_STOPBITS 1013 +#define IDC_FLOW 1014 +#define IDC_RESTOREBTN 1015 diff --git a/reactos/lib/serialui/serialui.c b/reactos/lib/serialui/serialui.c new file mode 100644 index 00000000000..ba9461bef70 --- /dev/null +++ b/reactos/lib/serialui/serialui.c @@ -0,0 +1,418 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS SerialUI DLL + * FILE: serialui.c + * PUROPSE: A dialog box to cunfigure COM port. + * Functions to set(and get too) default configuration. + * PROGRAMMERS: Saveliy Tretiakov (saveliyt@mail.ru) + * REVISIONS: + * ST (05/04/2005) Created. Implemented drvCommConfigDialog. + */ + +#include + +static HINSTANCE hDllInstance; + +/************************************ + * + * DATA + * + ************************************/ + +const DWORD Bauds[] = { + CBR_110, + CBR_300, + CBR_600, + CBR_1200, + CBR_2400, + CBR_4800, + CBR_9600, + CBR_14400, + CBR_19200, + CBR_38400, + CBR_56000, + CBR_57600, + CBR_115200, + CBR_128000, + CBR_256000, + 0 +}; + +const BYTE ByteSizes[] = { + 5, + 6, + 7, + 8, + 0 +}; + + +const PARITY_INFO Parities[] = { + { EVENPARITY, IDS_EVENPARITY }, + { MARKPARITY, IDS_MARKPARITY }, + { NOPARITY, IDS_NOPARITY }, + { ODDPARITY, IDS_ODDPARITY }, + { SPACEPARITY, IDS_SPACEPARITY }, + { 0, 0 } +}; + +const STOPBIT_INFO StopBits[] = { + { ONESTOPBIT, IDS_ONESTOPBIT }, + { ONE5STOPBITS, IDS_ONE5STOPBITS }, + { TWOSTOPBITS, IDS_TWOSTOPBITS }, + { 0, 0 } +}; + + +/************************************ + * + * DLLMAIN + * + ************************************/ + +BOOL +STDCALL +DllMain(HINSTANCE hInstance, + DWORD dwReason, + LPVOID reserved) +{ + if(dwReason==DLL_PROCESS_ATTACH) + { + hDllInstance = hInstance; + } + else if(dwReason==DLL_THREAD_ATTACH) + { + DisableThreadLibraryCalls(hInstance); + } + + return TRUE; +} + + +/************************************ + * + * EXPORTS + * + ************************************/ + +/* + * @implemented + */ +BOOL WINAPI drvCommConfigDialogW(LPCWSTR lpszDevice, + HWND hWnd, + LPCOMMCONFIG lpCommConfig) +{ + DIALOG_INFO DialogInfo; + + if(!lpszDevice || !lpCommConfig) + { + return FALSE; + } + + DialogInfo.lpszDevice = lpszDevice; + DialogInfo.lpCC = lpCommConfig; + + return DialogBoxParamW(hDllInstance, MAKEINTRESOURCEW(IDD_COMMDLG), + hWnd, (DLGPROC)CommDlgProc, (LPARAM)&DialogInfo); +} + +/* + * @implemented + */ +BOOL WINAPI drvCommConfigDialogA(LPCSTR lpszDevice, + HWND hWnd, + LPCOMMCONFIG lpCommConfig) +{ + BOOL result; + UINT len; + WCHAR *wstr; + + len = MultiByteToWideChar(CP_ACP, 0, lpszDevice, -1, NULL, 0); + if((wstr = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)))) + { + MultiByteToWideChar(CP_ACP, 0, lpszDevice, -1, wstr, len); + result = drvCommConfigDialogW(wstr, hWnd, lpCommConfig); + HeapFree(GetProcessHeap(), 0, wstr); + return result; + } + else + return FALSE; +} + +/* + * @unimplemented + */ +BOOL WINAPI drvSetDefaultCommConfigW(LPCWSTR lpszDevice, + LPCOMMCONFIG lpCommConfig, + DWORD dwSize) +{ + UNIMPLEMENTED +} + +/* + * @unimplemented + */ +BOOL WINAPI drvSetDefaultCommConfigA(LPCSTR lpszDevice, + LPCOMMCONFIG lpCommConfig, + DWORD dwSize) +{ + UNIMPLEMENTED +} + +/* + * @unimplemented + */ +BOOL WINAPI drvGetDefaultCommConfigW(LPCWSTR lpszDevice, + LPCOMMCONFIG lpCommConfig, + LPDWORD lpdwSize) +{ + UNIMPLEMENTED +} + +/* + * @unimplemented + */ +BOOL WINAPI drvGetDefaultCommConfigA(LPCSTR lpszDevice, + LPCOMMCONFIG lpCommConfig, + LPDWORD lpdwSize) +{ + UNIMPLEMENTED +} + + +/************************************ + * + * INTERNALS + * + ************************************/ + +LRESULT CommDlgProc(HWND hDlg, + UINT Msg, + WPARAM wParam, + LPARAM lParam) +{ + LPDIALOG_INFO lpDlgInfo = NULL; + HWND hBox; + + switch (Msg) + { + + case WM_INITDIALOG: + { + WCHAR wstr[255]; + RECT rc, rcDlg, rcOwner; + HWND hOwner; + INT i; + + lpDlgInfo = (LPDIALOG_INFO)lParam; + SetWindowLongPtrW(hDlg, DWL_USER, (LONG_PTR)lpDlgInfo); + + /* Set title */ + if(LoadStringW(hDllInstance, IDS_TITLE, wstr, sizeof(wstr) / sizeof(wstr[0]))) + { + SetWindowTextW(hDlg, wstr); + } + + /* FIXME - this won't work correctly systems with multiple monitors! */ + if(!(hOwner = GetParent(hDlg))) + hOwner = GetDesktopWindow(); + + /* Position dialog in the center of owner window */ + GetWindowRect(hOwner, &rcOwner); + GetWindowRect(hDlg, &rcDlg); + CopyRect(&rc, &rcOwner); + OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); + OffsetRect(&rc, -rc.left, -rc.top); + OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); + SetWindowPos(hDlg, HWND_TOP, + rcOwner.left + (rc.right / 2), + rcOwner.top + (rc.bottom / 2), + 0, 0, SWP_NOSIZE); + + /* Initialize baud rate combo */ + if(!(hBox = GetDlgItem(hDlg, IDC_BAUDRATE))) + EndDialog(hDlg, 0); + + for(i = 0; Bauds[i]; i++) + { + wsprintf(wstr, L"%d", Bauds[i]); + SendMessageW(hBox, CB_INSERTSTRING, (WPARAM)i, (LPARAM)wstr); + if(Bauds[i] == lpDlgInfo->lpCC->dcb.BaudRate) + SendMessageW(hBox, CB_SETCURSEL, (WPARAM)i, 0); + } + + if(SendMessageW(hBox, CB_GETCURSEL, 0, 0) == CB_ERR) + SendMessageW(hBox, CB_SETCURSEL, DEFAULT_BAUD_INDEX, 0); + + /* Initialize byte size combo */ + if(!(hBox = GetDlgItem(hDlg, IDC_BYTESIZE))) + EndDialog(hDlg, 0); + + for(i = 0; ByteSizes[i]; i++) + { + wsprintf(wstr, L"%d", Bauds[i]); + SendMessageW(hBox, CB_INSERTSTRING, (WPARAM)i, (LPARAM)wstr); + if(ByteSizes[i] == lpDlgInfo->lpCC->dcb.ByteSize) + SendMessageW(hBox, CB_SETCURSEL, (WPARAM)i, 0); + } + + if(SendMessageW(hBox, CB_GETCURSEL, 0, 0) == CB_ERR) + SendMessageW(hBox, CB_SETCURSEL, DEFAULT_BYTESIZE_INDEX, 0); + + /* Initialize parity combo */ + if(!(hBox = GetDlgItem(hDlg, IDC_PARITY))) + EndDialog(hDlg, 0); + + for(i = 0; Parities[i].StrId; i++) + { + if(LoadStringW(hDllInstance, Parities[i].StrId, wstr, sizeof(wstr) / sizeof(wstr[0]))) + { + SendMessageW(hBox, CB_INSERTSTRING, (WPARAM)i, (LPARAM)wstr); + if(Parities[i].Parity == lpDlgInfo->lpCC->dcb.Parity) + SendMessageW(hBox, CB_SETCURSEL, (WPARAM)i, 0); + } + } + + if(SendMessageW(hBox, CB_GETCURSEL, 0, 0)==CB_ERR) + SendMessageW(hBox, CB_SETCURSEL, DEFAULT_PARITY_INDEX, 0); + + /* Initialize stop bits combo */ + if(!(hBox = GetDlgItem(hDlg, IDC_STOPBITS))) + EndDialog(hDlg, 0); + + for(i = 0; StopBits[i].StrId; i++) + { + if(LoadStringW(hDllInstance, StopBits[i].StrId, wstr, sizeof(wstr) / sizeof(wstr[0]))) + { + SendMessageW(hBox, CB_INSERTSTRING, (WPARAM)i, (LPARAM)wstr); + if(StopBits[i].StopBit == lpDlgInfo->lpCC->dcb.StopBits) + SendMessageW(hBox, CB_SETCURSEL, (WPARAM)i, 0); + } + } + + if(SendMessageW(hBox, CB_GETCURSEL, 0, 0)==CB_ERR) + SendMessageW(hBox, CB_SETCURSEL, DEFAULT_STOPBITS_INDEX, 0); + + /* Initialize flow control combo */ + if(!(hBox = GetDlgItem(hDlg, IDC_FLOW))) + EndDialog(hDlg, 0); + + if(LoadStringW(hDllInstance, IDS_FC_NO, wstr, sizeof(wstr) / sizeof(wstr[0]))) + { + SendMessageW(hBox, CB_INSERTSTRING, 0, (LPARAM)wstr); + SendMessageW(hBox, CB_SETCURSEL, 0, 0); + lpDlgInfo->InitialFlowIndex = 0; + } + + + if(LoadStringW(hDllInstance, IDS_FC_CTSRTS, wstr, sizeof(wstr) / sizeof(wstr[0]))) + { + SendMessageW(hBox, CB_INSERTSTRING, 1, (LPARAM)wstr); + if(lpDlgInfo->lpCC->dcb.fRtsControl == RTS_CONTROL_HANDSHAKE + || lpDlgInfo->lpCC->dcb.fOutxCtsFlow == TRUE) + { + SendMessageW(hBox, CB_SETCURSEL, 1, 0); + lpDlgInfo->InitialFlowIndex = 1; + } + } + + if(LoadStringW(hDllInstance, IDS_FC_XONXOFF, wstr, sizeof(wstr) / sizeof(wstr[0]))) + { + SendMessageW(hBox, CB_INSERTSTRING, 2, (LPARAM)wstr); + if(lpDlgInfo->lpCC->dcb.fOutX || lpDlgInfo->lpCC->dcb.fInX) + { + SendMessageW(hBox, CB_SETCURSEL, 2, 0); + lpDlgInfo->InitialFlowIndex = 2; + } + } + + /* Set focus */ + SetFocus(GetDlgItem(hDlg, IDC_OKBTN)); + + return FALSE; + } /* WM_INITDIALOG */ + + case WM_COMMAND: + { + switch(wParam) + { + case IDC_CANCELBTN: + EndDialog(hDlg, FALSE); + break; + case IDC_OKBTN: + OkButton(hDlg); + EndDialog(hDlg, TRUE); + break; + } + return TRUE; + } /* WM_COMMAND */ + + case WM_CLOSE: + { + EndDialog(hDlg, FALSE); + return TRUE; + } /* WM_CLOSE */ + + default: + return FALSE; + } + +} + + +VOID OkButton(HWND hDlg) +{ + LPDIALOG_INFO lpDlgInfo; + UINT Index; + + lpDlgInfo = (LPDIALOG_INFO) GetWindowLongPtrW(hDlg, DWL_USER); + + /* Baud rate */ + Index = SendMessageW(GetDlgItem(hDlg, IDC_BAUDRATE), CB_GETCURSEL, 0, 0); + lpDlgInfo->lpCC->dcb.BaudRate = Bauds[Index]; + + /* Byte size */ + Index = SendMessageW(GetDlgItem(hDlg, IDC_BYTESIZE), CB_GETCURSEL, 0, 0); + lpDlgInfo->lpCC->dcb.ByteSize = ByteSizes[Index]; + + /* Parity */ + Index = SendMessageW(GetDlgItem(hDlg, IDC_PARITY), CB_GETCURSEL, 0, 0); + lpDlgInfo->lpCC->dcb.Parity = Parities[Index].Parity; + + /* Stop bits */ + Index = SendMessageW(GetDlgItem(hDlg, IDC_STOPBITS), CB_GETCURSEL, 0, 0); + lpDlgInfo->lpCC->dcb.StopBits = StopBits[Index].StopBit; + + /* Flow Control */ + Index = SendMessageW(GetDlgItem(hDlg, IDC_FLOW), CB_GETCURSEL, 0, 0); + if(lpDlgInfo->InitialFlowIndex != Index) + { + switch(Index) + { + case 0: /* NO */ + lpDlgInfo->lpCC->dcb.fDtrControl = DTR_CONTROL_DISABLE; + lpDlgInfo->lpCC->dcb.fRtsControl = RTS_CONTROL_DISABLE; + lpDlgInfo->lpCC->dcb.fOutxCtsFlow = FALSE; + lpDlgInfo->lpCC->dcb.fOutxDsrFlow = FALSE; + lpDlgInfo->lpCC->dcb.fOutX = FALSE; + lpDlgInfo->lpCC->dcb.fInX = FALSE; + break; + case 1: /* CTS/RTS */ + lpDlgInfo->lpCC->dcb.fDtrControl = DTR_CONTROL_DISABLE; + lpDlgInfo->lpCC->dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; + lpDlgInfo->lpCC->dcb.fOutxCtsFlow = TRUE; + lpDlgInfo->lpCC->dcb.fOutxDsrFlow = FALSE; + lpDlgInfo->lpCC->dcb.fOutX = FALSE; + lpDlgInfo->lpCC->dcb.fInX = FALSE; + break; + case 2: /* XON/XOFF */ + lpDlgInfo->lpCC->dcb.fDtrControl = DTR_CONTROL_DISABLE; + lpDlgInfo->lpCC->dcb.fRtsControl = RTS_CONTROL_DISABLE; + lpDlgInfo->lpCC->dcb.fOutxCtsFlow = FALSE; + lpDlgInfo->lpCC->dcb.fOutxDsrFlow = FALSE; + lpDlgInfo->lpCC->dcb.fOutX = TRUE; + lpDlgInfo->lpCC->dcb.fInX = TRUE; + break; + } + } +} diff --git a/reactos/lib/serialui/serialui.def b/reactos/lib/serialui/serialui.def new file mode 100644 index 00000000000..2b38aba9813 --- /dev/null +++ b/reactos/lib/serialui/serialui.def @@ -0,0 +1,10 @@ +LIBRARY serialui.dll + +EXPORTS +drvCommConfigDialogW@12 +drvCommConfigDialogA@12 +drvSetDefaultCommConfigW@12 +drvSetDefaultCommConfigA@12 +drvGetDefaultCommConfigW@12 +drvGetDefaultCommConfigA@12 + diff --git a/reactos/lib/serialui/serialui.h b/reactos/lib/serialui/serialui.h new file mode 100644 index 00000000000..e998a2117d7 --- /dev/null +++ b/reactos/lib/serialui/serialui.h @@ -0,0 +1,86 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS SerialUI DLL + * FILE: serialui.h + * PURPOSE: header file + * PROGRAMMERS: Saveliy Tretiakov (saveliyt@mail.ru) + */ + +#include +#include +#include "resource.h" + +#define UNIMPLEMENTED \ + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); \ + return FALSE; + +#define DEFAULT_BAUD_INDEX 6 +#define DEFAULT_BYTESIZE_INDEX 3 +#define DEFAULT_PARITY_INDEX 2 +#define DEFAULT_STOPBITS_INDEX 0 + +typedef struct _DIALOG_INFO +{ + LPCWSTR lpszDevice; + UINT InitialFlowIndex; + LPCOMMCONFIG lpCC; +} DIALOG_INFO, *LPDIALOG_INFO; + +typedef struct _PARITY_INFO +{ + BYTE Parity; + UINT StrId; +} PARITY_INFO, *PPARITY_INFO; + +typedef struct _STOPBIT_INFO +{ + BYTE StopBit; + UINT StrId; +} STOPBIT_INFO, *PSTOPBIT_INFO; + + +/************************************ + * + * EXPORTS + * + ************************************/ + +BOOL WINAPI drvCommConfigDialogW(LPCWSTR lpszDevice, + HWND hWnd, + LPCOMMCONFIG lpCommConfig); + +BOOL WINAPI drvCommConfigDialogA(LPCSTR lpszDevice, + HWND hWnd, + LPCOMMCONFIG lpCommConfig); + +BOOL WINAPI drvSetDefaultCommConfigW(LPCWSTR lpszDevice, + LPCOMMCONFIG lpCommConfig, + DWORD dwSize); + +BOOL WINAPI drvSetDefaultCommConfigA(LPCSTR lpszDevice, + LPCOMMCONFIG lpCommConfig, + DWORD dwSize); + +BOOL WINAPI drvGetDefaultCommConfigW(LPCWSTR lpszDevice, + LPCOMMCONFIG lpCommConfig, + LPDWORD lpdwSize); + +BOOL WINAPI drvGetDefaultCommConfigA(LPCSTR lpszDevice, + LPCOMMCONFIG lpCommConfig, + LPDWORD lpdwSize); + + +/************************************ + * + * INTERNALS + * + ************************************/ + +LRESULT CommDlgProc(HWND hDlg, + UINT Msg, + WPARAM wParam, + LPARAM lParam); + +VOID OkButton(HWND hDlg); + + diff --git a/reactos/lib/serialui/serialui.rc b/reactos/lib/serialui/serialui.rc new file mode 100644 index 00000000000..75adeb61db1 --- /dev/null +++ b/reactos/lib/serialui/serialui.rc @@ -0,0 +1,48 @@ +#include "resource.h" + +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS SerialUI DLL\0" +#define REACTOS_STR_INTERNAL_NAME "serialui\0" +#define REACTOS_STR_ORIGINAL_FILENAME "serialui.dll\0" +#include + + +STRINGTABLE LOADONCALL DISCARDABLE +BEGIN + IDS_EVENPARITY, "Even" + IDS_MARKPARITY, "Mark" + IDS_NOPARITY, "No parity" + IDS_ODDPARITY, "Odd" + IDS_SPACEPARITY, "Space" + + IDS_ONESTOPBIT, "1 stop bit" + IDS_ONE5STOPBITS, "1.5 stop bits" + IDS_TWOSTOPBITS, "2 stop bits" + + IDS_FC_NO, "No" + IDS_FC_CTSRTS, "CTS/RTS" + IDS_FC_XONXOFF, "XON/XOFF" + + IDS_TITLE, "Properties for %s" +END + +IDD_COMMDLG DIALOGEX 6,5,222,175 +FONT 8,"MS Sans Serif" +STYLE 0x10C80080 +EXSTYLE 0x00000001 +BEGIN + CONTROL "",IDC_GRP1,"Button",0x50000007,6,5,210,146,0x00000000 + CONTROL "OK",IDC_OKBTN,"Button",0x50010000,98,156,56,13,0x00000000 + CONTROL "Cancel",IDC_CANCELBTN,"Button",0x50010000,158,156,56,13,0x00000000 + CONTROL "Baud rate:",IDC_STC1,"Static",0x50000000,24,31,42,9,0x00000000 + CONTROL "Byte size:",IDC_STC2,"Static",0x50000000,24,53,42,9,0x00000000 + CONTROL "Parity:",IDC_STC3,"Static",0x50000000,24,73,42,9,0x00000000 + CONTROL "Stop bits:",IDC_STC4,"Static",0x50000000,24,96,42,9,0x00000000 + CONTROL "Flow control:",IDC_STC5,"Static",0x50000000,24,120,42,9,0x00000000 + CONTROL "",IDC_BAUDRATE,"ComboBox",0x50210003,98,29,100,50,0x00000000 + CONTROL "",IDC_BYTESIZE,"ComboBox",0x50210003,98,49,100,50,0x00000000 + CONTROL "",IDC_PARITY,"ComboBox",0x50210003,98,72,100,50,0x00000000 + CONTROL "",IDC_STOPBITS,"ComboBox",0x50210003,98,94,100,50,0x00000000 + CONTROL "",IDC_FLOW,"ComboBox",0x50210003,98,116,100,50,0x00000000 +END + diff --git a/reactos/lib/setupapi/Cs.rc b/reactos/lib/setupapi/Cs.rc index 330eeddd797..54d5ab0fa29 100644 --- a/reactos/lib/setupapi/Cs.rc +++ b/reactos/lib/setupapi/Cs.rc @@ -1,35 +1,35 @@ -/* - * Czech resources for SETUPAPI - * - * Copyright 2001 Andreas Mohr - * Copyright 2004 David Kredba - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_CZECH, SUBLANG_DEFAULT - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Kopíruji soubory..." -FONT 8, "MS Shell Dlg" -BEGIN - PUSHBUTTON "Storno", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "Zdroj:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "Cíl:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * Czech resources for SETUPAPI + * + * Copyright 2001 Andreas Mohr + * Copyright 2004 David Kredba + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_CZECH, SUBLANG_DEFAULT + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Kopíruji soubory..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Storno", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Zdroj:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Cíl:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/Da.rc b/reactos/lib/setupapi/Da.rc new file mode 100644 index 00000000000..5b4c24de20d --- /dev/null +++ b/reactos/lib/setupapi/Da.rc @@ -0,0 +1,15 @@ +LANGUAGE LANG_DANISH, SUBLANG_DEFAULT + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Overførere Filer..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Fortryd", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Kilde:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Destination:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | +WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/De.rc b/reactos/lib/setupapi/De.rc index 0fd9555a97f..08507396a41 100644 --- a/reactos/lib/setupapi/De.rc +++ b/reactos/lib/setupapi/De.rc @@ -1,34 +1,34 @@ -/* - * German resources for SETUPAPI - * - * Copyright 2004 Henning Gerhardt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Dateien kopieren..." -FONT 8, "MS Shell Dlg" -BEGIN - PUSHBUTTON "Abbrechen", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "Quelle:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "Ziel:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * German resources for SETUPAPI + * + * Copyright 2004 Henning Gerhardt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Dateien kopieren..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Abbrechen", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Quelle:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Ziel:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/En.rc b/reactos/lib/setupapi/En.rc index aa504ca13d8..bd142fa9b30 100644 --- a/reactos/lib/setupapi/En.rc +++ b/reactos/lib/setupapi/En.rc @@ -1,34 +1,34 @@ -/* - * English resources for SETUPAPI - * - * Copyright 2001 Andreas Mohr - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Copying Files..." -FONT 8, "MS Shell Dlg" -BEGIN - PUSHBUTTON "Cancel", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "Source:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "Destination:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * English resources for SETUPAPI + * + * Copyright 2001 Andreas Mohr + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Copying Files..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Cancel", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Source:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Destination:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/Es.rc b/reactos/lib/setupapi/Es.rc index f09e276ddd6..6ccd146b4c3 100644 --- a/reactos/lib/setupapi/Es.rc +++ b/reactos/lib/setupapi/Es.rc @@ -1,34 +1,34 @@ -/* - * Spanish resources for SETUPAPI - * - * Copyright 2003 José Manuel Ferrer Ortiz - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_SPANISH, SUBLANG_DEFAULT - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Copiando archivos..." -FONT 8, "MS Shell Dlg" -BEGIN - PUSHBUTTON "Cancelar", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "Origen:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "Destino:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * Spanish resources for SETUPAPI + * + * Copyright 2003 José Manuel Ferrer Ortiz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Copiando archivos..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Cancelar", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Origen:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Destino:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/Fr.rc b/reactos/lib/setupapi/Fr.rc index 40f01716a23..43d4fb3d99a 100644 --- a/reactos/lib/setupapi/Fr.rc +++ b/reactos/lib/setupapi/Fr.rc @@ -1,35 +1,35 @@ -/* - * French resources for SETUPAPI - * - * Copyright 2001 Andreas Mohr - * Copyright 2003 Vincent Béron - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Copie de fichiers..." -FONT 8, "MS Shell Dlg" -BEGIN - PUSHBUTTON "Annuler", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "Source:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "Destination:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * French resources for SETUPAPI + * + * Copyright 2001 Andreas Mohr + * Copyright 2003 Vincent Béron + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Copie de fichiers..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Annuler", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Source:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Destination:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/It.rc b/reactos/lib/setupapi/It.rc index dc7b679e315..7a47c7e4347 100644 --- a/reactos/lib/setupapi/It.rc +++ b/reactos/lib/setupapi/It.rc @@ -1,35 +1,35 @@ -/* - * Italian resources for SETUPAPI - * - * Copyright 2001 Andreas Mohr - * Copyright 2003 Ivan Leo Puoti - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_ITALIAN, SUBLANG_DEFAULT - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Copia dei file in corso..." -FONT 8, "MS Shell Dlg" -BEGIN - PUSHBUTTON "Anulla", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "Origine:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "Destinazione:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * Italian resources for SETUPAPI + * + * Copyright 2001 Andreas Mohr + * Copyright 2003 Ivan Leo Puoti + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_ITALIAN, SUBLANG_DEFAULT + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Copia dei file in corso..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Anulla", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Origine:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Destinazione:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/Ja.rc b/reactos/lib/setupapi/Ja.rc index 03513668ace..96cb39c9cc7 100644 --- a/reactos/lib/setupapi/Ja.rc +++ b/reactos/lib/setupapi/Ja.rc @@ -1,34 +1,34 @@ -/* - * Japanese resources for SETUPAPI - * - * Copyright 2004 Hajime Segawa - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "ƒtƒ@ƒCƒ‹‚ðƒRƒs[‚µ‚Ä‚¢‚Ü‚·..." -FONT 9, "MS UI Gothic" -BEGIN - PUSHBUTTON "ƒLƒƒƒ“ƒZƒ‹", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "ƒRƒs[Œ³:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "ƒRƒs[æ:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * Japanese resources for SETUPAPI + * + * Copyright 2004 Hajime Segawa + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "ƒtƒ@ƒCƒ‹‚ðƒRƒs[‚µ‚Ä‚¢‚Ü‚·..." +FONT 9, "MS UI Gothic" +BEGIN + PUSHBUTTON "ƒLƒƒƒ“ƒZƒ‹", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "ƒRƒs[Œ³:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "ƒRƒs[æ:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/Makefile.in b/reactos/lib/setupapi/Makefile.in index e502e2983ff..6321589d71f 100644 --- a/reactos/lib/setupapi/Makefile.in +++ b/reactos/lib/setupapi/Makefile.in @@ -1,35 +1,37 @@ -EXTRADEFS = -D_SETUPAPI_ -TOPSRCDIR = @top_srcdir@ -TOPOBJDIR = ../.. -SRCDIR = @srcdir@ -VPATH = @srcdir@ -MODULE = setupapi.dll -IMPORTS = user32 version advapi32 rpcrt4 kernel32 ntdll -DELAYIMPORTS = shell32 -EXTRALIBS = $(LIBUNICODE) - -C_SRCS = \ - cfgmgr.c \ - devinst.c \ - dirid.c \ - diskspace.c \ - install.c \ - misc.c \ - parser.c \ - queue.c \ - setupcab.c \ - stubs.c - -C_SRCS16 = \ - devinst16.c \ - infparse.c \ - setupx_main.c \ - virtcopy.c - -SPEC_SRCS16 = setupx.spec - -RC_SRCS= setupapi.rc - -@MAKE_DLL_RULES@ - -### Dependencies: +EXTRADEFS = -D_SETUPAPI_ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = setupapi.dll +IMPORTS = user32 version advapi32 rpcrt4 kernel32 ntdll +DELAYIMPORTS = shell32 +EXTRALIBS = $(LIBUNICODE) + +C_SRCS = \ + pnp_c.c \ + cfgmgr.c \ + devinst.c \ + dirid.c \ + diskspace.c \ + install.c \ + misc.c \ + parser.c \ + queue.c \ + setupcab.c \ + stubs.c \ + rpc.c + +C_SRCS16 = \ + devinst16.c \ + infparse.c \ + setupx_main.c \ + virtcopy.c + +SPEC_SRCS16 = setupx.spec + +RC_SRCS= setupapi.rc + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/lib/setupapi/Nl.rc b/reactos/lib/setupapi/Nl.rc index 76dc7ed1da4..d7c6f13bb49 100644 --- a/reactos/lib/setupapi/Nl.rc +++ b/reactos/lib/setupapi/Nl.rc @@ -1,34 +1,34 @@ -/* - * setupapi (Dutch resources) - * - * Copyright 2003 Hans Leidekker - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_DUTCH, SUBLANG_DEFAULT - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Bestanden worden gekopiëerd..." -FONT 8, "MS Shell Dlg" -BEGIN - PUSHBUTTON "Annuleren", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "Bron:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "Bestemming:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * setupapi (Dutch resources) + * + * Copyright 2003 Hans Leidekker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_DUTCH, SUBLANG_DEFAULT + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Bestanden worden gekopiëerd..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Annuleren", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Bron:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Bestemming:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/Pt.rc b/reactos/lib/setupapi/Pt.rc index 69bede2e379..dacb2b4228c 100644 --- a/reactos/lib/setupapi/Pt.rc +++ b/reactos/lib/setupapi/Pt.rc @@ -1,34 +1,50 @@ -/* - * Portuguese resources for SETUPAPI - * - * Copyright 2003 Marcelo Duarte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_PORTUGUESE, SUBLANG_DEFAULT - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Copiando arquivos..." -FONT 8, "MS Shell Dlg" -BEGIN - PUSHBUTTON "Cancelar", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "Origem:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "Destino:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * Portuguese resources for SETUPAPI + * + * Copyright 2003 Marcelo Duarte + * Copyright 2004 Américo José Melo + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Copiando arquivos..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Cancelar", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Origem:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Destino:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + +LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "A copiar ficheiros..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Cancelar", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Origem:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Destino:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/Ru.rc b/reactos/lib/setupapi/Ru.rc index 3efa2424097..09ee4f637c1 100644 --- a/reactos/lib/setupapi/Ru.rc +++ b/reactos/lib/setupapi/Ru.rc @@ -1,34 +1,34 @@ -/* - * SETUPAPI (Russian resources) - * - * Copyright 2003 Igor Stepin - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT - -COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 -STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Êîïèðîâàíèå ôàéëîâ..." -FONT 8, "MS Shell Dlg" -BEGIN - PUSHBUTTON "Îòìåíà", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP - LTEXT "Îòêóäà:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "Êóäà:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP - LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP - CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END +/* + * SETUPAPI (Russian resources) + * + * Copyright 2003 Igor Stepin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105 +STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Êîïèðîâàíèå ôàéëîâ..." +FONT 8, "MS Shell Dlg" +BEGIN + PUSHBUTTON "Îòìåíà", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + LTEXT "Îòêóäà:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Êóäà:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP + CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END diff --git a/reactos/lib/setupapi/cfgmgr.c b/reactos/lib/setupapi/cfgmgr.c index 91021b5d736..517a7b9e96f 100644 --- a/reactos/lib/setupapi/cfgmgr.c +++ b/reactos/lib/setupapi/cfgmgr.c @@ -29,11 +29,21 @@ #include "winreg.h" #include "setupapi.h" #include "cfgmgr32.h" +#include "setupapi_private.h" + +#include "rpc.h" + +#include "pnp_c.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(setupapi); +typedef struct _MACHINE_INFO +{ + RPC_BINDING_HANDLE BindingHandle; +} MACHINE_INFO, *PMACHINE_INFO; + /*********************************************************************** * CM_Connect_MachineA [SETUPAPI.@] @@ -46,14 +56,10 @@ CONFIGRET WINAPI CM_Connect_MachineA(PCSTR UNCServerName, PHMACHINE phMachine) TRACE("%s %p\n", UNCServerName, phMachine); if (UNCServerName == NULL || *UNCServerName == 0) - { return CM_Connect_MachineW(NULL, phMachine); - } if (CaptureAndConvertAnsiArg(UNCServerName, &pServerNameW)) - { return CR_INVALID_DATA; - } ret = CM_Connect_MachineW(pServerNameW, phMachine); @@ -68,8 +74,23 @@ CONFIGRET WINAPI CM_Connect_MachineA(PCSTR UNCServerName, PHMACHINE phMachine) */ CONFIGRET WINAPI CM_Connect_MachineW(PCWSTR UNCServerName, PHMACHINE phMachine) { - FIXME("%s %p\n", debugstr_w(UNCServerName), phMachine); - return CR_ACCESS_DENIED; + PMACHINE_INFO pMachine; + + TRACE("%s %p\n", debugstr_w(UNCServerName), phMachine); + + pMachine = (PMACHINE_INFO)GlobalAlloc(GPTR, sizeof(MACHINE_INFO)); + if (pMachine == NULL) + return CR_OUT_OF_MEMORY; + + if (!PnpBindRpc(UNCServerName, &pMachine->BindingHandle)) + { + GlobalFree(pMachine); + return CR_INVALID_MACHINENAME; + } + + phMachine = (PHMACHINE)pMachine; + + return CR_SUCCESS; } @@ -78,7 +99,41 @@ CONFIGRET WINAPI CM_Connect_MachineW(PCWSTR UNCServerName, PHMACHINE phMachine) */ CONFIGRET WINAPI CM_Disconnect_Machine(HMACHINE hMachine) { - FIXME("%lx\n", hMachine); + PMACHINE_INFO pMachine; + + TRACE("%lx\n", hMachine); + + if (hMachine == NULL) + return CR_SUCCESS; + + pMachine = (PMACHINE_INFO)hMachine; + if (!PnpUnbindRpc(pMachine->BindingHandle)) + return CR_ACCESS_DENIED; + + GlobalFree(pMachine); + + return CR_SUCCESS; +} + + +/*********************************************************************** + * CM_Get_Child [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Get_Child( + PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags) +{ + TRACE("%p %p %lx\n", pdnDevInst, dnDevInst, ulFlags); + return CM_Get_Sibling_Ex(pdnDevInst, dnDevInst, ulFlags, NULL); +} + + +/*********************************************************************** + * CM_Get_Child_Ex [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Get_Child_Ex( + PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags, HMACHINE hMachine) +{ + TRACE("%p %lx %lx %lx\n", pdnDevInst, dnDevInst, ulFlags, hMachine); return CR_SUCCESS; } @@ -153,9 +208,8 @@ CONFIGRET WINAPI CM_Get_Device_ID_List_SizeA( CONFIGRET WINAPI CM_Get_Device_ID_List_SizeW( PULONG pulLen, PCWSTR pszFilter, ULONG ulFlags) { - FIXME("%p %s %ld\n", pulLen, debugstr_w(pszFilter), ulFlags); - *pulLen = 2; - return CR_SUCCESS; + TRACE("%p %s %ld\n", pulLen, debugstr_w(pszFilter), ulFlags); + return CM_Get_Device_ID_List_Size_ExW(pulLen, pszFilter, ulFlags, NULL); } @@ -181,3 +235,128 @@ CONFIGRET WINAPI CM_Get_Device_ID_List_Size_ExW( *pulLen = 2; return CR_SUCCESS; } + + +/*********************************************************************** + * CM_Get_Parent [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Get_Parent( + PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags) +{ + TRACE("%p %p %lx\n", pdnDevInst, dnDevInst, ulFlags); + return CM_Get_Sibling_Ex(pdnDevInst, dnDevInst, ulFlags, NULL); +} + + +/*********************************************************************** + * CM_Get_Parent_Ex [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Get_Parent_Ex( + PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags, HMACHINE hMachine) +{ + TRACE("%p %lx %lx %lx\n", pdnDevInst, dnDevInst, ulFlags, hMachine); + return CR_SUCCESS; +} + + +/*********************************************************************** + * CM_Get_Sibling [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Get_Sibling( + PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags) +{ + TRACE("%p %p %lx\n", pdnDevInst, dnDevInst, ulFlags); + return CM_Get_Sibling_Ex(pdnDevInst, dnDevInst, ulFlags, NULL); +} + + +/*********************************************************************** + * CM_Get_Sibling_Ex [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Get_Sibling_Ex( + PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags, HMACHINE hMachine) +{ + TRACE("%p %lx %lx %lx\n", pdnDevInst, dnDevInst, ulFlags, hMachine); + return CR_SUCCESS; +} + + +/*********************************************************************** + * CM_Get_Version [SETUPAPI.@] + */ +WORD WINAPI CM_Get_Version(VOID) +{ + TRACE("\n"); + return CM_Get_Version_Ex(NULL); +} + + +/*********************************************************************** + * CM_Get_Version_Ex [SETUPAPI.@] + */ +WORD WINAPI CM_Get_Version_Ex(HMACHINE hMachine) +{ + RPC_BINDING_HANDLE BindingHandle = NULL; + RPC_STATUS Status; + + FIXME("%lx\n", hMachine); + + if (hMachine != NULL) + { + BindingHandle = ((PMACHINE_INFO)hMachine)->BindingHandle; + if (BindingHandle == NULL) + return 0; + } + else + { + Status = PnpGetLocalBindingHandle(&BindingHandle); + if (Status != RPC_S_OK) + return 0; + } + + return PNP_GetVersion(BindingHandle); +} + + +/*********************************************************************** + * CM_Locate_DevNodeA [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Locate_DevNodeA( + PDEVINST pdnDevInst, DEVINSTID_A pDeviceID, ULONG ulFlags) +{ + FIXME("%p %p %lu\n", pdnDevInst, pDeviceID, ulFlags); + return CR_SUCCESS; +} + + +/*********************************************************************** + * CM_Locate_DevNodeW [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Locate_DevNodeW( + PDEVINST pdnDevInst, DEVINSTID_W pDeviceID, ULONG ulFlags) +{ + TRACE("%p %p %lu\n", pdnDevInst, pDeviceID, ulFlags); + return CM_Locate_DevNode_ExW(pdnDevInst, pDeviceID, ulFlags, NULL); +} + + +/*********************************************************************** + * CM_Locate_DevNode_ExA [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Locate_DevNode_ExA( + PDEVINST pdnDevInst, DEVINSTID_A pDeviceID, ULONG ulFlags, HMACHINE hMachine) +{ + FIXME("%p %p %lu %lx\n", pdnDevInst, pDeviceID, ulFlags, hMachine); + return CR_SUCCESS; +} + + +/*********************************************************************** + * CM_Locate_DevNode_ExW [SETUPAPI.@] + */ +CONFIGRET WINAPI CM_Locate_DevNode_ExW( + PDEVINST pdnDevInst, DEVINSTID_W pDeviceID, ULONG ulFlags, HMACHINE hMachine) +{ + FIXME("%p %p %lu %lx\n", pdnDevInst, pDeviceID, ulFlags, hMachine); + return CR_SUCCESS; +} diff --git a/reactos/lib/setupapi/devinst.c b/reactos/lib/setupapi/devinst.c index 645dbb92574..30f8978a75f 100644 --- a/reactos/lib/setupapi/devinst.c +++ b/reactos/lib/setupapi/devinst.c @@ -1,1282 +1,1521 @@ -/* - * SetupAPI device installer - * - * Copyright 2000 Andreas Mohr for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" -#include "wine/port.h" - -#include - -#include "windef.h" -#include "winbase.h" -#include "winnt.h" -#include "winreg.h" -#include "winternl.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "setupapi.h" -#include "wine/debug.h" -#include "wine/unicode.h" - -#include "rpc.h" -#include "rpcdce.h" - -#include "setupapi_private.h" - - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -/* Unicode constants */ -static const WCHAR ClassGUID[] = {'C','l','a','s','s','G','U','I','D',0}; -static const WCHAR Class[] = {'C','l','a','s','s',0}; -static const WCHAR ClassInstall32[] = {'C','l','a','s','s','I','n','s','t','a','l','l','3','2',0}; -static const WCHAR NoDisplayClass[] = {'N','o','D','i','s','p','l','a','y','C','l','a','s','s',0}; -static const WCHAR NoInstallClass[] = {'N','o','I','s','t','a','l','l','C','l','a','s','s',0}; -static const WCHAR NoUseClass[] = {'N','o','U','s','e','C','l','a','s','s',0}; -static const WCHAR NtExtension[] = {'.','N','T',0}; -static const WCHAR NtPlatformExtension[] = {'.','N','T','x','8','6',0}; -static const WCHAR Version[] = {'V','e','r','s','i','o','n',0}; -static const WCHAR WinExtension[] = {'.','W','i','n',0}; - -/* Registry key and value names */ -static const WCHAR ControlClass[] = {'S','y','s','t','e','m','\\', - 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', - 'C','o','n','t','r','o','l','\\', - 'C','l','a','s','s',0}; - -static const WCHAR DeviceClasses[] = {'S','y','s','t','e','m','\\', - 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', - 'C','o','n','t','r','o','l','\\', - 'D','e','v','i','c','e','C','l','a','s','s','e','s',0}; - -/*********************************************************************** - * SetupDiBuildClassInfoList (SETUPAPI.@) - */ -BOOL WINAPI SetupDiBuildClassInfoList( - DWORD Flags, - LPGUID ClassGuidList, - DWORD ClassGuidListSize, - PDWORD RequiredSize) -{ - TRACE("\n"); - return SetupDiBuildClassInfoListExW(Flags, ClassGuidList, - ClassGuidListSize, RequiredSize, - NULL, NULL); -} - -/*********************************************************************** - * SetupDiBuildClassInfoListExA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiBuildClassInfoListExA( - DWORD Flags, - LPGUID ClassGuidList, - DWORD ClassGuidListSize, - PDWORD RequiredSize, - LPCSTR MachineName, - PVOID Reserved) -{ - LPWSTR MachineNameW = NULL; - BOOL bResult; - - TRACE("\n"); - - if (MachineName) - { - MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); - if (MachineNameW == NULL) - return FALSE; - } - - bResult = SetupDiBuildClassInfoListExW(Flags, ClassGuidList, - ClassGuidListSize, RequiredSize, - MachineNameW, Reserved); - - if (MachineNameW) - MyFree(MachineNameW); - - return bResult; -} - -/*********************************************************************** - * SetupDiBuildClassInfoListExW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiBuildClassInfoListExW( - DWORD Flags, - LPGUID ClassGuidList, - DWORD ClassGuidListSize, - PDWORD RequiredSize, - LPCWSTR MachineName, - PVOID Reserved) -{ - WCHAR szKeyName[40]; - HKEY hClassesKey; - HKEY hClassKey; - DWORD dwLength; - DWORD dwIndex; - LONG lError; - DWORD dwGuidListIndex = 0; - - TRACE("\n"); - - if (RequiredSize != NULL) - *RequiredSize = 0; - - hClassesKey = SetupDiOpenClassRegKeyExW(NULL, - KEY_ALL_ACCESS, - DIOCR_INSTALLER, - MachineName, - Reserved); - if (hClassesKey == INVALID_HANDLE_VALUE) - { - return FALSE; - } - - for (dwIndex = 0; ; dwIndex++) - { - dwLength = 40; - lError = RegEnumKeyExW(hClassesKey, - dwIndex, - szKeyName, - &dwLength, - NULL, - NULL, - NULL, - NULL); - TRACE("RegEnumKeyExW() returns %ld\n", lError); - if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA) - { - TRACE("Key name: %p\n", szKeyName); - - if (RegOpenKeyExW(hClassesKey, - szKeyName, - 0, - KEY_ALL_ACCESS, - &hClassKey)) - { - RegCloseKey(hClassesKey); - return FALSE; - } - - if (!RegQueryValueExW(hClassKey, - NoUseClass, - NULL, - NULL, - NULL, - NULL)) - { - TRACE("'NoUseClass' value found!\n"); - RegCloseKey(hClassKey); - continue; - } - - if ((Flags & DIBCI_NOINSTALLCLASS) && - (!RegQueryValueExW(hClassKey, - NoInstallClass, - NULL, - NULL, - NULL, - NULL))) - { - TRACE("'NoInstallClass' value found!\n"); - RegCloseKey(hClassKey); - continue; - } - - if ((Flags & DIBCI_NODISPLAYCLASS) && - (!RegQueryValueExW(hClassKey, - NoDisplayClass, - NULL, - NULL, - NULL, - NULL))) - { - TRACE("'NoDisplayClass' value found!\n"); - RegCloseKey(hClassKey); - continue; - } - - RegCloseKey(hClassKey); - - TRACE("Guid: %p\n", szKeyName); - if (dwGuidListIndex < ClassGuidListSize) - { - if (szKeyName[0] == L'{' && szKeyName[37] == L'}') - { - szKeyName[37] = 0; - } - TRACE("Guid: %p\n", &szKeyName[1]); - - UuidFromStringW(&szKeyName[1], - &ClassGuidList[dwGuidListIndex]); - } - - dwGuidListIndex++; - } - - if (lError != ERROR_SUCCESS) - break; - } - - RegCloseKey(hClassesKey); - - if (RequiredSize != NULL) - *RequiredSize = dwGuidListIndex; - - if (ClassGuidListSize < dwGuidListIndex) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return FALSE; - } - - return TRUE; -} - -/*********************************************************************** - * SetupDiClassGuidsFromNameA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiClassGuidsFromNameA( - LPCSTR ClassName, - LPGUID ClassGuidList, - DWORD ClassGuidListSize, - PDWORD RequiredSize) -{ - return SetupDiClassGuidsFromNameExA(ClassName, ClassGuidList, - ClassGuidListSize, RequiredSize, - NULL, NULL); -} - -/*********************************************************************** - * SetupDiClassGuidsFromNameW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiClassGuidsFromNameW( - LPCWSTR ClassName, - LPGUID ClassGuidList, - DWORD ClassGuidListSize, - PDWORD RequiredSize) -{ - return SetupDiClassGuidsFromNameExW(ClassName, ClassGuidList, - ClassGuidListSize, RequiredSize, - NULL, NULL); -} - -/*********************************************************************** - * SetupDiClassGuidsFromNameExA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiClassGuidsFromNameExA( - LPCSTR ClassName, - LPGUID ClassGuidList, - DWORD ClassGuidListSize, - PDWORD RequiredSize, - LPCSTR MachineName, - PVOID Reserved) -{ - LPWSTR ClassNameW = NULL; - LPWSTR MachineNameW = NULL; - BOOL bResult; - - FIXME("\n"); - - ClassNameW = MultiByteToUnicode(ClassName, CP_ACP); - if (ClassNameW == NULL) - return FALSE; - - if (MachineNameW) - { - MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); - if (MachineNameW == NULL) - { - MyFree(ClassNameW); - return FALSE; - } - } - - bResult = SetupDiClassGuidsFromNameExW(ClassNameW, ClassGuidList, - ClassGuidListSize, RequiredSize, - MachineNameW, Reserved); - - if (MachineNameW) - MyFree(MachineNameW); - - MyFree(ClassNameW); - - return bResult; -} - -/*********************************************************************** - * SetupDiClassGuidsFromNameExW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiClassGuidsFromNameExW( - LPCWSTR ClassName, - LPGUID ClassGuidList, - DWORD ClassGuidListSize, - PDWORD RequiredSize, - LPCWSTR MachineName, - PVOID Reserved) -{ - WCHAR szKeyName[40]; - WCHAR szClassName[256]; - HKEY hClassesKey; - HKEY hClassKey; - DWORD dwLength; - DWORD dwIndex; - LONG lError; - DWORD dwGuidListIndex = 0; - - if (RequiredSize != NULL) - *RequiredSize = 0; - - hClassesKey = SetupDiOpenClassRegKeyExW(NULL, - KEY_ALL_ACCESS, - DIOCR_INSTALLER, - MachineName, - Reserved); - if (hClassesKey == INVALID_HANDLE_VALUE) - { - return FALSE; - } - - for (dwIndex = 0; ; dwIndex++) - { - dwLength = 40; - lError = RegEnumKeyExW(hClassesKey, - dwIndex, - szKeyName, - &dwLength, - NULL, - NULL, - NULL, - NULL); - TRACE("RegEnumKeyExW() returns %ld\n", lError); - if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA) - { - TRACE("Key name: %p\n", szKeyName); - - if (RegOpenKeyExW(hClassesKey, - szKeyName, - 0, - KEY_ALL_ACCESS, - &hClassKey)) - { - RegCloseKey(hClassesKey); - return FALSE; - } - - dwLength = 256 * sizeof(WCHAR); - if (!RegQueryValueExW(hClassKey, - Class, - NULL, - NULL, - (LPBYTE)szClassName, - &dwLength)) - { - TRACE("Class name: %p\n", szClassName); - - if (strcmpiW(szClassName, ClassName) == 0) - { - TRACE("Found matching class name\n"); - - TRACE("Guid: %p\n", szKeyName); - if (dwGuidListIndex < ClassGuidListSize) - { - if (szKeyName[0] == L'{' && szKeyName[37] == L'}') - { - szKeyName[37] = 0; - } - TRACE("Guid: %p\n", &szKeyName[1]); - - UuidFromStringW(&szKeyName[1], - &ClassGuidList[dwGuidListIndex]); - } - - dwGuidListIndex++; - } - } - - RegCloseKey(hClassKey); - } - - if (lError != ERROR_SUCCESS) - break; - } - - RegCloseKey(hClassesKey); - - if (RequiredSize != NULL) - *RequiredSize = dwGuidListIndex; - - if (ClassGuidListSize < dwGuidListIndex) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return FALSE; - } - - return TRUE; -} - -/*********************************************************************** - * SetupDiClassNameFromGuidA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiClassNameFromGuidA( - const GUID* ClassGuid, - PSTR ClassName, - DWORD ClassNameSize, - PDWORD RequiredSize) -{ - return SetupDiClassNameFromGuidExA(ClassGuid, ClassName, - ClassNameSize, RequiredSize, - NULL, NULL); -} - -/*********************************************************************** - * SetupDiClassNameFromGuidW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiClassNameFromGuidW( - const GUID* ClassGuid, - PWSTR ClassName, - DWORD ClassNameSize, - PDWORD RequiredSize) -{ - return SetupDiClassNameFromGuidExW(ClassGuid, ClassName, - ClassNameSize, RequiredSize, - NULL, NULL); -} - -/*********************************************************************** - * SetupDiClassNameFromGuidExA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiClassNameFromGuidExA( - const GUID* ClassGuid, - PSTR ClassName, - DWORD ClassNameSize, - PDWORD RequiredSize, - PCSTR MachineName, - PVOID Reserved) -{ - FIXME("\n"); - return FALSE; -} - -/*********************************************************************** - * SetupDiClassNameFromGuidExW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiClassNameFromGuidExW( - const GUID* ClassGuid, - PWSTR ClassName, - DWORD ClassNameSize, - PDWORD RequiredSize, - PCWSTR MachineName, - PVOID Reserved) -{ - HKEY hKey; - DWORD dwLength; - - hKey = SetupDiOpenClassRegKeyExW(ClassGuid, - KEY_ALL_ACCESS, - DIOCR_INSTALLER, - MachineName, - Reserved); - if (hKey == INVALID_HANDLE_VALUE) - { - return FALSE; - } - - if (RequiredSize != NULL) - { - dwLength = 0; - if (RegQueryValueExW(hKey, - Class, - NULL, - NULL, - NULL, - &dwLength)) - { - RegCloseKey(hKey); - return FALSE; - } - - *RequiredSize = dwLength / sizeof(WCHAR); - } - - dwLength = ClassNameSize * sizeof(WCHAR); - if (RegQueryValueExW(hKey, - Class, - NULL, - NULL, - (LPBYTE)ClassName, - &dwLength)) - { - RegCloseKey(hKey); - return FALSE; - } - - RegCloseKey(hKey); - - return TRUE; -} - -/*********************************************************************** - * SetupDiCreateDeviceInfoList (SETUPAPI.@) - */ -HDEVINFO WINAPI -SetupDiCreateDeviceInfoList(const GUID *ClassGuid, - HWND hwndParent) -{ - return SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent, NULL, NULL); -} - -/*********************************************************************** - * SetupDiCreateDeviceInfoListExA (SETUPAPI.@) - */ -HDEVINFO WINAPI -SetupDiCreateDeviceInfoListExA(const GUID *ClassGuid, - HWND hwndParent, - PCSTR MachineName, - PVOID Reserved) -{ - LPWSTR MachineNameW = NULL; - HDEVINFO hDevInfo; - - TRACE("\n"); - - if (MachineName) - { - MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); - if (MachineNameW == NULL) - return (HDEVINFO)INVALID_HANDLE_VALUE; - } - - hDevInfo = SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent, - MachineNameW, Reserved); - - if (MachineNameW) - MyFree(MachineNameW); - - return hDevInfo; -} - -/*********************************************************************** - * SetupDiCreateDeviceInfoListExW (SETUPAPI.@) - */ -HDEVINFO WINAPI -SetupDiCreateDeviceInfoListExW(const GUID *ClassGuid, - HWND hwndParent, - PCWSTR MachineName, - PVOID Reserved) -{ - FIXME("\n"); - return (HDEVINFO)INVALID_HANDLE_VALUE; -} - -/*********************************************************************** - * SetupDiDestroyDeviceInfoList (SETUPAPI.@) - */ -BOOL WINAPI SetupDiDestroyDeviceInfoList(HDEVINFO devinfo) -{ - FIXME("%p\n", devinfo); - return FALSE; -} - -/*********************************************************************** - * SetupDiEnumDeviceInfo (SETUPAPI.@) - */ -BOOL WINAPI SetupDiEnumDeviceInfo( - HDEVINFO devinfo, - DWORD index, - PSP_DEVINFO_DATA info) -{ - FIXME("%p %ld %p\n", devinfo, index, info); - - if(info==NULL) - return FALSE; - if(info->cbSize < sizeof(*info)) - return FALSE; - - return FALSE; -} - -/*********************************************************************** - * SetupDiEnumDeviceInterfaces (SETUPAPI.@) - */ -BOOL WINAPI SetupDiEnumDeviceInterfaces( - HDEVINFO DeviceInfoSet, - PSP_DEVINFO_DATA DeviceInfoData, - CONST GUID * InterfaceClassGuid, - DWORD MemberIndex, - PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) -{ - FIXME("\n"); - return FALSE; -} - -/*********************************************************************** - * SetupDiGetActualSectionToInstallA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetActualSectionToInstallA( - HINF InfHandle, - PCSTR InfSectionName, - PSTR InfSectionWithExt, - DWORD InfSectionWithExtSize, - PDWORD RequiredSize, - PSTR *Extension) -{ - FIXME("\n"); - return FALSE; -} - -/*********************************************************************** - * SetupDiGetActualSectionToInstallW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetActualSectionToInstallW( - HINF InfHandle, - PCWSTR InfSectionName, - PWSTR InfSectionWithExt, - DWORD InfSectionWithExtSize, - PDWORD RequiredSize, - PWSTR *Extension) -{ - WCHAR szBuffer[MAX_PATH]; - DWORD dwLength; - DWORD dwFullLength; - LONG lLineCount = -1; - - lstrcpyW(szBuffer, InfSectionName); - dwLength = lstrlenW(szBuffer); - - if (OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) - { - /* Test section name with '.NTx86' extension */ - lstrcpyW(&szBuffer[dwLength], NtPlatformExtension); - lLineCount = SetupGetLineCountW(InfHandle, szBuffer); - - if (lLineCount == -1) - { - /* Test section name with '.NT' extension */ - lstrcpyW(&szBuffer[dwLength], NtExtension); - lLineCount = SetupGetLineCountW(InfHandle, szBuffer); - } - } - else - { - /* Test section name with '.Win' extension */ - lstrcpyW(&szBuffer[dwLength], WinExtension); - lLineCount = SetupGetLineCountW(InfHandle, szBuffer); - } - - if (lLineCount == -1) - { - /* Test section name without extension */ - szBuffer[dwLength] = 0; - lLineCount = SetupGetLineCountW(InfHandle, szBuffer); - } - - if (lLineCount == -1) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - dwFullLength = lstrlenW(szBuffer); - - if (InfSectionWithExt != NULL && InfSectionWithExtSize != 0) - { - if (InfSectionWithExtSize < (dwFullLength + 1)) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return FALSE; - } - - lstrcpyW(InfSectionWithExt, szBuffer); - if (Extension != NULL) - { - *Extension = (dwLength == dwFullLength) ? NULL : &InfSectionWithExt[dwLength]; - } - } - - if (RequiredSize != NULL) - { - *RequiredSize = dwFullLength + 1; - } - - return TRUE; -} - -/*********************************************************************** - * SetupDiGetClassDescriptionA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetClassDescriptionA( - const GUID* ClassGuid, - PSTR ClassDescription, - DWORD ClassDescriptionSize, - PDWORD RequiredSize) -{ - return SetupDiGetClassDescriptionExA(ClassGuid, ClassDescription, - ClassDescriptionSize, - RequiredSize, NULL, NULL); -} - -/*********************************************************************** - * SetupDiGetClassDescriptionW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetClassDescriptionW( - const GUID* ClassGuid, - PWSTR ClassDescription, - DWORD ClassDescriptionSize, - PDWORD RequiredSize) -{ - return SetupDiGetClassDescriptionExW(ClassGuid, ClassDescription, - ClassDescriptionSize, - RequiredSize, NULL, NULL); -} - -/*********************************************************************** - * SetupDiGetClassDescriptionExA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetClassDescriptionExA( - const GUID* ClassGuid, - PSTR ClassDescription, - DWORD ClassDescriptionSize, - PDWORD RequiredSize, - PCSTR MachineName, - PVOID Reserved) -{ - FIXME("\n"); - return FALSE; -} - -/*********************************************************************** - * SetupDiGetClassDescriptionExW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetClassDescriptionExW( - const GUID* ClassGuid, - PWSTR ClassDescription, - DWORD ClassDescriptionSize, - PDWORD RequiredSize, - PCWSTR MachineName, - PVOID Reserved) -{ - HKEY hKey; - DWORD dwLength; - - hKey = SetupDiOpenClassRegKeyExW(ClassGuid, - KEY_ALL_ACCESS, - DIOCR_INSTALLER, - MachineName, - Reserved); - if (hKey == INVALID_HANDLE_VALUE) - { - WARN("SetupDiOpenClassRegKeyExW() failed (Error %lu)\n", GetLastError()); - return FALSE; - } - - if (RequiredSize != NULL) - { - dwLength = 0; - if (RegQueryValueExW(hKey, - NULL, - NULL, - NULL, - NULL, - &dwLength)) - { - RegCloseKey(hKey); - return FALSE; - } - - *RequiredSize = dwLength / sizeof(WCHAR); - } - - dwLength = ClassDescriptionSize * sizeof(WCHAR); - if (RegQueryValueExW(hKey, - NULL, - NULL, - NULL, - (LPBYTE)ClassDescription, - &dwLength)) - { - RegCloseKey(hKey); - return FALSE; - } - - RegCloseKey(hKey); - - return TRUE; -} - -/*********************************************************************** - * SetupDiGetClassDevsA (SETUPAPI.@) - */ -HDEVINFO WINAPI SetupDiGetClassDevsA( - CONST GUID *class, - LPCSTR enumstr, - HWND parent, - DWORD flags) -{ - FIXME("%s %s %p %08lx\n",debugstr_guid(class),enumstr,parent,flags); - - return (HDEVINFO) INVALID_HANDLE_VALUE; -} - -/*********************************************************************** - * SetupDiGetClassDevsW (SETUPAPI.@) - */ -HDEVINFO WINAPI SetupDiGetClassDevsW( - CONST GUID *class, - LPCWSTR enumstr, - HWND parent, - DWORD flags) -{ - FIXME("%s %s %p %08lx\n",debugstr_guid(class),debugstr_w(enumstr),parent,flags); - - return (HDEVINFO) INVALID_HANDLE_VALUE; -} - -/*********************************************************************** - * SetupDiGetDeviceInterfaceDetailA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetDeviceInterfaceDetailA( - HDEVINFO DeviceInfoSet, - PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, - PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData, - DWORD DeviceInterfaceDetailDataSize, - PDWORD RequiredSize, - PSP_DEVINFO_DATA DeviceInfoData) -{ - FIXME("\n"); - return FALSE; -} - -/*********************************************************************** - * SetupDiGetDeviceInterfaceDetailW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetDeviceInterfaceDetailW( - HDEVINFO DeviceInfoSet, - PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, - PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData, - DWORD DeviceInterfaceDetailDataSize, - PDWORD RequiredSize, - PSP_DEVINFO_DATA DeviceInfoData) -{ - FIXME("\n"); - return FALSE; -} - -/*********************************************************************** - * SetupDiGetDeviceRegistryPropertyA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetDeviceRegistryPropertyA( - HDEVINFO devinfo, - PSP_DEVINFO_DATA DeviceInfoData, - DWORD Property, - PDWORD PropertyRegDataType, - PBYTE PropertyBuffer, - DWORD PropertyBufferSize, - PDWORD RequiredSize) -{ - FIXME("%04lx %p %ld %p %p %ld %p\n", (DWORD)devinfo, DeviceInfoData, - Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize, - RequiredSize); - return FALSE; -} - -/*********************************************************************** - * SetupDiInstallClassA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiInstallClassA( - HWND hwndParent, - PCSTR InfFileName, - DWORD Flags, - HSPFILEQ FileQueue) -{ - UNICODE_STRING FileNameW; - BOOL Result; - - if (!RtlCreateUnicodeStringFromAsciiz(&FileNameW, InfFileName)) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return FALSE; - } - - Result = SetupDiInstallClassW(hwndParent, FileNameW.Buffer, Flags, FileQueue); - - RtlFreeUnicodeString(&FileNameW); - - return Result; -} - -static HKEY CreateClassKey(HINF hInf) -{ - WCHAR FullBuffer[MAX_PATH]; - WCHAR Buffer[MAX_PATH]; - DWORD RequiredSize; - HKEY hClassKey; - - if (!SetupGetLineTextW(NULL, - hInf, - Version, - ClassGUID, - Buffer, - MAX_PATH, - &RequiredSize)) - { - return INVALID_HANDLE_VALUE; - } - - lstrcpyW(FullBuffer, ControlClass); - lstrcatW(FullBuffer, Buffer); - - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, - FullBuffer, - 0, - KEY_ALL_ACCESS, - &hClassKey)) - { - if (!SetupGetLineTextW(NULL, - hInf, - Version, - Class, - Buffer, - MAX_PATH, - &RequiredSize)) - { - return INVALID_HANDLE_VALUE; - } - - if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, - FullBuffer, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - KEY_ALL_ACCESS, - NULL, - &hClassKey, - NULL)) - { - return INVALID_HANDLE_VALUE; - } - - } - - if (RegSetValueExW(hClassKey, - Class, - 0, - REG_SZ, - (LPBYTE)Buffer, - RequiredSize * sizeof(WCHAR))) - { - RegCloseKey(hClassKey); - RegDeleteKeyW(HKEY_LOCAL_MACHINE, - FullBuffer); - return INVALID_HANDLE_VALUE; - } - - return hClassKey; -} - -/*********************************************************************** - * SetupDiInstallClassW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiInstallClassW( - HWND hwndParent, - PCWSTR InfFileName, - DWORD Flags, - HSPFILEQ FileQueue) -{ - WCHAR SectionName[MAX_PATH]; - DWORD SectionNameLength = 0; - HINF hInf; - BOOL bFileQueueCreated = FALSE; - HKEY hClassKey; - - - FIXME("\n"); - - if ((Flags & DI_NOVCP) && (FileQueue == NULL || FileQueue == INVALID_HANDLE_VALUE)) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - /* Open the .inf file */ - hInf = SetupOpenInfFileW(InfFileName, - NULL, - INF_STYLE_WIN4, - NULL); - if (hInf == INVALID_HANDLE_VALUE) - { - - return FALSE; - } - - /* Create or open the class registry key 'HKLM\\CurrentControlSet\\Class\\{GUID}' */ - hClassKey = CreateClassKey(hInf); - if (hClassKey == INVALID_HANDLE_VALUE) - { - SetupCloseInfFile(hInf); - return FALSE; - } - - - /* Try to append a layout file */ -#if 0 - SetupOpenAppendInfFileW(NULL, hInf, NULL); -#endif - - /* Retrieve the actual section name */ - SetupDiGetActualSectionToInstallW(hInf, - ClassInstall32, - SectionName, - MAX_PATH, - &SectionNameLength, - NULL); - -#if 0 - if (!(Flags & DI_NOVCP)) - { - FileQueue = SetupOpenFileQueue(); - if (FileQueue == INVALID_HANDLE_VALUE) - { - SetupCloseInfFile(hInf); - return FALSE; - } - - bFileQueueCreated = TRUE; - - } -#endif - - SetupInstallFromInfSectionW(NULL, - hInf, - SectionName, - SPINST_REGISTRY, - hClassKey, - NULL, - 0, - NULL, - NULL, - INVALID_HANDLE_VALUE, - NULL); - - /* FIXME: More code! */ - - if (bFileQueueCreated) - SetupCloseFileQueue(FileQueue); - - SetupCloseInfFile(hInf); - - return TRUE; -} - - -/*********************************************************************** - * SetupDiOpenClassRegKey (SETUPAPI.@) - */ -HKEY WINAPI SetupDiOpenClassRegKey( - const GUID* ClassGuid, - REGSAM samDesired) -{ - return SetupDiOpenClassRegKeyExW(ClassGuid, samDesired, - DIOCR_INSTALLER, NULL, NULL); -} - - -/*********************************************************************** - * SetupDiOpenClassRegKeyExA (SETUPAPI.@) - */ -HKEY WINAPI SetupDiOpenClassRegKeyExA( - const GUID* ClassGuid, - REGSAM samDesired, - DWORD Flags, - PCSTR MachineName, - PVOID Reserved) -{ - PWSTR MachineNameW = NULL; - HKEY hKey; - - TRACE("\n"); - - if (MachineName) - { - MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); - if (MachineNameW == NULL) - return INVALID_HANDLE_VALUE; - } - - hKey = SetupDiOpenClassRegKeyExW(ClassGuid, samDesired, - Flags, MachineNameW, Reserved); - - if (MachineNameW) - MyFree(MachineNameW); - - return hKey; -} - - -/*********************************************************************** - * SetupDiOpenClassRegKeyExW (SETUPAPI.@) - */ -HKEY WINAPI SetupDiOpenClassRegKeyExW( - const GUID* ClassGuid, - REGSAM samDesired, - DWORD Flags, - PCWSTR MachineName, - PVOID Reserved) -{ - LPWSTR lpGuidString; - HKEY hClassesKey; - HKEY hClassKey; - LPCWSTR lpKeyName; - - if (MachineName != NULL) - { - FIXME("Remote access not supported yet!\n"); - return INVALID_HANDLE_VALUE; - } - - if (Flags == DIOCR_INSTALLER) - { - lpKeyName = ControlClass; - } - else if (Flags == DIOCR_INTERFACE) - { - lpKeyName = DeviceClasses; - } - else - { - ERR("Invalid Flags parameter!\n"); - SetLastError(ERROR_INVALID_PARAMETER); - return INVALID_HANDLE_VALUE; - } - - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, - lpKeyName, - 0, - KEY_ALL_ACCESS, - &hClassesKey)) - { - return INVALID_HANDLE_VALUE; - } - - if (ClassGuid == NULL) - return hClassesKey; - - if (UuidToStringW((UUID*)ClassGuid, &lpGuidString) != RPC_S_OK) - { - RegCloseKey(hClassesKey); - return FALSE; - } - - if (RegOpenKeyExW(hClassesKey, - lpGuidString, - 0, - KEY_ALL_ACCESS, - &hClassKey)) - { - RpcStringFreeW(&lpGuidString); - RegCloseKey(hClassesKey); - return FALSE; - } - - RpcStringFreeW(&lpGuidString); - RegCloseKey(hClassesKey); - - return hClassKey; -} - -/*********************************************************************** - * SetupDiOpenDeviceInterfaceW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiOpenDeviceInterfaceW( - HDEVINFO DeviceInfoSet, - PCWSTR DevicePath, - DWORD OpenFlags, - PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) -{ - FIXME("%p %s %08lx %p\n", - DeviceInfoSet, debugstr_w(DevicePath), OpenFlags, DeviceInterfaceData); - return FALSE; -} - -/*********************************************************************** - * SetupDiOpenDeviceInterfaceA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiOpenDeviceInterfaceA( - HDEVINFO DeviceInfoSet, - PCSTR DevicePath, - DWORD OpenFlags, - PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) -{ - FIXME("%p %s %08lx %p\n", DeviceInfoSet, - debugstr_a(DevicePath), OpenFlags, DeviceInterfaceData); - return FALSE; -} - -/*********************************************************************** - * SetupDiSetClassInstallParamsA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiSetClassInstallParamsA( - HDEVINFO DeviceInfoSet, - PSP_DEVINFO_DATA DeviceInfoData, - PSP_CLASSINSTALL_HEADER ClassInstallParams, - DWORD ClassInstallParamsSize) -{ - FIXME("%p %p %x %lu\n",DeviceInfoSet, DeviceInfoData, - ClassInstallParams->InstallFunction, ClassInstallParamsSize); - return FALSE; -} - -/*********************************************************************** - * SetupDiCallClassInstaller (SETUPAPI.@) - */ -BOOL WINAPI SetupDiCallClassInstaller( - DWORD InstallFunction, - HDEVINFO DeviceInfoSet, - PSP_DEVINFO_DATA DeviceInfoData) -{ - FIXME("%ld %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData); - return FALSE; -} - -/*********************************************************************** - * SetupDiGetDeviceInstallParamsA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetDeviceInstallParamsA( - HDEVINFO DeviceInfoSet, - PSP_DEVINFO_DATA DeviceInfoData, - PSP_DEVINSTALL_PARAMS_A DeviceInstallParams) -{ - FIXME("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams); - return FALSE; -} - -/*********************************************************************** - * SetupDiOpenDevRegKey (SETUPAPI.@) - */ -HKEY WINAPI SetupDiOpenDevRegKey( - HDEVINFO DeviceInfoSet, - PSP_DEVINFO_DATA DeviceInfoData, - DWORD Scope, - DWORD HwProfile, - DWORD KeyType, - REGSAM samDesired) -{ - FIXME("%p %p %ld %ld %ld %lx\n", DeviceInfoSet, DeviceInfoData, - Scope, HwProfile, KeyType, samDesired); - return INVALID_HANDLE_VALUE; -} +/* + * SetupAPI device installer + * + * Copyright 2000 Andreas Mohr for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#include "windef.h" +#include "winbase.h" +#include "winnt.h" +#include "winreg.h" +#include "winternl.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "initguid.h" +#include "winioctl.h" +#include "rpc.h" +#include "rpcdce.h" + +#include "setupapi_private.h" + + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/* Unicode constants */ +static const WCHAR ClassGUID[] = {'C','l','a','s','s','G','U','I','D',0}; +static const WCHAR Class[] = {'C','l','a','s','s',0}; +static const WCHAR ClassInstall32[] = {'C','l','a','s','s','I','n','s','t','a','l','l','3','2',0}; +static const WCHAR NoDisplayClass[] = {'N','o','D','i','s','p','l','a','y','C','l','a','s','s',0}; +static const WCHAR NoInstallClass[] = {'N','o','I','s','t','a','l','l','C','l','a','s','s',0}; +static const WCHAR NoUseClass[] = {'N','o','U','s','e','C','l','a','s','s',0}; +static const WCHAR NtExtension[] = {'.','N','T',0}; +static const WCHAR NtPlatformExtension[] = {'.','N','T','x','8','6',0}; +static const WCHAR Version[] = {'V','e','r','s','i','o','n',0}; +static const WCHAR WinExtension[] = {'.','W','i','n',0}; + +/* Registry key and value names */ +static const WCHAR ControlClass[] = {'S','y','s','t','e','m','\\', + 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', + 'C','o','n','t','r','o','l','\\', + 'C','l','a','s','s',0}; + +static const WCHAR DeviceClasses[] = {'S','y','s','t','e','m','\\', + 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', + 'C','o','n','t','r','o','l','\\', + 'D','e','v','i','c','e','C','l','a','s','s','e','s',0}; + +/*********************************************************************** + * SetupDiBuildClassInfoList (SETUPAPI.@) + */ +BOOL WINAPI SetupDiBuildClassInfoList( + DWORD Flags, + LPGUID ClassGuidList, + DWORD ClassGuidListSize, + PDWORD RequiredSize) +{ + TRACE("\n"); + return SetupDiBuildClassInfoListExW(Flags, ClassGuidList, + ClassGuidListSize, RequiredSize, + NULL, NULL); +} + +/*********************************************************************** + * SetupDiBuildClassInfoListExA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiBuildClassInfoListExA( + DWORD Flags, + LPGUID ClassGuidList, + DWORD ClassGuidListSize, + PDWORD RequiredSize, + LPCSTR MachineName, + PVOID Reserved) +{ + LPWSTR MachineNameW = NULL; + BOOL bResult; + + TRACE("\n"); + + if (MachineName) + { + MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); + if (MachineNameW == NULL) return FALSE; + } + + bResult = SetupDiBuildClassInfoListExW(Flags, ClassGuidList, + ClassGuidListSize, RequiredSize, + MachineNameW, Reserved); + + if (MachineNameW) + MyFree(MachineNameW); + + return bResult; +} + +/*********************************************************************** + * SetupDiBuildClassInfoListExW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiBuildClassInfoListExW( + DWORD Flags, + LPGUID ClassGuidList, + DWORD ClassGuidListSize, + PDWORD RequiredSize, + LPCWSTR MachineName, + PVOID Reserved) +{ + WCHAR szKeyName[40]; + HKEY hClassesKey; + HKEY hClassKey; + DWORD dwLength; + DWORD dwIndex; + LONG lError; + DWORD dwGuidListIndex = 0; + + TRACE("\n"); + + if (RequiredSize != NULL) + *RequiredSize = 0; + + hClassesKey = SetupDiOpenClassRegKeyExW(NULL, + KEY_ALL_ACCESS, + DIOCR_INSTALLER, + MachineName, + Reserved); + if (hClassesKey == INVALID_HANDLE_VALUE) + { + return FALSE; + } + + for (dwIndex = 0; ; dwIndex++) + { + dwLength = 40; + lError = RegEnumKeyExW(hClassesKey, + dwIndex, + szKeyName, + &dwLength, + NULL, + NULL, + NULL, + NULL); + TRACE("RegEnumKeyExW() returns %ld\n", lError); + if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA) + { + TRACE("Key name: %p\n", szKeyName); + + if (RegOpenKeyExW(hClassesKey, + szKeyName, + 0, + KEY_ALL_ACCESS, + &hClassKey)) + { + RegCloseKey(hClassesKey); + return FALSE; + } + + if (!RegQueryValueExW(hClassKey, + NoUseClass, + NULL, + NULL, + NULL, + NULL)) + { + TRACE("'NoUseClass' value found!\n"); + RegCloseKey(hClassKey); + continue; + } + + if ((Flags & DIBCI_NOINSTALLCLASS) && + (!RegQueryValueExW(hClassKey, + NoInstallClass, + NULL, + NULL, + NULL, + NULL))) + { + TRACE("'NoInstallClass' value found!\n"); + RegCloseKey(hClassKey); + continue; + } + + if ((Flags & DIBCI_NODISPLAYCLASS) && + (!RegQueryValueExW(hClassKey, + NoDisplayClass, + NULL, + NULL, + NULL, + NULL))) + { + TRACE("'NoDisplayClass' value found!\n"); + RegCloseKey(hClassKey); + continue; + } + + RegCloseKey(hClassKey); + + TRACE("Guid: %p\n", szKeyName); + if (dwGuidListIndex < ClassGuidListSize) + { + if (szKeyName[0] == L'{' && szKeyName[37] == L'}') + { + szKeyName[37] = 0; + } + TRACE("Guid: %p\n", &szKeyName[1]); + + UuidFromStringW(&szKeyName[1], + &ClassGuidList[dwGuidListIndex]); + } + + dwGuidListIndex++; + } + + if (lError != ERROR_SUCCESS) + break; + } + + RegCloseKey(hClassesKey); + + if (RequiredSize != NULL) + *RequiredSize = dwGuidListIndex; + + if (ClassGuidListSize < dwGuidListIndex) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + return TRUE; +} + +/*********************************************************************** + * SetupDiClassGuidsFromNameA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassGuidsFromNameA( + LPCSTR ClassName, + LPGUID ClassGuidList, + DWORD ClassGuidListSize, + PDWORD RequiredSize) +{ + return SetupDiClassGuidsFromNameExA(ClassName, ClassGuidList, + ClassGuidListSize, RequiredSize, + NULL, NULL); +} + +/*********************************************************************** + * SetupDiClassGuidsFromNameW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassGuidsFromNameW( + LPCWSTR ClassName, + LPGUID ClassGuidList, + DWORD ClassGuidListSize, + PDWORD RequiredSize) +{ + return SetupDiClassGuidsFromNameExW(ClassName, ClassGuidList, + ClassGuidListSize, RequiredSize, + NULL, NULL); +} + +/*********************************************************************** + * SetupDiClassGuidsFromNameExA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassGuidsFromNameExA( + LPCSTR ClassName, + LPGUID ClassGuidList, + DWORD ClassGuidListSize, + PDWORD RequiredSize, + LPCSTR MachineName, + PVOID Reserved) +{ + LPWSTR ClassNameW = NULL; + LPWSTR MachineNameW = NULL; + BOOL bResult; + + FIXME("\n"); + + ClassNameW = MultiByteToUnicode(ClassName, CP_ACP); + if (ClassNameW == NULL) + return FALSE; + + if (MachineNameW) + { + MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); + if (MachineNameW == NULL) + { + MyFree(ClassNameW); + return FALSE; + } + } + + bResult = SetupDiClassGuidsFromNameExW(ClassNameW, ClassGuidList, + ClassGuidListSize, RequiredSize, + MachineNameW, Reserved); + + if (MachineNameW) + MyFree(MachineNameW); + + MyFree(ClassNameW); + + return bResult; +} + +/*********************************************************************** + * SetupDiClassGuidsFromNameExW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassGuidsFromNameExW( + LPCWSTR ClassName, + LPGUID ClassGuidList, + DWORD ClassGuidListSize, + PDWORD RequiredSize, + LPCWSTR MachineName, + PVOID Reserved) +{ + WCHAR szKeyName[40]; + WCHAR szClassName[256]; + HKEY hClassesKey; + HKEY hClassKey; + DWORD dwLength; + DWORD dwIndex; + LONG lError; + DWORD dwGuidListIndex = 0; + + if (RequiredSize != NULL) + *RequiredSize = 0; + + hClassesKey = SetupDiOpenClassRegKeyExW(NULL, + KEY_ALL_ACCESS, + DIOCR_INSTALLER, + MachineName, + Reserved); + if (hClassesKey == INVALID_HANDLE_VALUE) + { + return FALSE; + } + + for (dwIndex = 0; ; dwIndex++) + { + dwLength = 40; + lError = RegEnumKeyExW(hClassesKey, + dwIndex, + szKeyName, + &dwLength, + NULL, + NULL, + NULL, + NULL); + TRACE("RegEnumKeyExW() returns %ld\n", lError); + if (lError == ERROR_SUCCESS || lError == ERROR_MORE_DATA) + { + TRACE("Key name: %p\n", szKeyName); + + if (RegOpenKeyExW(hClassesKey, + szKeyName, + 0, + KEY_ALL_ACCESS, + &hClassKey)) + { + RegCloseKey(hClassesKey); + return FALSE; + } + + dwLength = 256 * sizeof(WCHAR); + if (!RegQueryValueExW(hClassKey, + Class, + NULL, + NULL, + (LPBYTE)szClassName, + &dwLength)) + { + TRACE("Class name: %p\n", szClassName); + + if (strcmpiW(szClassName, ClassName) == 0) + { + TRACE("Found matching class name\n"); + + TRACE("Guid: %p\n", szKeyName); + if (dwGuidListIndex < ClassGuidListSize) + { + if (szKeyName[0] == L'{' && szKeyName[37] == L'}') + { + szKeyName[37] = 0; + } + TRACE("Guid: %p\n", &szKeyName[1]); + + UuidFromStringW(&szKeyName[1], + &ClassGuidList[dwGuidListIndex]); + } + + dwGuidListIndex++; + } + } + + RegCloseKey(hClassKey); + } + + if (lError != ERROR_SUCCESS) + break; + } + + RegCloseKey(hClassesKey); + + if (RequiredSize != NULL) + *RequiredSize = dwGuidListIndex; + + if (ClassGuidListSize < dwGuidListIndex) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + return TRUE; +} + +/*********************************************************************** + * SetupDiClassNameFromGuidA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassNameFromGuidA( + const GUID* ClassGuid, + PSTR ClassName, + DWORD ClassNameSize, + PDWORD RequiredSize) +{ + return SetupDiClassNameFromGuidExA(ClassGuid, ClassName, + ClassNameSize, RequiredSize, + NULL, NULL); +} + +/*********************************************************************** + * SetupDiClassNameFromGuidW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassNameFromGuidW( + const GUID* ClassGuid, + PWSTR ClassName, + DWORD ClassNameSize, + PDWORD RequiredSize) +{ + return SetupDiClassNameFromGuidExW(ClassGuid, ClassName, + ClassNameSize, RequiredSize, + NULL, NULL); +} + +/*********************************************************************** + * SetupDiClassNameFromGuidExA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassNameFromGuidExA( + const GUID* ClassGuid, + PSTR ClassName, + DWORD ClassNameSize, + PDWORD RequiredSize, + PCSTR MachineName, + PVOID Reserved) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiClassNameFromGuidExW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassNameFromGuidExW( + const GUID* ClassGuid, + PWSTR ClassName, + DWORD ClassNameSize, + PDWORD RequiredSize, + PCWSTR MachineName, + PVOID Reserved) +{ + HKEY hKey; + DWORD dwLength; + + hKey = SetupDiOpenClassRegKeyExW(ClassGuid, + KEY_ALL_ACCESS, + DIOCR_INSTALLER, + MachineName, + Reserved); + if (hKey == INVALID_HANDLE_VALUE) + { + return FALSE; + } + + if (RequiredSize != NULL) + { + dwLength = 0; + if (RegQueryValueExW(hKey, + Class, + NULL, + NULL, + NULL, + &dwLength)) + { + RegCloseKey(hKey); + return FALSE; + } + + *RequiredSize = dwLength / sizeof(WCHAR); + } + + dwLength = ClassNameSize * sizeof(WCHAR); + if (RegQueryValueExW(hKey, + Class, + NULL, + NULL, + (LPBYTE)ClassName, + &dwLength)) + { + RegCloseKey(hKey); + return FALSE; + } + + RegCloseKey(hKey); + + return TRUE; +} + +/*********************************************************************** + * SetupDiCreateDeviceInfoList (SETUPAPI.@) + */ +HDEVINFO WINAPI +SetupDiCreateDeviceInfoList(const GUID *ClassGuid, + HWND hwndParent) +{ + return SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent, NULL, NULL); +} + +/*********************************************************************** + * SetupDiCreateDeviceInfoListExA (SETUPAPI.@) + */ +HDEVINFO WINAPI +SetupDiCreateDeviceInfoListExA(const GUID *ClassGuid, + HWND hwndParent, + PCSTR MachineName, + PVOID Reserved) +{ + LPWSTR MachineNameW = NULL; + HDEVINFO hDevInfo; + + TRACE("\n"); + + if (MachineName) + { + MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); + if (MachineNameW == NULL) + return (HDEVINFO)INVALID_HANDLE_VALUE; + } + + hDevInfo = SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent, + MachineNameW, Reserved); + + if (MachineNameW) + MyFree(MachineNameW); + + return hDevInfo; +} + +/*********************************************************************** + * SetupDiCreateDeviceInfoListExW (SETUPAPI.@) + */ +HDEVINFO WINAPI +SetupDiCreateDeviceInfoListExW(const GUID *ClassGuid, + HWND hwndParent, + PCWSTR MachineName, + PVOID Reserved) +{ + FIXME("\n"); + return (HDEVINFO)INVALID_HANDLE_VALUE; +} + +/*********************************************************************** + * SetupDiEnumDeviceInfo (SETUPAPI.@) + */ +BOOL WINAPI SetupDiEnumDeviceInfo( + HDEVINFO devinfo, + DWORD index, + PSP_DEVINFO_DATA info) +{ + FIXME("%p %ld %p\n", devinfo, index, info); + + if(info==NULL) + return FALSE; + if(info->cbSize < sizeof(*info)) + return FALSE; + + return FALSE; +} + +/*********************************************************************** + * SetupDiGetActualSectionToInstallA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetActualSectionToInstallA( + HINF InfHandle, + PCSTR InfSectionName, + PSTR InfSectionWithExt, + DWORD InfSectionWithExtSize, + PDWORD RequiredSize, + PSTR *Extension) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetActualSectionToInstallW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetActualSectionToInstallW( + HINF InfHandle, + PCWSTR InfSectionName, + PWSTR InfSectionWithExt, + DWORD InfSectionWithExtSize, + PDWORD RequiredSize, + PWSTR *Extension) +{ + WCHAR szBuffer[MAX_PATH]; + DWORD dwLength; + DWORD dwFullLength; + LONG lLineCount = -1; + + lstrcpyW(szBuffer, InfSectionName); + dwLength = lstrlenW(szBuffer); + + if (OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + /* Test section name with '.NTx86' extension */ + lstrcpyW(&szBuffer[dwLength], NtPlatformExtension); + lLineCount = SetupGetLineCountW(InfHandle, szBuffer); + + if (lLineCount == -1) + { + /* Test section name with '.NT' extension */ + lstrcpyW(&szBuffer[dwLength], NtExtension); + lLineCount = SetupGetLineCountW(InfHandle, szBuffer); + } + } + else + { + /* Test section name with '.Win' extension */ + lstrcpyW(&szBuffer[dwLength], WinExtension); + lLineCount = SetupGetLineCountW(InfHandle, szBuffer); + } + + if (lLineCount == -1) + { + /* Test section name without extension */ + szBuffer[dwLength] = 0; + lLineCount = SetupGetLineCountW(InfHandle, szBuffer); + } + + if (lLineCount == -1) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + dwFullLength = lstrlenW(szBuffer); + + if (InfSectionWithExt != NULL && InfSectionWithExtSize != 0) + { + if (InfSectionWithExtSize < (dwFullLength + 1)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + lstrcpyW(InfSectionWithExt, szBuffer); + if (Extension != NULL) + { + *Extension = (dwLength == dwFullLength) ? NULL : &InfSectionWithExt[dwLength]; + } + } + + if (RequiredSize != NULL) + { + *RequiredSize = dwFullLength + 1; + } + + return TRUE; +} + +/*********************************************************************** + * SetupDiGetClassDescriptionA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetClassDescriptionA( + const GUID* ClassGuid, + PSTR ClassDescription, + DWORD ClassDescriptionSize, + PDWORD RequiredSize) +{ + return SetupDiGetClassDescriptionExA(ClassGuid, ClassDescription, + ClassDescriptionSize, + RequiredSize, NULL, NULL); +} + +/*********************************************************************** + * SetupDiGetClassDescriptionW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetClassDescriptionW( + const GUID* ClassGuid, + PWSTR ClassDescription, + DWORD ClassDescriptionSize, + PDWORD RequiredSize) +{ + return SetupDiGetClassDescriptionExW(ClassGuid, ClassDescription, + ClassDescriptionSize, + RequiredSize, NULL, NULL); +} + +/*********************************************************************** + * SetupDiGetClassDescriptionExA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetClassDescriptionExA( + const GUID* ClassGuid, + PSTR ClassDescription, + DWORD ClassDescriptionSize, + PDWORD RequiredSize, + PCSTR MachineName, + PVOID Reserved) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetClassDescriptionExW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetClassDescriptionExW( + const GUID* ClassGuid, + PWSTR ClassDescription, + DWORD ClassDescriptionSize, + PDWORD RequiredSize, + PCWSTR MachineName, + PVOID Reserved) +{ + HKEY hKey; + DWORD dwLength; + + hKey = SetupDiOpenClassRegKeyExW(ClassGuid, + KEY_ALL_ACCESS, + DIOCR_INSTALLER, + MachineName, + Reserved); + if (hKey == INVALID_HANDLE_VALUE) + { + WARN("SetupDiOpenClassRegKeyExW() failed (Error %lu)\n", GetLastError()); + return FALSE; + } + + if (RequiredSize != NULL) + { + dwLength = 0; + if (RegQueryValueExW(hKey, + NULL, + NULL, + NULL, + NULL, + &dwLength)) + { + RegCloseKey(hKey); + return FALSE; + } + + *RequiredSize = dwLength / sizeof(WCHAR); + } + + dwLength = ClassDescriptionSize * sizeof(WCHAR); + if (RegQueryValueExW(hKey, + NULL, + NULL, + NULL, + (LPBYTE)ClassDescription, + &dwLength)) + { + RegCloseKey(hKey); + return FALSE; + } + + RegCloseKey(hKey); + + return TRUE; +} + +/*********************************************************************** + * SetupDiGetClassDevsA (SETUPAPI.@) + */ +HDEVINFO WINAPI SetupDiGetClassDevsA( + CONST GUID *class, + LPCSTR enumstr, + HWND parent, + DWORD flags) +{ + HDEVINFO ret; + LPWSTR enumstrW = NULL; + + if (enumstr) + { + int len = MultiByteToWideChar(CP_ACP, 0, enumstr, -1, NULL, 0); + enumstrW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!enumstrW) + { + ret = (HDEVINFO)INVALID_HANDLE_VALUE; + goto end; + } + MultiByteToWideChar(CP_ACP, 0, enumstr, -1, enumstrW, len); + } + ret = SetupDiGetClassDevsW(class, enumstrW, parent, flags); + HeapFree(GetProcessHeap(), 0, enumstrW); + +end: + return ret; +} + +#define SETUP_SERIAL_PORT_MAGIC 0xd00ff055 + +typedef struct _SerialPortName +{ + WCHAR name[5]; +} SerialPortName; + +typedef struct _SerialPortList +{ + DWORD magic; + UINT numPorts; + SerialPortName names[1]; +} SerialPortList; + +static HDEVINFO SETUP_CreateSerialDeviceList(void) +{ + static const size_t initialSize = 100; + size_t size; + WCHAR buf[initialSize]; + LPWSTR devices; + HDEVINFO ret; + BOOL failed = FALSE; + + devices = buf; + size = initialSize; + do { + if (QueryDosDeviceW(NULL, devices, size) == 0) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + size *= 2; + if (devices != buf) + HeapFree(GetProcessHeap(), 0, devices); + devices = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, + size * sizeof(WCHAR)); + if (!devices) + failed = TRUE; + else + *devices = 0; + } + else + failed = TRUE; + } + } while (!*devices && !failed); + if (!failed) + { + static const WCHAR comW[] = { 'C','O','M',0 }; + LPWSTR ptr; + UINT numSerialPorts = 0; + SerialPortList *list; + + for (ptr = devices; *ptr; ptr += strlenW(ptr) + 1) + { + if (!strncmpW(comW, ptr, sizeof(comW) / sizeof(comW[0]) - 1)) + numSerialPorts++; + } + list = HeapAlloc(GetProcessHeap(), 0, sizeof(SerialPortList) + + numSerialPorts ? (numSerialPorts - 1) * sizeof(SerialPortName) : 0); + if (list) + { + list->magic = SETUP_SERIAL_PORT_MAGIC; + list->numPorts = 0; + for (ptr = devices; *ptr; ptr += strlenW(ptr) + 1) + { + if (!strncmpW(comW, ptr, sizeof(comW) / sizeof(comW[0]) - 1)) + { + strncpyW(list->names[list->numPorts].name, ptr, + sizeof(list->names[list->numPorts].name) / + sizeof(list->names[list->numPorts].name[0])); + TRACE("Adding %s to list\n", + debugstr_w(list->names[list->numPorts].name)); + list->numPorts++; + } + } + TRACE("list->numPorts is %d\n", list->numPorts); + } + ret = (HDEVINFO)list; + } + else + ret = (HDEVINFO)INVALID_HANDLE_VALUE; + if (devices != buf) + HeapFree(GetProcessHeap(), 0, devices); + TRACE("returning %p\n", ret); + return ret; +} + +/*********************************************************************** + * SetupDiGetClassDevsW (SETUPAPI.@) + */ +HDEVINFO WINAPI SetupDiGetClassDevsW( + CONST GUID *class, + LPCWSTR enumstr, + HWND parent, + DWORD flags) +{ + HDEVINFO ret = (HDEVINFO)INVALID_HANDLE_VALUE; + + TRACE("%s %s %p 0x%08lx\n", debugstr_guid(class), debugstr_w(enumstr), + parent, flags); + + if (enumstr) + FIXME(": unimplemented for enumerator strings (%s)\n", + debugstr_w(enumstr)); + else if (flags & DIGCF_ALLCLASSES) + FIXME(": unimplemented for DIGCF_ALLCLASSES\n"); + else + { + if (IsEqualIID(class, &GUID_DEVINTERFACE_COMPORT)) + ret = SETUP_CreateSerialDeviceList(); + else if (IsEqualIID(class, &GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR)) + ret = SETUP_CreateSerialDeviceList(); + else + FIXME("(%s): stub\n", debugstr_guid(class)); + } + return ret; +} + +/*********************************************************************** + * SetupDiEnumDeviceInterfaces (SETUPAPI.@) + */ +BOOL WINAPI SetupDiEnumDeviceInterfaces( + HDEVINFO DeviceInfoSet, + PSP_DEVINFO_DATA DeviceInfoData, + CONST GUID * InterfaceClassGuid, + DWORD MemberIndex, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) +{ + BOOL ret = FALSE; + + TRACE("%p, %p, %s, 0x%08lx, %p\n", DeviceInfoSet, DeviceInfoData, + debugstr_guid(InterfaceClassGuid), MemberIndex, DeviceInterfaceData); + if (!DeviceInterfaceData) + SetLastError(ERROR_INVALID_PARAMETER); + else if (DeviceInfoData) + FIXME(": unimplemented with PSP_DEVINFO_DATA set\n"); + else if (DeviceInfoSet && DeviceInfoSet != (HDEVINFO)INVALID_HANDLE_VALUE) + { + /* FIXME: this assumes the only possible enumeration is of serial + * ports. + */ + SerialPortList *list = (SerialPortList *)DeviceInfoSet; + + if (list->magic == SETUP_SERIAL_PORT_MAGIC) + { + if (MemberIndex >= list->numPorts) + SetLastError(ERROR_NO_MORE_ITEMS); + else + { + DeviceInterfaceData->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + memcpy(&DeviceInterfaceData->InterfaceClassGuid, + &GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR, + sizeof(DeviceInterfaceData->InterfaceClassGuid)); + DeviceInterfaceData->Flags = 0; + /* Note: this appears to be dangerous, passing a private + * pointer a heap-allocated datum to the caller. However, the + * expected lifetime of the device data is the same as the + * HDEVINFO; once that is closed, the data are no longer valid. + */ + DeviceInterfaceData->Reserved = + (ULONG_PTR)&list->names[MemberIndex].name; + ret = TRUE; + } + } + else + SetLastError(ERROR_INVALID_HANDLE); + } + else + SetLastError(ERROR_INVALID_HANDLE); + return ret; +} + +/*********************************************************************** + * SetupDiDestroyDeviceInfoList (SETUPAPI.@) + */ +BOOL WINAPI SetupDiDestroyDeviceInfoList(HDEVINFO devinfo) +{ + BOOL ret = FALSE; + + TRACE("%p\n", devinfo); + if (devinfo && devinfo != (HDEVINFO)INVALID_HANDLE_VALUE) + { + /* FIXME: this assumes the only possible enumeration is of serial + * ports. + */ + SerialPortList *list = (SerialPortList *)devinfo; + + if (list->magic == SETUP_SERIAL_PORT_MAGIC) + { + HeapFree(GetProcessHeap(), 0, list); + ret = TRUE; + } + else + SetLastError(ERROR_INVALID_HANDLE); + } + else + SetLastError(ERROR_INVALID_HANDLE); + return ret; +} + +/*********************************************************************** + * SetupDiGetDeviceInterfaceDetailA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceInterfaceDetailA( + HDEVINFO DeviceInfoSet, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData, + DWORD DeviceInterfaceDetailDataSize, + PDWORD RequiredSize, + PSP_DEVINFO_DATA DeviceInfoData) +{ + BOOL ret = FALSE; + + TRACE("(%p, %p, %p, %ld, %p, %p)\n", DeviceInfoSet, + DeviceInterfaceData, DeviceInterfaceDetailData, + DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData); + if (!DeviceInterfaceData) + SetLastError(ERROR_INVALID_PARAMETER); + else if ((DeviceInterfaceDetailDataSize && !DeviceInterfaceDetailData) || + (DeviceInterfaceDetailData && !DeviceInterfaceDetailDataSize)) + SetLastError(ERROR_INVALID_PARAMETER); + else if (DeviceInfoSet && DeviceInfoSet != (HDEVINFO)INVALID_HANDLE_VALUE) + { + /* FIXME: this assumes the only possible enumeration is of serial + * ports. + */ + SerialPortList *list = (SerialPortList *)DeviceInfoSet; + + if (list->magic == SETUP_SERIAL_PORT_MAGIC) + { + LPCWSTR devName = (LPCWSTR)DeviceInterfaceData->Reserved; + DWORD sizeRequired = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A) + + lstrlenW(devName); + + if (sizeRequired > DeviceInterfaceDetailDataSize) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + if (RequiredSize) + *RequiredSize = sizeRequired; + } + else + { + LPSTR dst = DeviceInterfaceDetailData->DevicePath; + LPCWSTR src = devName; + + /* MSDN claims cbSize must be set by the caller, but it lies */ + DeviceInterfaceDetailData->cbSize = + sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + for ( ; *src; src++, dst++) + *dst = *src; + *dst = '\0'; + TRACE("DevicePath is %s\n", + debugstr_a(DeviceInterfaceDetailData->DevicePath)); + if (DeviceInfoData) + { + DeviceInfoData->cbSize = sizeof(SP_DEVINFO_DATA); + memcpy(&DeviceInfoData->ClassGuid, + &GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR, + sizeof(DeviceInfoData->ClassGuid)); + DeviceInfoData->DevInst = 0; + DeviceInfoData->Reserved = (ULONG_PTR)devName; + } + ret = TRUE; + } + } + else + SetLastError(ERROR_INVALID_HANDLE); + } + else + SetLastError(ERROR_INVALID_HANDLE); + TRACE("Returning %d\n", ret); + return ret; +} + +/*********************************************************************** + * SetupDiGetDeviceInterfaceDetailW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceInterfaceDetailW( + HDEVINFO DeviceInfoSet, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData, + DWORD DeviceInterfaceDetailDataSize, + PDWORD RequiredSize, + PSP_DEVINFO_DATA DeviceInfoData) +{ + FIXME("(%p, %p, %p, %ld, %p, %p): stub\n", DeviceInfoSet, + DeviceInterfaceData, DeviceInterfaceDetailData, + DeviceInterfaceDetailDataSize, RequiredSize, DeviceInfoData); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetDeviceRegistryPropertyA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceRegistryPropertyA( + HDEVINFO devinfo, + PSP_DEVINFO_DATA DeviceInfoData, + DWORD Property, + PDWORD PropertyRegDataType, + PBYTE PropertyBuffer, + DWORD PropertyBufferSize, + PDWORD RequiredSize) +{ + FIXME("%04lx %p %ld %p %p %ld %p\n", (DWORD)devinfo, DeviceInfoData, + Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize, + RequiredSize); + return FALSE; +} + +/*********************************************************************** + * SetupDiInstallClassA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiInstallClassA( + HWND hwndParent, + PCSTR InfFileName, + DWORD Flags, + HSPFILEQ FileQueue) +{ + UNICODE_STRING FileNameW; + BOOL Result; + + if (!RtlCreateUnicodeStringFromAsciiz(&FileNameW, InfFileName)) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + Result = SetupDiInstallClassW(hwndParent, FileNameW.Buffer, Flags, FileQueue); + + RtlFreeUnicodeString(&FileNameW); + + return Result; +} + +static HKEY CreateClassKey(HINF hInf) +{ + WCHAR FullBuffer[MAX_PATH]; + WCHAR Buffer[MAX_PATH]; + DWORD RequiredSize; + HKEY hClassKey; + + if (!SetupGetLineTextW(NULL, + hInf, + Version, + ClassGUID, + Buffer, + MAX_PATH, + &RequiredSize)) + { + return INVALID_HANDLE_VALUE; + } + + lstrcpyW(FullBuffer, ControlClass); + lstrcatW(FullBuffer, Buffer); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + FullBuffer, + 0, + KEY_ALL_ACCESS, + &hClassKey)) + { + if (!SetupGetLineTextW(NULL, + hInf, + Version, + Class, + Buffer, + MAX_PATH, + &RequiredSize)) + { + return INVALID_HANDLE_VALUE; + } + + if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, + FullBuffer, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hClassKey, + NULL)) + { + return INVALID_HANDLE_VALUE; + } + + } + + if (RegSetValueExW(hClassKey, + Class, + 0, + REG_SZ, + (LPBYTE)Buffer, + RequiredSize * sizeof(WCHAR))) + { + RegCloseKey(hClassKey); + RegDeleteKeyW(HKEY_LOCAL_MACHINE, + FullBuffer); + return INVALID_HANDLE_VALUE; + } + + return hClassKey; +} + +/*********************************************************************** + * SetupDiInstallClassW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiInstallClassW( + HWND hwndParent, + PCWSTR InfFileName, + DWORD Flags, + HSPFILEQ FileQueue) +{ + WCHAR SectionName[MAX_PATH]; + DWORD SectionNameLength = 0; + HINF hInf; + BOOL bFileQueueCreated = FALSE; + HKEY hClassKey; + + + FIXME("\n"); + + if ((Flags & DI_NOVCP) && (FileQueue == NULL || FileQueue == INVALID_HANDLE_VALUE)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + /* Open the .inf file */ + hInf = SetupOpenInfFileW(InfFileName, + NULL, + INF_STYLE_WIN4, + NULL); + if (hInf == INVALID_HANDLE_VALUE) + { + + return FALSE; + } + + /* Create or open the class registry key 'HKLM\\CurrentControlSet\\Class\\{GUID}' */ + hClassKey = CreateClassKey(hInf); + if (hClassKey == INVALID_HANDLE_VALUE) + { + SetupCloseInfFile(hInf); + return FALSE; + } + + + /* Try to append a layout file */ +#if 0 + SetupOpenAppendInfFileW(NULL, hInf, NULL); +#endif + + /* Retrieve the actual section name */ + SetupDiGetActualSectionToInstallW(hInf, + ClassInstall32, + SectionName, + MAX_PATH, + &SectionNameLength, + NULL); + +#if 0 + if (!(Flags & DI_NOVCP)) + { + FileQueue = SetupOpenFileQueue(); + if (FileQueue == INVALID_HANDLE_VALUE) + { + SetupCloseInfFile(hInf); + return FALSE; + } + + bFileQueueCreated = TRUE; + + } +#endif + + SetupInstallFromInfSectionW(NULL, + hInf, + SectionName, + SPINST_REGISTRY, + hClassKey, + NULL, + 0, + NULL, + NULL, + INVALID_HANDLE_VALUE, + NULL); + + /* FIXME: More code! */ + + if (bFileQueueCreated) + SetupCloseFileQueue(FileQueue); + + SetupCloseInfFile(hInf); + + return TRUE; +} + + +/*********************************************************************** + * SetupDiOpenClassRegKey (SETUPAPI.@) + */ +HKEY WINAPI SetupDiOpenClassRegKey( + const GUID* ClassGuid, + REGSAM samDesired) +{ + return SetupDiOpenClassRegKeyExW(ClassGuid, samDesired, + DIOCR_INSTALLER, NULL, NULL); +} + + +/*********************************************************************** + * SetupDiOpenClassRegKeyExA (SETUPAPI.@) + */ +HKEY WINAPI SetupDiOpenClassRegKeyExA( + const GUID* ClassGuid, + REGSAM samDesired, + DWORD Flags, + PCSTR MachineName, + PVOID Reserved) +{ + PWSTR MachineNameW = NULL; + HKEY hKey; + + TRACE("\n"); + + if (MachineName) + { + MachineNameW = MultiByteToUnicode(MachineName, CP_ACP); + if (MachineNameW == NULL) + return INVALID_HANDLE_VALUE; + } + + hKey = SetupDiOpenClassRegKeyExW(ClassGuid, samDesired, + Flags, MachineNameW, Reserved); + + if (MachineNameW) + MyFree(MachineNameW); + + return hKey; +} + + +/*********************************************************************** + * SetupDiOpenClassRegKeyExW (SETUPAPI.@) + */ +HKEY WINAPI SetupDiOpenClassRegKeyExW( + const GUID* ClassGuid, + REGSAM samDesired, + DWORD Flags, + PCWSTR MachineName, + PVOID Reserved) +{ + LPWSTR lpGuidString; + HKEY hClassesKey; + HKEY hClassKey; + LPCWSTR lpKeyName; + + if (MachineName != NULL) + { + FIXME("Remote access not supported yet!\n"); + return INVALID_HANDLE_VALUE; + } + + if (Flags == DIOCR_INSTALLER) + { + lpKeyName = ControlClass; + } + else if (Flags == DIOCR_INTERFACE) + { + lpKeyName = DeviceClasses; + } + else + { + ERR("Invalid Flags parameter!\n"); + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + lpKeyName, + 0, + KEY_ALL_ACCESS, + &hClassesKey)) + { + return INVALID_HANDLE_VALUE; + } + + if (ClassGuid == NULL) + return hClassesKey; + + if (UuidToStringW((UUID*)ClassGuid, &lpGuidString) != RPC_S_OK) + { + RegCloseKey(hClassesKey); + return FALSE; + } + + if (RegOpenKeyExW(hClassesKey, + lpGuidString, + 0, + KEY_ALL_ACCESS, + &hClassKey)) + { + RpcStringFreeW(&lpGuidString); + RegCloseKey(hClassesKey); + return FALSE; + } + + RpcStringFreeW(&lpGuidString); + RegCloseKey(hClassesKey); + + return hClassKey; +} + +/*********************************************************************** + * SetupDiOpenDeviceInterfaceW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiOpenDeviceInterfaceW( + HDEVINFO DeviceInfoSet, + PCWSTR DevicePath, + DWORD OpenFlags, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) +{ + FIXME("%p %s %08lx %p\n", + DeviceInfoSet, debugstr_w(DevicePath), OpenFlags, DeviceInterfaceData); + return FALSE; +} + +/*********************************************************************** + * SetupDiOpenDeviceInterfaceA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiOpenDeviceInterfaceA( + HDEVINFO DeviceInfoSet, + PCSTR DevicePath, + DWORD OpenFlags, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) +{ + FIXME("%p %s %08lx %p\n", DeviceInfoSet, + debugstr_a(DevicePath), OpenFlags, DeviceInterfaceData); + return FALSE; +} + +/*********************************************************************** + * SetupDiSetClassInstallParamsA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiSetClassInstallParamsA( + HDEVINFO DeviceInfoSet, + PSP_DEVINFO_DATA DeviceInfoData, + PSP_CLASSINSTALL_HEADER ClassInstallParams, + DWORD ClassInstallParamsSize) +{ + FIXME("%p %p %x %lu\n",DeviceInfoSet, DeviceInfoData, + ClassInstallParams->InstallFunction, ClassInstallParamsSize); + return FALSE; +} + +/*********************************************************************** + * SetupDiCallClassInstaller (SETUPAPI.@) + */ +BOOL WINAPI SetupDiCallClassInstaller( + DWORD InstallFunction, + HDEVINFO DeviceInfoSet, + PSP_DEVINFO_DATA DeviceInfoData) +{ + FIXME("%ld %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetDeviceInstallParamsA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceInstallParamsA( + HDEVINFO DeviceInfoSet, + PSP_DEVINFO_DATA DeviceInfoData, + PSP_DEVINSTALL_PARAMS_A DeviceInstallParams) +{ + FIXME("%p %p %p\n", DeviceInfoSet, DeviceInfoData, DeviceInstallParams); + return FALSE; +} + +/*********************************************************************** + * SetupDiOpenDevRegKey (SETUPAPI.@) + */ +HKEY WINAPI SetupDiOpenDevRegKey( + HDEVINFO DeviceInfoSet, + PSP_DEVINFO_DATA DeviceInfoData, + DWORD Scope, + DWORD HwProfile, + DWORD KeyType, + REGSAM samDesired) +{ + FIXME("%p %p %ld %ld %ld %lx\n", DeviceInfoSet, DeviceInfoData, + Scope, HwProfile, KeyType, samDesired); + return INVALID_HANDLE_VALUE; +} diff --git a/reactos/lib/setupapi/devinst16.c b/reactos/lib/setupapi/devinst16.c index f0c51544218..b746b1aafaf 100644 --- a/reactos/lib/setupapi/devinst16.c +++ b/reactos/lib/setupapi/devinst16.c @@ -1,134 +1,134 @@ -/* - * SetupAPI device installer - * - * Copyright 2000 Andreas Mohr for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#include "windef.h" -#include "winbase.h" -#include "setupx16.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -/*********************************************************************** - * DiGetClassDevs (SETUPX.304) - * Return a list of installed system devices. - * Uses HKLM\\ENUM to list devices. - */ -RETERR16 WINAPI DiGetClassDevs16(LPLPDEVICE_INFO16 lplpdi, - LPCSTR lpszClassName, HWND16 hwndParent, INT16 iFlags) -{ - LPDEVICE_INFO16 lpdi; - - FIXME("(%p, '%s', %04x, %04x), semi-stub.\n", - lplpdi, lpszClassName, hwndParent, iFlags); - lpdi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO16)); - lpdi->cbSize = sizeof(DEVICE_INFO16); - *lplpdi = (LPDEVICE_INFO16)MapLS(lpdi); - return OK; -} - -/*********************************************************************** - * DiBuildCompatDrvList (SETUPX.300) - */ -RETERR16 WINAPI DiBuildCompatDrvList16(LPDEVICE_INFO16 lpdi) -{ - FIXME("(%p): stub\n", lpdi); - lpdi->lpCompatDrvList = NULL; - return FALSE; -} - -/*********************************************************************** - * DiBuildClassDrvList (SETUPX.301) - */ -RETERR16 WINAPI DiBuildClassDrvList16(LPDEVICE_INFO16 lpdi) -{ - FIXME("(%p): stub\n", lpdi); - lpdi->lpCompatDrvList = NULL; - return FALSE; -} - -/*********************************************************************** - * DiCallClassInstaller (SETUPX.308) - */ -RETERR16 WINAPI DiCallClassInstaller16(DI_FUNCTION16 diFctn, LPDEVICE_INFO16 lpdi) -{ - FIXME("(%x, %p): stub\n", diFctn, lpdi); - return FALSE; -} - -/*********************************************************************** - * DiCreateDevRegKey (SETUPX.318) - */ -RETERR16 WINAPI DiCreateDevRegKey16(LPDEVICE_INFO16 lpdi, - VOID* p2, WORD w3, - LPCSTR s4, WORD w5) -{ - FIXME("(%p, %p, %x, %s, %x): stub\n", lpdi, p2, w3, debugstr_a(s4), w5); - return FALSE; -} - -/*********************************************************************** - * DiDeleteDevRegKey (SETUPX.344) - */ -RETERR16 WINAPI DiDeleteDevRegKey16(LPDEVICE_INFO16 lpdi, INT16 iFlags) -{ - FIXME("(%p, %x): stub\n", lpdi, iFlags); - return FALSE; -} - -/*********************************************************************** - * DiCreateDeviceInfo (SETUPX.303) - */ -RETERR16 WINAPI DiCreateDeviceInfo16(LPLPDEVICE_INFO16 lplpdi, - LPCSTR lpszDescription, DWORD dnDevnode, - HKEY16 hkey, LPCSTR lpszRegsubkey, - LPCSTR lpszClassName, HWND16 hwndParent) -{ - LPDEVICE_INFO16 lpdi; - FIXME("(%p %s %08lx %x %s %s %x): stub\n", lplpdi, - debugstr_a(lpszDescription), dnDevnode, hkey, - debugstr_a(lpszRegsubkey), debugstr_a(lpszClassName), hwndParent); - lpdi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO16)); - lpdi->cbSize = sizeof(DEVICE_INFO16); - strcpy(lpdi->szClassName, lpszClassName); - lpdi->hwndParent = hwndParent; - *lplpdi = (LPDEVICE_INFO16)MapLS(lpdi); - return OK; -} - -/*********************************************************************** - * DiDestroyDeviceInfoList (SETUPX.305) - */ -RETERR16 WINAPI DiDestroyDeviceInfoList16(LPDEVICE_INFO16 lpdi) -{ - FIXME("(%p): stub\n", lpdi); - return FALSE; -} - -/*********************************************************************** - * DiOpenDevRegKey (SETUPX.319) - */ -RETERR16 WINAPI DiOpenDevRegKey16(LPDEVICE_INFO16 lpdi, - LPHKEY16 lphk,INT16 iFlags) -{ - FIXME("(%p %p %d): stub\n", lpdi, lphk, iFlags); - return FALSE; -} +/* + * SetupAPI device installer + * + * Copyright 2000 Andreas Mohr for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "setupx16.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/*********************************************************************** + * DiGetClassDevs (SETUPX.304) + * Return a list of installed system devices. + * Uses HKLM\\ENUM to list devices. + */ +RETERR16 WINAPI DiGetClassDevs16(LPLPDEVICE_INFO16 lplpdi, + LPCSTR lpszClassName, HWND16 hwndParent, INT16 iFlags) +{ + LPDEVICE_INFO16 lpdi; + + FIXME("(%p, '%s', %04x, %04x), semi-stub.\n", + lplpdi, lpszClassName, hwndParent, iFlags); + lpdi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO16)); + lpdi->cbSize = sizeof(DEVICE_INFO16); + *lplpdi = (LPDEVICE_INFO16)MapLS(lpdi); + return OK; +} + +/*********************************************************************** + * DiBuildCompatDrvList (SETUPX.300) + */ +RETERR16 WINAPI DiBuildCompatDrvList16(LPDEVICE_INFO16 lpdi) +{ + FIXME("(%p): stub\n", lpdi); + lpdi->lpCompatDrvList = NULL; + return FALSE; +} + +/*********************************************************************** + * DiBuildClassDrvList (SETUPX.301) + */ +RETERR16 WINAPI DiBuildClassDrvList16(LPDEVICE_INFO16 lpdi) +{ + FIXME("(%p): stub\n", lpdi); + lpdi->lpCompatDrvList = NULL; + return FALSE; +} + +/*********************************************************************** + * DiCallClassInstaller (SETUPX.308) + */ +RETERR16 WINAPI DiCallClassInstaller16(DI_FUNCTION16 diFctn, LPDEVICE_INFO16 lpdi) +{ + FIXME("(%x, %p): stub\n", diFctn, lpdi); + return FALSE; +} + +/*********************************************************************** + * DiCreateDevRegKey (SETUPX.318) + */ +RETERR16 WINAPI DiCreateDevRegKey16(LPDEVICE_INFO16 lpdi, + VOID* p2, WORD w3, + LPCSTR s4, WORD w5) +{ + FIXME("(%p, %p, %x, %s, %x): stub\n", lpdi, p2, w3, debugstr_a(s4), w5); + return FALSE; +} + +/*********************************************************************** + * DiDeleteDevRegKey (SETUPX.344) + */ +RETERR16 WINAPI DiDeleteDevRegKey16(LPDEVICE_INFO16 lpdi, INT16 iFlags) +{ + FIXME("(%p, %x): stub\n", lpdi, iFlags); + return FALSE; +} + +/*********************************************************************** + * DiCreateDeviceInfo (SETUPX.303) + */ +RETERR16 WINAPI DiCreateDeviceInfo16(LPLPDEVICE_INFO16 lplpdi, + LPCSTR lpszDescription, DWORD dnDevnode, + HKEY16 hkey, LPCSTR lpszRegsubkey, + LPCSTR lpszClassName, HWND16 hwndParent) +{ + LPDEVICE_INFO16 lpdi; + FIXME("(%p %s %08lx %x %s %s %x): stub\n", lplpdi, + debugstr_a(lpszDescription), dnDevnode, hkey, + debugstr_a(lpszRegsubkey), debugstr_a(lpszClassName), hwndParent); + lpdi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO16)); + lpdi->cbSize = sizeof(DEVICE_INFO16); + strcpy(lpdi->szClassName, lpszClassName); + lpdi->hwndParent = hwndParent; + *lplpdi = (LPDEVICE_INFO16)MapLS(lpdi); + return OK; +} + +/*********************************************************************** + * DiDestroyDeviceInfoList (SETUPX.305) + */ +RETERR16 WINAPI DiDestroyDeviceInfoList16(LPDEVICE_INFO16 lpdi) +{ + FIXME("(%p): stub\n", lpdi); + return FALSE; +} + +/*********************************************************************** + * DiOpenDevRegKey (SETUPX.319) + */ +RETERR16 WINAPI DiOpenDevRegKey16(LPDEVICE_INFO16 lpdi, + LPHKEY16 lphk,INT16 iFlags) +{ + FIXME("(%p %p %d): stub\n", lpdi, lphk, iFlags); + return FALSE; +} diff --git a/reactos/lib/setupapi/dirid.c b/reactos/lib/setupapi/dirid.c index 639da022258..cec3373dfc3 100644 --- a/reactos/lib/setupapi/dirid.c +++ b/reactos/lib/setupapi/dirid.c @@ -1,288 +1,288 @@ -/* - * Directory id handling - * - * Copyright 2002 Alexandre Julliard for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "winternl.h" -#include "winerror.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "setupapi.h" -#include "shlobj.h" -#include "wine/unicode.h" -#include "setupapi_private.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -#define MAX_SYSTEM_DIRID DIRID_PRINTPROCESSOR -#define MIN_CSIDL_DIRID 0x4000 -#define MAX_CSIDL_DIRID 0x403f - -struct user_dirid -{ - int id; - WCHAR *str; -}; - -static int nb_user_dirids; /* number of user dirids in use */ -static int alloc_user_dirids; /* number of allocated user dirids */ -static struct user_dirid *user_dirids; -static const WCHAR *system_dirids[MAX_SYSTEM_DIRID+1]; -static const WCHAR *csidl_dirids[MAX_CSIDL_DIRID-MIN_CSIDL_DIRID+1]; - -/* retrieve the string for unknown dirids */ -static const WCHAR *get_unknown_dirid(void) -{ - static WCHAR *unknown_dirid; - static const WCHAR unknown_str[] = {'\\','u','n','k','n','o','w','n',0}; - - if (!unknown_dirid) - { - UINT len = GetSystemDirectoryW( NULL, 0 ) + strlenW(unknown_str); - if (!(unknown_dirid = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL; - GetSystemDirectoryW( unknown_dirid, len ); - strcatW( unknown_dirid, unknown_str ); - } - return unknown_dirid; -} - -static const WCHAR *get_csidl_dir(DWORD csidl); - -/* create the string for a system dirid */ -static const WCHAR *create_system_dirid( int dirid ) -{ - static const WCHAR Null[] = {0}; - static const WCHAR C_Root[] = {'C',':','\\',0}; - static const WCHAR Drivers[] = {'\\','d','r','i','v','e','r','s',0}; - static const WCHAR Inf[] = {'\\','i','n','f',0}; - static const WCHAR Help[] = {'\\','h','e','l','p',0}; - static const WCHAR Fonts[] = {'\\','f','o','n','t','s',0}; - static const WCHAR Viewers[] = {'\\','v','i','e','w','e','r','s',0}; - static const WCHAR System[] = {'\\','s','y','s','t','e','m',0}; - static const WCHAR Spool[] = {'\\','s','p','o','o','l',0}; - static const WCHAR UserProfile[] = {'U','S','E','R','P','R','O','F','I','L','E',0}; - - WCHAR buffer[MAX_PATH+32], *str; - int len; - - switch(dirid) - { - case DIRID_NULL: - return Null; - case DIRID_WINDOWS: - GetWindowsDirectoryW( buffer, MAX_PATH ); - break; - case DIRID_SYSTEM: - GetSystemDirectoryW( buffer, MAX_PATH ); - break; - case DIRID_DRIVERS: - GetSystemDirectoryW( buffer, MAX_PATH ); - strcatW( buffer, Drivers ); - break; - case DIRID_INF: - GetWindowsDirectoryW( buffer, MAX_PATH ); - strcatW( buffer, Inf ); - break; - case DIRID_HELP: - GetWindowsDirectoryW( buffer, MAX_PATH ); - strcatW( buffer, Help ); - break; - case DIRID_FONTS: - GetWindowsDirectoryW( buffer, MAX_PATH ); - strcatW( buffer, Fonts ); - break; - case DIRID_VIEWERS: - GetSystemDirectoryW( buffer, MAX_PATH ); - strcatW( buffer, Viewers ); - break; - case DIRID_APPS: - return C_Root; /* FIXME */ - case DIRID_SHARED: - GetWindowsDirectoryW( buffer, MAX_PATH ); - break; - case DIRID_BOOT: - return C_Root; /* FIXME */ - case DIRID_SYSTEM16: - GetWindowsDirectoryW( buffer, MAX_PATH ); - strcatW( buffer, System ); - break; - case DIRID_SPOOL: - case DIRID_SPOOLDRIVERS: /* FIXME */ - GetWindowsDirectoryW( buffer, MAX_PATH ); - strcatW( buffer, Spool ); - break; - case DIRID_USERPROFILE: - if (GetEnvironmentVariableW( UserProfile, buffer, MAX_PATH )) break; - return get_csidl_dir(CSIDL_PROFILE); - case DIRID_LOADER: - return C_Root; /* FIXME */ - case DIRID_COLOR: /* FIXME */ - case DIRID_PRINTPROCESSOR: /* FIXME */ - default: - FIXME( "unknown dirid %d\n", dirid ); - return get_unknown_dirid(); - } - len = (strlenW(buffer) + 1) * sizeof(WCHAR); - if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len ); - return str; -} - -static const WCHAR *get_csidl_dir( DWORD csidl ) -{ - WCHAR buffer[MAX_PATH], *str; - int len; - - if (!SHGetSpecialFolderPathW( NULL, buffer, csidl, TRUE )) - { - FIXME( "CSIDL %lx not found\n", csidl ); - return get_unknown_dirid(); - } - len = (strlenW(buffer) + 1) * sizeof(WCHAR); - if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len ); - return str; -} - -/* retrieve the string corresponding to a dirid, or NULL if none */ -const WCHAR *DIRID_get_string( HINF hinf, int dirid ) -{ - int i; - - if (dirid == DIRID_ABSOLUTE || dirid == DIRID_ABSOLUTE_16BIT) dirid = DIRID_NULL; - - if (dirid >= DIRID_USER) - { - for (i = 0; i < nb_user_dirids; i++) - if (user_dirids[i].id == dirid) return user_dirids[i].str; - ERR("user id %d not found\n", dirid ); - return NULL; - } - else if (dirid >= MIN_CSIDL_DIRID) - { - if (dirid > MAX_CSIDL_DIRID) return get_unknown_dirid(); - dirid -= MIN_CSIDL_DIRID; - if (!csidl_dirids[dirid]) csidl_dirids[dirid] = get_csidl_dir( dirid ); - return csidl_dirids[dirid]; - } - else - { - if (dirid > MAX_SYSTEM_DIRID) return get_unknown_dirid(); - if (dirid == DIRID_SRCPATH) return PARSER_get_src_root( hinf ); - if (!system_dirids[dirid]) system_dirids[dirid] = create_system_dirid( dirid ); - return system_dirids[dirid]; - } -} - -/* store a user dirid string */ -static BOOL store_user_dirid( HINF hinf, int id, WCHAR *str ) -{ - int i; - - for (i = 0; i < nb_user_dirids; i++) if (user_dirids[i].id == id) break; - - if (i < nb_user_dirids) HeapFree( GetProcessHeap(), 0, user_dirids[i].str ); - else - { - if (nb_user_dirids >= alloc_user_dirids) - { - int new_size = max( 32, alloc_user_dirids * 2 ); - - struct user_dirid *new; - - if (user_dirids) - new = HeapReAlloc( GetProcessHeap(), 0, user_dirids, - new_size * sizeof(*new) ); - else - new = HeapAlloc( GetProcessHeap(), 0, - new_size * sizeof(*new) ); - - if (!new) return FALSE; - user_dirids = new; - alloc_user_dirids = new_size; - } - nb_user_dirids++; - } - user_dirids[i].id = id; - user_dirids[i].str = str; - TRACE("id %d -> %s\n", id, debugstr_w(str) ); - return TRUE; -} - - -/*********************************************************************** - * SetupSetDirectoryIdA (SETUPAPI.@) - */ -BOOL WINAPI SetupSetDirectoryIdA( HINF hinf, DWORD id, PCSTR dir ) -{ - UNICODE_STRING dirW; - int i; - - if (!id) /* clear everything */ - { - for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str ); - nb_user_dirids = 0; - return TRUE; - } - if (id < DIRID_USER) - { - SetLastError( ERROR_INVALID_PARAMETER ); - return FALSE; - } - - /* duplicate the string */ - if (!RtlCreateUnicodeStringFromAsciiz( &dirW, dir )) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return FALSE; - } - return store_user_dirid( hinf, id, dirW.Buffer ); -} - - -/*********************************************************************** - * SetupSetDirectoryIdW (SETUPAPI.@) - */ -BOOL WINAPI SetupSetDirectoryIdW( HINF hinf, DWORD id, PCWSTR dir ) -{ - int i, len; - WCHAR *str; - - if (!id) /* clear everything */ - { - for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str ); - nb_user_dirids = 0; - return TRUE; - } - if (id < DIRID_USER) - { - SetLastError( ERROR_INVALID_PARAMETER ); - return FALSE; - } - - /* duplicate the string */ - len = (strlenW(dir)+1) * sizeof(WCHAR); - if (!(str = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE; - memcpy( str, dir, len ); - return store_user_dirid( hinf, id, str ); -} +/* + * Directory id handling + * + * Copyright 2002 Alexandre Julliard for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "winerror.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "shlobj.h" +#include "wine/unicode.h" +#include "setupapi_private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +#define MAX_SYSTEM_DIRID DIRID_PRINTPROCESSOR +#define MIN_CSIDL_DIRID 0x4000 +#define MAX_CSIDL_DIRID 0x403f + +struct user_dirid +{ + int id; + WCHAR *str; +}; + +static int nb_user_dirids; /* number of user dirids in use */ +static int alloc_user_dirids; /* number of allocated user dirids */ +static struct user_dirid *user_dirids; +static const WCHAR *system_dirids[MAX_SYSTEM_DIRID+1]; +static const WCHAR *csidl_dirids[MAX_CSIDL_DIRID-MIN_CSIDL_DIRID+1]; + +/* retrieve the string for unknown dirids */ +static const WCHAR *get_unknown_dirid(void) +{ + static WCHAR *unknown_dirid; + static const WCHAR unknown_str[] = {'\\','u','n','k','n','o','w','n',0}; + + if (!unknown_dirid) + { + UINT len = GetSystemDirectoryW( NULL, 0 ) + strlenW(unknown_str); + if (!(unknown_dirid = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL; + GetSystemDirectoryW( unknown_dirid, len ); + strcatW( unknown_dirid, unknown_str ); + } + return unknown_dirid; +} + +static const WCHAR *get_csidl_dir(DWORD csidl); + +/* create the string for a system dirid */ +static const WCHAR *create_system_dirid( int dirid ) +{ + static const WCHAR Null[] = {0}; + static const WCHAR C_Root[] = {'C',':','\\',0}; + static const WCHAR Drivers[] = {'\\','d','r','i','v','e','r','s',0}; + static const WCHAR Inf[] = {'\\','i','n','f',0}; + static const WCHAR Help[] = {'\\','h','e','l','p',0}; + static const WCHAR Fonts[] = {'\\','f','o','n','t','s',0}; + static const WCHAR Viewers[] = {'\\','v','i','e','w','e','r','s',0}; + static const WCHAR System[] = {'\\','s','y','s','t','e','m',0}; + static const WCHAR Spool[] = {'\\','s','p','o','o','l',0}; + static const WCHAR UserProfile[] = {'U','S','E','R','P','R','O','F','I','L','E',0}; + + WCHAR buffer[MAX_PATH+32], *str; + int len; + + switch(dirid) + { + case DIRID_NULL: + return Null; + case DIRID_WINDOWS: + GetWindowsDirectoryW( buffer, MAX_PATH ); + break; + case DIRID_SYSTEM: + GetSystemDirectoryW( buffer, MAX_PATH ); + break; + case DIRID_DRIVERS: + GetSystemDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Drivers ); + break; + case DIRID_INF: + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Inf ); + break; + case DIRID_HELP: + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Help ); + break; + case DIRID_FONTS: + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Fonts ); + break; + case DIRID_VIEWERS: + GetSystemDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Viewers ); + break; + case DIRID_APPS: + return C_Root; /* FIXME */ + case DIRID_SHARED: + GetWindowsDirectoryW( buffer, MAX_PATH ); + break; + case DIRID_BOOT: + return C_Root; /* FIXME */ + case DIRID_SYSTEM16: + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, System ); + break; + case DIRID_SPOOL: + case DIRID_SPOOLDRIVERS: /* FIXME */ + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Spool ); + break; + case DIRID_USERPROFILE: + if (GetEnvironmentVariableW( UserProfile, buffer, MAX_PATH )) break; + return get_csidl_dir(CSIDL_PROFILE); + case DIRID_LOADER: + return C_Root; /* FIXME */ + case DIRID_COLOR: /* FIXME */ + case DIRID_PRINTPROCESSOR: /* FIXME */ + default: + FIXME( "unknown dirid %d\n", dirid ); + return get_unknown_dirid(); + } + len = (strlenW(buffer) + 1) * sizeof(WCHAR); + if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len ); + return str; +} + +static const WCHAR *get_csidl_dir( DWORD csidl ) +{ + WCHAR buffer[MAX_PATH], *str; + int len; + + if (!SHGetSpecialFolderPathW( NULL, buffer, csidl, TRUE )) + { + FIXME( "CSIDL %lx not found\n", csidl ); + return get_unknown_dirid(); + } + len = (strlenW(buffer) + 1) * sizeof(WCHAR); + if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len ); + return str; +} + +/* retrieve the string corresponding to a dirid, or NULL if none */ +const WCHAR *DIRID_get_string( HINF hinf, int dirid ) +{ + int i; + + if (dirid == DIRID_ABSOLUTE || dirid == DIRID_ABSOLUTE_16BIT) dirid = DIRID_NULL; + + if (dirid >= DIRID_USER) + { + for (i = 0; i < nb_user_dirids; i++) + if (user_dirids[i].id == dirid) return user_dirids[i].str; + ERR("user id %d not found\n", dirid ); + return NULL; + } + else if (dirid >= MIN_CSIDL_DIRID) + { + if (dirid > MAX_CSIDL_DIRID) return get_unknown_dirid(); + dirid -= MIN_CSIDL_DIRID; + if (!csidl_dirids[dirid]) csidl_dirids[dirid] = get_csidl_dir( dirid ); + return csidl_dirids[dirid]; + } + else + { + if (dirid > MAX_SYSTEM_DIRID) return get_unknown_dirid(); + if (dirid == DIRID_SRCPATH) return PARSER_get_src_root( hinf ); + if (!system_dirids[dirid]) system_dirids[dirid] = create_system_dirid( dirid ); + return system_dirids[dirid]; + } +} + +/* store a user dirid string */ +static BOOL store_user_dirid( HINF hinf, int id, WCHAR *str ) +{ + int i; + + for (i = 0; i < nb_user_dirids; i++) if (user_dirids[i].id == id) break; + + if (i < nb_user_dirids) HeapFree( GetProcessHeap(), 0, user_dirids[i].str ); + else + { + if (nb_user_dirids >= alloc_user_dirids) + { + int new_size = max( 32, alloc_user_dirids * 2 ); + + struct user_dirid *new; + + if (user_dirids) + new = HeapReAlloc( GetProcessHeap(), 0, user_dirids, + new_size * sizeof(*new) ); + else + new = HeapAlloc( GetProcessHeap(), 0, + new_size * sizeof(*new) ); + + if (!new) return FALSE; + user_dirids = new; + alloc_user_dirids = new_size; + } + nb_user_dirids++; + } + user_dirids[i].id = id; + user_dirids[i].str = str; + TRACE("id %d -> %s\n", id, debugstr_w(str) ); + return TRUE; +} + + +/*********************************************************************** + * SetupSetDirectoryIdA (SETUPAPI.@) + */ +BOOL WINAPI SetupSetDirectoryIdA( HINF hinf, DWORD id, PCSTR dir ) +{ + UNICODE_STRING dirW; + int i; + + if (!id) /* clear everything */ + { + for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str ); + nb_user_dirids = 0; + return TRUE; + } + if (id < DIRID_USER) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + /* duplicate the string */ + if (!RtlCreateUnicodeStringFromAsciiz( &dirW, dir )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + return store_user_dirid( hinf, id, dirW.Buffer ); +} + + +/*********************************************************************** + * SetupSetDirectoryIdW (SETUPAPI.@) + */ +BOOL WINAPI SetupSetDirectoryIdW( HINF hinf, DWORD id, PCWSTR dir ) +{ + int i, len; + WCHAR *str; + + if (!id) /* clear everything */ + { + for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str ); + nb_user_dirids = 0; + return TRUE; + } + if (id < DIRID_USER) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + /* duplicate the string */ + len = (strlenW(dir)+1) * sizeof(WCHAR); + if (!(str = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE; + memcpy( str, dir, len ); + return store_user_dirid( hinf, id, str ); +} diff --git a/reactos/lib/setupapi/diskspace.c b/reactos/lib/setupapi/diskspace.c index 72c69280391..4bff4c66f36 100644 --- a/reactos/lib/setupapi/diskspace.c +++ b/reactos/lib/setupapi/diskspace.c @@ -1,153 +1,153 @@ -/* - * SetupAPI DiskSpace functions - * - * Copyright 2004 CodeWeavers (Aric Stewart) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#include "windef.h" -#include "winbase.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "winreg.h" -#include "setupapi.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -typedef struct { - WCHAR lpzName[20]; - LONGLONG dwFreeSpace; - LONGLONG dwWantedSpace; -} DRIVE_ENTRY, *LPDRIVE_ENTRY; - -typedef struct { - DWORD dwDriveCount; - DRIVE_ENTRY Drives[26]; -} DISKSPACELIST, *LPDISKSPACELIST; - - -/*********************************************************************** - * SetupCreateDiskSpaceListW (SETUPAPI.@) - */ -HDSKSPC WINAPI SetupCreateDiskSpaceListW(PVOID Reserved1, DWORD Reserved2, UINT Flags) -{ - WCHAR drives[255]; - DWORD rc; - WCHAR *ptr; - LPDISKSPACELIST list=NULL; - - rc = GetLogicalDriveStringsW(255,drives); - - if (rc == 0) - return NULL; - - list = (LPDISKSPACELIST)HeapAlloc(GetProcessHeap(),0,sizeof(DISKSPACELIST)); - - list->dwDriveCount = 0; - - ptr = drives; - - while (*ptr) - { - DWORD type = GetDriveTypeW(ptr); - DWORD len; - if (type == DRIVE_FIXED) - { - DWORD clusters; - DWORD sectors; - DWORD bytes; - DWORD total; - lstrcpyW(list->Drives[list->dwDriveCount].lpzName,ptr); - GetDiskFreeSpaceW(ptr,§ors,&bytes,&clusters,&total); - list->Drives[list->dwDriveCount].dwFreeSpace = clusters * sectors * - bytes; - list->Drives[list->dwDriveCount].dwWantedSpace = 0; - list->dwDriveCount++; - } - len = lstrlenW(ptr); - len++; - ptr+=sizeof(WCHAR)*len; - } - return (HANDLE)list; -} - - -/*********************************************************************** - * SetupCreateDiskSpaceListA (SETUPAPI.@) - */ -HDSKSPC WINAPI SetupCreateDiskSpaceListA(PVOID Reserved1, DWORD Reserved2, UINT Flags) -{ - return SetupCreateDiskSpaceListW( Reserved1, Reserved2, Flags ); -} - - -/*********************************************************************** - * SetupAddInstallSectionToDiskSpaceListA (SETUPAPI.@) - */ -BOOL WINAPI SetupAddInstallSectionToDiskSpaceListA(HDSKSPC DiskSpace, - HINF InfHandle, HINF LayoutInfHandle, - LPSTR SectionName, PVOID Reserved1, UINT Reserved2) -{ - FIXME ("Stub\n"); - return TRUE; -} - -/*********************************************************************** -* SetupQuerySpaceRequiredOnDriveA (SETUPAPI.@) -*/ -BOOL WINAPI SetupQuerySpaceRequiredOnDriveA(HDSKSPC DiskSpace, - LPSTR DriveSpec, LONGLONG* SpaceRequired, - PVOID Reserved1, UINT Reserved2) -{ - WCHAR driveW[20]; - unsigned int i; - LPDISKSPACELIST list = (LPDISKSPACELIST)DiskSpace; - BOOL rc = FALSE; - static const WCHAR bkslsh[]= {'\\',0}; - - MultiByteToWideChar(CP_ACP,0,DriveSpec,-1,driveW,20); - - lstrcatW(driveW,bkslsh); - - TRACE("Looking for drive %s\n",debugstr_w(driveW)); - - for (i = 0; i < list->dwDriveCount; i++) - { - TRACE("checking drive %s\n",debugstr_w(list->Drives[i].lpzName)); - if (lstrcmpW(driveW,list->Drives[i].lpzName)==0) - { - rc = TRUE; - *SpaceRequired = list->Drives[i].dwWantedSpace; - break; - } - } - - return rc; -} - -/*********************************************************************** -* SetupDestroyDiskSpaceList (SETUPAPI.@) -*/ -BOOL WINAPI SetupDestroyDiskSpaceList(HDSKSPC DiskSpace) -{ - LPDISKSPACELIST list = (LPDISKSPACELIST)DiskSpace; - HeapFree(GetProcessHeap(),0,list); - return TRUE; -} +/* + * SetupAPI DiskSpace functions + * + * Copyright 2004 CodeWeavers (Aric Stewart) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "setupapi.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +typedef struct { + WCHAR lpzName[20]; + LONGLONG dwFreeSpace; + LONGLONG dwWantedSpace; +} DRIVE_ENTRY, *LPDRIVE_ENTRY; + +typedef struct { + DWORD dwDriveCount; + DRIVE_ENTRY Drives[26]; +} DISKSPACELIST, *LPDISKSPACELIST; + + +/*********************************************************************** + * SetupCreateDiskSpaceListW (SETUPAPI.@) + */ +HDSKSPC WINAPI SetupCreateDiskSpaceListW(PVOID Reserved1, DWORD Reserved2, UINT Flags) +{ + WCHAR drives[255]; + DWORD rc; + WCHAR *ptr; + LPDISKSPACELIST list=NULL; + + rc = GetLogicalDriveStringsW(255,drives); + + if (rc == 0) + return NULL; + + list = (LPDISKSPACELIST)HeapAlloc(GetProcessHeap(),0,sizeof(DISKSPACELIST)); + + list->dwDriveCount = 0; + + ptr = drives; + + while (*ptr) + { + DWORD type = GetDriveTypeW(ptr); + DWORD len; + if (type == DRIVE_FIXED) + { + DWORD clusters; + DWORD sectors; + DWORD bytes; + DWORD total; + lstrcpyW(list->Drives[list->dwDriveCount].lpzName,ptr); + GetDiskFreeSpaceW(ptr,§ors,&bytes,&clusters,&total); + list->Drives[list->dwDriveCount].dwFreeSpace = clusters * sectors * + bytes; + list->Drives[list->dwDriveCount].dwWantedSpace = 0; + list->dwDriveCount++; + } + len = lstrlenW(ptr); + len++; + ptr+=sizeof(WCHAR)*len; + } + return (HANDLE)list; +} + + +/*********************************************************************** + * SetupCreateDiskSpaceListA (SETUPAPI.@) + */ +HDSKSPC WINAPI SetupCreateDiskSpaceListA(PVOID Reserved1, DWORD Reserved2, UINT Flags) +{ + return SetupCreateDiskSpaceListW( Reserved1, Reserved2, Flags ); +} + + +/*********************************************************************** + * SetupAddInstallSectionToDiskSpaceListA (SETUPAPI.@) + */ +BOOL WINAPI SetupAddInstallSectionToDiskSpaceListA(HDSKSPC DiskSpace, + HINF InfHandle, HINF LayoutInfHandle, + LPSTR SectionName, PVOID Reserved1, UINT Reserved2) +{ + FIXME ("Stub\n"); + return TRUE; +} + +/*********************************************************************** +* SetupQuerySpaceRequiredOnDriveA (SETUPAPI.@) +*/ +BOOL WINAPI SetupQuerySpaceRequiredOnDriveA(HDSKSPC DiskSpace, + LPSTR DriveSpec, LONGLONG* SpaceRequired, + PVOID Reserved1, UINT Reserved2) +{ + WCHAR driveW[20]; + unsigned int i; + LPDISKSPACELIST list = (LPDISKSPACELIST)DiskSpace; + BOOL rc = FALSE; + static const WCHAR bkslsh[]= {'\\',0}; + + MultiByteToWideChar(CP_ACP,0,DriveSpec,-1,driveW,20); + + lstrcatW(driveW,bkslsh); + + TRACE("Looking for drive %s\n",debugstr_w(driveW)); + + for (i = 0; i < list->dwDriveCount; i++) + { + TRACE("checking drive %s\n",debugstr_w(list->Drives[i].lpzName)); + if (lstrcmpW(driveW,list->Drives[i].lpzName)==0) + { + rc = TRUE; + *SpaceRequired = list->Drives[i].dwWantedSpace; + break; + } + } + + return rc; +} + +/*********************************************************************** +* SetupDestroyDiskSpaceList (SETUPAPI.@) +*/ +BOOL WINAPI SetupDestroyDiskSpaceList(HDSKSPC DiskSpace) +{ + LPDISKSPACELIST list = (LPDISKSPACELIST)DiskSpace; + HeapFree(GetProcessHeap(),0,list); + return TRUE; +} diff --git a/reactos/lib/setupapi/infparse.c b/reactos/lib/setupapi/infparse.c index c508db613ae..467f3760f63 100644 --- a/reactos/lib/setupapi/infparse.c +++ b/reactos/lib/setupapi/infparse.c @@ -1,198 +1,198 @@ -/* - * SetupX .inf file parsing functions - * - * Copyright 2000 Andreas Mohr for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * FIXME: - * - return values ??? - * - this should be reimplemented at some point to have its own - * file parsing instead of using profile functions, - * as some SETUPX exports probably demand that - * (IpSaveRestorePosition, IpFindNextMatchLine, ...). - */ - -#include -#include - -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "winternl.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "setupapi.h" -#include "setupx16.h" -#include "setupapi_private.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -#define MAX_HANDLES 16384 -#define FIRST_HANDLE 32 - -static HINF handles[MAX_HANDLES]; - - -static RETERR16 alloc_hinf16( HINF hinf, HINF16 *hinf16 ) -{ - int i; - for (i = 0; i < MAX_HANDLES; i++) - { - if (!handles[i]) - { - handles[i] = hinf; - *hinf16 = i + FIRST_HANDLE; - return OK; - } - } - return ERR_IP_OUT_OF_HANDLES; -} - -static HINF get_hinf( HINF16 hinf16 ) -{ - int idx = hinf16 - FIRST_HANDLE; - if (idx < 0 || idx >= MAX_HANDLES) return 0; - return handles[idx]; -} - - -static HINF free_hinf16( HINF16 hinf16 ) -{ - HINF ret; - int idx = hinf16 - FIRST_HANDLE; - - if (idx < 0 || idx >= MAX_HANDLES) return 0; - ret = handles[idx]; - handles[idx] = 0; - return ret; -} - -/* convert last error code to a RETERR16 value */ -static RETERR16 get_last_error(void) -{ - switch(GetLastError()) - { - case ERROR_EXPECTED_SECTION_NAME: - case ERROR_BAD_SECTION_NAME_LINE: - case ERROR_SECTION_NAME_TOO_LONG: return ERR_IP_INVALID_SECT_NAME; - case ERROR_SECTION_NOT_FOUND: return ERR_IP_SECT_NOT_FOUND; - case ERROR_LINE_NOT_FOUND: return ERR_IP_LINE_NOT_FOUND; - default: return IP_ERROR; /* FIXME */ - } -} - - -/*********************************************************************** - * IpOpen (SETUPX.2) - * - */ -RETERR16 WINAPI IpOpen16( LPCSTR filename, HINF16 *hinf16 ) -{ - HINF hinf = SetupOpenInfFileA( filename, NULL, INF_STYLE_WIN4, NULL ); - if (hinf == (HINF)INVALID_HANDLE_VALUE) return get_last_error(); - return alloc_hinf16( hinf, hinf16 ); -} - - -/*********************************************************************** - * IpClose (SETUPX.4) - */ -RETERR16 WINAPI IpClose16( HINF16 hinf16 ) -{ - HINF hinf = free_hinf16( hinf16 ); - if (!hinf) return ERR_IP_INVALID_HINF; - SetupCloseInfFile( hinf ); - return OK; -} - - -/*********************************************************************** - * IpGetProfileString (SETUPX.210) - */ -RETERR16 WINAPI IpGetProfileString16( HINF16 hinf16, LPCSTR section, LPCSTR entry, - LPSTR buffer, WORD buflen ) -{ - DWORD required_size; - HINF hinf = get_hinf( hinf16 ); - - if (!hinf) return ERR_IP_INVALID_HINF; - if (!SetupGetLineTextA( NULL, hinf, section, entry, buffer, buflen, &required_size )) - return get_last_error(); - TRACE("%p: section %s entry %s ret %s\n", - hinf, debugstr_a(section), debugstr_a(entry), debugstr_a(buffer) ); - return OK; -} - - -/*********************************************************************** - * GenFormStrWithoutPlaceHolders (SETUPX.103) - * - * ought to be pretty much implemented, I guess... - */ -void WINAPI GenFormStrWithoutPlaceHolders16( LPSTR dst, LPCSTR src, HINF16 hinf16 ) -{ - UNICODE_STRING srcW; - HINF hinf = get_hinf( hinf16 ); - - if (!hinf) return; - - if (!RtlCreateUnicodeStringFromAsciiz( &srcW, src )) return; - PARSER_string_substA( hinf, srcW.Buffer, dst, MAX_INF_STRING_LENGTH ); - RtlFreeUnicodeString( &srcW ); - TRACE( "%s -> %s\n", debugstr_a(src), debugstr_a(dst) ); -} - -/*********************************************************************** - * GenInstall (SETUPX.101) - * - * generic installer function for .INF file sections - * - * This is not perfect - patch whenever you can ! - * - * wFlags == GENINSTALL_DO_xxx - * e.g. NetMeeting: - * first call GENINSTALL_DO_REGSRCPATH | GENINSTALL_DO_FILES, - * second call GENINSTALL_DO_LOGCONFIG | CFGAUTO | INI2REG | REG | INI - */ -RETERR16 WINAPI GenInstall16( HINF16 hinf16, LPCSTR section, WORD genflags ) -{ - UINT flags = 0; - HINF hinf = get_hinf( hinf16 ); - RETERR16 ret = OK; - void *context; - - if (!hinf) return ERR_IP_INVALID_HINF; - - if (genflags & GENINSTALL_DO_FILES) flags |= SPINST_FILES; - if (genflags & GENINSTALL_DO_INI) flags |= SPINST_INIFILES; - if (genflags & GENINSTALL_DO_REG) flags |= SPINST_REGISTRY; - if (genflags & GENINSTALL_DO_INI2REG) flags |= SPINST_INI2REG; - if (genflags & GENINSTALL_DO_LOGCONFIG) flags |= SPINST_LOGCONFIG; - if (genflags & GENINSTALL_DO_REGSRCPATH) FIXME( "unsupported flag: GENINSTALL_DO_REGSRCPATH\n" ); - if (genflags & GENINSTALL_DO_CFGAUTO) FIXME( "unsupported flag: GENINSTALL_DO_CFGAUTO\n" ); - if (genflags & GENINSTALL_DO_PERUSER) FIXME( "unsupported flag: GENINSTALL_DO_PERUSER\n" ); - - context = SetupInitDefaultQueueCallback( 0 ); - if (!SetupInstallFromInfSectionA( 0, hinf, section, flags, 0, NULL, - SP_COPY_NEWER_OR_SAME, SetupDefaultQueueCallbackA, - context, 0, 0 )) - ret = get_last_error(); - - SetupTermDefaultQueueCallback( context ); - return ret; -} +/* + * SetupX .inf file parsing functions + * + * Copyright 2000 Andreas Mohr for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * FIXME: + * - return values ??? + * - this should be reimplemented at some point to have its own + * file parsing instead of using profile functions, + * as some SETUPX exports probably demand that + * (IpSaveRestorePosition, IpFindNextMatchLine, ...). + */ + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "setupx16.h" +#include "setupapi_private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +#define MAX_HANDLES 16384 +#define FIRST_HANDLE 32 + +static HINF handles[MAX_HANDLES]; + + +static RETERR16 alloc_hinf16( HINF hinf, HINF16 *hinf16 ) +{ + int i; + for (i = 0; i < MAX_HANDLES; i++) + { + if (!handles[i]) + { + handles[i] = hinf; + *hinf16 = i + FIRST_HANDLE; + return OK; + } + } + return ERR_IP_OUT_OF_HANDLES; +} + +static HINF get_hinf( HINF16 hinf16 ) +{ + int idx = hinf16 - FIRST_HANDLE; + if (idx < 0 || idx >= MAX_HANDLES) return 0; + return handles[idx]; +} + + +static HINF free_hinf16( HINF16 hinf16 ) +{ + HINF ret; + int idx = hinf16 - FIRST_HANDLE; + + if (idx < 0 || idx >= MAX_HANDLES) return 0; + ret = handles[idx]; + handles[idx] = 0; + return ret; +} + +/* convert last error code to a RETERR16 value */ +static RETERR16 get_last_error(void) +{ + switch(GetLastError()) + { + case ERROR_EXPECTED_SECTION_NAME: + case ERROR_BAD_SECTION_NAME_LINE: + case ERROR_SECTION_NAME_TOO_LONG: return ERR_IP_INVALID_SECT_NAME; + case ERROR_SECTION_NOT_FOUND: return ERR_IP_SECT_NOT_FOUND; + case ERROR_LINE_NOT_FOUND: return ERR_IP_LINE_NOT_FOUND; + default: return IP_ERROR; /* FIXME */ + } +} + + +/*********************************************************************** + * IpOpen (SETUPX.2) + * + */ +RETERR16 WINAPI IpOpen16( LPCSTR filename, HINF16 *hinf16 ) +{ + HINF hinf = SetupOpenInfFileA( filename, NULL, INF_STYLE_WIN4, NULL ); + if (hinf == (HINF)INVALID_HANDLE_VALUE) return get_last_error(); + return alloc_hinf16( hinf, hinf16 ); +} + + +/*********************************************************************** + * IpClose (SETUPX.4) + */ +RETERR16 WINAPI IpClose16( HINF16 hinf16 ) +{ + HINF hinf = free_hinf16( hinf16 ); + if (!hinf) return ERR_IP_INVALID_HINF; + SetupCloseInfFile( hinf ); + return OK; +} + + +/*********************************************************************** + * IpGetProfileString (SETUPX.210) + */ +RETERR16 WINAPI IpGetProfileString16( HINF16 hinf16, LPCSTR section, LPCSTR entry, + LPSTR buffer, WORD buflen ) +{ + DWORD required_size; + HINF hinf = get_hinf( hinf16 ); + + if (!hinf) return ERR_IP_INVALID_HINF; + if (!SetupGetLineTextA( NULL, hinf, section, entry, buffer, buflen, &required_size )) + return get_last_error(); + TRACE("%p: section %s entry %s ret %s\n", + hinf, debugstr_a(section), debugstr_a(entry), debugstr_a(buffer) ); + return OK; +} + + +/*********************************************************************** + * GenFormStrWithoutPlaceHolders (SETUPX.103) + * + * ought to be pretty much implemented, I guess... + */ +void WINAPI GenFormStrWithoutPlaceHolders16( LPSTR dst, LPCSTR src, HINF16 hinf16 ) +{ + UNICODE_STRING srcW; + HINF hinf = get_hinf( hinf16 ); + + if (!hinf) return; + + if (!RtlCreateUnicodeStringFromAsciiz( &srcW, src )) return; + PARSER_string_substA( hinf, srcW.Buffer, dst, MAX_INF_STRING_LENGTH ); + RtlFreeUnicodeString( &srcW ); + TRACE( "%s -> %s\n", debugstr_a(src), debugstr_a(dst) ); +} + +/*********************************************************************** + * GenInstall (SETUPX.101) + * + * generic installer function for .INF file sections + * + * This is not perfect - patch whenever you can ! + * + * wFlags == GENINSTALL_DO_xxx + * e.g. NetMeeting: + * first call GENINSTALL_DO_REGSRCPATH | GENINSTALL_DO_FILES, + * second call GENINSTALL_DO_LOGCONFIG | CFGAUTO | INI2REG | REG | INI + */ +RETERR16 WINAPI GenInstall16( HINF16 hinf16, LPCSTR section, WORD genflags ) +{ + UINT flags = 0; + HINF hinf = get_hinf( hinf16 ); + RETERR16 ret = OK; + void *context; + + if (!hinf) return ERR_IP_INVALID_HINF; + + if (genflags & GENINSTALL_DO_FILES) flags |= SPINST_FILES; + if (genflags & GENINSTALL_DO_INI) flags |= SPINST_INIFILES; + if (genflags & GENINSTALL_DO_REG) flags |= SPINST_REGISTRY; + if (genflags & GENINSTALL_DO_INI2REG) flags |= SPINST_INI2REG; + if (genflags & GENINSTALL_DO_LOGCONFIG) flags |= SPINST_LOGCONFIG; + if (genflags & GENINSTALL_DO_REGSRCPATH) FIXME( "unsupported flag: GENINSTALL_DO_REGSRCPATH\n" ); + if (genflags & GENINSTALL_DO_CFGAUTO) FIXME( "unsupported flag: GENINSTALL_DO_CFGAUTO\n" ); + if (genflags & GENINSTALL_DO_PERUSER) FIXME( "unsupported flag: GENINSTALL_DO_PERUSER\n" ); + + context = SetupInitDefaultQueueCallback( 0 ); + if (!SetupInstallFromInfSectionA( 0, hinf, section, flags, 0, NULL, + SP_COPY_NEWER_OR_SAME, SetupDefaultQueueCallbackA, + context, 0, 0 )) + ret = get_last_error(); + + SetupTermDefaultQueueCallback( context ); + return ret; +} diff --git a/reactos/lib/setupapi/install.c b/reactos/lib/setupapi/install.c index 344e91934ce..5215d1f096c 100644 --- a/reactos/lib/setupapi/install.c +++ b/reactos/lib/setupapi/install.c @@ -1,955 +1,955 @@ -/* - * Setupapi install routines - * - * Copyright 2002 Alexandre Julliard for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "winternl.h" -#include "winerror.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "setupapi.h" -#include "setupapi_private.h" -#include "wine/unicode.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -/* info passed to callback functions dealing with files */ -struct files_callback_info -{ - HSPFILEQ queue; - PCWSTR src_root; - UINT copy_flags; - HINF layout; -}; - -/* info passed to callback functions dealing with the registry */ -struct registry_callback_info -{ - HKEY default_root; - BOOL delete; -}; - -/* info passed to callback functions dealing with registering dlls */ -struct register_dll_info -{ - PSP_FILE_CALLBACK_W callback; - PVOID callback_context; - BOOL unregister; -}; - -typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg ); - -/* Unicode constants */ -static const WCHAR CopyFiles[] = {'C','o','p','y','F','i','l','e','s',0}; -static const WCHAR DelFiles[] = {'D','e','l','F','i','l','e','s',0}; -static const WCHAR RenFiles[] = {'R','e','n','F','i','l','e','s',0}; -static const WCHAR Ini2Reg[] = {'I','n','i','2','R','e','g',0}; -static const WCHAR LogConf[] = {'L','o','g','C','o','n','f',0}; -static const WCHAR AddReg[] = {'A','d','d','R','e','g',0}; -static const WCHAR DelReg[] = {'D','e','l','R','e','g',0}; -static const WCHAR BitReg[] = {'B','i','t','R','e','g',0}; -static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0}; -static const WCHAR CopyINF[] = {'C','o','p','y','I','N','F',0}; -static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0}; -static const WCHAR RegisterDlls[] = {'R','e','g','i','s','t','e','r','D','l','l','s',0}; -static const WCHAR UnregisterDlls[] = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0}; -static const WCHAR ProfileItems[] = {'P','r','o','f','i','l','e','I','t','e','m','s',0}; - - -/*********************************************************************** - * get_field_string - * - * Retrieve the contents of a field, dynamically growing the buffer if necessary. - */ -static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer, - WCHAR *static_buffer, DWORD *size ) -{ - DWORD required; - - if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer; - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) - { - /* now grow the buffer */ - if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer ); - if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL; - *size = required; - if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer; - } - if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer ); - return NULL; -} - - -/*********************************************************************** - * copy_files_callback - * - * Called once for each CopyFiles entry in a given section. - */ -static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg ) -{ - struct files_callback_info *info = arg; - - if (field[0] == '@') /* special case: copy single file */ - SetupQueueDefaultCopyW( info->queue, info->layout, info->src_root, NULL, field, info->copy_flags ); - else - SetupQueueCopySectionW( info->queue, info->src_root, info->layout, hinf, field, info->copy_flags ); - return TRUE; -} - - -/*********************************************************************** - * delete_files_callback - * - * Called once for each DelFiles entry in a given section. - */ -static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg ) -{ - struct files_callback_info *info = arg; - SetupQueueDeleteSectionW( info->queue, hinf, 0, field ); - return TRUE; -} - - -/*********************************************************************** - * rename_files_callback - * - * Called once for each RenFiles entry in a given section. - */ -static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg ) -{ - struct files_callback_info *info = arg; - SetupQueueRenameSectionW( info->queue, hinf, 0, field ); - return TRUE; -} - - -/*********************************************************************** - * get_root_key - * - * Retrieve the registry root key from its name. - */ -static HKEY get_root_key( const WCHAR *name, HKEY def_root ) -{ - static const WCHAR HKCR[] = {'H','K','C','R',0}; - static const WCHAR HKCU[] = {'H','K','C','U',0}; - static const WCHAR HKLM[] = {'H','K','L','M',0}; - static const WCHAR HKU[] = {'H','K','U',0}; - static const WCHAR HKR[] = {'H','K','R',0}; - - if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT; - if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER; - if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE; - if (!strcmpiW( name, HKU )) return HKEY_USERS; - if (!strcmpiW( name, HKR )) return def_root; - return 0; -} - - -/*********************************************************************** - * append_multi_sz_value - * - * Append a multisz string to a multisz registry value. - */ -static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings, - DWORD str_size ) -{ - DWORD size, type, total; - WCHAR *buffer, *p; - - if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return; - if (type != REG_MULTI_SZ) return; - - if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return; - if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done; - - /* compare each string against all the existing ones */ - total = size; - while (*strings) - { - int len = strlenW(strings) + 1; - - for (p = buffer; *p; p += strlenW(p) + 1) - if (!strcmpiW( p, strings )) break; - - if (!*p) /* not found, need to append it */ - { - memcpy( p, strings, len * sizeof(WCHAR) ); - p[len] = 0; - total += len; - } - strings += len; - } - if (total != size) - { - TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) ); - RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total ); - } - done: - HeapFree( GetProcessHeap(), 0, buffer ); -} - - -/*********************************************************************** - * delete_multi_sz_value - * - * Remove a string from a multisz registry value. - */ -static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string ) -{ - DWORD size, type; - WCHAR *buffer, *src, *dst; - - if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return; - if (type != REG_MULTI_SZ) return; - /* allocate double the size, one for value before and one for after */ - if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return; - if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done; - src = buffer; - dst = buffer + size; - while (*src) - { - int len = strlenW(src) + 1; - if (strcmpiW( src, string )) - { - memcpy( dst, src, len * sizeof(WCHAR) ); - dst += len; - } - src += len; - } - *dst++ = 0; - if (dst != buffer + 2*size) /* did we remove something? */ - { - TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) ); - RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, - (BYTE *)(buffer + size), dst - (buffer + size) ); - } - done: - HeapFree( GetProcessHeap(), 0, buffer ); -} - - -/*********************************************************************** - * do_reg_operation - * - * Perform an add/delete registry operation depending on the flags. - */ -static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags ) -{ - DWORD type, size; - - if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL)) /* deletion */ - { - if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON)) - { - if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING) - { - WCHAR *str; - - if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE; - if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE; - SetupGetStringFieldW( context, 5, str, size, NULL ); - delete_multi_sz_value( hkey, value, str ); - HeapFree( GetProcessHeap(), 0, str ); - } - else RegDeleteValueW( hkey, value ); - } - else RegDeleteKeyW( hkey, NULL ); - return TRUE; - } - - if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE; - - if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY)) - { - BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL ); - if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE; - if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE; - } - - switch(flags & FLG_ADDREG_TYPE_MASK) - { - case FLG_ADDREG_TYPE_SZ: type = REG_SZ; break; - case FLG_ADDREG_TYPE_MULTI_SZ: type = REG_MULTI_SZ; break; - case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break; - case FLG_ADDREG_TYPE_BINARY: type = REG_BINARY; break; - case FLG_ADDREG_TYPE_DWORD: type = REG_DWORD; break; - case FLG_ADDREG_TYPE_NONE: type = REG_NONE; break; - default: type = flags >> 16; break; - } - - if (!(flags & FLG_ADDREG_BINVALUETYPE) || - (type == REG_DWORD && SetupGetFieldCount(context) == 5)) - { - static const WCHAR empty; - WCHAR *str = NULL; - - if (type == REG_MULTI_SZ) - { - if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0; - if (size) - { - if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE; - SetupGetMultiSzFieldW( context, 5, str, size, NULL ); - } - if (flags & FLG_ADDREG_APPEND) - { - if (!str) return TRUE; - append_multi_sz_value( hkey, value, str, size ); - HeapFree( GetProcessHeap(), 0, str ); - return TRUE; - } - /* else fall through to normal string handling */ - } - else - { - if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0; - if (size) - { - if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE; - SetupGetStringFieldW( context, 5, str, size, NULL ); - } - } - - if (type == REG_DWORD) - { - DWORD dw = str ? strtoulW( str, NULL, 16 ) : 0; - TRACE( "setting dword %s to %lx\n", debugstr_w(value), dw ); - RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) ); - } - else - { - TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) ); - if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) ); - else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) ); - } - HeapFree( GetProcessHeap(), 0, str ); - return TRUE; - } - else /* get the binary data */ - { - BYTE *data = NULL; - - if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0; - if (size) - { - if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; - TRACE( "setting binary data %s len %ld\n", debugstr_w(value), size ); - SetupGetBinaryField( context, 5, data, size, NULL ); - } - RegSetValueExW( hkey, value, 0, type, data, size ); - HeapFree( GetProcessHeap(), 0, data ); - return TRUE; - } -} - - -/*********************************************************************** - * registry_callback - * - * Called once for each AddReg and DelReg entry in a given section. - */ -static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg ) -{ - struct registry_callback_info *info = arg; - INFCONTEXT context; - HKEY root_key, hkey; - - BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context ); - - for (; ok; ok = SetupFindNextLine( &context, &context )) - { - WCHAR buffer[MAX_INF_STRING_LENGTH]; - INT flags; - - /* get root */ - if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) - continue; - if (!(root_key = get_root_key( buffer, info->default_root ))) - continue; - - /* get key */ - if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) - *buffer = 0; - - /* get flags */ - if (!SetupGetIntField( &context, 4, &flags )) flags = 0; - - if (!info->delete) - { - if (flags & FLG_ADDREG_DELREG_BIT) continue; /* ignore this entry */ - } - else - { - if (!flags) flags = FLG_ADDREG_DELREG_BIT; - else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue; /* ignore this entry */ - } - - if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY)) - { - if (RegOpenKeyW( root_key, buffer, &hkey )) continue; /* ignore if it doesn't exist */ - } - else if (RegCreateKeyW( root_key, buffer, &hkey )) - { - ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) ); - continue; - } - TRACE( "key %p %s\n", root_key, debugstr_w(buffer) ); - - /* get value name */ - if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) - *buffer = 0; - - /* and now do it */ - if (!do_reg_operation( hkey, buffer, &context, flags )) - { - RegCloseKey( hkey ); - return FALSE; - } - RegCloseKey( hkey ); - } - return TRUE; -} - - -/*********************************************************************** - * do_register_dll - * - * Register or unregister a dll. - */ -static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path, - INT flags, INT timeout, const WCHAR *args ) -{ - HMODULE module; - HRESULT res; - SP_REGISTER_CONTROL_STATUSW status; - - status.cbSize = sizeof(status); - status.FileName = path; - status.FailureCode = SPREG_SUCCESS; - status.Win32Error = ERROR_SUCCESS; - - if (info->callback) - { - switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION, - (UINT_PTR)&status, !info->unregister )) - { - case FILEOP_ABORT: - SetLastError( ERROR_OPERATION_ABORTED ); - return FALSE; - case FILEOP_SKIP: - return TRUE; - case FILEOP_DOIT: - break; - } - } - - if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH ))) - { - WARN( "could not load %s\n", debugstr_w(path) ); - status.FailureCode = SPREG_LOADLIBRARY; - status.Win32Error = GetLastError(); - goto done; - } - - if (flags & FLG_REGSVR_DLLREGISTER) - { - const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer"; - HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point ); - - if (!func) - { - status.FailureCode = SPREG_GETPROCADDR; - status.Win32Error = GetLastError(); - goto done; - } - - TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) ); - res = func(); - - if (FAILED(res)) - { - WARN( "calling %s in %s returned error %lx\n", entry_point, debugstr_w(path), res ); - status.FailureCode = SPREG_REGSVR; - status.Win32Error = res; - goto done; - } - } - - if (flags & FLG_REGSVR_DLLINSTALL) - { - HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" ); - - if (!func) - { - status.FailureCode = SPREG_GETPROCADDR; - status.Win32Error = GetLastError(); - goto done; - } - - TRACE( "calling DllInstall(%d,%s) in %s\n", - !info->unregister, debugstr_w(args), debugstr_w(path) ); - res = func( !info->unregister, args ); - - if (FAILED(res)) - { - WARN( "calling DllInstall in %s returned error %lx\n", debugstr_w(path), res ); - status.FailureCode = SPREG_REGSVR; - status.Win32Error = res; - goto done; - } - } - -done: - if (module) FreeLibrary( module ); - if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION, - (UINT_PTR)&status, !info->unregister ); - return TRUE; -} - - -/*********************************************************************** - * register_dlls_callback - * - * Called once for each RegisterDlls entry in a given section. - */ -static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg ) -{ - struct register_dll_info *info = arg; - INFCONTEXT context; - BOOL ret = TRUE; - BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context ); - - for (; ok; ok = SetupFindNextLine( &context, &context )) - { - WCHAR *path, *args, *p; - WCHAR buffer[MAX_INF_STRING_LENGTH]; - INT flags, timeout; - - /* get directory */ - if (!(path = PARSER_get_dest_dir( &context ))) continue; - - /* get dll name */ - if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) - goto done; - if (!(p = HeapReAlloc( GetProcessHeap(), 0, path, - (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done; - path = p; - p += strlenW(p); - if (p == path || p[-1] != '\\') *p++ = '\\'; - strcpyW( p, buffer ); - - /* get flags */ - if (!SetupGetIntField( &context, 4, &flags )) flags = 0; - - /* get timeout */ - if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60; - - /* get command line */ - args = NULL; - if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) - args = buffer; - - ret = do_register_dll( info, path, flags, timeout, args ); - - done: - HeapFree( GetProcessHeap(), 0, path ); - if (!ret) break; - } - return ret; -} - -/*********************************************************************** - * update_ini_callback - * - * Called once for each UpdateInis entry in a given section. - */ -static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg ) -{ - INFCONTEXT context; - - BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context ); - - for (; ok; ok = SetupFindNextLine( &context, &context )) - { - WCHAR buffer[MAX_INF_STRING_LENGTH]; - WCHAR filename[MAX_INF_STRING_LENGTH]; - WCHAR section[MAX_INF_STRING_LENGTH]; - WCHAR entry[MAX_INF_STRING_LENGTH]; - WCHAR string[MAX_INF_STRING_LENGTH]; - LPWSTR divider; - - if (!SetupGetStringFieldW( &context, 1, filename, - sizeof(filename)/sizeof(WCHAR), NULL )) - continue; - - if (!SetupGetStringFieldW( &context, 2, section, - sizeof(section)/sizeof(WCHAR), NULL )) - continue; - - if (!SetupGetStringFieldW( &context, 4, buffer, - sizeof(buffer)/sizeof(WCHAR), NULL )) - continue; - - divider = strchrW(buffer,'='); - if (divider) - { - *divider = 0; - strcpyW(entry,buffer); - divider++; - strcpyW(string,divider); - } - else - { - strcpyW(entry,buffer); - string[0]=0; - } - - TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry), - debugstr_w(string),debugstr_w(section),debugstr_w(filename)); - WritePrivateProfileStringW(section,entry,string,filename); - - } - return TRUE; -} - -static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg ) -{ - FIXME( "should update ini fields %s\n", debugstr_w(field) ); - return TRUE; -} - -static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg ) -{ - FIXME( "should do ini2reg %s\n", debugstr_w(field) ); - return TRUE; -} - -static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg ) -{ - FIXME( "should do logconf %s\n", debugstr_w(field) ); - return TRUE; -} - -static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg ) -{ - FIXME( "should do bitreg %s\n", debugstr_w(field) ); - return TRUE; -} - -static BOOL profile_items_callback( HINF hinf, PCWSTR field, void *arg ) -{ - FIXME( "should do profile items %s\n", debugstr_w(field) ); - return TRUE; -} - -static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg ) -{ - FIXME( "should do copy inf %s\n", debugstr_w(field) ); - return TRUE; -} - - -/*********************************************************************** - * iterate_section_fields - * - * Iterate over all fields of a certain key of a certain section - */ -static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key, - iterate_fields_func callback, void *arg ) -{ - WCHAR static_buffer[200]; - WCHAR *buffer = static_buffer; - DWORD size = sizeof(static_buffer)/sizeof(WCHAR); - INFCONTEXT context; - BOOL ret = FALSE; - - BOOL ok = SetupFindFirstLineW( hinf, section, key, &context ); - while (ok) - { - UINT i, count = SetupGetFieldCount( &context ); - for (i = 1; i <= count; i++) - { - if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size ))) - goto done; - if (!callback( hinf, buffer, arg )) - { - WARN("callback failed for %s %s err %ld\n", - debugstr_w(section), debugstr_w(buffer), GetLastError() ); - goto done; - } - } - ok = SetupFindNextMatchLineW( &context, key, &context ); - } - ret = TRUE; - done: - if (buffer && buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer ); - return ret; -} - - -/*********************************************************************** - * SetupInstallFilesFromInfSectionA (SETUPAPI.@) - */ -BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue, - PCSTR section, PCSTR src_root, UINT flags ) -{ - UNICODE_STRING sectionW; - BOOL ret = FALSE; - - if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return FALSE; - } - if (!src_root) - ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer, - NULL, flags ); - else - { - UNICODE_STRING srcW; - if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root )) - { - ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer, - srcW.Buffer, flags ); - RtlFreeUnicodeString( &srcW ); - } - else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - } - RtlFreeUnicodeString( §ionW ); - return ret; -} - - -/*********************************************************************** - * SetupInstallFilesFromInfSectionW (SETUPAPI.@) - */ -BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue, - PCWSTR section, PCWSTR src_root, UINT flags ) -{ - struct files_callback_info info; - - info.queue = queue; - info.src_root = src_root; - info.copy_flags = flags; - info.layout = hlayout; - return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ); -} - - -/*********************************************************************** - * SetupInstallFromInfSectionA (SETUPAPI.@) - */ -BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags, - HKEY key_root, PCSTR src_root, UINT copy_flags, - PSP_FILE_CALLBACK_A callback, PVOID context, - HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data ) -{ - UNICODE_STRING sectionW, src_rootW; - struct callback_WtoA_context ctx; - BOOL ret = FALSE; - - src_rootW.Buffer = NULL; - if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root )) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return FALSE; - } - - if (RtlCreateUnicodeStringFromAsciiz( §ionW, section )) - { - ctx.orig_context = context; - ctx.orig_handler = callback; - ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root, - src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA, - &ctx, devinfo, devinfo_data ); - RtlFreeUnicodeString( §ionW ); - } - else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - - RtlFreeUnicodeString( &src_rootW ); - return ret; -} - - -/*********************************************************************** - * SetupInstallFromInfSectionW (SETUPAPI.@) - */ -BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags, - HKEY key_root, PCWSTR src_root, UINT copy_flags, - PSP_FILE_CALLBACK_W callback, PVOID context, - HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data ) -{ - if (flags & SPINST_FILES) - { - struct files_callback_info info; - HSPFILEQ queue; - BOOL ret; - - if (!(queue = SetupOpenFileQueue())) return FALSE; - info.queue = queue; - info.src_root = src_root; - info.copy_flags = copy_flags; - info.layout = hinf; - ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) && - iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) && - iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) && - SetupCommitFileQueueW( owner, queue, callback, context )); - SetupCloseFileQueue( queue ); - if (!ret) return FALSE; - } - if (flags & SPINST_INIFILES) - { - if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) || - !iterate_section_fields( hinf, section, UpdateIniFields, - update_ini_fields_callback, NULL )) - return FALSE; - } - if (flags & SPINST_INI2REG) - { - if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL )) - return FALSE; - } - if (flags & SPINST_LOGCONFIG) - { - if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL )) - return FALSE; - } - if (flags & SPINST_REGSVR) - { - struct register_dll_info info; - - info.unregister = FALSE; - if (flags & SPINST_REGISTERCALLBACKAWARE) - { - info.callback = callback; - info.callback_context = context; - } - else info.callback = NULL; - - if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info )) - return FALSE; - } - if (flags & SPINST_UNREGSVR) - { - struct register_dll_info info; - - info.unregister = TRUE; - if (flags & SPINST_REGISTERCALLBACKAWARE) - { - info.callback = callback; - info.callback_context = context; - } - else info.callback = NULL; - - if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info )) - return FALSE; - } - if (flags & SPINST_REGISTRY) - { - struct registry_callback_info info; - - info.default_root = key_root; - info.delete = TRUE; - if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info )) - return FALSE; - info.delete = FALSE; - if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info )) - return FALSE; - } - if (flags & SPINST_BITREG) - { - if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL )) - return FALSE; - } - if (flags & SPINST_PROFILEITEMS) - { - if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL )) - return FALSE; - } - if (flags & SPINST_COPYINF) - { - if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL )) - return FALSE; - } - - return TRUE; -} - - -/*********************************************************************** - * InstallHinfSectionW (SETUPAPI.@) - * - * NOTE: 'cmdline' is
from - * RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection
- */ -void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show ) -{ - WCHAR *p, *path, section[MAX_PATH]; - void *callback_context; - UINT mode; - HINF hinf; - - TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline)); - - lstrcpynW( section, cmdline, sizeof(section)/sizeof(WCHAR) ); - - if (!(p = strchrW( section, ' ' ))) return; - *p++ = 0; - while (*p == ' ') p++; - mode = atoiW( p ); - - if (!(p = strchrW( p, ' ' ))) return; - path = p + 1; - while (*path == ' ') path++; - - hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL ); - if (hinf == INVALID_HANDLE_VALUE) return; - - callback_context = SetupInitDefaultQueueCallback( hwnd ); - SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL, SP_COPY_NEWER, - SetupDefaultQueueCallbackW, callback_context, - NULL, NULL ); - SetupTermDefaultQueueCallback( callback_context ); - SetupCloseInfFile( hinf ); - - /* FIXME: should check the mode and maybe reboot */ - /* there isn't much point in doing that since we */ - /* don't yet handle deferred file copies anyway. */ -} - - -/*********************************************************************** - * InstallHinfSectionA (SETUPAPI.@) - */ -void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show ) -{ - UNICODE_STRING cmdlineW; - - if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline )) - { - InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show ); - RtlFreeUnicodeString( &cmdlineW ); - } -} +/* + * Setupapi install routines + * + * Copyright 2002 Alexandre Julliard for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "winerror.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "setupapi_private.h" +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/* info passed to callback functions dealing with files */ +struct files_callback_info +{ + HSPFILEQ queue; + PCWSTR src_root; + UINT copy_flags; + HINF layout; +}; + +/* info passed to callback functions dealing with the registry */ +struct registry_callback_info +{ + HKEY default_root; + BOOL delete; +}; + +/* info passed to callback functions dealing with registering dlls */ +struct register_dll_info +{ + PSP_FILE_CALLBACK_W callback; + PVOID callback_context; + BOOL unregister; +}; + +typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg ); + +/* Unicode constants */ +static const WCHAR CopyFiles[] = {'C','o','p','y','F','i','l','e','s',0}; +static const WCHAR DelFiles[] = {'D','e','l','F','i','l','e','s',0}; +static const WCHAR RenFiles[] = {'R','e','n','F','i','l','e','s',0}; +static const WCHAR Ini2Reg[] = {'I','n','i','2','R','e','g',0}; +static const WCHAR LogConf[] = {'L','o','g','C','o','n','f',0}; +static const WCHAR AddReg[] = {'A','d','d','R','e','g',0}; +static const WCHAR DelReg[] = {'D','e','l','R','e','g',0}; +static const WCHAR BitReg[] = {'B','i','t','R','e','g',0}; +static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0}; +static const WCHAR CopyINF[] = {'C','o','p','y','I','N','F',0}; +static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0}; +static const WCHAR RegisterDlls[] = {'R','e','g','i','s','t','e','r','D','l','l','s',0}; +static const WCHAR UnregisterDlls[] = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0}; +static const WCHAR ProfileItems[] = {'P','r','o','f','i','l','e','I','t','e','m','s',0}; + + +/*********************************************************************** + * get_field_string + * + * Retrieve the contents of a field, dynamically growing the buffer if necessary. + */ +static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer, + WCHAR *static_buffer, DWORD *size ) +{ + DWORD required; + + if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer; + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + /* now grow the buffer */ + if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer ); + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL; + *size = required; + if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer; + } + if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer ); + return NULL; +} + + +/*********************************************************************** + * copy_files_callback + * + * Called once for each CopyFiles entry in a given section. + */ +static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg ) +{ + struct files_callback_info *info = arg; + + if (field[0] == '@') /* special case: copy single file */ + SetupQueueDefaultCopyW( info->queue, info->layout, info->src_root, NULL, field, info->copy_flags ); + else + SetupQueueCopySectionW( info->queue, info->src_root, info->layout, hinf, field, info->copy_flags ); + return TRUE; +} + + +/*********************************************************************** + * delete_files_callback + * + * Called once for each DelFiles entry in a given section. + */ +static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg ) +{ + struct files_callback_info *info = arg; + SetupQueueDeleteSectionW( info->queue, hinf, 0, field ); + return TRUE; +} + + +/*********************************************************************** + * rename_files_callback + * + * Called once for each RenFiles entry in a given section. + */ +static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg ) +{ + struct files_callback_info *info = arg; + SetupQueueRenameSectionW( info->queue, hinf, 0, field ); + return TRUE; +} + + +/*********************************************************************** + * get_root_key + * + * Retrieve the registry root key from its name. + */ +static HKEY get_root_key( const WCHAR *name, HKEY def_root ) +{ + static const WCHAR HKCR[] = {'H','K','C','R',0}; + static const WCHAR HKCU[] = {'H','K','C','U',0}; + static const WCHAR HKLM[] = {'H','K','L','M',0}; + static const WCHAR HKU[] = {'H','K','U',0}; + static const WCHAR HKR[] = {'H','K','R',0}; + + if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT; + if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER; + if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE; + if (!strcmpiW( name, HKU )) return HKEY_USERS; + if (!strcmpiW( name, HKR )) return def_root; + return 0; +} + + +/*********************************************************************** + * append_multi_sz_value + * + * Append a multisz string to a multisz registry value. + */ +static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings, + DWORD str_size ) +{ + DWORD size, type, total; + WCHAR *buffer, *p; + + if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return; + if (type != REG_MULTI_SZ) return; + + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return; + if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done; + + /* compare each string against all the existing ones */ + total = size; + while (*strings) + { + int len = strlenW(strings) + 1; + + for (p = buffer; *p; p += strlenW(p) + 1) + if (!strcmpiW( p, strings )) break; + + if (!*p) /* not found, need to append it */ + { + memcpy( p, strings, len * sizeof(WCHAR) ); + p[len] = 0; + total += len; + } + strings += len; + } + if (total != size) + { + TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) ); + RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total ); + } + done: + HeapFree( GetProcessHeap(), 0, buffer ); +} + + +/*********************************************************************** + * delete_multi_sz_value + * + * Remove a string from a multisz registry value. + */ +static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string ) +{ + DWORD size, type; + WCHAR *buffer, *src, *dst; + + if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return; + if (type != REG_MULTI_SZ) return; + /* allocate double the size, one for value before and one for after */ + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return; + if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done; + src = buffer; + dst = buffer + size; + while (*src) + { + int len = strlenW(src) + 1; + if (strcmpiW( src, string )) + { + memcpy( dst, src, len * sizeof(WCHAR) ); + dst += len; + } + src += len; + } + *dst++ = 0; + if (dst != buffer + 2*size) /* did we remove something? */ + { + TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) ); + RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, + (BYTE *)(buffer + size), dst - (buffer + size) ); + } + done: + HeapFree( GetProcessHeap(), 0, buffer ); +} + + +/*********************************************************************** + * do_reg_operation + * + * Perform an add/delete registry operation depending on the flags. + */ +static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags ) +{ + DWORD type, size; + + if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL)) /* deletion */ + { + if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON)) + { + if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING) + { + WCHAR *str; + + if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE; + if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE; + SetupGetStringFieldW( context, 5, str, size, NULL ); + delete_multi_sz_value( hkey, value, str ); + HeapFree( GetProcessHeap(), 0, str ); + } + else RegDeleteValueW( hkey, value ); + } + else RegDeleteKeyW( hkey, NULL ); + return TRUE; + } + + if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE; + + if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY)) + { + BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL ); + if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE; + if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE; + } + + switch(flags & FLG_ADDREG_TYPE_MASK) + { + case FLG_ADDREG_TYPE_SZ: type = REG_SZ; break; + case FLG_ADDREG_TYPE_MULTI_SZ: type = REG_MULTI_SZ; break; + case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break; + case FLG_ADDREG_TYPE_BINARY: type = REG_BINARY; break; + case FLG_ADDREG_TYPE_DWORD: type = REG_DWORD; break; + case FLG_ADDREG_TYPE_NONE: type = REG_NONE; break; + default: type = flags >> 16; break; + } + + if (!(flags & FLG_ADDREG_BINVALUETYPE) || + (type == REG_DWORD && SetupGetFieldCount(context) == 5)) + { + static const WCHAR empty; + WCHAR *str = NULL; + + if (type == REG_MULTI_SZ) + { + if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0; + if (size) + { + if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE; + SetupGetMultiSzFieldW( context, 5, str, size, NULL ); + } + if (flags & FLG_ADDREG_APPEND) + { + if (!str) return TRUE; + append_multi_sz_value( hkey, value, str, size ); + HeapFree( GetProcessHeap(), 0, str ); + return TRUE; + } + /* else fall through to normal string handling */ + } + else + { + if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0; + if (size) + { + if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE; + SetupGetStringFieldW( context, 5, str, size, NULL ); + } + } + + if (type == REG_DWORD) + { + DWORD dw = str ? strtoulW( str, NULL, 16 ) : 0; + TRACE( "setting dword %s to %lx\n", debugstr_w(value), dw ); + RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) ); + } + else + { + TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) ); + if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) ); + else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) ); + } + HeapFree( GetProcessHeap(), 0, str ); + return TRUE; + } + else /* get the binary data */ + { + BYTE *data = NULL; + + if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0; + if (size) + { + if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; + TRACE( "setting binary data %s len %ld\n", debugstr_w(value), size ); + SetupGetBinaryField( context, 5, data, size, NULL ); + } + RegSetValueExW( hkey, value, 0, type, data, size ); + HeapFree( GetProcessHeap(), 0, data ); + return TRUE; + } +} + + +/*********************************************************************** + * registry_callback + * + * Called once for each AddReg and DelReg entry in a given section. + */ +static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg ) +{ + struct registry_callback_info *info = arg; + INFCONTEXT context; + HKEY root_key, hkey; + + BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context ); + + for (; ok; ok = SetupFindNextLine( &context, &context )) + { + WCHAR buffer[MAX_INF_STRING_LENGTH]; + INT flags; + + /* get root */ + if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + continue; + if (!(root_key = get_root_key( buffer, info->default_root ))) + continue; + + /* get key */ + if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + *buffer = 0; + + /* get flags */ + if (!SetupGetIntField( &context, 4, &flags )) flags = 0; + + if (!info->delete) + { + if (flags & FLG_ADDREG_DELREG_BIT) continue; /* ignore this entry */ + } + else + { + if (!flags) flags = FLG_ADDREG_DELREG_BIT; + else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue; /* ignore this entry */ + } + + if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY)) + { + if (RegOpenKeyW( root_key, buffer, &hkey )) continue; /* ignore if it doesn't exist */ + } + else if (RegCreateKeyW( root_key, buffer, &hkey )) + { + ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) ); + continue; + } + TRACE( "key %p %s\n", root_key, debugstr_w(buffer) ); + + /* get value name */ + if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + *buffer = 0; + + /* and now do it */ + if (!do_reg_operation( hkey, buffer, &context, flags )) + { + RegCloseKey( hkey ); + return FALSE; + } + RegCloseKey( hkey ); + } + return TRUE; +} + + +/*********************************************************************** + * do_register_dll + * + * Register or unregister a dll. + */ +static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path, + INT flags, INT timeout, const WCHAR *args ) +{ + HMODULE module; + HRESULT res; + SP_REGISTER_CONTROL_STATUSW status; + + status.cbSize = sizeof(status); + status.FileName = path; + status.FailureCode = SPREG_SUCCESS; + status.Win32Error = ERROR_SUCCESS; + + if (info->callback) + { + switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION, + (UINT_PTR)&status, !info->unregister )) + { + case FILEOP_ABORT: + SetLastError( ERROR_OPERATION_ABORTED ); + return FALSE; + case FILEOP_SKIP: + return TRUE; + case FILEOP_DOIT: + break; + } + } + + if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH ))) + { + WARN( "could not load %s\n", debugstr_w(path) ); + status.FailureCode = SPREG_LOADLIBRARY; + status.Win32Error = GetLastError(); + goto done; + } + + if (flags & FLG_REGSVR_DLLREGISTER) + { + const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer"; + HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point ); + + if (!func) + { + status.FailureCode = SPREG_GETPROCADDR; + status.Win32Error = GetLastError(); + goto done; + } + + TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) ); + res = func(); + + if (FAILED(res)) + { + WARN( "calling %s in %s returned error %lx\n", entry_point, debugstr_w(path), res ); + status.FailureCode = SPREG_REGSVR; + status.Win32Error = res; + goto done; + } + } + + if (flags & FLG_REGSVR_DLLINSTALL) + { + HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" ); + + if (!func) + { + status.FailureCode = SPREG_GETPROCADDR; + status.Win32Error = GetLastError(); + goto done; + } + + TRACE( "calling DllInstall(%d,%s) in %s\n", + !info->unregister, debugstr_w(args), debugstr_w(path) ); + res = func( !info->unregister, args ); + + if (FAILED(res)) + { + WARN( "calling DllInstall in %s returned error %lx\n", debugstr_w(path), res ); + status.FailureCode = SPREG_REGSVR; + status.Win32Error = res; + goto done; + } + } + +done: + if (module) FreeLibrary( module ); + if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION, + (UINT_PTR)&status, !info->unregister ); + return TRUE; +} + + +/*********************************************************************** + * register_dlls_callback + * + * Called once for each RegisterDlls entry in a given section. + */ +static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg ) +{ + struct register_dll_info *info = arg; + INFCONTEXT context; + BOOL ret = TRUE; + BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context ); + + for (; ok; ok = SetupFindNextLine( &context, &context )) + { + WCHAR *path, *args, *p; + WCHAR buffer[MAX_INF_STRING_LENGTH]; + INT flags, timeout; + + /* get directory */ + if (!(path = PARSER_get_dest_dir( &context ))) continue; + + /* get dll name */ + if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + goto done; + if (!(p = HeapReAlloc( GetProcessHeap(), 0, path, + (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done; + path = p; + p += strlenW(p); + if (p == path || p[-1] != '\\') *p++ = '\\'; + strcpyW( p, buffer ); + + /* get flags */ + if (!SetupGetIntField( &context, 4, &flags )) flags = 0; + + /* get timeout */ + if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60; + + /* get command line */ + args = NULL; + if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + args = buffer; + + ret = do_register_dll( info, path, flags, timeout, args ); + + done: + HeapFree( GetProcessHeap(), 0, path ); + if (!ret) break; + } + return ret; +} + +/*********************************************************************** + * update_ini_callback + * + * Called once for each UpdateInis entry in a given section. + */ +static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg ) +{ + INFCONTEXT context; + + BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context ); + + for (; ok; ok = SetupFindNextLine( &context, &context )) + { + WCHAR buffer[MAX_INF_STRING_LENGTH]; + WCHAR filename[MAX_INF_STRING_LENGTH]; + WCHAR section[MAX_INF_STRING_LENGTH]; + WCHAR entry[MAX_INF_STRING_LENGTH]; + WCHAR string[MAX_INF_STRING_LENGTH]; + LPWSTR divider; + + if (!SetupGetStringFieldW( &context, 1, filename, + sizeof(filename)/sizeof(WCHAR), NULL )) + continue; + + if (!SetupGetStringFieldW( &context, 2, section, + sizeof(section)/sizeof(WCHAR), NULL )) + continue; + + if (!SetupGetStringFieldW( &context, 4, buffer, + sizeof(buffer)/sizeof(WCHAR), NULL )) + continue; + + divider = strchrW(buffer,'='); + if (divider) + { + *divider = 0; + strcpyW(entry,buffer); + divider++; + strcpyW(string,divider); + } + else + { + strcpyW(entry,buffer); + string[0]=0; + } + + TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry), + debugstr_w(string),debugstr_w(section),debugstr_w(filename)); + WritePrivateProfileStringW(section,entry,string,filename); + + } + return TRUE; +} + +static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg ) +{ + FIXME( "should update ini fields %s\n", debugstr_w(field) ); + return TRUE; +} + +static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg ) +{ + FIXME( "should do ini2reg %s\n", debugstr_w(field) ); + return TRUE; +} + +static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg ) +{ + FIXME( "should do logconf %s\n", debugstr_w(field) ); + return TRUE; +} + +static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg ) +{ + FIXME( "should do bitreg %s\n", debugstr_w(field) ); + return TRUE; +} + +static BOOL profile_items_callback( HINF hinf, PCWSTR field, void *arg ) +{ + FIXME( "should do profile items %s\n", debugstr_w(field) ); + return TRUE; +} + +static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg ) +{ + FIXME( "should do copy inf %s\n", debugstr_w(field) ); + return TRUE; +} + + +/*********************************************************************** + * iterate_section_fields + * + * Iterate over all fields of a certain key of a certain section + */ +static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key, + iterate_fields_func callback, void *arg ) +{ + WCHAR static_buffer[200]; + WCHAR *buffer = static_buffer; + DWORD size = sizeof(static_buffer)/sizeof(WCHAR); + INFCONTEXT context; + BOOL ret = FALSE; + + BOOL ok = SetupFindFirstLineW( hinf, section, key, &context ); + while (ok) + { + UINT i, count = SetupGetFieldCount( &context ); + for (i = 1; i <= count; i++) + { + if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size ))) + goto done; + if (!callback( hinf, buffer, arg )) + { + WARN("callback failed for %s %s err %ld\n", + debugstr_w(section), debugstr_w(buffer), GetLastError() ); + goto done; + } + } + ok = SetupFindNextMatchLineW( &context, key, &context ); + } + ret = TRUE; + done: + if (buffer && buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer ); + return ret; +} + + +/*********************************************************************** + * SetupInstallFilesFromInfSectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue, + PCSTR section, PCSTR src_root, UINT flags ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + if (!src_root) + ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer, + NULL, flags ); + else + { + UNICODE_STRING srcW; + if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root )) + { + ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer, + srcW.Buffer, flags ); + RtlFreeUnicodeString( &srcW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + } + RtlFreeUnicodeString( §ionW ); + return ret; +} + + +/*********************************************************************** + * SetupInstallFilesFromInfSectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue, + PCWSTR section, PCWSTR src_root, UINT flags ) +{ + struct files_callback_info info; + + info.queue = queue; + info.src_root = src_root; + info.copy_flags = flags; + info.layout = hlayout; + return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ); +} + + +/*********************************************************************** + * SetupInstallFromInfSectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags, + HKEY key_root, PCSTR src_root, UINT copy_flags, + PSP_FILE_CALLBACK_A callback, PVOID context, + HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data ) +{ + UNICODE_STRING sectionW, src_rootW; + struct callback_WtoA_context ctx; + BOOL ret = FALSE; + + src_rootW.Buffer = NULL; + if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + if (RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + ctx.orig_context = context; + ctx.orig_handler = callback; + ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root, + src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA, + &ctx, devinfo, devinfo_data ); + RtlFreeUnicodeString( §ionW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + + RtlFreeUnicodeString( &src_rootW ); + return ret; +} + + +/*********************************************************************** + * SetupInstallFromInfSectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags, + HKEY key_root, PCWSTR src_root, UINT copy_flags, + PSP_FILE_CALLBACK_W callback, PVOID context, + HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data ) +{ + if (flags & SPINST_FILES) + { + struct files_callback_info info; + HSPFILEQ queue; + BOOL ret; + + if (!(queue = SetupOpenFileQueue())) return FALSE; + info.queue = queue; + info.src_root = src_root; + info.copy_flags = copy_flags; + info.layout = hinf; + ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) && + iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) && + iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) && + SetupCommitFileQueueW( owner, queue, callback, context )); + SetupCloseFileQueue( queue ); + if (!ret) return FALSE; + } + if (flags & SPINST_INIFILES) + { + if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) || + !iterate_section_fields( hinf, section, UpdateIniFields, + update_ini_fields_callback, NULL )) + return FALSE; + } + if (flags & SPINST_INI2REG) + { + if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL )) + return FALSE; + } + if (flags & SPINST_LOGCONFIG) + { + if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL )) + return FALSE; + } + if (flags & SPINST_REGSVR) + { + struct register_dll_info info; + + info.unregister = FALSE; + if (flags & SPINST_REGISTERCALLBACKAWARE) + { + info.callback = callback; + info.callback_context = context; + } + else info.callback = NULL; + + if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info )) + return FALSE; + } + if (flags & SPINST_UNREGSVR) + { + struct register_dll_info info; + + info.unregister = TRUE; + if (flags & SPINST_REGISTERCALLBACKAWARE) + { + info.callback = callback; + info.callback_context = context; + } + else info.callback = NULL; + + if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info )) + return FALSE; + } + if (flags & SPINST_REGISTRY) + { + struct registry_callback_info info; + + info.default_root = key_root; + info.delete = TRUE; + if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info )) + return FALSE; + info.delete = FALSE; + if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info )) + return FALSE; + } + if (flags & SPINST_BITREG) + { + if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL )) + return FALSE; + } + if (flags & SPINST_PROFILEITEMS) + { + if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL )) + return FALSE; + } + if (flags & SPINST_COPYINF) + { + if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL )) + return FALSE; + } + + return TRUE; +} + + +/*********************************************************************** + * InstallHinfSectionW (SETUPAPI.@) + * + * NOTE: 'cmdline' is
from + * RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection
+ */ +void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show ) +{ + WCHAR *p, *path, section[MAX_PATH]; + void *callback_context; + UINT mode; + HINF hinf; + + TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline)); + + lstrcpynW( section, cmdline, sizeof(section)/sizeof(WCHAR) ); + + if (!(p = strchrW( section, ' ' ))) return; + *p++ = 0; + while (*p == ' ') p++; + mode = atoiW( p ); + + if (!(p = strchrW( p, ' ' ))) return; + path = p + 1; + while (*path == ' ') path++; + + hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL ); + if (hinf == INVALID_HANDLE_VALUE) return; + + callback_context = SetupInitDefaultQueueCallback( hwnd ); + SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL, SP_COPY_NEWER, + SetupDefaultQueueCallbackW, callback_context, + NULL, NULL ); + SetupTermDefaultQueueCallback( callback_context ); + SetupCloseInfFile( hinf ); + + /* FIXME: should check the mode and maybe reboot */ + /* there isn't much point in doing that since we */ + /* don't yet handle deferred file copies anyway. */ +} + + +/*********************************************************************** + * InstallHinfSectionA (SETUPAPI.@) + */ +void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show ) +{ + UNICODE_STRING cmdlineW; + + if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline )) + { + InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show ); + RtlFreeUnicodeString( &cmdlineW ); + } +} diff --git a/reactos/lib/setupapi/misc.c b/reactos/lib/setupapi/misc.c index 89601f0a5d1..4cc0fe4f684 100644 --- a/reactos/lib/setupapi/misc.c +++ b/reactos/lib/setupapi/misc.c @@ -1,585 +1,585 @@ -/* - * Setupapi miscellaneous functions - * - * Copyright 2005 Eric Kohl - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#include "windef.h" -#include "winbase.h" -#include "wingdi.h" -#include "winuser.h" -#include "winreg.h" -#include "setupapi.h" - -#include "wine/unicode.h" -#include "wine/debug.h" - -#include "setupapi_private.h" - - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - - -/************************************************************************** - * MyFree [SETUPAPI.@] - * - * Frees an allocated memory block from the process heap. - * - * PARAMS - * lpMem [I] pointer to memory block which will be freed - * - * RETURNS - * None - */ -VOID WINAPI MyFree(LPVOID lpMem) -{ - TRACE("%p\n", lpMem); - HeapFree(GetProcessHeap(), 0, lpMem); -} - - -/************************************************************************** - * MyMalloc [SETUPAPI.@] - * - * Allocates memory block from the process heap. - * - * PARAMS - * dwSize [I] size of the allocated memory block - * - * RETURNS - * Success: pointer to allocated memory block - * Failure: NULL - */ -LPVOID WINAPI MyMalloc(DWORD dwSize) -{ - TRACE("%lu\n", dwSize); - return HeapAlloc(GetProcessHeap(), 0, dwSize); -} - - -/************************************************************************** - * MyRealloc [SETUPAPI.@] - * - * Changes the size of an allocated memory block or allocates a memory - * block from the process heap. - * - * PARAMS - * lpSrc [I] pointer to memory block which will be resized - * dwSize [I] new size of the memory block - * - * RETURNS - * Success: pointer to the resized memory block - * Failure: NULL - * - * NOTES - * If lpSrc is a NULL-pointer, then MyRealloc allocates a memory - * block like MyMalloc. - */ -LPVOID WINAPI MyRealloc(LPVOID lpSrc, DWORD dwSize) -{ - TRACE("%p %lu\n", lpSrc, dwSize); - - if (lpSrc == NULL) - return HeapAlloc(GetProcessHeap(), 0, dwSize); - - return HeapReAlloc(GetProcessHeap(), 0, lpSrc, dwSize); -} - - -/************************************************************************** - * DuplicateString [SETUPAPI.@] - * - * Duplicates a unicode string. - * - * PARAMS - * lpSrc [I] pointer to the unicode string that will be duplicated - * - * RETURNS - * Success: pointer to the duplicated unicode string - * Failure: NULL - * - * NOTES - * Call MyFree() to release the duplicated string. - */ -LPWSTR WINAPI DuplicateString(LPCWSTR lpSrc) -{ - LPWSTR lpDst; - - TRACE("%s\n", debugstr_w(lpSrc)); - - lpDst = MyMalloc((lstrlenW(lpSrc) + 1) * sizeof(WCHAR)); - if (lpDst == NULL) - return NULL; - - strcpyW(lpDst, lpSrc); - - return lpDst; -} - - -/************************************************************************** - * QueryRegistryValue [SETUPAPI.@] - * - * Retrieves value data from the registry and allocates memory for the - * value data. - * - * PARAMS - * hKey [I] Handle of the key to query - * lpValueName [I] Name of value under hkey to query - * lpData [O] Destination for the values contents, - * lpType [O] Destination for the value type - * lpcbData [O] Destination for the size of data - * - * RETURNS - * Success: ERROR_SUCCESS - * Failure: Otherwise - * - * NOTES - * Use MyFree to release the lpData buffer. - */ -LONG WINAPI QueryRegistryValue(HKEY hKey, - LPCWSTR lpValueName, - LPBYTE *lpData, - LPDWORD lpType, - LPDWORD lpcbData) -{ - LONG lError; - - TRACE("%lx %s %p %p %p\n", - hKey, debugstr_w(lpValueName), lpData, lpType, lpcbData); - - /* Get required buffer size */ - *lpcbData = 0; - lError = RegQueryValueExW(hKey, lpValueName, 0, lpType, NULL, lpcbData); - if (lError != ERROR_SUCCESS) - return lError; - - /* Allocate buffer */ - *lpData = MyMalloc(*lpcbData); - if (*lpData == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - /* Query registry value */ - lError = RegQueryValueExW(hKey, lpValueName, 0, lpType, *lpData, lpcbData); - if (lError != ERROR_SUCCESS) - MyFree(*lpData); - - return lError; -} - - -/************************************************************************** - * IsUserAdmin [SETUPAPI.@] - * - * Checks whether the current user is a member of the Administrators group. - * - * PARAMS - * None - * - * RETURNS - * Success: TRUE - * Failure: FALSE - */ -BOOL WINAPI IsUserAdmin(VOID) -{ - SID_IDENTIFIER_AUTHORITY Authority = {SECURITY_NT_AUTHORITY}; - HANDLE hToken; - DWORD dwSize; - PTOKEN_GROUPS lpGroups; - PSID lpSid; - DWORD i; - BOOL bResult = FALSE; - - TRACE("\n"); - - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) - { - return FALSE; - } - - if (!GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize)) - { - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - { - CloseHandle(hToken); - return FALSE; - } - } - - lpGroups = MyMalloc(dwSize); - if (lpGroups == NULL) - { - CloseHandle(hToken); - return FALSE; - } - - if (!GetTokenInformation(hToken, TokenGroups, lpGroups, dwSize, &dwSize)) - { - MyFree(lpGroups); - CloseHandle(hToken); - return FALSE; - } - - CloseHandle(hToken); - - if (!AllocateAndInitializeSid(&Authority, 2, SECURITY_BUILTIN_DOMAIN_RID, - DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, - &lpSid)) - { - MyFree(lpGroups); - return FALSE; - } - - for (i = 0; i < lpGroups->GroupCount; i++) - { - if (EqualSid(lpSid, &lpGroups->Groups[i].Sid)) - { - bResult = TRUE; - break; - } - } - - FreeSid(lpSid); - MyFree(lpGroups); - - return bResult; -} - - -/************************************************************************** - * MultiByteToUnicode [SETUPAPI.@] - * - * Converts a multi-byte string to a Unicode string. - * - * PARAMS - * lpMultiByteStr [I] Multi-byte string to be converted - * uCodePage [I] Code page - * - * RETURNS - * Success: pointer to the converted Unicode string - * Failure: NULL - * - * NOTE - * Use MyFree to release the returned Unicode string. - */ -LPWSTR WINAPI MultiByteToUnicode(LPCSTR lpMultiByteStr, UINT uCodePage) -{ - LPWSTR lpUnicodeStr; - int nLength; - - TRACE("%s %lu\n", debugstr_a(lpMultiByteStr), uCodePage); - - nLength = MultiByteToWideChar(uCodePage, 0, lpMultiByteStr, - -1, NULL, 0); - if (nLength == 0) - return NULL; - - lpUnicodeStr = MyMalloc(nLength * sizeof(WCHAR)); - if (lpUnicodeStr == NULL) - return NULL; - - if (!MultiByteToWideChar(uCodePage, 0, lpMultiByteStr, - nLength, lpUnicodeStr, nLength)) - { - MyFree(lpUnicodeStr); - return NULL; - } - - return lpUnicodeStr; -} - - -/************************************************************************** - * UnicodeToMultiByte [SETUPAPI.@] - * - * Converts a Unicode string to a multi-byte string. - * - * PARAMS - * lpUnicodeStr [I] Unicode string to be converted - * uCodePage [I] Code page - * - * RETURNS - * Success: pointer to the converted multi-byte string - * Failure: NULL - * - * NOTE - * Use MyFree to release the returned multi-byte string. - */ -LPSTR WINAPI UnicodeToMultiByte(LPCWSTR lpUnicodeStr, UINT uCodePage) -{ - LPSTR lpMultiByteStr; - int nLength; - - TRACE("%s %lu\n", debugstr_w(lpUnicodeStr), uCodePage); - - nLength = WideCharToMultiByte(uCodePage, 0, lpUnicodeStr, -1, - NULL, 0, NULL, NULL); - if (nLength == 0) - return NULL; - - lpMultiByteStr = MyMalloc(nLength); - if (lpMultiByteStr == NULL) - return NULL; - - if (!WideCharToMultiByte(uCodePage, 0, lpUnicodeStr, -1, - lpMultiByteStr, nLength, NULL, NULL)) - { - MyFree(lpMultiByteStr); - return NULL; - } - - return lpMultiByteStr; -} - - -/************************************************************************** - * DoesUserHavePrivilege [SETUPAPI.@] - * - * Check whether the current user has got a given privilege. - * - * PARAMS - * lpPrivilegeName [I] Name of the privilege to be checked - * - * RETURNS - * Success: TRUE - * Failure: FALSE - */ -BOOL WINAPI DoesUserHavePrivilege(LPCWSTR lpPrivilegeName) -{ - HANDLE hToken; - DWORD dwSize; - PTOKEN_PRIVILEGES lpPrivileges; - LUID PrivilegeLuid; - DWORD i; - BOOL bResult = FALSE; - - TRACE("%s\n", debugstr_w(lpPrivilegeName)); - - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) - return FALSE; - - if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize)) - { - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - { - CloseHandle(hToken); - return FALSE; - } - } - - lpPrivileges = MyMalloc(dwSize); - if (lpPrivileges == NULL) - { - CloseHandle(hToken); - return FALSE; - } - - if (!GetTokenInformation(hToken, TokenPrivileges, lpPrivileges, dwSize, &dwSize)) - { - MyFree(lpPrivileges); - CloseHandle(hToken); - return FALSE; - } - - CloseHandle(hToken); - - if (!LookupPrivilegeValueW(NULL, lpPrivilegeName, &PrivilegeLuid)) - { - MyFree(lpPrivileges); - return FALSE; - } - - for (i = 0; i < lpPrivileges->PrivilegeCount; i++) - { - if (lpPrivileges->Privileges[i].Luid.HighPart == PrivilegeLuid.HighPart && - lpPrivileges->Privileges[i].Luid.LowPart == PrivilegeLuid.LowPart) - { - bResult = TRUE; - } - } - - MyFree(lpPrivileges); - - return bResult; -} - - -/************************************************************************** - * EnablePrivilege [SETUPAPI.@] - * - * Enables or disables one of the current users privileges. - * - * PARAMS - * lpPrivilegeName [I] Name of the privilege to be changed - * bEnable [I] TRUE: Enables the privilege - * FALSE: Disables the privilege - * - * RETURNS - * Success: TRUE - * Failure: FALSE - */ -BOOL WINAPI EnablePrivilege(LPCWSTR lpPrivilegeName, BOOL bEnable) -{ - TOKEN_PRIVILEGES Privileges; - HANDLE hToken; - BOOL bResult; - - TRACE("%s %s\n", debugstr_w(lpPrivilegeName), bEnable ? "TRUE" : "FALSE"); - - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) - return FALSE; - - Privileges.PrivilegeCount = 1; - Privileges.Privileges[0].Attributes = (bEnable) ? SE_PRIVILEGE_ENABLED : 0; - - if (!LookupPrivilegeValueW(NULL, lpPrivilegeName, - &Privileges.Privileges[0].Luid)) - { - CloseHandle(hToken); - return FALSE; - } - - bResult = AdjustTokenPrivileges(hToken, FALSE, &Privileges, 0, NULL, NULL); - - CloseHandle(hToken); - - return bResult; -} - - -/************************************************************************** - * DelayedMove [SETUPAPI.@] - * - * Moves a file upon the next reboot. - * - * PARAMS - * lpExistingFileName [I] Current file name - * lpNewFileName [I] New file name - * - * RETURNS - * Success: TRUE - * Failure: FALSE - */ -BOOL WINAPI DelayedMove(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName) -{ - if (OsVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) - { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; - } - - return MoveFileExW(lpExistingFileName, lpNewFileName, - MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT); -} - - -/************************************************************************** - * FileExists [SETUPAPI.@] - * - * Checks whether a file exists. - * - * PARAMS - * lpFileName [I] Name of the file to check - * lpNewFileName [O] Optional information about the existing file - * - * RETURNS - * Success: TRUE - * Failure: FALSE - */ -BOOL WINAPI FileExists(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFileFindData) -{ - WIN32_FIND_DATAW FindData; - HANDLE hFind; - UINT uErrorMode; - DWORD dwError; - - uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); - - hFind = FindFirstFileW(lpFileName, &FindData); - if (hFind == INVALID_HANDLE_VALUE) - { - dwError = GetLastError(); - SetErrorMode(uErrorMode); - SetLastError(dwError); - return FALSE; - } - - FindClose(hFind); - - if (lpFileFindData) - memcpy(lpFileFindData, &FindData, sizeof(WIN32_FIND_DATAW)); - - SetErrorMode(uErrorMode); - - return TRUE; -} - - -/************************************************************************** - * CaptureStringArg [SETUPAPI.@] - * - * Captures a UNICODE string. - * - * PARAMS - * lpSrc [I] UNICODE string to be captured - * lpDst [O] Pointer to the captured UNICODE string - * - * RETURNS - * Success: ERROR_SUCCESS - * Failure: ERROR_INVALID_PARAMETER - * - * NOTE - * Call MyFree to release the captured UNICODE string. - */ -DWORD WINAPI CaptureStringArg(LPCWSTR pSrc, LPWSTR *pDst) -{ - if (pDst == NULL) - return ERROR_INVALID_PARAMETER; - - *pDst = DuplicateString(pSrc); - - return ERROR_SUCCESS; -} - - -/************************************************************************** - * CaptureAndConvertAnsiArg [SETUPAPI.@] - * - * Captures an ANSI string and converts it to a UNICODE string. - * - * PARAMS - * lpSrc [I] ANSI string to be captured - * lpDst [O] Pointer to the captured UNICODE string - * - * RETURNS - * Success: ERROR_SUCCESS - * Failure: ERROR_INVALID_PARAMETER - * - * NOTE - * Call MyFree to release the captured UNICODE string. - */ -DWORD WINAPI CaptureAndConvertAnsiArg(LPCSTR pSrc, LPWSTR *pDst) -{ - if (pDst == NULL) - return ERROR_INVALID_PARAMETER; - - *pDst = MultiByteToUnicode(pSrc, CP_ACP); - - return ERROR_SUCCESS; -} +/* + * Setupapi miscellaneous functions + * + * Copyright 2005 Eric Kohl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winreg.h" +#include "setupapi.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +#include "setupapi_private.h" + + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + + +/************************************************************************** + * MyFree [SETUPAPI.@] + * + * Frees an allocated memory block from the process heap. + * + * PARAMS + * lpMem [I] pointer to memory block which will be freed + * + * RETURNS + * None + */ +VOID WINAPI MyFree(LPVOID lpMem) +{ + TRACE("%p\n", lpMem); + HeapFree(GetProcessHeap(), 0, lpMem); +} + + +/************************************************************************** + * MyMalloc [SETUPAPI.@] + * + * Allocates memory block from the process heap. + * + * PARAMS + * dwSize [I] size of the allocated memory block + * + * RETURNS + * Success: pointer to allocated memory block + * Failure: NULL + */ +LPVOID WINAPI MyMalloc(DWORD dwSize) +{ + TRACE("%lu\n", dwSize); + return HeapAlloc(GetProcessHeap(), 0, dwSize); +} + + +/************************************************************************** + * MyRealloc [SETUPAPI.@] + * + * Changes the size of an allocated memory block or allocates a memory + * block from the process heap. + * + * PARAMS + * lpSrc [I] pointer to memory block which will be resized + * dwSize [I] new size of the memory block + * + * RETURNS + * Success: pointer to the resized memory block + * Failure: NULL + * + * NOTES + * If lpSrc is a NULL-pointer, then MyRealloc allocates a memory + * block like MyMalloc. + */ +LPVOID WINAPI MyRealloc(LPVOID lpSrc, DWORD dwSize) +{ + TRACE("%p %lu\n", lpSrc, dwSize); + + if (lpSrc == NULL) + return HeapAlloc(GetProcessHeap(), 0, dwSize); + + return HeapReAlloc(GetProcessHeap(), 0, lpSrc, dwSize); +} + + +/************************************************************************** + * DuplicateString [SETUPAPI.@] + * + * Duplicates a unicode string. + * + * PARAMS + * lpSrc [I] pointer to the unicode string that will be duplicated + * + * RETURNS + * Success: pointer to the duplicated unicode string + * Failure: NULL + * + * NOTES + * Call MyFree() to release the duplicated string. + */ +LPWSTR WINAPI DuplicateString(LPCWSTR lpSrc) +{ + LPWSTR lpDst; + + TRACE("%s\n", debugstr_w(lpSrc)); + + lpDst = MyMalloc((lstrlenW(lpSrc) + 1) * sizeof(WCHAR)); + if (lpDst == NULL) + return NULL; + + strcpyW(lpDst, lpSrc); + + return lpDst; +} + + +/************************************************************************** + * QueryRegistryValue [SETUPAPI.@] + * + * Retrieves value data from the registry and allocates memory for the + * value data. + * + * PARAMS + * hKey [I] Handle of the key to query + * lpValueName [I] Name of value under hkey to query + * lpData [O] Destination for the values contents, + * lpType [O] Destination for the value type + * lpcbData [O] Destination for the size of data + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: Otherwise + * + * NOTES + * Use MyFree to release the lpData buffer. + */ +LONG WINAPI QueryRegistryValue(HKEY hKey, + LPCWSTR lpValueName, + LPBYTE *lpData, + LPDWORD lpType, + LPDWORD lpcbData) +{ + LONG lError; + + TRACE("%lx %s %p %p %p\n", + hKey, debugstr_w(lpValueName), lpData, lpType, lpcbData); + + /* Get required buffer size */ + *lpcbData = 0; + lError = RegQueryValueExW(hKey, lpValueName, 0, lpType, NULL, lpcbData); + if (lError != ERROR_SUCCESS) + return lError; + + /* Allocate buffer */ + *lpData = MyMalloc(*lpcbData); + if (*lpData == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + /* Query registry value */ + lError = RegQueryValueExW(hKey, lpValueName, 0, lpType, *lpData, lpcbData); + if (lError != ERROR_SUCCESS) + MyFree(*lpData); + + return lError; +} + + +/************************************************************************** + * IsUserAdmin [SETUPAPI.@] + * + * Checks whether the current user is a member of the Administrators group. + * + * PARAMS + * None + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL WINAPI IsUserAdmin(VOID) +{ + SID_IDENTIFIER_AUTHORITY Authority = {SECURITY_NT_AUTHORITY}; + HANDLE hToken; + DWORD dwSize; + PTOKEN_GROUPS lpGroups; + PSID lpSid; + DWORD i; + BOOL bResult = FALSE; + + TRACE("\n"); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + return FALSE; + } + + if (!GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize)) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + CloseHandle(hToken); + return FALSE; + } + } + + lpGroups = MyMalloc(dwSize); + if (lpGroups == NULL) + { + CloseHandle(hToken); + return FALSE; + } + + if (!GetTokenInformation(hToken, TokenGroups, lpGroups, dwSize, &dwSize)) + { + MyFree(lpGroups); + CloseHandle(hToken); + return FALSE; + } + + CloseHandle(hToken); + + if (!AllocateAndInitializeSid(&Authority, 2, SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, + &lpSid)) + { + MyFree(lpGroups); + return FALSE; + } + + for (i = 0; i < lpGroups->GroupCount; i++) + { + if (EqualSid(lpSid, &lpGroups->Groups[i].Sid)) + { + bResult = TRUE; + break; + } + } + + FreeSid(lpSid); + MyFree(lpGroups); + + return bResult; +} + + +/************************************************************************** + * MultiByteToUnicode [SETUPAPI.@] + * + * Converts a multi-byte string to a Unicode string. + * + * PARAMS + * lpMultiByteStr [I] Multi-byte string to be converted + * uCodePage [I] Code page + * + * RETURNS + * Success: pointer to the converted Unicode string + * Failure: NULL + * + * NOTE + * Use MyFree to release the returned Unicode string. + */ +LPWSTR WINAPI MultiByteToUnicode(LPCSTR lpMultiByteStr, UINT uCodePage) +{ + LPWSTR lpUnicodeStr; + int nLength; + + TRACE("%s %lu\n", debugstr_a(lpMultiByteStr), uCodePage); + + nLength = MultiByteToWideChar(uCodePage, 0, lpMultiByteStr, + -1, NULL, 0); + if (nLength == 0) + return NULL; + + lpUnicodeStr = MyMalloc(nLength * sizeof(WCHAR)); + if (lpUnicodeStr == NULL) + return NULL; + + if (!MultiByteToWideChar(uCodePage, 0, lpMultiByteStr, + nLength, lpUnicodeStr, nLength)) + { + MyFree(lpUnicodeStr); + return NULL; + } + + return lpUnicodeStr; +} + + +/************************************************************************** + * UnicodeToMultiByte [SETUPAPI.@] + * + * Converts a Unicode string to a multi-byte string. + * + * PARAMS + * lpUnicodeStr [I] Unicode string to be converted + * uCodePage [I] Code page + * + * RETURNS + * Success: pointer to the converted multi-byte string + * Failure: NULL + * + * NOTE + * Use MyFree to release the returned multi-byte string. + */ +LPSTR WINAPI UnicodeToMultiByte(LPCWSTR lpUnicodeStr, UINT uCodePage) +{ + LPSTR lpMultiByteStr; + int nLength; + + TRACE("%s %lu\n", debugstr_w(lpUnicodeStr), uCodePage); + + nLength = WideCharToMultiByte(uCodePage, 0, lpUnicodeStr, -1, + NULL, 0, NULL, NULL); + if (nLength == 0) + return NULL; + + lpMultiByteStr = MyMalloc(nLength); + if (lpMultiByteStr == NULL) + return NULL; + + if (!WideCharToMultiByte(uCodePage, 0, lpUnicodeStr, -1, + lpMultiByteStr, nLength, NULL, NULL)) + { + MyFree(lpMultiByteStr); + return NULL; + } + + return lpMultiByteStr; +} + + +/************************************************************************** + * DoesUserHavePrivilege [SETUPAPI.@] + * + * Check whether the current user has got a given privilege. + * + * PARAMS + * lpPrivilegeName [I] Name of the privilege to be checked + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL WINAPI DoesUserHavePrivilege(LPCWSTR lpPrivilegeName) +{ + HANDLE hToken; + DWORD dwSize; + PTOKEN_PRIVILEGES lpPrivileges; + LUID PrivilegeLuid; + DWORD i; + BOOL bResult = FALSE; + + TRACE("%s\n", debugstr_w(lpPrivilegeName)); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + return FALSE; + + if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize)) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + CloseHandle(hToken); + return FALSE; + } + } + + lpPrivileges = MyMalloc(dwSize); + if (lpPrivileges == NULL) + { + CloseHandle(hToken); + return FALSE; + } + + if (!GetTokenInformation(hToken, TokenPrivileges, lpPrivileges, dwSize, &dwSize)) + { + MyFree(lpPrivileges); + CloseHandle(hToken); + return FALSE; + } + + CloseHandle(hToken); + + if (!LookupPrivilegeValueW(NULL, lpPrivilegeName, &PrivilegeLuid)) + { + MyFree(lpPrivileges); + return FALSE; + } + + for (i = 0; i < lpPrivileges->PrivilegeCount; i++) + { + if (lpPrivileges->Privileges[i].Luid.HighPart == PrivilegeLuid.HighPart && + lpPrivileges->Privileges[i].Luid.LowPart == PrivilegeLuid.LowPart) + { + bResult = TRUE; + } + } + + MyFree(lpPrivileges); + + return bResult; +} + + +/************************************************************************** + * EnablePrivilege [SETUPAPI.@] + * + * Enables or disables one of the current users privileges. + * + * PARAMS + * lpPrivilegeName [I] Name of the privilege to be changed + * bEnable [I] TRUE: Enables the privilege + * FALSE: Disables the privilege + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL WINAPI EnablePrivilege(LPCWSTR lpPrivilegeName, BOOL bEnable) +{ + TOKEN_PRIVILEGES Privileges; + HANDLE hToken; + BOOL bResult; + + TRACE("%s %s\n", debugstr_w(lpPrivilegeName), bEnable ? "TRUE" : "FALSE"); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + return FALSE; + + Privileges.PrivilegeCount = 1; + Privileges.Privileges[0].Attributes = (bEnable) ? SE_PRIVILEGE_ENABLED : 0; + + if (!LookupPrivilegeValueW(NULL, lpPrivilegeName, + &Privileges.Privileges[0].Luid)) + { + CloseHandle(hToken); + return FALSE; + } + + bResult = AdjustTokenPrivileges(hToken, FALSE, &Privileges, 0, NULL, NULL); + + CloseHandle(hToken); + + return bResult; +} + + +/************************************************************************** + * DelayedMove [SETUPAPI.@] + * + * Moves a file upon the next reboot. + * + * PARAMS + * lpExistingFileName [I] Current file name + * lpNewFileName [I] New file name + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL WINAPI DelayedMove(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName) +{ + if (OsVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) + { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; + } + + return MoveFileExW(lpExistingFileName, lpNewFileName, + MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT); +} + + +/************************************************************************** + * FileExists [SETUPAPI.@] + * + * Checks whether a file exists. + * + * PARAMS + * lpFileName [I] Name of the file to check + * lpNewFileName [O] Optional information about the existing file + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL WINAPI FileExists(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFileFindData) +{ + WIN32_FIND_DATAW FindData; + HANDLE hFind; + UINT uErrorMode; + DWORD dwError; + + uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); + + hFind = FindFirstFileW(lpFileName, &FindData); + if (hFind == INVALID_HANDLE_VALUE) + { + dwError = GetLastError(); + SetErrorMode(uErrorMode); + SetLastError(dwError); + return FALSE; + } + + FindClose(hFind); + + if (lpFileFindData) + memcpy(lpFileFindData, &FindData, sizeof(WIN32_FIND_DATAW)); + + SetErrorMode(uErrorMode); + + return TRUE; +} + + +/************************************************************************** + * CaptureStringArg [SETUPAPI.@] + * + * Captures a UNICODE string. + * + * PARAMS + * lpSrc [I] UNICODE string to be captured + * lpDst [O] Pointer to the captured UNICODE string + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: ERROR_INVALID_PARAMETER + * + * NOTE + * Call MyFree to release the captured UNICODE string. + */ +DWORD WINAPI CaptureStringArg(LPCWSTR pSrc, LPWSTR *pDst) +{ + if (pDst == NULL) + return ERROR_INVALID_PARAMETER; + + *pDst = DuplicateString(pSrc); + + return ERROR_SUCCESS; +} + + +/************************************************************************** + * CaptureAndConvertAnsiArg [SETUPAPI.@] + * + * Captures an ANSI string and converts it to a UNICODE string. + * + * PARAMS + * lpSrc [I] ANSI string to be captured + * lpDst [O] Pointer to the captured UNICODE string + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: ERROR_INVALID_PARAMETER + * + * NOTE + * Call MyFree to release the captured UNICODE string. + */ +DWORD WINAPI CaptureAndConvertAnsiArg(LPCSTR pSrc, LPWSTR *pDst) +{ + if (pDst == NULL) + return ERROR_INVALID_PARAMETER; + + *pDst = MultiByteToUnicode(pSrc, CP_ACP); + + return ERROR_SUCCESS; +} diff --git a/reactos/lib/setupapi/parser.c b/reactos/lib/setupapi/parser.c index 53c9e3968b8..74664bd4b82 100644 --- a/reactos/lib/setupapi/parser.c +++ b/reactos/lib/setupapi/parser.c @@ -1,1839 +1,1839 @@ -/* - * INF file parsing - * - * Copyright 2002 Alexandre Julliard for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" -#include "wine/port.h" - -#include -#include -#include -#include -#include - -#include "windef.h" -#include "winbase.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "winreg.h" -#include "winternl.h" -#include "winerror.h" -#include "setupapi.h" -#include "setupapi_private.h" - -#include "wine/unicode.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -#define CONTROL_Z '\x1a' -#define MAX_SECTION_NAME_LEN 255 -#define MAX_FIELD_LEN 511 /* larger fields get silently truncated */ -/* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */ -#define MAX_STRING_LEN (MAX_INF_STRING_LENGTH+1) - -/* inf file structure definitions */ - -struct field -{ - const WCHAR *text; /* field text */ -}; - -struct line -{ - int first_field; /* index of first field in field array */ - int nb_fields; /* number of fields in line */ - int key_field; /* index of field for key or -1 if no key */ -}; - -struct section -{ - const WCHAR *name; /* section name */ - unsigned int nb_lines; /* number of used lines */ - unsigned int alloc_lines; /* total number of allocated lines in array below */ - struct line lines[16]; /* lines information (grown dynamically, 16 is initial size) */ -}; - -struct inf_file -{ - struct inf_file *next; /* next appended file */ - WCHAR *strings; /* buffer for string data (section names and field values) */ - WCHAR *string_pos; /* position of next available string in buffer */ - unsigned int nb_sections; /* number of used sections */ - unsigned int alloc_sections; /* total number of allocated section pointers */ - struct section **sections; /* section pointers array */ - unsigned int nb_fields; - unsigned int alloc_fields; - struct field *fields; - int strings_section; /* index of [Strings] section or -1 if none */ - WCHAR *src_root; /* source root directory */ -}; - -/* parser definitions */ - -enum parser_state -{ - LINE_START, /* at beginning of a line */ - SECTION_NAME, /* parsing a section name */ - KEY_NAME, /* parsing a key name */ - VALUE_NAME, /* parsing a value name */ - EOL_BACKSLASH, /* backslash at end of line */ - QUOTES, /* inside quotes */ - LEADING_SPACES, /* leading spaces */ - TRAILING_SPACES, /* trailing spaces */ - COMMENT, /* inside a comment */ - NB_PARSER_STATES -}; - -struct parser -{ - const WCHAR *start; /* start position of item being parsed */ - const WCHAR *end; /* end of buffer */ - struct inf_file *file; /* file being built */ - enum parser_state state; /* current parser state */ - enum parser_state stack[4]; /* state stack */ - int stack_pos; /* current pos in stack */ - - int cur_section; /* index of section being parsed*/ - struct line *line; /* current line */ - unsigned int line_pos; /* current line position in file */ - unsigned int error; /* error code */ - unsigned int token_len; /* current token len */ - WCHAR token[MAX_FIELD_LEN+1]; /* current token */ -}; - -typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos ); - -/* parser state machine functions */ -static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos ); -static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos ); -static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos ); -static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos ); -static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos ); -static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos ); -static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos ); -static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos ); -static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos ); - -static const parser_state_func parser_funcs[NB_PARSER_STATES] = -{ - line_start_state, /* LINE_START */ - section_name_state, /* SECTION_NAME */ - key_name_state, /* KEY_NAME */ - value_name_state, /* VALUE_NAME */ - eol_backslash_state, /* EOL_BACKSLASH */ - quotes_state, /* QUOTES */ - leading_spaces_state, /* LEADING_SPACES */ - trailing_spaces_state, /* TRAILING_SPACES */ - comment_state /* COMMENT */ -}; - - -/* Unicode string constants */ -static const WCHAR Version[] = {'V','e','r','s','i','o','n',0}; -static const WCHAR Signature[] = {'S','i','g','n','a','t','u','r','e',0}; -static const WCHAR Chicago[] = {'$','C','h','i','c','a','g','o','$',0}; -static const WCHAR WindowsNT[] = {'$','W','i','n','d','o','w','s',' ','N','T','$',0}; -static const WCHAR Windows95[] = {'$','W','i','n','d','o','w','s',' ','9','5','$',0}; -static const WCHAR LayoutFile[] = {'L','a','y','o','u','t','F','i','l','e',0}; - -/* extend an array, allocating more memory if necessary */ -static void *grow_array( void *array, unsigned int *count, size_t elem ) -{ - void *new_array; - unsigned int new_count = *count + *count / 2; - if (new_count < 32) new_count = 32; - - if (array) - new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, array, new_count * elem ); - else - new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * elem ); - - if (new_array) - *count = new_count; - else - HeapFree( GetProcessHeap(), 0, array ); - return new_array; -} - - -/* find a section by name */ -static int find_section( struct inf_file *file, const WCHAR *name ) -{ - unsigned int i; - - for (i = 0; i < file->nb_sections; i++) - if (!strcmpiW( name, file->sections[i]->name )) return i; - return -1; -} - - -/* find a line by name */ -static struct line *find_line( struct inf_file *file, int section_index, const WCHAR *name ) -{ - struct section *section; - struct line *line; - int i; - - if (section_index < 0 || section_index >= file->nb_sections) return NULL; - section = file->sections[section_index]; - for (i = 0, line = section->lines; i < section->nb_lines; i++, line++) - { - if (line->key_field == -1) continue; - if (!strcmpiW( name, file->fields[line->key_field].text )) return line; - } - return NULL; -} - - -/* add a section to the file and return the section index */ -static int add_section( struct inf_file *file, const WCHAR *name ) -{ - struct section *section; - - if (file->nb_sections >= file->alloc_sections) - { - if (!(file->sections = grow_array( file->sections, &file->alloc_sections, - sizeof(file->sections[0]) ))) return -1; - } - if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) ))) return -1; - section->name = name; - section->nb_lines = 0; - section->alloc_lines = sizeof(section->lines)/sizeof(section->lines[0]); - file->sections[file->nb_sections] = section; - return file->nb_sections++; -} - - -/* add a line to a given section */ -static struct line *add_line( struct inf_file *file, int section_index ) -{ - struct section *section; - struct line *line; - - assert( section_index >= 0 && section_index < file->nb_sections ); - - section = file->sections[section_index]; - if (section->nb_lines == section->alloc_lines) /* need to grow the section */ - { - int size = sizeof(*section) - sizeof(section->lines) + 2*section->alloc_lines*sizeof(*line); - if (!(section = HeapReAlloc( GetProcessHeap(), 0, section, size ))) return NULL; - section->alloc_lines *= 2; - file->sections[section_index] = section; - } - line = §ion->lines[section->nb_lines++]; - line->first_field = file->nb_fields; - line->nb_fields = 0; - line->key_field = -1; - return line; -} - - -/* retrieve a given line from section/line index */ -inline static struct line *get_line( struct inf_file *file, unsigned int section_index, - unsigned int line_index ) -{ - struct section *section; - - if (section_index >= file->nb_sections) return NULL; - section = file->sections[section_index]; - if (line_index >= section->nb_lines) return NULL; - return §ion->lines[line_index]; -} - - -/* retrieve a given field from section/line/field index */ -static struct field *get_field( struct inf_file *file, int section_index, int line_index, - int field_index ) -{ - struct line *line = get_line( file, section_index, line_index ); - - if (!line) return NULL; - if (!field_index) /* get the key */ - { - if (line->key_field == -1) return NULL; - return &file->fields[line->key_field]; - } - field_index--; - if (field_index >= line->nb_fields) return NULL; - return &file->fields[line->first_field + field_index]; -} - - -/* allocate a new field, growing the array if necessary */ -static struct field *add_field( struct inf_file *file, const WCHAR *text ) -{ - struct field *field; - - if (file->nb_fields >= file->alloc_fields) - { - if (!(file->fields = grow_array( file->fields, &file->alloc_fields, - sizeof(file->fields[0]) ))) return NULL; - } - field = &file->fields[file->nb_fields++]; - field->text = text; - return field; -} - - -/* retrieve the string substitution for a directory id */ -static const WCHAR *get_dirid_subst( int dirid, unsigned int *len ) -{ - extern const WCHAR *DIRID_get_string( HINF hinf, int dirid ); - const WCHAR *ret = DIRID_get_string( 0, dirid ); - if (ret) *len = strlenW(ret); - return ret; -} - - -/* retrieve the string substitution for a given string, or NULL if not found */ -/* if found, len is set to the substitution length */ -static const WCHAR *get_string_subst( struct inf_file *file, const WCHAR *str, unsigned int *len ) -{ - static const WCHAR percent = '%'; - - struct section *strings_section; - struct line *line; - struct field *field; - unsigned int i; - int dirid; - WCHAR *dirid_str, *end; - const WCHAR *ret = NULL; - - if (!*len) /* empty string (%%) is replaced by single percent */ - { - *len = 1; - return &percent; - } - if (file->strings_section == -1) goto not_found; - strings_section = file->sections[file->strings_section]; - for (i = 0, line = strings_section->lines; i < strings_section->nb_lines; i++, line++) - { - if (line->key_field == -1) continue; - if (strncmpiW( str, file->fields[line->key_field].text, *len )) continue; - if (!file->fields[line->key_field].text[*len]) break; - } - if (i == strings_section->nb_lines || !line->nb_fields) goto not_found; - field = &file->fields[line->first_field]; - *len = strlenW( field->text ); - return field->text; - - not_found: /* check for integer id */ - if ((dirid_str = HeapAlloc( GetProcessHeap(), 0, (*len+1) * sizeof(WCHAR) ))) - { - memcpy( dirid_str, str, *len * sizeof(WCHAR) ); - dirid_str[*len] = 0; - dirid = strtolW( dirid_str, &end, 10 ); - if (!*end) ret = get_dirid_subst( dirid, len ); - HeapFree( GetProcessHeap(), 0, dirid_str ); - return ret; - } - return NULL; -} - - -/* do string substitutions on the specified text */ -/* the buffer is assumed to be large enough */ -/* returns necessary length not including terminating null */ -unsigned int PARSER_string_substW( struct inf_file *file, const WCHAR *text, WCHAR *buffer, - unsigned int size ) -{ - const WCHAR *start, *subst, *p; - unsigned int len, total = 0; - int inside = 0; - - if (!buffer) size = MAX_STRING_LEN + 1; - for (p = start = text; *p; p++) - { - if (*p != '%') continue; - inside = !inside; - if (inside) /* start of a %xx% string */ - { - len = p - start; - if (len > size - 1) len = size - 1; - if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) ); - total += len; - size -= len; - start = p; - } - else /* end of the %xx% string, find substitution */ - { - len = p - start - 1; - subst = get_string_subst( file, start + 1, &len ); - if (!subst) - { - subst = start; - len = p - start + 1; - } - if (len > size - 1) len = size - 1; - if (buffer) memcpy( buffer + total, subst, len * sizeof(WCHAR) ); - total += len; - size -= len; - start = p + 1; - } - } - - if (start != p) /* unfinished string, copy it */ - { - len = p - start; - if (len > size - 1) len = size - 1; - if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) ); - total += len; - } - if (buffer && size) buffer[total] = 0; - return total; -} - - -/* do string substitutions on the specified text */ -/* the buffer is assumed to be large enough */ -/* returns necessary length not including terminating null */ -unsigned int PARSER_string_substA( struct inf_file *file, const WCHAR *text, char *buffer, - unsigned int size ) -{ - WCHAR buffW[MAX_STRING_LEN+1]; - DWORD ret; - - unsigned int len = PARSER_string_substW( file, text, buffW, sizeof(buffW)/sizeof(WCHAR) ); - if (!buffer) RtlUnicodeToMultiByteSize( &ret, buffW, len * sizeof(WCHAR) ); - else - { - RtlUnicodeToMultiByteN( buffer, size-1, &ret, buffW, len * sizeof(WCHAR) ); - buffer[ret] = 0; - } - return ret; -} - - -/* push some string data into the strings buffer */ -static WCHAR *push_string( struct inf_file *file, const WCHAR *string ) -{ - WCHAR *ret = file->string_pos; - strcpyW( ret, string ); - file->string_pos += strlenW( ret ) + 1; - return ret; -} - - -/* push the current state on the parser stack */ -inline static void push_state( struct parser *parser, enum parser_state state ) -{ - assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) ); - parser->stack[parser->stack_pos++] = state; -} - - -/* pop the current state */ -inline static void pop_state( struct parser *parser ) -{ - assert( parser->stack_pos ); - parser->state = parser->stack[--parser->stack_pos]; -} - - -/* set the parser state and return the previous one */ -inline static enum parser_state set_state( struct parser *parser, enum parser_state state ) -{ - enum parser_state ret = parser->state; - parser->state = state; - return ret; -} - - -/* check if the pointer points to an end of file */ -inline static int is_eof( struct parser *parser, const WCHAR *ptr ) -{ - return (ptr >= parser->end || *ptr == CONTROL_Z); -} - - -/* check if the pointer points to an end of line */ -inline static int is_eol( struct parser *parser, const WCHAR *ptr ) -{ - return (ptr >= parser->end || *ptr == CONTROL_Z || *ptr == '\n'); -} - - -/* push data from current token start up to pos into the current token */ -static int push_token( struct parser *parser, const WCHAR *pos ) -{ - int len = pos - parser->start; - const WCHAR *src = parser->start; - WCHAR *dst = parser->token + parser->token_len; - - if (len > MAX_FIELD_LEN - parser->token_len) len = MAX_FIELD_LEN - parser->token_len; - - parser->token_len += len; - for ( ; len > 0; len--, dst++, src++) *dst = *src ? *src : ' '; - *dst = 0; - parser->start = pos; - return 0; -} - - -/* add a section with the current token as name */ -static int add_section_from_token( struct parser *parser ) -{ - int section_index; - - if (parser->token_len > MAX_SECTION_NAME_LEN) - { - parser->error = ERROR_SECTION_NAME_TOO_LONG; - return -1; - } - if ((section_index = find_section( parser->file, parser->token )) == -1) - { - /* need to create a new one */ - const WCHAR *name = push_string( parser->file, parser->token ); - if ((section_index = add_section( parser->file, name )) == -1) - { - parser->error = ERROR_NOT_ENOUGH_MEMORY; - return -1; - } - } - parser->token_len = 0; - parser->cur_section = section_index; - return section_index; -} - - -/* add a field containing the current token to the current line */ -static struct field *add_field_from_token( struct parser *parser, int is_key ) -{ - struct field *field; - WCHAR *text; - - if (!parser->line) /* need to start a new line */ - { - if (parser->cur_section == -1) /* got a line before the first section */ - { - parser->error = ERROR_WRONG_INF_STYLE; - return NULL; - } - if (!(parser->line = add_line( parser->file, parser->cur_section ))) goto error; - } - else assert(!is_key); - - text = push_string( parser->file, parser->token ); - if ((field = add_field( parser->file, text ))) - { - if (!is_key) parser->line->nb_fields++; - else - { - /* replace first field by key field */ - parser->line->key_field = parser->line->first_field; - parser->line->first_field++; - } - parser->token_len = 0; - return field; - } - error: - parser->error = ERROR_NOT_ENOUGH_MEMORY; - return NULL; -} - - -/* close the current line and prepare for parsing a new one */ -static void close_current_line( struct parser *parser ) -{ - struct line *cur_line = parser->line; - - if (cur_line) - { - /* if line has a single field and no key, the field is the key too */ - if (cur_line->nb_fields == 1 && cur_line->key_field == -1) - cur_line->key_field = cur_line->first_field; - } - parser->line = NULL; -} - - -/* handler for parser LINE_START state */ -static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos ) -{ - const WCHAR *p; - - for (p = pos; !is_eof( parser, p ); p++) - { - switch(*p) - { - case '\n': - parser->line_pos++; - close_current_line( parser ); - break; - case ';': - push_state( parser, LINE_START ); - set_state( parser, COMMENT ); - return p + 1; - case '[': - parser->start = p + 1; - set_state( parser, SECTION_NAME ); - return p + 1; - default: - if (!isspaceW(*p)) - { - parser->start = p; - set_state( parser, KEY_NAME ); - return p; - } - break; - } - } - close_current_line( parser ); - return NULL; -} - - -/* handler for parser SECTION_NAME state */ -static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos ) -{ - const WCHAR *p; - - for (p = pos; !is_eol( parser, p ); p++) - { - if (*p == ']') - { - push_token( parser, p ); - if (add_section_from_token( parser ) == -1) return NULL; - push_state( parser, LINE_START ); - set_state( parser, COMMENT ); /* ignore everything else on the line */ - return p + 1; - } - } - parser->error = ERROR_BAD_SECTION_NAME_LINE; /* unfinished section name */ - return NULL; -} - - -/* handler for parser KEY_NAME state */ -static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos ) -{ - const WCHAR *p, *token_end = parser->start; - - for (p = pos; !is_eol( parser, p ); p++) - { - if (*p == ',') break; - switch(*p) - { - - case '=': - push_token( parser, token_end ); - if (!add_field_from_token( parser, 1 )) return NULL; - parser->start = p + 1; - push_state( parser, VALUE_NAME ); - set_state( parser, LEADING_SPACES ); - return p + 1; - case ';': - push_token( parser, token_end ); - if (!add_field_from_token( parser, 0 )) return NULL; - push_state( parser, LINE_START ); - set_state( parser, COMMENT ); - return p + 1; - case '"': - push_token( parser, token_end ); - parser->start = p + 1; - push_state( parser, KEY_NAME ); - set_state( parser, QUOTES ); - return p + 1; - case '\\': - push_token( parser, token_end ); - parser->start = p; - push_state( parser, KEY_NAME ); - set_state( parser, EOL_BACKSLASH ); - return p; - default: - if (!isspaceW(*p)) token_end = p + 1; - else - { - push_token( parser, p ); - push_state( parser, KEY_NAME ); - set_state( parser, TRAILING_SPACES ); - return p; - } - break; - } - } - push_token( parser, token_end ); - set_state( parser, VALUE_NAME ); - return p; -} - - -/* handler for parser VALUE_NAME state */ -static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos ) -{ - const WCHAR *p, *token_end = parser->start; - - for (p = pos; !is_eol( parser, p ); p++) - { - switch(*p) - { - case ';': - push_token( parser, token_end ); - if (!add_field_from_token( parser, 0 )) return NULL; - push_state( parser, LINE_START ); - set_state( parser, COMMENT ); - return p + 1; - case ',': - push_token( parser, token_end ); - if (!add_field_from_token( parser, 0 )) return NULL; - parser->start = p + 1; - push_state( parser, VALUE_NAME ); - set_state( parser, LEADING_SPACES ); - return p + 1; - case '"': - push_token( parser, token_end ); - parser->start = p + 1; - push_state( parser, VALUE_NAME ); - set_state( parser, QUOTES ); - return p + 1; - case '\\': - push_token( parser, token_end ); - parser->start = p; - push_state( parser, VALUE_NAME ); - set_state( parser, EOL_BACKSLASH ); - return p; - default: - if (!isspaceW(*p)) token_end = p + 1; - else - { - push_token( parser, p ); - push_state( parser, VALUE_NAME ); - set_state( parser, TRAILING_SPACES ); - return p; - } - break; - } - } - push_token( parser, token_end ); - if (!add_field_from_token( parser, 0 )) return NULL; - set_state( parser, LINE_START ); - return p; -} - - -/* handler for parser EOL_BACKSLASH state */ -static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos ) -{ - const WCHAR *p; - - for (p = pos; !is_eof( parser, p ); p++) - { - switch(*p) - { - case '\n': - parser->line_pos++; - parser->start = p + 1; - set_state( parser, LEADING_SPACES ); - return p + 1; - case '\\': - continue; - case ';': - push_state( parser, EOL_BACKSLASH ); - set_state( parser, COMMENT ); - return p + 1; - default: - if (isspaceW(*p)) continue; - push_token( parser, p ); - pop_state( parser ); - return p; - } - } - parser->start = p; - pop_state( parser ); - return p; -} - - -/* handler for parser QUOTES state */ -static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos ) -{ - const WCHAR *p, *token_end = parser->start; - - for (p = pos; !is_eol( parser, p ); p++) - { - if (*p == '"') - { - if (p+1 < parser->end && p[1] == '"') /* double quotes */ - { - push_token( parser, p + 1 ); - parser->start = token_end = p + 2; - p++; - } - else /* end of quotes */ - { - push_token( parser, p ); - parser->start = p + 1; - pop_state( parser ); - return p + 1; - } - } - } - push_token( parser, p ); - pop_state( parser ); - return p; -} - - -/* handler for parser LEADING_SPACES state */ -static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos ) -{ - const WCHAR *p; - - for (p = pos; !is_eol( parser, p ); p++) - { - if (*p == '\\') - { - parser->start = p; - set_state( parser, EOL_BACKSLASH ); - return p; - } - if (!isspaceW(*p)) break; - } - parser->start = p; - pop_state( parser ); - return p; -} - - -/* handler for parser TRAILING_SPACES state */ -static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos ) -{ - const WCHAR *p; - - for (p = pos; !is_eol( parser, p ); p++) - { - if (*p == '\\') - { - set_state( parser, EOL_BACKSLASH ); - return p; - } - if (!isspaceW(*p)) break; - } - pop_state( parser ); - return p; -} - - -/* handler for parser COMMENT state */ -static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos ) -{ - const WCHAR *p = pos; - - while (!is_eol( parser, p )) p++; - pop_state( parser ); - return p; -} - - -/* parse a complete buffer */ -static DWORD parse_buffer( struct inf_file *file, const WCHAR *buffer, const WCHAR *end, - UINT *error_line ) -{ - static const WCHAR Strings[] = {'S','t','r','i','n','g','s',0}; - - struct parser parser; - const WCHAR *pos = buffer; - - parser.start = buffer; - parser.end = end; - parser.file = file; - parser.line = NULL; - parser.state = LINE_START; - parser.stack_pos = 0; - parser.cur_section = -1; - parser.line_pos = 1; - parser.error = 0; - parser.token_len = 0; - - /* parser main loop */ - while (pos) pos = (parser_funcs[parser.state])( &parser, pos ); - - /* trim excess buffer space */ - if (file->alloc_sections > file->nb_sections) - { - file->sections = HeapReAlloc( GetProcessHeap(), 0, file->sections, - file->nb_sections * sizeof(file->sections[0]) ); - file->alloc_sections = file->nb_sections; - } - if (file->alloc_fields > file->nb_fields) - { - file->fields = HeapReAlloc( GetProcessHeap(), 0, file->fields, - file->nb_fields * sizeof(file->fields[0]) ); - file->alloc_fields = file->nb_fields; - } - file->strings = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, file->strings, - (file->string_pos - file->strings) * sizeof(WCHAR) ); - - if (parser.error) - { - if (error_line) *error_line = parser.line_pos; - return parser.error; - } - - /* find the [strings] section */ - file->strings_section = find_section( file, Strings ); - return 0; -} - - -/* append a child INF file to its parent list, in a thread-safe manner */ -static void append_inf_file( struct inf_file *parent, struct inf_file *child ) -{ - struct inf_file **ppnext = &parent->next; - child->next = NULL; - - for (;;) - { - struct inf_file *next = InterlockedCompareExchangePointer( (void **)ppnext, child, NULL ); - if (!next) return; - ppnext = &next->next; - } -} - - -/*********************************************************************** - * parse_file - * - * parse an INF file. - */ -static struct inf_file *parse_file( HANDLE handle, const WCHAR *class, UINT *error_line ) -{ - void *buffer; - DWORD err = 0; - struct inf_file *file; - - DWORD size = GetFileSize( handle, NULL ); - HANDLE mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, size, NULL ); - if (!mapping) return NULL; - buffer = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, size ); - NtClose( mapping ); - if (!buffer) return NULL; - - if (class) FIXME( "class %s not supported yet\n", debugstr_w(class) ); - - if (!(file = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*file) ))) - { - err = ERROR_NOT_ENOUGH_MEMORY; - goto done; - } - - /* we won't need more strings space than the size of the file, - * so we can preallocate it here - */ - if (!(file->strings = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) - { - err = ERROR_NOT_ENOUGH_MEMORY; - goto done; - } - file->string_pos = file->strings; - file->strings_section = -1; - - if (!RtlIsTextUnicode( buffer, size, NULL )) - { - WCHAR *new_buff = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ); - if (new_buff) - { - DWORD len = MultiByteToWideChar( CP_ACP, 0, buffer, size, new_buff, - size * sizeof(WCHAR) ); - err = parse_buffer( file, new_buff, new_buff + len, error_line ); - HeapFree( GetProcessHeap(), 0, new_buff ); - } - } - else err = parse_buffer( file, buffer, (WCHAR *)((char *)buffer + size), error_line ); - - if (!err) /* now check signature */ - { - int version_index = find_section( file, Version ); - if (version_index != -1) - { - struct line *line = find_line( file, version_index, Signature ); - if (line && line->nb_fields > 0) - { - struct field *field = file->fields + line->first_field; - if (!strcmpiW( field->text, Chicago )) goto done; - if (!strcmpiW( field->text, WindowsNT )) goto done; - if (!strcmpiW( field->text, Windows95 )) goto done; - } - } - err = ERROR_WRONG_INF_STYLE; - } - - done: - UnmapViewOfFile( buffer ); - if (err) - { - HeapFree( GetProcessHeap(), 0, file ); - SetLastError( err ); - file = NULL; - } - return file; -} - - -/*********************************************************************** - * PARSER_get_src_root - * - * Retrieve the source directory of an inf file. - */ -const WCHAR *PARSER_get_src_root( HINF hinf ) -{ - struct inf_file *file = hinf; - return file->src_root; -} - - -/*********************************************************************** - * PARSER_get_dest_dir - * - * retrieve a destination dir of the form "dirid,relative_path" in the given entry. - * returned buffer must be freed by caller. - */ -WCHAR *PARSER_get_dest_dir( INFCONTEXT *context ) -{ - const WCHAR *dir; - WCHAR *ptr, *ret; - INT dirid; - DWORD len1, len2; - - if (!SetupGetIntField( context, 1, &dirid )) return NULL; - if (!(dir = DIRID_get_string( context->Inf, dirid ))) return NULL; - len1 = strlenW(dir) + 1; - if (!SetupGetStringFieldW( context, 2, NULL, 0, &len2 )) len2 = 0; - if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len1+len2) * sizeof(WCHAR) ))) return NULL; - strcpyW( ret, dir ); - ptr = ret + strlenW(ret); - if (len2 && ptr > ret && ptr[-1] != '\\') *ptr++ = '\\'; - if (!SetupGetStringFieldW( context, 2, ptr, len2, NULL )) *ptr = 0; - return ret; -} - - -/*********************************************************************** - * SetupOpenInfFileA (SETUPAPI.@) - */ -HINF WINAPI SetupOpenInfFileA( PCSTR name, PCSTR class, DWORD style, UINT *error ) -{ - UNICODE_STRING nameW, classW; - HINF ret = (HINF)INVALID_HANDLE_VALUE; - - classW.Buffer = NULL; - if (class && !RtlCreateUnicodeStringFromAsciiz( &classW, class )) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return ret; - } - if (RtlCreateUnicodeStringFromAsciiz( &nameW, name )) - { - ret = SetupOpenInfFileW( nameW.Buffer, classW.Buffer, style, error ); - RtlFreeUnicodeString( &nameW ); - } - RtlFreeUnicodeString( &classW ); - return ret; -} - - -/*********************************************************************** - * SetupOpenInfFileW (SETUPAPI.@) - */ -HINF WINAPI SetupOpenInfFileW( PCWSTR name, PCWSTR class, DWORD style, UINT *error ) -{ - struct inf_file *file = NULL; - HANDLE handle; - WCHAR *path, *p; - UINT len; - - if (strchrW( name, '\\' ) || strchrW( name, '/' )) - { - if (!(len = GetFullPathNameW( name, 0, NULL, NULL ))) return (HINF)INVALID_HANDLE_VALUE; - if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return (HINF)INVALID_HANDLE_VALUE; - } - GetFullPathNameW( name, len, path, NULL ); - handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); - } - else /* try Windows directory */ - { - static const WCHAR Inf[] = {'\\','i','n','f','\\',0}; - static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0}; - - len = GetWindowsDirectoryW( NULL, 0 ) + strlenW(name) + 12; - if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return (HINF)INVALID_HANDLE_VALUE; - } - GetWindowsDirectoryW( path, len ); - p = path + strlenW(path); - strcpyW( p, Inf ); - strcatW( p, name ); - handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); - if (handle == INVALID_HANDLE_VALUE) - { - strcpyW( p, System32 ); - strcatW( p, name ); - handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); - } - } - - if (handle != INVALID_HANDLE_VALUE) - { - file = parse_file( handle, class, error ); - CloseHandle( handle ); - } - if (!file) - { - HeapFree( GetProcessHeap(), 0, path ); - return (HINF)INVALID_HANDLE_VALUE; - } - TRACE( "%s -> %p\n", debugstr_w(path), file ); - file->src_root = path; - if ((p = strrchrW( path, '\\' ))) p[1] = 0; /* remove file name */ - SetLastError( 0 ); - return (HINF)file; -} - - -/*********************************************************************** - * SetupOpenAppendInfFileA (SETUPAPI.@) - */ -BOOL WINAPI SetupOpenAppendInfFileA( PCSTR name, HINF parent_hinf, UINT *error ) -{ - HINF child_hinf; - - if (!name) return SetupOpenAppendInfFileW( NULL, parent_hinf, error ); - child_hinf = SetupOpenInfFileA( name, NULL, INF_STYLE_WIN4, error ); - if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE; - append_inf_file( parent_hinf, child_hinf ); - TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_a(name), child_hinf ); - return TRUE; -} - - -/*********************************************************************** - * SetupOpenAppendInfFileW (SETUPAPI.@) - */ -BOOL WINAPI SetupOpenAppendInfFileW( PCWSTR name, HINF parent_hinf, UINT *error ) -{ - HINF child_hinf; - - if (!name) - { - INFCONTEXT context; - WCHAR filename[MAX_PATH]; - int idx = 1; - - if (!SetupFindFirstLineW( parent_hinf, Version, LayoutFile, &context )) return FALSE; - while (SetupGetStringFieldW( &context, idx++, filename, - sizeof(filename)/sizeof(WCHAR), NULL )) - { - child_hinf = SetupOpenInfFileW( filename, NULL, INF_STYLE_WIN4, error ); - if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE; - append_inf_file( parent_hinf, child_hinf ); - TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(filename), child_hinf ); - } - return TRUE; - } - child_hinf = SetupOpenInfFileW( name, NULL, INF_STYLE_WIN4, error ); - if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE; - append_inf_file( parent_hinf, child_hinf ); - TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(name), child_hinf ); - return TRUE; -} - - -/*********************************************************************** - * SetupOpenMasterInf (SETUPAPI.@) - */ -HINF WINAPI SetupOpenMasterInf( VOID ) -{ - static const WCHAR Layout[] = {'\\','i','n','f','\\', 'l', 'a', 'y', 'o', 'u', 't', '.', 'i', 'n', 'f', 0}; - WCHAR Buffer[MAX_PATH]; - - GetWindowsDirectoryW( Buffer, MAX_PATH ); - strcatW( Buffer, Layout ); - return SetupOpenInfFileW( Buffer, NULL, INF_STYLE_WIN4, NULL); -} - - - -/*********************************************************************** - * SetupCloseInfFile (SETUPAPI.@) - */ -void WINAPI SetupCloseInfFile( HINF hinf ) -{ - struct inf_file *file = hinf; - unsigned int i; - - for (i = 0; i < file->nb_sections; i++) HeapFree( GetProcessHeap(), 0, file->sections[i] ); - HeapFree( GetProcessHeap(), 0, file->src_root ); - HeapFree( GetProcessHeap(), 0, file->sections ); - HeapFree( GetProcessHeap(), 0, file->fields ); - HeapFree( GetProcessHeap(), 0, file->strings ); - HeapFree( GetProcessHeap(), 0, file ); -} - - -/*********************************************************************** - * SetupGetLineCountA (SETUPAPI.@) - */ -LONG WINAPI SetupGetLineCountA( HINF hinf, PCSTR name ) -{ - UNICODE_STRING sectionW; - LONG ret = -1; - - if (!RtlCreateUnicodeStringFromAsciiz( §ionW, name )) - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - else - { - ret = SetupGetLineCountW( hinf, sectionW.Buffer ); - RtlFreeUnicodeString( §ionW ); - } - return ret; -} - - -/*********************************************************************** - * SetupGetLineCountW (SETUPAPI.@) - */ -LONG WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section ) -{ - struct inf_file *file = hinf; - int section_index; - LONG ret = -1; - - for (file = hinf; file; file = file->next) - { - if ((section_index = find_section( file, section )) == -1) continue; - if (ret == -1) ret = 0; - ret += file->sections[section_index]->nb_lines; - } - TRACE( "(%p,%s) returning %ld\n", hinf, debugstr_w(section), ret ); - SetLastError( (ret == -1) ? ERROR_SECTION_NOT_FOUND : 0 ); - return ret; -} - - -/*********************************************************************** - * SetupGetLineByIndexA (SETUPAPI.@) - */ -BOOL WINAPI SetupGetLineByIndexA( HINF hinf, PCSTR section, DWORD index, INFCONTEXT *context ) -{ - UNICODE_STRING sectionW; - BOOL ret = FALSE; - - if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - else - { - ret = SetupGetLineByIndexW( hinf, sectionW.Buffer, index, context ); - RtlFreeUnicodeString( §ionW ); - } - return ret; -} - - -/*********************************************************************** - * SetupGetLineByIndexW (SETUPAPI.@) - */ -BOOL WINAPI SetupGetLineByIndexW( HINF hinf, PCWSTR section, DWORD index, INFCONTEXT *context ) -{ - struct inf_file *file = hinf; - int section_index; - - SetLastError( ERROR_SECTION_NOT_FOUND ); - for (file = hinf; file; file = file->next) - { - if ((section_index = find_section( file, section )) == -1) continue; - SetLastError( ERROR_LINE_NOT_FOUND ); - if (index < file->sections[section_index]->nb_lines) - { - context->Inf = hinf; - context->CurrentInf = file; - context->Section = section_index; - context->Line = index; - SetLastError( 0 ); - TRACE( "(%p,%s): returning %d/%ld\n", - hinf, debugstr_w(section), section_index, index ); - return TRUE; - } - index -= file->sections[section_index]->nb_lines; - } - TRACE( "(%p,%s) not found\n", hinf, debugstr_w(section) ); - return FALSE; -} - - -/*********************************************************************** - * SetupFindFirstLineA (SETUPAPI.@) - */ -BOOL WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context ) -{ - UNICODE_STRING sectionW, keyW; - BOOL ret = FALSE; - - if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return FALSE; - } - - if (!key) ret = SetupFindFirstLineW( hinf, sectionW.Buffer, NULL, context ); - else - { - if (RtlCreateUnicodeStringFromAsciiz( &keyW, key )) - { - ret = SetupFindFirstLineW( hinf, sectionW.Buffer, keyW.Buffer, context ); - RtlFreeUnicodeString( &keyW ); - } - else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - } - RtlFreeUnicodeString( §ionW ); - return ret; -} - - -/*********************************************************************** - * SetupFindFirstLineW (SETUPAPI.@) - */ -BOOL WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context ) -{ - struct inf_file *file; - int section_index; - - SetLastError( ERROR_SECTION_NOT_FOUND ); - for (file = hinf; file; file = file->next) - { - if ((section_index = find_section( file, section )) == -1) continue; - if (key) - { - INFCONTEXT ctx; - ctx.Inf = hinf; - ctx.CurrentInf = file; - ctx.Section = section_index; - ctx.Line = -1; - return SetupFindNextMatchLineW( &ctx, key, context ); - } - SetLastError( ERROR_LINE_NOT_FOUND ); /* found at least one section */ - if (file->sections[section_index]->nb_lines) - { - context->Inf = hinf; - context->CurrentInf = file; - context->Section = section_index; - context->Line = 0; - SetLastError( 0 ); - TRACE( "(%p,%s,%s): returning %d/0\n", - hinf, debugstr_w(section), debugstr_w(key), section_index ); - return TRUE; - } - } - TRACE( "(%p,%s,%s): not found\n", hinf, debugstr_w(section), debugstr_w(key) ); - return FALSE; -} - - -/*********************************************************************** - * SetupFindNextLine (SETUPAPI.@) - */ -BOOL WINAPI SetupFindNextLine( PINFCONTEXT context_in, PINFCONTEXT context_out ) -{ - struct inf_file *file = context_in->CurrentInf; - struct section *section; - - if (context_in->Section >= file->nb_sections) goto error; - - section = file->sections[context_in->Section]; - if (context_in->Line+1 < section->nb_lines) - { - if (context_out != context_in) *context_out = *context_in; - context_out->Line++; - SetLastError( 0 ); - return TRUE; - } - - /* now search the appended files */ - - for (file = file->next; file; file = file->next) - { - int section_index = find_section( file, section->name ); - if (section_index == -1) continue; - if (file->sections[section_index]->nb_lines) - { - context_out->Inf = context_in->Inf; - context_out->CurrentInf = file; - context_out->Section = section_index; - context_out->Line = 0; - SetLastError( 0 ); - return TRUE; - } - } - error: - SetLastError( ERROR_LINE_NOT_FOUND ); - return FALSE; -} - - -/*********************************************************************** - * SetupFindNextMatchLineA (SETUPAPI.@) - */ -BOOL WINAPI SetupFindNextMatchLineA( PINFCONTEXT context_in, PCSTR key, - PINFCONTEXT context_out ) -{ - UNICODE_STRING keyW; - BOOL ret = FALSE; - - if (!key) return SetupFindNextLine( context_in, context_out ); - - if (!RtlCreateUnicodeStringFromAsciiz( &keyW, key )) - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - else - { - ret = SetupFindNextMatchLineW( context_in, keyW.Buffer, context_out ); - RtlFreeUnicodeString( &keyW ); - } - return ret; -} - - -/*********************************************************************** - * SetupFindNextMatchLineW (SETUPAPI.@) - */ -BOOL WINAPI SetupFindNextMatchLineW( PINFCONTEXT context_in, PCWSTR key, - PINFCONTEXT context_out ) -{ - struct inf_file *file = context_in->CurrentInf; - struct section *section; - struct line *line; - unsigned int i; - - if (!key) return SetupFindNextLine( context_in, context_out ); - - if (context_in->Section >= file->nb_sections) goto error; - - section = file->sections[context_in->Section]; - - for (i = context_in->Line+1, line = §ion->lines[i]; i < section->nb_lines; i++, line++) - { - if (line->key_field == -1) continue; - if (!strcmpiW( key, file->fields[line->key_field].text )) - { - if (context_out != context_in) *context_out = *context_in; - context_out->Line = i; - SetLastError( 0 ); - TRACE( "(%p,%s,%s): returning %d\n", - file, debugstr_w(section->name), debugstr_w(key), i ); - return TRUE; - } - } - - /* now search the appended files */ - - for (file = file->next; file; file = file->next) - { - int section_index = find_section( file, section->name ); - if (section_index == -1) continue; - section = file->sections[section_index]; - for (i = 0, line = section->lines; i < section->nb_lines; i++, line++) - { - if (line->key_field == -1) continue; - if (!strcmpiW( key, file->fields[line->key_field].text )) - { - context_out->Inf = context_in->Inf; - context_out->CurrentInf = file; - context_out->Section = section_index; - context_out->Line = i; - SetLastError( 0 ); - TRACE( "(%p,%s,%s): returning %d/%d\n", - file, debugstr_w(section->name), debugstr_w(key), section_index, i ); - return TRUE; - } - } - } - TRACE( "(%p,%s,%s): not found\n", - context_in->CurrentInf, debugstr_w(section->name), debugstr_w(key) ); - error: - SetLastError( ERROR_LINE_NOT_FOUND ); - return FALSE; -} - - -/*********************************************************************** - * SetupGetLineTextW (SETUPAPI.@) - */ -BOOL WINAPI SetupGetLineTextW( PINFCONTEXT context, HINF hinf, PCWSTR section_name, - PCWSTR key_name, PWSTR buffer, DWORD size, PDWORD required ) -{ - struct inf_file *file; - struct line *line; - struct field *field; - int i; - DWORD total = 0; - - if (!context) - { - INFCONTEXT new_context; - if (!SetupFindFirstLineW( hinf, section_name, key_name, &new_context )) return FALSE; - file = new_context.CurrentInf; - line = get_line( file, new_context.Section, new_context.Line ); - } - else - { - file = context->CurrentInf; - if (!(line = get_line( file, context->Section, context->Line ))) - { - SetLastError( ERROR_LINE_NOT_FOUND ); - return FALSE; - } - } - - for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) - total += PARSER_string_substW( file, field->text, NULL, 0 ) + 1; - - if (required) *required = total; - if (buffer) - { - if (total > size) - { - SetLastError( ERROR_INSUFFICIENT_BUFFER ); - return FALSE; - } - for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) - { - unsigned int len = PARSER_string_substW( file, field->text, buffer, size ); - if (i+1 < line->nb_fields) buffer[len] = ','; - buffer += len + 1; - } - } - return TRUE; -} - - -/*********************************************************************** - * SetupGetLineTextA (SETUPAPI.@) - */ -BOOL WINAPI SetupGetLineTextA( PINFCONTEXT context, HINF hinf, PCSTR section_name, - PCSTR key_name, PSTR buffer, DWORD size, PDWORD required ) -{ - struct inf_file *file; - struct line *line; - struct field *field; - int i; - DWORD total = 0; - - if (!context) - { - INFCONTEXT new_context; - if (!SetupFindFirstLineA( hinf, section_name, key_name, &new_context )) return FALSE; - file = new_context.CurrentInf; - line = get_line( file, new_context.Section, new_context.Line ); - } - else - { - file = context->CurrentInf; - if (!(line = get_line( file, context->Section, context->Line ))) - { - SetLastError( ERROR_LINE_NOT_FOUND ); - return FALSE; - } - } - - for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) - total += PARSER_string_substA( file, field->text, NULL, 0 ) + 1; - - if (required) *required = total; - if (buffer) - { - if (total > size) - { - SetLastError( ERROR_INSUFFICIENT_BUFFER ); - return FALSE; - } - for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) - { - unsigned int len = PARSER_string_substA( file, field->text, buffer, size ); - if (i+1 < line->nb_fields) buffer[len] = ','; - buffer += len + 1; - } - } - return TRUE; -} - - -/*********************************************************************** - * SetupGetFieldCount (SETUPAPI.@) - */ -DWORD WINAPI SetupGetFieldCount( PINFCONTEXT context ) -{ - struct inf_file *file = context->CurrentInf; - struct line *line = get_line( file, context->Section, context->Line ); - - if (!line) return 0; - return line->nb_fields; -} - - -/*********************************************************************** - * SetupGetStringFieldA (SETUPAPI.@) - */ -BOOL WINAPI SetupGetStringFieldA( PINFCONTEXT context, DWORD index, PSTR buffer, - DWORD size, PDWORD required ) -{ - struct inf_file *file = context->CurrentInf; - struct field *field = get_field( file, context->Section, context->Line, index ); - unsigned int len; - - SetLastError(0); - if (!field) return FALSE; - len = PARSER_string_substA( file, field->text, NULL, 0 ); - if (required) *required = len + 1; - if (buffer) - { - if (size <= len) - { - SetLastError( ERROR_INSUFFICIENT_BUFFER ); - return FALSE; - } - PARSER_string_substA( file, field->text, buffer, size ); - - TRACE( "context %p/%p/%d/%d index %ld returning %s\n", - context->Inf, context->CurrentInf, context->Section, context->Line, - index, debugstr_a(buffer) ); - } - return TRUE; -} - - -/*********************************************************************** - * SetupGetStringFieldW (SETUPAPI.@) - */ -BOOL WINAPI SetupGetStringFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer, - DWORD size, PDWORD required ) -{ - struct inf_file *file = context->CurrentInf; - struct field *field = get_field( file, context->Section, context->Line, index ); - unsigned int len; - - SetLastError(0); - if (!field) return FALSE; - len = PARSER_string_substW( file, field->text, NULL, 0 ); - if (required) *required = len + 1; - if (buffer) - { - if (size <= len) - { - SetLastError( ERROR_INSUFFICIENT_BUFFER ); - return FALSE; - } - PARSER_string_substW( file, field->text, buffer, size ); - - TRACE( "context %p/%p/%d/%d index %ld returning %s\n", - context->Inf, context->CurrentInf, context->Section, context->Line, - index, debugstr_w(buffer) ); - } - return TRUE; -} - - -/*********************************************************************** - * SetupGetIntField (SETUPAPI.@) - */ -BOOL WINAPI SetupGetIntField( PINFCONTEXT context, DWORD index, PINT result ) -{ - char localbuff[20]; - char *end, *buffer = localbuff; - DWORD required; - INT res; - BOOL ret = FALSE; - - if (!SetupGetStringFieldA( context, index, localbuff, sizeof(localbuff), &required )) - { - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE; - if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required ))) return FALSE; - if (!SetupGetStringFieldA( context, index, buffer, required, NULL )) goto done; - } - res = strtol( buffer, &end, 0 ); - if (end != buffer && !*end) - { - *result = res; - ret = TRUE; - } - else SetLastError( ERROR_INVALID_DATA ); - - done: - if (buffer != localbuff) HeapFree( GetProcessHeap(), 0, buffer ); - return ret; -} - - -/*********************************************************************** - * SetupGetBinaryField (SETUPAPI.@) - */ -BOOL WINAPI SetupGetBinaryField( PINFCONTEXT context, DWORD index, BYTE *buffer, - DWORD size, LPDWORD required ) -{ - struct inf_file *file = context->CurrentInf; - struct line *line = get_line( file, context->Section, context->Line ); - struct field *field; - int i; - - if (!line) - { - SetLastError( ERROR_LINE_NOT_FOUND ); - return FALSE; - } - if (!index || index >= line->nb_fields) - { - SetLastError( ERROR_INVALID_PARAMETER ); - return FALSE; - } - index--; /* fields start at 0 */ - if (required) *required = line->nb_fields - index; - if (!buffer) return TRUE; - if (size < line->nb_fields - index) - { - SetLastError( ERROR_INSUFFICIENT_BUFFER ); - return FALSE; - } - field = &file->fields[line->first_field + index]; - for (i = index; i < line->nb_fields; i++, field++) - { - const WCHAR *p; - DWORD value = 0; - for (p = field->text; *p && isxdigitW(*p); p++) - { - if ((value <<= 4) > 255) - { - SetLastError( ERROR_INVALID_DATA ); - return FALSE; - } - if (*p <= '9') value |= (*p - '0'); - else value |= (tolowerW(*p) - 'a' + 10); - } - buffer[i - index] = value; - } - if (TRACE_ON(setupapi)) - { - TRACE( "%p/%p/%d/%d index %ld returning", - context->Inf, context->CurrentInf, context->Section, context->Line, index ); - for (i = index; i < line->nb_fields; i++) TRACE( " %02x", buffer[i - index] ); - TRACE( "\n" ); - } - return TRUE; -} - - -/*********************************************************************** - * SetupGetMultiSzFieldA (SETUPAPI.@) - */ -BOOL WINAPI SetupGetMultiSzFieldA( PINFCONTEXT context, DWORD index, PSTR buffer, - DWORD size, LPDWORD required ) -{ - struct inf_file *file = context->CurrentInf; - struct line *line = get_line( file, context->Section, context->Line ); - struct field *field; - unsigned int len; - int i; - DWORD total = 1; - - if (!line) - { - SetLastError( ERROR_LINE_NOT_FOUND ); - return FALSE; - } - if (!index || index >= line->nb_fields) - { - SetLastError( ERROR_INVALID_PARAMETER ); - return FALSE; - } - index--; /* fields start at 0 */ - field = &file->fields[line->first_field + index]; - for (i = index; i < line->nb_fields; i++, field++) - { - if (!(len = PARSER_string_substA( file, field->text, NULL, 0 ))) break; - total += len + 1; - } - - if (required) *required = total; - if (!buffer) return TRUE; - if (total > size) - { - SetLastError( ERROR_INSUFFICIENT_BUFFER ); - return FALSE; - } - field = &file->fields[line->first_field + index]; - for (i = index; i < line->nb_fields; i++, field++) - { - if (!(len = PARSER_string_substA( file, field->text, buffer, size ))) break; - buffer += len + 1; - } - *buffer = 0; /* add final null */ - return TRUE; -} - - -/*********************************************************************** - * SetupGetMultiSzFieldW (SETUPAPI.@) - */ -BOOL WINAPI SetupGetMultiSzFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer, - DWORD size, LPDWORD required ) -{ - struct inf_file *file = context->CurrentInf; - struct line *line = get_line( file, context->Section, context->Line ); - struct field *field; - unsigned int len; - int i; - DWORD total = 1; - - if (!line) - { - SetLastError( ERROR_LINE_NOT_FOUND ); - return FALSE; - } - if (!index || index >= line->nb_fields) - { - SetLastError( ERROR_INVALID_PARAMETER ); - return FALSE; - } - index--; /* fields start at 0 */ - field = &file->fields[line->first_field + index]; - for (i = index; i < line->nb_fields; i++, field++) - { - if (!(len = PARSER_string_substW( file, field->text, NULL, 0 ))) break; - total += len + 1; - } - - if (required) *required = total; - if (!buffer) return TRUE; - if (total > size) - { - SetLastError( ERROR_INSUFFICIENT_BUFFER ); - return FALSE; - } - field = &file->fields[line->first_field + index]; - for (i = index; i < line->nb_fields; i++, field++) - { - if (!(len = PARSER_string_substW( file, field->text, buffer, size ))) break; - buffer += len + 1; - } - *buffer = 0; /* add final null */ - return TRUE; -} +/* + * INF file parsing + * + * Copyright 2002 Alexandre Julliard for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "winternl.h" +#include "winerror.h" +#include "setupapi.h" +#include "setupapi_private.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +#define CONTROL_Z '\x1a' +#define MAX_SECTION_NAME_LEN 255 +#define MAX_FIELD_LEN 511 /* larger fields get silently truncated */ +/* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */ +#define MAX_STRING_LEN (MAX_INF_STRING_LENGTH+1) + +/* inf file structure definitions */ + +struct field +{ + const WCHAR *text; /* field text */ +}; + +struct line +{ + int first_field; /* index of first field in field array */ + int nb_fields; /* number of fields in line */ + int key_field; /* index of field for key or -1 if no key */ +}; + +struct section +{ + const WCHAR *name; /* section name */ + unsigned int nb_lines; /* number of used lines */ + unsigned int alloc_lines; /* total number of allocated lines in array below */ + struct line lines[16]; /* lines information (grown dynamically, 16 is initial size) */ +}; + +struct inf_file +{ + struct inf_file *next; /* next appended file */ + WCHAR *strings; /* buffer for string data (section names and field values) */ + WCHAR *string_pos; /* position of next available string in buffer */ + unsigned int nb_sections; /* number of used sections */ + unsigned int alloc_sections; /* total number of allocated section pointers */ + struct section **sections; /* section pointers array */ + unsigned int nb_fields; + unsigned int alloc_fields; + struct field *fields; + int strings_section; /* index of [Strings] section or -1 if none */ + WCHAR *src_root; /* source root directory */ +}; + +/* parser definitions */ + +enum parser_state +{ + LINE_START, /* at beginning of a line */ + SECTION_NAME, /* parsing a section name */ + KEY_NAME, /* parsing a key name */ + VALUE_NAME, /* parsing a value name */ + EOL_BACKSLASH, /* backslash at end of line */ + QUOTES, /* inside quotes */ + LEADING_SPACES, /* leading spaces */ + TRAILING_SPACES, /* trailing spaces */ + COMMENT, /* inside a comment */ + NB_PARSER_STATES +}; + +struct parser +{ + const WCHAR *start; /* start position of item being parsed */ + const WCHAR *end; /* end of buffer */ + struct inf_file *file; /* file being built */ + enum parser_state state; /* current parser state */ + enum parser_state stack[4]; /* state stack */ + int stack_pos; /* current pos in stack */ + + int cur_section; /* index of section being parsed*/ + struct line *line; /* current line */ + unsigned int line_pos; /* current line position in file */ + unsigned int error; /* error code */ + unsigned int token_len; /* current token len */ + WCHAR token[MAX_FIELD_LEN+1]; /* current token */ +}; + +typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos ); + +/* parser state machine functions */ +static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos ); + +static const parser_state_func parser_funcs[NB_PARSER_STATES] = +{ + line_start_state, /* LINE_START */ + section_name_state, /* SECTION_NAME */ + key_name_state, /* KEY_NAME */ + value_name_state, /* VALUE_NAME */ + eol_backslash_state, /* EOL_BACKSLASH */ + quotes_state, /* QUOTES */ + leading_spaces_state, /* LEADING_SPACES */ + trailing_spaces_state, /* TRAILING_SPACES */ + comment_state /* COMMENT */ +}; + + +/* Unicode string constants */ +static const WCHAR Version[] = {'V','e','r','s','i','o','n',0}; +static const WCHAR Signature[] = {'S','i','g','n','a','t','u','r','e',0}; +static const WCHAR Chicago[] = {'$','C','h','i','c','a','g','o','$',0}; +static const WCHAR WindowsNT[] = {'$','W','i','n','d','o','w','s',' ','N','T','$',0}; +static const WCHAR Windows95[] = {'$','W','i','n','d','o','w','s',' ','9','5','$',0}; +static const WCHAR LayoutFile[] = {'L','a','y','o','u','t','F','i','l','e',0}; + +/* extend an array, allocating more memory if necessary */ +static void *grow_array( void *array, unsigned int *count, size_t elem ) +{ + void *new_array; + unsigned int new_count = *count + *count / 2; + if (new_count < 32) new_count = 32; + + if (array) + new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, array, new_count * elem ); + else + new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * elem ); + + if (new_array) + *count = new_count; + else + HeapFree( GetProcessHeap(), 0, array ); + return new_array; +} + + +/* find a section by name */ +static int find_section( struct inf_file *file, const WCHAR *name ) +{ + unsigned int i; + + for (i = 0; i < file->nb_sections; i++) + if (!strcmpiW( name, file->sections[i]->name )) return i; + return -1; +} + + +/* find a line by name */ +static struct line *find_line( struct inf_file *file, int section_index, const WCHAR *name ) +{ + struct section *section; + struct line *line; + int i; + + if (section_index < 0 || section_index >= file->nb_sections) return NULL; + section = file->sections[section_index]; + for (i = 0, line = section->lines; i < section->nb_lines; i++, line++) + { + if (line->key_field == -1) continue; + if (!strcmpiW( name, file->fields[line->key_field].text )) return line; + } + return NULL; +} + + +/* add a section to the file and return the section index */ +static int add_section( struct inf_file *file, const WCHAR *name ) +{ + struct section *section; + + if (file->nb_sections >= file->alloc_sections) + { + if (!(file->sections = grow_array( file->sections, &file->alloc_sections, + sizeof(file->sections[0]) ))) return -1; + } + if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) ))) return -1; + section->name = name; + section->nb_lines = 0; + section->alloc_lines = sizeof(section->lines)/sizeof(section->lines[0]); + file->sections[file->nb_sections] = section; + return file->nb_sections++; +} + + +/* add a line to a given section */ +static struct line *add_line( struct inf_file *file, int section_index ) +{ + struct section *section; + struct line *line; + + assert( section_index >= 0 && section_index < file->nb_sections ); + + section = file->sections[section_index]; + if (section->nb_lines == section->alloc_lines) /* need to grow the section */ + { + int size = sizeof(*section) - sizeof(section->lines) + 2*section->alloc_lines*sizeof(*line); + if (!(section = HeapReAlloc( GetProcessHeap(), 0, section, size ))) return NULL; + section->alloc_lines *= 2; + file->sections[section_index] = section; + } + line = §ion->lines[section->nb_lines++]; + line->first_field = file->nb_fields; + line->nb_fields = 0; + line->key_field = -1; + return line; +} + + +/* retrieve a given line from section/line index */ +inline static struct line *get_line( struct inf_file *file, unsigned int section_index, + unsigned int line_index ) +{ + struct section *section; + + if (section_index >= file->nb_sections) return NULL; + section = file->sections[section_index]; + if (line_index >= section->nb_lines) return NULL; + return §ion->lines[line_index]; +} + + +/* retrieve a given field from section/line/field index */ +static struct field *get_field( struct inf_file *file, int section_index, int line_index, + int field_index ) +{ + struct line *line = get_line( file, section_index, line_index ); + + if (!line) return NULL; + if (!field_index) /* get the key */ + { + if (line->key_field == -1) return NULL; + return &file->fields[line->key_field]; + } + field_index--; + if (field_index >= line->nb_fields) return NULL; + return &file->fields[line->first_field + field_index]; +} + + +/* allocate a new field, growing the array if necessary */ +static struct field *add_field( struct inf_file *file, const WCHAR *text ) +{ + struct field *field; + + if (file->nb_fields >= file->alloc_fields) + { + if (!(file->fields = grow_array( file->fields, &file->alloc_fields, + sizeof(file->fields[0]) ))) return NULL; + } + field = &file->fields[file->nb_fields++]; + field->text = text; + return field; +} + + +/* retrieve the string substitution for a directory id */ +static const WCHAR *get_dirid_subst( int dirid, unsigned int *len ) +{ + extern const WCHAR *DIRID_get_string( HINF hinf, int dirid ); + const WCHAR *ret = DIRID_get_string( 0, dirid ); + if (ret) *len = strlenW(ret); + return ret; +} + + +/* retrieve the string substitution for a given string, or NULL if not found */ +/* if found, len is set to the substitution length */ +static const WCHAR *get_string_subst( struct inf_file *file, const WCHAR *str, unsigned int *len ) +{ + static const WCHAR percent = '%'; + + struct section *strings_section; + struct line *line; + struct field *field; + unsigned int i; + int dirid; + WCHAR *dirid_str, *end; + const WCHAR *ret = NULL; + + if (!*len) /* empty string (%%) is replaced by single percent */ + { + *len = 1; + return &percent; + } + if (file->strings_section == -1) goto not_found; + strings_section = file->sections[file->strings_section]; + for (i = 0, line = strings_section->lines; i < strings_section->nb_lines; i++, line++) + { + if (line->key_field == -1) continue; + if (strncmpiW( str, file->fields[line->key_field].text, *len )) continue; + if (!file->fields[line->key_field].text[*len]) break; + } + if (i == strings_section->nb_lines || !line->nb_fields) goto not_found; + field = &file->fields[line->first_field]; + *len = strlenW( field->text ); + return field->text; + + not_found: /* check for integer id */ + if ((dirid_str = HeapAlloc( GetProcessHeap(), 0, (*len+1) * sizeof(WCHAR) ))) + { + memcpy( dirid_str, str, *len * sizeof(WCHAR) ); + dirid_str[*len] = 0; + dirid = strtolW( dirid_str, &end, 10 ); + if (!*end) ret = get_dirid_subst( dirid, len ); + HeapFree( GetProcessHeap(), 0, dirid_str ); + return ret; + } + return NULL; +} + + +/* do string substitutions on the specified text */ +/* the buffer is assumed to be large enough */ +/* returns necessary length not including terminating null */ +unsigned int PARSER_string_substW( struct inf_file *file, const WCHAR *text, WCHAR *buffer, + unsigned int size ) +{ + const WCHAR *start, *subst, *p; + unsigned int len, total = 0; + int inside = 0; + + if (!buffer) size = MAX_STRING_LEN + 1; + for (p = start = text; *p; p++) + { + if (*p != '%') continue; + inside = !inside; + if (inside) /* start of a %xx% string */ + { + len = p - start; + if (len > size - 1) len = size - 1; + if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) ); + total += len; + size -= len; + start = p; + } + else /* end of the %xx% string, find substitution */ + { + len = p - start - 1; + subst = get_string_subst( file, start + 1, &len ); + if (!subst) + { + subst = start; + len = p - start + 1; + } + if (len > size - 1) len = size - 1; + if (buffer) memcpy( buffer + total, subst, len * sizeof(WCHAR) ); + total += len; + size -= len; + start = p + 1; + } + } + + if (start != p) /* unfinished string, copy it */ + { + len = p - start; + if (len > size - 1) len = size - 1; + if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) ); + total += len; + } + if (buffer && size) buffer[total] = 0; + return total; +} + + +/* do string substitutions on the specified text */ +/* the buffer is assumed to be large enough */ +/* returns necessary length not including terminating null */ +unsigned int PARSER_string_substA( struct inf_file *file, const WCHAR *text, char *buffer, + unsigned int size ) +{ + WCHAR buffW[MAX_STRING_LEN+1]; + DWORD ret; + + unsigned int len = PARSER_string_substW( file, text, buffW, sizeof(buffW)/sizeof(WCHAR) ); + if (!buffer) RtlUnicodeToMultiByteSize( &ret, buffW, len * sizeof(WCHAR) ); + else + { + RtlUnicodeToMultiByteN( buffer, size-1, &ret, buffW, len * sizeof(WCHAR) ); + buffer[ret] = 0; + } + return ret; +} + + +/* push some string data into the strings buffer */ +static WCHAR *push_string( struct inf_file *file, const WCHAR *string ) +{ + WCHAR *ret = file->string_pos; + strcpyW( ret, string ); + file->string_pos += strlenW( ret ) + 1; + return ret; +} + + +/* push the current state on the parser stack */ +inline static void push_state( struct parser *parser, enum parser_state state ) +{ + assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) ); + parser->stack[parser->stack_pos++] = state; +} + + +/* pop the current state */ +inline static void pop_state( struct parser *parser ) +{ + assert( parser->stack_pos ); + parser->state = parser->stack[--parser->stack_pos]; +} + + +/* set the parser state and return the previous one */ +inline static enum parser_state set_state( struct parser *parser, enum parser_state state ) +{ + enum parser_state ret = parser->state; + parser->state = state; + return ret; +} + + +/* check if the pointer points to an end of file */ +inline static int is_eof( struct parser *parser, const WCHAR *ptr ) +{ + return (ptr >= parser->end || *ptr == CONTROL_Z); +} + + +/* check if the pointer points to an end of line */ +inline static int is_eol( struct parser *parser, const WCHAR *ptr ) +{ + return (ptr >= parser->end || *ptr == CONTROL_Z || *ptr == '\n'); +} + + +/* push data from current token start up to pos into the current token */ +static int push_token( struct parser *parser, const WCHAR *pos ) +{ + int len = pos - parser->start; + const WCHAR *src = parser->start; + WCHAR *dst = parser->token + parser->token_len; + + if (len > MAX_FIELD_LEN - parser->token_len) len = MAX_FIELD_LEN - parser->token_len; + + parser->token_len += len; + for ( ; len > 0; len--, dst++, src++) *dst = *src ? *src : ' '; + *dst = 0; + parser->start = pos; + return 0; +} + + +/* add a section with the current token as name */ +static int add_section_from_token( struct parser *parser ) +{ + int section_index; + + if (parser->token_len > MAX_SECTION_NAME_LEN) + { + parser->error = ERROR_SECTION_NAME_TOO_LONG; + return -1; + } + if ((section_index = find_section( parser->file, parser->token )) == -1) + { + /* need to create a new one */ + const WCHAR *name = push_string( parser->file, parser->token ); + if ((section_index = add_section( parser->file, name )) == -1) + { + parser->error = ERROR_NOT_ENOUGH_MEMORY; + return -1; + } + } + parser->token_len = 0; + parser->cur_section = section_index; + return section_index; +} + + +/* add a field containing the current token to the current line */ +static struct field *add_field_from_token( struct parser *parser, int is_key ) +{ + struct field *field; + WCHAR *text; + + if (!parser->line) /* need to start a new line */ + { + if (parser->cur_section == -1) /* got a line before the first section */ + { + parser->error = ERROR_WRONG_INF_STYLE; + return NULL; + } + if (!(parser->line = add_line( parser->file, parser->cur_section ))) goto error; + } + else assert(!is_key); + + text = push_string( parser->file, parser->token ); + if ((field = add_field( parser->file, text ))) + { + if (!is_key) parser->line->nb_fields++; + else + { + /* replace first field by key field */ + parser->line->key_field = parser->line->first_field; + parser->line->first_field++; + } + parser->token_len = 0; + return field; + } + error: + parser->error = ERROR_NOT_ENOUGH_MEMORY; + return NULL; +} + + +/* close the current line and prepare for parsing a new one */ +static void close_current_line( struct parser *parser ) +{ + struct line *cur_line = parser->line; + + if (cur_line) + { + /* if line has a single field and no key, the field is the key too */ + if (cur_line->nb_fields == 1 && cur_line->key_field == -1) + cur_line->key_field = cur_line->first_field; + } + parser->line = NULL; +} + + +/* handler for parser LINE_START state */ +static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eof( parser, p ); p++) + { + switch(*p) + { + case '\n': + parser->line_pos++; + close_current_line( parser ); + break; + case ';': + push_state( parser, LINE_START ); + set_state( parser, COMMENT ); + return p + 1; + case '[': + parser->start = p + 1; + set_state( parser, SECTION_NAME ); + return p + 1; + default: + if (!isspaceW(*p)) + { + parser->start = p; + set_state( parser, KEY_NAME ); + return p; + } + break; + } + } + close_current_line( parser ); + return NULL; +} + + +/* handler for parser SECTION_NAME state */ +static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == ']') + { + push_token( parser, p ); + if (add_section_from_token( parser ) == -1) return NULL; + push_state( parser, LINE_START ); + set_state( parser, COMMENT ); /* ignore everything else on the line */ + return p + 1; + } + } + parser->error = ERROR_BAD_SECTION_NAME_LINE; /* unfinished section name */ + return NULL; +} + + +/* handler for parser KEY_NAME state */ +static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p, *token_end = parser->start; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == ',') break; + switch(*p) + { + + case '=': + push_token( parser, token_end ); + if (!add_field_from_token( parser, 1 )) return NULL; + parser->start = p + 1; + push_state( parser, VALUE_NAME ); + set_state( parser, LEADING_SPACES ); + return p + 1; + case ';': + push_token( parser, token_end ); + if (!add_field_from_token( parser, 0 )) return NULL; + push_state( parser, LINE_START ); + set_state( parser, COMMENT ); + return p + 1; + case '"': + push_token( parser, token_end ); + parser->start = p + 1; + push_state( parser, KEY_NAME ); + set_state( parser, QUOTES ); + return p + 1; + case '\\': + push_token( parser, token_end ); + parser->start = p; + push_state( parser, KEY_NAME ); + set_state( parser, EOL_BACKSLASH ); + return p; + default: + if (!isspaceW(*p)) token_end = p + 1; + else + { + push_token( parser, p ); + push_state( parser, KEY_NAME ); + set_state( parser, TRAILING_SPACES ); + return p; + } + break; + } + } + push_token( parser, token_end ); + set_state( parser, VALUE_NAME ); + return p; +} + + +/* handler for parser VALUE_NAME state */ +static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p, *token_end = parser->start; + + for (p = pos; !is_eol( parser, p ); p++) + { + switch(*p) + { + case ';': + push_token( parser, token_end ); + if (!add_field_from_token( parser, 0 )) return NULL; + push_state( parser, LINE_START ); + set_state( parser, COMMENT ); + return p + 1; + case ',': + push_token( parser, token_end ); + if (!add_field_from_token( parser, 0 )) return NULL; + parser->start = p + 1; + push_state( parser, VALUE_NAME ); + set_state( parser, LEADING_SPACES ); + return p + 1; + case '"': + push_token( parser, token_end ); + parser->start = p + 1; + push_state( parser, VALUE_NAME ); + set_state( parser, QUOTES ); + return p + 1; + case '\\': + push_token( parser, token_end ); + parser->start = p; + push_state( parser, VALUE_NAME ); + set_state( parser, EOL_BACKSLASH ); + return p; + default: + if (!isspaceW(*p)) token_end = p + 1; + else + { + push_token( parser, p ); + push_state( parser, VALUE_NAME ); + set_state( parser, TRAILING_SPACES ); + return p; + } + break; + } + } + push_token( parser, token_end ); + if (!add_field_from_token( parser, 0 )) return NULL; + set_state( parser, LINE_START ); + return p; +} + + +/* handler for parser EOL_BACKSLASH state */ +static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eof( parser, p ); p++) + { + switch(*p) + { + case '\n': + parser->line_pos++; + parser->start = p + 1; + set_state( parser, LEADING_SPACES ); + return p + 1; + case '\\': + continue; + case ';': + push_state( parser, EOL_BACKSLASH ); + set_state( parser, COMMENT ); + return p + 1; + default: + if (isspaceW(*p)) continue; + push_token( parser, p ); + pop_state( parser ); + return p; + } + } + parser->start = p; + pop_state( parser ); + return p; +} + + +/* handler for parser QUOTES state */ +static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p, *token_end = parser->start; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == '"') + { + if (p+1 < parser->end && p[1] == '"') /* double quotes */ + { + push_token( parser, p + 1 ); + parser->start = token_end = p + 2; + p++; + } + else /* end of quotes */ + { + push_token( parser, p ); + parser->start = p + 1; + pop_state( parser ); + return p + 1; + } + } + } + push_token( parser, p ); + pop_state( parser ); + return p; +} + + +/* handler for parser LEADING_SPACES state */ +static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == '\\') + { + parser->start = p; + set_state( parser, EOL_BACKSLASH ); + return p; + } + if (!isspaceW(*p)) break; + } + parser->start = p; + pop_state( parser ); + return p; +} + + +/* handler for parser TRAILING_SPACES state */ +static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == '\\') + { + set_state( parser, EOL_BACKSLASH ); + return p; + } + if (!isspaceW(*p)) break; + } + pop_state( parser ); + return p; +} + + +/* handler for parser COMMENT state */ +static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p = pos; + + while (!is_eol( parser, p )) p++; + pop_state( parser ); + return p; +} + + +/* parse a complete buffer */ +static DWORD parse_buffer( struct inf_file *file, const WCHAR *buffer, const WCHAR *end, + UINT *error_line ) +{ + static const WCHAR Strings[] = {'S','t','r','i','n','g','s',0}; + + struct parser parser; + const WCHAR *pos = buffer; + + parser.start = buffer; + parser.end = end; + parser.file = file; + parser.line = NULL; + parser.state = LINE_START; + parser.stack_pos = 0; + parser.cur_section = -1; + parser.line_pos = 1; + parser.error = 0; + parser.token_len = 0; + + /* parser main loop */ + while (pos) pos = (parser_funcs[parser.state])( &parser, pos ); + + /* trim excess buffer space */ + if (file->alloc_sections > file->nb_sections) + { + file->sections = HeapReAlloc( GetProcessHeap(), 0, file->sections, + file->nb_sections * sizeof(file->sections[0]) ); + file->alloc_sections = file->nb_sections; + } + if (file->alloc_fields > file->nb_fields) + { + file->fields = HeapReAlloc( GetProcessHeap(), 0, file->fields, + file->nb_fields * sizeof(file->fields[0]) ); + file->alloc_fields = file->nb_fields; + } + file->strings = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, file->strings, + (file->string_pos - file->strings) * sizeof(WCHAR) ); + + if (parser.error) + { + if (error_line) *error_line = parser.line_pos; + return parser.error; + } + + /* find the [strings] section */ + file->strings_section = find_section( file, Strings ); + return 0; +} + + +/* append a child INF file to its parent list, in a thread-safe manner */ +static void append_inf_file( struct inf_file *parent, struct inf_file *child ) +{ + struct inf_file **ppnext = &parent->next; + child->next = NULL; + + for (;;) + { + struct inf_file *next = InterlockedCompareExchangePointer( (void **)ppnext, child, NULL ); + if (!next) return; + ppnext = &next->next; + } +} + + +/*********************************************************************** + * parse_file + * + * parse an INF file. + */ +static struct inf_file *parse_file( HANDLE handle, const WCHAR *class, UINT *error_line ) +{ + void *buffer; + DWORD err = 0; + struct inf_file *file; + + DWORD size = GetFileSize( handle, NULL ); + HANDLE mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, size, NULL ); + if (!mapping) return NULL; + buffer = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, size ); + NtClose( mapping ); + if (!buffer) return NULL; + + if (class) FIXME( "class %s not supported yet\n", debugstr_w(class) ); + + if (!(file = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*file) ))) + { + err = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + /* we won't need more strings space than the size of the file, + * so we can preallocate it here + */ + if (!(file->strings = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) + { + err = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + file->string_pos = file->strings; + file->strings_section = -1; + + if (!RtlIsTextUnicode( buffer, size, NULL )) + { + WCHAR *new_buff = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ); + if (new_buff) + { + DWORD len = MultiByteToWideChar( CP_ACP, 0, buffer, size, new_buff, + size * sizeof(WCHAR) ); + err = parse_buffer( file, new_buff, new_buff + len, error_line ); + HeapFree( GetProcessHeap(), 0, new_buff ); + } + } + else err = parse_buffer( file, buffer, (WCHAR *)((char *)buffer + size), error_line ); + + if (!err) /* now check signature */ + { + int version_index = find_section( file, Version ); + if (version_index != -1) + { + struct line *line = find_line( file, version_index, Signature ); + if (line && line->nb_fields > 0) + { + struct field *field = file->fields + line->first_field; + if (!strcmpiW( field->text, Chicago )) goto done; + if (!strcmpiW( field->text, WindowsNT )) goto done; + if (!strcmpiW( field->text, Windows95 )) goto done; + } + } + err = ERROR_WRONG_INF_STYLE; + } + + done: + UnmapViewOfFile( buffer ); + if (err) + { + HeapFree( GetProcessHeap(), 0, file ); + SetLastError( err ); + file = NULL; + } + return file; +} + + +/*********************************************************************** + * PARSER_get_src_root + * + * Retrieve the source directory of an inf file. + */ +const WCHAR *PARSER_get_src_root( HINF hinf ) +{ + struct inf_file *file = hinf; + return file->src_root; +} + + +/*********************************************************************** + * PARSER_get_dest_dir + * + * retrieve a destination dir of the form "dirid,relative_path" in the given entry. + * returned buffer must be freed by caller. + */ +WCHAR *PARSER_get_dest_dir( INFCONTEXT *context ) +{ + const WCHAR *dir; + WCHAR *ptr, *ret; + INT dirid; + DWORD len1, len2; + + if (!SetupGetIntField( context, 1, &dirid )) return NULL; + if (!(dir = DIRID_get_string( context->Inf, dirid ))) return NULL; + len1 = strlenW(dir) + 1; + if (!SetupGetStringFieldW( context, 2, NULL, 0, &len2 )) len2 = 0; + if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len1+len2) * sizeof(WCHAR) ))) return NULL; + strcpyW( ret, dir ); + ptr = ret + strlenW(ret); + if (len2 && ptr > ret && ptr[-1] != '\\') *ptr++ = '\\'; + if (!SetupGetStringFieldW( context, 2, ptr, len2, NULL )) *ptr = 0; + return ret; +} + + +/*********************************************************************** + * SetupOpenInfFileA (SETUPAPI.@) + */ +HINF WINAPI SetupOpenInfFileA( PCSTR name, PCSTR class, DWORD style, UINT *error ) +{ + UNICODE_STRING nameW, classW; + HINF ret = (HINF)INVALID_HANDLE_VALUE; + + classW.Buffer = NULL; + if (class && !RtlCreateUnicodeStringFromAsciiz( &classW, class )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return ret; + } + if (RtlCreateUnicodeStringFromAsciiz( &nameW, name )) + { + ret = SetupOpenInfFileW( nameW.Buffer, classW.Buffer, style, error ); + RtlFreeUnicodeString( &nameW ); + } + RtlFreeUnicodeString( &classW ); + return ret; +} + + +/*********************************************************************** + * SetupOpenInfFileW (SETUPAPI.@) + */ +HINF WINAPI SetupOpenInfFileW( PCWSTR name, PCWSTR class, DWORD style, UINT *error ) +{ + struct inf_file *file = NULL; + HANDLE handle; + WCHAR *path, *p; + UINT len; + + if (strchrW( name, '\\' ) || strchrW( name, '/' )) + { + if (!(len = GetFullPathNameW( name, 0, NULL, NULL ))) return (HINF)INVALID_HANDLE_VALUE; + if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return (HINF)INVALID_HANDLE_VALUE; + } + GetFullPathNameW( name, len, path, NULL ); + handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + } + else /* try Windows directory */ + { + static const WCHAR Inf[] = {'\\','i','n','f','\\',0}; + static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0}; + + len = GetWindowsDirectoryW( NULL, 0 ) + strlenW(name) + 12; + if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return (HINF)INVALID_HANDLE_VALUE; + } + GetWindowsDirectoryW( path, len ); + p = path + strlenW(path); + strcpyW( p, Inf ); + strcatW( p, name ); + handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + if (handle == INVALID_HANDLE_VALUE) + { + strcpyW( p, System32 ); + strcatW( p, name ); + handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + } + } + + if (handle != INVALID_HANDLE_VALUE) + { + file = parse_file( handle, class, error ); + CloseHandle( handle ); + } + if (!file) + { + HeapFree( GetProcessHeap(), 0, path ); + return (HINF)INVALID_HANDLE_VALUE; + } + TRACE( "%s -> %p\n", debugstr_w(path), file ); + file->src_root = path; + if ((p = strrchrW( path, '\\' ))) p[1] = 0; /* remove file name */ + SetLastError( 0 ); + return (HINF)file; +} + + +/*********************************************************************** + * SetupOpenAppendInfFileA (SETUPAPI.@) + */ +BOOL WINAPI SetupOpenAppendInfFileA( PCSTR name, HINF parent_hinf, UINT *error ) +{ + HINF child_hinf; + + if (!name) return SetupOpenAppendInfFileW( NULL, parent_hinf, error ); + child_hinf = SetupOpenInfFileA( name, NULL, INF_STYLE_WIN4, error ); + if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE; + append_inf_file( parent_hinf, child_hinf ); + TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_a(name), child_hinf ); + return TRUE; +} + + +/*********************************************************************** + * SetupOpenAppendInfFileW (SETUPAPI.@) + */ +BOOL WINAPI SetupOpenAppendInfFileW( PCWSTR name, HINF parent_hinf, UINT *error ) +{ + HINF child_hinf; + + if (!name) + { + INFCONTEXT context; + WCHAR filename[MAX_PATH]; + int idx = 1; + + if (!SetupFindFirstLineW( parent_hinf, Version, LayoutFile, &context )) return FALSE; + while (SetupGetStringFieldW( &context, idx++, filename, + sizeof(filename)/sizeof(WCHAR), NULL )) + { + child_hinf = SetupOpenInfFileW( filename, NULL, INF_STYLE_WIN4, error ); + if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE; + append_inf_file( parent_hinf, child_hinf ); + TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(filename), child_hinf ); + } + return TRUE; + } + child_hinf = SetupOpenInfFileW( name, NULL, INF_STYLE_WIN4, error ); + if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE; + append_inf_file( parent_hinf, child_hinf ); + TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(name), child_hinf ); + return TRUE; +} + + +/*********************************************************************** + * SetupOpenMasterInf (SETUPAPI.@) + */ +HINF WINAPI SetupOpenMasterInf( VOID ) +{ + static const WCHAR Layout[] = {'\\','i','n','f','\\', 'l', 'a', 'y', 'o', 'u', 't', '.', 'i', 'n', 'f', 0}; + WCHAR Buffer[MAX_PATH]; + + GetWindowsDirectoryW( Buffer, MAX_PATH ); + strcatW( Buffer, Layout ); + return SetupOpenInfFileW( Buffer, NULL, INF_STYLE_WIN4, NULL); +} + + + +/*********************************************************************** + * SetupCloseInfFile (SETUPAPI.@) + */ +void WINAPI SetupCloseInfFile( HINF hinf ) +{ + struct inf_file *file = hinf; + unsigned int i; + + for (i = 0; i < file->nb_sections; i++) HeapFree( GetProcessHeap(), 0, file->sections[i] ); + HeapFree( GetProcessHeap(), 0, file->src_root ); + HeapFree( GetProcessHeap(), 0, file->sections ); + HeapFree( GetProcessHeap(), 0, file->fields ); + HeapFree( GetProcessHeap(), 0, file->strings ); + HeapFree( GetProcessHeap(), 0, file ); +} + + +/*********************************************************************** + * SetupGetLineCountA (SETUPAPI.@) + */ +LONG WINAPI SetupGetLineCountA( HINF hinf, PCSTR name ) +{ + UNICODE_STRING sectionW; + LONG ret = -1; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, name )) + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + else + { + ret = SetupGetLineCountW( hinf, sectionW.Buffer ); + RtlFreeUnicodeString( §ionW ); + } + return ret; +} + + +/*********************************************************************** + * SetupGetLineCountW (SETUPAPI.@) + */ +LONG WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section ) +{ + struct inf_file *file = hinf; + int section_index; + LONG ret = -1; + + for (file = hinf; file; file = file->next) + { + if ((section_index = find_section( file, section )) == -1) continue; + if (ret == -1) ret = 0; + ret += file->sections[section_index]->nb_lines; + } + TRACE( "(%p,%s) returning %ld\n", hinf, debugstr_w(section), ret ); + SetLastError( (ret == -1) ? ERROR_SECTION_NOT_FOUND : 0 ); + return ret; +} + + +/*********************************************************************** + * SetupGetLineByIndexA (SETUPAPI.@) + */ +BOOL WINAPI SetupGetLineByIndexA( HINF hinf, PCSTR section, DWORD index, INFCONTEXT *context ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + else + { + ret = SetupGetLineByIndexW( hinf, sectionW.Buffer, index, context ); + RtlFreeUnicodeString( §ionW ); + } + return ret; +} + + +/*********************************************************************** + * SetupGetLineByIndexW (SETUPAPI.@) + */ +BOOL WINAPI SetupGetLineByIndexW( HINF hinf, PCWSTR section, DWORD index, INFCONTEXT *context ) +{ + struct inf_file *file = hinf; + int section_index; + + SetLastError( ERROR_SECTION_NOT_FOUND ); + for (file = hinf; file; file = file->next) + { + if ((section_index = find_section( file, section )) == -1) continue; + SetLastError( ERROR_LINE_NOT_FOUND ); + if (index < file->sections[section_index]->nb_lines) + { + context->Inf = hinf; + context->CurrentInf = file; + context->Section = section_index; + context->Line = index; + SetLastError( 0 ); + TRACE( "(%p,%s): returning %d/%ld\n", + hinf, debugstr_w(section), section_index, index ); + return TRUE; + } + index -= file->sections[section_index]->nb_lines; + } + TRACE( "(%p,%s) not found\n", hinf, debugstr_w(section) ); + return FALSE; +} + + +/*********************************************************************** + * SetupFindFirstLineA (SETUPAPI.@) + */ +BOOL WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context ) +{ + UNICODE_STRING sectionW, keyW; + BOOL ret = FALSE; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + if (!key) ret = SetupFindFirstLineW( hinf, sectionW.Buffer, NULL, context ); + else + { + if (RtlCreateUnicodeStringFromAsciiz( &keyW, key )) + { + ret = SetupFindFirstLineW( hinf, sectionW.Buffer, keyW.Buffer, context ); + RtlFreeUnicodeString( &keyW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + } + RtlFreeUnicodeString( §ionW ); + return ret; +} + + +/*********************************************************************** + * SetupFindFirstLineW (SETUPAPI.@) + */ +BOOL WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context ) +{ + struct inf_file *file; + int section_index; + + SetLastError( ERROR_SECTION_NOT_FOUND ); + for (file = hinf; file; file = file->next) + { + if ((section_index = find_section( file, section )) == -1) continue; + if (key) + { + INFCONTEXT ctx; + ctx.Inf = hinf; + ctx.CurrentInf = file; + ctx.Section = section_index; + ctx.Line = -1; + return SetupFindNextMatchLineW( &ctx, key, context ); + } + SetLastError( ERROR_LINE_NOT_FOUND ); /* found at least one section */ + if (file->sections[section_index]->nb_lines) + { + context->Inf = hinf; + context->CurrentInf = file; + context->Section = section_index; + context->Line = 0; + SetLastError( 0 ); + TRACE( "(%p,%s,%s): returning %d/0\n", + hinf, debugstr_w(section), debugstr_w(key), section_index ); + return TRUE; + } + } + TRACE( "(%p,%s,%s): not found\n", hinf, debugstr_w(section), debugstr_w(key) ); + return FALSE; +} + + +/*********************************************************************** + * SetupFindNextLine (SETUPAPI.@) + */ +BOOL WINAPI SetupFindNextLine( PINFCONTEXT context_in, PINFCONTEXT context_out ) +{ + struct inf_file *file = context_in->CurrentInf; + struct section *section; + + if (context_in->Section >= file->nb_sections) goto error; + + section = file->sections[context_in->Section]; + if (context_in->Line+1 < section->nb_lines) + { + if (context_out != context_in) *context_out = *context_in; + context_out->Line++; + SetLastError( 0 ); + return TRUE; + } + + /* now search the appended files */ + + for (file = file->next; file; file = file->next) + { + int section_index = find_section( file, section->name ); + if (section_index == -1) continue; + if (file->sections[section_index]->nb_lines) + { + context_out->Inf = context_in->Inf; + context_out->CurrentInf = file; + context_out->Section = section_index; + context_out->Line = 0; + SetLastError( 0 ); + return TRUE; + } + } + error: + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; +} + + +/*********************************************************************** + * SetupFindNextMatchLineA (SETUPAPI.@) + */ +BOOL WINAPI SetupFindNextMatchLineA( PINFCONTEXT context_in, PCSTR key, + PINFCONTEXT context_out ) +{ + UNICODE_STRING keyW; + BOOL ret = FALSE; + + if (!key) return SetupFindNextLine( context_in, context_out ); + + if (!RtlCreateUnicodeStringFromAsciiz( &keyW, key )) + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + else + { + ret = SetupFindNextMatchLineW( context_in, keyW.Buffer, context_out ); + RtlFreeUnicodeString( &keyW ); + } + return ret; +} + + +/*********************************************************************** + * SetupFindNextMatchLineW (SETUPAPI.@) + */ +BOOL WINAPI SetupFindNextMatchLineW( PINFCONTEXT context_in, PCWSTR key, + PINFCONTEXT context_out ) +{ + struct inf_file *file = context_in->CurrentInf; + struct section *section; + struct line *line; + unsigned int i; + + if (!key) return SetupFindNextLine( context_in, context_out ); + + if (context_in->Section >= file->nb_sections) goto error; + + section = file->sections[context_in->Section]; + + for (i = context_in->Line+1, line = §ion->lines[i]; i < section->nb_lines; i++, line++) + { + if (line->key_field == -1) continue; + if (!strcmpiW( key, file->fields[line->key_field].text )) + { + if (context_out != context_in) *context_out = *context_in; + context_out->Line = i; + SetLastError( 0 ); + TRACE( "(%p,%s,%s): returning %d\n", + file, debugstr_w(section->name), debugstr_w(key), i ); + return TRUE; + } + } + + /* now search the appended files */ + + for (file = file->next; file; file = file->next) + { + int section_index = find_section( file, section->name ); + if (section_index == -1) continue; + section = file->sections[section_index]; + for (i = 0, line = section->lines; i < section->nb_lines; i++, line++) + { + if (line->key_field == -1) continue; + if (!strcmpiW( key, file->fields[line->key_field].text )) + { + context_out->Inf = context_in->Inf; + context_out->CurrentInf = file; + context_out->Section = section_index; + context_out->Line = i; + SetLastError( 0 ); + TRACE( "(%p,%s,%s): returning %d/%d\n", + file, debugstr_w(section->name), debugstr_w(key), section_index, i ); + return TRUE; + } + } + } + TRACE( "(%p,%s,%s): not found\n", + context_in->CurrentInf, debugstr_w(section->name), debugstr_w(key) ); + error: + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; +} + + +/*********************************************************************** + * SetupGetLineTextW (SETUPAPI.@) + */ +BOOL WINAPI SetupGetLineTextW( PINFCONTEXT context, HINF hinf, PCWSTR section_name, + PCWSTR key_name, PWSTR buffer, DWORD size, PDWORD required ) +{ + struct inf_file *file; + struct line *line; + struct field *field; + int i; + DWORD total = 0; + + if (!context) + { + INFCONTEXT new_context; + if (!SetupFindFirstLineW( hinf, section_name, key_name, &new_context )) return FALSE; + file = new_context.CurrentInf; + line = get_line( file, new_context.Section, new_context.Line ); + } + else + { + file = context->CurrentInf; + if (!(line = get_line( file, context->Section, context->Line ))) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + } + + for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) + total += PARSER_string_substW( file, field->text, NULL, 0 ) + 1; + + if (required) *required = total; + if (buffer) + { + if (total > size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) + { + unsigned int len = PARSER_string_substW( file, field->text, buffer, size ); + if (i+1 < line->nb_fields) buffer[len] = ','; + buffer += len + 1; + } + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetLineTextA (SETUPAPI.@) + */ +BOOL WINAPI SetupGetLineTextA( PINFCONTEXT context, HINF hinf, PCSTR section_name, + PCSTR key_name, PSTR buffer, DWORD size, PDWORD required ) +{ + struct inf_file *file; + struct line *line; + struct field *field; + int i; + DWORD total = 0; + + if (!context) + { + INFCONTEXT new_context; + if (!SetupFindFirstLineA( hinf, section_name, key_name, &new_context )) return FALSE; + file = new_context.CurrentInf; + line = get_line( file, new_context.Section, new_context.Line ); + } + else + { + file = context->CurrentInf; + if (!(line = get_line( file, context->Section, context->Line ))) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + } + + for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) + total += PARSER_string_substA( file, field->text, NULL, 0 ) + 1; + + if (required) *required = total; + if (buffer) + { + if (total > size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) + { + unsigned int len = PARSER_string_substA( file, field->text, buffer, size ); + if (i+1 < line->nb_fields) buffer[len] = ','; + buffer += len + 1; + } + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetFieldCount (SETUPAPI.@) + */ +DWORD WINAPI SetupGetFieldCount( PINFCONTEXT context ) +{ + struct inf_file *file = context->CurrentInf; + struct line *line = get_line( file, context->Section, context->Line ); + + if (!line) return 0; + return line->nb_fields; +} + + +/*********************************************************************** + * SetupGetStringFieldA (SETUPAPI.@) + */ +BOOL WINAPI SetupGetStringFieldA( PINFCONTEXT context, DWORD index, PSTR buffer, + DWORD size, PDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct field *field = get_field( file, context->Section, context->Line, index ); + unsigned int len; + + SetLastError(0); + if (!field) return FALSE; + len = PARSER_string_substA( file, field->text, NULL, 0 ); + if (required) *required = len + 1; + if (buffer) + { + if (size <= len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + PARSER_string_substA( file, field->text, buffer, size ); + + TRACE( "context %p/%p/%d/%d index %ld returning %s\n", + context->Inf, context->CurrentInf, context->Section, context->Line, + index, debugstr_a(buffer) ); + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetStringFieldW (SETUPAPI.@) + */ +BOOL WINAPI SetupGetStringFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer, + DWORD size, PDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct field *field = get_field( file, context->Section, context->Line, index ); + unsigned int len; + + SetLastError(0); + if (!field) return FALSE; + len = PARSER_string_substW( file, field->text, NULL, 0 ); + if (required) *required = len + 1; + if (buffer) + { + if (size <= len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + PARSER_string_substW( file, field->text, buffer, size ); + + TRACE( "context %p/%p/%d/%d index %ld returning %s\n", + context->Inf, context->CurrentInf, context->Section, context->Line, + index, debugstr_w(buffer) ); + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetIntField (SETUPAPI.@) + */ +BOOL WINAPI SetupGetIntField( PINFCONTEXT context, DWORD index, PINT result ) +{ + char localbuff[20]; + char *end, *buffer = localbuff; + DWORD required; + INT res; + BOOL ret = FALSE; + + if (!SetupGetStringFieldA( context, index, localbuff, sizeof(localbuff), &required )) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE; + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required ))) return FALSE; + if (!SetupGetStringFieldA( context, index, buffer, required, NULL )) goto done; + } + res = strtol( buffer, &end, 0 ); + if (end != buffer && !*end) + { + *result = res; + ret = TRUE; + } + else SetLastError( ERROR_INVALID_DATA ); + + done: + if (buffer != localbuff) HeapFree( GetProcessHeap(), 0, buffer ); + return ret; +} + + +/*********************************************************************** + * SetupGetBinaryField (SETUPAPI.@) + */ +BOOL WINAPI SetupGetBinaryField( PINFCONTEXT context, DWORD index, BYTE *buffer, + DWORD size, LPDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct line *line = get_line( file, context->Section, context->Line ); + struct field *field; + int i; + + if (!line) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + if (!index || index >= line->nb_fields) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + index--; /* fields start at 0 */ + if (required) *required = line->nb_fields - index; + if (!buffer) return TRUE; + if (size < line->nb_fields - index) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + const WCHAR *p; + DWORD value = 0; + for (p = field->text; *p && isxdigitW(*p); p++) + { + if ((value <<= 4) > 255) + { + SetLastError( ERROR_INVALID_DATA ); + return FALSE; + } + if (*p <= '9') value |= (*p - '0'); + else value |= (tolowerW(*p) - 'a' + 10); + } + buffer[i - index] = value; + } + if (TRACE_ON(setupapi)) + { + TRACE( "%p/%p/%d/%d index %ld returning", + context->Inf, context->CurrentInf, context->Section, context->Line, index ); + for (i = index; i < line->nb_fields; i++) TRACE( " %02x", buffer[i - index] ); + TRACE( "\n" ); + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetMultiSzFieldA (SETUPAPI.@) + */ +BOOL WINAPI SetupGetMultiSzFieldA( PINFCONTEXT context, DWORD index, PSTR buffer, + DWORD size, LPDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct line *line = get_line( file, context->Section, context->Line ); + struct field *field; + unsigned int len; + int i; + DWORD total = 1; + + if (!line) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + if (!index || index >= line->nb_fields) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + index--; /* fields start at 0 */ + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + if (!(len = PARSER_string_substA( file, field->text, NULL, 0 ))) break; + total += len + 1; + } + + if (required) *required = total; + if (!buffer) return TRUE; + if (total > size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + if (!(len = PARSER_string_substA( file, field->text, buffer, size ))) break; + buffer += len + 1; + } + *buffer = 0; /* add final null */ + return TRUE; +} + + +/*********************************************************************** + * SetupGetMultiSzFieldW (SETUPAPI.@) + */ +BOOL WINAPI SetupGetMultiSzFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer, + DWORD size, LPDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct line *line = get_line( file, context->Section, context->Line ); + struct field *field; + unsigned int len; + int i; + DWORD total = 1; + + if (!line) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + if (!index || index >= line->nb_fields) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + index--; /* fields start at 0 */ + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + if (!(len = PARSER_string_substW( file, field->text, NULL, 0 ))) break; + total += len + 1; + } + + if (required) *required = total; + if (!buffer) return TRUE; + if (total > size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + if (!(len = PARSER_string_substW( file, field->text, buffer, size ))) break; + buffer += len + 1; + } + *buffer = 0; /* add final null */ + return TRUE; +} diff --git a/reactos/lib/setupapi/queue.c b/reactos/lib/setupapi/queue.c index 87693f01581..66da7b7e688 100644 --- a/reactos/lib/setupapi/queue.c +++ b/reactos/lib/setupapi/queue.c @@ -1,1426 +1,1426 @@ -/* - * Setupapi file queue routines - * - * Copyright 2002 Alexandre Julliard for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "winternl.h" -#include "winerror.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "setupapi.h" -#include "wine/unicode.h" -#include "setupapi_private.h" -#include "winver.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -/* context structure for the default queue callback */ -struct default_callback_context -{ - HWND owner; - HWND progress; - UINT message; -}; - -struct file_op -{ - struct file_op *next; - UINT style; - WCHAR *src_root; - WCHAR *src_path; - WCHAR *src_file; - WCHAR *src_descr; - WCHAR *src_tag; - WCHAR *dst_path; - WCHAR *dst_file; -}; - -struct file_op_queue -{ - struct file_op *head; - struct file_op *tail; - unsigned int count; -}; - -struct file_queue -{ - struct file_op_queue copy_queue; - struct file_op_queue delete_queue; - struct file_op_queue rename_queue; - DWORD flags; -}; - - -inline static WCHAR *strdupW( const WCHAR *str ) -{ - WCHAR *ret = NULL; - if (str) - { - int len = (strlenW(str) + 1) * sizeof(WCHAR); - if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( ret, str, len ); - } - return ret; -} - - -inline static WCHAR *strdupAtoW( const char *str ) -{ - WCHAR *ret = NULL; - if (str) - { - DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); - if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) - MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); - } - return ret; -} - -inline static char *strdupWtoA( const WCHAR *str ) -{ - char *ret = NULL; - if (str) - { - DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL ); - if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) - WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL ); - } - return ret; -} - -/* append a file operation to a queue */ -inline static void queue_file_op( struct file_op_queue *queue, struct file_op *op ) -{ - op->next = NULL; - if (queue->tail) queue->tail->next = op; - else queue->head = op; - queue->tail = op; - queue->count++; -} - -/* free all the file operations on a given queue */ -static void free_file_op_queue( struct file_op_queue *queue ) -{ - struct file_op *t, *op = queue->head; - - while( op ) - { - HeapFree( GetProcessHeap(), 0, op->src_root ); - HeapFree( GetProcessHeap(), 0, op->src_path ); - HeapFree( GetProcessHeap(), 0, op->src_file ); - HeapFree( GetProcessHeap(), 0, op->src_descr ); - HeapFree( GetProcessHeap(), 0, op->src_tag ); - HeapFree( GetProcessHeap(), 0, op->dst_path ); - if (op->dst_file != op->src_file) HeapFree( GetProcessHeap(), 0, op->dst_file ); - t = op; - op = op->next; - HeapFree( GetProcessHeap(), 0, t ); - } -} - -/* concat 3 strings to make a path, handling separators correctly */ -static void concat_W( WCHAR *buffer, const WCHAR *src1, const WCHAR *src2, const WCHAR *src3 ) -{ - *buffer = 0; - if (src1 && *src1) - { - strcpyW( buffer, src1 ); - buffer += strlenW(buffer ); - if (buffer[-1] != '\\') *buffer++ = '\\'; - if (src2) while (*src2 == '\\') src2++; - } - - if (src2) - { - strcpyW( buffer, src2 ); - buffer += strlenW(buffer ); - if (buffer[-1] != '\\') *buffer++ = '\\'; - if (src3) while (*src3 == '\\') src3++; - } - if (src3) - { - strcpyW( buffer, src3 ); - buffer += strlenW(buffer ); - } -} - - -/*********************************************************************** - * build_filepathsW - * - * Build a FILEPATHS_W structure for a given file operation. - */ -static BOOL build_filepathsW( const struct file_op *op, FILEPATHS_W *paths ) -{ - unsigned int src_len = 1, dst_len = 1; - WCHAR *source = (PWSTR)paths->Source, *target = (PWSTR)paths->Target; - - if (op->src_root) src_len += strlenW(op->src_root) + 1; - if (op->src_path) src_len += strlenW(op->src_path) + 1; - if (op->src_file) src_len += strlenW(op->src_file) + 1; - if (op->dst_path) dst_len += strlenW(op->dst_path) + 1; - if (op->dst_file) dst_len += strlenW(op->dst_file) + 1; - src_len *= sizeof(WCHAR); - dst_len *= sizeof(WCHAR); - - if (!source || HeapSize( GetProcessHeap(), 0, source ) < src_len ) - { - HeapFree( GetProcessHeap(), 0, source ); - paths->Source = source = HeapAlloc( GetProcessHeap(), 0, src_len ); - } - if (!target || HeapSize( GetProcessHeap(), 0, target ) < dst_len ) - { - HeapFree( GetProcessHeap(), 0, target ); - paths->Target = target = HeapAlloc( GetProcessHeap(), 0, dst_len ); - } - if (!source || !target) return FALSE; - concat_W( source, op->src_root, op->src_path, op->src_file ); - concat_W( target, NULL, op->dst_path, op->dst_file ); - paths->Win32Error = 0; - paths->Flags = 0; - return TRUE; -} - - -/*********************************************************************** - * QUEUE_callback_WtoA - * - * Map a file callback parameters from W to A and call the A callback. - */ -UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification, - UINT_PTR param1, UINT_PTR param2 ) -{ - struct callback_WtoA_context *callback_ctx = context; - char buffer[MAX_PATH]; - UINT ret; - UINT_PTR old_param2 = param2; - - switch(notification) - { - case SPFILENOTIFY_COPYERROR: - param2 = (UINT_PTR)&buffer; - /* fall through */ - case SPFILENOTIFY_STARTDELETE: - case SPFILENOTIFY_ENDDELETE: - case SPFILENOTIFY_DELETEERROR: - case SPFILENOTIFY_STARTRENAME: - case SPFILENOTIFY_ENDRENAME: - case SPFILENOTIFY_RENAMEERROR: - case SPFILENOTIFY_STARTCOPY: - case SPFILENOTIFY_ENDCOPY: - { - FILEPATHS_W *pathsW = (FILEPATHS_W *)param1; - FILEPATHS_A pathsA; - - pathsA.Source = strdupWtoA( pathsW->Source ); - pathsA.Target = strdupWtoA( pathsW->Target ); - pathsA.Win32Error = pathsW->Win32Error; - pathsA.Flags = pathsW->Flags; - ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, - (UINT_PTR)&pathsA, param2 ); - HeapFree( GetProcessHeap(), 0, (void *)pathsA.Source ); - HeapFree( GetProcessHeap(), 0, (void *)pathsA.Target ); - } - if (notification == SPFILENOTIFY_COPYERROR) - MultiByteToWideChar( CP_ACP, 0, buffer, -1, (WCHAR *)old_param2, MAX_PATH ); - break; - - case SPFILENOTIFY_STARTREGISTRATION: - case SPFILENOTIFY_ENDREGISTRATION: - { - SP_REGISTER_CONTROL_STATUSW *statusW = (SP_REGISTER_CONTROL_STATUSW *)param1; - SP_REGISTER_CONTROL_STATUSA statusA; - - statusA.cbSize = sizeof(statusA); - statusA.FileName = strdupWtoA( statusW->FileName ); - statusA.Win32Error = statusW->Win32Error; - statusA.FailureCode = statusW->FailureCode; - ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, - (UINT_PTR)&statusA, param2 ); - HeapFree( GetProcessHeap(), 0, (LPSTR)statusA.FileName ); - } - break; - - case SPFILENOTIFY_NEEDMEDIA: - case SPFILENOTIFY_QUEUESCAN: - FIXME("mapping for %d not implemented\n",notification); - case SPFILENOTIFY_STARTQUEUE: - case SPFILENOTIFY_ENDQUEUE: - case SPFILENOTIFY_STARTSUBQUEUE: - case SPFILENOTIFY_ENDSUBQUEUE: - default: - ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, param1, param2 ); - break; - } - return ret; -} - - -/*********************************************************************** - * get_src_file_info - * - * Retrieve the source file information for a given file. - */ -static void get_src_file_info( HINF hinf, struct file_op *op ) -{ - static const WCHAR SourceDisksNames[] = - {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0}; - static const WCHAR SourceDisksFiles[] = - {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0}; - - INFCONTEXT file_ctx, disk_ctx; - INT id, diskid; - DWORD len, len2; - - /* find the SourceDisksFiles entry */ - if (!SetupFindFirstLineW( hinf, SourceDisksFiles, op->src_file, &file_ctx )) - { - const WCHAR *dir; - - if ((op->style & (SP_COPY_SOURCE_ABSOLUTE|SP_COPY_SOURCEPATH_ABSOLUTE))) return; - /* no specific info, use .inf file source directory */ - if (!op->src_root && (dir = DIRID_get_string( hinf, DIRID_SRCPATH ))) - op->src_root = strdupW( dir ); - return; - } - if (!SetupGetIntField( &file_ctx, 1, &diskid )) return; - - /* now find the diskid in the SourceDisksNames section */ - if (!SetupFindFirstLineW( hinf, SourceDisksNames, NULL, &disk_ctx )) return; - for (;;) - { - if (SetupGetIntField( &disk_ctx, 0, &id ) && (id == diskid)) break; - if (!SetupFindNextLine( &disk_ctx, &disk_ctx )) return; - } - - /* and fill in the missing info */ - - if (!op->src_descr) - { - if (SetupGetStringFieldW( &disk_ctx, 1, NULL, 0, &len ) && - (op->src_descr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) ))) - SetupGetStringFieldW( &disk_ctx, 1, op->src_descr, len, NULL ); - } - if (!op->src_tag) - { - if (SetupGetStringFieldW( &disk_ctx, 2, NULL, 0, &len ) && - (op->src_tag = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) ))) - SetupGetStringFieldW( &disk_ctx, 2, op->src_tag, len, NULL ); - } - if (!op->src_path && !(op->style & SP_COPY_SOURCE_ABSOLUTE)) - { - if (!(op->style & SP_COPY_SOURCEPATH_ABSOLUTE)) - { - /* retrieve relative path for this disk */ - if (!SetupGetStringFieldW( &disk_ctx, 4, NULL, 0, &len )) len = 0; - } - /* retrieve relative path for this file */ - if (!SetupGetStringFieldW( &file_ctx, 2, NULL, 0, &len2 )) len2 = 0; - - if ((len || len2) && - (op->src_path = HeapAlloc( GetProcessHeap(), 0, (len+len2)*sizeof(WCHAR) ))) - { - WCHAR *ptr = op->src_path; - if (len) - { - SetupGetStringFieldW( &disk_ctx, 4, op->src_path, len, NULL ); - ptr = op->src_path + strlenW(op->src_path); - if (len2 && ptr > op->src_path && ptr[-1] != '\\') *ptr++ = '\\'; - } - if (!SetupGetStringFieldW( &disk_ctx, 4, ptr, len2, NULL )) *ptr = 0; - } - } - if (!op->src_root) op->src_root = strdupW( PARSER_get_src_root(hinf) ); -} - - -/*********************************************************************** - * get_destination_dir - * - * Retrieve the destination dir for a given section. - */ -static WCHAR *get_destination_dir( HINF hinf, const WCHAR *section ) -{ - static const WCHAR Dest[] = {'D','e','s','t','i','n','a','t','i','o','n','D','i','r','s',0}; - static const WCHAR Def[] = {'D','e','f','a','u','l','t','D','e','s','t','D','i','r',0}; - INFCONTEXT context; - - if (!SetupFindFirstLineW( hinf, Dest, section, &context ) && - !SetupFindFirstLineW( hinf, Dest, Def, &context )) return NULL; - return PARSER_get_dest_dir( &context ); -} - - -static void (WINAPI *pExtractFiles)( LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD ); - -/*********************************************************************** - * extract_cabinet_file - * - * Extract a file from a .cab file. - */ -static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root, - const WCHAR *src, const WCHAR *dst ) -{ - static const WCHAR extW[] = {'.','c','a','b',0}; - static HMODULE advpack; - - char *cab_path, *cab_file; - int len = strlenW( cabinet ); - - /* make sure the cabinet file has a .cab extension */ - if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE; - if (!pExtractFiles) - { - if (!advpack && !(advpack = LoadLibraryA( "advpack.dll" ))) - { - ERR( "could not load advpack.dll\n" ); - return FALSE; - } - if (!(pExtractFiles = (void *)GetProcAddress( advpack, "ExtractFiles" ))) - { - ERR( "could not find ExtractFiles in advpack.dll\n" ); - return FALSE; - } - } - - if (!(cab_path = strdupWtoA( root ))) return FALSE; - len = WideCharToMultiByte( CP_ACP, 0, cabinet, -1, NULL, 0, NULL, NULL ); - if (!(cab_file = HeapAlloc( GetProcessHeap(), 0, strlen(cab_path) + len + 1 ))) - { - HeapFree( GetProcessHeap(), 0, cab_path ); - return FALSE; - } - strcpy( cab_file, cab_path ); - if (cab_file[0] && cab_file[strlen(cab_file)-1] != '\\') strcat( cab_file, "\\" ); - WideCharToMultiByte( CP_ACP, 0, cabinet, -1, cab_file + strlen(cab_file), len, NULL, NULL ); - FIXME( "awful hack: extracting cabinet %s\n", debugstr_a(cab_file) ); - pExtractFiles( cab_file, cab_path, 0, 0, 0, 0 ); - HeapFree( GetProcessHeap(), 0, cab_file ); - HeapFree( GetProcessHeap(), 0, cab_path ); - return CopyFileW( src, dst, FALSE /*FIXME*/ ); -} - - -/*********************************************************************** - * SetupOpenFileQueue (SETUPAPI.@) - */ -HSPFILEQ WINAPI SetupOpenFileQueue(void) -{ - struct file_queue *queue; - - if (!(queue = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*queue)))) - return (HSPFILEQ)INVALID_HANDLE_VALUE; - return queue; -} - - -/*********************************************************************** - * SetupCloseFileQueue (SETUPAPI.@) - */ -BOOL WINAPI SetupCloseFileQueue( HSPFILEQ handle ) -{ - struct file_queue *queue = handle; - - free_file_op_queue( &queue->copy_queue ); - free_file_op_queue( &queue->rename_queue ); - free_file_op_queue( &queue->delete_queue ); - HeapFree( GetProcessHeap(), 0, queue ); - return TRUE; -} - - -/*********************************************************************** - * SetupQueueCopyIndirectA (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueCopyIndirectA( PSP_FILE_COPY_PARAMS_A params ) -{ - struct file_queue *queue = params->QueueHandle; - struct file_op *op; - - if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; - op->style = params->CopyStyle; - op->src_root = strdupAtoW( params->SourceRootPath ); - op->src_path = strdupAtoW( params->SourcePath ); - op->src_file = strdupAtoW( params->SourceFilename ); - op->src_descr = strdupAtoW( params->SourceDescription ); - op->src_tag = strdupAtoW( params->SourceTagfile ); - op->dst_path = strdupAtoW( params->TargetDirectory ); - op->dst_file = strdupAtoW( params->TargetFilename ); - - /* some defaults */ - if (!op->src_file) op->src_file = op->dst_file; - if (params->LayoutInf) - { - get_src_file_info( params->LayoutInf, op ); - if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file ); - } - - TRACE( "root=%s path=%s file=%s -> dir=%s file=%s descr=%s tag=%s\n", - debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file), - debugstr_w(op->dst_path), debugstr_w(op->dst_file), - debugstr_w(op->src_descr), debugstr_w(op->src_tag) ); - - queue_file_op( &queue->copy_queue, op ); - return TRUE; -} - - -/*********************************************************************** - * SetupQueueCopyIndirectW (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueCopyIndirectW( PSP_FILE_COPY_PARAMS_W params ) -{ - struct file_queue *queue = params->QueueHandle; - struct file_op *op; - - if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; - op->style = params->CopyStyle; - op->src_root = strdupW( params->SourceRootPath ); - op->src_path = strdupW( params->SourcePath ); - op->src_file = strdupW( params->SourceFilename ); - op->src_descr = strdupW( params->SourceDescription ); - op->src_tag = strdupW( params->SourceTagfile ); - op->dst_path = strdupW( params->TargetDirectory ); - op->dst_file = strdupW( params->TargetFilename ); - - /* some defaults */ - if (!op->src_file) op->src_file = op->dst_file; - if (params->LayoutInf) - { - get_src_file_info( params->LayoutInf, op ); - if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file ); - } - - TRACE( "root=%s path=%s file=%s -> dir=%s file=%s descr=%s tag=%s\n", - debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file), - debugstr_w(op->dst_path), debugstr_w(op->dst_file), - debugstr_w(op->src_descr), debugstr_w(op->src_tag) ); - - queue_file_op( &queue->copy_queue, op ); - return TRUE; -} - - -/*********************************************************************** - * SetupQueueCopyA (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueCopyA( HSPFILEQ queue, PCSTR src_root, PCSTR src_path, PCSTR src_file, - PCSTR src_descr, PCSTR src_tag, PCSTR dst_dir, PCSTR dst_file, - DWORD style ) -{ - SP_FILE_COPY_PARAMS_A params; - - params.cbSize = sizeof(params); - params.QueueHandle = queue; - params.SourceRootPath = src_root; - params.SourcePath = src_path; - params.SourceFilename = src_file; - params.SourceDescription = src_descr; - params.SourceTagfile = src_tag; - params.TargetDirectory = dst_dir; - params.TargetFilename = dst_file; - params.CopyStyle = style; - params.LayoutInf = 0; - params.SecurityDescriptor = NULL; - return SetupQueueCopyIndirectA( ¶ms ); -} - - -/*********************************************************************** - * SetupQueueCopyW (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueCopyW( HSPFILEQ queue, PCWSTR src_root, PCWSTR src_path, PCWSTR src_file, - PCWSTR src_descr, PCWSTR src_tag, PCWSTR dst_dir, PCWSTR dst_file, - DWORD style ) -{ - SP_FILE_COPY_PARAMS_W params; - - params.cbSize = sizeof(params); - params.QueueHandle = queue; - params.SourceRootPath = src_root; - params.SourcePath = src_path; - params.SourceFilename = src_file; - params.SourceDescription = src_descr; - params.SourceTagfile = src_tag; - params.TargetDirectory = dst_dir; - params.TargetFilename = dst_file; - params.CopyStyle = style; - params.LayoutInf = 0; - params.SecurityDescriptor = NULL; - return SetupQueueCopyIndirectW( ¶ms ); -} - - -/*********************************************************************** - * SetupQueueDefaultCopyA (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueDefaultCopyA( HSPFILEQ queue, HINF hinf, PCSTR src_root, PCSTR src_file, - PCSTR dst_file, DWORD style ) -{ - SP_FILE_COPY_PARAMS_A params; - - params.cbSize = sizeof(params); - params.QueueHandle = queue; - params.SourceRootPath = src_root; - params.SourcePath = NULL; - params.SourceFilename = src_file; - params.SourceDescription = NULL; - params.SourceTagfile = NULL; - params.TargetDirectory = NULL; - params.TargetFilename = dst_file; - params.CopyStyle = style; - params.LayoutInf = hinf; - params.SecurityDescriptor = NULL; - return SetupQueueCopyIndirectA( ¶ms ); -} - - -/*********************************************************************** - * SetupQueueDefaultCopyW (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueDefaultCopyW( HSPFILEQ queue, HINF hinf, PCWSTR src_root, PCWSTR src_file, - PCWSTR dst_file, DWORD style ) -{ - SP_FILE_COPY_PARAMS_W params; - - params.cbSize = sizeof(params); - params.QueueHandle = queue; - params.SourceRootPath = src_root; - params.SourcePath = NULL; - params.SourceFilename = src_file; - params.SourceDescription = NULL; - params.SourceTagfile = NULL; - params.TargetDirectory = NULL; - params.TargetFilename = dst_file; - params.CopyStyle = style; - params.LayoutInf = hinf; - params.SecurityDescriptor = NULL; - return SetupQueueCopyIndirectW( ¶ms ); -} - - -/*********************************************************************** - * SetupQueueDeleteA (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueDeleteA( HSPFILEQ handle, PCSTR part1, PCSTR part2 ) -{ - struct file_queue *queue = handle; - struct file_op *op; - - if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; - op->style = 0; - op->src_root = NULL; - op->src_path = NULL; - op->src_file = NULL; - op->src_descr = NULL; - op->src_tag = NULL; - op->dst_path = strdupAtoW( part1 ); - op->dst_file = strdupAtoW( part2 ); - queue_file_op( &queue->delete_queue, op ); - return TRUE; -} - - -/*********************************************************************** - * SetupQueueDeleteW (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueDeleteW( HSPFILEQ handle, PCWSTR part1, PCWSTR part2 ) -{ - struct file_queue *queue = handle; - struct file_op *op; - - if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; - op->style = 0; - op->src_root = NULL; - op->src_path = NULL; - op->src_file = NULL; - op->src_descr = NULL; - op->src_tag = NULL; - op->dst_path = strdupW( part1 ); - op->dst_file = strdupW( part2 ); - queue_file_op( &queue->delete_queue, op ); - return TRUE; -} - - -/*********************************************************************** - * SetupQueueRenameA (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueRenameA( HSPFILEQ handle, PCSTR SourcePath, PCSTR SourceFilename, - PCSTR TargetPath, PCSTR TargetFilename ) -{ - struct file_queue *queue = handle; - struct file_op *op; - - if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; - op->style = 0; - op->src_root = NULL; - op->src_path = strdupAtoW( SourcePath ); - op->src_file = strdupAtoW( SourceFilename ); - op->src_descr = NULL; - op->src_tag = NULL; - op->dst_path = strdupAtoW( TargetPath ); - op->dst_file = strdupAtoW( TargetFilename ); - queue_file_op( &queue->rename_queue, op ); - return TRUE; -} - - -/*********************************************************************** - * SetupQueueRenameW (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueRenameW( HSPFILEQ handle, PCWSTR SourcePath, PCWSTR SourceFilename, - PCWSTR TargetPath, PCWSTR TargetFilename ) -{ - struct file_queue *queue = handle; - struct file_op *op; - - if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; - op->style = 0; - op->src_root = NULL; - op->src_path = strdupW( SourcePath ); - op->src_file = strdupW( SourceFilename ); - op->src_descr = NULL; - op->src_tag = NULL; - op->dst_path = strdupW( TargetPath ); - op->dst_file = strdupW( TargetFilename ); - queue_file_op( &queue->rename_queue, op ); - return TRUE; -} - - -/*********************************************************************** - * SetupQueueCopySectionA (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueCopySectionA( HSPFILEQ queue, PCSTR src_root, HINF hinf, HINF hlist, - PCSTR section, DWORD style ) -{ - UNICODE_STRING sectionW; - BOOL ret = FALSE; - - if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return FALSE; - } - if (!src_root) - ret = SetupQueueCopySectionW( queue, NULL, hinf, hlist, sectionW.Buffer, style ); - else - { - UNICODE_STRING srcW; - if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root )) - { - ret = SetupQueueCopySectionW( queue, srcW.Buffer, hinf, hlist, sectionW.Buffer, style ); - RtlFreeUnicodeString( &srcW ); - } - else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - } - RtlFreeUnicodeString( §ionW ); - return ret; -} - - -/*********************************************************************** - * SetupQueueCopySectionW (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueCopySectionW( HSPFILEQ queue, PCWSTR src_root, HINF hinf, HINF hlist, - PCWSTR section, DWORD style ) -{ - SP_FILE_COPY_PARAMS_W params; - INFCONTEXT context; - WCHAR dest[MAX_PATH], src[MAX_PATH]; - INT flags; - - TRACE( "hinf=%p/%p section=%s root=%s\n", - hinf, hlist, debugstr_w(section), debugstr_w(src_root) ); - - params.cbSize = sizeof(params); - params.QueueHandle = queue; - params.SourceRootPath = src_root; - params.SourcePath = NULL; - params.SourceDescription = NULL; - params.SourceTagfile = NULL; - params.TargetFilename = dest; - params.CopyStyle = style; - params.LayoutInf = hinf; - params.SecurityDescriptor = NULL; - - if (!hlist) hlist = hinf; - if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE; - if (!(params.TargetDirectory = get_destination_dir( hinf, section ))) return FALSE; - do - { - if (!SetupGetStringFieldW( &context, 1, dest, sizeof(dest)/sizeof(WCHAR), NULL )) - return FALSE; - if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) *src = 0; - if (!SetupGetIntField( &context, 4, &flags )) flags = 0; /* FIXME */ - - params.SourceFilename = *src ? src : NULL; - if (!SetupQueueCopyIndirectW( ¶ms )) return FALSE; - } while (SetupFindNextLine( &context, &context )); - return TRUE; -} - - -/*********************************************************************** - * SetupQueueDeleteSectionA (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueDeleteSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section ) -{ - UNICODE_STRING sectionW; - BOOL ret = FALSE; - - if (RtlCreateUnicodeStringFromAsciiz( §ionW, section )) - { - ret = SetupQueueDeleteSectionW( queue, hinf, hlist, sectionW.Buffer ); - RtlFreeUnicodeString( §ionW ); - } - else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return ret; -} - - -/*********************************************************************** - * SetupQueueDeleteSectionW (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueDeleteSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section ) -{ - INFCONTEXT context; - WCHAR *dest_dir; - WCHAR buffer[MAX_PATH]; - BOOL ret = FALSE; - INT flags; - - TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) ); - - if (!hlist) hlist = hinf; - if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE; - if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE; - do - { - if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) - goto done; - if (!SetupGetIntField( &context, 4, &flags )) flags = 0; - if (!SetupQueueDeleteW( queue, dest_dir, buffer )) goto done; - } while (SetupFindNextLine( &context, &context )); - - ret = TRUE; - done: - HeapFree( GetProcessHeap(), 0, dest_dir ); - return ret; -} - - -/*********************************************************************** - * SetupQueueRenameSectionA (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueRenameSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section ) -{ - UNICODE_STRING sectionW; - BOOL ret = FALSE; - - if (RtlCreateUnicodeStringFromAsciiz( §ionW, section )) - { - ret = SetupQueueRenameSectionW( queue, hinf, hlist, sectionW.Buffer ); - RtlFreeUnicodeString( §ionW ); - } - else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return ret; -} - - -/*********************************************************************** - * SetupQueueRenameSectionW (SETUPAPI.@) - */ -BOOL WINAPI SetupQueueRenameSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section ) -{ - INFCONTEXT context; - WCHAR *dest_dir; - WCHAR src[MAX_PATH], dst[MAX_PATH]; - BOOL ret = FALSE; - - TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) ); - - if (!hlist) hlist = hinf; - if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE; - if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE; - do - { - if (!SetupGetStringFieldW( &context, 1, dst, sizeof(dst)/sizeof(WCHAR), NULL )) - goto done; - if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) - goto done; - if (!SetupQueueRenameW( queue, dest_dir, src, NULL, dst )) goto done; - } while (SetupFindNextLine( &context, &context )); - - ret = TRUE; - done: - HeapFree( GetProcessHeap(), 0, dest_dir ); - return ret; -} - - -/*********************************************************************** - * SetupCommitFileQueueA (SETUPAPI.@) - */ -BOOL WINAPI SetupCommitFileQueueA( HWND owner, HSPFILEQ queue, PSP_FILE_CALLBACK_A handler, - PVOID context ) -{ - struct callback_WtoA_context ctx; - - ctx.orig_context = context; - ctx.orig_handler = handler; - return SetupCommitFileQueueW( owner, queue, QUEUE_callback_WtoA, &ctx ); -} - - -/*********************************************************************** - * create_full_pathW - * - * Recursively create all directories in the path. - */ -static BOOL create_full_pathW(const WCHAR *path) -{ - BOOL ret = TRUE; - int len; - WCHAR *new_path; - - new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR)); - strcpyW(new_path, path); - - while((len = strlenW(new_path)) && new_path[len - 1] == '\\') - new_path[len - 1] = 0; - - while(!CreateDirectoryW(new_path, NULL)) - { - WCHAR *slash; - DWORD last_error = GetLastError(); - - if(last_error == ERROR_ALREADY_EXISTS) - break; - - if(last_error != ERROR_PATH_NOT_FOUND) - { - ret = FALSE; - break; - } - - if(!(slash = strrchrW(new_path, '\\'))) - { - ret = FALSE; - break; - } - - len = slash - new_path; - new_path[len] = 0; - if(!create_full_pathW(new_path)) - { - ret = FALSE; - break; - } - new_path[len] = '\\'; - } - - HeapFree(GetProcessHeap(), 0, new_path); - return ret; -} - -BOOL static do_file_copyW( LPCWSTR source, LPCWSTR target, DWORD style) -{ - BOOL rc = FALSE; - BOOL docopy = TRUE; - - TRACE("copy %s to %s style 0x%lx\n",debugstr_w(source),debugstr_w(target),style); - - /* before copy processing */ - if (style & SP_COPY_REPLACEONLY) - { - if (GetFileAttributesW(target) == INVALID_FILE_ATTRIBUTES) - docopy = FALSE; - } - if (style & (SP_COPY_NEWER_OR_SAME | SP_COPY_NEWER_ONLY | SP_COPY_FORCE_NEWER)) - { - DWORD VersionSizeSource=0; - DWORD VersionSizeTarget=0; - DWORD zero=0; - - /* - * This is sort of an interesting workaround. You see, calling - * GetVersionInfoSize on a builtin dll loads that dll into memory - * and we do not properly unload builtin dlls.. so we effectively - * lock into memory all the targets we are replacing. This leads - * to problems when we try to register the replaced dlls. - * - * So I will test for the existence of the files first so that - * we just basically unconditionally replace the builtin versions. - */ - if ((GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) && - (GetFileAttributesW(source) != INVALID_FILE_ATTRIBUTES)) - { - VersionSizeSource = GetFileVersionInfoSizeW(source,&zero); - VersionSizeTarget = GetFileVersionInfoSizeW(target,&zero); - } - - TRACE("SizeTarget %li ... SizeSource %li\n",VersionSizeTarget, - VersionSizeSource); - - if (VersionSizeSource && VersionSizeTarget) - { - LPVOID VersionSource; - LPVOID VersionTarget; - VS_FIXEDFILEINFO *TargetInfo; - VS_FIXEDFILEINFO *SourceInfo; - UINT length; - WCHAR SubBlock[2]={'\\',0}; - DWORD ret; - - VersionSource = HeapAlloc(GetProcessHeap(),0,VersionSizeSource); - VersionTarget = HeapAlloc(GetProcessHeap(),0,VersionSizeTarget); - - ret = GetFileVersionInfoW(source,0,VersionSizeSource,VersionSource); - if (ret) - ret = GetFileVersionInfoW(target, 0, VersionSizeTarget, - VersionTarget); - - if (ret) - { - ret = VerQueryValueW(VersionSource, SubBlock, - (LPVOID*)&SourceInfo, &length); - if (ret) - ret = VerQueryValueW(VersionTarget, SubBlock, - (LPVOID*)&TargetInfo, &length); - - if (ret) - { - TRACE("Versions: Source %li.%li target %li.%li\n", - SourceInfo->dwFileVersionMS, SourceInfo->dwFileVersionLS, - TargetInfo->dwFileVersionMS, TargetInfo->dwFileVersionLS); - - if (TargetInfo->dwFileVersionMS > SourceInfo->dwFileVersionMS) - { - FIXME("Notify that target version is greater..\n"); - docopy = FALSE; - } - else if ((TargetInfo->dwFileVersionMS == SourceInfo->dwFileVersionMS) - && (TargetInfo->dwFileVersionLS > SourceInfo->dwFileVersionLS)) - { - FIXME("Notify that target version is greater..\n"); - docopy = FALSE; - } - else if ((style & SP_COPY_NEWER_ONLY) && - (TargetInfo->dwFileVersionMS == - SourceInfo->dwFileVersionMS) - &&(TargetInfo->dwFileVersionLS == - SourceInfo->dwFileVersionLS)) - { - FIXME("Notify that target version is greater..\n"); - docopy = FALSE; - } - } - } - HeapFree(GetProcessHeap(),0,VersionSource); - HeapFree(GetProcessHeap(),0,VersionTarget); - } - } - if (style & (SP_COPY_NOOVERWRITE | SP_COPY_FORCE_NOOVERWRITE)) - { - if (GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) - { - FIXME("Notify user target file exists\n"); - docopy = FALSE; - } - } - if (style & (SP_COPY_NODECOMP | SP_COPY_LANGUAGEAWARE | SP_COPY_FORCE_IN_USE | - SP_COPY_IN_USE_NEEDS_REBOOT | SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP)) - { - ERR("Unsupported style(s) 0x%lx\n",style); - } - - if (docopy) - { - rc = CopyFileW(source,target,FALSE); - TRACE("Did copy... rc was %i\n",rc); - } - - /* after copy processing */ - if (style & SP_COPY_DELETESOURCE) - { - if (rc) - DeleteFileW(source); - } - - return rc; -} - -/*********************************************************************** - * SetupCommitFileQueueW (SETUPAPI.@) - */ -BOOL WINAPI SetupCommitFileQueueW( HWND owner, HSPFILEQ handle, PSP_FILE_CALLBACK_W handler, - PVOID context ) -{ - struct file_queue *queue = handle; - struct file_op *op; - BOOL result = FALSE; - FILEPATHS_W paths; - UINT op_result; - - paths.Source = paths.Target = NULL; - - if (!queue->copy_queue.count && !queue->delete_queue.count && !queue->rename_queue.count) - return TRUE; /* nothing to do */ - - if (!handler( context, SPFILENOTIFY_STARTQUEUE, (UINT)owner, 0 )) return FALSE; - - /* perform deletes */ - - if (queue->delete_queue.count) - { - if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_DELETE, - queue->delete_queue.count ))) goto done; - for (op = queue->delete_queue.head; op; op = op->next) - { - build_filepathsW( op, &paths ); - op_result = handler( context, SPFILENOTIFY_STARTDELETE, (UINT_PTR)&paths, FILEOP_DELETE); - if (op_result == FILEOP_ABORT) goto done; - while (op_result == FILEOP_DOIT) - { - TRACE( "deleting file %s\n", debugstr_w(paths.Target) ); - if (DeleteFileW( paths.Target )) break; /* success */ - paths.Win32Error = GetLastError(); - op_result = handler( context, SPFILENOTIFY_DELETEERROR, (UINT_PTR)&paths, 0 ); - if (op_result == FILEOP_ABORT) goto done; - } - handler( context, SPFILENOTIFY_ENDDELETE, (UINT_PTR)&paths, 0 ); - } - handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_DELETE, 0 ); - } - - /* perform renames */ - - if (queue->rename_queue.count) - { - if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_RENAME, - queue->rename_queue.count ))) goto done; - for (op = queue->rename_queue.head; op; op = op->next) - { - build_filepathsW( op, &paths ); - op_result = handler( context, SPFILENOTIFY_STARTRENAME, (UINT_PTR)&paths, FILEOP_RENAME); - if (op_result == FILEOP_ABORT) goto done; - while (op_result == FILEOP_DOIT) - { - TRACE( "renaming file %s -> %s\n", - debugstr_w(paths.Source), debugstr_w(paths.Target) ); - if (MoveFileW( paths.Source, paths.Target )) break; /* success */ - paths.Win32Error = GetLastError(); - op_result = handler( context, SPFILENOTIFY_RENAMEERROR, (UINT_PTR)&paths, 0 ); - if (op_result == FILEOP_ABORT) goto done; - } - handler( context, SPFILENOTIFY_ENDRENAME, (UINT_PTR)&paths, 0 ); - } - handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_RENAME, 0 ); - } - - /* perform copies */ - - if (queue->copy_queue.count) - { - if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_COPY, - queue->copy_queue.count ))) goto done; - for (op = queue->copy_queue.head; op; op = op->next) - { - WCHAR newpath[MAX_PATH]; - - build_filepathsW( op, &paths ); - op_result = handler( context, SPFILENOTIFY_STARTCOPY, (UINT_PTR)&paths, FILEOP_COPY ); - if (op_result == FILEOP_ABORT) goto done; - if (op_result == FILEOP_NEWPATH) op_result = FILEOP_DOIT; - while (op_result == FILEOP_DOIT || op_result == FILEOP_NEWPATH) - { - TRACE( "copying file %s -> %s\n", - debugstr_w( op_result == FILEOP_NEWPATH ? newpath : paths.Source ), - debugstr_w(paths.Target) ); - if (op->dst_path) - { - if (!create_full_pathW( op->dst_path )) - { - paths.Win32Error = GetLastError(); - op_result = handler( context, SPFILENOTIFY_COPYERROR, - (UINT_PTR)&paths, (UINT_PTR)newpath ); - if (op_result == FILEOP_ABORT) goto done; - } - } - if (do_file_copyW( op_result == FILEOP_NEWPATH ? newpath : paths.Source, - paths.Target, op->style )) break; /* success */ - /* try to extract it from the cabinet file */ - if (op->src_tag) - { - if (extract_cabinet_file( op->src_tag, op->src_root, - paths.Source, paths.Target )) break; - } - paths.Win32Error = GetLastError(); - op_result = handler( context, SPFILENOTIFY_COPYERROR, - (UINT_PTR)&paths, (UINT_PTR)newpath ); - if (op_result == FILEOP_ABORT) goto done; - } - handler( context, SPFILENOTIFY_ENDCOPY, (UINT_PTR)&paths, 0 ); - } - handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_COPY, 0 ); - } - - - result = TRUE; - - done: - handler( context, SPFILENOTIFY_ENDQUEUE, result, 0 ); - HeapFree( GetProcessHeap(), 0, (void *)paths.Source ); - HeapFree( GetProcessHeap(), 0, (void *)paths.Target ); - return result; -} - - -/*********************************************************************** - * SetupScanFileQueueA (SETUPAPI.@) - */ -BOOL WINAPI SetupScanFileQueueA( HSPFILEQ queue, DWORD flags, HWND window, - PSP_FILE_CALLBACK_A callback, PVOID context, PDWORD result ) -{ - FIXME("stub\n"); - return FALSE; -} - - -/*********************************************************************** - * SetupScanFileQueueW (SETUPAPI.@) - */ -BOOL WINAPI SetupScanFileQueueW( HSPFILEQ queue, DWORD flags, HWND window, - PSP_FILE_CALLBACK_W callback, PVOID context, PDWORD result ) -{ - FIXME("stub\n"); - return FALSE; -} - - -/*********************************************************************** - * SetupGetFileQueueCount (SETUPAPI.@) - */ -BOOL WINAPI SetupGetFileQueueCount( HSPFILEQ handle, UINT op, PUINT result ) -{ - struct file_queue *queue = handle; - - switch(op) - { - case FILEOP_COPY: - *result = queue->copy_queue.count; - return TRUE; - case FILEOP_RENAME: - *result = queue->rename_queue.count; - return TRUE; - case FILEOP_DELETE: - *result = queue->delete_queue.count; - return TRUE; - } - return FALSE; -} - - -/*********************************************************************** - * SetupGetFileQueueFlags (SETUPAPI.@) - */ -BOOL WINAPI SetupGetFileQueueFlags( HSPFILEQ handle, PDWORD flags ) -{ - struct file_queue *queue = handle; - *flags = queue->flags; - return TRUE; -} - - -/*********************************************************************** - * SetupSetFileQueueFlags (SETUPAPI.@) - */ -BOOL WINAPI SetupSetFileQueueFlags( HSPFILEQ handle, DWORD mask, DWORD flags ) -{ - struct file_queue *queue = handle; - queue->flags = (queue->flags & ~mask) | flags; - return TRUE; -} - - -/*********************************************************************** - * SetupInitDefaultQueueCallback (SETUPAPI.@) - */ -PVOID WINAPI SetupInitDefaultQueueCallback( HWND owner ) -{ - return SetupInitDefaultQueueCallbackEx( owner, 0, 0, 0, NULL ); -} - - -/*********************************************************************** - * SetupInitDefaultQueueCallbackEx (SETUPAPI.@) - */ -PVOID WINAPI SetupInitDefaultQueueCallbackEx( HWND owner, HWND progress, UINT msg, - DWORD reserved1, PVOID reserved2 ) -{ - struct default_callback_context *context; - - if ((context = HeapAlloc( GetProcessHeap(), 0, sizeof(*context) ))) - { - context->owner = owner; - context->progress = progress; - context->message = msg; - } - return context; -} - - -/*********************************************************************** - * SetupTermDefaultQueueCallback (SETUPAPI.@) - */ -void WINAPI SetupTermDefaultQueueCallback( PVOID context ) -{ - HeapFree( GetProcessHeap(), 0, context ); -} - - -/*********************************************************************** - * SetupDefaultQueueCallbackA (SETUPAPI.@) - */ -UINT WINAPI SetupDefaultQueueCallbackA( PVOID context, UINT notification, - UINT_PTR param1, UINT_PTR param2 ) -{ - FILEPATHS_A *paths = (FILEPATHS_A *)param1; - - switch(notification) - { - case SPFILENOTIFY_STARTQUEUE: - TRACE( "start queue\n" ); - return TRUE; - case SPFILENOTIFY_ENDQUEUE: - TRACE( "end queue\n" ); - return 0; - case SPFILENOTIFY_STARTSUBQUEUE: - TRACE( "start subqueue %d count %d\n", param1, param2 ); - return TRUE; - case SPFILENOTIFY_ENDSUBQUEUE: - TRACE( "end subqueue %d\n", param1 ); - return 0; - case SPFILENOTIFY_STARTDELETE: - TRACE( "start delete %s\n", debugstr_a(paths->Target) ); - return FILEOP_DOIT; - case SPFILENOTIFY_ENDDELETE: - TRACE( "end delete %s\n", debugstr_a(paths->Target) ); - return 0; - case SPFILENOTIFY_DELETEERROR: - ERR( "delete error %d %s\n", paths->Win32Error, debugstr_a(paths->Target) ); - return FILEOP_SKIP; - case SPFILENOTIFY_STARTRENAME: - TRACE( "start rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); - return FILEOP_DOIT; - case SPFILENOTIFY_ENDRENAME: - TRACE( "end rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); - return 0; - case SPFILENOTIFY_RENAMEERROR: - ERR( "rename error %d %s -> %s\n", paths->Win32Error, - debugstr_a(paths->Source), debugstr_a(paths->Target) ); - return FILEOP_SKIP; - case SPFILENOTIFY_STARTCOPY: - TRACE( "start copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); - return FILEOP_DOIT; - case SPFILENOTIFY_ENDCOPY: - TRACE( "end copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); - return 0; - case SPFILENOTIFY_COPYERROR: - ERR( "copy error %d %s -> %s\n", paths->Win32Error, - debugstr_a(paths->Source), debugstr_a(paths->Target) ); - return FILEOP_SKIP; - case SPFILENOTIFY_NEEDMEDIA: - TRACE( "need media\n" ); - return FILEOP_SKIP; - default: - FIXME( "notification %d params %x,%x\n", notification, param1, param2 ); - break; - } - return 0; -} - - -/*********************************************************************** - * SetupDefaultQueueCallbackW (SETUPAPI.@) - */ -UINT WINAPI SetupDefaultQueueCallbackW( PVOID context, UINT notification, - UINT_PTR param1, UINT_PTR param2 ) -{ - FILEPATHS_W *paths = (FILEPATHS_W *)param1; - - switch(notification) - { - case SPFILENOTIFY_STARTQUEUE: - TRACE( "start queue\n" ); - return TRUE; - case SPFILENOTIFY_ENDQUEUE: - TRACE( "end queue\n" ); - return 0; - case SPFILENOTIFY_STARTSUBQUEUE: - TRACE( "start subqueue %d count %d\n", param1, param2 ); - return TRUE; - case SPFILENOTIFY_ENDSUBQUEUE: - TRACE( "end subqueue %d\n", param1 ); - return 0; - case SPFILENOTIFY_STARTDELETE: - TRACE( "start delete %s\n", debugstr_w(paths->Target) ); - return FILEOP_DOIT; - case SPFILENOTIFY_ENDDELETE: - TRACE( "end delete %s\n", debugstr_w(paths->Target) ); - return 0; - case SPFILENOTIFY_DELETEERROR: - ERR( "delete error %d %s\n", paths->Win32Error, debugstr_w(paths->Target) ); - return FILEOP_SKIP; - case SPFILENOTIFY_STARTRENAME: - TRACE( "start rename %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) ); - return FILEOP_DOIT; - case SPFILENOTIFY_ENDRENAME: - TRACE( "end rename %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) ); - return 0; - case SPFILENOTIFY_RENAMEERROR: - ERR( "rename error %d %s -> %s\n", paths->Win32Error, - debugstr_w(paths->Source), debugstr_w(paths->Target) ); - return FILEOP_SKIP; - case SPFILENOTIFY_STARTCOPY: - TRACE( "start copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) ); - return FILEOP_DOIT; - case SPFILENOTIFY_ENDCOPY: - TRACE( "end copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) ); - return 0; - case SPFILENOTIFY_COPYERROR: - ERR( "copy error %d %s -> %s\n", paths->Win32Error, - debugstr_w(paths->Source), debugstr_w(paths->Target) ); - return FILEOP_SKIP; - case SPFILENOTIFY_NEEDMEDIA: - TRACE( "need media\n" ); - return FILEOP_SKIP; - default: - FIXME( "notification %d params %x,%x\n", notification, param1, param2 ); - break; - } - return 0; -} +/* + * Setupapi file queue routines + * + * Copyright 2002 Alexandre Julliard for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "winerror.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "wine/unicode.h" +#include "setupapi_private.h" +#include "winver.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/* context structure for the default queue callback */ +struct default_callback_context +{ + HWND owner; + HWND progress; + UINT message; +}; + +struct file_op +{ + struct file_op *next; + UINT style; + WCHAR *src_root; + WCHAR *src_path; + WCHAR *src_file; + WCHAR *src_descr; + WCHAR *src_tag; + WCHAR *dst_path; + WCHAR *dst_file; +}; + +struct file_op_queue +{ + struct file_op *head; + struct file_op *tail; + unsigned int count; +}; + +struct file_queue +{ + struct file_op_queue copy_queue; + struct file_op_queue delete_queue; + struct file_op_queue rename_queue; + DWORD flags; +}; + + +inline static WCHAR *strdupW( const WCHAR *str ) +{ + WCHAR *ret = NULL; + if (str) + { + int len = (strlenW(str) + 1) * sizeof(WCHAR); + if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( ret, str, len ); + } + return ret; +} + + +inline static WCHAR *strdupAtoW( const char *str ) +{ + WCHAR *ret = NULL; + if (str) + { + DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); + } + return ret; +} + +inline static char *strdupWtoA( const WCHAR *str ) +{ + char *ret = NULL; + if (str) + { + DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL ); + if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) + WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL ); + } + return ret; +} + +/* append a file operation to a queue */ +inline static void queue_file_op( struct file_op_queue *queue, struct file_op *op ) +{ + op->next = NULL; + if (queue->tail) queue->tail->next = op; + else queue->head = op; + queue->tail = op; + queue->count++; +} + +/* free all the file operations on a given queue */ +static void free_file_op_queue( struct file_op_queue *queue ) +{ + struct file_op *t, *op = queue->head; + + while( op ) + { + HeapFree( GetProcessHeap(), 0, op->src_root ); + HeapFree( GetProcessHeap(), 0, op->src_path ); + HeapFree( GetProcessHeap(), 0, op->src_file ); + HeapFree( GetProcessHeap(), 0, op->src_descr ); + HeapFree( GetProcessHeap(), 0, op->src_tag ); + HeapFree( GetProcessHeap(), 0, op->dst_path ); + if (op->dst_file != op->src_file) HeapFree( GetProcessHeap(), 0, op->dst_file ); + t = op; + op = op->next; + HeapFree( GetProcessHeap(), 0, t ); + } +} + +/* concat 3 strings to make a path, handling separators correctly */ +static void concat_W( WCHAR *buffer, const WCHAR *src1, const WCHAR *src2, const WCHAR *src3 ) +{ + *buffer = 0; + if (src1 && *src1) + { + strcpyW( buffer, src1 ); + buffer += strlenW(buffer ); + if (buffer[-1] != '\\') *buffer++ = '\\'; + if (src2) while (*src2 == '\\') src2++; + } + + if (src2) + { + strcpyW( buffer, src2 ); + buffer += strlenW(buffer ); + if (buffer[-1] != '\\') *buffer++ = '\\'; + if (src3) while (*src3 == '\\') src3++; + } + if (src3) + { + strcpyW( buffer, src3 ); + buffer += strlenW(buffer ); + } +} + + +/*********************************************************************** + * build_filepathsW + * + * Build a FILEPATHS_W structure for a given file operation. + */ +static BOOL build_filepathsW( const struct file_op *op, FILEPATHS_W *paths ) +{ + unsigned int src_len = 1, dst_len = 1; + WCHAR *source = (PWSTR)paths->Source, *target = (PWSTR)paths->Target; + + if (op->src_root) src_len += strlenW(op->src_root) + 1; + if (op->src_path) src_len += strlenW(op->src_path) + 1; + if (op->src_file) src_len += strlenW(op->src_file) + 1; + if (op->dst_path) dst_len += strlenW(op->dst_path) + 1; + if (op->dst_file) dst_len += strlenW(op->dst_file) + 1; + src_len *= sizeof(WCHAR); + dst_len *= sizeof(WCHAR); + + if (!source || HeapSize( GetProcessHeap(), 0, source ) < src_len ) + { + HeapFree( GetProcessHeap(), 0, source ); + paths->Source = source = HeapAlloc( GetProcessHeap(), 0, src_len ); + } + if (!target || HeapSize( GetProcessHeap(), 0, target ) < dst_len ) + { + HeapFree( GetProcessHeap(), 0, target ); + paths->Target = target = HeapAlloc( GetProcessHeap(), 0, dst_len ); + } + if (!source || !target) return FALSE; + concat_W( source, op->src_root, op->src_path, op->src_file ); + concat_W( target, NULL, op->dst_path, op->dst_file ); + paths->Win32Error = 0; + paths->Flags = 0; + return TRUE; +} + + +/*********************************************************************** + * QUEUE_callback_WtoA + * + * Map a file callback parameters from W to A and call the A callback. + */ +UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification, + UINT_PTR param1, UINT_PTR param2 ) +{ + struct callback_WtoA_context *callback_ctx = context; + char buffer[MAX_PATH]; + UINT ret; + UINT_PTR old_param2 = param2; + + switch(notification) + { + case SPFILENOTIFY_COPYERROR: + param2 = (UINT_PTR)&buffer; + /* fall through */ + case SPFILENOTIFY_STARTDELETE: + case SPFILENOTIFY_ENDDELETE: + case SPFILENOTIFY_DELETEERROR: + case SPFILENOTIFY_STARTRENAME: + case SPFILENOTIFY_ENDRENAME: + case SPFILENOTIFY_RENAMEERROR: + case SPFILENOTIFY_STARTCOPY: + case SPFILENOTIFY_ENDCOPY: + { + FILEPATHS_W *pathsW = (FILEPATHS_W *)param1; + FILEPATHS_A pathsA; + + pathsA.Source = strdupWtoA( pathsW->Source ); + pathsA.Target = strdupWtoA( pathsW->Target ); + pathsA.Win32Error = pathsW->Win32Error; + pathsA.Flags = pathsW->Flags; + ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, + (UINT_PTR)&pathsA, param2 ); + HeapFree( GetProcessHeap(), 0, (void *)pathsA.Source ); + HeapFree( GetProcessHeap(), 0, (void *)pathsA.Target ); + } + if (notification == SPFILENOTIFY_COPYERROR) + MultiByteToWideChar( CP_ACP, 0, buffer, -1, (WCHAR *)old_param2, MAX_PATH ); + break; + + case SPFILENOTIFY_STARTREGISTRATION: + case SPFILENOTIFY_ENDREGISTRATION: + { + SP_REGISTER_CONTROL_STATUSW *statusW = (SP_REGISTER_CONTROL_STATUSW *)param1; + SP_REGISTER_CONTROL_STATUSA statusA; + + statusA.cbSize = sizeof(statusA); + statusA.FileName = strdupWtoA( statusW->FileName ); + statusA.Win32Error = statusW->Win32Error; + statusA.FailureCode = statusW->FailureCode; + ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, + (UINT_PTR)&statusA, param2 ); + HeapFree( GetProcessHeap(), 0, (LPSTR)statusA.FileName ); + } + break; + + case SPFILENOTIFY_NEEDMEDIA: + case SPFILENOTIFY_QUEUESCAN: + FIXME("mapping for %d not implemented\n",notification); + case SPFILENOTIFY_STARTQUEUE: + case SPFILENOTIFY_ENDQUEUE: + case SPFILENOTIFY_STARTSUBQUEUE: + case SPFILENOTIFY_ENDSUBQUEUE: + default: + ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, param1, param2 ); + break; + } + return ret; +} + + +/*********************************************************************** + * get_src_file_info + * + * Retrieve the source file information for a given file. + */ +static void get_src_file_info( HINF hinf, struct file_op *op ) +{ + static const WCHAR SourceDisksNames[] = + {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0}; + static const WCHAR SourceDisksFiles[] = + {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0}; + + INFCONTEXT file_ctx, disk_ctx; + INT id, diskid; + DWORD len, len2; + + /* find the SourceDisksFiles entry */ + if (!SetupFindFirstLineW( hinf, SourceDisksFiles, op->src_file, &file_ctx )) + { + const WCHAR *dir; + + if ((op->style & (SP_COPY_SOURCE_ABSOLUTE|SP_COPY_SOURCEPATH_ABSOLUTE))) return; + /* no specific info, use .inf file source directory */ + if (!op->src_root && (dir = DIRID_get_string( hinf, DIRID_SRCPATH ))) + op->src_root = strdupW( dir ); + return; + } + if (!SetupGetIntField( &file_ctx, 1, &diskid )) return; + + /* now find the diskid in the SourceDisksNames section */ + if (!SetupFindFirstLineW( hinf, SourceDisksNames, NULL, &disk_ctx )) return; + for (;;) + { + if (SetupGetIntField( &disk_ctx, 0, &id ) && (id == diskid)) break; + if (!SetupFindNextLine( &disk_ctx, &disk_ctx )) return; + } + + /* and fill in the missing info */ + + if (!op->src_descr) + { + if (SetupGetStringFieldW( &disk_ctx, 1, NULL, 0, &len ) && + (op->src_descr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) ))) + SetupGetStringFieldW( &disk_ctx, 1, op->src_descr, len, NULL ); + } + if (!op->src_tag) + { + if (SetupGetStringFieldW( &disk_ctx, 2, NULL, 0, &len ) && + (op->src_tag = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) ))) + SetupGetStringFieldW( &disk_ctx, 2, op->src_tag, len, NULL ); + } + if (!op->src_path && !(op->style & SP_COPY_SOURCE_ABSOLUTE)) + { + if (!(op->style & SP_COPY_SOURCEPATH_ABSOLUTE)) + { + /* retrieve relative path for this disk */ + if (!SetupGetStringFieldW( &disk_ctx, 4, NULL, 0, &len )) len = 0; + } + /* retrieve relative path for this file */ + if (!SetupGetStringFieldW( &file_ctx, 2, NULL, 0, &len2 )) len2 = 0; + + if ((len || len2) && + (op->src_path = HeapAlloc( GetProcessHeap(), 0, (len+len2)*sizeof(WCHAR) ))) + { + WCHAR *ptr = op->src_path; + if (len) + { + SetupGetStringFieldW( &disk_ctx, 4, op->src_path, len, NULL ); + ptr = op->src_path + strlenW(op->src_path); + if (len2 && ptr > op->src_path && ptr[-1] != '\\') *ptr++ = '\\'; + } + if (!SetupGetStringFieldW( &disk_ctx, 4, ptr, len2, NULL )) *ptr = 0; + } + } + if (!op->src_root) op->src_root = strdupW( PARSER_get_src_root(hinf) ); +} + + +/*********************************************************************** + * get_destination_dir + * + * Retrieve the destination dir for a given section. + */ +static WCHAR *get_destination_dir( HINF hinf, const WCHAR *section ) +{ + static const WCHAR Dest[] = {'D','e','s','t','i','n','a','t','i','o','n','D','i','r','s',0}; + static const WCHAR Def[] = {'D','e','f','a','u','l','t','D','e','s','t','D','i','r',0}; + INFCONTEXT context; + + if (!SetupFindFirstLineW( hinf, Dest, section, &context ) && + !SetupFindFirstLineW( hinf, Dest, Def, &context )) return NULL; + return PARSER_get_dest_dir( &context ); +} + + +static void (WINAPI *pExtractFiles)( LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD ); + +/*********************************************************************** + * extract_cabinet_file + * + * Extract a file from a .cab file. + */ +static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root, + const WCHAR *src, const WCHAR *dst ) +{ + static const WCHAR extW[] = {'.','c','a','b',0}; + static HMODULE advpack; + + char *cab_path, *cab_file; + int len = strlenW( cabinet ); + + /* make sure the cabinet file has a .cab extension */ + if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE; + if (!pExtractFiles) + { + if (!advpack && !(advpack = LoadLibraryA( "advpack.dll" ))) + { + ERR( "could not load advpack.dll\n" ); + return FALSE; + } + if (!(pExtractFiles = (void *)GetProcAddress( advpack, "ExtractFiles" ))) + { + ERR( "could not find ExtractFiles in advpack.dll\n" ); + return FALSE; + } + } + + if (!(cab_path = strdupWtoA( root ))) return FALSE; + len = WideCharToMultiByte( CP_ACP, 0, cabinet, -1, NULL, 0, NULL, NULL ); + if (!(cab_file = HeapAlloc( GetProcessHeap(), 0, strlen(cab_path) + len + 1 ))) + { + HeapFree( GetProcessHeap(), 0, cab_path ); + return FALSE; + } + strcpy( cab_file, cab_path ); + if (cab_file[0] && cab_file[strlen(cab_file)-1] != '\\') strcat( cab_file, "\\" ); + WideCharToMultiByte( CP_ACP, 0, cabinet, -1, cab_file + strlen(cab_file), len, NULL, NULL ); + FIXME( "awful hack: extracting cabinet %s\n", debugstr_a(cab_file) ); + pExtractFiles( cab_file, cab_path, 0, 0, 0, 0 ); + HeapFree( GetProcessHeap(), 0, cab_file ); + HeapFree( GetProcessHeap(), 0, cab_path ); + return CopyFileW( src, dst, FALSE /*FIXME*/ ); +} + + +/*********************************************************************** + * SetupOpenFileQueue (SETUPAPI.@) + */ +HSPFILEQ WINAPI SetupOpenFileQueue(void) +{ + struct file_queue *queue; + + if (!(queue = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*queue)))) + return (HSPFILEQ)INVALID_HANDLE_VALUE; + return queue; +} + + +/*********************************************************************** + * SetupCloseFileQueue (SETUPAPI.@) + */ +BOOL WINAPI SetupCloseFileQueue( HSPFILEQ handle ) +{ + struct file_queue *queue = handle; + + free_file_op_queue( &queue->copy_queue ); + free_file_op_queue( &queue->rename_queue ); + free_file_op_queue( &queue->delete_queue ); + HeapFree( GetProcessHeap(), 0, queue ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueCopyIndirectA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopyIndirectA( PSP_FILE_COPY_PARAMS_A params ) +{ + struct file_queue *queue = params->QueueHandle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = params->CopyStyle; + op->src_root = strdupAtoW( params->SourceRootPath ); + op->src_path = strdupAtoW( params->SourcePath ); + op->src_file = strdupAtoW( params->SourceFilename ); + op->src_descr = strdupAtoW( params->SourceDescription ); + op->src_tag = strdupAtoW( params->SourceTagfile ); + op->dst_path = strdupAtoW( params->TargetDirectory ); + op->dst_file = strdupAtoW( params->TargetFilename ); + + /* some defaults */ + if (!op->src_file) op->src_file = op->dst_file; + if (params->LayoutInf) + { + get_src_file_info( params->LayoutInf, op ); + if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file ); + } + + TRACE( "root=%s path=%s file=%s -> dir=%s file=%s descr=%s tag=%s\n", + debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file), + debugstr_w(op->dst_path), debugstr_w(op->dst_file), + debugstr_w(op->src_descr), debugstr_w(op->src_tag) ); + + queue_file_op( &queue->copy_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueCopyIndirectW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopyIndirectW( PSP_FILE_COPY_PARAMS_W params ) +{ + struct file_queue *queue = params->QueueHandle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = params->CopyStyle; + op->src_root = strdupW( params->SourceRootPath ); + op->src_path = strdupW( params->SourcePath ); + op->src_file = strdupW( params->SourceFilename ); + op->src_descr = strdupW( params->SourceDescription ); + op->src_tag = strdupW( params->SourceTagfile ); + op->dst_path = strdupW( params->TargetDirectory ); + op->dst_file = strdupW( params->TargetFilename ); + + /* some defaults */ + if (!op->src_file) op->src_file = op->dst_file; + if (params->LayoutInf) + { + get_src_file_info( params->LayoutInf, op ); + if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file ); + } + + TRACE( "root=%s path=%s file=%s -> dir=%s file=%s descr=%s tag=%s\n", + debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file), + debugstr_w(op->dst_path), debugstr_w(op->dst_file), + debugstr_w(op->src_descr), debugstr_w(op->src_tag) ); + + queue_file_op( &queue->copy_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueCopyA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopyA( HSPFILEQ queue, PCSTR src_root, PCSTR src_path, PCSTR src_file, + PCSTR src_descr, PCSTR src_tag, PCSTR dst_dir, PCSTR dst_file, + DWORD style ) +{ + SP_FILE_COPY_PARAMS_A params; + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = src_path; + params.SourceFilename = src_file; + params.SourceDescription = src_descr; + params.SourceTagfile = src_tag; + params.TargetDirectory = dst_dir; + params.TargetFilename = dst_file; + params.CopyStyle = style; + params.LayoutInf = 0; + params.SecurityDescriptor = NULL; + return SetupQueueCopyIndirectA( ¶ms ); +} + + +/*********************************************************************** + * SetupQueueCopyW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopyW( HSPFILEQ queue, PCWSTR src_root, PCWSTR src_path, PCWSTR src_file, + PCWSTR src_descr, PCWSTR src_tag, PCWSTR dst_dir, PCWSTR dst_file, + DWORD style ) +{ + SP_FILE_COPY_PARAMS_W params; + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = src_path; + params.SourceFilename = src_file; + params.SourceDescription = src_descr; + params.SourceTagfile = src_tag; + params.TargetDirectory = dst_dir; + params.TargetFilename = dst_file; + params.CopyStyle = style; + params.LayoutInf = 0; + params.SecurityDescriptor = NULL; + return SetupQueueCopyIndirectW( ¶ms ); +} + + +/*********************************************************************** + * SetupQueueDefaultCopyA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDefaultCopyA( HSPFILEQ queue, HINF hinf, PCSTR src_root, PCSTR src_file, + PCSTR dst_file, DWORD style ) +{ + SP_FILE_COPY_PARAMS_A params; + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = NULL; + params.SourceFilename = src_file; + params.SourceDescription = NULL; + params.SourceTagfile = NULL; + params.TargetDirectory = NULL; + params.TargetFilename = dst_file; + params.CopyStyle = style; + params.LayoutInf = hinf; + params.SecurityDescriptor = NULL; + return SetupQueueCopyIndirectA( ¶ms ); +} + + +/*********************************************************************** + * SetupQueueDefaultCopyW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDefaultCopyW( HSPFILEQ queue, HINF hinf, PCWSTR src_root, PCWSTR src_file, + PCWSTR dst_file, DWORD style ) +{ + SP_FILE_COPY_PARAMS_W params; + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = NULL; + params.SourceFilename = src_file; + params.SourceDescription = NULL; + params.SourceTagfile = NULL; + params.TargetDirectory = NULL; + params.TargetFilename = dst_file; + params.CopyStyle = style; + params.LayoutInf = hinf; + params.SecurityDescriptor = NULL; + return SetupQueueCopyIndirectW( ¶ms ); +} + + +/*********************************************************************** + * SetupQueueDeleteA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDeleteA( HSPFILEQ handle, PCSTR part1, PCSTR part2 ) +{ + struct file_queue *queue = handle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = 0; + op->src_root = NULL; + op->src_path = NULL; + op->src_file = NULL; + op->src_descr = NULL; + op->src_tag = NULL; + op->dst_path = strdupAtoW( part1 ); + op->dst_file = strdupAtoW( part2 ); + queue_file_op( &queue->delete_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueDeleteW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDeleteW( HSPFILEQ handle, PCWSTR part1, PCWSTR part2 ) +{ + struct file_queue *queue = handle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = 0; + op->src_root = NULL; + op->src_path = NULL; + op->src_file = NULL; + op->src_descr = NULL; + op->src_tag = NULL; + op->dst_path = strdupW( part1 ); + op->dst_file = strdupW( part2 ); + queue_file_op( &queue->delete_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueRenameA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueRenameA( HSPFILEQ handle, PCSTR SourcePath, PCSTR SourceFilename, + PCSTR TargetPath, PCSTR TargetFilename ) +{ + struct file_queue *queue = handle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = 0; + op->src_root = NULL; + op->src_path = strdupAtoW( SourcePath ); + op->src_file = strdupAtoW( SourceFilename ); + op->src_descr = NULL; + op->src_tag = NULL; + op->dst_path = strdupAtoW( TargetPath ); + op->dst_file = strdupAtoW( TargetFilename ); + queue_file_op( &queue->rename_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueRenameW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueRenameW( HSPFILEQ handle, PCWSTR SourcePath, PCWSTR SourceFilename, + PCWSTR TargetPath, PCWSTR TargetFilename ) +{ + struct file_queue *queue = handle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = 0; + op->src_root = NULL; + op->src_path = strdupW( SourcePath ); + op->src_file = strdupW( SourceFilename ); + op->src_descr = NULL; + op->src_tag = NULL; + op->dst_path = strdupW( TargetPath ); + op->dst_file = strdupW( TargetFilename ); + queue_file_op( &queue->rename_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueCopySectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopySectionA( HSPFILEQ queue, PCSTR src_root, HINF hinf, HINF hlist, + PCSTR section, DWORD style ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + if (!src_root) + ret = SetupQueueCopySectionW( queue, NULL, hinf, hlist, sectionW.Buffer, style ); + else + { + UNICODE_STRING srcW; + if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root )) + { + ret = SetupQueueCopySectionW( queue, srcW.Buffer, hinf, hlist, sectionW.Buffer, style ); + RtlFreeUnicodeString( &srcW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + } + RtlFreeUnicodeString( §ionW ); + return ret; +} + + +/*********************************************************************** + * SetupQueueCopySectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopySectionW( HSPFILEQ queue, PCWSTR src_root, HINF hinf, HINF hlist, + PCWSTR section, DWORD style ) +{ + SP_FILE_COPY_PARAMS_W params; + INFCONTEXT context; + WCHAR dest[MAX_PATH], src[MAX_PATH]; + INT flags; + + TRACE( "hinf=%p/%p section=%s root=%s\n", + hinf, hlist, debugstr_w(section), debugstr_w(src_root) ); + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = NULL; + params.SourceDescription = NULL; + params.SourceTagfile = NULL; + params.TargetFilename = dest; + params.CopyStyle = style; + params.LayoutInf = hinf; + params.SecurityDescriptor = NULL; + + if (!hlist) hlist = hinf; + if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE; + if (!(params.TargetDirectory = get_destination_dir( hinf, section ))) return FALSE; + do + { + if (!SetupGetStringFieldW( &context, 1, dest, sizeof(dest)/sizeof(WCHAR), NULL )) + return FALSE; + if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) *src = 0; + if (!SetupGetIntField( &context, 4, &flags )) flags = 0; /* FIXME */ + + params.SourceFilename = *src ? src : NULL; + if (!SetupQueueCopyIndirectW( ¶ms )) return FALSE; + } while (SetupFindNextLine( &context, &context )); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueDeleteSectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDeleteSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + ret = SetupQueueDeleteSectionW( queue, hinf, hlist, sectionW.Buffer ); + RtlFreeUnicodeString( §ionW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return ret; +} + + +/*********************************************************************** + * SetupQueueDeleteSectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDeleteSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section ) +{ + INFCONTEXT context; + WCHAR *dest_dir; + WCHAR buffer[MAX_PATH]; + BOOL ret = FALSE; + INT flags; + + TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) ); + + if (!hlist) hlist = hinf; + if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE; + if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE; + do + { + if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + goto done; + if (!SetupGetIntField( &context, 4, &flags )) flags = 0; + if (!SetupQueueDeleteW( queue, dest_dir, buffer )) goto done; + } while (SetupFindNextLine( &context, &context )); + + ret = TRUE; + done: + HeapFree( GetProcessHeap(), 0, dest_dir ); + return ret; +} + + +/*********************************************************************** + * SetupQueueRenameSectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueRenameSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + ret = SetupQueueRenameSectionW( queue, hinf, hlist, sectionW.Buffer ); + RtlFreeUnicodeString( §ionW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return ret; +} + + +/*********************************************************************** + * SetupQueueRenameSectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueRenameSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section ) +{ + INFCONTEXT context; + WCHAR *dest_dir; + WCHAR src[MAX_PATH], dst[MAX_PATH]; + BOOL ret = FALSE; + + TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) ); + + if (!hlist) hlist = hinf; + if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE; + if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE; + do + { + if (!SetupGetStringFieldW( &context, 1, dst, sizeof(dst)/sizeof(WCHAR), NULL )) + goto done; + if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) + goto done; + if (!SetupQueueRenameW( queue, dest_dir, src, NULL, dst )) goto done; + } while (SetupFindNextLine( &context, &context )); + + ret = TRUE; + done: + HeapFree( GetProcessHeap(), 0, dest_dir ); + return ret; +} + + +/*********************************************************************** + * SetupCommitFileQueueA (SETUPAPI.@) + */ +BOOL WINAPI SetupCommitFileQueueA( HWND owner, HSPFILEQ queue, PSP_FILE_CALLBACK_A handler, + PVOID context ) +{ + struct callback_WtoA_context ctx; + + ctx.orig_context = context; + ctx.orig_handler = handler; + return SetupCommitFileQueueW( owner, queue, QUEUE_callback_WtoA, &ctx ); +} + + +/*********************************************************************** + * create_full_pathW + * + * Recursively create all directories in the path. + */ +static BOOL create_full_pathW(const WCHAR *path) +{ + BOOL ret = TRUE; + int len; + WCHAR *new_path; + + new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR)); + strcpyW(new_path, path); + + while((len = strlenW(new_path)) && new_path[len - 1] == '\\') + new_path[len - 1] = 0; + + while(!CreateDirectoryW(new_path, NULL)) + { + WCHAR *slash; + DWORD last_error = GetLastError(); + + if(last_error == ERROR_ALREADY_EXISTS) + break; + + if(last_error != ERROR_PATH_NOT_FOUND) + { + ret = FALSE; + break; + } + + if(!(slash = strrchrW(new_path, '\\'))) + { + ret = FALSE; + break; + } + + len = slash - new_path; + new_path[len] = 0; + if(!create_full_pathW(new_path)) + { + ret = FALSE; + break; + } + new_path[len] = '\\'; + } + + HeapFree(GetProcessHeap(), 0, new_path); + return ret; +} + +BOOL static do_file_copyW( LPCWSTR source, LPCWSTR target, DWORD style) +{ + BOOL rc = FALSE; + BOOL docopy = TRUE; + + TRACE("copy %s to %s style 0x%lx\n",debugstr_w(source),debugstr_w(target),style); + + /* before copy processing */ + if (style & SP_COPY_REPLACEONLY) + { + if (GetFileAttributesW(target) == INVALID_FILE_ATTRIBUTES) + docopy = FALSE; + } + if (style & (SP_COPY_NEWER_OR_SAME | SP_COPY_NEWER_ONLY | SP_COPY_FORCE_NEWER)) + { + DWORD VersionSizeSource=0; + DWORD VersionSizeTarget=0; + DWORD zero=0; + + /* + * This is sort of an interesting workaround. You see, calling + * GetVersionInfoSize on a builtin dll loads that dll into memory + * and we do not properly unload builtin dlls.. so we effectively + * lock into memory all the targets we are replacing. This leads + * to problems when we try to register the replaced dlls. + * + * So I will test for the existence of the files first so that + * we just basically unconditionally replace the builtin versions. + */ + if ((GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) && + (GetFileAttributesW(source) != INVALID_FILE_ATTRIBUTES)) + { + VersionSizeSource = GetFileVersionInfoSizeW(source,&zero); + VersionSizeTarget = GetFileVersionInfoSizeW(target,&zero); + } + + TRACE("SizeTarget %li ... SizeSource %li\n",VersionSizeTarget, + VersionSizeSource); + + if (VersionSizeSource && VersionSizeTarget) + { + LPVOID VersionSource; + LPVOID VersionTarget; + VS_FIXEDFILEINFO *TargetInfo; + VS_FIXEDFILEINFO *SourceInfo; + UINT length; + WCHAR SubBlock[2]={'\\',0}; + DWORD ret; + + VersionSource = HeapAlloc(GetProcessHeap(),0,VersionSizeSource); + VersionTarget = HeapAlloc(GetProcessHeap(),0,VersionSizeTarget); + + ret = GetFileVersionInfoW(source,0,VersionSizeSource,VersionSource); + if (ret) + ret = GetFileVersionInfoW(target, 0, VersionSizeTarget, + VersionTarget); + + if (ret) + { + ret = VerQueryValueW(VersionSource, SubBlock, + (LPVOID*)&SourceInfo, &length); + if (ret) + ret = VerQueryValueW(VersionTarget, SubBlock, + (LPVOID*)&TargetInfo, &length); + + if (ret) + { + TRACE("Versions: Source %li.%li target %li.%li\n", + SourceInfo->dwFileVersionMS, SourceInfo->dwFileVersionLS, + TargetInfo->dwFileVersionMS, TargetInfo->dwFileVersionLS); + + if (TargetInfo->dwFileVersionMS > SourceInfo->dwFileVersionMS) + { + FIXME("Notify that target version is greater..\n"); + docopy = FALSE; + } + else if ((TargetInfo->dwFileVersionMS == SourceInfo->dwFileVersionMS) + && (TargetInfo->dwFileVersionLS > SourceInfo->dwFileVersionLS)) + { + FIXME("Notify that target version is greater..\n"); + docopy = FALSE; + } + else if ((style & SP_COPY_NEWER_ONLY) && + (TargetInfo->dwFileVersionMS == + SourceInfo->dwFileVersionMS) + &&(TargetInfo->dwFileVersionLS == + SourceInfo->dwFileVersionLS)) + { + FIXME("Notify that target version is greater..\n"); + docopy = FALSE; + } + } + } + HeapFree(GetProcessHeap(),0,VersionSource); + HeapFree(GetProcessHeap(),0,VersionTarget); + } + } + if (style & (SP_COPY_NOOVERWRITE | SP_COPY_FORCE_NOOVERWRITE)) + { + if (GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) + { + FIXME("Notify user target file exists\n"); + docopy = FALSE; + } + } + if (style & (SP_COPY_NODECOMP | SP_COPY_LANGUAGEAWARE | SP_COPY_FORCE_IN_USE | + SP_COPY_IN_USE_NEEDS_REBOOT | SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP)) + { + ERR("Unsupported style(s) 0x%lx\n",style); + } + + if (docopy) + { + rc = CopyFileW(source,target,FALSE); + TRACE("Did copy... rc was %i\n",rc); + } + + /* after copy processing */ + if (style & SP_COPY_DELETESOURCE) + { + if (rc) + DeleteFileW(source); + } + + return rc; +} + +/*********************************************************************** + * SetupCommitFileQueueW (SETUPAPI.@) + */ +BOOL WINAPI SetupCommitFileQueueW( HWND owner, HSPFILEQ handle, PSP_FILE_CALLBACK_W handler, + PVOID context ) +{ + struct file_queue *queue = handle; + struct file_op *op; + BOOL result = FALSE; + FILEPATHS_W paths; + UINT op_result; + + paths.Source = paths.Target = NULL; + + if (!queue->copy_queue.count && !queue->delete_queue.count && !queue->rename_queue.count) + return TRUE; /* nothing to do */ + + if (!handler( context, SPFILENOTIFY_STARTQUEUE, (UINT)owner, 0 )) return FALSE; + + /* perform deletes */ + + if (queue->delete_queue.count) + { + if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_DELETE, + queue->delete_queue.count ))) goto done; + for (op = queue->delete_queue.head; op; op = op->next) + { + build_filepathsW( op, &paths ); + op_result = handler( context, SPFILENOTIFY_STARTDELETE, (UINT_PTR)&paths, FILEOP_DELETE); + if (op_result == FILEOP_ABORT) goto done; + while (op_result == FILEOP_DOIT) + { + TRACE( "deleting file %s\n", debugstr_w(paths.Target) ); + if (DeleteFileW( paths.Target )) break; /* success */ + paths.Win32Error = GetLastError(); + op_result = handler( context, SPFILENOTIFY_DELETEERROR, (UINT_PTR)&paths, 0 ); + if (op_result == FILEOP_ABORT) goto done; + } + handler( context, SPFILENOTIFY_ENDDELETE, (UINT_PTR)&paths, 0 ); + } + handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_DELETE, 0 ); + } + + /* perform renames */ + + if (queue->rename_queue.count) + { + if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_RENAME, + queue->rename_queue.count ))) goto done; + for (op = queue->rename_queue.head; op; op = op->next) + { + build_filepathsW( op, &paths ); + op_result = handler( context, SPFILENOTIFY_STARTRENAME, (UINT_PTR)&paths, FILEOP_RENAME); + if (op_result == FILEOP_ABORT) goto done; + while (op_result == FILEOP_DOIT) + { + TRACE( "renaming file %s -> %s\n", + debugstr_w(paths.Source), debugstr_w(paths.Target) ); + if (MoveFileW( paths.Source, paths.Target )) break; /* success */ + paths.Win32Error = GetLastError(); + op_result = handler( context, SPFILENOTIFY_RENAMEERROR, (UINT_PTR)&paths, 0 ); + if (op_result == FILEOP_ABORT) goto done; + } + handler( context, SPFILENOTIFY_ENDRENAME, (UINT_PTR)&paths, 0 ); + } + handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_RENAME, 0 ); + } + + /* perform copies */ + + if (queue->copy_queue.count) + { + if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_COPY, + queue->copy_queue.count ))) goto done; + for (op = queue->copy_queue.head; op; op = op->next) + { + WCHAR newpath[MAX_PATH]; + + build_filepathsW( op, &paths ); + op_result = handler( context, SPFILENOTIFY_STARTCOPY, (UINT_PTR)&paths, FILEOP_COPY ); + if (op_result == FILEOP_ABORT) goto done; + if (op_result == FILEOP_NEWPATH) op_result = FILEOP_DOIT; + while (op_result == FILEOP_DOIT || op_result == FILEOP_NEWPATH) + { + TRACE( "copying file %s -> %s\n", + debugstr_w( op_result == FILEOP_NEWPATH ? newpath : paths.Source ), + debugstr_w(paths.Target) ); + if (op->dst_path) + { + if (!create_full_pathW( op->dst_path )) + { + paths.Win32Error = GetLastError(); + op_result = handler( context, SPFILENOTIFY_COPYERROR, + (UINT_PTR)&paths, (UINT_PTR)newpath ); + if (op_result == FILEOP_ABORT) goto done; + } + } + if (do_file_copyW( op_result == FILEOP_NEWPATH ? newpath : paths.Source, + paths.Target, op->style )) break; /* success */ + /* try to extract it from the cabinet file */ + if (op->src_tag) + { + if (extract_cabinet_file( op->src_tag, op->src_root, + paths.Source, paths.Target )) break; + } + paths.Win32Error = GetLastError(); + op_result = handler( context, SPFILENOTIFY_COPYERROR, + (UINT_PTR)&paths, (UINT_PTR)newpath ); + if (op_result == FILEOP_ABORT) goto done; + } + handler( context, SPFILENOTIFY_ENDCOPY, (UINT_PTR)&paths, 0 ); + } + handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_COPY, 0 ); + } + + + result = TRUE; + + done: + handler( context, SPFILENOTIFY_ENDQUEUE, result, 0 ); + HeapFree( GetProcessHeap(), 0, (void *)paths.Source ); + HeapFree( GetProcessHeap(), 0, (void *)paths.Target ); + return result; +} + + +/*********************************************************************** + * SetupScanFileQueueA (SETUPAPI.@) + */ +BOOL WINAPI SetupScanFileQueueA( HSPFILEQ queue, DWORD flags, HWND window, + PSP_FILE_CALLBACK_A callback, PVOID context, PDWORD result ) +{ + FIXME("stub\n"); + return FALSE; +} + + +/*********************************************************************** + * SetupScanFileQueueW (SETUPAPI.@) + */ +BOOL WINAPI SetupScanFileQueueW( HSPFILEQ queue, DWORD flags, HWND window, + PSP_FILE_CALLBACK_W callback, PVOID context, PDWORD result ) +{ + FIXME("stub\n"); + return FALSE; +} + + +/*********************************************************************** + * SetupGetFileQueueCount (SETUPAPI.@) + */ +BOOL WINAPI SetupGetFileQueueCount( HSPFILEQ handle, UINT op, PUINT result ) +{ + struct file_queue *queue = handle; + + switch(op) + { + case FILEOP_COPY: + *result = queue->copy_queue.count; + return TRUE; + case FILEOP_RENAME: + *result = queue->rename_queue.count; + return TRUE; + case FILEOP_DELETE: + *result = queue->delete_queue.count; + return TRUE; + } + return FALSE; +} + + +/*********************************************************************** + * SetupGetFileQueueFlags (SETUPAPI.@) + */ +BOOL WINAPI SetupGetFileQueueFlags( HSPFILEQ handle, PDWORD flags ) +{ + struct file_queue *queue = handle; + *flags = queue->flags; + return TRUE; +} + + +/*********************************************************************** + * SetupSetFileQueueFlags (SETUPAPI.@) + */ +BOOL WINAPI SetupSetFileQueueFlags( HSPFILEQ handle, DWORD mask, DWORD flags ) +{ + struct file_queue *queue = handle; + queue->flags = (queue->flags & ~mask) | flags; + return TRUE; +} + + +/*********************************************************************** + * SetupInitDefaultQueueCallback (SETUPAPI.@) + */ +PVOID WINAPI SetupInitDefaultQueueCallback( HWND owner ) +{ + return SetupInitDefaultQueueCallbackEx( owner, 0, 0, 0, NULL ); +} + + +/*********************************************************************** + * SetupInitDefaultQueueCallbackEx (SETUPAPI.@) + */ +PVOID WINAPI SetupInitDefaultQueueCallbackEx( HWND owner, HWND progress, UINT msg, + DWORD reserved1, PVOID reserved2 ) +{ + struct default_callback_context *context; + + if ((context = HeapAlloc( GetProcessHeap(), 0, sizeof(*context) ))) + { + context->owner = owner; + context->progress = progress; + context->message = msg; + } + return context; +} + + +/*********************************************************************** + * SetupTermDefaultQueueCallback (SETUPAPI.@) + */ +void WINAPI SetupTermDefaultQueueCallback( PVOID context ) +{ + HeapFree( GetProcessHeap(), 0, context ); +} + + +/*********************************************************************** + * SetupDefaultQueueCallbackA (SETUPAPI.@) + */ +UINT WINAPI SetupDefaultQueueCallbackA( PVOID context, UINT notification, + UINT_PTR param1, UINT_PTR param2 ) +{ + FILEPATHS_A *paths = (FILEPATHS_A *)param1; + + switch(notification) + { + case SPFILENOTIFY_STARTQUEUE: + TRACE( "start queue\n" ); + return TRUE; + case SPFILENOTIFY_ENDQUEUE: + TRACE( "end queue\n" ); + return 0; + case SPFILENOTIFY_STARTSUBQUEUE: + TRACE( "start subqueue %d count %d\n", param1, param2 ); + return TRUE; + case SPFILENOTIFY_ENDSUBQUEUE: + TRACE( "end subqueue %d\n", param1 ); + return 0; + case SPFILENOTIFY_STARTDELETE: + TRACE( "start delete %s\n", debugstr_a(paths->Target) ); + return FILEOP_DOIT; + case SPFILENOTIFY_ENDDELETE: + TRACE( "end delete %s\n", debugstr_a(paths->Target) ); + return 0; + case SPFILENOTIFY_DELETEERROR: + ERR( "delete error %d %s\n", paths->Win32Error, debugstr_a(paths->Target) ); + return FILEOP_SKIP; + case SPFILENOTIFY_STARTRENAME: + TRACE( "start rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return FILEOP_DOIT; + case SPFILENOTIFY_ENDRENAME: + TRACE( "end rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return 0; + case SPFILENOTIFY_RENAMEERROR: + ERR( "rename error %d %s -> %s\n", paths->Win32Error, + debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return FILEOP_SKIP; + case SPFILENOTIFY_STARTCOPY: + TRACE( "start copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return FILEOP_DOIT; + case SPFILENOTIFY_ENDCOPY: + TRACE( "end copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return 0; + case SPFILENOTIFY_COPYERROR: + ERR( "copy error %d %s -> %s\n", paths->Win32Error, + debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return FILEOP_SKIP; + case SPFILENOTIFY_NEEDMEDIA: + TRACE( "need media\n" ); + return FILEOP_SKIP; + default: + FIXME( "notification %d params %x,%x\n", notification, param1, param2 ); + break; + } + return 0; +} + + +/*********************************************************************** + * SetupDefaultQueueCallbackW (SETUPAPI.@) + */ +UINT WINAPI SetupDefaultQueueCallbackW( PVOID context, UINT notification, + UINT_PTR param1, UINT_PTR param2 ) +{ + FILEPATHS_W *paths = (FILEPATHS_W *)param1; + + switch(notification) + { + case SPFILENOTIFY_STARTQUEUE: + TRACE( "start queue\n" ); + return TRUE; + case SPFILENOTIFY_ENDQUEUE: + TRACE( "end queue\n" ); + return 0; + case SPFILENOTIFY_STARTSUBQUEUE: + TRACE( "start subqueue %d count %d\n", param1, param2 ); + return TRUE; + case SPFILENOTIFY_ENDSUBQUEUE: + TRACE( "end subqueue %d\n", param1 ); + return 0; + case SPFILENOTIFY_STARTDELETE: + TRACE( "start delete %s\n", debugstr_w(paths->Target) ); + return FILEOP_DOIT; + case SPFILENOTIFY_ENDDELETE: + TRACE( "end delete %s\n", debugstr_w(paths->Target) ); + return 0; + case SPFILENOTIFY_DELETEERROR: + ERR( "delete error %d %s\n", paths->Win32Error, debugstr_w(paths->Target) ); + return FILEOP_SKIP; + case SPFILENOTIFY_STARTRENAME: + TRACE( "start rename %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) ); + return FILEOP_DOIT; + case SPFILENOTIFY_ENDRENAME: + TRACE( "end rename %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) ); + return 0; + case SPFILENOTIFY_RENAMEERROR: + ERR( "rename error %d %s -> %s\n", paths->Win32Error, + debugstr_w(paths->Source), debugstr_w(paths->Target) ); + return FILEOP_SKIP; + case SPFILENOTIFY_STARTCOPY: + TRACE( "start copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) ); + return FILEOP_DOIT; + case SPFILENOTIFY_ENDCOPY: + TRACE( "end copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) ); + return 0; + case SPFILENOTIFY_COPYERROR: + ERR( "copy error %d %s -> %s\n", paths->Win32Error, + debugstr_w(paths->Source), debugstr_w(paths->Target) ); + return FILEOP_SKIP; + case SPFILENOTIFY_NEEDMEDIA: + TRACE( "need media\n" ); + return FILEOP_SKIP; + default: + FIXME( "notification %d params %x,%x\n", notification, param1, param2 ); + break; + } + return 0; +} diff --git a/reactos/lib/setupapi/rpc.c b/reactos/lib/setupapi/rpc.c new file mode 100644 index 00000000000..cb3b08bc7c0 --- /dev/null +++ b/reactos/lib/setupapi/rpc.c @@ -0,0 +1,82 @@ +/* rpc.c */ + +#include +#include +#include + + +static RPC_BINDING_HANDLE LocalBindingHandle = NULL; + + +RPC_STATUS +PnpBindRpc(LPWSTR pszMachine, + RPC_BINDING_HANDLE* BindingHandle) +{ + PWSTR pszStringBinding = NULL; + RPC_STATUS Status; + + Status = RpcStringBindingComposeW(NULL, + L"ncacn_np", + pszMachine, + L"\\pipe\\umpnpmgr", + NULL, + &pszStringBinding); + if (Status != RPC_S_OK) + return Status; + + Status = RpcBindingFromStringBindingW(pszStringBinding, + BindingHandle); + + RpcStringFreeW(&pszStringBinding); + + return Status; +} + + +RPC_STATUS +PnpUnbindRpc(RPC_BINDING_HANDLE *BindingHandle) +{ + if (BindingHandle != NULL) + { + RpcBindingFree(*BindingHandle); + *BindingHandle = NULL; + } + + return RPC_S_OK; +} + + +RPC_STATUS +PnpGetLocalBindingHandle(RPC_BINDING_HANDLE *BindingHandle) +{ + if (LocalBindingHandle != NULL) + { + BindingHandle = LocalBindingHandle; + return RPC_S_OK; + } + + return PnpBindRpc(NULL, BindingHandle); +} + + +RPC_STATUS +PnpUnbindLocalBindingHandle(VOID) +{ + return PnpUnbindRpc(&LocalBindingHandle); +} + + +void __RPC_FAR * __RPC_USER +midl_user_allocate(size_t len) +{ + return GlobalAlloc(GPTR, len); +} + + +void __RPC_USER +midl_user_free(void __RPC_FAR * ptr) +{ + GlobalFree(ptr); +} + +/* EOF */ diff --git a/reactos/lib/setupapi/setupapi.rc b/reactos/lib/setupapi/setupapi.rc index ed5d6428e26..9cc3d89ddff 100644 --- a/reactos/lib/setupapi/setupapi.rc +++ b/reactos/lib/setupapi/setupapi.rc @@ -1,36 +1,37 @@ -/* - * Top level resource file for SETUPX - * - * Copyright 2001 Andreas Mohr - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "windef.h" -#include "winbase.h" -#include "winuser.h" -#include "winnls.h" -#include "setupapi_private.h" - -#include "Cs.rc" -#include "De.rc" -#include "En.rc" -#include "Es.rc" -#include "Fr.rc" -#include "It.rc" -#include "Ja.rc" -#include "Nl.rc" -#include "Pt.rc" -#include "Ru.rc" +/* + * Top level resource file for SETUPX + * + * Copyright 2001 Andreas Mohr + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi_private.h" + +#include "Cs.rc" +#include "Da.rc" +#include "De.rc" +#include "En.rc" +#include "Es.rc" +#include "Fr.rc" +#include "It.rc" +#include "Ja.rc" +#include "Nl.rc" +#include "Pt.rc" +#include "Ru.rc" diff --git a/reactos/lib/setupapi/setupapi.spec b/reactos/lib/setupapi/setupapi.spec index 262f4573ddd..277bea8a94a 100644 --- a/reactos/lib/setupapi/setupapi.spec +++ b/reactos/lib/setupapi/setupapi.spec @@ -1,552 +1,552 @@ -@ stub AcquireSCMLock -@ stub AddMiniIconToList -@ stub AddTagToGroupOrderListEntry -@ stub AppendStringToMultiSz -@ stub AssertFail -@ stub CMP_Init_Detection -@ stub CMP_RegisterNotification -@ stub CMP_Report_LogOn -@ stub CMP_UnregisterNotification -@ stub CMP_WaitNoPendingInstallEvents -@ stub CMP_WaitServices -@ stub CM_Add_Empty_Log_Conf -@ stub CM_Add_Empty_Log_Conf_Ex -@ stub CM_Add_IDA -@ stub CM_Add_IDW -@ stub CM_Add_ID_ExA -@ stub CM_Add_ID_ExW -@ stub CM_Add_Range -@ stub CM_Add_Res_Des -@ stub CM_Add_Res_Des_Ex -@ stdcall CM_Connect_MachineA(str ptr) -@ stdcall CM_Connect_MachineW(wstr ptr) -@ stub CM_Create_DevNodeA -@ stub CM_Create_DevNodeW -@ stub CM_Create_DevNode_ExA -@ stub CM_Create_DevNode_ExW -@ stub CM_Create_Range_List -@ stub CM_Delete_Class_Key -@ stub CM_Delete_Class_Key_Ex -@ stub CM_Delete_DevNode_Key -@ stub CM_Delete_DevNode_Key_Ex -@ stub CM_Delete_Range -@ stub CM_Detect_Resource_Conflict -@ stub CM_Detect_Resource_Conflict_Ex -@ stub CM_Disable_DevNode -@ stub CM_Disable_DevNode_Ex -@ stdcall CM_Disconnect_Machine(long) -@ stub CM_Dup_Range_List -@ stub CM_Enable_DevNode -@ stub CM_Enable_DevNode_Ex -@ stub CM_Enumerate_Classes -@ stub CM_Enumerate_Classes_Ex -@ stub CM_Enumerate_EnumeratorsA -@ stub CM_Enumerate_EnumeratorsW -@ stub CM_Enumerate_Enumerators_ExA -@ stub CM_Enumerate_Enumerators_ExW -@ stub CM_Find_Range -@ stub CM_First_Range -@ stub CM_Free_Log_Conf -@ stub CM_Free_Log_Conf_Ex -@ stub CM_Free_Log_Conf_Handle -@ stub CM_Free_Range_List -@ stub CM_Free_Res_Des -@ stub CM_Free_Res_Des_Ex -@ stub CM_Free_Res_Des_Handle -@ stub CM_Get_Child -@ stub CM_Get_Child_Ex -@ stub CM_Get_Class_Key_NameA -@ stub CM_Get_Class_Key_NameW -@ stub CM_Get_Class_Key_Name_ExA -@ stub CM_Get_Class_Key_Name_ExW -@ stub CM_Get_Class_NameA -@ stub CM_Get_Class_NameW -@ stub CM_Get_Class_Name_ExA -@ stub CM_Get_Class_Name_ExW -@ stub CM_Get_Depth -@ stub CM_Get_Depth_Ex -@ stub CM_Get_DevNode_Registry_PropertyA -@ stub CM_Get_DevNode_Registry_PropertyW -@ stub CM_Get_DevNode_Registry_Property_ExA -@ stub CM_Get_DevNode_Registry_Property_ExW -@ stub CM_Get_DevNode_Status -@ stub CM_Get_DevNode_Status_Ex -@ stub CM_Get_Device_IDA -@ stub CM_Get_Device_IDW -@ stub CM_Get_Device_ID_ExA -@ stub CM_Get_Device_ID_ExW -@ stdcall CM_Get_Device_ID_ListA(str str long long) -@ stdcall CM_Get_Device_ID_ListW(wstr wstr long long) -@ stdcall CM_Get_Device_ID_List_ExA(str str long long long) -@ stdcall CM_Get_Device_ID_List_ExW(wstr wstr long long long) -@ stdcall CM_Get_Device_ID_List_SizeA(ptr str long) -@ stdcall CM_Get_Device_ID_List_SizeW(ptr wstr long) -@ stdcall CM_Get_Device_ID_List_Size_ExA(ptr str long long) -@ stdcall CM_Get_Device_ID_List_Size_ExW(ptr wstr long long) -@ stub CM_Get_Device_ID_Size -@ stub CM_Get_Device_ID_Size_Ex -@ stub CM_Get_Device_Interface_AliasA -@ stub CM_Get_Device_Interface_AliasW -@ stub CM_Get_Device_Interface_Alias_ExA -@ stub CM_Get_Device_Interface_Alias_ExW -@ stub CM_Get_Device_Interface_ListA -@ stub CM_Get_Device_Interface_ListW -@ stub CM_Get_Device_Interface_List_ExA -@ stub CM_Get_Device_Interface_List_ExW -@ stub CM_Get_Device_Interface_List_SizeA -@ stub CM_Get_Device_Interface_List_SizeW -@ stub CM_Get_Device_Interface_List_Size_ExA -@ stub CM_Get_Device_Interface_List_Size_ExW -@ stub CM_Get_First_Log_Conf -@ stub CM_Get_First_Log_Conf_Ex -@ stub CM_Get_Global_State -@ stub CM_Get_Global_State_Ex -@ stub CM_Get_HW_Prof_FlagsA -@ stub CM_Get_HW_Prof_FlagsW -@ stub CM_Get_HW_Prof_Flags_ExA -@ stub CM_Get_HW_Prof_Flags_ExW -@ stub CM_Get_Hardware_Profile_InfoA -@ stub CM_Get_Hardware_Profile_InfoW -@ stub CM_Get_Hardware_Profile_Info_ExA -@ stub CM_Get_Hardware_Profile_Info_ExW -@ stub CM_Get_Log_Conf_Priority -@ stub CM_Get_Log_Conf_Priority_Ex -@ stub CM_Get_Next_Log_Conf -@ stub CM_Get_Next_Log_Conf_Ex -@ stub CM_Get_Next_Res_Des -@ stub CM_Get_Next_Res_Des_Ex -@ stub CM_Get_Parent -@ stub CM_Get_Parent_Ex -@ stub CM_Get_Res_Des_Data -@ stub CM_Get_Res_Des_Data_Ex -@ stub CM_Get_Res_Des_Data_Size -@ stub CM_Get_Res_Des_Data_Size_Ex -@ stub CM_Get_Sibling -@ stub CM_Get_Sibling_Ex -@ stub CM_Get_Version -@ stub CM_Get_Version_Ex -@ stub CM_Intersect_Range_List -@ stub CM_Invert_Range_List -@ stub CM_Is_Dock_Station_Present -@ stub CM_Locate_DevNodeA -@ stub CM_Locate_DevNodeW -@ stub CM_Locate_DevNode_ExA -@ stub CM_Locate_DevNode_ExW -@ stub CM_Merge_Range_List -@ stub CM_Modify_Res_Des -@ stub CM_Modify_Res_Des_Ex -@ stub CM_Move_DevNode -@ stub CM_Move_DevNode_Ex -@ stub CM_Next_Range -@ stub CM_Open_Class_KeyA -@ stub CM_Open_Class_KeyW -@ stub CM_Open_Class_Key_ExA -@ stub CM_Open_Class_Key_ExW -@ stub CM_Open_DevNode_Key -@ stub CM_Open_DevNode_Key_Ex -@ stub CM_Query_Arbitrator_Free_Data -@ stub CM_Query_Arbitrator_Free_Data_Ex -@ stub CM_Query_Arbitrator_Free_Size -@ stub CM_Query_Arbitrator_Free_Size_Ex -@ stub CM_Query_Remove_SubTree -@ stub CM_Query_Remove_SubTree_Ex -@ stub CM_Reenumerate_DevNode -@ stub CM_Reenumerate_DevNode_Ex -@ stub CM_Register_Device_Driver -@ stub CM_Register_Device_Driver_Ex -@ stub CM_Register_Device_InterfaceA -@ stub CM_Register_Device_InterfaceW -@ stub CM_Register_Device_Interface_ExA -@ stub CM_Register_Device_Interface_ExW -@ stub CM_Remove_SubTree -@ stub CM_Remove_SubTree_Ex -@ stub CM_Remove_Unmarked_Children -@ stub CM_Remove_Unmarked_Children_Ex -@ stub CM_Request_Device_EjectA -@ stub CM_Request_Device_EjectW -@ stub CM_Request_Eject_PC -@ stub CM_Reset_Children_Marks -@ stub CM_Reset_Children_Marks_Ex -@ stub CM_Run_Detection -@ stub CM_Run_Detection_Ex -@ stub CM_Set_DevNode_Problem -@ stub CM_Set_DevNode_Problem_Ex -@ stub CM_Set_DevNode_Registry_PropertyA -@ stub CM_Set_DevNode_Registry_PropertyW -@ stub CM_Set_DevNode_Registry_Property_ExA -@ stub CM_Set_DevNode_Registry_Property_ExW -@ stub CM_Set_HW_Prof -@ stub CM_Set_HW_Prof_Ex -@ stub CM_Set_HW_Prof_FlagsA -@ stub CM_Set_HW_Prof_FlagsW -@ stub CM_Set_HW_Prof_Flags_ExA -@ stub CM_Set_HW_Prof_Flags_ExW -@ stub CM_Setup_DevNode -@ stub CM_Setup_DevNode_Ex -@ stub CM_Test_Range_Available -@ stub CM_Uninstall_DevNode -@ stub CM_Uninstall_DevNode_Ex -@ stub CM_Unregister_Device_InterfaceA -@ stub CM_Unregister_Device_InterfaceW -@ stub CM_Unregister_Device_Interface_ExA -@ stub CM_Unregister_Device_Interface_ExW -@ stdcall CaptureAndConvertAnsiArg(str ptr) -@ stdcall CaptureStringArg(wstr ptr) -@ stub CenterWindowRelativeToParent -@ stub ConcatenatePaths -@ stdcall DelayedMove(wstr wstr) -@ stub DelimStringToMultiSz -@ stub DestroyTextFileReadBuffer -@ stdcall DoesUserHavePrivilege(wstr) -@ stdcall DuplicateString(wstr) -@ stdcall EnablePrivilege(wstr long) -@ stub ExtensionPropSheetPageProc -@ stdcall FileExists(wstr ptr) -@ stub FreeStringArray -@ stub GetCurrentDriverSigningPolicy -@ stub GetNewInfName -@ stub GetSetFileTimestamp -@ stub GetVersionInfoFromImage -@ stub InfIsFromOemLocation -@ stub InstallCatalog -@ stdcall InstallHinfSection(long long str long) InstallHinfSectionA -@ stdcall InstallHinfSectionA(long long str long) -@ stdcall InstallHinfSectionW(long long wstr long) -@ stub InstallStop -@ stub InstallStopEx -@ stdcall IsUserAdmin() -@ stub LookUpStringInTable -@ stub MemoryInitialize -@ stdcall MultiByteToUnicode(str long) -@ stub MultiSzFromSearchControl -@ stdcall MyFree(ptr) -@ stub MyGetFileTitle -@ stdcall MyMalloc(long) -@ stdcall MyRealloc(ptr long) -@ stub OpenAndMapFileForRead -@ stub OutOfMemory -@ stub QueryMultiSzValueToArray -@ stdcall QueryRegistryValue(long wstr ptr ptr ptr) -@ stub ReadAsciiOrUnicodeTextFile -@ stub RegistryDelnode -@ stub RetreiveFileSecurity -@ stub RetrieveServiceConfig -@ stub SearchForInfFile -@ stub SetArrayToMultiSzValue -@ stdcall SetupAddInstallSectionToDiskSpaceListA(long long long str ptr long) -@ stub SetupAddInstallSectionToDiskSpaceListW -@ stub SetupAddSectionToDiskSpaceListA -@ stub SetupAddSectionToDiskSpaceListW -@ stub SetupAddToDiskSpaceListA -@ stub SetupAddToDiskSpaceListW -@ stub SetupAddToSourceListA -@ stub SetupAddToSourceListW -@ stub SetupAdjustDiskSpaceListA -@ stub SetupAdjustDiskSpaceListW -@ stub SetupCancelTemporarySourceList -@ stdcall SetupCloseFileQueue(ptr) -@ stdcall SetupCloseInfFile(long) -@ stub SetupCloseLog -@ stdcall SetupCommitFileQueue(long long ptr ptr) SetupCommitFileQueueW -@ stdcall SetupCommitFileQueueA(long long ptr ptr) -@ stdcall SetupCommitFileQueueW(long long ptr ptr) -@ stub SetupCopyErrorA -@ stub SetupCopyErrorW -@ stdcall SetupCopyOEMInfA(str str long long ptr long ptr ptr) -@ stub SetupCopyOEMInfW -@ stdcall SetupCreateDiskSpaceListA(ptr long long) -@ stdcall SetupCreateDiskSpaceListW(ptr long long) -@ stub SetupDecompressOrCopyFileA -@ stub SetupDecompressOrCopyFileW -@ stub SetupDefaultQueueCallback -@ stdcall SetupDefaultQueueCallbackA(ptr long long long) -@ stdcall SetupDefaultQueueCallbackW(ptr long long long) -@ stub SetupDeleteErrorA -@ stub SetupDeleteErrorW -@ stdcall SetupDestroyDiskSpaceList(long) -@ stub SetupDiAskForOEMDisk -@ stdcall SetupDiBuildClassInfoList(long ptr long ptr) -@ stdcall SetupDiBuildClassInfoListExA(long ptr long ptr str ptr) -@ stdcall SetupDiBuildClassInfoListExW(long ptr long ptr wstr ptr) -@ stub SetupDiBuildDriverInfoList -@ stdcall SetupDiCallClassInstaller(long ptr ptr) -@ stub SetupDiCancelDriverInfoSearch -@ stub SetupDiChangeState -@ stdcall SetupDiClassGuidsFromNameA(str ptr long ptr) -@ stdcall SetupDiClassGuidsFromNameExA(str ptr long ptr str ptr) -@ stdcall SetupDiClassGuidsFromNameExW(wstr ptr long ptr wstr ptr) -@ stdcall SetupDiClassGuidsFromNameW(wstr ptr long ptr) -@ stdcall SetupDiClassNameFromGuidA(ptr str long ptr) -@ stdcall SetupDiClassNameFromGuidExA(ptr str long ptr wstr ptr) -@ stdcall SetupDiClassNameFromGuidExW(ptr wstr long ptr wstr ptr) -@ stdcall SetupDiClassNameFromGuidW(ptr wstr long ptr) -@ stub SetupDiCreateDevRegKeyA -@ stub SetupDiCreateDevRegKeyW -@ stub SetupDiCreateDeviceInfoA -@ stdcall SetupDiCreateDeviceInfoList(ptr ptr) -@ stdcall SetupDiCreateDeviceInfoListExA(ptr long str ptr) -@ stdcall SetupDiCreateDeviceInfoListExW(ptr long str ptr) -@ stub SetupDiCreateDeviceInfoW -@ stub SetupDiDeleteDevRegKey -@ stub SetupDiDeleteDeviceInfo -@ stub SetupDiDeleteDeviceInterfaceData -@ stub SetupDiDeleteDeviceRegKey -@ stub SetupDiDestroyClassImageList -@ stdcall SetupDiDestroyDeviceInfoList(long) -@ stub SetupDiDestroyDriverInfoList -@ stub SetupDiDrawMiniIcon -@ stdcall SetupDiEnumDeviceInfo(long long ptr) -@ stdcall SetupDiEnumDeviceInterfaces(long ptr ptr long ptr) -@ stub SetupDiEnumDriverInfoA -@ stub SetupDiEnumDriverInfoW -@ stdcall SetupDiGetActualSectionToInstallA(long str str long ptr ptr) -@ stdcall SetupDiGetActualSectionToInstallW(long wstr wstr long ptr ptr) -@ stub SetupDiGetClassBitmapIndex -@ stdcall SetupDiGetClassDescriptionA(ptr str long ptr) -@ stdcall SetupDiGetClassDescriptionExA(ptr str long ptr str ptr) -@ stdcall SetupDiGetClassDescriptionExW(ptr wstr long ptr wstr ptr) -@ stdcall SetupDiGetClassDescriptionW(ptr wstr long ptr) -@ stub SetupDiGetClassDevPropertySheetsA -@ stub SetupDiGetClassDevPropertySheetsW -@ stdcall SetupDiGetClassDevsA(ptr ptr long long) -@ stdcall SetupDiGetClassDevsExA(ptr str ptr long ptr str ptr) -@ stdcall SetupDiGetClassDevsExW(ptr wstr ptr long ptr wstr ptr) -@ stdcall SetupDiGetClassDevsW(ptr ptr long long) -@ stub SetupDiGetClassImageIndex -@ stub SetupDiGetClassImageList -@ stub SetupDiGetClassImageListExA -@ stub SetupDiGetClassImageListExW -@ stub SetupDiGetClassInstallParamsA -@ stub SetupDiGetClassInstallParamsW -@ stub SetupDiGetDeviceInfoListClass -@ stdcall SetupDiGetDeviceInfoListDetailA(ptr ptr) -@ stdcall SetupDiGetDeviceInfoListDetailW(ptr ptr) -@ stdcall SetupDiGetDeviceInstallParamsA(ptr ptr ptr) -@ stub SetupDiGetDeviceInstallParamsW -@ stub SetupDiGetDeviceInstanceIdA -@ stub SetupDiGetDeviceInstanceIdW -@ stdcall SetupDiGetDeviceRegistryPropertyA(long ptr long ptr ptr long ptr) -@ stub SetupDiGetDeviceRegistryPropertyW -@ stub SetupDiGetDriverInfoDetailA -@ stub SetupDiGetDriverInfoDetailW -@ stub SetupDiGetDriverInstallParamsA -@ stub SetupDiGetDriverInstallParamsW -@ stub SetupDiGetDeviceInterfaceAlias -@ stdcall SetupDiGetDeviceInterfaceDetailA(long ptr ptr long ptr ptr) -@ stdcall SetupDiGetDeviceInterfaceDetailW(long ptr ptr long ptr ptr) -@ stub SetupDiGetHwProfileFriendlyNameA -@ stub SetupDiGetHwProfileFriendlyNameExA -@ stub SetupDiGetHwProfileFriendlyNameExW -@ stub SetupDiGetHwProfileFriendlyNameW -@ stub SetupDiGetHwProfileList -@ stub SetupDiGetHwProfileListExA -@ stub SetupDiGetHwProfileListExW -@ stub SetupDiGetINFClassA -@ stub SetupDiGetINFClassW -@ stub SetupDiGetSelectedDevice -@ stub SetupDiGetSelectedDriverA -@ stub SetupDiGetSelectedDriverW -@ stub SetupDiGetWizardPage -@ stdcall SetupDiInstallClassA(long str long ptr) -@ stub SetupDiInstallClassExA -@ stub SetupDiInstallClassExW -@ stdcall SetupDiInstallClassW(long wstr long ptr) -@ stub SetupDiInstallDevice -@ stub SetupDiInstallDriverFiles -@ stub SetupDiLoadClassIcon -@ stub SetupDiMoveDuplicateDevice -@ stdcall SetupDiOpenClassRegKey(ptr long) -@ stdcall SetupDiOpenClassRegKeyExA(ptr long long str ptr) -@ stdcall SetupDiOpenClassRegKeyExW(ptr long long wstr ptr) -@ stdcall SetupDiOpenDevRegKey(ptr ptr long long long long) -@ stub SetupDiOpenDeviceInfoA -@ stub SetupDiOpenDeviceInfoW -@ stdcall SetupDiOpenDeviceInterfaceA(ptr str long ptr) -@ stub SetupDiOpenDeviceInterfaceRegKey -@ stdcall SetupDiOpenDeviceInterfaceW(ptr wstr long ptr) -@ stub SetupDiRegisterDeviceInfo -@ stub SetupDiRemoveDevice -@ stub SetupDiRemoveDeviceInterface -@ stub SetupDiSelectDevice -@ stub SetupDiSelectOEMDrv -@ stdcall SetupDiSetClassInstallParamsA(ptr ptr ptr long) -@ stub SetupDiSetClassInstallParamsW -@ stub SetupDiSetDeviceInstallParamsA -@ stub SetupDiSetDeviceInstallParamsW -@ stub SetupDiSetDeviceRegistryPropertyA -@ stub SetupDiSetDeviceRegistryPropertyW -@ stub SetupDiSetDriverInstallParamsA -@ stub SetupDiSetDriverInstallParamsW -@ stub SetupDiSetSelectedDevice -@ stub SetupDiSetSelectedDriverA -@ stub SetupDiSetSelectedDriverW -@ stub SetupDiUnremoveDevice -@ stub SetupDuplicateDiskSpaceListA -@ stub SetupDuplicateDiskSpaceListW -@ stdcall SetupFindFirstLineA(long str str ptr) -@ stdcall SetupFindFirstLineW(long wstr wstr ptr) -@ stdcall SetupFindNextLine(ptr ptr) -@ stdcall SetupFindNextMatchLineA(ptr str ptr) -@ stdcall SetupFindNextMatchLineW(ptr wstr ptr) -@ stub SetupFreeSourceListA -@ stub SetupFreeSourceListW -@ stub SetupGetBackupInformationA -@ stub SetupGetBackupInformationW -@ stdcall SetupGetBinaryField(ptr long ptr long ptr) -@ stdcall SetupGetFieldCount(ptr) -@ stub SetupGetFileCompressionInfoA -@ stub SetupGetFileCompressionInfoW -@ stdcall SetupGetFileQueueCount(long long ptr) -@ stdcall SetupGetFileQueueFlags(long ptr) -@ stub SetupGetInfFileListA -@ stub SetupGetInfFileListW -@ stdcall SetupGetInfInformationA(ptr long ptr long ptr) -@ stub SetupGetInfInformationW -@ stub SetupGetInfSections -@ stdcall SetupGetIntField(ptr long ptr) -@ stdcall SetupGetLineByIndexA(long str long ptr) -@ stdcall SetupGetLineByIndexW(long wstr long ptr) -@ stdcall SetupGetLineCountA(long str) -@ stdcall SetupGetLineCountW(long wstr) -@ stdcall SetupGetLineTextA(ptr long str str ptr long ptr) -@ stdcall SetupGetLineTextW(ptr long wstr wstr ptr long ptr) -@ stdcall SetupGetMultiSzFieldA(ptr long ptr long ptr) -@ stdcall SetupGetMultiSzFieldW(ptr long ptr long ptr) -@ stub SetupGetSourceFileLocationA -@ stub SetupGetSourceFileLocationW -@ stub SetupGetSourceFileSizeA -@ stub SetupGetSourceFileSizeW -@ stub SetupGetSourceInfoA -@ stub SetupGetSourceInfoW -@ stdcall SetupGetStringFieldA(ptr long ptr long ptr) -@ stdcall SetupGetStringFieldW(ptr long ptr long ptr) -@ stub SetupGetTargetPathA -@ stub SetupGetTargetPathW -@ stdcall SetupInitDefaultQueueCallback(long) -@ stdcall SetupInitDefaultQueueCallbackEx(long long long long ptr) -@ stdcall SetupInitializeFileLogA (str long) -@ stdcall SetupInitializeFileLogW (wstr long) -@ stub SetupInstallFileA -@ stub SetupInstallFileExA -@ stub SetupInstallFileExW -@ stub SetupInstallFileW -@ stdcall SetupInstallFilesFromInfSectionA(long long long str str long) -@ stdcall SetupInstallFilesFromInfSectionW(long long long wstr wstr long) -@ stdcall SetupInstallFromInfSectionA(long long str long long str long ptr ptr long ptr) -@ stdcall SetupInstallFromInfSectionW(long long wstr long long wstr long ptr ptr long ptr) -@ stub SetupInstallServicesFromInfSectionA -@ stub SetupInstallServicesFromInfSectionExA -@ stub SetupInstallServicesFromInfSectionExW -@ stub SetupInstallServicesFromInfSectionW -@ stdcall SetupIterateCabinetA(str long ptr ptr) -@ stdcall SetupIterateCabinetW(wstr long ptr ptr) -@ stub SetupLogErrorA -@ stub SetupLogErrorW -@ stub SetupLogFileA -@ stub SetupLogFileW -@ stdcall SetupOpenAppendInfFileA(str long ptr) -@ stdcall SetupOpenAppendInfFileW(wstr long ptr) -@ stdcall SetupOpenFileQueue() -@ stdcall SetupOpenInfFileA(str str long ptr) -@ stdcall SetupOpenInfFileW(wstr wstr long ptr) -@ stdcall SetupOpenMasterInf() -@ stub SetupPromptForDiskA -@ stub SetupPromptForDiskW -@ stub SetupPromptReboot -@ stub SetupQueryDrivesInDiskSpaceListA -@ stub SetupQueryDrivesInDiskSpaceListW -@ stub SetupQueryFileLogA -@ stub SetupQueryFileLogW -@ stub SetupQueryInfFileInformationA -@ stub SetupQueryInfFileInformationW -@ stub SetupQueryInfOriginalFileInformationA -@ stub SetupQueryInfOriginalFileInformationW -@ stub SetupQueryInfVersionInformationA -@ stub SetupQueryInfVersionInformationW -@ stub SetupQuerySourceListA -@ stub SetupQuerySourceListW -@ stdcall SetupQuerySpaceRequiredOnDriveA(long str ptr ptr long) -@ stub SetupQuerySpaceRequiredOnDriveW -@ stdcall SetupQueueCopyA(long str str str str str str str long) -@ stdcall SetupQueueCopyIndirectA(ptr) -@ stdcall SetupQueueCopyIndirectW(ptr) -@ stdcall SetupQueueCopySectionA(long str long long str long) -@ stdcall SetupQueueCopySectionW(long wstr long long wstr long) -@ stdcall SetupQueueCopyW(long wstr wstr wstr wstr wstr wstr wstr long) -@ stdcall SetupQueueDefaultCopyA(long long str str str long) -@ stdcall SetupQueueDefaultCopyW(long long wstr wstr wstr long) -@ stdcall SetupQueueDeleteA(long str str) -@ stdcall SetupQueueDeleteSectionA(long long long str) -@ stdcall SetupQueueDeleteSectionW(long long long wstr) -@ stdcall SetupQueueDeleteW(long wstr wstr) -@ stdcall SetupQueueRenameA(long str str str str) -@ stdcall SetupQueueRenameSectionA(long long long str) -@ stdcall SetupQueueRenameSectionW(long long long wstr) -@ stdcall SetupQueueRenameW(long wstr wstr wstr wstr) -@ stub SetupRemoveFileLogEntryA -@ stub SetupRemoveFileLogEntryW -@ stub SetupRemoveFromDiskSpaceListA -@ stub SetupRemoveFromDiskSpaceListW -@ stub SetupRemoveFromSourceListA -@ stub SetupRemoveFromSourceListW -@ stub SetupRemoveInstallSectionFromDiskSpaceListA -@ stub SetupRemoveInstallSectionFromDiskSpaceListW -@ stub SetupRemoveSectionFromDiskSpaceListA -@ stub SetupRemoveSectionFromDiskSpaceListW -@ stub SetupRenameErrorA -@ stub SetupRenameErrorW -@ stub SetupScanFileQueue -@ stdcall SetupScanFileQueueA(long long long ptr ptr ptr) -@ stdcall SetupScanFileQueueW(long long long ptr ptr ptr) -@ stdcall SetupSetDirectoryIdA(long long str) -@ stub SetupSetDirectoryIdExA -@ stub SetupSetDirectoryIdExW -@ stdcall SetupSetDirectoryIdW(long long wstr) -@ stub SetupFileQueueAlternatePlatformA -@ stub SetupFileQueueAlternatePlatformW -@ stdcall SetupSetFileQueueFlags(long long long) -@ stub SetupSetPlatformPathOverrideA -@ stub SetupSetPlatformPathOverrideW -@ stub SetupSetSourceListA -@ stub SetupSetSourceListW -@ stdcall SetupTermDefaultQueueCallback(ptr) -@ stdcall SetupTerminateFileLog(long) -@ stub ShouldDeviceBeExcluded -@ stub StampFileSecurity -@ stub StringTableAddString -@ stub StringTableAddStringEx -@ stub StringTableDestroy -@ stub StringTableDuplicate -@ stub StringTableEnum -@ stub StringTableGetExtraData -@ stub StringTableInitialize -@ stub StringTableInitializeEx -@ stub StringTableLookUpString -@ stub StringTableLookUpStringEx -@ stub StringTableSetExtraData -@ stub StringTableStringFromId -@ stub StringTableTrim -@ stub TakeOwnershipOfFile -@ stdcall UnicodeToMultiByte(wstr long) -@ stub UnmapAndCloseFile -@ stub VerifyCatalogFile -@ stub VerifyFile -@ stub pSetupAccessRunOnceNodeList -@ stub pSetupAddMiniIconToList -@ stub pSetupAddTagToGroupOrderListEntry -@ stub pSetupAppendStringToMultiSz -@ stub pSetupDestroyRunOnceNodeList -@ stub pSetupDirectoryIdToPath -@ stub pSetupGetField -@ stub pSetupGetGlobalFlags -@ stub pSetupGetOsLoaderDriveAndPath -@ stub pSetupGetQueueFlags -@ stub pSetupGetVersionDatum -@ stub pSetupGuidFromString -@ stub pSetupIsGuidNull -@ stub pSetupMakeSurePathExists -@ stub pSetupSetGlobalFlags -@ stub pSetupSetQueueFlags -@ stub pSetupSetSystemSourceFlags -@ stub pSetupStringFromGuid -@ stub pSetupVerifyQueuedCatalogs +@ stub AcquireSCMLock +@ stub AddMiniIconToList +@ stub AddTagToGroupOrderListEntry +@ stub AppendStringToMultiSz +@ stub AssertFail +@ stub CMP_Init_Detection +@ stub CMP_RegisterNotification +@ stub CMP_Report_LogOn +@ stub CMP_UnregisterNotification +@ stub CMP_WaitNoPendingInstallEvents +@ stub CMP_WaitServices +@ stub CM_Add_Empty_Log_Conf +@ stub CM_Add_Empty_Log_Conf_Ex +@ stub CM_Add_IDA +@ stub CM_Add_IDW +@ stub CM_Add_ID_ExA +@ stub CM_Add_ID_ExW +@ stub CM_Add_Range +@ stub CM_Add_Res_Des +@ stub CM_Add_Res_Des_Ex +@ stdcall CM_Connect_MachineA(str ptr) +@ stdcall CM_Connect_MachineW(wstr ptr) +@ stub CM_Create_DevNodeA +@ stub CM_Create_DevNodeW +@ stub CM_Create_DevNode_ExA +@ stub CM_Create_DevNode_ExW +@ stub CM_Create_Range_List +@ stub CM_Delete_Class_Key +@ stub CM_Delete_Class_Key_Ex +@ stub CM_Delete_DevNode_Key +@ stub CM_Delete_DevNode_Key_Ex +@ stub CM_Delete_Range +@ stub CM_Detect_Resource_Conflict +@ stub CM_Detect_Resource_Conflict_Ex +@ stub CM_Disable_DevNode +@ stub CM_Disable_DevNode_Ex +@ stdcall CM_Disconnect_Machine(long) +@ stub CM_Dup_Range_List +@ stub CM_Enable_DevNode +@ stub CM_Enable_DevNode_Ex +@ stub CM_Enumerate_Classes +@ stub CM_Enumerate_Classes_Ex +@ stub CM_Enumerate_EnumeratorsA +@ stub CM_Enumerate_EnumeratorsW +@ stub CM_Enumerate_Enumerators_ExA +@ stub CM_Enumerate_Enumerators_ExW +@ stub CM_Find_Range +@ stub CM_First_Range +@ stub CM_Free_Log_Conf +@ stub CM_Free_Log_Conf_Ex +@ stub CM_Free_Log_Conf_Handle +@ stub CM_Free_Range_List +@ stub CM_Free_Res_Des +@ stub CM_Free_Res_Des_Ex +@ stub CM_Free_Res_Des_Handle +@ stdcall CM_Get_Child(ptr long long) +@ stdcall CM_Get_Child_Ex(ptr long long long) +@ stub CM_Get_Class_Key_NameA +@ stub CM_Get_Class_Key_NameW +@ stub CM_Get_Class_Key_Name_ExA +@ stub CM_Get_Class_Key_Name_ExW +@ stub CM_Get_Class_NameA +@ stub CM_Get_Class_NameW +@ stub CM_Get_Class_Name_ExA +@ stub CM_Get_Class_Name_ExW +@ stub CM_Get_Depth +@ stub CM_Get_Depth_Ex +@ stub CM_Get_DevNode_Registry_PropertyA +@ stub CM_Get_DevNode_Registry_PropertyW +@ stub CM_Get_DevNode_Registry_Property_ExA +@ stub CM_Get_DevNode_Registry_Property_ExW +@ stub CM_Get_DevNode_Status +@ stub CM_Get_DevNode_Status_Ex +@ stub CM_Get_Device_IDA +@ stub CM_Get_Device_IDW +@ stub CM_Get_Device_ID_ExA +@ stub CM_Get_Device_ID_ExW +@ stdcall CM_Get_Device_ID_ListA(str str long long) +@ stdcall CM_Get_Device_ID_ListW(wstr wstr long long) +@ stdcall CM_Get_Device_ID_List_ExA(str str long long long) +@ stdcall CM_Get_Device_ID_List_ExW(wstr wstr long long long) +@ stdcall CM_Get_Device_ID_List_SizeA(ptr str long) +@ stdcall CM_Get_Device_ID_List_SizeW(ptr wstr long) +@ stdcall CM_Get_Device_ID_List_Size_ExA(ptr str long long) +@ stdcall CM_Get_Device_ID_List_Size_ExW(ptr wstr long long) +@ stub CM_Get_Device_ID_Size +@ stub CM_Get_Device_ID_Size_Ex +@ stub CM_Get_Device_Interface_AliasA +@ stub CM_Get_Device_Interface_AliasW +@ stub CM_Get_Device_Interface_Alias_ExA +@ stub CM_Get_Device_Interface_Alias_ExW +@ stub CM_Get_Device_Interface_ListA +@ stub CM_Get_Device_Interface_ListW +@ stub CM_Get_Device_Interface_List_ExA +@ stub CM_Get_Device_Interface_List_ExW +@ stub CM_Get_Device_Interface_List_SizeA +@ stub CM_Get_Device_Interface_List_SizeW +@ stub CM_Get_Device_Interface_List_Size_ExA +@ stub CM_Get_Device_Interface_List_Size_ExW +@ stub CM_Get_First_Log_Conf +@ stub CM_Get_First_Log_Conf_Ex +@ stub CM_Get_Global_State +@ stub CM_Get_Global_State_Ex +@ stub CM_Get_HW_Prof_FlagsA +@ stub CM_Get_HW_Prof_FlagsW +@ stub CM_Get_HW_Prof_Flags_ExA +@ stub CM_Get_HW_Prof_Flags_ExW +@ stub CM_Get_Hardware_Profile_InfoA +@ stub CM_Get_Hardware_Profile_InfoW +@ stub CM_Get_Hardware_Profile_Info_ExA +@ stub CM_Get_Hardware_Profile_Info_ExW +@ stub CM_Get_Log_Conf_Priority +@ stub CM_Get_Log_Conf_Priority_Ex +@ stub CM_Get_Next_Log_Conf +@ stub CM_Get_Next_Log_Conf_Ex +@ stub CM_Get_Next_Res_Des +@ stub CM_Get_Next_Res_Des_Ex +@ stdcall CM_Get_Parent(ptr long long) +@ stdcall CM_Get_Parent_Ex(ptr long long long) +@ stub CM_Get_Res_Des_Data +@ stub CM_Get_Res_Des_Data_Ex +@ stub CM_Get_Res_Des_Data_Size +@ stub CM_Get_Res_Des_Data_Size_Ex +@ stdcall CM_Get_Sibling(ptr long long) +@ stdcall CM_Get_Sibling_Ex(ptr long long long) +@ stdcall CM_Get_Version() +@ stdcall CM_Get_Version_Ex(long) +@ stub CM_Intersect_Range_List +@ stub CM_Invert_Range_List +@ stub CM_Is_Dock_Station_Present +@ stdcall CM_Locate_DevNodeA(ptr str long) +@ stdcall CM_Locate_DevNodeW(ptr wstr long) +@ stdcall CM_Locate_DevNode_ExA(ptr str long long) +@ stdcall CM_Locate_DevNode_ExW(ptr wstr long long) +@ stub CM_Merge_Range_List +@ stub CM_Modify_Res_Des +@ stub CM_Modify_Res_Des_Ex +@ stub CM_Move_DevNode +@ stub CM_Move_DevNode_Ex +@ stub CM_Next_Range +@ stub CM_Open_Class_KeyA +@ stub CM_Open_Class_KeyW +@ stub CM_Open_Class_Key_ExA +@ stub CM_Open_Class_Key_ExW +@ stub CM_Open_DevNode_Key +@ stub CM_Open_DevNode_Key_Ex +@ stub CM_Query_Arbitrator_Free_Data +@ stub CM_Query_Arbitrator_Free_Data_Ex +@ stub CM_Query_Arbitrator_Free_Size +@ stub CM_Query_Arbitrator_Free_Size_Ex +@ stub CM_Query_Remove_SubTree +@ stub CM_Query_Remove_SubTree_Ex +@ stub CM_Reenumerate_DevNode +@ stub CM_Reenumerate_DevNode_Ex +@ stub CM_Register_Device_Driver +@ stub CM_Register_Device_Driver_Ex +@ stub CM_Register_Device_InterfaceA +@ stub CM_Register_Device_InterfaceW +@ stub CM_Register_Device_Interface_ExA +@ stub CM_Register_Device_Interface_ExW +@ stub CM_Remove_SubTree +@ stub CM_Remove_SubTree_Ex +@ stub CM_Remove_Unmarked_Children +@ stub CM_Remove_Unmarked_Children_Ex +@ stub CM_Request_Device_EjectA +@ stub CM_Request_Device_EjectW +@ stub CM_Request_Eject_PC +@ stub CM_Reset_Children_Marks +@ stub CM_Reset_Children_Marks_Ex +@ stub CM_Run_Detection +@ stub CM_Run_Detection_Ex +@ stub CM_Set_DevNode_Problem +@ stub CM_Set_DevNode_Problem_Ex +@ stub CM_Set_DevNode_Registry_PropertyA +@ stub CM_Set_DevNode_Registry_PropertyW +@ stub CM_Set_DevNode_Registry_Property_ExA +@ stub CM_Set_DevNode_Registry_Property_ExW +@ stub CM_Set_HW_Prof +@ stub CM_Set_HW_Prof_Ex +@ stub CM_Set_HW_Prof_FlagsA +@ stub CM_Set_HW_Prof_FlagsW +@ stub CM_Set_HW_Prof_Flags_ExA +@ stub CM_Set_HW_Prof_Flags_ExW +@ stub CM_Setup_DevNode +@ stub CM_Setup_DevNode_Ex +@ stub CM_Test_Range_Available +@ stub CM_Uninstall_DevNode +@ stub CM_Uninstall_DevNode_Ex +@ stub CM_Unregister_Device_InterfaceA +@ stub CM_Unregister_Device_InterfaceW +@ stub CM_Unregister_Device_Interface_ExA +@ stub CM_Unregister_Device_Interface_ExW +@ stdcall CaptureAndConvertAnsiArg(str ptr) +@ stdcall CaptureStringArg(wstr ptr) +@ stub CenterWindowRelativeToParent +@ stub ConcatenatePaths +@ stdcall DelayedMove(wstr wstr) +@ stub DelimStringToMultiSz +@ stub DestroyTextFileReadBuffer +@ stdcall DoesUserHavePrivilege(wstr) +@ stdcall DuplicateString(wstr) +@ stdcall EnablePrivilege(wstr long) +@ stub ExtensionPropSheetPageProc +@ stdcall FileExists(wstr ptr) +@ stub FreeStringArray +@ stub GetCurrentDriverSigningPolicy +@ stub GetNewInfName +@ stub GetSetFileTimestamp +@ stub GetVersionInfoFromImage +@ stub InfIsFromOemLocation +@ stub InstallCatalog +@ stdcall InstallHinfSection(long long str long) InstallHinfSectionA +@ stdcall InstallHinfSectionA(long long str long) +@ stdcall InstallHinfSectionW(long long wstr long) +@ stub InstallStop +@ stub InstallStopEx +@ stdcall IsUserAdmin() +@ stub LookUpStringInTable +@ stub MemoryInitialize +@ stdcall MultiByteToUnicode(str long) +@ stub MultiSzFromSearchControl +@ stdcall MyFree(ptr) +@ stub MyGetFileTitle +@ stdcall MyMalloc(long) +@ stdcall MyRealloc(ptr long) +@ stub OpenAndMapFileForRead +@ stub OutOfMemory +@ stub QueryMultiSzValueToArray +@ stdcall QueryRegistryValue(long wstr ptr ptr ptr) +@ stub ReadAsciiOrUnicodeTextFile +@ stub RegistryDelnode +@ stub RetreiveFileSecurity +@ stub RetrieveServiceConfig +@ stub SearchForInfFile +@ stub SetArrayToMultiSzValue +@ stdcall SetupAddInstallSectionToDiskSpaceListA(long long long str ptr long) +@ stub SetupAddInstallSectionToDiskSpaceListW +@ stub SetupAddSectionToDiskSpaceListA +@ stub SetupAddSectionToDiskSpaceListW +@ stub SetupAddToDiskSpaceListA +@ stub SetupAddToDiskSpaceListW +@ stub SetupAddToSourceListA +@ stub SetupAddToSourceListW +@ stub SetupAdjustDiskSpaceListA +@ stub SetupAdjustDiskSpaceListW +@ stub SetupCancelTemporarySourceList +@ stdcall SetupCloseFileQueue(ptr) +@ stdcall SetupCloseInfFile(long) +@ stub SetupCloseLog +@ stdcall SetupCommitFileQueue(long long ptr ptr) SetupCommitFileQueueW +@ stdcall SetupCommitFileQueueA(long long ptr ptr) +@ stdcall SetupCommitFileQueueW(long long ptr ptr) +@ stub SetupCopyErrorA +@ stub SetupCopyErrorW +@ stdcall SetupCopyOEMInfA(str str long long ptr long ptr ptr) +@ stub SetupCopyOEMInfW +@ stdcall SetupCreateDiskSpaceListA(ptr long long) +@ stdcall SetupCreateDiskSpaceListW(ptr long long) +@ stub SetupDecompressOrCopyFileA +@ stub SetupDecompressOrCopyFileW +@ stub SetupDefaultQueueCallback +@ stdcall SetupDefaultQueueCallbackA(ptr long long long) +@ stdcall SetupDefaultQueueCallbackW(ptr long long long) +@ stub SetupDeleteErrorA +@ stub SetupDeleteErrorW +@ stdcall SetupDestroyDiskSpaceList(long) +@ stub SetupDiAskForOEMDisk +@ stdcall SetupDiBuildClassInfoList(long ptr long ptr) +@ stdcall SetupDiBuildClassInfoListExA(long ptr long ptr str ptr) +@ stdcall SetupDiBuildClassInfoListExW(long ptr long ptr wstr ptr) +@ stub SetupDiBuildDriverInfoList +@ stdcall SetupDiCallClassInstaller(long ptr ptr) +@ stub SetupDiCancelDriverInfoSearch +@ stub SetupDiChangeState +@ stdcall SetupDiClassGuidsFromNameA(str ptr long ptr) +@ stdcall SetupDiClassGuidsFromNameExA(str ptr long ptr str ptr) +@ stdcall SetupDiClassGuidsFromNameExW(wstr ptr long ptr wstr ptr) +@ stdcall SetupDiClassGuidsFromNameW(wstr ptr long ptr) +@ stdcall SetupDiClassNameFromGuidA(ptr str long ptr) +@ stdcall SetupDiClassNameFromGuidExA(ptr str long ptr wstr ptr) +@ stdcall SetupDiClassNameFromGuidExW(ptr wstr long ptr wstr ptr) +@ stdcall SetupDiClassNameFromGuidW(ptr wstr long ptr) +@ stub SetupDiCreateDevRegKeyA +@ stub SetupDiCreateDevRegKeyW +@ stub SetupDiCreateDeviceInfoA +@ stdcall SetupDiCreateDeviceInfoList(ptr ptr) +@ stdcall SetupDiCreateDeviceInfoListExA(ptr long str ptr) +@ stdcall SetupDiCreateDeviceInfoListExW(ptr long str ptr) +@ stub SetupDiCreateDeviceInfoW +@ stub SetupDiDeleteDevRegKey +@ stub SetupDiDeleteDeviceInfo +@ stub SetupDiDeleteDeviceInterfaceData +@ stub SetupDiDeleteDeviceRegKey +@ stub SetupDiDestroyClassImageList +@ stdcall SetupDiDestroyDeviceInfoList(long) +@ stub SetupDiDestroyDriverInfoList +@ stub SetupDiDrawMiniIcon +@ stdcall SetupDiEnumDeviceInfo(long long ptr) +@ stdcall SetupDiEnumDeviceInterfaces(long ptr ptr long ptr) +@ stub SetupDiEnumDriverInfoA +@ stub SetupDiEnumDriverInfoW +@ stdcall SetupDiGetActualSectionToInstallA(long str str long ptr ptr) +@ stdcall SetupDiGetActualSectionToInstallW(long wstr wstr long ptr ptr) +@ stub SetupDiGetClassBitmapIndex +@ stdcall SetupDiGetClassDescriptionA(ptr str long ptr) +@ stdcall SetupDiGetClassDescriptionExA(ptr str long ptr str ptr) +@ stdcall SetupDiGetClassDescriptionExW(ptr wstr long ptr wstr ptr) +@ stdcall SetupDiGetClassDescriptionW(ptr wstr long ptr) +@ stub SetupDiGetClassDevPropertySheetsA +@ stub SetupDiGetClassDevPropertySheetsW +@ stdcall SetupDiGetClassDevsA(ptr ptr long long) +@ stdcall SetupDiGetClassDevsExA(ptr str ptr long ptr str ptr) +@ stdcall SetupDiGetClassDevsExW(ptr wstr ptr long ptr wstr ptr) +@ stdcall SetupDiGetClassDevsW(ptr ptr long long) +@ stub SetupDiGetClassImageIndex +@ stub SetupDiGetClassImageList +@ stub SetupDiGetClassImageListExA +@ stub SetupDiGetClassImageListExW +@ stub SetupDiGetClassInstallParamsA +@ stub SetupDiGetClassInstallParamsW +@ stub SetupDiGetDeviceInfoListClass +@ stdcall SetupDiGetDeviceInfoListDetailA(ptr ptr) +@ stdcall SetupDiGetDeviceInfoListDetailW(ptr ptr) +@ stdcall SetupDiGetDeviceInstallParamsA(ptr ptr ptr) +@ stub SetupDiGetDeviceInstallParamsW +@ stub SetupDiGetDeviceInstanceIdA +@ stub SetupDiGetDeviceInstanceIdW +@ stdcall SetupDiGetDeviceRegistryPropertyA(long ptr long ptr ptr long ptr) +@ stub SetupDiGetDeviceRegistryPropertyW +@ stub SetupDiGetDriverInfoDetailA +@ stub SetupDiGetDriverInfoDetailW +@ stub SetupDiGetDriverInstallParamsA +@ stub SetupDiGetDriverInstallParamsW +@ stub SetupDiGetDeviceInterfaceAlias +@ stdcall SetupDiGetDeviceInterfaceDetailA(long ptr ptr long ptr ptr) +@ stdcall SetupDiGetDeviceInterfaceDetailW(long ptr ptr long ptr ptr) +@ stub SetupDiGetHwProfileFriendlyNameA +@ stub SetupDiGetHwProfileFriendlyNameExA +@ stub SetupDiGetHwProfileFriendlyNameExW +@ stub SetupDiGetHwProfileFriendlyNameW +@ stub SetupDiGetHwProfileList +@ stub SetupDiGetHwProfileListExA +@ stub SetupDiGetHwProfileListExW +@ stub SetupDiGetINFClassA +@ stub SetupDiGetINFClassW +@ stub SetupDiGetSelectedDevice +@ stub SetupDiGetSelectedDriverA +@ stub SetupDiGetSelectedDriverW +@ stub SetupDiGetWizardPage +@ stdcall SetupDiInstallClassA(long str long ptr) +@ stub SetupDiInstallClassExA +@ stub SetupDiInstallClassExW +@ stdcall SetupDiInstallClassW(long wstr long ptr) +@ stub SetupDiInstallDevice +@ stub SetupDiInstallDriverFiles +@ stub SetupDiLoadClassIcon +@ stub SetupDiMoveDuplicateDevice +@ stdcall SetupDiOpenClassRegKey(ptr long) +@ stdcall SetupDiOpenClassRegKeyExA(ptr long long str ptr) +@ stdcall SetupDiOpenClassRegKeyExW(ptr long long wstr ptr) +@ stdcall SetupDiOpenDevRegKey(ptr ptr long long long long) +@ stub SetupDiOpenDeviceInfoA +@ stub SetupDiOpenDeviceInfoW +@ stdcall SetupDiOpenDeviceInterfaceA(ptr str long ptr) +@ stub SetupDiOpenDeviceInterfaceRegKey +@ stdcall SetupDiOpenDeviceInterfaceW(ptr wstr long ptr) +@ stub SetupDiRegisterDeviceInfo +@ stub SetupDiRemoveDevice +@ stub SetupDiRemoveDeviceInterface +@ stub SetupDiSelectDevice +@ stub SetupDiSelectOEMDrv +@ stdcall SetupDiSetClassInstallParamsA(ptr ptr ptr long) +@ stub SetupDiSetClassInstallParamsW +@ stub SetupDiSetDeviceInstallParamsA +@ stub SetupDiSetDeviceInstallParamsW +@ stub SetupDiSetDeviceRegistryPropertyA +@ stub SetupDiSetDeviceRegistryPropertyW +@ stub SetupDiSetDriverInstallParamsA +@ stub SetupDiSetDriverInstallParamsW +@ stub SetupDiSetSelectedDevice +@ stub SetupDiSetSelectedDriverA +@ stub SetupDiSetSelectedDriverW +@ stub SetupDiUnremoveDevice +@ stub SetupDuplicateDiskSpaceListA +@ stub SetupDuplicateDiskSpaceListW +@ stdcall SetupFindFirstLineA(long str str ptr) +@ stdcall SetupFindFirstLineW(long wstr wstr ptr) +@ stdcall SetupFindNextLine(ptr ptr) +@ stdcall SetupFindNextMatchLineA(ptr str ptr) +@ stdcall SetupFindNextMatchLineW(ptr wstr ptr) +@ stub SetupFreeSourceListA +@ stub SetupFreeSourceListW +@ stub SetupGetBackupInformationA +@ stub SetupGetBackupInformationW +@ stdcall SetupGetBinaryField(ptr long ptr long ptr) +@ stdcall SetupGetFieldCount(ptr) +@ stub SetupGetFileCompressionInfoA +@ stub SetupGetFileCompressionInfoW +@ stdcall SetupGetFileQueueCount(long long ptr) +@ stdcall SetupGetFileQueueFlags(long ptr) +@ stub SetupGetInfFileListA +@ stub SetupGetInfFileListW +@ stdcall SetupGetInfInformationA(ptr long ptr long ptr) +@ stub SetupGetInfInformationW +@ stub SetupGetInfSections +@ stdcall SetupGetIntField(ptr long ptr) +@ stdcall SetupGetLineByIndexA(long str long ptr) +@ stdcall SetupGetLineByIndexW(long wstr long ptr) +@ stdcall SetupGetLineCountA(long str) +@ stdcall SetupGetLineCountW(long wstr) +@ stdcall SetupGetLineTextA(ptr long str str ptr long ptr) +@ stdcall SetupGetLineTextW(ptr long wstr wstr ptr long ptr) +@ stdcall SetupGetMultiSzFieldA(ptr long ptr long ptr) +@ stdcall SetupGetMultiSzFieldW(ptr long ptr long ptr) +@ stub SetupGetSourceFileLocationA +@ stub SetupGetSourceFileLocationW +@ stub SetupGetSourceFileSizeA +@ stub SetupGetSourceFileSizeW +@ stub SetupGetSourceInfoA +@ stub SetupGetSourceInfoW +@ stdcall SetupGetStringFieldA(ptr long ptr long ptr) +@ stdcall SetupGetStringFieldW(ptr long ptr long ptr) +@ stub SetupGetTargetPathA +@ stub SetupGetTargetPathW +@ stdcall SetupInitDefaultQueueCallback(long) +@ stdcall SetupInitDefaultQueueCallbackEx(long long long long ptr) +@ stdcall SetupInitializeFileLogA (str long) +@ stdcall SetupInitializeFileLogW (wstr long) +@ stub SetupInstallFileA +@ stub SetupInstallFileExA +@ stub SetupInstallFileExW +@ stub SetupInstallFileW +@ stdcall SetupInstallFilesFromInfSectionA(long long long str str long) +@ stdcall SetupInstallFilesFromInfSectionW(long long long wstr wstr long) +@ stdcall SetupInstallFromInfSectionA(long long str long long str long ptr ptr long ptr) +@ stdcall SetupInstallFromInfSectionW(long long wstr long long wstr long ptr ptr long ptr) +@ stub SetupInstallServicesFromInfSectionA +@ stub SetupInstallServicesFromInfSectionExA +@ stub SetupInstallServicesFromInfSectionExW +@ stub SetupInstallServicesFromInfSectionW +@ stdcall SetupIterateCabinetA(str long ptr ptr) +@ stdcall SetupIterateCabinetW(wstr long ptr ptr) +@ stub SetupLogErrorA +@ stub SetupLogErrorW +@ stub SetupLogFileA +@ stub SetupLogFileW +@ stdcall SetupOpenAppendInfFileA(str long ptr) +@ stdcall SetupOpenAppendInfFileW(wstr long ptr) +@ stdcall SetupOpenFileQueue() +@ stdcall SetupOpenInfFileA(str str long ptr) +@ stdcall SetupOpenInfFileW(wstr wstr long ptr) +@ stdcall SetupOpenMasterInf() +@ stub SetupPromptForDiskA +@ stub SetupPromptForDiskW +@ stub SetupPromptReboot +@ stub SetupQueryDrivesInDiskSpaceListA +@ stub SetupQueryDrivesInDiskSpaceListW +@ stub SetupQueryFileLogA +@ stub SetupQueryFileLogW +@ stub SetupQueryInfFileInformationA +@ stub SetupQueryInfFileInformationW +@ stub SetupQueryInfOriginalFileInformationA +@ stub SetupQueryInfOriginalFileInformationW +@ stub SetupQueryInfVersionInformationA +@ stub SetupQueryInfVersionInformationW +@ stub SetupQuerySourceListA +@ stub SetupQuerySourceListW +@ stdcall SetupQuerySpaceRequiredOnDriveA(long str ptr ptr long) +@ stub SetupQuerySpaceRequiredOnDriveW +@ stdcall SetupQueueCopyA(long str str str str str str str long) +@ stdcall SetupQueueCopyIndirectA(ptr) +@ stdcall SetupQueueCopyIndirectW(ptr) +@ stdcall SetupQueueCopySectionA(long str long long str long) +@ stdcall SetupQueueCopySectionW(long wstr long long wstr long) +@ stdcall SetupQueueCopyW(long wstr wstr wstr wstr wstr wstr wstr long) +@ stdcall SetupQueueDefaultCopyA(long long str str str long) +@ stdcall SetupQueueDefaultCopyW(long long wstr wstr wstr long) +@ stdcall SetupQueueDeleteA(long str str) +@ stdcall SetupQueueDeleteSectionA(long long long str) +@ stdcall SetupQueueDeleteSectionW(long long long wstr) +@ stdcall SetupQueueDeleteW(long wstr wstr) +@ stdcall SetupQueueRenameA(long str str str str) +@ stdcall SetupQueueRenameSectionA(long long long str) +@ stdcall SetupQueueRenameSectionW(long long long wstr) +@ stdcall SetupQueueRenameW(long wstr wstr wstr wstr) +@ stub SetupRemoveFileLogEntryA +@ stub SetupRemoveFileLogEntryW +@ stub SetupRemoveFromDiskSpaceListA +@ stub SetupRemoveFromDiskSpaceListW +@ stub SetupRemoveFromSourceListA +@ stub SetupRemoveFromSourceListW +@ stub SetupRemoveInstallSectionFromDiskSpaceListA +@ stub SetupRemoveInstallSectionFromDiskSpaceListW +@ stub SetupRemoveSectionFromDiskSpaceListA +@ stub SetupRemoveSectionFromDiskSpaceListW +@ stub SetupRenameErrorA +@ stub SetupRenameErrorW +@ stub SetupScanFileQueue +@ stdcall SetupScanFileQueueA(long long long ptr ptr ptr) +@ stdcall SetupScanFileQueueW(long long long ptr ptr ptr) +@ stdcall SetupSetDirectoryIdA(long long str) +@ stub SetupSetDirectoryIdExA +@ stub SetupSetDirectoryIdExW +@ stdcall SetupSetDirectoryIdW(long long wstr) +@ stub SetupFileQueueAlternatePlatformA +@ stub SetupFileQueueAlternatePlatformW +@ stdcall SetupSetFileQueueFlags(long long long) +@ stub SetupSetPlatformPathOverrideA +@ stub SetupSetPlatformPathOverrideW +@ stub SetupSetSourceListA +@ stub SetupSetSourceListW +@ stdcall SetupTermDefaultQueueCallback(ptr) +@ stdcall SetupTerminateFileLog(long) +@ stub ShouldDeviceBeExcluded +@ stub StampFileSecurity +@ stub StringTableAddString +@ stub StringTableAddStringEx +@ stub StringTableDestroy +@ stub StringTableDuplicate +@ stub StringTableEnum +@ stub StringTableGetExtraData +@ stub StringTableInitialize +@ stub StringTableInitializeEx +@ stub StringTableLookUpString +@ stub StringTableLookUpStringEx +@ stub StringTableSetExtraData +@ stub StringTableStringFromId +@ stub StringTableTrim +@ stub TakeOwnershipOfFile +@ stdcall UnicodeToMultiByte(wstr long) +@ stub UnmapAndCloseFile +@ stub VerifyCatalogFile +@ stub VerifyFile +@ stub pSetupAccessRunOnceNodeList +@ stub pSetupAddMiniIconToList +@ stub pSetupAddTagToGroupOrderListEntry +@ stub pSetupAppendStringToMultiSz +@ stub pSetupDestroyRunOnceNodeList +@ stub pSetupDirectoryIdToPath +@ stub pSetupGetField +@ stub pSetupGetGlobalFlags +@ stub pSetupGetOsLoaderDriveAndPath +@ stub pSetupGetQueueFlags +@ stub pSetupGetVersionDatum +@ stub pSetupGuidFromString +@ stub pSetupIsGuidNull +@ stub pSetupMakeSurePathExists +@ stub pSetupSetGlobalFlags +@ stub pSetupSetQueueFlags +@ stub pSetupSetSystemSourceFlags +@ stub pSetupStringFromGuid +@ stub pSetupVerifyQueuedCatalogs diff --git a/reactos/lib/setupapi/setupapi.xml b/reactos/lib/setupapi/setupapi.xml index cea0b8c512c..739d6c520a0 100644 --- a/reactos/lib/setupapi/setupapi.xml +++ b/reactos/lib/setupapi/setupapi.xml @@ -2,6 +2,7 @@ . include/wine + . @@ -11,6 +12,7 @@ 0x501 + pnp_client wine ntdll kernel32 @@ -28,6 +30,7 @@ queue.c setupcab.c stubs.c + rpc.c setupapi.rc setupapi.spec diff --git a/reactos/lib/setupapi/setupapi_private.h b/reactos/lib/setupapi/setupapi_private.h index 80d1f5559c2..96d90198cf2 100644 --- a/reactos/lib/setupapi/setupapi_private.h +++ b/reactos/lib/setupapi/setupapi_private.h @@ -1,59 +1,61 @@ -/* - * Copyright 2001 Andreas Mohr - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __SETUPAPI_PRIVATE_H -#define __SETUPAPI_PRIVATE_H - -#define COPYFILEDLGORD 1000 -#define SOURCESTRORD 500 -#define DESTSTRORD 501 -#define PROGRESSORD 502 - - -#define REG_INSTALLEDFILES "System\\CurrentControlSet\\Control\\InstalledFiles" -#define REGPART_RENAME "\\Rename" -#define REG_VERSIONCONFLICT "Software\\Microsoft\\VersionConflictManager" - -/* string substitutions */ - -struct inf_file; -extern const WCHAR *DIRID_get_string( HINF hinf, int dirid ); -extern unsigned int PARSER_string_substA( struct inf_file *file, const WCHAR *text, - char *buffer, unsigned int size ); -extern unsigned int PARSER_string_substW( struct inf_file *file, const WCHAR *text, - WCHAR *buffer, unsigned int size ); -extern const WCHAR *PARSER_get_src_root( HINF hinf ); -extern WCHAR *PARSER_get_dest_dir( INFCONTEXT *context ); - -/* support for Ascii queue callback functions */ - -struct callback_WtoA_context -{ - void *orig_context; - PSP_FILE_CALLBACK_A orig_handler; -}; - -UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification, UINT_PTR, UINT_PTR ); - -/* from msvcrt/sys/stat.h */ -#define _S_IWRITE 0x0080 -#define _S_IREAD 0x0100 - -extern OSVERSIONINFOW OsVersionInfo; - -#endif /* __SETUPAPI_PRIVATE_H */ +/* + * Copyright 2001 Andreas Mohr + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SETUPAPI_PRIVATE_H +#define __SETUPAPI_PRIVATE_H + +#define COPYFILEDLGORD 1000 +#define SOURCESTRORD 500 +#define DESTSTRORD 501 +#define PROGRESSORD 502 + + +#define REG_INSTALLEDFILES "System\\CurrentControlSet\\Control\\InstalledFiles" +#define REGPART_RENAME "\\Rename" +#define REG_VERSIONCONFLICT "Software\\Microsoft\\VersionConflictManager" + +/* string substitutions */ + +struct inf_file; +extern const WCHAR *DIRID_get_string( HINF hinf, int dirid ); +extern unsigned int PARSER_string_substA( struct inf_file *file, const WCHAR *text, + char *buffer, unsigned int size ); +extern unsigned int PARSER_string_substW( struct inf_file *file, const WCHAR *text, + WCHAR *buffer, unsigned int size ); +extern const WCHAR *PARSER_get_src_root( HINF hinf ); +extern WCHAR *PARSER_get_dest_dir( INFCONTEXT *context ); + +/* support for Ascii queue callback functions */ + +struct callback_WtoA_context +{ + void *orig_context; + PSP_FILE_CALLBACK_A orig_handler; +}; + +UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification, UINT_PTR, UINT_PTR ); + +/* from msvcrt/sys/stat.h */ +#define _S_IWRITE 0x0080 +#define _S_IREAD 0x0100 + +extern OSVERSIONINFOW OsVersionInfo; + +DWORD WINAPI CaptureAndConvertAnsiArg(LPCSTR pSrc, LPWSTR *pDst); + +#endif /* __SETUPAPI_PRIVATE_H */ diff --git a/reactos/lib/setupapi/setupcab.c b/reactos/lib/setupapi/setupcab.c index 9c1081337a9..470f8aaa933 100644 --- a/reactos/lib/setupapi/setupcab.c +++ b/reactos/lib/setupapi/setupcab.c @@ -1,689 +1,689 @@ -/* - * Setupapi cabinet routines - * - * Copyright 2003 Gregory M. Turner - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * - * Many useful traces are commented in code, uncomment them if you have - * trouble and run with WINEDEBUG=+setupapi - * - */ - -#include -#include -#include - -#include "wine/debug.h" -#include "windef.h" -#include "winbase.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "winreg.h" -#include "setupapi.h" -#include "setupapi_private.h" -#include "fdi.h" -#include "wine/unicode.h" - -#include "fcntl.h" -#include "share.h" - -#include "wine/debug.h" - -OSVERSIONINFOW OsVersionInfo; - -static HINSTANCE CABINET_hInstance = 0; - -static HFDI (__cdecl *sc_FDICreate)(PFNALLOC, PFNFREE, PFNOPEN, - PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF); - -static BOOL (__cdecl *sc_FDICopy)(HFDI, char *, char *, int, - PFNFDINOTIFY, PFNFDIDECRYPT, void *); - -static BOOL (__cdecl *sc_FDIDestroy)(HFDI); - -#define SC_HSC_A_MAGIC 0xACABFEED -typedef struct { - UINT magic; - HFDI hfdi; - PSP_FILE_CALLBACK_A msghandler; - PVOID context; - CHAR most_recent_cabinet_name[MAX_PATH]; -} SC_HSC_A, *PSC_HSC_A; - -#define SC_HSC_W_MAGIC 0x0CABFEED -typedef struct { - UINT magic; - HFDI hfdi; - PSP_FILE_CALLBACK_W msghandler; - PVOID context; - WCHAR most_recent_cabinet_name[MAX_PATH]; -} SC_HSC_W, *PSC_HSC_W; - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -static BOOL LoadCABINETDll(void) -{ - if (!CABINET_hInstance) { - CABINET_hInstance = LoadLibraryA("cabinet.dll"); - if (CABINET_hInstance) { - sc_FDICreate = (void *)GetProcAddress(CABINET_hInstance, "FDICreate"); - sc_FDICopy = (void *)GetProcAddress(CABINET_hInstance, "FDICopy"); - sc_FDIDestroy = (void *)GetProcAddress(CABINET_hInstance, "FDIDestroy"); - return TRUE; - } else { - ERR("load cabinet dll failed.\n"); - return FALSE; - } - } else - return TRUE; -} - -static void UnloadCABINETDll(void) -{ - if (CABINET_hInstance) { - FreeLibrary(CABINET_hInstance); - CABINET_hInstance = 0; - } -} - -/* FDICreate callbacks */ - -static void *sc_cb_alloc(ULONG cb) -{ - return malloc(cb); -} - -static void sc_cb_free(void *pv) -{ - free(pv); -} - -static INT_PTR sc_cb_open(char *pszFile, int oflag, int pmode) -{ - DWORD creation = 0, sharing = 0; - int ioflag = 0; - INT_PTR ret = 0; - SECURITY_ATTRIBUTES sa; - - /* TRACE("(pszFile == %s, oflag == %d, pmode == %d)\n", debugstr_a(pszFile), oflag, pmode); */ - - switch(oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { - case _O_RDONLY: - ioflag |= GENERIC_READ; - break; - case _O_WRONLY: - ioflag |= GENERIC_WRITE; - break; - case _O_RDWR: - ioflag |= GENERIC_READ & GENERIC_WRITE; - break; - case _O_WRONLY | _O_RDWR: /* hmmm.. */ - ERR("_O_WRONLY & _O_RDWR in oflag?\n"); - return -1; - } - - if (oflag & _O_CREAT) { - if (oflag & _O_EXCL) - creation = CREATE_NEW; - else if (oflag & _O_TRUNC) - creation = CREATE_ALWAYS; - else - creation = OPEN_ALWAYS; - } else /* no _O_CREAT */ { - if (oflag & _O_TRUNC) - creation = TRUNCATE_EXISTING; - else - creation = OPEN_EXISTING; - } - - switch( pmode & 0x70 ) { - case _SH_DENYRW: - sharing = 0L; - break; - case _SH_DENYWR: - sharing = FILE_SHARE_READ; - break; - case _SH_DENYRD: - sharing = FILE_SHARE_WRITE; - break; - case _SH_COMPAT: - case _SH_DENYNO: - sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; - break; - default: - ERR("<-- -1 (Unhandled pmode 0x%x)\n", pmode); - return -1; - } - - if (oflag & ~(_O_BINARY | _O_TRUNC | _O_EXCL | _O_CREAT | _O_RDWR | _O_WRONLY | _O_NOINHERIT)) - WARN("unsupported oflag 0x%04x\n",oflag); - - sa.nLength = sizeof( SECURITY_ATTRIBUTES ); - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = (ioflag & _O_NOINHERIT) ? FALSE : TRUE; - - ret = (INT_PTR) CreateFileA(pszFile, ioflag, sharing, &sa, creation, FILE_ATTRIBUTE_NORMAL, NULL); - - /* TRACE("<-- %d\n", ret); */ - - return ret; -} - -static UINT sc_cb_read(INT_PTR hf, void *pv, UINT cb) -{ - DWORD num_read; - BOOL rslt; - - /* TRACE("(hf == %d, pv == ^%p, cb == %u)\n", hf, pv, cb); */ - - rslt = ReadFile((HANDLE) hf, pv, cb, &num_read, NULL); - - - /* eof and failure both give "-1" return */ - if ((! rslt) || ((cb > 0) && (num_read == 0))) { - /* TRACE("<-- -1\n"); */ - return -1; - } - - /* TRACE("<-- %lu\n", num_read); */ - return num_read; -} - -static UINT sc_cb_write(INT_PTR hf, void *pv, UINT cb) -{ - DWORD num_written; - /* BOOL rv; */ - - /* TRACE("(hf == %d, pv == ^%p, cb == %u)\n", hf, pv, cb); */ - - if ( /* (rv = */ WriteFile((HANDLE) hf, pv, cb, &num_written, NULL) /* ) */ - && (num_written == cb)) { - /* TRACE("<-- %lu\n", num_written); */ - return num_written; - } else { - /* TRACE("rv == %d, num_written == %lu, cb == %u\n", rv, num_written,cb); */ - /* TRACE("<-- -1\n"); */ - return -1; - } -} - -static int sc_cb_close(INT_PTR hf) -{ - /* TRACE("(hf == %d)\n", hf); */ - - if (CloseHandle((HANDLE) hf)) - return 0; - else - return -1; -} - -static long sc_cb_lseek(INT_PTR hf, long dist, int seektype) -{ - DWORD ret; - - /* TRACE("(hf == %d, dist == %ld, seektype == %d)\n", hf, dist, seektype); */ - - if (seektype < 0 || seektype > 2) - return -1; - - if (((ret = SetFilePointer((HANDLE) hf, dist, NULL, seektype)) != INVALID_SET_FILE_POINTER) || !GetLastError()) { - /* TRACE("<-- %lu\n", ret); */ - return ret; - } else { - /* TRACE("<-- -1\n"); */ - return -1; - } -} - -#define SIZEOF_MYSTERIO (MAX_PATH*3) - -/* FDICopy callbacks */ - -static INT_PTR sc_FNNOTIFY_A(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) -{ - FILE_IN_CABINET_INFO_A fici; - PSC_HSC_A phsc; - CABINET_INFO_A ci; - FILEPATHS_A fp; - UINT err; - - CHAR mysterio[SIZEOF_MYSTERIO]; /* how big? undocumented! probably 256... */ - - memset(&(mysterio[0]), 0, SIZEOF_MYSTERIO); - - TRACE("(fdint == %d, pfdin == ^%p)\n", fdint, pfdin); - - if (pfdin && pfdin->pv && (*((void **) pfdin->pv) == (void *)SC_HSC_A_MAGIC)) - phsc = (PSC_HSC_A) pfdin->pv; - else { - ERR("pv %p is not an SC_HSC_A.\n", (pfdin) ? pfdin->pv : NULL); - return -1; - } - - switch (fdint) { - case fdintCABINET_INFO: - TRACE("Cabinet info notification\n"); - /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); - TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); - TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); - TRACE(" Cabinet Set#: %d\n", pfdin->setID); - TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ - WARN("SPFILENOTIFY_CABINETINFO undocumented: guess implementation.\n"); - ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]); - ci.CabinetPath = pfdin->psz3; - ci.DiskName = pfdin->psz2; - ci.SetId = pfdin->setID; - ci.CabinetNumber = pfdin->iCabinet; - phsc->msghandler(phsc->context, SPFILENOTIFY_CABINETINFO, (UINT) &ci, 0); - return 0; - case fdintPARTIAL_FILE: - TRACE("Partial file notification\n"); - /* TRACE(" Partial file name: %s\n", debugstr_a(pfdin->psz1)); */ - return 0; - case fdintCOPY_FILE: - TRACE("Copy file notification\n"); - TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); - /* TRACE(" File size: %ld\n", pfdin->cb); - TRACE(" File date: %u\n", pfdin->date); - TRACE(" File time: %u\n", pfdin->time); - TRACE(" File attr: %u\n", pfdin->attribs); */ - fici.NameInCabinet = pfdin->psz1; - fici.FileSize = pfdin->cb; - fici.Win32Error = 0; - fici.DosDate = pfdin->date; - fici.DosTime = pfdin->time; - fici.DosAttribs = pfdin->attribs; - memset(&(fici.FullTargetName[0]), 0, MAX_PATH); - err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEINCABINET, - (UINT) &fici, (UINT) pfdin->psz1); - if (err == FILEOP_DOIT) { - TRACE(" Callback specified filename: %s\n", debugstr_a(&(fici.FullTargetName[0]))); - if (!fici.FullTargetName[0]) { - WARN(" Empty return string causing abort.\n"); - SetLastError(ERROR_PATH_NOT_FOUND); - return -1; - } - return sc_cb_open(&(fici.FullTargetName[0]), _O_BINARY | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE); - } else { - TRACE(" Callback skipped file.\n"); - return 0; - } - case fdintCLOSE_FILE_INFO: - TRACE("Close file notification\n"); - /* TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); - TRACE(" Exec file? %s\n", (pfdin->cb) ? "Yes" : "No"); - TRACE(" File hndl: %d\n", pfdin->hf); */ - fp.Source = &(phsc->most_recent_cabinet_name[0]); - fp.Target = pfdin->psz1; - fp.Win32Error = 0; - fp.Flags = 0; - /* the following should be a fixme -- but it occurs too many times */ - WARN("Should set file date/time/attribs (and execute files?)\n"); - err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEEXTRACTED, (UINT) &fp, 0); - if (sc_cb_close(pfdin->hf)) - WARN("_close failed.\n"); - if (err) { - SetLastError(err); - return FALSE; - } else - return TRUE; - case fdintNEXT_CABINET: - TRACE("Next cabinet notification\n"); - /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); - TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); - TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); - TRACE(" Cabinet Set#: %d\n", pfdin->setID); - TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ - ci.CabinetFile = pfdin->psz1; - ci.CabinetPath = pfdin->psz3; - ci.DiskName = pfdin->psz2; - ci.SetId = pfdin->setID; - ci.CabinetNumber = pfdin->iCabinet; - /* remember the new cabinet name */ - strcpy(&(phsc->most_recent_cabinet_name[0]), pfdin->psz1); - err = phsc->msghandler(phsc->context, SPFILENOTIFY_NEEDNEWCABINET, (UINT) &ci, (UINT) &(mysterio[0])); - if (err) { - SetLastError(err); - return -1; - } else { - if (mysterio[0]) { - /* some easy paranoia. no such carefulness exists on the wide API IIRC */ - mysterio[SIZEOF_MYSTERIO - 1] = '\0'; - strncpy(pfdin->psz3, &(mysterio[0]), 255); - mysterio[255] = '\0'; - } - return 0; - } - default: - FIXME("Unknown notification type %d.\n", fdint); - return 0; - } -} - -static INT_PTR sc_FNNOTIFY_W(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) -{ - FILE_IN_CABINET_INFO_W fici; - PSC_HSC_W phsc; - CABINET_INFO_W ci; - FILEPATHS_W fp; - UINT err; - int len; - - WCHAR mysterio[SIZEOF_MYSTERIO]; /* how big? undocumented! */ - WCHAR buf[MAX_PATH], buf2[MAX_PATH]; - CHAR charbuf[MAX_PATH]; - - memset(&(mysterio[0]), 0, SIZEOF_MYSTERIO * sizeof(WCHAR)); - memset(&(buf[0]), 0, MAX_PATH * sizeof(WCHAR)); - memset(&(buf2[0]), 0, MAX_PATH * sizeof(WCHAR)); - memset(&(charbuf[0]), 0, MAX_PATH); - - TRACE("(fdint == %d, pfdin == ^%p)\n", fdint, pfdin); - - if (pfdin && pfdin->pv && (*((void **) pfdin->pv) == (void *)SC_HSC_W_MAGIC)) - phsc = (PSC_HSC_W) pfdin->pv; - else { - ERR("pv %p is not an SC_HSC_W.\n", (pfdin) ? pfdin->pv : NULL); - return -1; - } - - switch (fdint) { - case fdintCABINET_INFO: - TRACE("Cabinet info notification\n"); - /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); - TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); - TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); - TRACE(" Cabinet Set#: %d\n", pfdin->setID); - TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ - WARN("SPFILENOTIFY_CABINETINFO undocumented: guess implementation.\n"); - ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]); - len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz3, -1, &(buf[0]), MAX_PATH); - if ((len > MAX_PATH) || (len <= 1)) - buf[0] = '\0'; - ci.CabinetPath = &(buf[0]); - len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz2, -1, &(buf2[0]), MAX_PATH); - if ((len > MAX_PATH) || (len <= 1)) - buf2[0] = '\0'; - ci.DiskName = &(buf2[0]); - ci.SetId = pfdin->setID; - ci.CabinetNumber = pfdin->iCabinet; - phsc->msghandler(phsc->context, SPFILENOTIFY_CABINETINFO, (UINT) &ci, 0); - return 0; - case fdintPARTIAL_FILE: - TRACE("Partial file notification\n"); - /* TRACE(" Partial file name: %s\n", debugstr_a(pfdin->psz1)); */ - return 0; - case fdintCOPY_FILE: - TRACE("Copy file notification\n"); - TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); - /* TRACE(" File size: %ld\n", pfdin->cb); - TRACE(" File date: %u\n", pfdin->date); - TRACE(" File time: %u\n", pfdin->time); - TRACE(" File attr: %u\n", pfdin->attribs); */ - len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(buf2[0]), MAX_PATH); - if ((len > MAX_PATH) || (len <= 1)) - buf2[0] = '\0'; - fici.NameInCabinet = &(buf2[0]); - fici.FileSize = pfdin->cb; - fici.Win32Error = 0; - fici.DosDate = pfdin->date; - fici.DosTime = pfdin->time; - fici.DosAttribs = pfdin->attribs; - memset(&(fici.FullTargetName[0]), 0, MAX_PATH * sizeof(WCHAR)); - err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEINCABINET, - (UINT) &fici, (UINT) pfdin->psz1); - if (err == FILEOP_DOIT) { - TRACE(" Callback specified filename: %s\n", debugstr_w(&(fici.FullTargetName[0]))); - if (fici.FullTargetName[0]) { - len = strlenW(&(fici.FullTargetName[0])) + 1; - if ((len > MAX_PATH ) || (len <= 1)) - return 0; - if (!WideCharToMultiByte(CP_ACP, 0, &(fici.FullTargetName[0]), len, &(charbuf[0]), MAX_PATH, 0, 0)) - return 0; - } else { - WARN("Empty buffer string caused abort.\n"); - SetLastError(ERROR_PATH_NOT_FOUND); - return -1; - } - return sc_cb_open(&(charbuf[0]), _O_BINARY | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE); - } else { - TRACE(" Callback skipped file.\n"); - return 0; - } - case fdintCLOSE_FILE_INFO: - TRACE("Close file notification\n"); - /* TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); - TRACE(" Exec file? %s\n", (pfdin->cb) ? "Yes" : "No"); - TRACE(" File hndl: %d\n", pfdin->hf); */ - fp.Source = &(phsc->most_recent_cabinet_name[0]); - len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(buf[0]), MAX_PATH); - if ((len > MAX_PATH) || (len <= 1)) - buf[0] = '\0'; - fp.Target = &(buf[0]); - fp.Win32Error = 0; - fp.Flags = 0; - /* a valid fixme -- but occurs too many times */ - /* FIXME("Should set file date/time/attribs (and execute files?)\n"); */ - err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEEXTRACTED, (UINT) &fp, 0); - if (sc_cb_close(pfdin->hf)) - WARN("_close failed.\n"); - if (err) { - SetLastError(err); - return FALSE; - } else - return TRUE; - case fdintNEXT_CABINET: - TRACE("Next cabinet notification\n"); - /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); - TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); - TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); - TRACE(" Cabinet Set#: %d\n", pfdin->setID); - TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ - /* remember the new cabinet name */ - len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(phsc->most_recent_cabinet_name[0]), MAX_PATH); - if ((len > MAX_PATH) || (len <= 1)) - phsc->most_recent_cabinet_name[0] = '\0'; - ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]); - len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz3, -1, &(buf[0]), MAX_PATH); - if ((len > MAX_PATH) || (len <= 1)) - buf[0] = '\0'; - ci.CabinetPath = &(buf[0]); - len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz2, -1, &(buf2[0]), MAX_PATH); - if ((len > MAX_PATH) || (len <= 1)) - buf2[0] = '\0'; - ci.DiskName = &(buf2[0]); - ci.SetId = pfdin->setID; - ci.CabinetNumber = pfdin->iCabinet; - err = phsc->msghandler(phsc->context, SPFILENOTIFY_NEEDNEWCABINET, (UINT) &ci, (UINT) &(mysterio[0])); - if (err) { - SetLastError(err); - return -1; - } else { - if (mysterio[0]) { - len = strlenW(&(mysterio[0])) + 1; - if ((len > 255) || (len <= 1)) - return 0; - if (!WideCharToMultiByte(CP_ACP, 0, &(mysterio[0]), len, pfdin->psz3, 255, 0, 0)) - return 0; - } - return 0; - } - default: - FIXME("Unknown notification type %d.\n", fdint); - return 0; - } -} - -/*********************************************************************** - * SetupIterateCabinetA (SETUPAPI.@) - */ -BOOL WINAPI SetupIterateCabinetA(PCSTR CabinetFile, DWORD Reserved, - PSP_FILE_CALLBACK_A MsgHandler, PVOID Context) -{ - - SC_HSC_A my_hsc; - ERF erf; - CHAR pszCabinet[MAX_PATH], pszCabPath[MAX_PATH], *p; - DWORD fpnsize; - BOOL ret; - - - TRACE("(CabinetFile == %s, Reserved == %lu, MsgHandler == ^%p, Context == ^%p)\n", - debugstr_a(CabinetFile), Reserved, MsgHandler, Context); - - if (! LoadCABINETDll()) - return FALSE; - - memset(&my_hsc, 0, sizeof(SC_HSC_A)); - pszCabinet[0] = '\0'; - pszCabPath[0] = '\0'; - - fpnsize = strlen(CabinetFile); - if (fpnsize >= MAX_PATH) { - SetLastError(ERROR_BAD_PATHNAME); - return FALSE; - } - - fpnsize = GetFullPathNameA(CabinetFile, MAX_PATH, &(pszCabPath[0]), &p); - if (fpnsize > MAX_PATH) { - SetLastError(ERROR_BAD_PATHNAME); - return FALSE; - } - - if (p) { - strcpy(pszCabinet, p); - *p = '\0'; - } else { - strcpy(pszCabinet, CabinetFile); - pszCabPath[0] = '\0'; - } - - TRACE("path: %s, cabfile: %s\n", debugstr_a(pszCabPath), debugstr_a(pszCabinet)); - - /* remember the cabinet name */ - strcpy(&(my_hsc.most_recent_cabinet_name[0]), pszCabinet); - - my_hsc.magic = SC_HSC_A_MAGIC; - my_hsc.msghandler = MsgHandler; - my_hsc.context = Context; - my_hsc.hfdi = sc_FDICreate( sc_cb_alloc, sc_cb_free, sc_cb_open, sc_cb_read, - sc_cb_write, sc_cb_close, sc_cb_lseek, cpuUNKNOWN, &erf ); - - if (!my_hsc.hfdi) return FALSE; - - ret = ( sc_FDICopy(my_hsc.hfdi, pszCabinet, pszCabPath, - 0, sc_FNNOTIFY_A, NULL, &my_hsc) ) ? TRUE : FALSE; - - sc_FDIDestroy(my_hsc.hfdi); - return ret; -} - - -/*********************************************************************** - * SetupIterateCabinetW (SETUPAPI.@) - */ -BOOL WINAPI SetupIterateCabinetW(PCWSTR CabinetFile, DWORD Reserved, - PSP_FILE_CALLBACK_W MsgHandler, PVOID Context) -{ - CHAR pszCabinet[MAX_PATH], pszCabPath[MAX_PATH]; - UINT len; - SC_HSC_W my_hsc; - ERF erf; - WCHAR pszCabPathW[MAX_PATH], *p; - DWORD fpnsize; - BOOL ret; - - TRACE("(CabinetFile == %s, Reserved == %lu, MsgHandler == ^%p, Context == ^%p)\n", - debugstr_w(CabinetFile), Reserved, MsgHandler, Context); - - if (!LoadCABINETDll()) - return FALSE; - - if (!CabinetFile) return FALSE; - - memset(&my_hsc, 0, sizeof(SC_HSC_W)); - - fpnsize = GetFullPathNameW(CabinetFile, MAX_PATH, pszCabPathW, &p); - if (fpnsize > MAX_PATH) { - SetLastError(ERROR_BAD_PATHNAME); - return FALSE; - } - - if (p) { - strcpyW(my_hsc.most_recent_cabinet_name, p); - *p = 0; - len = WideCharToMultiByte(CP_ACP, 0, pszCabPathW, -1, pszCabPath, - MAX_PATH, 0, 0); - if (!len) return FALSE; - } else { - strcpyW(my_hsc.most_recent_cabinet_name, CabinetFile); - pszCabPath[0] = '\0'; - } - - len = WideCharToMultiByte(CP_ACP, 0, my_hsc.most_recent_cabinet_name, -1, - pszCabinet, MAX_PATH, 0, 0); - if (!len) return FALSE; - - TRACE("path: %s, cabfile: %s\n", - debugstr_a(pszCabPath), debugstr_a(pszCabinet)); - - my_hsc.magic = SC_HSC_W_MAGIC; - my_hsc.msghandler = MsgHandler; - my_hsc.context = Context; - my_hsc.hfdi = sc_FDICreate( sc_cb_alloc, sc_cb_free, sc_cb_open, sc_cb_read, - sc_cb_write, sc_cb_close, sc_cb_lseek, cpuUNKNOWN, &erf ); - - if (!my_hsc.hfdi) return FALSE; - - ret = ( sc_FDICopy(my_hsc.hfdi, pszCabinet, pszCabPath, - 0, sc_FNNOTIFY_W, NULL, &my_hsc) ) ? TRUE : FALSE; - - sc_FDIDestroy(my_hsc.hfdi); - return ret; -} - - -/*********************************************************************** - * DllMain - * - * PARAMS - * hinstDLL [I] handle to the DLL's instance - * fdwReason [I] - * lpvReserved [I] reserved, must be NULL - * - * RETURNS - * Success: TRUE - * Failure: FALSE - */ - -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) -{ - switch (fdwReason) { - case DLL_PROCESS_ATTACH: - DisableThreadLibraryCalls(hinstDLL); - OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); - if (!GetVersionExW(&OsVersionInfo)) - return FALSE; - break; - case DLL_PROCESS_DETACH: - UnloadCABINETDll(); - break; - } - - return TRUE; -} +/* + * Setupapi cabinet routines + * + * Copyright 2003 Gregory M. Turner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Many useful traces are commented in code, uncomment them if you have + * trouble and run with WINEDEBUG=+setupapi + * + */ + +#include +#include +#include + +#include "wine/debug.h" +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "setupapi.h" +#include "setupapi_private.h" +#include "fdi.h" +#include "wine/unicode.h" + +#include "fcntl.h" +#include "share.h" + +#include "wine/debug.h" + +OSVERSIONINFOW OsVersionInfo; + +static HINSTANCE CABINET_hInstance = 0; + +static HFDI (__cdecl *sc_FDICreate)(PFNALLOC, PFNFREE, PFNOPEN, + PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF); + +static BOOL (__cdecl *sc_FDICopy)(HFDI, char *, char *, int, + PFNFDINOTIFY, PFNFDIDECRYPT, void *); + +static BOOL (__cdecl *sc_FDIDestroy)(HFDI); + +#define SC_HSC_A_MAGIC 0xACABFEED +typedef struct { + UINT magic; + HFDI hfdi; + PSP_FILE_CALLBACK_A msghandler; + PVOID context; + CHAR most_recent_cabinet_name[MAX_PATH]; +} SC_HSC_A, *PSC_HSC_A; + +#define SC_HSC_W_MAGIC 0x0CABFEED +typedef struct { + UINT magic; + HFDI hfdi; + PSP_FILE_CALLBACK_W msghandler; + PVOID context; + WCHAR most_recent_cabinet_name[MAX_PATH]; +} SC_HSC_W, *PSC_HSC_W; + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +static BOOL LoadCABINETDll(void) +{ + if (!CABINET_hInstance) { + CABINET_hInstance = LoadLibraryA("cabinet.dll"); + if (CABINET_hInstance) { + sc_FDICreate = (void *)GetProcAddress(CABINET_hInstance, "FDICreate"); + sc_FDICopy = (void *)GetProcAddress(CABINET_hInstance, "FDICopy"); + sc_FDIDestroy = (void *)GetProcAddress(CABINET_hInstance, "FDIDestroy"); + return TRUE; + } else { + ERR("load cabinet dll failed.\n"); + return FALSE; + } + } else + return TRUE; +} + +static void UnloadCABINETDll(void) +{ + if (CABINET_hInstance) { + FreeLibrary(CABINET_hInstance); + CABINET_hInstance = 0; + } +} + +/* FDICreate callbacks */ + +static void *sc_cb_alloc(ULONG cb) +{ + return malloc(cb); +} + +static void sc_cb_free(void *pv) +{ + free(pv); +} + +static INT_PTR sc_cb_open(char *pszFile, int oflag, int pmode) +{ + DWORD creation = 0, sharing = 0; + int ioflag = 0; + INT_PTR ret = 0; + SECURITY_ATTRIBUTES sa; + + /* TRACE("(pszFile == %s, oflag == %d, pmode == %d)\n", debugstr_a(pszFile), oflag, pmode); */ + + switch(oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { + case _O_RDONLY: + ioflag |= GENERIC_READ; + break; + case _O_WRONLY: + ioflag |= GENERIC_WRITE; + break; + case _O_RDWR: + ioflag |= GENERIC_READ & GENERIC_WRITE; + break; + case _O_WRONLY | _O_RDWR: /* hmmm.. */ + ERR("_O_WRONLY & _O_RDWR in oflag?\n"); + return -1; + } + + if (oflag & _O_CREAT) { + if (oflag & _O_EXCL) + creation = CREATE_NEW; + else if (oflag & _O_TRUNC) + creation = CREATE_ALWAYS; + else + creation = OPEN_ALWAYS; + } else /* no _O_CREAT */ { + if (oflag & _O_TRUNC) + creation = TRUNCATE_EXISTING; + else + creation = OPEN_EXISTING; + } + + switch( pmode & 0x70 ) { + case _SH_DENYRW: + sharing = 0L; + break; + case _SH_DENYWR: + sharing = FILE_SHARE_READ; + break; + case _SH_DENYRD: + sharing = FILE_SHARE_WRITE; + break; + case _SH_COMPAT: + case _SH_DENYNO: + sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; + break; + default: + ERR("<-- -1 (Unhandled pmode 0x%x)\n", pmode); + return -1; + } + + if (oflag & ~(_O_BINARY | _O_TRUNC | _O_EXCL | _O_CREAT | _O_RDWR | _O_WRONLY | _O_NOINHERIT)) + WARN("unsupported oflag 0x%04x\n",oflag); + + sa.nLength = sizeof( SECURITY_ATTRIBUTES ); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = (ioflag & _O_NOINHERIT) ? FALSE : TRUE; + + ret = (INT_PTR) CreateFileA(pszFile, ioflag, sharing, &sa, creation, FILE_ATTRIBUTE_NORMAL, NULL); + + /* TRACE("<-- %d\n", ret); */ + + return ret; +} + +static UINT sc_cb_read(INT_PTR hf, void *pv, UINT cb) +{ + DWORD num_read; + BOOL rslt; + + /* TRACE("(hf == %d, pv == ^%p, cb == %u)\n", hf, pv, cb); */ + + rslt = ReadFile((HANDLE) hf, pv, cb, &num_read, NULL); + + + /* eof and failure both give "-1" return */ + if ((! rslt) || ((cb > 0) && (num_read == 0))) { + /* TRACE("<-- -1\n"); */ + return -1; + } + + /* TRACE("<-- %lu\n", num_read); */ + return num_read; +} + +static UINT sc_cb_write(INT_PTR hf, void *pv, UINT cb) +{ + DWORD num_written; + /* BOOL rv; */ + + /* TRACE("(hf == %d, pv == ^%p, cb == %u)\n", hf, pv, cb); */ + + if ( /* (rv = */ WriteFile((HANDLE) hf, pv, cb, &num_written, NULL) /* ) */ + && (num_written == cb)) { + /* TRACE("<-- %lu\n", num_written); */ + return num_written; + } else { + /* TRACE("rv == %d, num_written == %lu, cb == %u\n", rv, num_written,cb); */ + /* TRACE("<-- -1\n"); */ + return -1; + } +} + +static int sc_cb_close(INT_PTR hf) +{ + /* TRACE("(hf == %d)\n", hf); */ + + if (CloseHandle((HANDLE) hf)) + return 0; + else + return -1; +} + +static long sc_cb_lseek(INT_PTR hf, long dist, int seektype) +{ + DWORD ret; + + /* TRACE("(hf == %d, dist == %ld, seektype == %d)\n", hf, dist, seektype); */ + + if (seektype < 0 || seektype > 2) + return -1; + + if (((ret = SetFilePointer((HANDLE) hf, dist, NULL, seektype)) != INVALID_SET_FILE_POINTER) || !GetLastError()) { + /* TRACE("<-- %lu\n", ret); */ + return ret; + } else { + /* TRACE("<-- -1\n"); */ + return -1; + } +} + +#define SIZEOF_MYSTERIO (MAX_PATH*3) + +/* FDICopy callbacks */ + +static INT_PTR sc_FNNOTIFY_A(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) +{ + FILE_IN_CABINET_INFO_A fici; + PSC_HSC_A phsc; + CABINET_INFO_A ci; + FILEPATHS_A fp; + UINT err; + + CHAR mysterio[SIZEOF_MYSTERIO]; /* how big? undocumented! probably 256... */ + + memset(&(mysterio[0]), 0, SIZEOF_MYSTERIO); + + TRACE("(fdint == %d, pfdin == ^%p)\n", fdint, pfdin); + + if (pfdin && pfdin->pv && (*((void **) pfdin->pv) == (void *)SC_HSC_A_MAGIC)) + phsc = (PSC_HSC_A) pfdin->pv; + else { + ERR("pv %p is not an SC_HSC_A.\n", (pfdin) ? pfdin->pv : NULL); + return -1; + } + + switch (fdint) { + case fdintCABINET_INFO: + TRACE("Cabinet info notification\n"); + /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); + TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); + TRACE(" Cabinet Set#: %d\n", pfdin->setID); + TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ + WARN("SPFILENOTIFY_CABINETINFO undocumented: guess implementation.\n"); + ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]); + ci.CabinetPath = pfdin->psz3; + ci.DiskName = pfdin->psz2; + ci.SetId = pfdin->setID; + ci.CabinetNumber = pfdin->iCabinet; + phsc->msghandler(phsc->context, SPFILENOTIFY_CABINETINFO, (UINT) &ci, 0); + return 0; + case fdintPARTIAL_FILE: + TRACE("Partial file notification\n"); + /* TRACE(" Partial file name: %s\n", debugstr_a(pfdin->psz1)); */ + return 0; + case fdintCOPY_FILE: + TRACE("Copy file notification\n"); + TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); + /* TRACE(" File size: %ld\n", pfdin->cb); + TRACE(" File date: %u\n", pfdin->date); + TRACE(" File time: %u\n", pfdin->time); + TRACE(" File attr: %u\n", pfdin->attribs); */ + fici.NameInCabinet = pfdin->psz1; + fici.FileSize = pfdin->cb; + fici.Win32Error = 0; + fici.DosDate = pfdin->date; + fici.DosTime = pfdin->time; + fici.DosAttribs = pfdin->attribs; + memset(&(fici.FullTargetName[0]), 0, MAX_PATH); + err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEINCABINET, + (UINT) &fici, (UINT) pfdin->psz1); + if (err == FILEOP_DOIT) { + TRACE(" Callback specified filename: %s\n", debugstr_a(&(fici.FullTargetName[0]))); + if (!fici.FullTargetName[0]) { + WARN(" Empty return string causing abort.\n"); + SetLastError(ERROR_PATH_NOT_FOUND); + return -1; + } + return sc_cb_open(&(fici.FullTargetName[0]), _O_BINARY | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE); + } else { + TRACE(" Callback skipped file.\n"); + return 0; + } + case fdintCLOSE_FILE_INFO: + TRACE("Close file notification\n"); + /* TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Exec file? %s\n", (pfdin->cb) ? "Yes" : "No"); + TRACE(" File hndl: %d\n", pfdin->hf); */ + fp.Source = &(phsc->most_recent_cabinet_name[0]); + fp.Target = pfdin->psz1; + fp.Win32Error = 0; + fp.Flags = 0; + /* the following should be a fixme -- but it occurs too many times */ + WARN("Should set file date/time/attribs (and execute files?)\n"); + err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEEXTRACTED, (UINT) &fp, 0); + if (sc_cb_close(pfdin->hf)) + WARN("_close failed.\n"); + if (err) { + SetLastError(err); + return FALSE; + } else + return TRUE; + case fdintNEXT_CABINET: + TRACE("Next cabinet notification\n"); + /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); + TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); + TRACE(" Cabinet Set#: %d\n", pfdin->setID); + TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ + ci.CabinetFile = pfdin->psz1; + ci.CabinetPath = pfdin->psz3; + ci.DiskName = pfdin->psz2; + ci.SetId = pfdin->setID; + ci.CabinetNumber = pfdin->iCabinet; + /* remember the new cabinet name */ + strcpy(&(phsc->most_recent_cabinet_name[0]), pfdin->psz1); + err = phsc->msghandler(phsc->context, SPFILENOTIFY_NEEDNEWCABINET, (UINT) &ci, (UINT) &(mysterio[0])); + if (err) { + SetLastError(err); + return -1; + } else { + if (mysterio[0]) { + /* some easy paranoia. no such carefulness exists on the wide API IIRC */ + mysterio[SIZEOF_MYSTERIO - 1] = '\0'; + strncpy(pfdin->psz3, &(mysterio[0]), 255); + mysterio[255] = '\0'; + } + return 0; + } + default: + FIXME("Unknown notification type %d.\n", fdint); + return 0; + } +} + +static INT_PTR sc_FNNOTIFY_W(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) +{ + FILE_IN_CABINET_INFO_W fici; + PSC_HSC_W phsc; + CABINET_INFO_W ci; + FILEPATHS_W fp; + UINT err; + int len; + + WCHAR mysterio[SIZEOF_MYSTERIO]; /* how big? undocumented! */ + WCHAR buf[MAX_PATH], buf2[MAX_PATH]; + CHAR charbuf[MAX_PATH]; + + memset(&(mysterio[0]), 0, SIZEOF_MYSTERIO * sizeof(WCHAR)); + memset(&(buf[0]), 0, MAX_PATH * sizeof(WCHAR)); + memset(&(buf2[0]), 0, MAX_PATH * sizeof(WCHAR)); + memset(&(charbuf[0]), 0, MAX_PATH); + + TRACE("(fdint == %d, pfdin == ^%p)\n", fdint, pfdin); + + if (pfdin && pfdin->pv && (*((void **) pfdin->pv) == (void *)SC_HSC_W_MAGIC)) + phsc = (PSC_HSC_W) pfdin->pv; + else { + ERR("pv %p is not an SC_HSC_W.\n", (pfdin) ? pfdin->pv : NULL); + return -1; + } + + switch (fdint) { + case fdintCABINET_INFO: + TRACE("Cabinet info notification\n"); + /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); + TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); + TRACE(" Cabinet Set#: %d\n", pfdin->setID); + TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ + WARN("SPFILENOTIFY_CABINETINFO undocumented: guess implementation.\n"); + ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz3, -1, &(buf[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf[0] = '\0'; + ci.CabinetPath = &(buf[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz2, -1, &(buf2[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf2[0] = '\0'; + ci.DiskName = &(buf2[0]); + ci.SetId = pfdin->setID; + ci.CabinetNumber = pfdin->iCabinet; + phsc->msghandler(phsc->context, SPFILENOTIFY_CABINETINFO, (UINT) &ci, 0); + return 0; + case fdintPARTIAL_FILE: + TRACE("Partial file notification\n"); + /* TRACE(" Partial file name: %s\n", debugstr_a(pfdin->psz1)); */ + return 0; + case fdintCOPY_FILE: + TRACE("Copy file notification\n"); + TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); + /* TRACE(" File size: %ld\n", pfdin->cb); + TRACE(" File date: %u\n", pfdin->date); + TRACE(" File time: %u\n", pfdin->time); + TRACE(" File attr: %u\n", pfdin->attribs); */ + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(buf2[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf2[0] = '\0'; + fici.NameInCabinet = &(buf2[0]); + fici.FileSize = pfdin->cb; + fici.Win32Error = 0; + fici.DosDate = pfdin->date; + fici.DosTime = pfdin->time; + fici.DosAttribs = pfdin->attribs; + memset(&(fici.FullTargetName[0]), 0, MAX_PATH * sizeof(WCHAR)); + err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEINCABINET, + (UINT) &fici, (UINT) pfdin->psz1); + if (err == FILEOP_DOIT) { + TRACE(" Callback specified filename: %s\n", debugstr_w(&(fici.FullTargetName[0]))); + if (fici.FullTargetName[0]) { + len = strlenW(&(fici.FullTargetName[0])) + 1; + if ((len > MAX_PATH ) || (len <= 1)) + return 0; + if (!WideCharToMultiByte(CP_ACP, 0, &(fici.FullTargetName[0]), len, &(charbuf[0]), MAX_PATH, 0, 0)) + return 0; + } else { + WARN("Empty buffer string caused abort.\n"); + SetLastError(ERROR_PATH_NOT_FOUND); + return -1; + } + return sc_cb_open(&(charbuf[0]), _O_BINARY | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE); + } else { + TRACE(" Callback skipped file.\n"); + return 0; + } + case fdintCLOSE_FILE_INFO: + TRACE("Close file notification\n"); + /* TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Exec file? %s\n", (pfdin->cb) ? "Yes" : "No"); + TRACE(" File hndl: %d\n", pfdin->hf); */ + fp.Source = &(phsc->most_recent_cabinet_name[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(buf[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf[0] = '\0'; + fp.Target = &(buf[0]); + fp.Win32Error = 0; + fp.Flags = 0; + /* a valid fixme -- but occurs too many times */ + /* FIXME("Should set file date/time/attribs (and execute files?)\n"); */ + err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEEXTRACTED, (UINT) &fp, 0); + if (sc_cb_close(pfdin->hf)) + WARN("_close failed.\n"); + if (err) { + SetLastError(err); + return FALSE; + } else + return TRUE; + case fdintNEXT_CABINET: + TRACE("Next cabinet notification\n"); + /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); + TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); + TRACE(" Cabinet Set#: %d\n", pfdin->setID); + TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ + /* remember the new cabinet name */ + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(phsc->most_recent_cabinet_name[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + phsc->most_recent_cabinet_name[0] = '\0'; + ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz3, -1, &(buf[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf[0] = '\0'; + ci.CabinetPath = &(buf[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz2, -1, &(buf2[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf2[0] = '\0'; + ci.DiskName = &(buf2[0]); + ci.SetId = pfdin->setID; + ci.CabinetNumber = pfdin->iCabinet; + err = phsc->msghandler(phsc->context, SPFILENOTIFY_NEEDNEWCABINET, (UINT) &ci, (UINT) &(mysterio[0])); + if (err) { + SetLastError(err); + return -1; + } else { + if (mysterio[0]) { + len = strlenW(&(mysterio[0])) + 1; + if ((len > 255) || (len <= 1)) + return 0; + if (!WideCharToMultiByte(CP_ACP, 0, &(mysterio[0]), len, pfdin->psz3, 255, 0, 0)) + return 0; + } + return 0; + } + default: + FIXME("Unknown notification type %d.\n", fdint); + return 0; + } +} + +/*********************************************************************** + * SetupIterateCabinetA (SETUPAPI.@) + */ +BOOL WINAPI SetupIterateCabinetA(PCSTR CabinetFile, DWORD Reserved, + PSP_FILE_CALLBACK_A MsgHandler, PVOID Context) +{ + + SC_HSC_A my_hsc; + ERF erf; + CHAR pszCabinet[MAX_PATH], pszCabPath[MAX_PATH], *p; + DWORD fpnsize; + BOOL ret; + + + TRACE("(CabinetFile == %s, Reserved == %lu, MsgHandler == ^%p, Context == ^%p)\n", + debugstr_a(CabinetFile), Reserved, MsgHandler, Context); + + if (! LoadCABINETDll()) + return FALSE; + + memset(&my_hsc, 0, sizeof(SC_HSC_A)); + pszCabinet[0] = '\0'; + pszCabPath[0] = '\0'; + + fpnsize = strlen(CabinetFile); + if (fpnsize >= MAX_PATH) { + SetLastError(ERROR_BAD_PATHNAME); + return FALSE; + } + + fpnsize = GetFullPathNameA(CabinetFile, MAX_PATH, &(pszCabPath[0]), &p); + if (fpnsize > MAX_PATH) { + SetLastError(ERROR_BAD_PATHNAME); + return FALSE; + } + + if (p) { + strcpy(pszCabinet, p); + *p = '\0'; + } else { + strcpy(pszCabinet, CabinetFile); + pszCabPath[0] = '\0'; + } + + TRACE("path: %s, cabfile: %s\n", debugstr_a(pszCabPath), debugstr_a(pszCabinet)); + + /* remember the cabinet name */ + strcpy(&(my_hsc.most_recent_cabinet_name[0]), pszCabinet); + + my_hsc.magic = SC_HSC_A_MAGIC; + my_hsc.msghandler = MsgHandler; + my_hsc.context = Context; + my_hsc.hfdi = sc_FDICreate( sc_cb_alloc, sc_cb_free, sc_cb_open, sc_cb_read, + sc_cb_write, sc_cb_close, sc_cb_lseek, cpuUNKNOWN, &erf ); + + if (!my_hsc.hfdi) return FALSE; + + ret = ( sc_FDICopy(my_hsc.hfdi, pszCabinet, pszCabPath, + 0, sc_FNNOTIFY_A, NULL, &my_hsc) ) ? TRUE : FALSE; + + sc_FDIDestroy(my_hsc.hfdi); + return ret; +} + + +/*********************************************************************** + * SetupIterateCabinetW (SETUPAPI.@) + */ +BOOL WINAPI SetupIterateCabinetW(PCWSTR CabinetFile, DWORD Reserved, + PSP_FILE_CALLBACK_W MsgHandler, PVOID Context) +{ + CHAR pszCabinet[MAX_PATH], pszCabPath[MAX_PATH]; + UINT len; + SC_HSC_W my_hsc; + ERF erf; + WCHAR pszCabPathW[MAX_PATH], *p; + DWORD fpnsize; + BOOL ret; + + TRACE("(CabinetFile == %s, Reserved == %lu, MsgHandler == ^%p, Context == ^%p)\n", + debugstr_w(CabinetFile), Reserved, MsgHandler, Context); + + if (!LoadCABINETDll()) + return FALSE; + + if (!CabinetFile) return FALSE; + + memset(&my_hsc, 0, sizeof(SC_HSC_W)); + + fpnsize = GetFullPathNameW(CabinetFile, MAX_PATH, pszCabPathW, &p); + if (fpnsize > MAX_PATH) { + SetLastError(ERROR_BAD_PATHNAME); + return FALSE; + } + + if (p) { + strcpyW(my_hsc.most_recent_cabinet_name, p); + *p = 0; + len = WideCharToMultiByte(CP_ACP, 0, pszCabPathW, -1, pszCabPath, + MAX_PATH, 0, 0); + if (!len) return FALSE; + } else { + strcpyW(my_hsc.most_recent_cabinet_name, CabinetFile); + pszCabPath[0] = '\0'; + } + + len = WideCharToMultiByte(CP_ACP, 0, my_hsc.most_recent_cabinet_name, -1, + pszCabinet, MAX_PATH, 0, 0); + if (!len) return FALSE; + + TRACE("path: %s, cabfile: %s\n", + debugstr_a(pszCabPath), debugstr_a(pszCabinet)); + + my_hsc.magic = SC_HSC_W_MAGIC; + my_hsc.msghandler = MsgHandler; + my_hsc.context = Context; + my_hsc.hfdi = sc_FDICreate( sc_cb_alloc, sc_cb_free, sc_cb_open, sc_cb_read, + sc_cb_write, sc_cb_close, sc_cb_lseek, cpuUNKNOWN, &erf ); + + if (!my_hsc.hfdi) return FALSE; + + ret = ( sc_FDICopy(my_hsc.hfdi, pszCabinet, pszCabPath, + 0, sc_FNNOTIFY_W, NULL, &my_hsc) ) ? TRUE : FALSE; + + sc_FDIDestroy(my_hsc.hfdi); + return ret; +} + + +/*********************************************************************** + * DllMain + * + * PARAMS + * hinstDLL [I] handle to the DLL's instance + * fdwReason [I] + * lpvReserved [I] reserved, must be NULL + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + if (!GetVersionExW(&OsVersionInfo)) + return FALSE; + break; + case DLL_PROCESS_DETACH: + UnloadCABINETDll(); + break; + } + + return TRUE; +} diff --git a/reactos/lib/setupapi/setupx.spec b/reactos/lib/setupapi/setupx.spec index be6bcfd640f..2c40e0e4031 100644 --- a/reactos/lib/setupapi/setupx.spec +++ b/reactos/lib/setupapi/setupx.spec @@ -1,269 +1,269 @@ -1 stub WEP -2 pascal -ret16 IpOpen(str ptr) IpOpen16 -3 stub IpOpenAppend #(str word) -4 pascal -ret16 IpClose(word) IpClose16 -5 stub IpGetLongField #(word ptr word ptr) -6 stub IpGetStringField #(word ptr word ptr word ptr) -7 stub IpFindFirstLine #(word str str ptr) -8 stub IpGetLineCount #(word str ptr) -9 stub IpGetFieldCount #(word ptr ptr) -10 stub IpGetIntField #(word ptr word ptr) -11 stub IpFindNextLine #(word ptr) -12 stub IpGetFileName #(word ptr word) -13 pascal -ret16 VcpQueueCopy(str str str str word word ptr word long) VcpQueueCopy16 -14 stub NOAUTORUNWNDPROC -15 stub __DEBUGMSG -16 stub __ASSERTMSG -17 pascal -ret16 VcpQueueDelete(str str word long) VcpQueueDelete16 -18 stub TpOpenFile #(str ptr word) -19 stub TpCloseFile #(word) -20 stub TpOpenSection #(word ptr str word) -21 stub TpCloseSection #(word) -22 stub TpCommitSection #(word word str word) -23 stub TpGetLine #(word str str word word ptr) -24 stub TpGetNextLine #(word str str ptr) -25 stub TpInsertLine #(word str str word word word) -26 stub TpReplaceLine #(word str str word word word) -27 stub TpDeleteLine #(word word word word) -28 stub TpMoveLine #(word word word word word word) -29 stub TpGetLineContents #(word ptr word ptr word word word) -30 stub TpGetPrivateProfileString #(str str str ptr word str) -31 stub TpWritePrivateProfileString #(str str str str) -32 stub TpGetProfileString #(str str str ptr word) -33 pascal -ret16 CtlSetLdd(ptr) CtlSetLdd16 -34 pascal -ret16 CtlGetLdd(ptr) CtlGetLdd16 -35 pascal -ret16 CtlFindLdd(ptr) CtlFindLdd16 -36 pascal -ret16 CtlAddLdd(ptr) CtlAddLdd16 -37 pascal -ret16 CtlDelLdd(word) CtlDelLdd16 -38 pascal -ret16 CtlGetLddPath(word ptr) CtlGetLddPath16 -39 stub SURegCloseKey #(word) -40 stub SURegCreateKey #(word) -41 stub SURegDeleteKey #(word str) -42 stub SURegDeleteValue #(word str) -43 stub SURegEnumKey #(word long ptr long) -44 stub SURegEnumValue #(word long str ptr ptr ptr ptr ptr) -45 stub SURegFlush #() -46 stub SURegInit #() -47 pascal SURegOpenKey(word str ptr) SURegOpenKey -48 stub SURegQueryValue -49 stub SURegQueryValue16 #(word str ptr ptr) -50 pascal SURegQueryValueEx(long str ptr ptr ptr ptr) SURegQueryValueEx -51 stub SURegSetValue -52 stub SURegSetValue16 #(word str long ptr long) -53 stub SURegSetValueEx #(word str long long ptr long) -54 stub SURegSaveKey #(word str ptr) -55 stub SURegLoadKey #(word str str) -56 stub SURegUnLoadKey #(word str) -60 stub DiskInfoFromLdid #(word ptr) -61 pascal suErrorToIds(word word) suErrorToIds16 -62 pascal -ret16 TPWriteProfileString(str str str) TPWriteProfileString16 -63 stub SURPLSETUP -# does SUSTORELDIDPATH set the path of an LDID in the registry ? -64 stub SUSTORELDIDPATH -65 stub WILDCARDSTRCMPI -101 pascal -ret16 GenInstall(word str word) GenInstall16 -102 stub GenWinInitRename #(str str word) -103 pascal GenFormStrWithoutPlaceHolders(str str word) GenFormStrWithoutPlaceHolders16 -104 stub SETUPX -105 stub CfgSetupMerge #(word) -106 stub INITDEPENDANTLDIDS -107 stub CFGOBJFINDKEYCMD -108 stub GenSURegSetValueEx -109 stub GENINSTALLWITHQUEUE -110 stub GenInstallEx #(word str word word ptr long) -111 stub GenCopyLogConfig2Reg #(word word str) -112 stub SUGetSetSetupFlags #(ptr word) -114 stub CFGPARSELINE # returns array -115 stub CFGSETAUTOPROCESS -116 stub CFGOBJTOSTR -117 stub CFGLNTOOBJ -118 stub MATCHCMDEXT -119 stub IpFindNextMatchLine #(word str ptr) -120 stub P_SETDEFAULTOPTION -121 stub CFGCLEANBOOT -122 stub CFGMATCHCMDEXT -123 stub CFGWASFILEUPDATED -124 stub AUTOMATCHCMDEXT -125 stub P_VALIDATEOC -126 stub GENMAPROOTREGSTR2KEY -127 stub P_CDROMOC -128 stub P_MEDIAOC -129 stub CFGCLEAN1STBOOT -130 stub suFormatMessage -131 stub suvFormatMessage #(word str str word ptr) -132 stub suFormatMessageBox -#133 stub suHelp # W98SE conflict !! -135 stub suHelp #(word word) -#135 stub P_WEBTVOC # W98SE conflict !! -136 stub P_WBEMOC -137 stub P_THEMESOC -138 stub P_IMAGINGOC -139 stub P_SCHEMESOC -140 stub suVerConflict #(word ptr word ptr) -141 stub suVerConflictInit #(word) -142 stub suVerConflictTerm #(ptr) -# Emergency Boot Disk -143 stub suCreateEBD #(word ptr long) -144 stub suCopyToEBD -145 stub sxIsMSDOS7Running #() -150 stub DS_INIT -151 stub DS_DESTROY -152 stub DS_SSYNCDRIVES -153 stub DS_GETDRIVEDATA -154 stub DS_ADDSECTION -155 stub DS_ENABLESECTION -156 stub DS_DISABLESECTION -157 stub DS_SETSWAPSIZE -158 stub DS_SETREQUIREDPAD -159 stub DS_SETAVAILABLEPAD -160 stub SXUPDATEDS -170 stub SUSETMEM -171 stub WriteDMFBootData #(word ptr word) -200 pascal VcpOpen(segptr ptr) VcpOpen16 -201 pascal VcpClose(word str) VcpClose16 -202 pascal -ret16 vcpDefCallbackProc(ptr word word long long) vcpDefCallbackProc16 -203 stub vcpEnumFiles #(ptr long) -204 pascal -ret16 VcpQueueRename(str str str str word word long) VcpQueueRename16 -205 pascal -ret16 vsmGetStringName(word ptr word) vsmGetStringName16 -206 pascal -ret16 vsmStringDelete(word) vsmStringDelete16 -207 pascal -ret16 vsmStringAdd(str) vsmStringAdd16 -208 pascal vsmGetStringRawName(word) vsmGetStringRawName16 -209 stub IpSaveRestorePosition #(word word) -210 pascal -ret16 IpGetProfileString(word str str ptr word) IpGetProfileString16 -211 stub IpOpenEx #(str ptr word) -212 stub IpOpenAppendEx #(str word word) -213 pascal -ret16 vcpUICallbackProc(ptr word word long long) vcpUICallbackProc16 -214 stub VcpAddMRUPath #(str) -300 pascal -ret16 DiBuildCompatDrvList (ptr) DiBuildCompatDrvList16 -301 pascal -ret16 DiBuildClassDrvList (ptr) DiBuildClassDrvList16 -302 stub DiDestroyDriverNodeList #(ptr) -303 pascal -ret16 DiCreateDeviceInfo (ptr str long long str str word) DiCreateDeviceInfo16 -304 pascal -ret16 DiGetClassDevs(ptr str word word) DiGetClassDevs16 -305 pascal -ret16 DiDestroyDeviceInfoList (ptr) DiDestroyDeviceInfoList16 -306 stub DiRemoveDevice #(ptr) -308 pascal -ret16 DiCallClassInstaller (word ptr) DiCallClassInstaller16 -309 stub DiCreateDriverNode #(ptr word word word str str str str str str long) -310 stub DiDrawMiniIcon -311 stub DiGetClassBitmapIndex #(str ptr) -312 stub DiSelectDevice #(ptr) -313 stub DiInstallDevice #(ptr) -314 stub DiLoadClassIcon #(str ptr ptr) -315 stub DiAskForOEMDisk #(ptr) -316 stub Display_SetMode #(ptr word word word) -317 stub Display_ClassInstaller #(word ptr) -318 pascal -ret16 DiCreateDevRegKey (ptr ptr word str word) DiCreateDevRegKey16 -319 pascal -ret16 DiOpenDevRegKey (ptr ptr word) DiOpenDevRegKey16 -320 stub DiInstallDrvSection #(str str str str long) -321 stub DiInstallClass #(str long) -322 stub DiOpenClassRegKey #(ptr str) -323 stub Display_SetFontSize #(str) -324 stub Display_OpenFontSizeKey #(ptr) -325 stub DiBuildClassDrvListFromOldInf #(ptr str ptr long) -326 stub DiIsThereNeedToCopy #(word long) -333 stub DiChangeState #(ptr long long long) -334 stub WALKSUBTREE -340 stub GetFctn #(word str str ptr ptr) -341 stub DiBuildClassInfoList #(ptr) -342 stub DiDestroyClassInfoList #(ptr) -343 stub DiGetDeviceClassInfo #(ptr ptr) -344 pascal -ret16 DiDeleteDevRegKey (ptr word) DiDeleteDevRegKey16 -350 stub DiSelectOEMDrv #(word ptr) -351 stub DiGetINFClass #(str word str long) -353 stub DIPICKBESTDRIVER -355 stub COPYINFFILE -360 stub GenInfLCToDevNode #(word str word word long) -361 stub GETDOSMESSAGE -362 stub Mouse_ClassInstaller #(word ptr) -363 stub sxCompareDosAppVer #(str str) -364 stub MONITOR_CLASSINSTALLER -365 stub FCEGETRESDESOFFSET -366 stub FCEGETALLOCVALUE -367 stub FCEADDRESDES -368 stub FCEDELETERESDES -369 stub FCEINIT -370 stub FCEGETRESDES -371 stub FCEGETFIRSTVALUE -372 stub FCEGETOTHERVALUE -373 stub FCEGETVALIDATEVALUE -374 stub FCEWRITETHISFORCEDCONFIGNOW -375 stub SUCreatePropertySheetPage #(ptr) -376 stub SUDestroyPropertySheetPage #(word) -377 stub SUPropertySheet #(ptr) -380 stub DiReadRegLogConf #(ptr str ptr ptr) -381 stub DiReadRegConf #(ptr ptr ptr long) -390 stub DiBuildPotentialDuplicatesList #(ptr ptr long ptr ptr) -395 stub InitSubstrData #(ptr str) -396 stub GetFirstSubstr #(ptr) -397 stub GetNextSubstr #(ptr) -398 stub INITSUBSTRDATAEX -400 stub bIsFileInVMM32 #(str) -401 stub DiInstallDriverFiles #(ptr) -405 stub DiBuildClassInfoListEx #(ptr long) -406 stub DiGetClassDevsEx #(ptr str str word word) -407 stub DiCopyRegSubKeyValue #(word str str str) -408 stub IPGETDRIVERDATE -409 stub IPGETDRIVERVERSION -410 stub IpGetVersionString #(str str ptr word str) -411 pascal VcpExplain(ptr long) VcpExplain16 -412 stub DiBuildDriverIndex #(word) -413 stub DiAddSingleInfToDrvIdx #(str word word) -414 stub FCEGETFLAGS -450 stub UiMakeDlgNonBold #(word) -451 stub UiDeleteNonBoldFont #(word) -500 stub SUEBDPAGE -501 stub SUOCPAGE -502 stub SXLISTSUBPROC -503 stub SXFILLLB -504 stub SXOCPAGEDLG -506 stub SXOCBATCHSETTINGS -507 stub SXOCFIXNEEDS -508 pascal -ret16 CtlSetLddPath(word str) CtlSetLddPath16 -509 stub SXCALLOCPROC -510 stub BUILDINFOCS -511 stub BUILDREGOCS -512 stub DELETEOCS -520 stub DiBuildClassDrvInfoList #(ptr) -521 stub DiBuildCompatDrvInfoList #(ptr) -522 stub DiDestroyDrvInfoList #(ptr) -523 stub DiConvertDriverInfoToDriverNode #(ptr ptr) -524 stub DISELECTBESTCOMPATDRV -525 stub FirstBootMoveToDOSSTART #(str word) -526 stub DOSOptEnableCurCfg #(str) -527 pascal -ret16 InstallHinfSection(word word str word) InstallHinfSection16 -528 stub SXMAKEUNCPATH -529 stub SXISSBSSERVERFILE -530 stub SXFINDBATCHFILES -531 stub ISPANEUROPEAN -532 stub UPGRADENIGGLINGS -533 stub DISPLAY_ISSECONDDISPLAY -534 stub ISWINDOWSFILE -540 stub VERIFYSELECTEDDRIVER -575 stub SXCALLMIGRATIONDLLS -576 stub SXCALLMIGRATIONDLLS_RUNDLL -600 stub PidConstruct #(str str str word) -601 stub PidValidate #(str str) -602 stub GETJAPANESEKEYBOARDTYPE -610 stub CRC32COMPUTE -621 stub SXSAVEINFO -622 stub SXADDPAGEEX -623 stub OPKREMOVEINSTALLEDNETDEVICE -640 stub DOFIRSTRUNSCREENS -700 stub SXSHOWREBOOTDLG -701 stub SXSHOWREBOOTDLG_RUNDLL -750 stub UIPOSITIONDIALOG -775 stub ASPICLEAN -800 stub EXTRACTCABFILE -825 stub PIDGEN3 -826 stub GETSETUPINFO -827 stub SETSETUPINFO -828 stub GETKEYBOARDOPTIONS -829 stub GETLOCALEOPTIONS -830 stub SETINTLOPTIONS -831 stub GETPRODUCTTYPE -832 stub ISOPKMODE -833 stub AUDITONETIMEINSTALL -834 stub DISKDUP -835 stub OPKPREINSTALL -836 stub ISAUDITMODE -837 stub ISAUDITAUTO -838 stub GETVALIDEULA +1 stub WEP +2 pascal -ret16 IpOpen(str ptr) IpOpen16 +3 stub IpOpenAppend #(str word) +4 pascal -ret16 IpClose(word) IpClose16 +5 stub IpGetLongField #(word ptr word ptr) +6 stub IpGetStringField #(word ptr word ptr word ptr) +7 stub IpFindFirstLine #(word str str ptr) +8 stub IpGetLineCount #(word str ptr) +9 stub IpGetFieldCount #(word ptr ptr) +10 stub IpGetIntField #(word ptr word ptr) +11 stub IpFindNextLine #(word ptr) +12 stub IpGetFileName #(word ptr word) +13 pascal -ret16 VcpQueueCopy(str str str str word word ptr word long) VcpQueueCopy16 +14 stub NOAUTORUNWNDPROC +15 stub __DEBUGMSG +16 stub __ASSERTMSG +17 pascal -ret16 VcpQueueDelete(str str word long) VcpQueueDelete16 +18 stub TpOpenFile #(str ptr word) +19 stub TpCloseFile #(word) +20 stub TpOpenSection #(word ptr str word) +21 stub TpCloseSection #(word) +22 stub TpCommitSection #(word word str word) +23 stub TpGetLine #(word str str word word ptr) +24 stub TpGetNextLine #(word str str ptr) +25 stub TpInsertLine #(word str str word word word) +26 stub TpReplaceLine #(word str str word word word) +27 stub TpDeleteLine #(word word word word) +28 stub TpMoveLine #(word word word word word word) +29 stub TpGetLineContents #(word ptr word ptr word word word) +30 stub TpGetPrivateProfileString #(str str str ptr word str) +31 stub TpWritePrivateProfileString #(str str str str) +32 stub TpGetProfileString #(str str str ptr word) +33 pascal -ret16 CtlSetLdd(ptr) CtlSetLdd16 +34 pascal -ret16 CtlGetLdd(ptr) CtlGetLdd16 +35 pascal -ret16 CtlFindLdd(ptr) CtlFindLdd16 +36 pascal -ret16 CtlAddLdd(ptr) CtlAddLdd16 +37 pascal -ret16 CtlDelLdd(word) CtlDelLdd16 +38 pascal -ret16 CtlGetLddPath(word ptr) CtlGetLddPath16 +39 stub SURegCloseKey #(word) +40 stub SURegCreateKey #(word) +41 stub SURegDeleteKey #(word str) +42 stub SURegDeleteValue #(word str) +43 stub SURegEnumKey #(word long ptr long) +44 stub SURegEnumValue #(word long str ptr ptr ptr ptr ptr) +45 stub SURegFlush #() +46 stub SURegInit #() +47 pascal SURegOpenKey(word str ptr) SURegOpenKey +48 stub SURegQueryValue +49 stub SURegQueryValue16 #(word str ptr ptr) +50 pascal SURegQueryValueEx(long str ptr ptr ptr ptr) SURegQueryValueEx +51 stub SURegSetValue +52 stub SURegSetValue16 #(word str long ptr long) +53 stub SURegSetValueEx #(word str long long ptr long) +54 stub SURegSaveKey #(word str ptr) +55 stub SURegLoadKey #(word str str) +56 stub SURegUnLoadKey #(word str) +60 stub DiskInfoFromLdid #(word ptr) +61 pascal suErrorToIds(word word) suErrorToIds16 +62 pascal -ret16 TPWriteProfileString(str str str) TPWriteProfileString16 +63 stub SURPLSETUP +# does SUSTORELDIDPATH set the path of an LDID in the registry ? +64 stub SUSTORELDIDPATH +65 stub WILDCARDSTRCMPI +101 pascal -ret16 GenInstall(word str word) GenInstall16 +102 stub GenWinInitRename #(str str word) +103 pascal GenFormStrWithoutPlaceHolders(str str word) GenFormStrWithoutPlaceHolders16 +104 stub SETUPX +105 stub CfgSetupMerge #(word) +106 stub INITDEPENDANTLDIDS +107 stub CFGOBJFINDKEYCMD +108 stub GenSURegSetValueEx +109 stub GENINSTALLWITHQUEUE +110 stub GenInstallEx #(word str word word ptr long) +111 stub GenCopyLogConfig2Reg #(word word str) +112 stub SUGetSetSetupFlags #(ptr word) +114 stub CFGPARSELINE # returns array +115 stub CFGSETAUTOPROCESS +116 stub CFGOBJTOSTR +117 stub CFGLNTOOBJ +118 stub MATCHCMDEXT +119 stub IpFindNextMatchLine #(word str ptr) +120 stub P_SETDEFAULTOPTION +121 stub CFGCLEANBOOT +122 stub CFGMATCHCMDEXT +123 stub CFGWASFILEUPDATED +124 stub AUTOMATCHCMDEXT +125 stub P_VALIDATEOC +126 stub GENMAPROOTREGSTR2KEY +127 stub P_CDROMOC +128 stub P_MEDIAOC +129 stub CFGCLEAN1STBOOT +130 stub suFormatMessage +131 stub suvFormatMessage #(word str str word ptr) +132 stub suFormatMessageBox +#133 stub suHelp # W98SE conflict !! +135 stub suHelp #(word word) +#135 stub P_WEBTVOC # W98SE conflict !! +136 stub P_WBEMOC +137 stub P_THEMESOC +138 stub P_IMAGINGOC +139 stub P_SCHEMESOC +140 stub suVerConflict #(word ptr word ptr) +141 stub suVerConflictInit #(word) +142 stub suVerConflictTerm #(ptr) +# Emergency Boot Disk +143 stub suCreateEBD #(word ptr long) +144 stub suCopyToEBD +145 stub sxIsMSDOS7Running #() +150 stub DS_INIT +151 stub DS_DESTROY +152 stub DS_SSYNCDRIVES +153 stub DS_GETDRIVEDATA +154 stub DS_ADDSECTION +155 stub DS_ENABLESECTION +156 stub DS_DISABLESECTION +157 stub DS_SETSWAPSIZE +158 stub DS_SETREQUIREDPAD +159 stub DS_SETAVAILABLEPAD +160 stub SXUPDATEDS +170 stub SUSETMEM +171 stub WriteDMFBootData #(word ptr word) +200 pascal VcpOpen(segptr ptr) VcpOpen16 +201 pascal VcpClose(word str) VcpClose16 +202 pascal -ret16 vcpDefCallbackProc(ptr word word long long) vcpDefCallbackProc16 +203 stub vcpEnumFiles #(ptr long) +204 pascal -ret16 VcpQueueRename(str str str str word word long) VcpQueueRename16 +205 pascal -ret16 vsmGetStringName(word ptr word) vsmGetStringName16 +206 pascal -ret16 vsmStringDelete(word) vsmStringDelete16 +207 pascal -ret16 vsmStringAdd(str) vsmStringAdd16 +208 pascal vsmGetStringRawName(word) vsmGetStringRawName16 +209 stub IpSaveRestorePosition #(word word) +210 pascal -ret16 IpGetProfileString(word str str ptr word) IpGetProfileString16 +211 stub IpOpenEx #(str ptr word) +212 stub IpOpenAppendEx #(str word word) +213 pascal -ret16 vcpUICallbackProc(ptr word word long long) vcpUICallbackProc16 +214 stub VcpAddMRUPath #(str) +300 pascal -ret16 DiBuildCompatDrvList (ptr) DiBuildCompatDrvList16 +301 pascal -ret16 DiBuildClassDrvList (ptr) DiBuildClassDrvList16 +302 stub DiDestroyDriverNodeList #(ptr) +303 pascal -ret16 DiCreateDeviceInfo (ptr str long long str str word) DiCreateDeviceInfo16 +304 pascal -ret16 DiGetClassDevs(ptr str word word) DiGetClassDevs16 +305 pascal -ret16 DiDestroyDeviceInfoList (ptr) DiDestroyDeviceInfoList16 +306 stub DiRemoveDevice #(ptr) +308 pascal -ret16 DiCallClassInstaller (word ptr) DiCallClassInstaller16 +309 stub DiCreateDriverNode #(ptr word word word str str str str str str long) +310 stub DiDrawMiniIcon +311 stub DiGetClassBitmapIndex #(str ptr) +312 stub DiSelectDevice #(ptr) +313 stub DiInstallDevice #(ptr) +314 stub DiLoadClassIcon #(str ptr ptr) +315 stub DiAskForOEMDisk #(ptr) +316 stub Display_SetMode #(ptr word word word) +317 stub Display_ClassInstaller #(word ptr) +318 pascal -ret16 DiCreateDevRegKey (ptr ptr word str word) DiCreateDevRegKey16 +319 pascal -ret16 DiOpenDevRegKey (ptr ptr word) DiOpenDevRegKey16 +320 stub DiInstallDrvSection #(str str str str long) +321 stub DiInstallClass #(str long) +322 stub DiOpenClassRegKey #(ptr str) +323 stub Display_SetFontSize #(str) +324 stub Display_OpenFontSizeKey #(ptr) +325 stub DiBuildClassDrvListFromOldInf #(ptr str ptr long) +326 stub DiIsThereNeedToCopy #(word long) +333 stub DiChangeState #(ptr long long long) +334 stub WALKSUBTREE +340 stub GetFctn #(word str str ptr ptr) +341 stub DiBuildClassInfoList #(ptr) +342 stub DiDestroyClassInfoList #(ptr) +343 stub DiGetDeviceClassInfo #(ptr ptr) +344 pascal -ret16 DiDeleteDevRegKey (ptr word) DiDeleteDevRegKey16 +350 stub DiSelectOEMDrv #(word ptr) +351 stub DiGetINFClass #(str word str long) +353 stub DIPICKBESTDRIVER +355 stub COPYINFFILE +360 stub GenInfLCToDevNode #(word str word word long) +361 stub GETDOSMESSAGE +362 stub Mouse_ClassInstaller #(word ptr) +363 stub sxCompareDosAppVer #(str str) +364 stub MONITOR_CLASSINSTALLER +365 stub FCEGETRESDESOFFSET +366 stub FCEGETALLOCVALUE +367 stub FCEADDRESDES +368 stub FCEDELETERESDES +369 stub FCEINIT +370 stub FCEGETRESDES +371 stub FCEGETFIRSTVALUE +372 stub FCEGETOTHERVALUE +373 stub FCEGETVALIDATEVALUE +374 stub FCEWRITETHISFORCEDCONFIGNOW +375 stub SUCreatePropertySheetPage #(ptr) +376 stub SUDestroyPropertySheetPage #(word) +377 stub SUPropertySheet #(ptr) +380 stub DiReadRegLogConf #(ptr str ptr ptr) +381 stub DiReadRegConf #(ptr ptr ptr long) +390 stub DiBuildPotentialDuplicatesList #(ptr ptr long ptr ptr) +395 stub InitSubstrData #(ptr str) +396 stub GetFirstSubstr #(ptr) +397 stub GetNextSubstr #(ptr) +398 stub INITSUBSTRDATAEX +400 stub bIsFileInVMM32 #(str) +401 stub DiInstallDriverFiles #(ptr) +405 stub DiBuildClassInfoListEx #(ptr long) +406 stub DiGetClassDevsEx #(ptr str str word word) +407 stub DiCopyRegSubKeyValue #(word str str str) +408 stub IPGETDRIVERDATE +409 stub IPGETDRIVERVERSION +410 stub IpGetVersionString #(str str ptr word str) +411 pascal VcpExplain(ptr long) VcpExplain16 +412 stub DiBuildDriverIndex #(word) +413 stub DiAddSingleInfToDrvIdx #(str word word) +414 stub FCEGETFLAGS +450 stub UiMakeDlgNonBold #(word) +451 stub UiDeleteNonBoldFont #(word) +500 stub SUEBDPAGE +501 stub SUOCPAGE +502 stub SXLISTSUBPROC +503 stub SXFILLLB +504 stub SXOCPAGEDLG +506 stub SXOCBATCHSETTINGS +507 stub SXOCFIXNEEDS +508 pascal -ret16 CtlSetLddPath(word str) CtlSetLddPath16 +509 stub SXCALLOCPROC +510 stub BUILDINFOCS +511 stub BUILDREGOCS +512 stub DELETEOCS +520 stub DiBuildClassDrvInfoList #(ptr) +521 stub DiBuildCompatDrvInfoList #(ptr) +522 stub DiDestroyDrvInfoList #(ptr) +523 stub DiConvertDriverInfoToDriverNode #(ptr ptr) +524 stub DISELECTBESTCOMPATDRV +525 stub FirstBootMoveToDOSSTART #(str word) +526 stub DOSOptEnableCurCfg #(str) +527 pascal -ret16 InstallHinfSection(word word str word) InstallHinfSection16 +528 stub SXMAKEUNCPATH +529 stub SXISSBSSERVERFILE +530 stub SXFINDBATCHFILES +531 stub ISPANEUROPEAN +532 stub UPGRADENIGGLINGS +533 stub DISPLAY_ISSECONDDISPLAY +534 stub ISWINDOWSFILE +540 stub VERIFYSELECTEDDRIVER +575 stub SXCALLMIGRATIONDLLS +576 stub SXCALLMIGRATIONDLLS_RUNDLL +600 stub PidConstruct #(str str str word) +601 stub PidValidate #(str str) +602 stub GETJAPANESEKEYBOARDTYPE +610 stub CRC32COMPUTE +621 stub SXSAVEINFO +622 stub SXADDPAGEEX +623 stub OPKREMOVEINSTALLEDNETDEVICE +640 stub DOFIRSTRUNSCREENS +700 stub SXSHOWREBOOTDLG +701 stub SXSHOWREBOOTDLG_RUNDLL +750 stub UIPOSITIONDIALOG +775 stub ASPICLEAN +800 stub EXTRACTCABFILE +825 stub PIDGEN3 +826 stub GETSETUPINFO +827 stub SETSETUPINFO +828 stub GETKEYBOARDOPTIONS +829 stub GETLOCALEOPTIONS +830 stub SETINTLOPTIONS +831 stub GETPRODUCTTYPE +832 stub ISOPKMODE +833 stub AUDITONETIMEINSTALL +834 stub DISKDUP +835 stub OPKPREINSTALL +836 stub ISAUDITMODE +837 stub ISAUDITAUTO +838 stub GETVALIDEULA diff --git a/reactos/lib/setupapi/setupx16.h b/reactos/lib/setupapi/setupx16.h index 4b5468fd5a3..6fd50df07e6 100644 --- a/reactos/lib/setupapi/setupx16.h +++ b/reactos/lib/setupapi/setupx16.h @@ -1,563 +1,563 @@ -/* - * Copyright 2000 Andreas Mohr for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __SETUPX16_H -#define __SETUPX16_H - -#include "wine/windef16.h" - -typedef UINT16 DI_FUNCTION16; -typedef UINT16 HINF16; -typedef UINT16 LOGDISKID16; -typedef UINT16 VHSTR; - -#define LINE_LEN 256 - -/* error codes stuff */ - -typedef UINT16 RETERR16; -#define OK 0 -#define IP_ERROR (UINT16)100 -#define TP_ERROR (UINT16)200 -#define VCP_ERROR (UINT16)300 -#define GEN_ERROR (UINT16)400 -#define DI_ERROR (UINT16)500 - -enum { - ERR_IP_INVALID_FILENAME = IP_ERROR+1, - ERR_IP_ALLOC_ERR, - ERR_IP_INVALID_SECT_NAME, - ERR_IP_OUT_OF_HANDLES, - ERR_IP_INF_NOT_FOUND, - ERR_IP_INVALID_INFFILE, - ERR_IP_INVALID_HINF, - ERR_IP_INVALID_FIELD, - ERR_IP_SECT_NOT_FOUND, - ERR_IP_END_OF_SECTION, - ERR_IP_PROFILE_NOT_FOUND, - ERR_IP_LINE_NOT_FOUND, - ERR_IP_FILEREAD, - ERR_IP_TOOMANYINFFILES, - ERR_IP_INVALID_SAVERESTORE, - ERR_IP_INVALID_INFTYPE -}; - -/****** virtual copy operations ******/ - -typedef DWORD LPEXPANDVTBL; - -typedef struct { - DWORD dwSoFar; - DWORD dwTotal; -} VCPPROGRESS, *LPVCPPROGRESS; - -typedef struct { - WORD cbSize; - LOGDISKID16 ldid; - VHSTR vhstrRoot; - VHSTR vhstrVolumeLabel; - VHSTR vhstrDiskName; - WORD wVolumeTime; - WORD wVolumeDate; - DWORD dwSerialNumber; - WORD fl; - LPARAM lparamRef; - - VCPPROGRESS prgFileRead; - VCPPROGRESS prgByteRead; - - VCPPROGRESS prgFileWrite; - VCPPROGRESS prgByteWrite; -} VCPDISKINFO, *LPVCPDISKINFO; - -typedef struct { - LOGDISKID16 ldid; - VHSTR vhstrDir; - VHSTR vhstrFileName; -} VCPFILESPEC, *LPVCPFILESPEC; - -typedef struct { - UINT16 uiMDate; - UINT16 uiMTime; - UINT16 uiADate; - UINT16 uiATime; - UINT16 uiAttr; - DWORD llenIn; - DWORD llenOut; -} VCPFATTR, *LPVCPFATTR; - -typedef struct { - UINT16 uDate; - UINT16 uTime; - DWORD dwSize; -} VCPFILESTAT, *LPVCPFILESTAT; - -typedef struct -{ - HFILE16 hFileSrc; - HFILE16 hFileDst; - VCPFATTR fAttr; - WORD dosError; - VHSTR vhstrFileName; - WPARAM vcpm; -} VIRTNODEEX, *LPVIRTNODEEX; - -typedef struct { - WORD cbSize; - VCPFILESPEC vfsSrc; - VCPFILESPEC vfsDst; - WORD fl; - LPARAM lParam; - LPEXPANDVTBL lpExpandVtbl; - LPVIRTNODEEX lpvnex; - VHSTR vhstrDstFinalName; - VCPFILESTAT vFileStat; -} VIRTNODE, *LPVIRTNODE; - -typedef struct { - WORD cbSize; - VCPPROGRESS prgDiskRead; - VCPPROGRESS prgFileRead; - VCPPROGRESS prgByteRead; - - VCPPROGRESS prgDiskWrite; - VCPPROGRESS prgFileWrite; - VCPPROGRESS prgByteWrite; - - LPVCPDISKINFO lpvdiIn; - LPVCPDISKINFO lpvdiOut; - LPVIRTNODE lpvn; -} VCPSTATUS, *LPVCPSTATUS; - -#define CNFL_BACKUP 0x0001 -#define CNFL_DELETEONFAILURE 0x0002 -#define CNFL_RENAMEONSUCCESS 0x0004 -#define CNFL_CONTINUATION 0x0008 -#define CNFL_SKIPPED 0x0010 -#define CNFL_IGNOREERRORS 0x0020 -#define CNFL_RETRYFILE 0x0040 -#define CNFL_COPIED 0x0080 -#define VNFL_UNIQUE 0x0000 -#define VNFL_MULTIPLEOK 0x0100 -#define VNFL_DESTROYOLD 0x0200 -#define VNFL_COPY 0x0000 -#define VNFL_DELETE 0x0800 -#define VNFL_RENAME 0x1000 -#define VNFL_NODE_TYPE (VNFL_RENAME|VNFL_DELETE|VNFL_COPY) -#define VNFL_CREATED 0x2000 -#define VNFL_REJECTED 0x4000 -#define VNFL_DEVICEINSTALLER 0x8000 - -enum { - ERR_VCP_IOFAIL = VCP_ERROR+1, - ERR_VCP_STRINGTOOLONG, - ERR_VCP_NOMEM, - ERR_VCP_QUEUEFULL, - ERR_VCP_NOVHSTR, - ERR_VCP_OVERFLOW, - ERR_VCP_BADARG, - ERR_VCP_UNINIT, - ERR_VCP_NOTFOUND, - ERR_VCP_BUSY, - ERR_VCP_INTERRUPTED, - ERR_VCP_BADDEST, - ERR_VCP_SKIPPED, - ERR_VCP_IO, - ERR_VCP_LOCKED, - ERR_VCP_WRONGDISK, - ERR_VCP_CHANGEMODE, - ERR_VCP_LDDINVALID, - ERR_VCP_LDDFIND, - ERR_VCP_LDDUNINIT, - ERR_VCP_LDDPATH_INVALID, - ERR_VCP_NOEXPANSION, - ERR_VCP_NOTOPEN, - ERR_VCP_NO_DIGITAL_SIGNATURE_CATALOG, - ERR_VCP_NO_DIGITAL_SIGNATURE_FILE -}; - -#define VCPN_OK 0 -#define VCPN_PROCEED 0 -#define VCPN_ABORT -1 -#define VCPN_RETRY -2 -#define VCPN_IGNORE -3 -#define VCPN_SKIP -4 -#define VCPN_FORCE -5 -#define VCPN_DEFER -6 -#define VCPN_FAIL -7 -#define VCPN_RETRYFILE -8 - -#define VCPFL_ABANDON 0x00 -#define VCPFL_BACKUP 0x01 -#define VCPFL_COPY 0x02 -#define VCPFL_BACKUPANDCOPY (VCPFL_BACKUP|VCPFL_COPY) -#define VCPFL_INSPECIFIEDORDER 0x04 -#define VCPFL_DELETE 0x08 -#define VCPFL_RENAME 0x10 -#define VCPFL_ALL (VCPFL_COPY|VCPFL_DELETE|VCPFL_RENAME) - -#define CFNL_BACKUP 0x0001 -#define CFNL_DELETEONFAILURE 0x0002 -#define CFNL_RENAMEONSUCCESS 0x0004 -#define CFNL_CONTINUATION 0x0008 -#define CFNL_SKIPPED 0x0010 -#define CFNL_IGNOREERRORS 0x0020 -#define CFNL_RETRYFILE 0x0040 -#define CFNL_COPIED 0x0080 -#define VFNL_MULTIPLEOK 0x0100 -#define VFNL_DESTROYOLD 0x0200 -#define VFNL_NOW 0x0400 -#define VFNL_COPY 0x0000 -#define VFNL_DELETE 0x0800 -#define VFNL_RENAME 0x1000 -#define VFNL_CREATED 0x2000 -#define VFNL_REJECTED 0x4000 -#define VCPM_DISKCLASS 0x01 -#define VCPM_DISKFIRST 0x0100 -#define VCPM_DISKLAST 0x01ff - -enum { - VCPM_DISKCREATEINFO = VCPM_DISKFIRST, - VCPM_DISKGETINFO, - VCPM_DISKDESTROYINFO, - VCPM_DISKPREPINFO, - VCPM_DISKENSURE, - VCPM_DISKPROMPT, - VCPM_DISKFORMATBEGIN, - VCPM_DISKFORMATTING, - VCPM_DISKFORMATEND -}; - -#define VCPM_FILEINCLASS 0x02 -#define VCPM_FILEOUTCLASS 0x03 -#define VCPM_FILEFIRSTIN 0x0200 -#define VCPM_FILEFIRSTOUT 0x0300 -#define VCPM_FILELAST 0x03ff - -enum { - VCPM_FILEOPENIN = VCPM_FILEFIRSTIN, - VCPM_FILEGETFATTR, - VCPM_FILECLOSEIN, - VCPM_FILECOPY, - VCPM_FILENEEDED, - - VCPM_FILEOPENOUT = VCPM_FILEFIRSTOUT, - VCPM_FILESETFATTR, - VCPM_FILECLOSEOUT, - VCPM_FILEFINALIZE, - VCPM_FILEDELETE, - VCPM_FILERENAME -}; - -#define VCPM_NODECLASS 0x04 -#define VCPM_NODEFIRST 0x0400 -#define VCPM_NODELAST 0x04ff - -enum { - VCPM_NODECREATE = VCPM_NODEFIRST, - VCPM_NODEACCEPT, - VCPM_NODEREJECT, - VCPM_NODEDESTROY, - VCPM_NODECHANGEDESTDIR, - VCPM_NODECOMPARE -}; - -#define VCPM_TALLYCLASS 0x05 -#define VCPM_TALLYFIRST 0x0500 -#define VCPM_TALLYLAST 0x05ff - -enum { - VCPM_TALLYSTART = VCPM_TALLYFIRST, - VCPM_TALLYEND, - VCPM_TALLYFILE, - VCPM_TALLYDISK -}; - -#define VCPM_VERCLASS 0x06 -#define VCPM_VERFIRST 0x0600 -#define VCPM_VERLAST 0x06ff - -enum { - VCPM_VERCHECK = VCPM_VERFIRST, - VCPM_VERCHECKDONE, - VCPM_VERRESOLVECONFLICT -}; - -#define VCPM_VSTATCLASS 0x07 -#define VCPM_VSTATFIRST 0x0700 -#define VCPM_VSTATLAST 0x07ff - -enum { - VCPM_VSTATSTART = VCPM_VSTATFIRST, - VCPM_VSTATEND, - VCPM_VSTATREAD, - VCPM_VSTATWRITE, - VCPM_VSTATNEWDISK, - VCPM_VSTATCLOSESTART, - VCPM_VSTATCLOSEEND, - VCPM_VSTATBACKUPSTART, - VCPM_VSTATBACKUPEND, - VCPM_VSTATRENAMESTART, - VCPM_VSTATRENAMEEND, - VCPM_VSTATCOPYSTART, - VCPM_VSTATCOPYEND, - VCPM_VSTATDELETESTART, - VCPM_VSTATDELETEEND, - VCPM_VSTATPATHCHECKSTART, - VCPM_VSTATPATHCHECKEND, - VCPM_VSTATCERTIFYSTART, - VCPM_VSTATCERTIFYEND, - VCPM_VSTATUSERABORT, - VCPM_VSTATYIELD -}; - -#define VCPM_PATHCLASS 0x08 -#define VCPM_PATHFIRST 0x0800 -#define VCPM_PATHLAST 0x08ff - -enum { - VCPM_BUILDPATH = VCPM_PATHFIRST, - VCPM_UNIQUEPATH, - VCPM_CHECKPATH -}; - -#define VCPM_PATCHCLASS 0x09 -#define VCPM_PATCHFIRST 0x0900 -#define VCPM_PATCHLAST 0x09ff - -enum { - VCPM_FILEPATCHBEFORECPY = VCPM_PATCHFIRST, - VCPM_FILEPATCHAFTERCPY, - VCPM_FILEPATCHINFOPEN, - VCPM_FILEPATCHINFCLOSE -}; - -#define VCPM_CERTCLASS 0x0a -#define VCPM_CERTFIRST 0x0a00 -#define VCPM_CERTLAST 0x0aff - -enum { - VCPM_FILECERTIFY = VCPM_CERTFIRST, - VCPM_FILECERTIFYWARN -}; - -typedef LRESULT (CALLBACK *VIFPROC)(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, LPARAM lParam, LPARAM lparamRef); - -typedef int (CALLBACK *VCPENUMPROC)(LPVIRTNODE lpvn, LPARAM lparamRef); - -RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef); - -/* VcpQueueCopy flags */ -#define VNLP_SYSCRITICAL 0x0001 -#define VNLP_SETUPCRITICAL 0x0002 -#define VNLP_NOVERCHECK 0x0004 -#define VNLP_FORCETEMP 0x0008 -#define VNLP_IFEXISTS 0x0010 -#define VNLP_KEEPNEWER 0x0020 -#define VNLP_PATCHIFEXIST 0x0040 -#define VNLP_NOPATCH 0x0080 -#define VNLP_CATALOGCERT 0x0100 -#define VNLP_NEEDCERTIFY 0x0200 -#define VNLP_COPYIFEXISTS 0x0400 - -RETERR16 WINAPI VcpQueueCopy16( - LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName, - LPCSTR lpszSrcDir, LPCSTR lpszDstDir, - LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst, - LPEXPANDVTBL lpExpandVtbl, - WORD fl, LPARAM lParam -); -RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest); -RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest); - -/* VcpExplain flags */ -enum { - VCPEX_SRC_DISK, - VCPEX_SRC_CABINET, - VCPEX_SRC_LOCN, - VCPEX_DST_LOCN, - VCPEX_SRC_FILE, - VCPEX_DST_FILE, - VCPEX_DST_FILE_FINAL, - VCPEX_DOS_ERROR, - VCPEX_MESSAGE, - VCPEX_DOS_SOLUTION, - VCPEX_SRC_FULL, - VCPEX_DST_FULL, - VCPEX_DST_FULL_FINAL -}; - -LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat); - -/****** logical disk management ******/ - -typedef struct _LOGDISKDESC_S { /* ldd */ - WORD cbSize; /* struct size */ - LOGDISKID16 ldid; /* logical disk ID */ - LPSTR pszPath; /* path this descriptor points to */ - LPSTR pszVolLabel; /* volume label of the disk related to it */ - LPSTR pszDiskName; /* name of this disk */ - WORD wVolTime; /* modification time of volume label */ - WORD wVolDate; /* modification date */ - DWORD dwSerNum; /* serial number of disk */ - WORD wFlags; -} LOGDISKDESC_S, *LPLOGDISKDESC; - -/** logical disk identifiers (LDID) **/ - -/* predefined LDIDs */ -#define LDID_PREDEF_START 0x0001 -#define LDID_PREDEF_END 0x7fff - -/* registry-assigned LDIDs */ -#define LDID_VAR_START 0x7000 -#define LDID_VAR_END 0x7fff - -/* dynamically assigned LDIDs */ -#define LDID_ASSIGN_START 0x8000 -#define LDID_ASSIGN_END 0xbfff - -#define LDID_NULL 0 -#define LDID_ABSOLUTE ((UINT)-1) -#define LDID_SRCPATH 1 /* setup source path */ -#define LDID_SETUPTEMP 2 /* setup temp dir */ -#define LDID_UNINSTALL 3 /* uninstall dir */ -#define LDID_BACKUP 4 /* backup dir */ -#define LDID_SETUPSCRATCH 5 /* setup scratch dir */ -#define LDID_WIN 10 /* win dir */ -#define LDID_SYS 11 /* win system dir */ -#define LDID_IOS 12 /* win Iosubsys dir */ -#define LDID_CMD 13 /* win command dir */ -#define LDID_CPL 14 /* win control panel dir */ -#define LDID_PRINT 15 /* win printer dir */ -#define LDID_MAIL 16 /* win mail dir */ -#define LDID_INF 17 /* win inf dir */ -#define LDID_HELP 18 /* win help dir */ -#define LDID_WINADMIN 19 /* admin dir */ -#define LDID_FONTS 20 /* win fonts dir */ -#define LDID_VIEWERS 21 /* win viewers dir */ -#define LDID_VMM32 22 /* win VMM32 dir */ -#define LDID_COLOR 23 /* win color mngment dir */ -#define LDID_APPS 24 /* win apps dir */ -#define LDID_SHARED 25 /* win shared dir */ -#define LDID_WINBOOT 26 /* guaranteed win boot drive */ -#define LDID_MACHINE 27 /* machine specific files */ -#define LDID_HOST_WINBOOT 28 -#define LDID_BOOT 30 /* boot drive root dir */ -#define LDID_BOOT_HOST 31 /* boot drive host root dir */ -#define LDID_OLD_WINBOOT 32 /* root subdir */ -#define LDID_OLD_WIN 33 /* old windows dir */ - -/* flags for GenInstall() */ -#define GENINSTALL_DO_FILES 1 -#define GENINSTALL_DO_INI 2 -#define GENINSTALL_DO_REG 4 -#define GENINSTALL_DO_INI2REG 8 -#define GENINSTALL_DO_CFGAUTO 16 -#define GENINSTALL_DO_LOGCONFIG 32 -#define GENINSTALL_DO_REGSRCPATH 64 -#define GENINSTALL_DO_PERUSER 128 - -#define GEINISTALL_DO_INIREG 14 -#define GENINSTALL_DO_ALL 255 - -/* - * flags for InstallHinfSection() - * 128 can be added, too. This means that the .inf file is provided by you - * instead of being a 32 bit file (i.e. Windows .inf file). - * In this case all files you install must be in the same dir - * as your .inf file on the install disk. - */ -#define HOW_NEVER_REBOOT 0 -#define HOW_ALWAYS_SILENT_REBOOT 1 -#define HOW_ALWAYS_PROMPT_REBOOT 2 -#define HOW_SILENT_REBOOT 3 -#define HOW_PROMPT_REBOOT 4 - -/****** device installation stuff ******/ - -#define MAX_CLASS_NAME_LEN 32 -#define MAX_DEVNODE_ID_LEN 256 -#define MAX_GUID_STR 50 - -typedef struct _DEVICE_INFO -{ - UINT16 cbSize; - struct _DEVICE_INFO *lpNextDi; - char szDescription[LINE_LEN]; - DWORD dnDevnode; - HKEY hRegKey; - char szRegSubkey[MAX_DEVNODE_ID_LEN]; - char szClassName[MAX_CLASS_NAME_LEN]; - DWORD Flags; - HWND16 hwndParent; - /*LPDRIVER_NODE*/ LPVOID lpCompatDrvList; - /*LPDRIVER_NODE*/ LPVOID lpClassDrvList; - /*LPDRIVER_NODE*/ LPVOID lpSelectedDriver; - ATOM atDriverPath; - ATOM atTempInfFile; - HINSTANCE16 hinstClassInstaller; - HINSTANCE16 hinstClassPropProvidor; - HINSTANCE16 hinstDevicePropProvidor; - HINSTANCE16 hinstBasicPropProvidor; - FARPROC16 fpClassInstaller; - FARPROC16 fpClassEnumPropPages; - FARPROC16 fpDeviceEnumPropPages; - FARPROC16 fpEnumBasicProperties; - DWORD dwSetupReserved; - DWORD dwClassInstallReserved; - /*GENCALLBACKPROC*/ LPVOID gicpGenInstallCallBack; - - LPARAM gicplParam; - UINT16 InfType; - - HINSTANCE16 hinstPrivateProblemHandler; - FARPROC16 fpPrivateProblemHandler; - LPARAM lpClassInstallParams; - struct _DEVICE_INFO *lpdiChildList; - DWORD dwFlagsEx; - /*LPDRIVER_INFO*/ LPVOID lpCompatDrvInfoList; - /*LPDRIVER_INFO*/ LPVOID lpClassDrvInfoList; - char szClassGUID[MAX_GUID_STR]; -} DEVICE_INFO16, *LPDEVICE_INFO16, **LPLPDEVICE_INFO16; - - -extern void WINAPI GenFormStrWithoutPlaceHolders16(LPSTR,LPCSTR,HINF16); -extern RETERR16 WINAPI IpOpen16(LPCSTR,HINF16 *); -extern RETERR16 WINAPI IpClose16(HINF16); -extern RETERR16 WINAPI CtlSetLdd16(LPLOGDISKDESC); -extern RETERR16 WINAPI CtlGetLdd16(LPLOGDISKDESC); -extern RETERR16 WINAPI CtlFindLdd16(LPLOGDISKDESC); -extern RETERR16 WINAPI CtlAddLdd16(LPLOGDISKDESC); -extern RETERR16 WINAPI CtlDelLdd16(LOGDISKID16); -extern RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath); -extern RETERR16 WINAPI GenInstall16(HINF16,LPCSTR,WORD); - -typedef struct tagLDD_LIST { - LPLOGDISKDESC pldd; - struct tagLDD_LIST *next; -} LDD_LIST; - -#define INIT_LDD(ldd, LDID) \ - do { \ - memset(&(ldd), 0, sizeof(LOGDISKDESC_S)); \ - (ldd).cbSize = sizeof(LOGDISKDESC_S); \ - ldd.ldid = LDID; \ - } while(0) - -#endif /* __SETUPX16_H */ +/* + * Copyright 2000 Andreas Mohr for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SETUPX16_H +#define __SETUPX16_H + +#include "wine/windef16.h" + +typedef UINT16 DI_FUNCTION16; +typedef UINT16 HINF16; +typedef UINT16 LOGDISKID16; +typedef UINT16 VHSTR; + +#define LINE_LEN 256 + +/* error codes stuff */ + +typedef UINT16 RETERR16; +#define OK 0 +#define IP_ERROR (UINT16)100 +#define TP_ERROR (UINT16)200 +#define VCP_ERROR (UINT16)300 +#define GEN_ERROR (UINT16)400 +#define DI_ERROR (UINT16)500 + +enum { + ERR_IP_INVALID_FILENAME = IP_ERROR+1, + ERR_IP_ALLOC_ERR, + ERR_IP_INVALID_SECT_NAME, + ERR_IP_OUT_OF_HANDLES, + ERR_IP_INF_NOT_FOUND, + ERR_IP_INVALID_INFFILE, + ERR_IP_INVALID_HINF, + ERR_IP_INVALID_FIELD, + ERR_IP_SECT_NOT_FOUND, + ERR_IP_END_OF_SECTION, + ERR_IP_PROFILE_NOT_FOUND, + ERR_IP_LINE_NOT_FOUND, + ERR_IP_FILEREAD, + ERR_IP_TOOMANYINFFILES, + ERR_IP_INVALID_SAVERESTORE, + ERR_IP_INVALID_INFTYPE +}; + +/****** virtual copy operations ******/ + +typedef DWORD LPEXPANDVTBL; + +typedef struct { + DWORD dwSoFar; + DWORD dwTotal; +} VCPPROGRESS, *LPVCPPROGRESS; + +typedef struct { + WORD cbSize; + LOGDISKID16 ldid; + VHSTR vhstrRoot; + VHSTR vhstrVolumeLabel; + VHSTR vhstrDiskName; + WORD wVolumeTime; + WORD wVolumeDate; + DWORD dwSerialNumber; + WORD fl; + LPARAM lparamRef; + + VCPPROGRESS prgFileRead; + VCPPROGRESS prgByteRead; + + VCPPROGRESS prgFileWrite; + VCPPROGRESS prgByteWrite; +} VCPDISKINFO, *LPVCPDISKINFO; + +typedef struct { + LOGDISKID16 ldid; + VHSTR vhstrDir; + VHSTR vhstrFileName; +} VCPFILESPEC, *LPVCPFILESPEC; + +typedef struct { + UINT16 uiMDate; + UINT16 uiMTime; + UINT16 uiADate; + UINT16 uiATime; + UINT16 uiAttr; + DWORD llenIn; + DWORD llenOut; +} VCPFATTR, *LPVCPFATTR; + +typedef struct { + UINT16 uDate; + UINT16 uTime; + DWORD dwSize; +} VCPFILESTAT, *LPVCPFILESTAT; + +typedef struct +{ + HFILE16 hFileSrc; + HFILE16 hFileDst; + VCPFATTR fAttr; + WORD dosError; + VHSTR vhstrFileName; + WPARAM vcpm; +} VIRTNODEEX, *LPVIRTNODEEX; + +typedef struct { + WORD cbSize; + VCPFILESPEC vfsSrc; + VCPFILESPEC vfsDst; + WORD fl; + LPARAM lParam; + LPEXPANDVTBL lpExpandVtbl; + LPVIRTNODEEX lpvnex; + VHSTR vhstrDstFinalName; + VCPFILESTAT vFileStat; +} VIRTNODE, *LPVIRTNODE; + +typedef struct { + WORD cbSize; + VCPPROGRESS prgDiskRead; + VCPPROGRESS prgFileRead; + VCPPROGRESS prgByteRead; + + VCPPROGRESS prgDiskWrite; + VCPPROGRESS prgFileWrite; + VCPPROGRESS prgByteWrite; + + LPVCPDISKINFO lpvdiIn; + LPVCPDISKINFO lpvdiOut; + LPVIRTNODE lpvn; +} VCPSTATUS, *LPVCPSTATUS; + +#define CNFL_BACKUP 0x0001 +#define CNFL_DELETEONFAILURE 0x0002 +#define CNFL_RENAMEONSUCCESS 0x0004 +#define CNFL_CONTINUATION 0x0008 +#define CNFL_SKIPPED 0x0010 +#define CNFL_IGNOREERRORS 0x0020 +#define CNFL_RETRYFILE 0x0040 +#define CNFL_COPIED 0x0080 +#define VNFL_UNIQUE 0x0000 +#define VNFL_MULTIPLEOK 0x0100 +#define VNFL_DESTROYOLD 0x0200 +#define VNFL_COPY 0x0000 +#define VNFL_DELETE 0x0800 +#define VNFL_RENAME 0x1000 +#define VNFL_NODE_TYPE (VNFL_RENAME|VNFL_DELETE|VNFL_COPY) +#define VNFL_CREATED 0x2000 +#define VNFL_REJECTED 0x4000 +#define VNFL_DEVICEINSTALLER 0x8000 + +enum { + ERR_VCP_IOFAIL = VCP_ERROR+1, + ERR_VCP_STRINGTOOLONG, + ERR_VCP_NOMEM, + ERR_VCP_QUEUEFULL, + ERR_VCP_NOVHSTR, + ERR_VCP_OVERFLOW, + ERR_VCP_BADARG, + ERR_VCP_UNINIT, + ERR_VCP_NOTFOUND, + ERR_VCP_BUSY, + ERR_VCP_INTERRUPTED, + ERR_VCP_BADDEST, + ERR_VCP_SKIPPED, + ERR_VCP_IO, + ERR_VCP_LOCKED, + ERR_VCP_WRONGDISK, + ERR_VCP_CHANGEMODE, + ERR_VCP_LDDINVALID, + ERR_VCP_LDDFIND, + ERR_VCP_LDDUNINIT, + ERR_VCP_LDDPATH_INVALID, + ERR_VCP_NOEXPANSION, + ERR_VCP_NOTOPEN, + ERR_VCP_NO_DIGITAL_SIGNATURE_CATALOG, + ERR_VCP_NO_DIGITAL_SIGNATURE_FILE +}; + +#define VCPN_OK 0 +#define VCPN_PROCEED 0 +#define VCPN_ABORT -1 +#define VCPN_RETRY -2 +#define VCPN_IGNORE -3 +#define VCPN_SKIP -4 +#define VCPN_FORCE -5 +#define VCPN_DEFER -6 +#define VCPN_FAIL -7 +#define VCPN_RETRYFILE -8 + +#define VCPFL_ABANDON 0x00 +#define VCPFL_BACKUP 0x01 +#define VCPFL_COPY 0x02 +#define VCPFL_BACKUPANDCOPY (VCPFL_BACKUP|VCPFL_COPY) +#define VCPFL_INSPECIFIEDORDER 0x04 +#define VCPFL_DELETE 0x08 +#define VCPFL_RENAME 0x10 +#define VCPFL_ALL (VCPFL_COPY|VCPFL_DELETE|VCPFL_RENAME) + +#define CFNL_BACKUP 0x0001 +#define CFNL_DELETEONFAILURE 0x0002 +#define CFNL_RENAMEONSUCCESS 0x0004 +#define CFNL_CONTINUATION 0x0008 +#define CFNL_SKIPPED 0x0010 +#define CFNL_IGNOREERRORS 0x0020 +#define CFNL_RETRYFILE 0x0040 +#define CFNL_COPIED 0x0080 +#define VFNL_MULTIPLEOK 0x0100 +#define VFNL_DESTROYOLD 0x0200 +#define VFNL_NOW 0x0400 +#define VFNL_COPY 0x0000 +#define VFNL_DELETE 0x0800 +#define VFNL_RENAME 0x1000 +#define VFNL_CREATED 0x2000 +#define VFNL_REJECTED 0x4000 +#define VCPM_DISKCLASS 0x01 +#define VCPM_DISKFIRST 0x0100 +#define VCPM_DISKLAST 0x01ff + +enum { + VCPM_DISKCREATEINFO = VCPM_DISKFIRST, + VCPM_DISKGETINFO, + VCPM_DISKDESTROYINFO, + VCPM_DISKPREPINFO, + VCPM_DISKENSURE, + VCPM_DISKPROMPT, + VCPM_DISKFORMATBEGIN, + VCPM_DISKFORMATTING, + VCPM_DISKFORMATEND +}; + +#define VCPM_FILEINCLASS 0x02 +#define VCPM_FILEOUTCLASS 0x03 +#define VCPM_FILEFIRSTIN 0x0200 +#define VCPM_FILEFIRSTOUT 0x0300 +#define VCPM_FILELAST 0x03ff + +enum { + VCPM_FILEOPENIN = VCPM_FILEFIRSTIN, + VCPM_FILEGETFATTR, + VCPM_FILECLOSEIN, + VCPM_FILECOPY, + VCPM_FILENEEDED, + + VCPM_FILEOPENOUT = VCPM_FILEFIRSTOUT, + VCPM_FILESETFATTR, + VCPM_FILECLOSEOUT, + VCPM_FILEFINALIZE, + VCPM_FILEDELETE, + VCPM_FILERENAME +}; + +#define VCPM_NODECLASS 0x04 +#define VCPM_NODEFIRST 0x0400 +#define VCPM_NODELAST 0x04ff + +enum { + VCPM_NODECREATE = VCPM_NODEFIRST, + VCPM_NODEACCEPT, + VCPM_NODEREJECT, + VCPM_NODEDESTROY, + VCPM_NODECHANGEDESTDIR, + VCPM_NODECOMPARE +}; + +#define VCPM_TALLYCLASS 0x05 +#define VCPM_TALLYFIRST 0x0500 +#define VCPM_TALLYLAST 0x05ff + +enum { + VCPM_TALLYSTART = VCPM_TALLYFIRST, + VCPM_TALLYEND, + VCPM_TALLYFILE, + VCPM_TALLYDISK +}; + +#define VCPM_VERCLASS 0x06 +#define VCPM_VERFIRST 0x0600 +#define VCPM_VERLAST 0x06ff + +enum { + VCPM_VERCHECK = VCPM_VERFIRST, + VCPM_VERCHECKDONE, + VCPM_VERRESOLVECONFLICT +}; + +#define VCPM_VSTATCLASS 0x07 +#define VCPM_VSTATFIRST 0x0700 +#define VCPM_VSTATLAST 0x07ff + +enum { + VCPM_VSTATSTART = VCPM_VSTATFIRST, + VCPM_VSTATEND, + VCPM_VSTATREAD, + VCPM_VSTATWRITE, + VCPM_VSTATNEWDISK, + VCPM_VSTATCLOSESTART, + VCPM_VSTATCLOSEEND, + VCPM_VSTATBACKUPSTART, + VCPM_VSTATBACKUPEND, + VCPM_VSTATRENAMESTART, + VCPM_VSTATRENAMEEND, + VCPM_VSTATCOPYSTART, + VCPM_VSTATCOPYEND, + VCPM_VSTATDELETESTART, + VCPM_VSTATDELETEEND, + VCPM_VSTATPATHCHECKSTART, + VCPM_VSTATPATHCHECKEND, + VCPM_VSTATCERTIFYSTART, + VCPM_VSTATCERTIFYEND, + VCPM_VSTATUSERABORT, + VCPM_VSTATYIELD +}; + +#define VCPM_PATHCLASS 0x08 +#define VCPM_PATHFIRST 0x0800 +#define VCPM_PATHLAST 0x08ff + +enum { + VCPM_BUILDPATH = VCPM_PATHFIRST, + VCPM_UNIQUEPATH, + VCPM_CHECKPATH +}; + +#define VCPM_PATCHCLASS 0x09 +#define VCPM_PATCHFIRST 0x0900 +#define VCPM_PATCHLAST 0x09ff + +enum { + VCPM_FILEPATCHBEFORECPY = VCPM_PATCHFIRST, + VCPM_FILEPATCHAFTERCPY, + VCPM_FILEPATCHINFOPEN, + VCPM_FILEPATCHINFCLOSE +}; + +#define VCPM_CERTCLASS 0x0a +#define VCPM_CERTFIRST 0x0a00 +#define VCPM_CERTLAST 0x0aff + +enum { + VCPM_FILECERTIFY = VCPM_CERTFIRST, + VCPM_FILECERTIFYWARN +}; + +typedef LRESULT (CALLBACK *VIFPROC)(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, LPARAM lParam, LPARAM lparamRef); + +typedef int (CALLBACK *VCPENUMPROC)(LPVIRTNODE lpvn, LPARAM lparamRef); + +RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef); + +/* VcpQueueCopy flags */ +#define VNLP_SYSCRITICAL 0x0001 +#define VNLP_SETUPCRITICAL 0x0002 +#define VNLP_NOVERCHECK 0x0004 +#define VNLP_FORCETEMP 0x0008 +#define VNLP_IFEXISTS 0x0010 +#define VNLP_KEEPNEWER 0x0020 +#define VNLP_PATCHIFEXIST 0x0040 +#define VNLP_NOPATCH 0x0080 +#define VNLP_CATALOGCERT 0x0100 +#define VNLP_NEEDCERTIFY 0x0200 +#define VNLP_COPYIFEXISTS 0x0400 + +RETERR16 WINAPI VcpQueueCopy16( + LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName, + LPCSTR lpszSrcDir, LPCSTR lpszDstDir, + LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst, + LPEXPANDVTBL lpExpandVtbl, + WORD fl, LPARAM lParam +); +RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest); +RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest); + +/* VcpExplain flags */ +enum { + VCPEX_SRC_DISK, + VCPEX_SRC_CABINET, + VCPEX_SRC_LOCN, + VCPEX_DST_LOCN, + VCPEX_SRC_FILE, + VCPEX_DST_FILE, + VCPEX_DST_FILE_FINAL, + VCPEX_DOS_ERROR, + VCPEX_MESSAGE, + VCPEX_DOS_SOLUTION, + VCPEX_SRC_FULL, + VCPEX_DST_FULL, + VCPEX_DST_FULL_FINAL +}; + +LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat); + +/****** logical disk management ******/ + +typedef struct _LOGDISKDESC_S { /* ldd */ + WORD cbSize; /* struct size */ + LOGDISKID16 ldid; /* logical disk ID */ + LPSTR pszPath; /* path this descriptor points to */ + LPSTR pszVolLabel; /* volume label of the disk related to it */ + LPSTR pszDiskName; /* name of this disk */ + WORD wVolTime; /* modification time of volume label */ + WORD wVolDate; /* modification date */ + DWORD dwSerNum; /* serial number of disk */ + WORD wFlags; +} LOGDISKDESC_S, *LPLOGDISKDESC; + +/** logical disk identifiers (LDID) **/ + +/* predefined LDIDs */ +#define LDID_PREDEF_START 0x0001 +#define LDID_PREDEF_END 0x7fff + +/* registry-assigned LDIDs */ +#define LDID_VAR_START 0x7000 +#define LDID_VAR_END 0x7fff + +/* dynamically assigned LDIDs */ +#define LDID_ASSIGN_START 0x8000 +#define LDID_ASSIGN_END 0xbfff + +#define LDID_NULL 0 +#define LDID_ABSOLUTE ((UINT)-1) +#define LDID_SRCPATH 1 /* setup source path */ +#define LDID_SETUPTEMP 2 /* setup temp dir */ +#define LDID_UNINSTALL 3 /* uninstall dir */ +#define LDID_BACKUP 4 /* backup dir */ +#define LDID_SETUPSCRATCH 5 /* setup scratch dir */ +#define LDID_WIN 10 /* win dir */ +#define LDID_SYS 11 /* win system dir */ +#define LDID_IOS 12 /* win Iosubsys dir */ +#define LDID_CMD 13 /* win command dir */ +#define LDID_CPL 14 /* win control panel dir */ +#define LDID_PRINT 15 /* win printer dir */ +#define LDID_MAIL 16 /* win mail dir */ +#define LDID_INF 17 /* win inf dir */ +#define LDID_HELP 18 /* win help dir */ +#define LDID_WINADMIN 19 /* admin dir */ +#define LDID_FONTS 20 /* win fonts dir */ +#define LDID_VIEWERS 21 /* win viewers dir */ +#define LDID_VMM32 22 /* win VMM32 dir */ +#define LDID_COLOR 23 /* win color mngment dir */ +#define LDID_APPS 24 /* win apps dir */ +#define LDID_SHARED 25 /* win shared dir */ +#define LDID_WINBOOT 26 /* guaranteed win boot drive */ +#define LDID_MACHINE 27 /* machine specific files */ +#define LDID_HOST_WINBOOT 28 +#define LDID_BOOT 30 /* boot drive root dir */ +#define LDID_BOOT_HOST 31 /* boot drive host root dir */ +#define LDID_OLD_WINBOOT 32 /* root subdir */ +#define LDID_OLD_WIN 33 /* old windows dir */ + +/* flags for GenInstall() */ +#define GENINSTALL_DO_FILES 1 +#define GENINSTALL_DO_INI 2 +#define GENINSTALL_DO_REG 4 +#define GENINSTALL_DO_INI2REG 8 +#define GENINSTALL_DO_CFGAUTO 16 +#define GENINSTALL_DO_LOGCONFIG 32 +#define GENINSTALL_DO_REGSRCPATH 64 +#define GENINSTALL_DO_PERUSER 128 + +#define GEINISTALL_DO_INIREG 14 +#define GENINSTALL_DO_ALL 255 + +/* + * flags for InstallHinfSection() + * 128 can be added, too. This means that the .inf file is provided by you + * instead of being a 32 bit file (i.e. Windows .inf file). + * In this case all files you install must be in the same dir + * as your .inf file on the install disk. + */ +#define HOW_NEVER_REBOOT 0 +#define HOW_ALWAYS_SILENT_REBOOT 1 +#define HOW_ALWAYS_PROMPT_REBOOT 2 +#define HOW_SILENT_REBOOT 3 +#define HOW_PROMPT_REBOOT 4 + +/****** device installation stuff ******/ + +#define MAX_CLASS_NAME_LEN 32 +#define MAX_DEVNODE_ID_LEN 256 +#define MAX_GUID_STR 50 + +typedef struct _DEVICE_INFO +{ + UINT16 cbSize; + struct _DEVICE_INFO *lpNextDi; + char szDescription[LINE_LEN]; + DWORD dnDevnode; + HKEY hRegKey; + char szRegSubkey[MAX_DEVNODE_ID_LEN]; + char szClassName[MAX_CLASS_NAME_LEN]; + DWORD Flags; + HWND16 hwndParent; + /*LPDRIVER_NODE*/ LPVOID lpCompatDrvList; + /*LPDRIVER_NODE*/ LPVOID lpClassDrvList; + /*LPDRIVER_NODE*/ LPVOID lpSelectedDriver; + ATOM atDriverPath; + ATOM atTempInfFile; + HINSTANCE16 hinstClassInstaller; + HINSTANCE16 hinstClassPropProvidor; + HINSTANCE16 hinstDevicePropProvidor; + HINSTANCE16 hinstBasicPropProvidor; + FARPROC16 fpClassInstaller; + FARPROC16 fpClassEnumPropPages; + FARPROC16 fpDeviceEnumPropPages; + FARPROC16 fpEnumBasicProperties; + DWORD dwSetupReserved; + DWORD dwClassInstallReserved; + /*GENCALLBACKPROC*/ LPVOID gicpGenInstallCallBack; + + LPARAM gicplParam; + UINT16 InfType; + + HINSTANCE16 hinstPrivateProblemHandler; + FARPROC16 fpPrivateProblemHandler; + LPARAM lpClassInstallParams; + struct _DEVICE_INFO *lpdiChildList; + DWORD dwFlagsEx; + /*LPDRIVER_INFO*/ LPVOID lpCompatDrvInfoList; + /*LPDRIVER_INFO*/ LPVOID lpClassDrvInfoList; + char szClassGUID[MAX_GUID_STR]; +} DEVICE_INFO16, *LPDEVICE_INFO16, **LPLPDEVICE_INFO16; + + +extern void WINAPI GenFormStrWithoutPlaceHolders16(LPSTR,LPCSTR,HINF16); +extern RETERR16 WINAPI IpOpen16(LPCSTR,HINF16 *); +extern RETERR16 WINAPI IpClose16(HINF16); +extern RETERR16 WINAPI CtlSetLdd16(LPLOGDISKDESC); +extern RETERR16 WINAPI CtlGetLdd16(LPLOGDISKDESC); +extern RETERR16 WINAPI CtlFindLdd16(LPLOGDISKDESC); +extern RETERR16 WINAPI CtlAddLdd16(LPLOGDISKDESC); +extern RETERR16 WINAPI CtlDelLdd16(LOGDISKID16); +extern RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath); +extern RETERR16 WINAPI GenInstall16(HINF16,LPCSTR,WORD); + +typedef struct tagLDD_LIST { + LPLOGDISKDESC pldd; + struct tagLDD_LIST *next; +} LDD_LIST; + +#define INIT_LDD(ldd, LDID) \ + do { \ + memset(&(ldd), 0, sizeof(LOGDISKDESC_S)); \ + (ldd).cbSize = sizeof(LOGDISKDESC_S); \ + ldd.ldid = LDID; \ + } while(0) + +#endif /* __SETUPX16_H */ diff --git a/reactos/lib/setupapi/setupx_main.c b/reactos/lib/setupapi/setupx_main.c index 8610ce0a55f..df1a5daadc0 100644 --- a/reactos/lib/setupapi/setupx_main.c +++ b/reactos/lib/setupapi/setupx_main.c @@ -1,615 +1,615 @@ -/* - * SETUPX library - * - * Copyright 1998,2000 Andreas Mohr - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * FIXME: Rather non-functional functions for now. - * - * See: - * http://www.geocities.com/SiliconValley/Network/5317/drivers.html - * http://willemer.de/informatik/windows/inf_info.htm (German) - * http://www.microsoft.com/ddk/ddkdocs/win98ddk/devinst_12uw.htm - * DDK: setupx.h - * http://mmatrix.tripod.com/customsystemfolder/infsysntaxfull.html - * http://www.rdrop.com/~cary/html/inf_faq.html - * http://support.microsoft.com/support/kb/articles/q194/6/40.asp - * - * Stuff tested with: - * - rs405deu.exe (German Acroread 4.05 setup) - * - ie5setup.exe - * - Netmeeting - * - * FIXME: - * - string handling is... weird ;) (buflen etc.) - * - memory leaks ? - * - separate that mess (but probably only when it's done completely) - * - * SETUPX consists of several parts with the following acronyms/prefixes: - * Di device installer (devinst.c ?) - * Gen generic installer (geninst.c ?) - * Ip .INF parsing (infparse.c) - * LDD logical device descriptor (ldd.c ?) - * LDID logical device ID - * SU setup (setup.c ?) - * Tp text processing (textproc.c ?) - * Vcp virtual copy module (vcp.c ?) - * ... - * - * The SETUPX DLL is NOT thread-safe. That's why many installers urge you to - * "close all open applications". - * All in all the design of it seems to be a bit weak. - * Not sure whether my implementation of it is better, though ;-) - */ - -#include -#include -#include -#include -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "winerror.h" -#include "wine/winuser16.h" -#include "wownt32.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "setupapi.h" -#include "setupx16.h" -#include "setupapi_private.h" -#include "winerror.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -#define HINSTANCE_32(h16) ((HINSTANCE)(ULONG_PTR)(h16)) - -/*********************************************************************** - * SURegOpenKey (SETUPX.47) - */ -DWORD WINAPI SURegOpenKey( HKEY hkey, LPCSTR lpszSubKey, PHKEY retkey ) -{ - FIXME("(%p,%s,%p), semi-stub.\n",hkey,debugstr_a(lpszSubKey),retkey); - return RegOpenKeyA( hkey, lpszSubKey, retkey ); -} - -/*********************************************************************** - * SURegQueryValueEx (SETUPX.50) - */ -DWORD WINAPI SURegQueryValueEx( HKEY hkey, LPSTR lpszValueName, - LPDWORD lpdwReserved, LPDWORD lpdwType, - LPBYTE lpbData, LPDWORD lpcbData ) -{ - FIXME("(%p,%s,%p,%p,%p,%ld), semi-stub.\n",hkey,debugstr_a(lpszValueName), - lpdwReserved,lpdwType,lpbData,lpcbData?*lpcbData:0); - return RegQueryValueExA( hkey, lpszValueName, lpdwReserved, lpdwType, - lpbData, lpcbData ); -} - - -/*********************************************************************** - * InstallHinfSection (SETUPX.527) - * - * hwnd = parent window - * hinst = instance of SETUPX.DLL - * lpszCmdLine = e.g. "DefaultInstall 132 C:\MYINSTALL\MYDEV.INF" - * Here "DefaultInstall" is the .inf file section to be installed (optional). - * The 132 value is made of the HOW_xxx flags and sometimes 128 (-> setupx16.h). - * - * nCmdShow = nCmdShow of CreateProcess - */ -RETERR16 WINAPI InstallHinfSection16( HWND16 hwnd, HINSTANCE16 hinst, LPCSTR lpszCmdLine, INT16 nCmdShow) -{ - InstallHinfSectionA( HWND_32(hwnd), HINSTANCE_32(hinst), lpszCmdLine, nCmdShow ); - return OK; -} - -typedef struct -{ - LPCSTR RegValName; - LPCSTR StdString; /* fallback string; sub dir of windows directory */ -} LDID_DATA; - -static const LDID_DATA LDID_Data[34] = -{ - { /* 0 (LDID_NULL) -- not defined */ - NULL, - NULL - }, - { /* 1 (LDID_SRCPATH) = source of installation. hmm, what to do here ? */ - "SourcePath", /* hmm, does SETUPX have to care about updating it ?? */ - NULL - }, - { /* 2 (LDID_SETUPTEMP) = setup temp dir */ - "SetupTempDir", - NULL - }, - { /* 3 (LDID_UNINSTALL) = uninstall backup dir */ - "UninstallDir", - NULL - }, - { /* 4 (LDID_BACKUP) = backup dir */ - "BackupDir", - NULL - }, - { /* 5 (LDID_SETUPSCRATCH) = setup scratch dir */ - "SetupScratchDir", - NULL - }, - { /* 6 -- not defined */ - NULL, - NULL - }, - { /* 7 -- not defined */ - NULL, - NULL - }, - { /* 8 -- not defined */ - NULL, - NULL - }, - { /* 9 -- not defined */ - NULL, - NULL - }, - { /* 10 (LDID_WIN) = windows dir */ - "WinDir", - "" - }, - { /* 11 (LDID_SYS) = system dir */ - "SysDir", - NULL /* call GetSystemDirectory() instead */ - }, - { /* 12 (LDID_IOS) = IOSubSys dir */ - NULL, /* FIXME: registry string ? */ - "SYSTEM\\IOSUBSYS" - }, - { /* 13 (LDID_CMD) = COMMAND dir */ - NULL, /* FIXME: registry string ? */ - "COMMAND" - }, - { /* 14 (LDID_CPL) = control panel dir */ - NULL, - "" - }, - { /* 15 (LDID_PRINT) = windows printer dir */ - NULL, - "SYSTEM" /* correct ?? */ - }, - { /* 16 (LDID_MAIL) = destination mail dir */ - NULL, - "" - }, - { /* 17 (LDID_INF) = INF dir */ - "SetupScratchDir", /* correct ? */ - "INF" - }, - { /* 18 (LDID_HELP) = HELP dir */ - NULL, /* ??? */ - "HELP" - }, - { /* 19 (LDID_WINADMIN) = Admin dir */ - "WinAdminDir", - "" - }, - { /* 20 (LDID_FONTS) = Fonts dir */ - NULL, /* ??? */ - "FONTS" - }, - { /* 21 (LDID_VIEWERS) = Viewers */ - NULL, /* ??? */ - "SYSTEM\\VIEWERS" - }, - { /* 22 (LDID_VMM32) = VMM32 dir */ - NULL, /* ??? */ - "SYSTEM\\VMM32" - }, - { /* 23 (LDID_COLOR) = ICM dir */ - "ICMPath", - "SYSTEM\\COLOR" - }, - { /* 24 (LDID_APPS) = root of boot drive ? */ - "AppsDir", - "C:\\" - }, - { /* 25 (LDID_SHARED) = shared dir */ - "SharedDir", - "" - }, - { /* 26 (LDID_WINBOOT) = Windows boot dir */ - "WinBootDir", - "" - }, - { /* 27 (LDID_MACHINE) = machine specific files */ - "MachineDir", - NULL - }, - { /* 28 (LDID_HOST_WINBOOT) = Host Windows boot dir */ - "HostWinBootDir", - NULL - }, - { /* 29 -- not defined */ - NULL, - NULL - }, - { /* 30 (LDID_BOOT) = Root of boot drive */ - "BootDir", - NULL - }, - { /* 31 (LDID_BOOT_HOST) = Root of boot drive host */ - "BootHost", - NULL - }, - { /* 32 (LDID_OLD_WINBOOT) = subdir of root */ - "OldWinBootDir", - NULL - }, - { /* 33 (LDID_OLD_WIN) = old win dir */ - "OldWinDir", - NULL - } - /* the rest (34-38) isn't too interesting, so I'll forget about it */ -}; - -/* - * LDD == Logical Device Descriptor - * LDID == Logical Device ID - * - * The whole LDD/LDID business might go into a separate file named - * ldd.c. - * At the moment I don't know what the hell these functions are really doing. - * That's why I added reporting stubs. - * The only thing I do know is that I need them for the LDD/LDID infrastructure. - * That's why I implemented them in a way that's suitable for my purpose. - */ -static LDD_LIST *pFirstLDD = NULL; - -static BOOL std_LDDs_done = FALSE; - -void SETUPX_CreateStandardLDDs(void) -{ - HKEY hKey = 0; - WORD n; - DWORD type, len; - LOGDISKDESC_S ldd; - char buffer[MAX_PATH]; - - /* has to be here, otherwise loop */ - std_LDDs_done = TRUE; - - RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup", &hKey); - - for (n=0; n < sizeof(LDID_Data)/sizeof(LDID_DATA); n++) - { - buffer[0] = '\0'; - - len = MAX_PATH; - if ( (hKey) && (LDID_Data[n].RegValName) - && (RegQueryValueExA(hKey, LDID_Data[n].RegValName, - NULL, &type, buffer, &len) == ERROR_SUCCESS) - && (type == REG_SZ) ) - { - TRACE("found value '%s' for LDID %d\n", buffer, n); - } - else - switch(n) - { - case LDID_SRCPATH: - FIXME("LDID_SRCPATH: what exactly do we have to do here ?\n"); - strcpy(buffer, "X:\\FIXME"); - break; - case LDID_SYS: - GetSystemDirectoryA(buffer, MAX_PATH); - break; - case LDID_APPS: - case LDID_MACHINE: - case LDID_HOST_WINBOOT: - case LDID_BOOT: - case LDID_BOOT_HOST: - strcpy(buffer, "C:\\"); - break; - default: - if (LDID_Data[n].StdString) - { - DWORD len = GetWindowsDirectoryA(buffer, MAX_PATH); - LPSTR p; - p = buffer + len; - *p++ = '\\'; - strcpy(p, LDID_Data[n].StdString); - } - break; - } - if (buffer[0]) - { - INIT_LDD(ldd, n); - ldd.pszPath = buffer; - TRACE("LDID %d -> '%s'\n", ldd.ldid, ldd.pszPath); - CtlSetLdd16(&ldd); - } - } - if (hKey) RegCloseKey(hKey); -} - -/*********************************************************************** - * CtlDelLdd (SETUPX.37) - * - * RETURN - * ERR_VCP_LDDINVALID if ldid < LDID_ASSIGN_START. - */ -RETERR16 SETUPX_DelLdd(LOGDISKID16 ldid) -{ - LDD_LIST *pCurr, *pPrev = NULL; - - TRACE("(%d)\n", ldid); - - if (!std_LDDs_done) - SETUPX_CreateStandardLDDs(); - - if (ldid < LDID_ASSIGN_START) - return ERR_VCP_LDDINVALID; - - pCurr = pFirstLDD; - /* search until we find the appropriate LDD or hit the end */ - while ((pCurr != NULL) && (ldid > pCurr->pldd->ldid)) - { - pPrev = pCurr; - pCurr = pCurr->next; - } - if ( (pCurr == NULL) /* hit end of list */ - || (ldid != pCurr->pldd->ldid) ) - return ERR_VCP_LDDFIND; /* correct ? */ - - /* ok, found our victim: eliminate it */ - - if (pPrev) - pPrev->next = pCurr->next; - - if (pCurr == pFirstLDD) - pFirstLDD = NULL; - HeapFree(GetProcessHeap(), 0, pCurr); - - return OK; -} - -/*********************************************************************** - * CtlDelLdd (SETUPX.37) - */ -RETERR16 WINAPI CtlDelLdd16(LOGDISKID16 ldid) -{ - FIXME("(%d); - please report this!\n", ldid); - return SETUPX_DelLdd(ldid); -} - -/*********************************************************************** - * CtlFindLdd (SETUPX.35) - * - * doesn't check pldd ptr validity: crash (W98SE) - * - * RETURN - * ERR_VCP_LDDINVALID if pldd->cbSize != structsize - * 1 in all other cases ?? - * - */ -RETERR16 WINAPI CtlFindLdd16(LPLOGDISKDESC pldd) -{ - LDD_LIST *pCurr, *pPrev = NULL; - - TRACE("(%p)\n", pldd); - - if (!std_LDDs_done) - SETUPX_CreateStandardLDDs(); - - if (pldd->cbSize != sizeof(LOGDISKDESC_S)) - return ERR_VCP_LDDINVALID; - - pCurr = pFirstLDD; - /* search until we find the appropriate LDD or hit the end */ - while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid)) - { - pPrev = pCurr; - pCurr = pCurr->next; - } - if ( (pCurr == NULL) /* hit end of list */ - || (pldd->ldid != pCurr->pldd->ldid) ) - return ERR_VCP_LDDFIND; /* correct ? */ - - memcpy(pldd, pCurr->pldd, pldd->cbSize); - /* hmm, we probably ought to strcpy() the string ptrs here */ - - return 1; /* what is this ?? */ -} - -/*********************************************************************** - * CtlSetLdd (SETUPX.33) - * - * Set an LDD entry. - * - * RETURN - * ERR_VCP_LDDINVALID if pldd.cbSize != sizeof(LOGDISKDESC_S) - * - */ -RETERR16 WINAPI CtlSetLdd16(LPLOGDISKDESC pldd) -{ - LDD_LIST *pCurr, *pPrev = NULL; - LPLOGDISKDESC pCurrLDD; - HANDLE heap; - BOOL is_new = FALSE; - - TRACE("(%p)\n", pldd); - - if (!std_LDDs_done) - SETUPX_CreateStandardLDDs(); - - if (pldd->cbSize != sizeof(LOGDISKDESC_S)) - return ERR_VCP_LDDINVALID; - - heap = GetProcessHeap(); - pCurr = pFirstLDD; - /* search until we find the appropriate LDD or hit the end */ - while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid)) - { - pPrev = pCurr; - pCurr = pCurr->next; - } - if (!pCurr || pldd->ldid != pCurr->pldd->ldid) - { - is_new = TRUE; - pCurr = HeapAlloc(heap, 0, sizeof(LDD_LIST)); - pCurr->pldd = HeapAlloc(heap, 0, sizeof(LOGDISKDESC_S)); - pCurr->next = NULL; - pCurrLDD = pCurr->pldd; - } - else - { - pCurrLDD = pCurr->pldd; - HeapFree(heap, 0, pCurrLDD->pszPath); - HeapFree(heap, 0, pCurrLDD->pszVolLabel); - HeapFree(heap, 0, pCurrLDD->pszDiskName); - } - - memcpy(pCurrLDD, pldd, sizeof(LOGDISKDESC_S)); - - if (pldd->pszPath) - { - pCurrLDD->pszPath = HeapAlloc( heap, 0, strlen(pldd->pszPath)+1 ); - strcpy( pCurrLDD->pszPath, pldd->pszPath ); - } - if (pldd->pszVolLabel) - { - pCurrLDD->pszVolLabel = HeapAlloc( heap, 0, strlen(pldd->pszVolLabel)+1 ); - strcpy( pCurrLDD->pszVolLabel, pldd->pszVolLabel ); - } - if (pldd->pszDiskName) - { - pCurrLDD->pszDiskName = HeapAlloc( heap, 0, strlen(pldd->pszDiskName)+1 ); - strcpy( pCurrLDD->pszDiskName, pldd->pszDiskName ); - } - - if (is_new) /* link into list */ - { - if (pPrev) - { - pCurr->next = pPrev->next; - pPrev->next = pCurr; - } - if (!pFirstLDD) - pFirstLDD = pCurr; - } - - return OK; -} - - -/*********************************************************************** - * CtlAddLdd (SETUPX.36) - * - * doesn't check pldd ptr validity: crash (W98SE) - * - */ -static LOGDISKID16 ldid_to_add = LDID_ASSIGN_START; -RETERR16 WINAPI CtlAddLdd16(LPLOGDISKDESC pldd) -{ - pldd->ldid = ldid_to_add++; - return CtlSetLdd16(pldd); -} - -/*********************************************************************** - * CtlGetLdd (SETUPX.34) - * - * doesn't check pldd ptr validity: crash (W98SE) - * What the !@#$%&*( is the difference between CtlFindLdd() and CtlGetLdd() ?? - * - * RETURN - * ERR_VCP_LDDINVALID if pldd->cbSize != structsize - * - */ -static RETERR16 SETUPX_GetLdd(LPLOGDISKDESC pldd) -{ - LDD_LIST *pCurr, *pPrev = NULL; - - if (!std_LDDs_done) - SETUPX_CreateStandardLDDs(); - - if (pldd->cbSize != sizeof(LOGDISKDESC_S)) - return ERR_VCP_LDDINVALID; - - pCurr = pFirstLDD; - /* search until we find the appropriate LDD or hit the end */ - while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid)) - { - pPrev = pCurr; - pCurr = pCurr->next; - } - if (pCurr == NULL) /* hit end of list */ - return ERR_VCP_LDDFIND; /* correct ? */ - - memcpy(pldd, pCurr->pldd, pldd->cbSize); - /* hmm, we probably ought to strcpy() the string ptrs here */ - - return OK; -} - -/**********************************************************************/ - -RETERR16 WINAPI CtlGetLdd16(LPLOGDISKDESC pldd) -{ - FIXME("(%p); - please report this!\n", pldd); - return SETUPX_GetLdd(pldd); -} - -/*********************************************************************** - * CtlGetLddPath (SETUPX.38) - * - * Gets the path of an LDD. - * No crash if szPath == NULL. - * szPath has to be at least MAX_PATH_LEN bytes long. - * RETURN - * ERR_VCP_LDDUNINIT if LDD for LDID not found. - */ -RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath) -{ - TRACE("(%d, %p);\n", ldid, szPath); - - if (szPath) - { - LOGDISKDESC_S ldd; - INIT_LDD(ldd, ldid); - if (CtlFindLdd16(&ldd) == ERR_VCP_LDDFIND) - return ERR_VCP_LDDUNINIT; - SETUPX_GetLdd(&ldd); - strcpy(szPath, ldd.pszPath); - TRACE("ret '%s' for LDID %d\n", szPath, ldid); - } - return OK; -} - -/*********************************************************************** - * CtlSetLddPath (SETUPX.508) - * - * Sets the path of an LDD. - * Creates LDD for LDID if not existing yet. - */ -RETERR16 WINAPI CtlSetLddPath16(LOGDISKID16 ldid, LPSTR szPath) -{ - LOGDISKDESC_S ldd; - TRACE("(%d, '%s');\n", ldid, szPath); - - SetupSetDirectoryIdA( 0, ldid, szPath ); - INIT_LDD(ldd, ldid); - ldd.pszPath = szPath; - return CtlSetLdd16(&ldd); -} +/* + * SETUPX library + * + * Copyright 1998,2000 Andreas Mohr + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * FIXME: Rather non-functional functions for now. + * + * See: + * http://www.geocities.com/SiliconValley/Network/5317/drivers.html + * http://willemer.de/informatik/windows/inf_info.htm (German) + * http://www.microsoft.com/ddk/ddkdocs/win98ddk/devinst_12uw.htm + * DDK: setupx.h + * http://mmatrix.tripod.com/customsystemfolder/infsysntaxfull.html + * http://www.rdrop.com/~cary/html/inf_faq.html + * http://support.microsoft.com/support/kb/articles/q194/6/40.asp + * + * Stuff tested with: + * - rs405deu.exe (German Acroread 4.05 setup) + * - ie5setup.exe + * - Netmeeting + * + * FIXME: + * - string handling is... weird ;) (buflen etc.) + * - memory leaks ? + * - separate that mess (but probably only when it's done completely) + * + * SETUPX consists of several parts with the following acronyms/prefixes: + * Di device installer (devinst.c ?) + * Gen generic installer (geninst.c ?) + * Ip .INF parsing (infparse.c) + * LDD logical device descriptor (ldd.c ?) + * LDID logical device ID + * SU setup (setup.c ?) + * Tp text processing (textproc.c ?) + * Vcp virtual copy module (vcp.c ?) + * ... + * + * The SETUPX DLL is NOT thread-safe. That's why many installers urge you to + * "close all open applications". + * All in all the design of it seems to be a bit weak. + * Not sure whether my implementation of it is better, though ;-) + */ + +#include +#include +#include +#include +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winerror.h" +#include "wine/winuser16.h" +#include "wownt32.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "setupx16.h" +#include "setupapi_private.h" +#include "winerror.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +#define HINSTANCE_32(h16) ((HINSTANCE)(ULONG_PTR)(h16)) + +/*********************************************************************** + * SURegOpenKey (SETUPX.47) + */ +DWORD WINAPI SURegOpenKey( HKEY hkey, LPCSTR lpszSubKey, PHKEY retkey ) +{ + FIXME("(%p,%s,%p), semi-stub.\n",hkey,debugstr_a(lpszSubKey),retkey); + return RegOpenKeyA( hkey, lpszSubKey, retkey ); +} + +/*********************************************************************** + * SURegQueryValueEx (SETUPX.50) + */ +DWORD WINAPI SURegQueryValueEx( HKEY hkey, LPSTR lpszValueName, + LPDWORD lpdwReserved, LPDWORD lpdwType, + LPBYTE lpbData, LPDWORD lpcbData ) +{ + FIXME("(%p,%s,%p,%p,%p,%ld), semi-stub.\n",hkey,debugstr_a(lpszValueName), + lpdwReserved,lpdwType,lpbData,lpcbData?*lpcbData:0); + return RegQueryValueExA( hkey, lpszValueName, lpdwReserved, lpdwType, + lpbData, lpcbData ); +} + + +/*********************************************************************** + * InstallHinfSection (SETUPX.527) + * + * hwnd = parent window + * hinst = instance of SETUPX.DLL + * lpszCmdLine = e.g. "DefaultInstall 132 C:\MYINSTALL\MYDEV.INF" + * Here "DefaultInstall" is the .inf file section to be installed (optional). + * The 132 value is made of the HOW_xxx flags and sometimes 128 (-> setupx16.h). + * + * nCmdShow = nCmdShow of CreateProcess + */ +RETERR16 WINAPI InstallHinfSection16( HWND16 hwnd, HINSTANCE16 hinst, LPCSTR lpszCmdLine, INT16 nCmdShow) +{ + InstallHinfSectionA( HWND_32(hwnd), HINSTANCE_32(hinst), lpszCmdLine, nCmdShow ); + return OK; +} + +typedef struct +{ + LPCSTR RegValName; + LPCSTR StdString; /* fallback string; sub dir of windows directory */ +} LDID_DATA; + +static const LDID_DATA LDID_Data[34] = +{ + { /* 0 (LDID_NULL) -- not defined */ + NULL, + NULL + }, + { /* 1 (LDID_SRCPATH) = source of installation. hmm, what to do here ? */ + "SourcePath", /* hmm, does SETUPX have to care about updating it ?? */ + NULL + }, + { /* 2 (LDID_SETUPTEMP) = setup temp dir */ + "SetupTempDir", + NULL + }, + { /* 3 (LDID_UNINSTALL) = uninstall backup dir */ + "UninstallDir", + NULL + }, + { /* 4 (LDID_BACKUP) = backup dir */ + "BackupDir", + NULL + }, + { /* 5 (LDID_SETUPSCRATCH) = setup scratch dir */ + "SetupScratchDir", + NULL + }, + { /* 6 -- not defined */ + NULL, + NULL + }, + { /* 7 -- not defined */ + NULL, + NULL + }, + { /* 8 -- not defined */ + NULL, + NULL + }, + { /* 9 -- not defined */ + NULL, + NULL + }, + { /* 10 (LDID_WIN) = windows dir */ + "WinDir", + "" + }, + { /* 11 (LDID_SYS) = system dir */ + "SysDir", + NULL /* call GetSystemDirectory() instead */ + }, + { /* 12 (LDID_IOS) = IOSubSys dir */ + NULL, /* FIXME: registry string ? */ + "SYSTEM\\IOSUBSYS" + }, + { /* 13 (LDID_CMD) = COMMAND dir */ + NULL, /* FIXME: registry string ? */ + "COMMAND" + }, + { /* 14 (LDID_CPL) = control panel dir */ + NULL, + "" + }, + { /* 15 (LDID_PRINT) = windows printer dir */ + NULL, + "SYSTEM" /* correct ?? */ + }, + { /* 16 (LDID_MAIL) = destination mail dir */ + NULL, + "" + }, + { /* 17 (LDID_INF) = INF dir */ + "SetupScratchDir", /* correct ? */ + "INF" + }, + { /* 18 (LDID_HELP) = HELP dir */ + NULL, /* ??? */ + "HELP" + }, + { /* 19 (LDID_WINADMIN) = Admin dir */ + "WinAdminDir", + "" + }, + { /* 20 (LDID_FONTS) = Fonts dir */ + NULL, /* ??? */ + "FONTS" + }, + { /* 21 (LDID_VIEWERS) = Viewers */ + NULL, /* ??? */ + "SYSTEM\\VIEWERS" + }, + { /* 22 (LDID_VMM32) = VMM32 dir */ + NULL, /* ??? */ + "SYSTEM\\VMM32" + }, + { /* 23 (LDID_COLOR) = ICM dir */ + "ICMPath", + "SYSTEM\\COLOR" + }, + { /* 24 (LDID_APPS) = root of boot drive ? */ + "AppsDir", + "C:\\" + }, + { /* 25 (LDID_SHARED) = shared dir */ + "SharedDir", + "" + }, + { /* 26 (LDID_WINBOOT) = Windows boot dir */ + "WinBootDir", + "" + }, + { /* 27 (LDID_MACHINE) = machine specific files */ + "MachineDir", + NULL + }, + { /* 28 (LDID_HOST_WINBOOT) = Host Windows boot dir */ + "HostWinBootDir", + NULL + }, + { /* 29 -- not defined */ + NULL, + NULL + }, + { /* 30 (LDID_BOOT) = Root of boot drive */ + "BootDir", + NULL + }, + { /* 31 (LDID_BOOT_HOST) = Root of boot drive host */ + "BootHost", + NULL + }, + { /* 32 (LDID_OLD_WINBOOT) = subdir of root */ + "OldWinBootDir", + NULL + }, + { /* 33 (LDID_OLD_WIN) = old win dir */ + "OldWinDir", + NULL + } + /* the rest (34-38) isn't too interesting, so I'll forget about it */ +}; + +/* + * LDD == Logical Device Descriptor + * LDID == Logical Device ID + * + * The whole LDD/LDID business might go into a separate file named + * ldd.c. + * At the moment I don't know what the hell these functions are really doing. + * That's why I added reporting stubs. + * The only thing I do know is that I need them for the LDD/LDID infrastructure. + * That's why I implemented them in a way that's suitable for my purpose. + */ +static LDD_LIST *pFirstLDD = NULL; + +static BOOL std_LDDs_done = FALSE; + +void SETUPX_CreateStandardLDDs(void) +{ + HKEY hKey = 0; + WORD n; + DWORD type, len; + LOGDISKDESC_S ldd; + char buffer[MAX_PATH]; + + /* has to be here, otherwise loop */ + std_LDDs_done = TRUE; + + RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup", &hKey); + + for (n=0; n < sizeof(LDID_Data)/sizeof(LDID_DATA); n++) + { + buffer[0] = '\0'; + + len = MAX_PATH; + if ( (hKey) && (LDID_Data[n].RegValName) + && (RegQueryValueExA(hKey, LDID_Data[n].RegValName, + NULL, &type, buffer, &len) == ERROR_SUCCESS) + && (type == REG_SZ) ) + { + TRACE("found value '%s' for LDID %d\n", buffer, n); + } + else + switch(n) + { + case LDID_SRCPATH: + FIXME("LDID_SRCPATH: what exactly do we have to do here ?\n"); + strcpy(buffer, "X:\\FIXME"); + break; + case LDID_SYS: + GetSystemDirectoryA(buffer, MAX_PATH); + break; + case LDID_APPS: + case LDID_MACHINE: + case LDID_HOST_WINBOOT: + case LDID_BOOT: + case LDID_BOOT_HOST: + strcpy(buffer, "C:\\"); + break; + default: + if (LDID_Data[n].StdString) + { + DWORD len = GetWindowsDirectoryA(buffer, MAX_PATH); + LPSTR p; + p = buffer + len; + *p++ = '\\'; + strcpy(p, LDID_Data[n].StdString); + } + break; + } + if (buffer[0]) + { + INIT_LDD(ldd, n); + ldd.pszPath = buffer; + TRACE("LDID %d -> '%s'\n", ldd.ldid, ldd.pszPath); + CtlSetLdd16(&ldd); + } + } + if (hKey) RegCloseKey(hKey); +} + +/*********************************************************************** + * CtlDelLdd (SETUPX.37) + * + * RETURN + * ERR_VCP_LDDINVALID if ldid < LDID_ASSIGN_START. + */ +RETERR16 SETUPX_DelLdd(LOGDISKID16 ldid) +{ + LDD_LIST *pCurr, *pPrev = NULL; + + TRACE("(%d)\n", ldid); + + if (!std_LDDs_done) + SETUPX_CreateStandardLDDs(); + + if (ldid < LDID_ASSIGN_START) + return ERR_VCP_LDDINVALID; + + pCurr = pFirstLDD; + /* search until we find the appropriate LDD or hit the end */ + while ((pCurr != NULL) && (ldid > pCurr->pldd->ldid)) + { + pPrev = pCurr; + pCurr = pCurr->next; + } + if ( (pCurr == NULL) /* hit end of list */ + || (ldid != pCurr->pldd->ldid) ) + return ERR_VCP_LDDFIND; /* correct ? */ + + /* ok, found our victim: eliminate it */ + + if (pPrev) + pPrev->next = pCurr->next; + + if (pCurr == pFirstLDD) + pFirstLDD = NULL; + HeapFree(GetProcessHeap(), 0, pCurr); + + return OK; +} + +/*********************************************************************** + * CtlDelLdd (SETUPX.37) + */ +RETERR16 WINAPI CtlDelLdd16(LOGDISKID16 ldid) +{ + FIXME("(%d); - please report this!\n", ldid); + return SETUPX_DelLdd(ldid); +} + +/*********************************************************************** + * CtlFindLdd (SETUPX.35) + * + * doesn't check pldd ptr validity: crash (W98SE) + * + * RETURN + * ERR_VCP_LDDINVALID if pldd->cbSize != structsize + * 1 in all other cases ?? + * + */ +RETERR16 WINAPI CtlFindLdd16(LPLOGDISKDESC pldd) +{ + LDD_LIST *pCurr, *pPrev = NULL; + + TRACE("(%p)\n", pldd); + + if (!std_LDDs_done) + SETUPX_CreateStandardLDDs(); + + if (pldd->cbSize != sizeof(LOGDISKDESC_S)) + return ERR_VCP_LDDINVALID; + + pCurr = pFirstLDD; + /* search until we find the appropriate LDD or hit the end */ + while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid)) + { + pPrev = pCurr; + pCurr = pCurr->next; + } + if ( (pCurr == NULL) /* hit end of list */ + || (pldd->ldid != pCurr->pldd->ldid) ) + return ERR_VCP_LDDFIND; /* correct ? */ + + memcpy(pldd, pCurr->pldd, pldd->cbSize); + /* hmm, we probably ought to strcpy() the string ptrs here */ + + return 1; /* what is this ?? */ +} + +/*********************************************************************** + * CtlSetLdd (SETUPX.33) + * + * Set an LDD entry. + * + * RETURN + * ERR_VCP_LDDINVALID if pldd.cbSize != sizeof(LOGDISKDESC_S) + * + */ +RETERR16 WINAPI CtlSetLdd16(LPLOGDISKDESC pldd) +{ + LDD_LIST *pCurr, *pPrev = NULL; + LPLOGDISKDESC pCurrLDD; + HANDLE heap; + BOOL is_new = FALSE; + + TRACE("(%p)\n", pldd); + + if (!std_LDDs_done) + SETUPX_CreateStandardLDDs(); + + if (pldd->cbSize != sizeof(LOGDISKDESC_S)) + return ERR_VCP_LDDINVALID; + + heap = GetProcessHeap(); + pCurr = pFirstLDD; + /* search until we find the appropriate LDD or hit the end */ + while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid)) + { + pPrev = pCurr; + pCurr = pCurr->next; + } + if (!pCurr || pldd->ldid != pCurr->pldd->ldid) + { + is_new = TRUE; + pCurr = HeapAlloc(heap, 0, sizeof(LDD_LIST)); + pCurr->pldd = HeapAlloc(heap, 0, sizeof(LOGDISKDESC_S)); + pCurr->next = NULL; + pCurrLDD = pCurr->pldd; + } + else + { + pCurrLDD = pCurr->pldd; + HeapFree(heap, 0, pCurrLDD->pszPath); + HeapFree(heap, 0, pCurrLDD->pszVolLabel); + HeapFree(heap, 0, pCurrLDD->pszDiskName); + } + + memcpy(pCurrLDD, pldd, sizeof(LOGDISKDESC_S)); + + if (pldd->pszPath) + { + pCurrLDD->pszPath = HeapAlloc( heap, 0, strlen(pldd->pszPath)+1 ); + strcpy( pCurrLDD->pszPath, pldd->pszPath ); + } + if (pldd->pszVolLabel) + { + pCurrLDD->pszVolLabel = HeapAlloc( heap, 0, strlen(pldd->pszVolLabel)+1 ); + strcpy( pCurrLDD->pszVolLabel, pldd->pszVolLabel ); + } + if (pldd->pszDiskName) + { + pCurrLDD->pszDiskName = HeapAlloc( heap, 0, strlen(pldd->pszDiskName)+1 ); + strcpy( pCurrLDD->pszDiskName, pldd->pszDiskName ); + } + + if (is_new) /* link into list */ + { + if (pPrev) + { + pCurr->next = pPrev->next; + pPrev->next = pCurr; + } + if (!pFirstLDD) + pFirstLDD = pCurr; + } + + return OK; +} + + +/*********************************************************************** + * CtlAddLdd (SETUPX.36) + * + * doesn't check pldd ptr validity: crash (W98SE) + * + */ +static LOGDISKID16 ldid_to_add = LDID_ASSIGN_START; +RETERR16 WINAPI CtlAddLdd16(LPLOGDISKDESC pldd) +{ + pldd->ldid = ldid_to_add++; + return CtlSetLdd16(pldd); +} + +/*********************************************************************** + * CtlGetLdd (SETUPX.34) + * + * doesn't check pldd ptr validity: crash (W98SE) + * What the !@#$%&*( is the difference between CtlFindLdd() and CtlGetLdd() ?? + * + * RETURN + * ERR_VCP_LDDINVALID if pldd->cbSize != structsize + * + */ +static RETERR16 SETUPX_GetLdd(LPLOGDISKDESC pldd) +{ + LDD_LIST *pCurr, *pPrev = NULL; + + if (!std_LDDs_done) + SETUPX_CreateStandardLDDs(); + + if (pldd->cbSize != sizeof(LOGDISKDESC_S)) + return ERR_VCP_LDDINVALID; + + pCurr = pFirstLDD; + /* search until we find the appropriate LDD or hit the end */ + while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid)) + { + pPrev = pCurr; + pCurr = pCurr->next; + } + if (pCurr == NULL) /* hit end of list */ + return ERR_VCP_LDDFIND; /* correct ? */ + + memcpy(pldd, pCurr->pldd, pldd->cbSize); + /* hmm, we probably ought to strcpy() the string ptrs here */ + + return OK; +} + +/**********************************************************************/ + +RETERR16 WINAPI CtlGetLdd16(LPLOGDISKDESC pldd) +{ + FIXME("(%p); - please report this!\n", pldd); + return SETUPX_GetLdd(pldd); +} + +/*********************************************************************** + * CtlGetLddPath (SETUPX.38) + * + * Gets the path of an LDD. + * No crash if szPath == NULL. + * szPath has to be at least MAX_PATH_LEN bytes long. + * RETURN + * ERR_VCP_LDDUNINIT if LDD for LDID not found. + */ +RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath) +{ + TRACE("(%d, %p);\n", ldid, szPath); + + if (szPath) + { + LOGDISKDESC_S ldd; + INIT_LDD(ldd, ldid); + if (CtlFindLdd16(&ldd) == ERR_VCP_LDDFIND) + return ERR_VCP_LDDUNINIT; + SETUPX_GetLdd(&ldd); + strcpy(szPath, ldd.pszPath); + TRACE("ret '%s' for LDID %d\n", szPath, ldid); + } + return OK; +} + +/*********************************************************************** + * CtlSetLddPath (SETUPX.508) + * + * Sets the path of an LDD. + * Creates LDD for LDID if not existing yet. + */ +RETERR16 WINAPI CtlSetLddPath16(LOGDISKID16 ldid, LPSTR szPath) +{ + LOGDISKDESC_S ldd; + TRACE("(%d, '%s');\n", ldid, szPath); + + SetupSetDirectoryIdA( 0, ldid, szPath ); + INIT_LDD(ldd, ldid); + ldd.pszPath = szPath; + return CtlSetLdd16(&ldd); +} diff --git a/reactos/lib/setupapi/stubs.c b/reactos/lib/setupapi/stubs.c index f0ab6e22518..fa0cbd8a498 100644 --- a/reactos/lib/setupapi/stubs.c +++ b/reactos/lib/setupapi/stubs.c @@ -1,143 +1,143 @@ -/* - * SetupAPI stubs - * - * Copyright 2000 James Hatheway - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#include "wine/debug.h" -#include "windef.h" -#include "winbase.h" -#include "wingdi.h" -#include "winuser.h" -#include "winnls.h" -#include "winreg.h" -#include "setupapi.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -/*********************************************************************** - * TPWriteProfileString (SETUPX.62) - */ -BOOL WINAPI TPWriteProfileString16( LPCSTR section, LPCSTR entry, LPCSTR string ) -{ - FIXME( "%s %s %s: stub\n", debugstr_a(section), debugstr_a(entry), debugstr_a(string) ); - return TRUE; -} - - -/*********************************************************************** - * suErrorToIds (SETUPX.61) - */ -DWORD WINAPI suErrorToIds16( WORD w1, WORD w2 ) -{ - FIXME( "%x %x: stub\n", w1, w2 ); - return 0; -} - -/*********************************************************************** - * SetupDiGetDeviceInfoListDetailA (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetDeviceInfoListDetailA(HDEVINFO devinfo, PSP_DEVINFO_LIST_DETAIL_DATA_A devinfo_data ) -{ - FIXME("\n"); - return FALSE; -} - -/*********************************************************************** - * SetupDiGetDeviceInfoListDetailW (SETUPAPI.@) - */ -BOOL WINAPI SetupDiGetDeviceInfoListDetailW(HDEVINFO devinfo, PSP_DEVINFO_LIST_DETAIL_DATA_W devinfo_data ) -{ - FIXME("\n"); - return FALSE; -} - -/*********************************************************************** - * (SETUPAPI.@) - * - * NO WINAPI in description given - */ -HDEVINFO WINAPI SetupDiGetClassDevsExA(const GUID *class, PCSTR filter, HWND parent, DWORD flags, HDEVINFO deviceset, PCSTR machine, PVOID reserved) -{ - FIXME("filter %s machine %s\n",debugstr_a(filter),debugstr_a(machine)); - return FALSE; -} - -/*********************************************************************** - * (SETUPAPI.@) - * - * NO WINAPI in description given - */ -HDEVINFO WINAPI SetupDiGetClassDevsExW(const GUID *class, PCWSTR filter, HWND parent, DWORD flags, HDEVINFO deviceset, PCWSTR machine, PVOID reserved) -{ - FIXME("\n"); - return FALSE; -} - - -/*********************************************************************** - * SetupCopyOEMInfA (SETUPAPI.@) - */ -BOOL WINAPI SetupCopyOEMInfA(PCSTR sourceinffile, PCSTR sourcemedialoc, - DWORD mediatype, DWORD copystyle, PSTR destinfname, - DWORD destnamesize, PDWORD required, - PSTR *destinfnamecomponent) -{ - FIXME("stub: source %s location %s ...\n",sourceinffile, sourcemedialoc); - return FALSE; -} - -/*********************************************************************** - * SetupGetInfInformationA (SETUPAPI.@) - */ -BOOL WINAPI SetupGetInfInformationA( LPCVOID InfSpec, DWORD SearchControl, - PSP_INF_INFORMATION ReturnBuffer, - DWORD ReturnBufferSize, PDWORD RequiredSize) -{ - FIXME("(%p, %ld, %p, %ld, %p) Stub!\n", - InfSpec, SearchControl, ReturnBuffer, ReturnBufferSize, RequiredSize ); - return TRUE; -} - -/*********************************************************************** - * SetupInitializeFileLogW(SETUPAPI.@) - */ -HANDLE WINAPI SetupInitializeFileLogW(LPWSTR LogFileName, DWORD Flags) -{ - FIXME("Stub %s, 0x%lx\n",debugstr_w(LogFileName),Flags); - return INVALID_HANDLE_VALUE; -} - -/*********************************************************************** - * SetupInitializeFileLogA(SETUPAPI.@) - */ -HANDLE WINAPI SetupInitializeFileLogA(LPSTR LogFileName, DWORD Flags) -{ - FIXME("Stub %s, 0x%lx\n",debugstr_a(LogFileName),Flags); - return INVALID_HANDLE_VALUE; -} - -/*********************************************************************** - * SetupTerminateFileLog(SETUPAPI.@) - */ -BOOL WINAPI SetupTerminateFileLog(HANDLE FileLogHandle) -{ - FIXME ("Stub %p\n",FileLogHandle); - return TRUE; -} +/* + * SetupAPI stubs + * + * Copyright 2000 James Hatheway + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "wine/debug.h" +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "setupapi.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/*********************************************************************** + * TPWriteProfileString (SETUPX.62) + */ +BOOL WINAPI TPWriteProfileString16( LPCSTR section, LPCSTR entry, LPCSTR string ) +{ + FIXME( "%s %s %s: stub\n", debugstr_a(section), debugstr_a(entry), debugstr_a(string) ); + return TRUE; +} + + +/*********************************************************************** + * suErrorToIds (SETUPX.61) + */ +DWORD WINAPI suErrorToIds16( WORD w1, WORD w2 ) +{ + FIXME( "%x %x: stub\n", w1, w2 ); + return 0; +} + +/*********************************************************************** + * SetupDiGetDeviceInfoListDetailA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceInfoListDetailA(HDEVINFO devinfo, PSP_DEVINFO_LIST_DETAIL_DATA_A devinfo_data ) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetDeviceInfoListDetailW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceInfoListDetailW(HDEVINFO devinfo, PSP_DEVINFO_LIST_DETAIL_DATA_W devinfo_data ) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * (SETUPAPI.@) + * + * NO WINAPI in description given + */ +HDEVINFO WINAPI SetupDiGetClassDevsExA(const GUID *class, PCSTR filter, HWND parent, DWORD flags, HDEVINFO deviceset, PCSTR machine, PVOID reserved) +{ + FIXME("filter %s machine %s\n",debugstr_a(filter),debugstr_a(machine)); + return FALSE; +} + +/*********************************************************************** + * (SETUPAPI.@) + * + * NO WINAPI in description given + */ +HDEVINFO WINAPI SetupDiGetClassDevsExW(const GUID *class, PCWSTR filter, HWND parent, DWORD flags, HDEVINFO deviceset, PCWSTR machine, PVOID reserved) +{ + FIXME("\n"); + return FALSE; +} + + +/*********************************************************************** + * SetupCopyOEMInfA (SETUPAPI.@) + */ +BOOL WINAPI SetupCopyOEMInfA(PCSTR sourceinffile, PCSTR sourcemedialoc, + DWORD mediatype, DWORD copystyle, PSTR destinfname, + DWORD destnamesize, PDWORD required, + PSTR *destinfnamecomponent) +{ + FIXME("stub: source %s location %s ...\n",sourceinffile, sourcemedialoc); + return FALSE; +} + +/*********************************************************************** + * SetupGetInfInformationA (SETUPAPI.@) + */ +BOOL WINAPI SetupGetInfInformationA( LPCVOID InfSpec, DWORD SearchControl, + PSP_INF_INFORMATION ReturnBuffer, + DWORD ReturnBufferSize, PDWORD RequiredSize) +{ + FIXME("(%p, %ld, %p, %ld, %p) Stub!\n", + InfSpec, SearchControl, ReturnBuffer, ReturnBufferSize, RequiredSize ); + return TRUE; +} + +/*********************************************************************** + * SetupInitializeFileLogW(SETUPAPI.@) + */ +HANDLE WINAPI SetupInitializeFileLogW(LPWSTR LogFileName, DWORD Flags) +{ + FIXME("Stub %s, 0x%lx\n",debugstr_w(LogFileName),Flags); + return INVALID_HANDLE_VALUE; +} + +/*********************************************************************** + * SetupInitializeFileLogA(SETUPAPI.@) + */ +HANDLE WINAPI SetupInitializeFileLogA(LPSTR LogFileName, DWORD Flags) +{ + FIXME("Stub %s, 0x%lx\n",debugstr_a(LogFileName),Flags); + return INVALID_HANDLE_VALUE; +} + +/*********************************************************************** + * SetupTerminateFileLog(SETUPAPI.@) + */ +BOOL WINAPI SetupTerminateFileLog(HANDLE FileLogHandle) +{ + FIXME ("Stub %p\n",FileLogHandle); + return TRUE; +} diff --git a/reactos/lib/setupapi/virtcopy.c b/reactos/lib/setupapi/virtcopy.c index 8ffbfdddc85..b21692eb091 100644 --- a/reactos/lib/setupapi/virtcopy.c +++ b/reactos/lib/setupapi/virtcopy.c @@ -1,789 +1,789 @@ -/* - * SetupAPI virtual copy operations - * - * Copyright 2001 Andreas Mohr - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * FIXME: we now rely on builtin setupapi.dll for dialog resources. - * This is bad ! We ought to have 16bit resource handling working. - */ - -#include -#include -#include "windef.h" -#include "winbase.h" -#include "winuser.h" -#include "winreg.h" -#include "wownt32.h" -#include "wingdi.h" -#include "winnls.h" -#include "setupapi.h" -#include "setupx16.h" -#include "setupapi_private.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(setupapi); - -static FARPROC16 VCP_Proc = NULL; -static LPARAM VCP_MsgRef = 0; - -static BOOL VCP_opened = FALSE; - -static VCPSTATUS vcp_status; - -static HINSTANCE SETUPAPI_hInstance; - -static WORD VCP_Callback( LPVOID obj, UINT16 msg, WPARAM16 wParam, LPARAM lParam, LPARAM lParamRef ) -{ - WORD args[8]; - DWORD ret = OK; - if (VCP_Proc) - { - args[7] = HIWORD(obj); - args[6] = LOWORD(obj); - args[5] = msg; - args[4] = wParam; - args[3] = HIWORD(lParam); - args[2] = LOWORD(lParam); - args[1] = HIWORD(lParamRef); - args[0] = LOWORD(lParamRef); - WOWCallback16Ex( (DWORD)VCP_Proc, WCB16_PASCAL, sizeof(args), args, &ret ); - } - return (WORD)ret; -} - -/****************************** VHSTR management ******************************/ - -/* - * This is a totally braindead implementation for now; - * I don't care about speed at all ! Size and implementation time - * is much more important IMHO. I could have created some sophisticated - * tree structure, but... what the hell ! :-) - */ -typedef struct { - DWORD refcount; - LPCSTR pStr; -} VHSTR_STRUCT; - -static VHSTR_STRUCT **vhstrlist = NULL; -static VHSTR vhstr_alloc = 0; - -#define VALID_VHSTR(x) ((x < vhstr_alloc) && (vhstrlist[x]) && (vhstrlist[x]->refcount)) - -/*********************************************************************** - * vsmStringAdd (SETUPX.207) - */ -VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName) -{ - VHSTR n; - VHSTR index = 0xffff; - HANDLE heap; - - TRACE("add string '%s'\n", lpszName); - /* search whether string already inserted */ - TRACE("searching for existing string...\n"); - for (n = 0; n < vhstr_alloc; n++) - { - if ((vhstrlist[n]) && (vhstrlist[n]->refcount)) - { - TRACE("checking item: %d\n", n); - if (!strcmp(vhstrlist[n]->pStr, lpszName)) - { - TRACE("found\n"); - vhstrlist[n]->refcount++; - return n; - } - } - } - - /* hmm, not found yet, let's insert it */ - TRACE("inserting item\n"); - for (n = 0; n < vhstr_alloc; n++) - { - if ((!(vhstrlist[n])) || (!(vhstrlist[n]->refcount))) - { - index = n; - break; - } - } - heap = GetProcessHeap(); - if (n == vhstr_alloc) /* hmm, no free index found yet */ - { - index = vhstr_alloc; - vhstr_alloc += 20; - - if (vhstrlist) - vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist, - sizeof(VHSTR_STRUCT *) * vhstr_alloc); - else - vhstrlist = HeapAlloc(heap, HEAP_ZERO_MEMORY, - sizeof(VHSTR_STRUCT *) * vhstr_alloc); - } - if (index == 0xffff) - return 0xffff; /* failure */ - if (!vhstrlist[index]) - vhstrlist[index] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VHSTR_STRUCT)); - vhstrlist[index]->refcount = 1; - vhstrlist[index]->pStr = HeapAlloc(heap, 0, strlen(lpszName)+1); - strcpy((LPSTR)vhstrlist[index]->pStr, lpszName); - return index; -} - -/*********************************************************************** - * vsmStringDelete (SETUPX.206) - */ -INT16 WINAPI vsmStringDelete16(VHSTR vhstr) -{ - if (VALID_VHSTR(vhstr)) - { - vhstrlist[vhstr]->refcount--; - if (!vhstrlist[vhstr]->refcount) - { - HeapFree(GetProcessHeap(), 0, (LPSTR)vhstrlist[vhstr]->pStr); - vhstrlist[vhstr]->pStr = NULL; - } - return VCPN_OK; - } - - /* string not found */ - return VCPN_FAIL; -} - -/* - * vsmStringFind() - not exported from a standard SETUPX.DLL, it seems - */ -VHSTR WINAPI vsmStringFind16(LPCSTR lpszName) -{ - WORD n; - for (n = 0; n < vhstr_alloc; n++) - if ((vhstrlist[n]) && (vhstrlist[n]->refcount) && (!strcmp(vhstrlist[n]->pStr, lpszName))) - return n; - return 0xffff; -} - -/*********************************************************************** - * vsmGetStringName (SETUPX.205) - * - * Pretty correct, I guess - */ -INT16 WINAPI vsmGetStringName16(VHSTR vhstr, LPSTR lpszBuffer, int cbBuffer) -{ - if (VALID_VHSTR(vhstr)) - { - int len = strlen(vhstrlist[vhstr]->pStr)+1; - if (cbBuffer >= len) - { - if (lpszBuffer) - strcpy(lpszBuffer, vhstrlist[vhstr]->pStr); - return len; - } - } - return VCPN_FAIL; -} - -/*********************************************************************** - * vsmStringCompare (not exported from a standard SETUPX.DLL, it seems) - */ -INT16 WINAPI vsmStringCompare16(VHSTR vhstrA, VHSTR vhstrB) -{ - if ((!VALID_VHSTR(vhstrA)) || (!VALID_VHSTR(vhstrB))) - return VCPN_FAIL; /* correct ? */ - return strcmp(vhstrlist[vhstrA]->pStr, vhstrlist[vhstrB]->pStr); -} - -/*********************************************************************** - * vsmGetStringRawName (SETUPX.208) - */ -LPCSTR WINAPI vsmGetStringRawName16(VHSTR vhstr) -{ - return (VALID_VHSTR(vhstr)) ? vhstrlist[vhstr]->pStr : NULL; -} - - -/***************************** VIRTNODE management ****************************/ -static LPVIRTNODE *pvnlist = NULL; -static DWORD vn_num = 0; -static DWORD vn_last = 0; - -RETERR16 VCP_VirtnodeCreate(LPVCPFILESPEC vfsSrc, LPVCPFILESPEC vfsDst, WORD fl, LPARAM lParam, LPEXPANDVTBL lpExpandVtbl) -{ - HANDLE heap; - LPVIRTNODE lpvn; - RETERR16 cbres; - - while (vn_last < vn_num) - { - if (pvnlist[vn_last] == NULL) - break; - vn_last++; - } - heap = GetProcessHeap(); - if (vn_last == vn_num) - { - vn_num += 20; - if (pvnlist) - pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist, - sizeof(LPVIRTNODE *) * vn_num); - else - pvnlist = HeapAlloc(heap, HEAP_ZERO_MEMORY, - sizeof(LPVIRTNODE *) * vn_num); - } - pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE)); - lpvn = pvnlist[vn_last]; - vn_last++; - - lpvn->cbSize = sizeof(VIRTNODE); - - if (vfsSrc) - memcpy(&lpvn->vfsSrc, vfsSrc, sizeof(VCPFILESPEC)); - - if (vfsDst) - memcpy(&lpvn->vfsDst, vfsDst, sizeof(VCPFILESPEC)); - - lpvn->fl = fl; - lpvn->lParam = lParam; - lpvn->lpExpandVtbl = lpExpandVtbl; - - lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */ - - cbres = VCP_Callback(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef); - lpvn->fl |= VFNL_CREATED; - cbres = VCP_Callback(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef); - - return OK; -} - -BOOL VCP_VirtnodeDelete(LPVIRTNODE lpvnDel) -{ - DWORD n; - RETERR16 cbres; - - for (n = 0; n < vn_last; n++) - { - if (pvnlist[n] == lpvnDel) - { - cbres = VCP_Callback(lpvnDel, VCPM_NODEDESTROY, 0, 0, VCP_MsgRef); - HeapFree(GetProcessHeap(), 0, lpvnDel); - pvnlist[n] = NULL; - return TRUE; - } - } - return FALSE; -} - -/*********************************************************************** - * VcpOpen (SETUPX.200) - * - * Sets up a virtual copy operation. - * This means that functions such as GenInstall() - * create a VIRTNODE struct for every file to be touched in a .INF file - * instead of actually touching the file. - * The actual copy/move/rename gets started when VcpClose or - * VcpFlush is called; several different callbacks are made - * (copy, rename, open, close, version conflicts, ...) on every file copied. - */ -RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef) -{ - TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef); - if (VCP_opened) - return ERR_VCP_BUSY; - - VCP_Proc = (FARPROC16)vifproc; - VCP_MsgRef = lparamMsgRef; - - /* load SETUPAPI needed for dialog resources etc. */ - SETUPAPI_hInstance = LoadLibraryA("setupapi.dll"); - if (!SETUPAPI_hInstance) - { - ERR("Could not load sibling setupapi.dll\n"); - return ERR_VCP_NOMEM; - } - VCP_opened = TRUE; - return OK; -} - -/*********************************************************************** - * VcpQueueCopy [SETUPX.13] - * - * lpExpandVtbl seems to be deprecated. - * fl are the CNFL_xxx and VNFL_xxx flags. - * lParam are the VNLP_xxx flags. - */ -RETERR16 WINAPI VcpQueueCopy16( - LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName, - LPCSTR lpszSrcDir, LPCSTR lpszDstDir, - LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst, - LPEXPANDVTBL lpExpandVtbl, - WORD fl, LPARAM lParam -) -{ - VCPFILESPEC vfsSrc, vfsDst; - - if (!VCP_opened) - return ERR_VCP_NOTOPEN; - - TRACE("srcdir: %s, srcfile: %s, dstdir: %s, dstfile: %s\n", - lpszSrcDir, lpszSrcFileName, lpszDstDir, lpszDstFileName); - - TRACE("ldidSrc == %d, ldidDst == %d\n", ldidSrc, ldidDst); - - vfsSrc.ldid = ldidSrc; - vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir); - vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName); - - vfsDst.ldid = ldidDst; - vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir); - vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName); - - return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam, - lpExpandVtbl); -} - -/*********************************************************************** - * VcpQueueDelete [SETUPX.17] - * - * Is lParamRef the same as lParam in VcpQueueCopy ? - * Damn docu !! Err... which docu ? - */ -RETERR16 WINAPI VcpQueueDelete16( - LPCSTR lpszDstFileName, - LPCSTR lpszDstDir, - LOGDISKID16 ldidDst, - LPARAM lParamRef -) -{ - VCPFILESPEC vfsDst; - - if (!VCP_opened) - return ERR_VCP_NOTOPEN; - - vfsDst.ldid = ldidDst; - vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir); - vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName); - - return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef, 0); -} - -/*********************************************************************** - * VcpQueueRename [SETUPX.204] - * - */ -RETERR16 WINAPI VcpQueueRename16( - LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName, - LPCSTR lpszSrcDir, LPCSTR lpszDstDir, - LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst, - LPARAM lParam -) -{ - VCPFILESPEC vfsSrc, vfsDst; - - if (!VCP_opened) - return ERR_VCP_NOTOPEN; - - vfsSrc.ldid = ldidSrc; - vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir); - vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName); - - vfsDst.ldid = ldidDst; - vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir); - vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName); - - return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam, - 0); -} - -/*********************************************************************** - * VcpEnumFiles (SETUPX.@) - */ -INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef) -{ - WORD n; - - for (n = 0; n < vn_last; n++) - vep(pvnlist[n], lParamRef); - - return 0; /* FIXME: return value ? */ -} - -/*********************************************************************** - * VcpExplain (SETUPX.411) - */ -LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat) -{ - static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */ - buffer[0] = '\0'; - switch (dwWhat) - { - case VCPEX_SRC_FULL: - case VCPEX_DST_FULL: - { - LPVCPFILESPEC lpvfs = - (dwWhat == VCPEX_SRC_FULL) ? &lpVn->vfsSrc : &lpVn->vfsDst; - - /* if we have an ldid, use it, otherwise use the string */ - /* from the vhstrlist array */ - if (lpvfs->ldid != 0xffff) - CtlGetLddPath16(lpvfs->ldid, buffer); - else - strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir)); - - strcat(buffer, "\\"); - strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName)); - } - break; - default: - FIXME("%ld unimplemented !\n", dwWhat); - strcpy(buffer, "Unknown error"); - break; - } - return buffer; -} - -RETERR16 VCP_CheckPaths(void) -{ - DWORD n; - LPVIRTNODE lpvn; - RETERR16 cbres; - - cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef); - for (n = 0; n < vn_num; n++) - { - lpvn = pvnlist[n]; - if (!lpvn) continue; - /* FIXME: check paths of all VIRTNODEs here ! */ - cbres = VCP_Callback(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef); - } - cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef); - return OK; -} - -RETERR16 VCP_CopyFiles(void) -{ - char fn_src[MAX_PATH], fn_dst[MAX_PATH]; - RETERR16 res = OK, cbres; - DWORD n; - LPVIRTNODE lpvn; - - cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef); - for (n = 0; n < vn_num; n++) - { - lpvn = pvnlist[n]; - if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue; - /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */ - strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL)); - strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL)); - /* FIXME: what is this VCPM_VSTATWRITE here for ? - * I guess it's to signal successful destination file creation */ - cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef); - - /* FIXME: need to do the file copy in small chunks for notifications */ - TRACE("copying '%s' to '%s'\n", fn_src, fn_dst); - /* perform the file copy */ - if (!(CopyFileA(fn_src, fn_dst, - (lpvn->fl & VNLP_COPYIFEXISTS) ? FALSE : TRUE ))) - { - ERR("error copying, src: %s -> dst: %s\n", fn_src, fn_dst); - res = ERR_VCP_IOFAIL; - } - - vcp_status.prgFileRead.dwSoFar++; - cbres = VCP_Callback(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef); - vcp_status.prgFileWrite.dwSoFar++; - cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef); - } - - cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef); - return res; -} - -/*********************************************************************** - * VcpFlush - internal (not exported), but documented - * - * VNFL_NOW is used for VcpFlush. - */ -RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest) -{ - return OK; -} - -/*********************************************************************** - * VcpClose (SETUPX.201) - * - * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART, - * VCPM_VSTATCLOSEEND. - * - * fl gets VCPFL_xxx flags to indicate what to do with the - * VIRTNODEs (files to mess with) created by e.g. GenInstall() - */ -RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest) -{ - RETERR16 res = OK; - WORD cbres = VCPN_PROCEED; - - TRACE("(%04x, '%s')\n", fl, lpszBackupDest); - - /* FIXME: needs to sort virtnodes in case VCPFL_INSPECIFIEDORDER - * is not set. This is done by VCP_Callback(VCPM_NODECOMPARE) */ - - TRACE("#1\n"); - memset(&vcp_status, 0, sizeof(VCPSTATUS)); - /* yes, vcp_status.cbSize is 0 ! */ - TRACE("#2\n"); - cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef); - TRACE("#3\n"); - - res = VCP_CheckPaths(); - TRACE("#4\n"); - if (res != OK) - return res; /* is this ok ? */ - VCP_CopyFiles(); - - TRACE("#5\n"); - cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef); - TRACE("#6\n"); - VCP_Proc = NULL; - FreeLibrary(SETUPAPI_hInstance); - VCP_opened = FALSE; - return OK; -} - -RETERR16 VCP_RenameFiles(void) -{ - char fn_src[MAX_PATH], fn_dst[MAX_PATH]; - RETERR16 res = OK, cbres; - DWORD n; - LPVIRTNODE lpvn; - - cbres = VCP_Callback(&vcp_status, VCPM_VSTATRENAMESTART, 0, 0, VCP_MsgRef); - for (n = 0; n < vn_num; n++) - { - lpvn = pvnlist[n]; - if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_RENAME)) continue; - strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL)); - strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL)); - cbres = VCP_Callback(&lpvn->vfsDst, VCPM_FILEOPENOUT, 0, (LPARAM)lpvn, VCP_MsgRef); - if (!(MoveFileExA(fn_src, fn_dst, MOVEFILE_REPLACE_EXISTING))) - res = ERR_VCP_IOFAIL; - else - VCP_VirtnodeDelete(lpvn); - } - cbres = VCP_Callback(&vcp_status, VCPM_VSTATRENAMEEND, 0, 0, VCP_MsgRef); - return res; -} - -/*********************************************************************** - * vcpDefCallbackProc (SETUPX.202) - */ -RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, - LPARAM lParam, LPARAM lParamRef) -{ - static int count = 0; - if (count < 10) - FIXME("(%p, %04x, %04x, %08lx, %08lx) - what to do here ?\n", - lpvObj, uMsg, wParam, lParam, lParamRef); - count++; - return OK; -} - -/********************* point-and-click stuff from here ***********************/ - -static HWND hDlgCopy = 0; -static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0; -static char BackupDir[12]; - -static INT_PTR CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) -{ - INT_PTR retval = FALSE; - - if (iMsg == WM_INITDIALOG) - { - ShowWindow(hWndDlg, SW_SHOWNORMAL); - UpdateWindow(hWndDlg); - retval = TRUE; - } - return retval; -} - -BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32) -{ - HRSRC hResInfo; - HGLOBAL hDlgTmpl32; - - if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), (LPSTR)RT_DIALOG))) - return FALSE; - if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) || - !(*template32 = LockResource( hDlgTmpl32 ))) - return FALSE; - return TRUE; -} - -static LRESULT WINAPI -VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (uMsg != WM_CREATE) - return DefWindowProcA (hwnd, uMsg, wParam, lParam); - - switch (uMsg) - { - case WM_CREATE: - return 0; - default: - FIXME("%04x: unhandled.\n", uMsg); - } - - return 0; -} - -void VCP_UI_RegisterProgressClass(void) -{ - static BOOL registered = FALSE; - WNDCLASSA wndClass; - - if (registered) - return; - - registered = TRUE; - ZeroMemory (&wndClass, sizeof(WNDCLASSA)); - wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; - wndClass.lpfnWndProc = VCP_UI_FileCopyWndProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW); - wndClass.hbrBackground = NULL; - wndClass.lpszClassName = "setupx_progress"; - - RegisterClassA (&wndClass); -} - -RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2) -{ - LPCSTR file1, file2; - file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName); - file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName); - return (RETERR16)strcmp(file1, file2); -} - -RETERR16 VCP_UI_CopyStart(void) -{ - LPCVOID template32; - char buf[256]; /* plenty */ - BOOL dirty; - DWORD len; - - /* FIXME: should be registered at DLL startup instead */ - VCP_UI_RegisterProgressClass(); - if (!(VCP_UI_GetDialogTemplate(&template32))) - return VCPN_FAIL; - - if (vn_num > 10) /* hack */ - { - hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0, - VCP_UI_FileCopyDlgProc, 0); - if (!hDlgCopy) - return VCPN_FAIL; - SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ..."); - SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET"); - } - strcpy(buf, REG_INSTALLEDFILES); - if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles)) - return VCPN_FAIL; - strcat(buf, REGPART_RENAME); - if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename)) - return VCPN_FAIL; - if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict)) - return VCPN_FAIL; - len = 1; - if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len))) - { - /* FIXME: what does SETUPX.DLL do in this case ? */ - MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n"); - return VCPN_FAIL; - } - dirty = TRUE; - if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1)) - return VCPN_FAIL; - len = 12; - if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, BackupDir, &len))) - strcpy(BackupDir, "VCM"); - - /* create C:\WINDOWS\[BackupDir] and set registry key to it */ - GetWindowsDirectoryA(buf, 256); - strcat(buf, "\\"); - strcat(buf, BackupDir); - if (!(CreateDirectoryA(buf, NULL))) - return VCPN_FAIL; - if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1)) - return VCPN_FAIL; - RegCloseKey(hKeyConflict); - - return VCPN_OK; -} - -/*********************************************************************** - * vcpUICallbackProc (SETUPX.213) - */ -RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, - LPARAM lParam, LPARAM lParamRef) -{ - static int count = 0; - RETERR16 res = VCPN_OK, cbres; - - if (count < 5) - FIXME("(%p, %04x, %04x, %08lx, %08lx) - semi-stub\n", - lpvObj, uMsg, wParam, lParam, lParamRef); - count++; - switch (uMsg) - { - /* unused messages, it seems */ - case VCPM_DISKPREPINFO: - - case VCPM_FILENEEDED: - - case VCPM_NODECREATE: - case VCPM_NODEACCEPT: - - case VCPM_VSTATCLOSESTART: - case VCPM_VSTATPATHCHECKSTART: - case VCPM_VSTATPATHCHECKEND: - - case VCPM_CHECKPATH: - break; - - /* the real stuff */ - case VCPM_NODECOMPARE: - res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam); - break; - case VCPM_VSTATREAD: - break; - case VCPM_VSTATWRITE: - cbres = VCP_Callback(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef); - break; - case VCPM_VSTATCLOSEEND: - RegCloseKey(hKeyFiles); - RegCloseKey(hKeyRename); - RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT); - break; - case VCPM_VSTATCOPYSTART: - res = VCP_UI_CopyStart(); - break; - case VCPM_VSTATCOPYEND: - if (hDlgCopy) DestroyWindow(hDlgCopy); - break; - default: - FIXME("unhandled msg 0x%04x\n", uMsg); - } - return res; -} +/* + * SetupAPI virtual copy operations + * + * Copyright 2001 Andreas Mohr + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * FIXME: we now rely on builtin setupapi.dll for dialog resources. + * This is bad ! We ought to have 16bit resource handling working. + */ + +#include +#include +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winreg.h" +#include "wownt32.h" +#include "wingdi.h" +#include "winnls.h" +#include "setupapi.h" +#include "setupx16.h" +#include "setupapi_private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +static FARPROC16 VCP_Proc = NULL; +static LPARAM VCP_MsgRef = 0; + +static BOOL VCP_opened = FALSE; + +static VCPSTATUS vcp_status; + +static HINSTANCE SETUPAPI_hInstance; + +static WORD VCP_Callback( LPVOID obj, UINT16 msg, WPARAM16 wParam, LPARAM lParam, LPARAM lParamRef ) +{ + WORD args[8]; + DWORD ret = OK; + if (VCP_Proc) + { + args[7] = HIWORD(obj); + args[6] = LOWORD(obj); + args[5] = msg; + args[4] = wParam; + args[3] = HIWORD(lParam); + args[2] = LOWORD(lParam); + args[1] = HIWORD(lParamRef); + args[0] = LOWORD(lParamRef); + WOWCallback16Ex( (DWORD)VCP_Proc, WCB16_PASCAL, sizeof(args), args, &ret ); + } + return (WORD)ret; +} + +/****************************** VHSTR management ******************************/ + +/* + * This is a totally braindead implementation for now; + * I don't care about speed at all ! Size and implementation time + * is much more important IMHO. I could have created some sophisticated + * tree structure, but... what the hell ! :-) + */ +typedef struct { + DWORD refcount; + LPCSTR pStr; +} VHSTR_STRUCT; + +static VHSTR_STRUCT **vhstrlist = NULL; +static VHSTR vhstr_alloc = 0; + +#define VALID_VHSTR(x) ((x < vhstr_alloc) && (vhstrlist[x]) && (vhstrlist[x]->refcount)) + +/*********************************************************************** + * vsmStringAdd (SETUPX.207) + */ +VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName) +{ + VHSTR n; + VHSTR index = 0xffff; + HANDLE heap; + + TRACE("add string '%s'\n", lpszName); + /* search whether string already inserted */ + TRACE("searching for existing string...\n"); + for (n = 0; n < vhstr_alloc; n++) + { + if ((vhstrlist[n]) && (vhstrlist[n]->refcount)) + { + TRACE("checking item: %d\n", n); + if (!strcmp(vhstrlist[n]->pStr, lpszName)) + { + TRACE("found\n"); + vhstrlist[n]->refcount++; + return n; + } + } + } + + /* hmm, not found yet, let's insert it */ + TRACE("inserting item\n"); + for (n = 0; n < vhstr_alloc; n++) + { + if ((!(vhstrlist[n])) || (!(vhstrlist[n]->refcount))) + { + index = n; + break; + } + } + heap = GetProcessHeap(); + if (n == vhstr_alloc) /* hmm, no free index found yet */ + { + index = vhstr_alloc; + vhstr_alloc += 20; + + if (vhstrlist) + vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist, + sizeof(VHSTR_STRUCT *) * vhstr_alloc); + else + vhstrlist = HeapAlloc(heap, HEAP_ZERO_MEMORY, + sizeof(VHSTR_STRUCT *) * vhstr_alloc); + } + if (index == 0xffff) + return 0xffff; /* failure */ + if (!vhstrlist[index]) + vhstrlist[index] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VHSTR_STRUCT)); + vhstrlist[index]->refcount = 1; + vhstrlist[index]->pStr = HeapAlloc(heap, 0, strlen(lpszName)+1); + strcpy((LPSTR)vhstrlist[index]->pStr, lpszName); + return index; +} + +/*********************************************************************** + * vsmStringDelete (SETUPX.206) + */ +INT16 WINAPI vsmStringDelete16(VHSTR vhstr) +{ + if (VALID_VHSTR(vhstr)) + { + vhstrlist[vhstr]->refcount--; + if (!vhstrlist[vhstr]->refcount) + { + HeapFree(GetProcessHeap(), 0, (LPSTR)vhstrlist[vhstr]->pStr); + vhstrlist[vhstr]->pStr = NULL; + } + return VCPN_OK; + } + + /* string not found */ + return VCPN_FAIL; +} + +/* + * vsmStringFind() - not exported from a standard SETUPX.DLL, it seems + */ +VHSTR WINAPI vsmStringFind16(LPCSTR lpszName) +{ + WORD n; + for (n = 0; n < vhstr_alloc; n++) + if ((vhstrlist[n]) && (vhstrlist[n]->refcount) && (!strcmp(vhstrlist[n]->pStr, lpszName))) + return n; + return 0xffff; +} + +/*********************************************************************** + * vsmGetStringName (SETUPX.205) + * + * Pretty correct, I guess + */ +INT16 WINAPI vsmGetStringName16(VHSTR vhstr, LPSTR lpszBuffer, int cbBuffer) +{ + if (VALID_VHSTR(vhstr)) + { + int len = strlen(vhstrlist[vhstr]->pStr)+1; + if (cbBuffer >= len) + { + if (lpszBuffer) + strcpy(lpszBuffer, vhstrlist[vhstr]->pStr); + return len; + } + } + return VCPN_FAIL; +} + +/*********************************************************************** + * vsmStringCompare (not exported from a standard SETUPX.DLL, it seems) + */ +INT16 WINAPI vsmStringCompare16(VHSTR vhstrA, VHSTR vhstrB) +{ + if ((!VALID_VHSTR(vhstrA)) || (!VALID_VHSTR(vhstrB))) + return VCPN_FAIL; /* correct ? */ + return strcmp(vhstrlist[vhstrA]->pStr, vhstrlist[vhstrB]->pStr); +} + +/*********************************************************************** + * vsmGetStringRawName (SETUPX.208) + */ +LPCSTR WINAPI vsmGetStringRawName16(VHSTR vhstr) +{ + return (VALID_VHSTR(vhstr)) ? vhstrlist[vhstr]->pStr : NULL; +} + + +/***************************** VIRTNODE management ****************************/ +static LPVIRTNODE *pvnlist = NULL; +static DWORD vn_num = 0; +static DWORD vn_last = 0; + +RETERR16 VCP_VirtnodeCreate(LPVCPFILESPEC vfsSrc, LPVCPFILESPEC vfsDst, WORD fl, LPARAM lParam, LPEXPANDVTBL lpExpandVtbl) +{ + HANDLE heap; + LPVIRTNODE lpvn; + RETERR16 cbres; + + while (vn_last < vn_num) + { + if (pvnlist[vn_last] == NULL) + break; + vn_last++; + } + heap = GetProcessHeap(); + if (vn_last == vn_num) + { + vn_num += 20; + if (pvnlist) + pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist, + sizeof(LPVIRTNODE *) * vn_num); + else + pvnlist = HeapAlloc(heap, HEAP_ZERO_MEMORY, + sizeof(LPVIRTNODE *) * vn_num); + } + pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE)); + lpvn = pvnlist[vn_last]; + vn_last++; + + lpvn->cbSize = sizeof(VIRTNODE); + + if (vfsSrc) + memcpy(&lpvn->vfsSrc, vfsSrc, sizeof(VCPFILESPEC)); + + if (vfsDst) + memcpy(&lpvn->vfsDst, vfsDst, sizeof(VCPFILESPEC)); + + lpvn->fl = fl; + lpvn->lParam = lParam; + lpvn->lpExpandVtbl = lpExpandVtbl; + + lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */ + + cbres = VCP_Callback(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef); + lpvn->fl |= VFNL_CREATED; + cbres = VCP_Callback(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef); + + return OK; +} + +BOOL VCP_VirtnodeDelete(LPVIRTNODE lpvnDel) +{ + DWORD n; + RETERR16 cbres; + + for (n = 0; n < vn_last; n++) + { + if (pvnlist[n] == lpvnDel) + { + cbres = VCP_Callback(lpvnDel, VCPM_NODEDESTROY, 0, 0, VCP_MsgRef); + HeapFree(GetProcessHeap(), 0, lpvnDel); + pvnlist[n] = NULL; + return TRUE; + } + } + return FALSE; +} + +/*********************************************************************** + * VcpOpen (SETUPX.200) + * + * Sets up a virtual copy operation. + * This means that functions such as GenInstall() + * create a VIRTNODE struct for every file to be touched in a .INF file + * instead of actually touching the file. + * The actual copy/move/rename gets started when VcpClose or + * VcpFlush is called; several different callbacks are made + * (copy, rename, open, close, version conflicts, ...) on every file copied. + */ +RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef) +{ + TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef); + if (VCP_opened) + return ERR_VCP_BUSY; + + VCP_Proc = (FARPROC16)vifproc; + VCP_MsgRef = lparamMsgRef; + + /* load SETUPAPI needed for dialog resources etc. */ + SETUPAPI_hInstance = LoadLibraryA("setupapi.dll"); + if (!SETUPAPI_hInstance) + { + ERR("Could not load sibling setupapi.dll\n"); + return ERR_VCP_NOMEM; + } + VCP_opened = TRUE; + return OK; +} + +/*********************************************************************** + * VcpQueueCopy [SETUPX.13] + * + * lpExpandVtbl seems to be deprecated. + * fl are the CNFL_xxx and VNFL_xxx flags. + * lParam are the VNLP_xxx flags. + */ +RETERR16 WINAPI VcpQueueCopy16( + LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName, + LPCSTR lpszSrcDir, LPCSTR lpszDstDir, + LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst, + LPEXPANDVTBL lpExpandVtbl, + WORD fl, LPARAM lParam +) +{ + VCPFILESPEC vfsSrc, vfsDst; + + if (!VCP_opened) + return ERR_VCP_NOTOPEN; + + TRACE("srcdir: %s, srcfile: %s, dstdir: %s, dstfile: %s\n", + lpszSrcDir, lpszSrcFileName, lpszDstDir, lpszDstFileName); + + TRACE("ldidSrc == %d, ldidDst == %d\n", ldidSrc, ldidDst); + + vfsSrc.ldid = ldidSrc; + vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir); + vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName); + + vfsDst.ldid = ldidDst; + vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir); + vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName); + + return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam, + lpExpandVtbl); +} + +/*********************************************************************** + * VcpQueueDelete [SETUPX.17] + * + * Is lParamRef the same as lParam in VcpQueueCopy ? + * Damn docu !! Err... which docu ? + */ +RETERR16 WINAPI VcpQueueDelete16( + LPCSTR lpszDstFileName, + LPCSTR lpszDstDir, + LOGDISKID16 ldidDst, + LPARAM lParamRef +) +{ + VCPFILESPEC vfsDst; + + if (!VCP_opened) + return ERR_VCP_NOTOPEN; + + vfsDst.ldid = ldidDst; + vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir); + vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName); + + return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef, 0); +} + +/*********************************************************************** + * VcpQueueRename [SETUPX.204] + * + */ +RETERR16 WINAPI VcpQueueRename16( + LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName, + LPCSTR lpszSrcDir, LPCSTR lpszDstDir, + LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst, + LPARAM lParam +) +{ + VCPFILESPEC vfsSrc, vfsDst; + + if (!VCP_opened) + return ERR_VCP_NOTOPEN; + + vfsSrc.ldid = ldidSrc; + vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir); + vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName); + + vfsDst.ldid = ldidDst; + vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir); + vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName); + + return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam, + 0); +} + +/*********************************************************************** + * VcpEnumFiles (SETUPX.@) + */ +INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef) +{ + WORD n; + + for (n = 0; n < vn_last; n++) + vep(pvnlist[n], lParamRef); + + return 0; /* FIXME: return value ? */ +} + +/*********************************************************************** + * VcpExplain (SETUPX.411) + */ +LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat) +{ + static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */ + buffer[0] = '\0'; + switch (dwWhat) + { + case VCPEX_SRC_FULL: + case VCPEX_DST_FULL: + { + LPVCPFILESPEC lpvfs = + (dwWhat == VCPEX_SRC_FULL) ? &lpVn->vfsSrc : &lpVn->vfsDst; + + /* if we have an ldid, use it, otherwise use the string */ + /* from the vhstrlist array */ + if (lpvfs->ldid != 0xffff) + CtlGetLddPath16(lpvfs->ldid, buffer); + else + strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir)); + + strcat(buffer, "\\"); + strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName)); + } + break; + default: + FIXME("%ld unimplemented !\n", dwWhat); + strcpy(buffer, "Unknown error"); + break; + } + return buffer; +} + +RETERR16 VCP_CheckPaths(void) +{ + DWORD n; + LPVIRTNODE lpvn; + RETERR16 cbres; + + cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef); + for (n = 0; n < vn_num; n++) + { + lpvn = pvnlist[n]; + if (!lpvn) continue; + /* FIXME: check paths of all VIRTNODEs here ! */ + cbres = VCP_Callback(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef); + } + cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef); + return OK; +} + +RETERR16 VCP_CopyFiles(void) +{ + char fn_src[MAX_PATH], fn_dst[MAX_PATH]; + RETERR16 res = OK, cbres; + DWORD n; + LPVIRTNODE lpvn; + + cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef); + for (n = 0; n < vn_num; n++) + { + lpvn = pvnlist[n]; + if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue; + /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */ + strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL)); + strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL)); + /* FIXME: what is this VCPM_VSTATWRITE here for ? + * I guess it's to signal successful destination file creation */ + cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef); + + /* FIXME: need to do the file copy in small chunks for notifications */ + TRACE("copying '%s' to '%s'\n", fn_src, fn_dst); + /* perform the file copy */ + if (!(CopyFileA(fn_src, fn_dst, + (lpvn->fl & VNLP_COPYIFEXISTS) ? FALSE : TRUE ))) + { + ERR("error copying, src: %s -> dst: %s\n", fn_src, fn_dst); + res = ERR_VCP_IOFAIL; + } + + vcp_status.prgFileRead.dwSoFar++; + cbres = VCP_Callback(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef); + vcp_status.prgFileWrite.dwSoFar++; + cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef); + } + + cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef); + return res; +} + +/*********************************************************************** + * VcpFlush - internal (not exported), but documented + * + * VNFL_NOW is used for VcpFlush. + */ +RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest) +{ + return OK; +} + +/*********************************************************************** + * VcpClose (SETUPX.201) + * + * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART, + * VCPM_VSTATCLOSEEND. + * + * fl gets VCPFL_xxx flags to indicate what to do with the + * VIRTNODEs (files to mess with) created by e.g. GenInstall() + */ +RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest) +{ + RETERR16 res = OK; + WORD cbres = VCPN_PROCEED; + + TRACE("(%04x, '%s')\n", fl, lpszBackupDest); + + /* FIXME: needs to sort virtnodes in case VCPFL_INSPECIFIEDORDER + * is not set. This is done by VCP_Callback(VCPM_NODECOMPARE) */ + + TRACE("#1\n"); + memset(&vcp_status, 0, sizeof(VCPSTATUS)); + /* yes, vcp_status.cbSize is 0 ! */ + TRACE("#2\n"); + cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef); + TRACE("#3\n"); + + res = VCP_CheckPaths(); + TRACE("#4\n"); + if (res != OK) + return res; /* is this ok ? */ + VCP_CopyFiles(); + + TRACE("#5\n"); + cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef); + TRACE("#6\n"); + VCP_Proc = NULL; + FreeLibrary(SETUPAPI_hInstance); + VCP_opened = FALSE; + return OK; +} + +RETERR16 VCP_RenameFiles(void) +{ + char fn_src[MAX_PATH], fn_dst[MAX_PATH]; + RETERR16 res = OK, cbres; + DWORD n; + LPVIRTNODE lpvn; + + cbres = VCP_Callback(&vcp_status, VCPM_VSTATRENAMESTART, 0, 0, VCP_MsgRef); + for (n = 0; n < vn_num; n++) + { + lpvn = pvnlist[n]; + if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_RENAME)) continue; + strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL)); + strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL)); + cbres = VCP_Callback(&lpvn->vfsDst, VCPM_FILEOPENOUT, 0, (LPARAM)lpvn, VCP_MsgRef); + if (!(MoveFileExA(fn_src, fn_dst, MOVEFILE_REPLACE_EXISTING))) + res = ERR_VCP_IOFAIL; + else + VCP_VirtnodeDelete(lpvn); + } + cbres = VCP_Callback(&vcp_status, VCPM_VSTATRENAMEEND, 0, 0, VCP_MsgRef); + return res; +} + +/*********************************************************************** + * vcpDefCallbackProc (SETUPX.202) + */ +RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, + LPARAM lParam, LPARAM lParamRef) +{ + static int count = 0; + if (count < 10) + FIXME("(%p, %04x, %04x, %08lx, %08lx) - what to do here ?\n", + lpvObj, uMsg, wParam, lParam, lParamRef); + count++; + return OK; +} + +/********************* point-and-click stuff from here ***********************/ + +static HWND hDlgCopy = 0; +static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0; +static char BackupDir[12]; + +static INT_PTR CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + INT_PTR retval = FALSE; + + if (iMsg == WM_INITDIALOG) + { + ShowWindow(hWndDlg, SW_SHOWNORMAL); + UpdateWindow(hWndDlg); + retval = TRUE; + } + return retval; +} + +BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32) +{ + HRSRC hResInfo; + HGLOBAL hDlgTmpl32; + + if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), (LPSTR)RT_DIALOG))) + return FALSE; + if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) || + !(*template32 = LockResource( hDlgTmpl32 ))) + return FALSE; + return TRUE; +} + +static LRESULT WINAPI +VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg != WM_CREATE) + return DefWindowProcA (hwnd, uMsg, wParam, lParam); + + switch (uMsg) + { + case WM_CREATE: + return 0; + default: + FIXME("%04x: unhandled.\n", uMsg); + } + + return 0; +} + +void VCP_UI_RegisterProgressClass(void) +{ + static BOOL registered = FALSE; + WNDCLASSA wndClass; + + if (registered) + return; + + registered = TRUE; + ZeroMemory (&wndClass, sizeof(WNDCLASSA)); + wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = VCP_UI_FileCopyWndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW); + wndClass.hbrBackground = NULL; + wndClass.lpszClassName = "setupx_progress"; + + RegisterClassA (&wndClass); +} + +RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2) +{ + LPCSTR file1, file2; + file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName); + file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName); + return (RETERR16)strcmp(file1, file2); +} + +RETERR16 VCP_UI_CopyStart(void) +{ + LPCVOID template32; + char buf[256]; /* plenty */ + BOOL dirty; + DWORD len; + + /* FIXME: should be registered at DLL startup instead */ + VCP_UI_RegisterProgressClass(); + if (!(VCP_UI_GetDialogTemplate(&template32))) + return VCPN_FAIL; + + if (vn_num > 10) /* hack */ + { + hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0, + VCP_UI_FileCopyDlgProc, 0); + if (!hDlgCopy) + return VCPN_FAIL; + SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ..."); + SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET"); + } + strcpy(buf, REG_INSTALLEDFILES); + if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles)) + return VCPN_FAIL; + strcat(buf, REGPART_RENAME); + if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename)) + return VCPN_FAIL; + if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict)) + return VCPN_FAIL; + len = 1; + if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len))) + { + /* FIXME: what does SETUPX.DLL do in this case ? */ + MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n"); + return VCPN_FAIL; + } + dirty = TRUE; + if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1)) + return VCPN_FAIL; + len = 12; + if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, BackupDir, &len))) + strcpy(BackupDir, "VCM"); + + /* create C:\WINDOWS\[BackupDir] and set registry key to it */ + GetWindowsDirectoryA(buf, 256); + strcat(buf, "\\"); + strcat(buf, BackupDir); + if (!(CreateDirectoryA(buf, NULL))) + return VCPN_FAIL; + if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1)) + return VCPN_FAIL; + RegCloseKey(hKeyConflict); + + return VCPN_OK; +} + +/*********************************************************************** + * vcpUICallbackProc (SETUPX.213) + */ +RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, + LPARAM lParam, LPARAM lParamRef) +{ + static int count = 0; + RETERR16 res = VCPN_OK, cbres; + + if (count < 5) + FIXME("(%p, %04x, %04x, %08lx, %08lx) - semi-stub\n", + lpvObj, uMsg, wParam, lParam, lParamRef); + count++; + switch (uMsg) + { + /* unused messages, it seems */ + case VCPM_DISKPREPINFO: + + case VCPM_FILENEEDED: + + case VCPM_NODECREATE: + case VCPM_NODEACCEPT: + + case VCPM_VSTATCLOSESTART: + case VCPM_VSTATPATHCHECKSTART: + case VCPM_VSTATPATHCHECKEND: + + case VCPM_CHECKPATH: + break; + + /* the real stuff */ + case VCPM_NODECOMPARE: + res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam); + break; + case VCPM_VSTATREAD: + break; + case VCPM_VSTATWRITE: + cbres = VCP_Callback(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef); + break; + case VCPM_VSTATCLOSEEND: + RegCloseKey(hKeyFiles); + RegCloseKey(hKeyRename); + RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT); + break; + case VCPM_VSTATCOPYSTART: + res = VCP_UI_CopyStart(); + break; + case VCPM_VSTATCOPYEND: + if (hDlgCopy) DestroyWindow(hDlgCopy); + break; + default: + FIXME("unhandled msg 0x%04x\n", uMsg); + } + return res; +} diff --git a/reactos/lib/shdocvw/De.rc b/reactos/lib/shdocvw/De.rc new file mode 100644 index 00000000000..564d9986073 --- /dev/null +++ b/reactos/lib/shdocvw/De.rc @@ -0,0 +1,36 @@ +/* + * Copyright 2005 Henning Gerhardt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT + +100 DIALOG LOADONCALL MOVEABLE DISCARDABLE 0, 0, 220, 62 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Lade die Mozilla Active X Steuerung" +FONT 8, "MS Shell Dlg" +{ + CONTROL "Progress1",1000,"msctls_progress32",WS_BORDER|PBS_SMOOTH,10,10,200,12 + LTEXT "", 104, 10, 30, 200, 10, SS_CENTER + PUSHBUTTON "Abbrechen", IDCANCEL, 85, 44, 50, 15, WS_GROUP | WS_TABSTOP +} + +STRINGTABLE +BEGIN + 1001 "Diese Anwendung fordert ein ActiveX Browser Objekt an,\n" \ + "aber die Mozilla Active X Steuerung ist nicht installiert." \ + "Möchten Sie, dass es aus dem Internet geladen und installiert wird ?" +END diff --git a/reactos/lib/shdocvw/En.rc b/reactos/lib/shdocvw/En.rc new file mode 100644 index 00000000000..c6d5150ea12 --- /dev/null +++ b/reactos/lib/shdocvw/En.rc @@ -0,0 +1,36 @@ +/* + * Copyright 2004 Mike McCormack for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +100 DIALOG LOADONCALL MOVEABLE DISCARDABLE 0, 0, 220, 62 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Downloading the Mozilla Active X control" +FONT 8, "MS Shell Dlg" +{ + CONTROL "Progress1",1000,"msctls_progress32",WS_BORDER|PBS_SMOOTH,10,10,200,12 + LTEXT "", 104, 10, 30, 200, 10, SS_CENTER + PUSHBUTTON "Cancel", IDCANCEL, 85, 44, 50, 15, WS_GROUP | WS_TABSTOP +} + +STRINGTABLE +BEGIN + 1001 "This application is requesting an ActiveX browser object\n" \ + "but the Mozilla Active X control is currently not installed." \ + "Do you wish to download and install it?" +END diff --git a/reactos/lib/shdocvw/Fr.rc b/reactos/lib/shdocvw/Fr.rc new file mode 100644 index 00000000000..cab59b45f47 --- /dev/null +++ b/reactos/lib/shdocvw/Fr.rc @@ -0,0 +1,36 @@ +/* + * Copyright 2004 Jonathan Ernst + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_FRENCH, SUBLANG_DEFAULT + +100 DIALOG LOADONCALL MOVEABLE DISCARDABLE 0, 0, 220, 62 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Téléchargement du contrôle ActiveX de Mozilla" +FONT 8, "MS Shell Dlg" +{ + CONTROL "Progress1",1000,"msctls_progress32",WS_BORDER|PBS_SMOOTH,10,10,200,12 + LTEXT "", 104, 10, 30, 200, 10, SS_CENTER + PUSHBUTTON "Annuler", IDCANCEL, 85, 44, 50, 15, WS_GROUP | WS_TABSTOP +} + +STRINGTABLE +BEGIN + 1001 "Cette application requiert un object navigateur ActiveX\n" \ + "mais le contrôle Active X de Mozilla n'est pas installé." \ + "Souhaitez-vous le télécharger et l'installer ?" +END diff --git a/reactos/lib/shdocvw/Pt.rc b/reactos/lib/shdocvw/Pt.rc new file mode 100644 index 00000000000..a0f3796962e --- /dev/null +++ b/reactos/lib/shdocvw/Pt.rc @@ -0,0 +1,36 @@ +/* + * Copyright 2005 Marcelo Duarte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_PORTUGUESE, SUBLANG_DEFAULT + +100 DIALOG LOADONCALL MOVEABLE DISCARDABLE 0, 0, 220, 62 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Baixando o controle Mozilla ActiveX" +FONT 8, "MS Shell Dlg" +{ + CONTROL "Progress1",1000,"msctls_progress32",WS_BORDER|PBS_SMOOTH,10,10,200,12 + LTEXT "", 104, 10, 30, 200, 10, SS_CENTER + PUSHBUTTON "Cancelar", IDCANCEL, 85, 44, 50, 15, WS_GROUP | WS_TABSTOP +} + +STRINGTABLE +BEGIN + 1001 "Esta aplicação requer um objecto ActiveX do navegador\n" \ + "mas o controle Mozilla ActiveX atualmente não está instalado." \ + "Você deseja baixá-lo e instalá-lo?" +END diff --git a/reactos/lib/shdocvw/shdocvw.rc b/reactos/lib/shdocvw/shdocvw.rc index 6909c380c22..77a8d2d569b 100644 --- a/reactos/lib/shdocvw/shdocvw.rc +++ b/reactos/lib/shdocvw/shdocvw.rc @@ -23,21 +23,7 @@ #include "version.rc" -LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT - -100 DIALOG LOADONCALL MOVEABLE DISCARDABLE 0, 0, 220, 62 -STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Downloading the Mozilla Active X control" -FONT 8, "Helv" -{ - CONTROL "Progress1",1000,"msctls_progress32",WS_BORDER|PBS_SMOOTH,10,10,200,12 - LTEXT "", 104, 10, 30, 200, 10, SS_CENTER - PUSHBUTTON "Cancel", IDCANCEL, 85, 44, 50, 15, WS_GROUP | WS_TABSTOP -} - -STRINGTABLE -BEGIN - 1001 "This application is requesting an ActiveX browser object\n" \ - "but the Mozilla Active X control is currently not installed." \ - "Do you wish to download and install it?" -END +#include "De.rc" +#include "En.rc" +#include "Fr.rc" +#include "Pt.rc" diff --git a/reactos/lib/shdocvw/shdocvw_main.c b/reactos/lib/shdocvw/shdocvw_main.c index b5e16701615..6d4cc4ac894 100644 --- a/reactos/lib/shdocvw/shdocvw_main.c +++ b/reactos/lib/shdocvw/shdocvw_main.c @@ -130,17 +130,16 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad) */ HRESULT WINAPI SHDOCVW_DllCanUnloadNow(void) { - HRESULT moz_can_unload = S_FALSE; + HRESULT moz_can_unload = S_OK; fnCanUnloadNow pCanUnloadNow; if (hMozCtl) { pCanUnloadNow = (fnCanUnloadNow) GetProcAddress(hMozCtl, "DllCanUnloadNow"); - moz_can_unload = pCanUnloadNow(); + if (pCanUnloadNow) + moz_can_unload = pCanUnloadNow(); } - else - moz_can_unload = S_OK; if (moz_can_unload == S_OK && SHDOCVW_refCount == 0) return S_OK; diff --git a/reactos/lib/shell32/Makefile.in b/reactos/lib/shell32/Makefile.in index 36b71403ad8..9d74b969c1d 100644 --- a/reactos/lib/shell32/Makefile.in +++ b/reactos/lib/shell32/Makefile.in @@ -37,6 +37,7 @@ C_SRCS = \ shfldr_desktop.c \ shfldr_fs.c \ shfldr_mycomp.c \ + shfldr_unixfs.c \ shlexec.c \ shlfileop.c \ shlfolder.c \ @@ -77,7 +78,7 @@ version16.res: version16.rc $(LDPATH) $(RC16) $(RC16FLAGS) -fo$@ $(SRCDIR)/version16.rc shell.spec.c: shell.spec version16.res - $(WINEBUILD) $(DEFS) $(DLLFLAGS) -o $@ --main-module $(MODULE) --res version16.res --dll $(SRCDIR)/shell.spec + $(WINEBUILD) $(DEFS) $(DLLFLAGS) --dll -o $@ --main-module $(MODULE) --res version16.res --export $(SRCDIR)/shell.spec authors.c: $(TOPSRCDIR)/AUTHORS (LC_ALL=C; export LC_ALL; echo 'const char * const SHELL_Authors[] = {' && \ diff --git a/reactos/lib/shell32/brsfolder.c b/reactos/lib/shell32/brsfolder.c index 198c18d40f8..63be4a38079 100644 --- a/reactos/lib/shell32/brsfolder.c +++ b/reactos/lib/shell32/brsfolder.c @@ -55,68 +55,95 @@ static inline DWORD BrowseFlagsToSHCONTF(UINT ulFlags) return SHCONTF_FOLDERS | (ulFlags & BIF_BROWSEINCLUDEFILES ? SHCONTF_NONFOLDERS : 0); } +/****************************************************************************** + * InitializeTreeView [Internal] + * + * Called from WM_INITDIALOG handler. + * + * PARAMS + * hwndParent [I] The BrowseForFolder dialog + * root [I] ITEMIDLIST of the root shell folder + */ static void InitializeTreeView(HWND hwndParent, LPCITEMIDLIST root) { - HIMAGELIST hImageList; - IShellFolder * lpsf; - HRESULT hr; - IEnumIDList * pEnumIL = NULL; - LPITEMIDLIST parentofroot; - parentofroot = ILClone(root); - ILRemoveLastID(parentofroot); + LPITEMIDLIST pidlParent, pidlChild; + HIMAGELIST hImageList; + HRESULT hr; + IShellFolder *lpsfParent, *lpsfRoot; + IEnumIDList * pEnumChildren = NULL; - hwndTreeView = GetDlgItem (hwndParent, IDD_TREEVIEW); - Shell_GetImageList(NULL, &hImageList); + TRACE("dlg=%p tree=%p\n", hwndParent, hwndTreeView ); + + hwndTreeView = GetDlgItem (hwndParent, IDD_TREEVIEW); + if (!hwndTreeView) { + FIXME("Could not get handle to treeview control! Error: %08lx\n", GetLastError()); + return; + } + Shell_GetImageList(NULL, &hImageList); - TRACE("dlg=%p tree=%p\n", hwndParent, hwndTreeView ); + if (hImageList) + TreeView_SetImageList(hwndTreeView, hImageList, 0); - if (hImageList && hwndTreeView) - TreeView_SetImageList(hwndTreeView, hImageList, 0); + /* We want to call InsertTreeViewItem down the code, in order to insert + * the root item of the treeview. Due to InsertTreeViewItem's signature, + * we need the following to do this: + * + * + An ITEMIDLIST corresponding to _the parent_ of root. + * + An ITEMIDLIST, which is a relative path from root's parent to root + * (containing a single SHITEMID). + * + An IShellFolder interface pointer of root's parent folder. + * + * If root is 'Desktop', then root's parent is also 'Desktop'. + */ - if (_ILIsDesktop (root)) { - hr = SHGetDesktopFolder(&lpsf); - } else { - IShellFolder * lpsfdesktop; + pidlParent = ILClone(root); + ILRemoveLastID(pidlParent); + pidlChild = ILClone(ILFindLastID(root)); + + if (_ILIsDesktop(pidlParent)) { + hr = SHGetDesktopFolder(&lpsfParent); + } else { + IShellFolder *lpsfDesktop; + hr = SHGetDesktopFolder(&lpsfDesktop); + if (!SUCCEEDED(hr)) { + WARN("SHGetDesktopFolder failed! hr = %08lx\n", hr); + return; + } + hr = IShellFolder_BindToObject(lpsfDesktop, pidlParent, 0, &IID_IShellFolder, (LPVOID*)&lpsfParent); + IShellFolder_Release(lpsfDesktop); + } + + if (!SUCCEEDED(hr)) { + WARN("Could not bind to parent shell folder! hr = %08lx\n", hr); + return; + } - hr = SHGetDesktopFolder(&lpsfdesktop); - if (SUCCEEDED(hr)) { - hr = IShellFolder_BindToObject(lpsfdesktop, parentofroot, 0,(REFIID)&IID_IShellFolder,(LPVOID *)&lpsf); - IShellFolder_Release(lpsfdesktop); - } - } - if (SUCCEEDED(hr)) - { - IShellFolder * pSFRoot; - if (_ILIsPidlSimple(root)) - { - pSFRoot = lpsf; - IShellFolder_AddRef(pSFRoot); - } - else - hr = IShellFolder_BindToObject(lpsf,ILFindLastID(root),0,&IID_IShellFolder,(LPVOID *)&pSFRoot); - if (SUCCEEDED(hr)) - { - hr = IShellFolder_EnumObjects( - pSFRoot, - hwndParent, - BrowseFlagsToSHCONTF(lpBrowseInfo->ulFlags), - &pEnumIL); - IShellFolder_Release(pSFRoot); - } - } + if (pidlChild && pidlChild->mkid.cb) { + hr = IShellFolder_BindToObject(lpsfParent, pidlChild, 0, &IID_IShellFolder, (LPVOID*)&lpsfRoot); + } else { + lpsfRoot = lpsfParent; + hr = IShellFolder_AddRef(lpsfParent); + } + + if (!SUCCEEDED(hr)) { + WARN("Could not bind to root shell folder! hr = %08lx\n", hr); + IShellFolder_Release(lpsfParent); + return; + } - if (SUCCEEDED(hr) && hwndTreeView) - { - TreeView_DeleteAllItems(hwndTreeView); - TreeView_Expand(hwndTreeView, - InsertTreeViewItem(lpsf, _ILIsPidlSimple(root) ? root : ILFindLastID(root), parentofroot, pEnumIL, TVI_ROOT), - TVE_EXPAND); - } + hr = IShellFolder_EnumObjects(lpsfRoot, hwndParent, BrowseFlagsToSHCONTF(lpBrowseInfo->ulFlags), &pEnumChildren); + if (!SUCCEEDED(hr)) { + WARN("Could not get child iterator! hr = %08lx\n", hr); + IShellFolder_Release(lpsfParent); + IShellFolder_Release(lpsfRoot); + return; + } - if (SUCCEEDED(hr)) - IShellFolder_Release(lpsf); + TreeView_DeleteAllItems(hwndTreeView); + TreeView_Expand(hwndTreeView, InsertTreeViewItem(lpsfParent, pidlChild, pidlParent, pEnumChildren, TVI_ROOT), TVE_EXPAND); - TRACE("done\n"); + IShellFolder_Release(lpsfRoot); + IShellFolder_Release(lpsfParent); } static int GetIcon(LPITEMIDLIST lpi, UINT uFlags) @@ -149,12 +176,27 @@ static void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, LPTVITEMW lpTV_ITEM) typedef struct tagID { - LPSHELLFOLDER lpsfParent; - LPITEMIDLIST lpi; - LPITEMIDLIST lpifq; - IEnumIDList* pEnumIL; + LPSHELLFOLDER lpsfParent; /* IShellFolder of the parent */ + LPITEMIDLIST lpi; /* PIDL relativ to parent */ + LPITEMIDLIST lpifq; /* Fully qualified PIDL */ + IEnumIDList* pEnumIL; /* Children iterator */ } TV_ITEMDATA, *LPTV_ITEMDATA; +/****************************************************************************** + * GetName [Internal] + * + * Query a shell folder for the display name of one of it's children + * + * PARAMS + * lpsf [I] IShellFolder interface of the folder to be queried. + * lpi [I] ITEMIDLIST of the child, relative to parent + * dwFlags [I] as in IShellFolder::GetDisplayNameOf + * lpFriendlyName [O] The desired display name in unicode + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ static BOOL GetName(LPSHELLFOLDER lpsf, LPCITEMIDLIST lpi, DWORD dwFlags, LPWSTR lpFriendlyName) { BOOL bSuccess=TRUE; @@ -175,7 +217,22 @@ static BOOL GetName(LPSHELLFOLDER lpsf, LPCITEMIDLIST lpi, DWORD dwFlags, LPWSTR return bSuccess; } -static HTREEITEM InsertTreeViewItem(IShellFolder * lpsf, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlParent, IEnumIDList* pEnumIL, HTREEITEM hParent) +/****************************************************************************** + * InsertTreeViewItem [Internal] + * + * PARAMS + * lpsf [I] IShellFolder interface of the item's parent shell folder + * pidl [I] ITEMIDLIST of the child to insert, relativ to parent + * pidlParent [I] ITEMIDLIST of the parent shell folder + * pEnumIL [I] Iterator for the children of the item to be inserted + * hParent [I] The treeview-item that represents the parent shell folder + * + * RETURNS + * Success: Handle to the created and inserted treeview-item + * Failure: NULL + */ +static HTREEITEM InsertTreeViewItem(IShellFolder * lpsf, LPCITEMIDLIST pidl, + LPCITEMIDLIST pidlParent, IEnumIDList* pEnumIL, HTREEITEM hParent) { TVITEMW tvi; TVINSERTSTRUCTW tvins; @@ -211,6 +268,18 @@ static HTREEITEM InsertTreeViewItem(IShellFolder * lpsf, LPCITEMIDLIST pidl, LPC return (HTREEITEM)TreeView_InsertItemW(hwndTreeView, &tvins); } +/****************************************************************************** + * FillTreeView [Internal] + * + * For each child (given by lpe) of the parent shell folder, which is given by + * lpsf and whose PIDL is pidl, insert a treeview-item right under hParent + * + * PARAMS + * lpsf [I] IShellFolder interface of the parent shell folder + * pidl [I] ITEMIDLIST of the parent shell folder + * hParent [I] The treeview item that represents the parent shell folder + * lpe [I] An iterator for the children of the parent shell folder + */ static void FillTreeView(IShellFolder * lpsf, LPITEMIDLIST pidl, HTREEITEM hParent, IEnumIDList* lpe) { HTREEITEM hPrev = 0; @@ -219,7 +288,11 @@ static void FillTreeView(IShellFolder * lpsf, LPITEMIDLIST pidl, HTREEITEM hPar HRESULT hr; HWND hwnd=GetParent(hwndTreeView); - TRACE("%p %p %x\n",lpsf, pidl, (INT)hParent); + TRACE("%p %p %x %p\n",lpsf, pidl, (INT)hParent, lpe); + + /* No IEnumIDList -> No children */ + if (!lpe) return; + SetCapture(GetParent(hwndTreeView)); SetCursor(LoadCursorA(0, (LPSTR)IDC_WAIT)); diff --git a/reactos/lib/shell32/classes.c b/reactos/lib/shell32/classes.c index 0e47f6f9fb4..f5e8c382951 100644 --- a/reactos/lib/shell32/classes.c +++ b/reactos/lib/shell32/classes.c @@ -116,7 +116,7 @@ BOOL HCR_MapTypeToValueA(LPCSTR szExtension, LPSTR szFileType, DWORD len, BOOL b BOOL HCR_GetExecuteCommandW( HKEY hkeyClass, LPCWSTR szClass, LPCWSTR szVerb, LPWSTR szDest, DWORD len ) { - static const WCHAR swShell[] = {'\\','s','h','e','l','l','\\',0}; + static const WCHAR swShell[] = {'s','h','e','l','l','\\',0}; static const WCHAR swCommand[] = {'\\','c','o','m','m','a','n','d',0}; BOOL ret = FALSE; diff --git a/reactos/lib/shell32/clipboard.c b/reactos/lib/shell32/clipboard.c index 71c929365b9..9f06d9703d3 100644 --- a/reactos/lib/shell32/clipboard.c +++ b/reactos/lib/shell32/clipboard.c @@ -26,7 +26,7 @@ * - a right mousebutton-copy sets the following formats: * classic: * Shell IDList Array - * Prefered Drop Effect + * Preferred Drop Effect * Shell Object Offsets * HDROP * FileName diff --git a/reactos/lib/shell32/dialogs.c b/reactos/lib/shell32/dialogs.c index 522e005819d..a243768bb63 100644 --- a/reactos/lib/shell32/dialogs.c +++ b/reactos/lib/shell32/dialogs.c @@ -127,9 +127,9 @@ INT_PTR CALLBACK RunDlgProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lPar case WM_INITDIALOG : prfdp = (RUNFILEDLGPARAMS *)lParam ; SetWindowTextA (hwnd, prfdp->lpstrTitle) ; - SetClassLongA (hwnd, GCL_HICON, (LPARAM)prfdp->hIcon) ; - SendMessageA (GetDlgItem (hwnd, 12297), STM_SETICON, - (WPARAM)LoadIconA (NULL, (LPSTR)IDI_WINLOGO), 0); + SetClassLongPtrW (hwnd, GCLP_HICON, (LPARAM)prfdp->hIcon) ; + SendMessageW (GetDlgItem (hwnd, 12297), STM_SETICON, + (WPARAM)LoadIconW (NULL, (LPCWSTR)IDI_WINLOGO), 0); FillList (GetDlgItem (hwnd, 12298), NULL) ; SetFocus (GetDlgItem (hwnd, 12298)) ; return TRUE ; @@ -283,7 +283,7 @@ void FillList (HWND hCb, char *pszLatest) if (NULL != pszLatest) { - if (!strcasecmp (pszCmd, pszLatest)) + if (!lstrcmpiA(pszCmd, pszLatest)) { /* sprintf (szDbgMsg, "Found existing (%d).\n", Nix) ; diff --git a/reactos/lib/shell32/enumidlist.c b/reactos/lib/shell32/enumidlist.c index 1d78e29cbe3..072e3288f3d 100644 --- a/reactos/lib/shell32/enumidlist.c +++ b/reactos/lib/shell32/enumidlist.c @@ -25,6 +25,7 @@ #define COBJMACROS #include "wine/debug.h" +#include "wine/unicode.h" #include "windef.h" #include "winbase.h" #include "winreg.h" @@ -103,24 +104,27 @@ BOOL AddToEnumList( */ BOOL CreateFolderEnumList( IEnumIDList *list, - LPCSTR lpszPath, + LPCWSTR lpszPath, DWORD dwFlags) { LPITEMIDLIST pidl=NULL; - WIN32_FIND_DATAA stffile; + WIN32_FIND_DATAW stffile; HANDLE hFile; - CHAR szPath[MAX_PATH]; + WCHAR szPath[MAX_PATH]; BOOL succeeded = TRUE; + const static WCHAR stars[] = { '*','.','*',0 }; + const static WCHAR dot[] = { '.',0 }; + const static WCHAR dotdot[] = { '.','.',0 }; - TRACE("(%p)->(path=%s flags=0x%08lx) \n",list,debugstr_a(lpszPath),dwFlags); + TRACE("(%p)->(path=%s flags=0x%08lx) \n",list,debugstr_w(lpszPath),dwFlags); if(!lpszPath || !lpszPath[0]) return FALSE; - strcpy(szPath, lpszPath); - PathAddBackslashA(szPath); - strcat(szPath,"*.*"); + strcpyW(szPath, lpszPath); + PathAddBackslashW(szPath); + strcatW(szPath,stars); - hFile = FindFirstFileA(szPath,&stffile); + hFile = FindFirstFileW(szPath,&stffile); if ( hFile != INVALID_HANDLE_VALUE ) { BOOL findFinished = FALSE; @@ -132,21 +136,21 @@ BOOL CreateFolderEnumList( { if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && dwFlags & SHCONTF_FOLDERS && - strcmp (stffile.cFileName, ".") && strcmp (stffile.cFileName, "..")) + strcmpW(stffile.cFileName, dot) && strcmpW(stffile.cFileName, dotdot)) { - pidl = _ILCreateFromFindDataA(&stffile); + pidl = _ILCreateFromFindDataW(&stffile); succeeded = succeeded && AddToEnumList(list, pidl); } else if (!(stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && dwFlags & SHCONTF_NONFOLDERS) { - pidl = _ILCreateFromFindDataA(&stffile); + pidl = _ILCreateFromFindDataW(&stffile); succeeded = succeeded && AddToEnumList(list, pidl); } } if (succeeded) { - if (!FindNextFileA(hFile, &stffile)) + if (!FindNextFileW(hFile, &stffile)) { if (GetLastError() == ERROR_NO_MORE_FILES) findFinished = TRUE; diff --git a/reactos/lib/shell32/enumidlist.h b/reactos/lib/shell32/enumidlist.h index 53b8b5ff111..26972aa191a 100644 --- a/reactos/lib/shell32/enumidlist.h +++ b/reactos/lib/shell32/enumidlist.h @@ -25,6 +25,6 @@ BOOL AddToEnumList(IEnumIDList *list, LPITEMIDLIST pidl); /* Enumerates the folders and/or files (depending on dwFlags) in lpszPath and * adds them to the already-created list. */ -BOOL CreateFolderEnumList(IEnumIDList *list, LPCSTR lpszPath, DWORD dwFlags); +BOOL CreateFolderEnumList(IEnumIDList *list, LPCWSTR lpszPath, DWORD dwFlags); #endif /* ndef __ENUMIDLIST_H__ */ diff --git a/reactos/lib/shell32/folders.c b/reactos/lib/shell32/folders.c index 12b6fe4d52a..efa3b2bdcb7 100644 --- a/reactos/lib/shell32/folders.c +++ b/reactos/lib/shell32/folders.c @@ -328,7 +328,7 @@ static HRESULT WINAPI IExtractIconW_fnGetIconLocation( found = TRUE; } - else if (!strcasecmp(sTemp, "lnkfile")) + else if (!lstrcmpiA(sTemp, "lnkfile")) { /* extract icon from shell shortcut */ IShellFolder* dsf; diff --git a/reactos/lib/shell32/pidl.c b/reactos/lib/shell32/pidl.c index d0f4206c5b7..1c1cf1b9158 100644 --- a/reactos/lib/shell32/pidl.c +++ b/reactos/lib/shell32/pidl.c @@ -1,7 +1,7 @@ /* - * pidl Handling + * pidl Handling * - * Copyright 1998 Juergen Schmied + * Copyright 1998 Juergen Schmied * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -57,7 +57,7 @@ extern LPVOID WINAPI Alloc(INT); extern BOOL WINAPI Free(LPVOID); /************************************************************************* - * ILGetDisplayNameEx [SHELL32.186] + * ILGetDisplayNameEx [SHELL32.186] * * Retrieves the display name of an ItemIDList * @@ -75,106 +75,108 @@ extern BOOL WINAPI Free(LPVOID); */ BOOL WINAPI ILGetDisplayNameExA(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPSTR path, DWORD type) { - BOOL ret = FALSE; - WCHAR wPath[MAX_PATH]; + BOOL ret = FALSE; + WCHAR wPath[MAX_PATH]; - TRACE("%p %p %p %ld\n", psf, pidl, path, type); + TRACE("%p %p %p %ld\n", psf, pidl, path, type); - if (!pidl || !path) - return FALSE; + if (!pidl || !path) + return FALSE; - ret = ILGetDisplayNameExW(psf, pidl, wPath, type); - WideCharToMultiByte(CP_ACP, 0, wPath, -1, path, MAX_PATH, NULL, NULL); - TRACE("%p %p %s\n", psf, pidl, debugstr_a(path)); + ret = ILGetDisplayNameExW(psf, pidl, wPath, type); + WideCharToMultiByte(CP_ACP, 0, wPath, -1, path, MAX_PATH, NULL, NULL); + TRACE("%p %p %s\n", psf, pidl, debugstr_a(path)); - return ret; + return ret; } BOOL WINAPI ILGetDisplayNameExW(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPWSTR path, DWORD type) { - LPSHELLFOLDER psfParent, lsf = psf; - HRESULT ret = NO_ERROR; - LPCITEMIDLIST pidllast; - STRRET strret; - DWORD flag; + LPSHELLFOLDER psfParent, lsf = psf; + HRESULT ret = NO_ERROR; + LPCITEMIDLIST pidllast; + STRRET strret; + DWORD flag; - TRACE("%p %p %p %ld\n", psf, pidl, path, type); + TRACE("%p %p %p %ld\n", psf, pidl, path, type); - if (!pidl || !path) - return FALSE; + if (!pidl || !path) + return FALSE; - if (!lsf) - { - ret = SHGetDesktopFolder(&lsf); - if (FAILED(ret)) - return FALSE; - } + if (!lsf) + { + ret = SHGetDesktopFolder(&lsf); + if (FAILED(ret)) + return FALSE; + } - if (type >= 0 && type <= 2) - { - switch (type) - { - case ILGDN_FORPARSING: - flag = SHGDN_FORPARSING | SHGDN_FORADDRESSBAR; - break; - case ILGDN_NORMAL: - flag = SHGDN_NORMAL; - break; - case ILGDN_INFOLDER: - flag = SHGDN_INFOLDER; - break; - default: - FIXME("Unknown type parameter = %lx\n", type); - flag = SHGDN_FORPARSING | SHGDN_FORADDRESSBAR; - break; - } - if (!*(const WORD*)pidl || type == ILGDN_FORPARSING) - { - ret = IShellFolder_GetDisplayNameOf(lsf, pidl, flag, &strret); - if (SUCCEEDED(ret)) - { - ret = StrRetToStrNW(path, MAX_PATH, &strret, pidl); - } - } - else - { - ret = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psfParent, &pidllast); - if (SUCCEEDED(ret)) - { - ret = IShellFolder_GetDisplayNameOf(psfParent, pidllast, flag, &strret); - if (SUCCEEDED(ret)) - { - ret = StrRetToStrNW(path, MAX_PATH, &strret, pidllast); - } - IShellFolder_Release(psfParent); - } - } - } + if (type >= 0 && type <= 2) + { + switch (type) + { + case ILGDN_FORPARSING: + flag = SHGDN_FORPARSING | SHGDN_FORADDRESSBAR; + break; + case ILGDN_NORMAL: + flag = SHGDN_NORMAL; + break; + case ILGDN_INFOLDER: + flag = SHGDN_INFOLDER; + break; + default: + FIXME("Unknown type parameter = %lx\n", type); + flag = SHGDN_FORPARSING | SHGDN_FORADDRESSBAR; + break; + } + if (!*(const WORD*)pidl || type == ILGDN_FORPARSING) + { + ret = IShellFolder_GetDisplayNameOf(lsf, pidl, flag, &strret); + if (SUCCEEDED(ret)) + { + ret = StrRetToStrNW(path, MAX_PATH, &strret, pidl); + } + } + else + { + ret = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psfParent, &pidllast); + if (SUCCEEDED(ret)) + { + ret = IShellFolder_GetDisplayNameOf(psfParent, pidllast, flag, &strret); + if (SUCCEEDED(ret)) + { + ret = StrRetToStrNW(path, MAX_PATH, &strret, pidllast); + } + IShellFolder_Release(psfParent); + } + } + } - TRACE("%p %p %s\n", psf, pidl, debugstr_w(path)); + TRACE("%p %p %s\n", psf, pidl, debugstr_w(path)); - if (!psf) - IShellFolder_Release(lsf); - return SUCCEEDED(ret); + if (!psf) + IShellFolder_Release(lsf); + return SUCCEEDED(ret); } BOOL WINAPI ILGetDisplayNameEx(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPVOID path, DWORD type) { - TRACE_(shell)("%p %p %p %ld\n", psf, pidl, path, type); - if (SHELL_OsIsUnicode()) - return ILGetDisplayNameExW(psf, pidl, path, type); - return ILGetDisplayNameExA(psf, pidl, path, type); + TRACE_(shell)("%p %p %p %ld\n", psf, pidl, path, type); + + if (SHELL_OsIsUnicode()) + return ILGetDisplayNameExW(psf, pidl, path, type); + return ILGetDisplayNameExA(psf, pidl, path, type); } /************************************************************************* - * ILGetDisplayName [SHELL32.15] + * ILGetDisplayName [SHELL32.15] */ BOOL WINAPI ILGetDisplayName(LPCITEMIDLIST pidl, LPVOID path) { - TRACE_(shell)("%p %p\n", pidl, path); - if (SHELL_OsIsUnicode()) - return ILGetDisplayNameExW(NULL, pidl, path, ILGDN_FORPARSING); - return ILGetDisplayNameExA(NULL, pidl, path, ILGDN_FORPARSING); + TRACE_(shell)("%p %p\n", pidl, path); + + if (SHELL_OsIsUnicode()) + return ILGetDisplayNameExW(NULL, pidl, path, ILGDN_FORPARSING); + return ILGetDisplayNameExA(NULL, pidl, path, ILGDN_FORPARSING); } /************************************************************************* @@ -185,20 +187,21 @@ BOOL WINAPI ILGetDisplayName(LPCITEMIDLIST pidl, LPVOID path) */ LPITEMIDLIST WINAPI ILFindLastID(LPCITEMIDLIST pidl) { - LPCITEMIDLIST pidlLast = pidl; + LPCITEMIDLIST pidlLast = pidl; - TRACE("(pidl=%p)\n",pidl); + TRACE("(pidl=%p)\n",pidl); - if (!pidl) - return NULL; + if (!pidl) + return NULL; - while (pidl->mkid.cb) - { - pidlLast = pidl; - pidl = ILGetNext(pidl); - } - return (LPITEMIDLIST)pidlLast; + while (pidl->mkid.cb) + { + pidlLast = pidl; + pidl = ILGetNext(pidl); + } + return (LPITEMIDLIST)pidlLast; } + /************************************************************************* * ILRemoveLastID [SHELL32.17] * @@ -207,12 +210,12 @@ LPITEMIDLIST WINAPI ILFindLastID(LPCITEMIDLIST pidl) */ BOOL WINAPI ILRemoveLastID(LPITEMIDLIST pidl) { - TRACE_(shell)("pidl=%p\n",pidl); + TRACE_(shell)("pidl=%p\n",pidl); - if (!pidl || !pidl->mkid.cb) - return 0; - ILFindLastID(pidl)->mkid.cb = 0; - return 1; + if (!pidl || !pidl->mkid.cb) + return 0; + ILFindLastID(pidl)->mkid.cb = 0; + return 1; } /************************************************************************* @@ -222,22 +225,24 @@ BOOL WINAPI ILRemoveLastID(LPITEMIDLIST pidl) * duplicate an idlist */ LPITEMIDLIST WINAPI ILClone (LPCITEMIDLIST pidl) -{ DWORD len; - LPITEMIDLIST newpidl; +{ + DWORD len; + LPITEMIDLIST newpidl; - if (!pidl) - return NULL; + if (!pidl) + return NULL; - len = ILGetSize(pidl); - newpidl = (LPITEMIDLIST)SHAlloc(len); - if (newpidl) - memcpy(newpidl,pidl,len); + len = ILGetSize(pidl); + newpidl = (LPITEMIDLIST)SHAlloc(len); + if (newpidl) + memcpy(newpidl,pidl,len); - TRACE("pidl=%p newpidl=%p\n",pidl, newpidl); - pdump(pidl); + TRACE("pidl=%p newpidl=%p\n",pidl, newpidl); + pdump(pidl); - return newpidl; + return newpidl; } + /************************************************************************* * ILCloneFirst [SHELL32.19] * @@ -245,27 +250,28 @@ LPITEMIDLIST WINAPI ILClone (LPCITEMIDLIST pidl) * duplicates the first idlist of a complex pidl */ LPITEMIDLIST WINAPI ILCloneFirst(LPCITEMIDLIST pidl) -{ DWORD len; - LPITEMIDLIST pidlNew = NULL; +{ + DWORD len; + LPITEMIDLIST pidlNew = NULL; - TRACE("pidl=%p \n",pidl); - pdump(pidl); + TRACE("pidl=%p \n",pidl); + pdump(pidl); - if (pidl) - { - len = pidl->mkid.cb; - pidlNew = (LPITEMIDLIST) SHAlloc (len+2); - if (pidlNew) - { - memcpy(pidlNew,pidl,len+2); /* 2 -> mind a desktop pidl */ + if (pidl) + { + len = pidl->mkid.cb; + pidlNew = (LPITEMIDLIST) SHAlloc (len+2); + if (pidlNew) + { + memcpy(pidlNew,pidl,len+2); /* 2 -> mind a desktop pidl */ - if (len) - ILGetNext(pidlNew)->mkid.cb = 0x00; - } - } - TRACE("-- newpidl=%p\n",pidlNew); + if (len) + ILGetNext(pidlNew)->mkid.cb = 0x00; + } + } + TRACE("-- newpidl=%p\n",pidlNew); - return pidlNew; + return pidlNew; } /************************************************************************* @@ -275,51 +281,58 @@ LPITEMIDLIST WINAPI ILCloneFirst(LPCITEMIDLIST pidl) * the first two bytes are the len, the pidl is following then */ HRESULT WINAPI ILLoadFromStream (IStream * pStream, LPITEMIDLIST * ppPidl) -{ WORD wLen = 0; - DWORD dwBytesRead; - HRESULT ret = E_FAIL; +{ + WORD wLen = 0; + DWORD dwBytesRead; + HRESULT ret = E_FAIL; - TRACE_(shell)("%p %p\n", pStream , ppPidl); + TRACE_(shell)("%p %p\n", pStream , ppPidl); - if (*ppPidl) - { SHFree(*ppPidl); - *ppPidl = NULL; - } + if (*ppPidl) + { + SHFree(*ppPidl); + *ppPidl = NULL; + } - IStream_AddRef (pStream); + IStream_AddRef (pStream); - if (SUCCEEDED(IStream_Read(pStream, (LPVOID)&wLen, 2, &dwBytesRead))) - { - TRACE("PIDL length is %d\n", wLen); - if (wLen != 0) { - *ppPidl = SHAlloc (wLen); - if (SUCCEEDED(IStream_Read(pStream, *ppPidl , wLen, &dwBytesRead))) { - TRACE("Stream read OK\n"); - ret = S_OK; - } else { - WARN("reading pidl failed\n"); - SHFree(*ppPidl); - *ppPidl = NULL; - } - } else { - *ppPidl = NULL; - ret = S_OK; - } - } + if (SUCCEEDED(IStream_Read(pStream, (LPVOID)&wLen, 2, &dwBytesRead))) + { + TRACE("PIDL length is %d\n", wLen); + if (wLen != 0) + { + *ppPidl = SHAlloc (wLen); + if (SUCCEEDED(IStream_Read(pStream, *ppPidl , wLen, &dwBytesRead))) + { + TRACE("Stream read OK\n"); + ret = S_OK; + } + else + { + WARN("reading pidl failed\n"); + SHFree(*ppPidl); + *ppPidl = NULL; + } + } + else + { + *ppPidl = NULL; + ret = S_OK; + } + } - /* we are not yet fully compatible */ - if (*ppPidl && !pcheck(*ppPidl)) - { - WARN("Check failed\n"); - SHFree(*ppPidl); - *ppPidl = NULL; - } + /* we are not yet fully compatible */ + if (*ppPidl && !pcheck(*ppPidl)) + { + WARN("Check failed\n"); + SHFree(*ppPidl); + *ppPidl = NULL; + } - - IStream_Release (pStream); - TRACE("done\n"); - return ret; + IStream_Release (pStream); + TRACE("done\n"); + return ret; } /************************************************************************* @@ -330,30 +343,29 @@ HRESULT WINAPI ILLoadFromStream (IStream * pStream, LPITEMIDLIST * ppPidl) */ HRESULT WINAPI ILSaveToStream (IStream * pStream, LPCITEMIDLIST pPidl) { - LPCITEMIDLIST pidl; - WORD wLen = 0; - HRESULT ret = E_FAIL; + LPCITEMIDLIST pidl; + WORD wLen = 0; + HRESULT ret = E_FAIL; - TRACE_(shell)("%p %p\n", pStream, pPidl); + TRACE_(shell)("%p %p\n", pStream, pPidl); - IStream_AddRef (pStream); + IStream_AddRef (pStream); - pidl = pPidl; - while (pidl->mkid.cb) - { - wLen += sizeof(WORD) + pidl->mkid.cb; - pidl = ILGetNext(pidl); - } + pidl = pPidl; + while (pidl->mkid.cb) + { + wLen += sizeof(WORD) + pidl->mkid.cb; + pidl = ILGetNext(pidl); + } - if (SUCCEEDED(IStream_Write(pStream, (LPVOID)&wLen, 2, NULL))) - { - if (SUCCEEDED(IStream_Write(pStream, pPidl, wLen, NULL))) - { ret = S_OK; - } - } - IStream_Release (pStream); + if (SUCCEEDED(IStream_Write(pStream, (LPVOID)&wLen, 2, NULL))) + { + if (SUCCEEDED(IStream_Write(pStream, pPidl, wLen, NULL))) + ret = S_OK; + } + IStream_Release (pStream); - return ret; + return ret; } /************************************************************************* @@ -375,45 +387,37 @@ HRESULT WINAPI ILSaveToStream (IStream * pStream, LPCITEMIDLIST pPidl) */ HRESULT WINAPI SHILCreateFromPathA(LPCSTR path, LPITEMIDLIST * ppidl, DWORD * attributes) { - LPSHELLFOLDER sf; - WCHAR lpszDisplayName[MAX_PATH]; - DWORD pchEaten; - HRESULT ret = E_FAIL; + WCHAR lpszDisplayName[MAX_PATH]; - TRACE_(shell)("%s %p 0x%08lx\n", path, ppidl, attributes ? *attributes : 0); + TRACE_(shell)("%s %p 0x%08lx\n", path, ppidl, attributes ? *attributes : 0); - if (!MultiByteToWideChar(CP_ACP, 0, path, -1, lpszDisplayName, MAX_PATH)) - lpszDisplayName[MAX_PATH-1] = 0; + if (!MultiByteToWideChar(CP_ACP, 0, path, -1, lpszDisplayName, MAX_PATH)) + lpszDisplayName[MAX_PATH-1] = 0; - if (SUCCEEDED (SHGetDesktopFolder(&sf))) - { - ret = IShellFolder_ParseDisplayName(sf, 0, NULL, lpszDisplayName, &pchEaten, ppidl, attributes); - IShellFolder_Release(sf); - } - return ret; + return SHILCreateFromPathW(lpszDisplayName, ppidl, attributes); } HRESULT WINAPI SHILCreateFromPathW(LPCWSTR path, LPITEMIDLIST * ppidl, DWORD * attributes) { - LPSHELLFOLDER sf; - DWORD pchEaten; - HRESULT ret = E_FAIL; + LPSHELLFOLDER sf; + DWORD pchEaten; + HRESULT ret = E_FAIL; - TRACE_(shell)("%s %p 0x%08lx\n", debugstr_w(path), ppidl, attributes ? *attributes : 0); + TRACE_(shell)("%s %p 0x%08lx\n", debugstr_w(path), ppidl, attributes ? *attributes : 0); - if (SUCCEEDED (SHGetDesktopFolder(&sf))) - { - ret = IShellFolder_ParseDisplayName(sf, 0, NULL, (LPWSTR)path, &pchEaten, ppidl, attributes); - IShellFolder_Release(sf); - } - return ret; + if (SUCCEEDED (SHGetDesktopFolder(&sf))) + { + ret = IShellFolder_ParseDisplayName(sf, 0, NULL, (LPWSTR)path, &pchEaten, ppidl, attributes); + IShellFolder_Release(sf); + } + return ret; } HRESULT WINAPI SHILCreateFromPathAW (LPCVOID path, LPITEMIDLIST * ppidl, DWORD * attributes) { - if ( SHELL_OsIsUnicode()) - return SHILCreateFromPathW (path, ppidl, attributes); - return SHILCreateFromPathA (path, ppidl, attributes); + if ( SHELL_OsIsUnicode()) + return SHILCreateFromPathW (path, ppidl, attributes); + return SHILCreateFromPathA (path, ppidl, attributes); } /************************************************************************* @@ -422,9 +426,9 @@ HRESULT WINAPI SHILCreateFromPathAW (LPCVOID path, LPITEMIDLIST * ppidl, DWORD * * Create an ItemIDList to one of the special folders. * PARAMS - * hwndOwner [in] - * nFolder [in] CSIDL_xxxxx - * fCreate [in] Create folder if it does not exist + * hwndOwner [in] + * nFolder [in] CSIDL_xxxxx + * fCreate [in] Create folder if it does not exist * * RETURNS * Success: The newly created pidl @@ -436,14 +440,15 @@ HRESULT WINAPI SHILCreateFromPathAW (LPCVOID path, LPITEMIDLIST * ppidl, DWORD * * shells IMalloc interface, aka ILFree. */ LPITEMIDLIST WINAPI SHCloneSpecialIDList(HWND hwndOwner, DWORD nFolder, BOOL fCreate) -{ LPITEMIDLIST ppidl; - TRACE_(shell)("(hwnd=%p,csidl=0x%lx,%s).\n", hwndOwner, nFolder, fCreate ? "T" : "F"); +{ + LPITEMIDLIST ppidl; + TRACE_(shell)("(hwnd=%p,csidl=0x%lx,%s).\n", hwndOwner, nFolder, fCreate ? "T" : "F"); - if (fCreate) - nFolder |= CSIDL_FLAG_CREATE; + if (fCreate) + nFolder |= CSIDL_FLAG_CREATE; - SHGetSpecialFolderLocation(hwndOwner, nFolder, &ppidl); - return ppidl; + SHGetSpecialFolderLocation(hwndOwner, nFolder, &ppidl); + return ppidl; } /************************************************************************* @@ -461,21 +466,22 @@ LPITEMIDLIST WINAPI SHCloneSpecialIDList(HWND hwndOwner, DWORD nFolder, BOOL fCr * exported by ordinal. */ LPITEMIDLIST WINAPI ILGlobalClone(LPCITEMIDLIST pidl) -{ DWORD len; - LPITEMIDLIST newpidl; +{ + DWORD len; + LPITEMIDLIST newpidl; - if (!pidl) - return NULL; + if (!pidl) + return NULL; - len = ILGetSize(pidl); - newpidl = (LPITEMIDLIST)Alloc(len); - if (newpidl) - memcpy(newpidl,pidl,len); + len = ILGetSize(pidl); + newpidl = (LPITEMIDLIST)Alloc(len); + if (newpidl) + memcpy(newpidl,pidl,len); - TRACE("pidl=%p newpidl=%p\n",pidl, newpidl); - pdump(pidl); + TRACE("pidl=%p newpidl=%p\n",pidl, newpidl); + pdump(pidl); - return newpidl; + return newpidl; } /************************************************************************* @@ -484,42 +490,45 @@ LPITEMIDLIST WINAPI ILGlobalClone(LPCITEMIDLIST pidl) */ BOOL WINAPI ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { - char szData1[MAX_PATH]; - char szData2[MAX_PATH]; + char szData1[MAX_PATH]; + char szData2[MAX_PATH]; - LPCITEMIDLIST pidltemp1 = pidl1; - LPCITEMIDLIST pidltemp2 = pidl2; + LPCITEMIDLIST pidltemp1 = pidl1; + LPCITEMIDLIST pidltemp2 = pidl2; - TRACE("pidl1=%p pidl2=%p\n",pidl1, pidl2); + TRACE("pidl1=%p pidl2=%p\n",pidl1, pidl2); - /* explorer reads from registry directly (StreamMRU), - so we can only check here */ - if ((!pcheck (pidl1)) || (!pcheck (pidl2))) return FALSE; + /* + * Explorer reads from registry directly (StreamMRU), + * so we can only check here + */ + if (!pcheck(pidl1) || !pcheck (pidl2)) + return FALSE; - pdump (pidl1); - pdump (pidl2); + pdump (pidl1); + pdump (pidl2); - if ( (!pidl1) || (!pidl2) ) return FALSE; + if (!pidl1 || !pidl2) + return FALSE; - while (pidltemp1->mkid.cb && pidltemp2->mkid.cb) - { - _ILSimpleGetText(pidltemp1, szData1, MAX_PATH); - _ILSimpleGetText(pidltemp2, szData2, MAX_PATH); + while (pidltemp1->mkid.cb && pidltemp2->mkid.cb) + { + _ILSimpleGetText(pidltemp1, szData1, MAX_PATH); + _ILSimpleGetText(pidltemp2, szData2, MAX_PATH); - if (strcasecmp ( szData1, szData2 )!=0 ) - return FALSE; + if (strcasecmp( szData1, szData2 )) + return FALSE; - pidltemp1 = ILGetNext(pidltemp1); - pidltemp2 = ILGetNext(pidltemp2); - } + pidltemp1 = ILGetNext(pidltemp1); + pidltemp2 = ILGetNext(pidltemp2); + } - if (!pidltemp1->mkid.cb && !pidltemp2->mkid.cb) - { - return TRUE; - } + if (!pidltemp1->mkid.cb && !pidltemp2->mkid.cb) + return TRUE; - return FALSE; + return FALSE; } + /************************************************************************* * ILIsParent [SHELL32.23] * @@ -542,35 +551,37 @@ BOOL WINAPI ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) */ BOOL WINAPI ILIsParent(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild, BOOL bImmediate) { - char szData1[MAX_PATH]; - char szData2[MAX_PATH]; + char szData1[MAX_PATH]; + char szData2[MAX_PATH]; + LPCITEMIDLIST pParent = pidlParent; + LPCITEMIDLIST pChild = pidlChild; - LPCITEMIDLIST pParent = pidlParent; - LPCITEMIDLIST pChild = pidlChild; + TRACE("%p %p %x\n", pidlParent, pidlChild, bImmediate); - TRACE("%p %p %x\n", pidlParent, pidlChild, bImmediate); + if (!pParent || !pChild) + return FALSE; - if (!pParent || !pChild) return FALSE; + while (pParent->mkid.cb && pChild->mkid.cb) + { + _ILSimpleGetText(pParent, szData1, MAX_PATH); + _ILSimpleGetText(pChild, szData2, MAX_PATH); - while (pParent->mkid.cb && pChild->mkid.cb) - { - _ILSimpleGetText(pParent, szData1, MAX_PATH); - _ILSimpleGetText(pChild, szData2, MAX_PATH); + if (strcasecmp( szData1, szData2 )) + return FALSE; - if (strcasecmp ( szData1, szData2 )!=0 ) - return FALSE; + pParent = ILGetNext(pParent); + pChild = ILGetNext(pChild); + } - pParent = ILGetNext(pParent); - pChild = ILGetNext(pChild); - } + /* child shorter or has equal length to parent */ + if (pParent->mkid.cb || !pChild->mkid.cb) + return FALSE; - if ( pParent->mkid.cb || ! pChild->mkid.cb) /* child shorter or has equal length to parent */ - return FALSE; + /* not immediate descent */ + if ( ILGetNext(pChild)->mkid.cb && bImmediate) + return FALSE; - if ( ILGetNext(pChild)->mkid.cb && bImmediate) /* not immediate descent */ - return FALSE; - - return TRUE; + return TRUE; } /************************************************************************* @@ -594,49 +605,47 @@ BOOL WINAPI ILIsParent(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild, BOOL b */ LPITEMIDLIST WINAPI ILFindChild(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { - char szData1[MAX_PATH]; - char szData2[MAX_PATH]; + char szData1[MAX_PATH]; + char szData2[MAX_PATH]; - LPCITEMIDLIST pidltemp1 = pidl1; - LPCITEMIDLIST pidltemp2 = pidl2; - LPCITEMIDLIST ret=NULL; + LPCITEMIDLIST pidltemp1 = pidl1; + LPCITEMIDLIST pidltemp2 = pidl2; + LPCITEMIDLIST ret=NULL; - TRACE("pidl1=%p pidl2=%p\n",pidl1, pidl2); + TRACE("pidl1=%p pidl2=%p\n",pidl1, pidl2); - /* explorer reads from registry directly (StreamMRU), - so we can only check here */ - if ((!pcheck (pidl1)) || (!pcheck (pidl2))) - return FALSE; + /* explorer reads from registry directly (StreamMRU), + so we can only check here */ + if ((!pcheck (pidl1)) || (!pcheck (pidl2))) + return FALSE; - pdump (pidl1); - pdump (pidl2); + pdump (pidl1); + pdump (pidl2); - if ( _ILIsDesktop(pidl1) ) - { - ret = pidl2; - } - else - { - while (pidltemp1->mkid.cb && pidltemp2->mkid.cb) - { - _ILSimpleGetText(pidltemp1, szData1, MAX_PATH); - _ILSimpleGetText(pidltemp2, szData2, MAX_PATH); + if (_ILIsDesktop(pidl1)) + { + ret = pidl2; + } + else + { + while (pidltemp1->mkid.cb && pidltemp2->mkid.cb) + { + _ILSimpleGetText(pidltemp1, szData1, MAX_PATH); + _ILSimpleGetText(pidltemp2, szData2, MAX_PATH); - if (strcasecmp(szData1,szData2)) - break; + if (strcasecmp(szData1,szData2)) + break; - pidltemp1 = ILGetNext(pidltemp1); - pidltemp2 = ILGetNext(pidltemp2); - ret = pidltemp2; - } + pidltemp1 = ILGetNext(pidltemp1); + pidltemp2 = ILGetNext(pidltemp2); + ret = pidltemp2; + } - if (pidltemp1->mkid.cb) - { - ret = NULL; /* elements of pidl1 left*/ - } - } - TRACE_(shell)("--- %p\n", ret); - return (LPITEMIDLIST)ret; /* pidl 1 is shorter */ + if (pidltemp1->mkid.cb) + ret = NULL; /* elements of pidl1 left*/ + } + TRACE_(shell)("--- %p\n", ret); + return (LPITEMIDLIST)ret; /* pidl 1 is shorter */ } /************************************************************************* @@ -660,40 +669,40 @@ LPITEMIDLIST WINAPI ILFindChild(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) */ LPITEMIDLIST WINAPI ILCombine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { - DWORD len1,len2; - LPITEMIDLIST pidlNew; + DWORD len1,len2; + LPITEMIDLIST pidlNew; - TRACE("pidl=%p pidl=%p\n",pidl1,pidl2); + TRACE("pidl=%p pidl=%p\n",pidl1,pidl2); - if(!pidl1 && !pidl2) return NULL; + if (!pidl1 && !pidl2) return NULL; - pdump (pidl1); - pdump (pidl2); + pdump (pidl1); + pdump (pidl2); - if(!pidl1) - { - pidlNew = ILClone(pidl2); - return pidlNew; - } + if (!pidl1) + { + pidlNew = ILClone(pidl2); + return pidlNew; + } - if(!pidl2) - { - pidlNew = ILClone(pidl1); - return pidlNew; - } + if (!pidl2) + { + pidlNew = ILClone(pidl1); + return pidlNew; + } - len1 = ILGetSize(pidl1)-2; - len2 = ILGetSize(pidl2); - pidlNew = SHAlloc(len1+len2); + len1 = ILGetSize(pidl1)-2; + len2 = ILGetSize(pidl2); + pidlNew = SHAlloc(len1+len2); - if (pidlNew) - { - memcpy(pidlNew,pidl1,len1); - memcpy(((BYTE *)pidlNew)+len1,pidl2,len2); - } + if (pidlNew) + { + memcpy(pidlNew,pidl1,len1); + memcpy(((BYTE *)pidlNew)+len1,pidl2,len2); + } - /* TRACE(pidl,"--new pidl=%p\n",pidlNew);*/ - return pidlNew; + /* TRACE(pidl,"--new pidl=%p\n",pidlNew);*/ + return pidlNew; } /************************************************************************* @@ -703,57 +712,61 @@ LPITEMIDLIST WINAPI ILCombine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) */ HRESULT WINAPI SHGetRealIDL(LPSHELLFOLDER lpsf, LPCITEMIDLIST pidlSimple, LPITEMIDLIST *pidlReal) { - IDataObject* pDataObj; - HRESULT hr = IShellFolder_GetUIObjectOf(lpsf, 0, 1, &pidlSimple, &IID_IDataObject, 0, (LPVOID*)&pDataObj); + IDataObject* pDataObj; + HRESULT hr; - if (SUCCEEDED(hr)) { - STGMEDIUM medium; - FORMATETC fmt; + hr = IShellFolder_GetUIObjectOf(lpsf, 0, 1, &pidlSimple, + &IID_IDataObject, 0, (LPVOID*)&pDataObj); + if (SUCCEEDED(hr)) + { + STGMEDIUM medium; + FORMATETC fmt; - fmt.cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLIST); - fmt.ptd = NULL; - fmt.dwAspect = DVASPECT_CONTENT; - fmt.lindex = -1; - fmt.tymed = TYMED_HGLOBAL; + fmt.cfFormat = RegisterClipboardFormatA(CFSTR_SHELLIDLIST); + fmt.ptd = NULL; + fmt.dwAspect = DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = TYMED_HGLOBAL; - hr = IDataObject_GetData(pDataObj, &fmt, &medium); + hr = IDataObject_GetData(pDataObj, &fmt, &medium); - IDataObject_Release(pDataObj); + IDataObject_Release(pDataObj); - if (SUCCEEDED(hr)) { - /*assert(pida->cidl==1);*/ - LPIDA pida = (LPIDA)GlobalLock(medium.u.hGlobal); + if (SUCCEEDED(hr)) + { + /*assert(pida->cidl==1);*/ + LPIDA pida = (LPIDA)GlobalLock(medium.u.hGlobal); - LPCITEMIDLIST pidl_folder = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]); - LPCITEMIDLIST pidl_child = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[1]); + LPCITEMIDLIST pidl_folder = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]); + LPCITEMIDLIST pidl_child = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[1]); - *pidlReal = ILCombine(pidl_folder, pidl_child); + *pidlReal = ILCombine(pidl_folder, pidl_child); - if (!*pidlReal) - hr = E_OUTOFMEMORY; + if (!*pidlReal) + hr = E_OUTOFMEMORY; - GlobalUnlock(medium.u.hGlobal); - GlobalFree(medium.u.hGlobal); - } - } + GlobalUnlock(medium.u.hGlobal); + GlobalFree(medium.u.hGlobal); + } + } - return hr; + return hr; } /************************************************************************* * SHLogILFromFSIL [SHELL32.95] * * NOTES - * pild = CSIDL_DESKTOP ret = 0 - * pild = CSIDL_DRIVES ret = 0 + * pild = CSIDL_DESKTOP ret = 0 + * pild = CSIDL_DRIVES ret = 0 */ LPITEMIDLIST WINAPI SHLogILFromFSIL(LPITEMIDLIST pidl) { - FIXME("(pidl=%p)\n",pidl); + FIXME("(pidl=%p)\n",pidl); - pdump(pidl); + pdump(pidl); - return 0; + return 0; } /************************************************************************* @@ -772,18 +785,20 @@ LPITEMIDLIST WINAPI SHLogILFromFSIL(LPITEMIDLIST pidl) */ UINT WINAPI ILGetSize(LPCITEMIDLIST pidl) { - LPCSHITEMID si = &(pidl->mkid); - UINT len=0; + LPCSHITEMID si = &(pidl->mkid); + UINT len=0; - if (pidl) - { while (si->cb) - { len += si->cb; - si = (LPCSHITEMID)(((const BYTE*)si)+si->cb); - } - len += 2; - } - TRACE("pidl=%p size=%u\n",pidl, len); - return len; + if (pidl) + { + while (si->cb) + { + len += si->cb; + si = (LPCSHITEMID)(((const BYTE*)si)+si->cb); + } + len += 2; + } + TRACE("pidl=%p size=%u\n",pidl, len); + return len; } /************************************************************************* @@ -804,21 +819,21 @@ UINT WINAPI ILGetSize(LPCITEMIDLIST pidl) */ LPITEMIDLIST WINAPI ILGetNext(LPCITEMIDLIST pidl) { - WORD len; + WORD len; - TRACE("%p\n", pidl); + TRACE("%p\n", pidl); - if(pidl) - { - len = pidl->mkid.cb; - if (len) - { - pidl = (LPCITEMIDLIST) (((const BYTE*)pidl)+len); - TRACE("-- %p\n", pidl); - return (LPITEMIDLIST)pidl; - } - } - return NULL; + if (pidl) + { + len = pidl->mkid.cb; + if (len) + { + pidl = (LPCITEMIDLIST) (((const BYTE*)pidl)+len); + TRACE("-- %p\n", pidl); + return (LPITEMIDLIST)pidl; + } + } + return NULL; } /************************************************************************* @@ -838,32 +853,28 @@ LPITEMIDLIST WINAPI ILGetNext(LPCITEMIDLIST pidl) */ LPITEMIDLIST WINAPI ILAppend(LPITEMIDLIST pidl, LPCITEMIDLIST item, BOOL bEnd) { - LPITEMIDLIST idlRet; + LPITEMIDLIST idlRet; - WARN("(pidl=%p,pidl=%p,%08u)semi-stub\n",pidl,item,bEnd); + WARN("(pidl=%p,pidl=%p,%08u)semi-stub\n",pidl,item,bEnd); - pdump (pidl); - pdump (item); + pdump (pidl); + pdump (item); - if (_ILIsDesktop(pidl)) - { - idlRet = ILClone(item); - if (pidl) - SHFree (pidl); - return idlRet; - } + if (_ILIsDesktop(pidl)) + { + idlRet = ILClone(item); + if (pidl) + SHFree (pidl); + return idlRet; + } - if (bEnd) - { - idlRet = ILCombine(pidl, item); - } - else - { - idlRet = ILCombine(item, pidl); - } + if (bEnd) + idlRet = ILCombine(pidl, item); + else + idlRet = ILCombine(item, pidl); - SHFree(pidl); - return idlRet; + SHFree(pidl); + return idlRet; } /************************************************************************* @@ -879,8 +890,9 @@ LPITEMIDLIST WINAPI ILAppend(LPITEMIDLIST pidl, LPCITEMIDLIST item, BOOL bEnd) */ void WINAPI ILFree(LPITEMIDLIST pidl) { - TRACE("(pidl=%p)\n",pidl); - if(pidl) SHFree(pidl); + TRACE("(pidl=%p)\n",pidl); + if (pidl) + SHFree(pidl); } /************************************************************************* @@ -896,10 +908,10 @@ void WINAPI ILFree(LPITEMIDLIST pidl) */ void WINAPI ILGlobalFree( LPITEMIDLIST pidl) { - TRACE("%p\n", pidl); + TRACE("%p\n", pidl); - if(!pidl) return; - Free(pidl); + if (pidl) + Free(pidl); } /************************************************************************* @@ -916,16 +928,15 @@ void WINAPI ILGlobalFree( LPITEMIDLIST pidl) * NOTES * exported by ordinal. */ - LPITEMIDLIST WINAPI ILCreateFromPathA (LPCSTR path) { - LPITEMIDLIST pidlnew = NULL; + LPITEMIDLIST pidlnew = NULL; - TRACE_(shell)("%s\n", debugstr_a(path)); + TRACE_(shell)("%s\n", debugstr_a(path)); - if (SUCCEEDED(SHILCreateFromPathA(path, &pidlnew, NULL))) - return pidlnew; - return NULL; + if (SUCCEEDED(SHILCreateFromPathA(path, &pidlnew, NULL))) + return pidlnew; + return NULL; } /************************************************************************* @@ -933,13 +944,13 @@ LPITEMIDLIST WINAPI ILCreateFromPathA (LPCSTR path) */ LPITEMIDLIST WINAPI ILCreateFromPathW (LPCWSTR path) { - LPITEMIDLIST pidlnew = NULL; + LPITEMIDLIST pidlnew = NULL; - TRACE_(shell)("%s\n", debugstr_w(path)); + TRACE_(shell)("%s\n", debugstr_w(path)); - if (SUCCEEDED(SHILCreateFromPathW(path, &pidlnew, NULL))) - return pidlnew; - return NULL; + if (SUCCEEDED(SHILCreateFromPathW(path, &pidlnew, NULL))) + return pidlnew; + return NULL; } /************************************************************************* @@ -947,9 +958,9 @@ LPITEMIDLIST WINAPI ILCreateFromPathW (LPCWSTR path) */ LPITEMIDLIST WINAPI ILCreateFromPathAW (LPCVOID path) { - if ( SHELL_OsIsUnicode()) - return ILCreateFromPathW (path); - return ILCreateFromPathA (path); + if ( SHELL_OsIsUnicode()) + return ILCreateFromPathW (path); + return ILCreateFromPathA (path); } /************************************************************************* @@ -982,42 +993,40 @@ LPITEMIDLIST WINAPI ILCreateFromPathAW (LPCVOID path) static HRESULT WINAPI _ILParsePathW(LPCWSTR path, LPWIN32_FIND_DATAW lpFindFile, BOOL bBindCtx, LPITEMIDLIST *ppidl, LPDWORD prgfInOut) { - LPSHELLFOLDER pSF = NULL; - LPBC pBC = NULL; - HRESULT ret; + LPSHELLFOLDER pSF = NULL; + LPBC pBC = NULL; + HRESULT ret; - TRACE("%s %p %d (%p)->%p (%p)->0x%lx\n", debugstr_w(path), lpFindFile, bBindCtx, - ppidl, ppidl ? *ppidl : NULL, - prgfInOut, prgfInOut ? *prgfInOut : 0); + TRACE("%s %p %d (%p)->%p (%p)->0x%lx\n", debugstr_w(path), lpFindFile, bBindCtx, + ppidl, ppidl ? *ppidl : NULL, + prgfInOut, prgfInOut ? *prgfInOut : 0); - ret = SHGetDesktopFolder(&pSF); - if (FAILED(ret)) - { - return ret; - } + ret = SHGetDesktopFolder(&pSF); + if (FAILED(ret)) + return ret; - if (lpFindFile || bBindCtx) - ret = IFileSystemBindData_Constructor(lpFindFile, &pBC); + if (lpFindFile || bBindCtx) + ret = IFileSystemBindData_Constructor(lpFindFile, &pBC); - if (SUCCEEDED(ret)) - { - ret = IShellFolder_ParseDisplayName(pSF, 0, pBC, (LPOLESTR)path, NULL, ppidl, prgfInOut); - } + if (SUCCEEDED(ret)) + { + ret = IShellFolder_ParseDisplayName(pSF, 0, pBC, (LPOLESTR)path, NULL, ppidl, prgfInOut); + } - if (pBC) - { - IBindCtx_Release(pBC); - pBC = NULL; - } + if (pBC) + { + IBindCtx_Release(pBC); + pBC = NULL; + } - IShellFolder_Release(pSF); + IShellFolder_Release(pSF); - if (!SUCCEEDED(ret) && ppidl) - *ppidl = NULL; + if (!SUCCEEDED(ret) && ppidl) + *ppidl = NULL; - TRACE("%s %p 0x%lx\n", debugstr_w(path), ppidl ? *ppidl : NULL, prgfInOut ? *prgfInOut : 0); + TRACE("%s %p 0x%lx\n", debugstr_w(path), ppidl ? *ppidl : NULL, prgfInOut ? *prgfInOut : 0); - return ret; + return ret; } /************************************************************************* @@ -1064,20 +1073,20 @@ LPITEMIDLIST WINAPI SHSimpleIDListFromPathA(LPCSTR lpszPath) LPITEMIDLIST WINAPI SHSimpleIDListFromPathW(LPCWSTR lpszPath) { - LPITEMIDLIST pidl = NULL; + LPITEMIDLIST pidl = NULL; - TRACE("%s\n", debugstr_w(lpszPath)); + TRACE("%s\n", debugstr_w(lpszPath)); - _ILParsePathW(lpszPath, NULL, TRUE, &pidl, NULL); - TRACE("%s %p\n", debugstr_w(lpszPath), pidl); - return pidl; + _ILParsePathW(lpszPath, NULL, TRUE, &pidl, NULL); + TRACE("%s %p\n", debugstr_w(lpszPath), pidl); + return pidl; } LPITEMIDLIST WINAPI SHSimpleIDListFromPathAW(LPCVOID lpszPath) { - if ( SHELL_OsIsUnicode()) - return SHSimpleIDListFromPathW (lpszPath); - return SHSimpleIDListFromPathA (lpszPath); + if ( SHELL_OsIsUnicode()) + return SHSimpleIDListFromPathW (lpszPath); + return SHSimpleIDListFromPathA (lpszPath); } /************************************************************************* @@ -1087,110 +1096,117 @@ LPITEMIDLIST WINAPI SHSimpleIDListFromPathAW(LPCVOID lpszPath) * the pidl can be a simple one. since we can't get the path out of the pidl * we have to take all data from the pidl */ -HRESULT WINAPI SHGetDataFromIDListA(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, int nFormat, LPVOID dest, int len) +HRESULT WINAPI SHGetDataFromIDListA(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, + int nFormat, LPVOID dest, int len) { - TRACE_(shell)("sf=%p pidl=%p 0x%04x %p 0x%04x stub\n",psf,pidl,nFormat,dest,len); + LPSTR filename, shortname; + WIN32_FIND_DATAA * pfd; - pdump(pidl); - if (!psf || !dest ) return E_INVALIDARG; + TRACE_(shell)("sf=%p pidl=%p 0x%04x %p 0x%04x stub\n",psf,pidl,nFormat,dest,len); - switch (nFormat) - { - case SHGDFIL_FINDDATA: - { - LPSTR filename, shortname; - WIN32_FIND_DATAA * pfd = dest; + pdump(pidl); + if (!psf || !dest) + return E_INVALIDARG; - if (_ILIsDrive(pidl)) - return E_INVALIDARG; + switch (nFormat) + { + case SHGDFIL_FINDDATA: + pfd = dest; - if (len < (int)sizeof(WIN32_FIND_DATAA)) return E_INVALIDARG; + if (_ILIsDrive(pidl)) + return E_INVALIDARG; - ZeroMemory(pfd, sizeof (WIN32_FIND_DATAA)); - _ILGetFileDateTime( pidl, &(pfd->ftLastWriteTime)); - pfd->dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0); - pfd->nFileSizeLow = _ILGetFileSize ( pidl, NULL, 0); + if (len < sizeof(WIN32_FIND_DATAA)) + return E_INVALIDARG; - filename = _ILGetTextPointer(pidl); - shortname = _ILGetSTextPointer(pidl); + ZeroMemory(pfd, sizeof (WIN32_FIND_DATAA)); + _ILGetFileDateTime( pidl, &(pfd->ftLastWriteTime)); + pfd->dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0); + pfd->nFileSizeLow = _ILGetFileSize ( pidl, NULL, 0); - if (filename) - lstrcpynA(pfd->cFileName, filename, MAX_PATH); - else - pfd->cFileName[0] = '\0'; + filename = _ILGetTextPointer(pidl); + shortname = _ILGetSTextPointer(pidl); - if (shortname) - lstrcpynA(pfd->cAlternateFileName, shortname, MAX_PATH); - else - pfd->cAlternateFileName[0] = '\0'; - } - return NOERROR; + if (filename) + lstrcpynA(pfd->cFileName, filename, MAX_PATH); + else + pfd->cFileName[0] = '\0'; - case SHGDFIL_NETRESOURCE: - case SHGDFIL_DESCRIPTIONID: - FIXME_(shell)("SHGDFIL %i stub\n", nFormat); - break; + if (shortname) + lstrcpynA(pfd->cAlternateFileName, shortname, MAX_PATH); + else + pfd->cAlternateFileName[0] = '\0'; + return NOERROR; - default: - ERR_(shell)("Unknown SHGDFIL %i, please report\n", nFormat); - } + case SHGDFIL_NETRESOURCE: + case SHGDFIL_DESCRIPTIONID: + FIXME_(shell)("SHGDFIL %i stub\n", nFormat); + break; - return E_INVALIDARG; + default: + ERR_(shell)("Unknown SHGDFIL %i, please report\n", nFormat); + } + + return E_INVALIDARG; } /************************************************************************* * SHGetDataFromIDListW [SHELL32.248] * */ -HRESULT WINAPI SHGetDataFromIDListW(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, int nFormat, LPVOID dest, int len) +HRESULT WINAPI SHGetDataFromIDListW(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, + int nFormat, LPVOID dest, int len) { - TRACE_(shell)("sf=%p pidl=%p 0x%04x %p 0x%04x stub\n",psf,pidl,nFormat,dest,len); + LPSTR filename, shortname; + WIN32_FIND_DATAW * pfd = dest; - pdump(pidl); + TRACE_(shell)("sf=%p pidl=%p 0x%04x %p 0x%04x stub\n",psf,pidl,nFormat,dest,len); - if (! psf || !dest ) return E_INVALIDARG; + pdump(pidl); - switch (nFormat) - { - case SHGDFIL_FINDDATA: - { - LPSTR filename, shortname; - WIN32_FIND_DATAW * pfd = dest; + if (!psf || !dest) + return E_INVALIDARG; - if (_ILIsDrive(pidl)) - return E_INVALIDARG; + switch (nFormat) + { + case SHGDFIL_FINDDATA: + pfd = dest; - if (len < (int)sizeof(WIN32_FIND_DATAW)) return E_INVALIDARG; + if (_ILIsDrive(pidl)) + return E_INVALIDARG; - ZeroMemory(pfd, sizeof (WIN32_FIND_DATAA)); - _ILGetFileDateTime( pidl, &(pfd->ftLastWriteTime)); - pfd->dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0); - pfd->nFileSizeLow = _ILGetFileSize ( pidl, NULL, 0); + if (len < sizeof(WIN32_FIND_DATAW)) + return E_INVALIDARG; - filename = _ILGetTextPointer(pidl); - shortname = _ILGetSTextPointer(pidl); + ZeroMemory(pfd, sizeof (WIN32_FIND_DATAA)); + _ILGetFileDateTime( pidl, &(pfd->ftLastWriteTime)); + pfd->dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0); + pfd->nFileSizeLow = _ILGetFileSize ( pidl, NULL, 0); - if (!filename) - pfd->cFileName[0] = '\0'; - else if (!MultiByteToWideChar(CP_ACP, 0, filename, -1, pfd->cFileName, MAX_PATH)) - pfd->cFileName[MAX_PATH-1] = 0; + filename = _ILGetTextPointer(pidl); + shortname = _ILGetSTextPointer(pidl); - if (!shortname) - pfd->cAlternateFileName[0] = '\0'; - else if (!MultiByteToWideChar(CP_ACP, 0, shortname, -1, pfd->cAlternateFileName, 14)) - pfd->cAlternateFileName[13] = 0; - } - return NOERROR; - case SHGDFIL_NETRESOURCE: - case SHGDFIL_DESCRIPTIONID: - FIXME_(shell)("SHGDFIL %i stub\n", nFormat); - break; + if (!filename) + pfd->cFileName[0] = '\0'; + else if (!MultiByteToWideChar(CP_ACP, 0, filename, -1, pfd->cFileName, MAX_PATH)) + pfd->cFileName[MAX_PATH-1] = 0; - default: - ERR_(shell)("Unknown SHGDFIL %i, please report\n", nFormat); - } + if (!shortname) + pfd->cAlternateFileName[0] = '\0'; + else if (!MultiByteToWideChar(CP_ACP, 0, shortname, -1, pfd->cAlternateFileName, 14)) + pfd->cAlternateFileName[13] = 0; + return NOERROR; - return E_INVALIDARG; + case SHGDFIL_NETRESOURCE: + case SHGDFIL_DESCRIPTIONID: + FIXME_(shell)("SHGDFIL %i stub\n", nFormat); + break; + + default: + ERR_(shell)("Unknown SHGDFIL %i, please report\n", nFormat); + } + + return E_INVALIDARG; } /************************************************************************* @@ -1198,63 +1214,84 @@ HRESULT WINAPI SHGetDataFromIDListW(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, int n */ HRESULT SHELL_GetPathFromIDListA(LPCITEMIDLIST pidl, LPSTR pszPath, UINT uOutSize) { - HRESULT hr = S_OK; + HRESULT hr = S_OK; - pszPath[0]=0; + pszPath[0]=0; - /* One case is a PIDL rooted at desktop level */ - if (_ILIsValue(pidl) || _ILIsFolder(pidl)) { - hr = SHGetSpecialFolderPathA(0, pszPath, CSIDL_DESKTOP, FALSE); + /* One case is a PIDL rooted at desktop level */ + if (_ILIsValue(pidl) || _ILIsFolder(pidl)) + { + hr = SHGetSpecialFolderPathA(0, pszPath, CSIDL_DESKTOP, FALSE); - if (SUCCEEDED(hr)) - PathAddBackslashA(pszPath); - } - /* The only other valid case is a item ID list beginning at "My Computer" */ - else if (_ILIsMyComputer(pidl)) - pidl = ILGetNext(pidl); + if (SUCCEEDED(hr)) + PathAddBackslashA(pszPath); + } + /* The only other valid case is a item ID list beginning at "My Computer" */ + else if (_ILIsMyComputer(pidl)) + pidl = ILGetNext(pidl); - if (SUCCEEDED(hr)) { - LPSTR txt; + if (SUCCEEDED(hr)) + { + LPSTR txt; - while(pidl && pidl->mkid.cb) { - if (_ILIsSpecialFolder(pidl)) - {hr = E_INVALIDARG; break;} + while(pidl && pidl->mkid.cb) + { + if (_ILIsSpecialFolder(pidl)) + { + hr = E_INVALIDARG; + break; + } - txt = _ILGetTextPointer(pidl); - if (!txt) - {hr = E_INVALIDARG; break;} + txt = _ILGetTextPointer(pidl); + if (!txt) + { + hr = E_INVALIDARG; + break; + } - if (lstrlenA(txt) > pidl->mkid.cb) - ERR("pidl %p is borked\n",pidl); + if (lstrlenA(txt) > pidl->mkid.cb) + ERR("pidl %p is borked\n",pidl); - /* make sure there's enough space for the next segment */ - if ( (lstrlenA(txt) + lstrlenA(pszPath)) > uOutSize) - {hr = E_INVALIDARG; break;} - lstrcatA( pszPath, txt ); + /* make sure there's enough space for the next segment */ + if ((lstrlenA(txt) + lstrlenA(pszPath)) > uOutSize) + { + hr = E_INVALIDARG; + break; + } + lstrcatA( pszPath, txt ); - pidl = ILGetNext(pidl); - if (!pidl) - {hr = E_INVALIDARG; break;} + pidl = ILGetNext(pidl); + if (!pidl) + { + hr = E_INVALIDARG; + break; + } - if (!pidl->mkid.cb) { - /* We are at the end and successfully converted the complete PIDL. */ - break; - } + /* Are we at the end and successfully converted the complete PIDL? */ + if (!pidl->mkid.cb) + break; - if( (lstrlenA(pszPath) + 1) > uOutSize) - {hr = E_INVALIDARG; break;} - if (!PathAddBackslashA(pszPath)) - {hr = E_INVALIDARG; break;} - } - } else - hr = E_INVALIDARG; + if ((lstrlenA(pszPath) + 1) > uOutSize) + { + hr = E_INVALIDARG; + break; + } + if (!PathAddBackslashA(pszPath)) + { + hr = E_INVALIDARG; + break; + } + } + } + else + hr = E_INVALIDARG; - TRACE_(shell)("-- %s, 0x%08lx\n", pszPath, hr); - return hr; + TRACE_(shell)("-- %s, 0x%08lx\n", pszPath, hr); + return hr; } /************************************************************************* - * SHGetPathFromIDListA [SHELL32.@][NT 4.0: SHELL32.220] + * SHGetPathFromIDListA [SHELL32.@][NT 4.0: SHELL32.220] * * PARAMETERS * pidl, [IN] pidl @@ -1264,23 +1301,23 @@ HRESULT SHELL_GetPathFromIDListA(LPCITEMIDLIST pidl, LPSTR pszPath, UINT uOutSiz * path from a passed PIDL. * * NOTES - * NULL returns FALSE - * desktop pidl gives path to desktop directory back - * special pidls returning FALSE + * NULL returns FALSE + * desktop pidl gives path to desktop directory back + * special pidls returning FALSE */ BOOL WINAPI SHGetPathFromIDListA(LPCITEMIDLIST pidl, LPSTR pszPath) { - HRESULT hr; + HRESULT hr; - TRACE_(shell)("(pidl=%p,%p)\n",pidl,pszPath); - pdump(pidl); + TRACE_(shell)("(pidl=%p,%p)\n",pidl,pszPath); + pdump(pidl); - if (!pidl) - return FALSE; + if (!pidl) + return FALSE; - hr = SHELL_GetPathFromIDListA(pidl, pszPath, MAX_PATH); + hr = SHELL_GetPathFromIDListA(pidl, pszPath, MAX_PATH); - return SUCCEEDED(hr); + return SUCCEEDED(hr); } /************************************************************************* @@ -1288,144 +1325,166 @@ BOOL WINAPI SHGetPathFromIDListA(LPCITEMIDLIST pidl, LPSTR pszPath) */ HRESULT SHELL_GetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, UINT uOutSize) { - HRESULT hr = S_OK; - UINT len; + HRESULT hr = S_OK; + UINT len; - pszPath[0]=0; + pszPath[0]=0; - /* One case is a PIDL rooted at desktop level */ - if (_ILIsValue(pidl) || _ILIsFolder(pidl)) { - hr = SHGetSpecialFolderPathW(0, pszPath, CSIDL_DESKTOP, FALSE); + /* One case is a PIDL rooted at desktop level */ + if (_ILIsValue(pidl) || _ILIsFolder(pidl)) + { + hr = SHGetSpecialFolderPathW(0, pszPath, CSIDL_DESKTOP, FALSE); - if (SUCCEEDED(hr)) - PathAddBackslashW(pszPath); - } - /* The only other valid case is a item ID list beginning at "My Computer" */ - else if (_ILIsMyComputer(pidl)) - pidl = ILGetNext(pidl); + if (SUCCEEDED(hr)) + PathAddBackslashW(pszPath); + } + /* The only other valid case is a item ID list beginning at "My Computer" */ + else if (_ILIsMyComputer(pidl)) + pidl = ILGetNext(pidl); - if (SUCCEEDED(hr)) { - LPSTR txt; + if (SUCCEEDED(hr)) + { + LPSTR txt; - while(pidl && pidl->mkid.cb) { - if (_ILIsSpecialFolder(pidl)) - {hr = E_INVALIDARG; break;} + while(pidl && pidl->mkid.cb) + { + if (_ILIsSpecialFolder(pidl)) + { + hr = E_INVALIDARG; + break; + } - txt = _ILGetTextPointer(pidl); - if (!txt) - {hr = E_INVALIDARG; break;} + txt = _ILGetTextPointer(pidl); + if (!txt) + { + hr = E_INVALIDARG; + break; + } - if (lstrlenA(txt) > pidl->mkid.cb) - ERR("pidl %p is borked\n",pidl); - len = MultiByteToWideChar(CP_ACP, 0, txt, -1, NULL, 0); - if ( (lstrlenW(pszPath) + len) > uOutSize ) - {hr = E_INVALIDARG; break;} + if (lstrlenA(txt) > pidl->mkid.cb) + ERR("pidl %p is borked\n",pidl); + len = MultiByteToWideChar(CP_ACP, 0, txt, -1, NULL, 0); + if ( (lstrlenW(pszPath) + len) > uOutSize ) + { + hr = E_INVALIDARG; + break; + } - MultiByteToWideChar(CP_ACP, 0, txt, -1, - &pszPath[lstrlenW(pszPath)], len); + MultiByteToWideChar(CP_ACP, 0, txt, -1, + &pszPath[lstrlenW(pszPath)], len); - pidl = ILGetNext(pidl); - if (!pidl) - {hr = E_INVALIDARG; break;} + pidl = ILGetNext(pidl); + if (!pidl) + { + hr = E_INVALIDARG; + break; + } - if (!pidl->mkid.cb) { - /* We are at the end and successfully converted the complete PIDL. */ - break; - } + /* Are we at the end and successfully converted the complete PIDL? */ + if (!pidl->mkid.cb) + break; - if ( (lstrlenW(pszPath) + 1) > uOutSize ) - {hr = E_INVALIDARG; break;} - if (!PathAddBackslashW(pszPath)) - {hr = E_INVALIDARG; break;} - } - } else - hr = E_INVALIDARG; + if ((lstrlenW(pszPath) + 1) > uOutSize ) + { + hr = E_INVALIDARG; + break; + } + if (!PathAddBackslashW(pszPath)) + { + hr = E_INVALIDARG; + break; + } + } + } + else + hr = E_INVALIDARG; TRACE_(shell)("-- %s, 0x%08lx\n", debugstr_w(pszPath), hr); return hr; } /************************************************************************* - * SHGetPathFromIDListW [SHELL32.@] + * SHGetPathFromIDListW [SHELL32.@] */ BOOL WINAPI SHGetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath) { - HRESULT hr; + HRESULT hr; - TRACE_(shell)("(pidl=%p,%p)\n", pidl, debugstr_w(pszPath)); - pdump(pidl); + TRACE_(shell)("(pidl=%p,%p)\n", pidl, debugstr_w(pszPath)); + pdump(pidl); - if (!pidl) - return FALSE; + if (!pidl) + return FALSE; - hr = SHELL_GetPathFromIDListW(pidl, pszPath, MAX_PATH); + hr = SHELL_GetPathFromIDListW(pidl, pszPath, MAX_PATH); - TRACE_(shell)("-- %s, 0x%08lx\n",debugstr_w(pszPath), hr); - return SUCCEEDED(hr); + TRACE_(shell)("-- %s, 0x%08lx\n",debugstr_w(pszPath), hr); + return SUCCEEDED(hr); } /************************************************************************* - * SHBindToParent [shell version 5.0] + * SHBindToParent [shell version 5.0] */ HRESULT WINAPI SHBindToParent(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv, LPCITEMIDLIST *ppidlLast) { - IShellFolder * psf; - LPITEMIDLIST pidlChild, pidlParent; - HRESULT hr=E_FAIL; + IShellFolder * psf; + LPITEMIDLIST pidlChild, pidlParent; + HRESULT hr=E_FAIL; - TRACE_(shell)("pidl=%p\n", pidl); - pdump(pidl); + TRACE_(shell)("pidl=%p\n", pidl); + pdump(pidl); - *ppv = NULL; - if (ppidlLast) *ppidlLast = NULL; + *ppv = NULL; + if (ppidlLast) + *ppidlLast = NULL; - if (_ILIsPidlSimple(pidl)) - { - IShellFolder* desktop; + if (_ILIsPidlSimple(pidl)) + { + IShellFolder* desktop; - /* we are on desktop level */ - hr = SHGetDesktopFolder(&desktop); + /* we are on desktop level */ + hr = SHGetDesktopFolder(&desktop); - if (SUCCEEDED(hr)) - { - hr = IShellFolder_QueryInterface(desktop, riid, ppv); + if (SUCCEEDED(hr)) + { + hr = IShellFolder_QueryInterface(desktop, riid, ppv); - if (SUCCEEDED(hr) && ppidlLast) - *ppidlLast = ILClone(pidl); + if (SUCCEEDED(hr) && ppidlLast) + *ppidlLast = ILClone(pidl); - IShellFolder_Release(desktop); - } - } - else - { - pidlChild = ILClone(ILFindLastID(pidl)); - pidlParent = ILClone(pidl); - ILRemoveLastID(pidlParent); + IShellFolder_Release(desktop); + } + } + else + { + pidlChild = ILClone(ILFindLastID(pidl)); + pidlParent = ILClone(pidl); + ILRemoveLastID(pidlParent); - hr = SHGetDesktopFolder(&psf); + hr = SHGetDesktopFolder(&psf); - if (SUCCEEDED(hr)) - hr = IShellFolder_BindToObject(psf, pidlParent, NULL, riid, ppv); + if (SUCCEEDED(hr)) + hr = IShellFolder_BindToObject(psf, pidlParent, NULL, riid, ppv); - if (SUCCEEDED(hr) && ppidlLast) - *ppidlLast = pidlChild; - else - ILFree (pidlChild); + if (SUCCEEDED(hr) && ppidlLast) + *ppidlLast = pidlChild; + else + ILFree (pidlChild); - SHFree (pidlParent); - if (psf) IShellFolder_Release(psf); - } + SHFree (pidlParent); + if (psf) + IShellFolder_Release(psf); + } - - TRACE_(shell)("-- psf=%p pidl=%p ret=0x%08lx\n", *ppv, (ppidlLast)?*ppidlLast:NULL, hr); - return hr; + TRACE_(shell)("-- psf=%p pidl=%p ret=0x%08lx\n", *ppv, (ppidlLast)?*ppidlLast:NULL, hr); + return hr; } /************************************************************************** * - * internal functions + * internal functions * - * ### 1. section creating pidls ### + * ### 1. section creating pidls ### * ************************************************************************* */ @@ -1433,7 +1492,8 @@ LPITEMIDLIST _ILAlloc(PIDLTYPE type, size_t size) { LPITEMIDLIST pidlOut = NULL; - if((pidlOut = SHAlloc(size + 5))) + pidlOut = SHAlloc(size + 5); + if(pidlOut) { LPPIDLDATA pData; LPITEMIDLIST pidlNext; @@ -1441,10 +1501,12 @@ LPITEMIDLIST _ILAlloc(PIDLTYPE type, size_t size) ZeroMemory(pidlOut, size + 5); pidlOut->mkid.cb = size + 3; - if ((pData = _ILGetDataPointer(pidlOut))) + pData = _ILGetDataPointer(pidlOut); + if (pData) pData->type = type; - if ((pidlNext = ILGetNext(pidlOut))) + pidlNext = ILGetNext(pidlOut); + if (pidlNext) pidlNext->mkid.cb = 0x00; TRACE("-- (pidl=%p, size=%u)\n", pidlOut, size); } @@ -1464,13 +1526,15 @@ LPITEMIDLIST _ILCreateDesktop() } LPITEMIDLIST _ILCreateMyComputer() -{ TRACE("()\n"); - return _ILCreateGuid(PT_GUID, &CLSID_MyComputer); +{ + TRACE("()\n"); + return _ILCreateGuid(PT_GUID, &CLSID_MyComputer); } LPITEMIDLIST _ILCreateIExplore() -{ TRACE("()\n"); - return _ILCreateGuid(PT_GUID, &CLSID_Internet); +{ + TRACE("()\n"); + return _ILCreateGuid(PT_GUID, &CLSID_Internet); } LPITEMIDLIST _ILCreateControlPanel() @@ -1512,13 +1576,15 @@ LPITEMIDLIST _ILCreatePrinters() } LPITEMIDLIST _ILCreateNetwork() -{ TRACE("()\n"); - return _ILCreateGuid(PT_GUID, &CLSID_NetworkPlaces); +{ + TRACE("()\n"); + return _ILCreateGuid(PT_GUID, &CLSID_NetworkPlaces); } LPITEMIDLIST _ILCreateBitBucket() -{ TRACE("()\n"); - return _ILCreateGuid(PT_GUID, &CLSID_RecycleBin); +{ + TRACE("()\n"); + return _ILCreateGuid(PT_GUID, &CLSID_RecycleBin); } LPITEMIDLIST _ILCreateGuid(PIDLTYPE type, REFIID guid) @@ -1534,7 +1600,7 @@ LPITEMIDLIST _ILCreateGuid(PIDLTYPE type, REFIID guid) memcpy(&(pData->u.guid.guid), guid, sizeof(GUID)); TRACE("-- create GUID-pidl %s\n", - debugstr_guid(&(pData->u.guid.guid))); + debugstr_guid(&(pData->u.guid.guid))); } } else @@ -1547,21 +1613,52 @@ LPITEMIDLIST _ILCreateGuid(PIDLTYPE type, REFIID guid) LPITEMIDLIST _ILCreateGuidFromStrA(LPCSTR szGUID) { - IID iid; + IID iid; - if (!SUCCEEDED(SHCLSIDFromStringA(szGUID, &iid))) - { - ERR("%s is not a GUID\n", szGUID); - return NULL; - } - return _ILCreateGuid(PT_GUID, &iid); + if (!SUCCEEDED(SHCLSIDFromStringA(szGUID, &iid))) + { + ERR("%s is not a GUID\n", szGUID); + return NULL; + } + return _ILCreateGuid(PT_GUID, &iid); +} + +LPITEMIDLIST _ILCreateGuidFromStrW(LPCWSTR szGUID) +{ + IID iid; + + if (!SUCCEEDED(SHCLSIDFromStringW(szGUID, &iid))) + { + ERR("%s is not a GUID\n", debugstr_w(szGUID)); + return NULL; + } + return _ILCreateGuid(PT_GUID, &iid); +} + +LPITEMIDLIST _ILCreateFromFindDataW( WIN32_FIND_DATAW *wfd ) +{ + /* FIXME: should make unicode PIDLs */ + WIN32_FIND_DATAA fda; + + memset( &fda, 0, sizeof fda ); + fda.dwFileAttributes = wfd->dwFileAttributes; + fda.ftCreationTime = wfd->ftCreationTime; + fda.ftLastAccessTime = wfd->ftLastAccessTime; + fda.ftLastWriteTime = wfd->ftLastWriteTime; + fda.nFileSizeHigh = wfd->nFileSizeHigh; + fda.nFileSizeLow = wfd->nFileSizeLow; + fda.dwReserved0 = wfd->dwReserved0; + fda.dwReserved1 = wfd->dwReserved1; + WideCharToMultiByte( CP_ACP, 0, wfd->cFileName, -1, + fda.cFileName, MAX_PATH, NULL, NULL ); + return _ILCreateFromFindDataA( &fda ); } LPITEMIDLIST _ILCreateFromFindDataA(WIN32_FIND_DATAA * stffile ) { - char buff[MAX_PATH + 14 +1]; /* see WIN32_FIND_DATA */ - char * pbuff = buff; - size_t len, len1; + char buff[MAX_PATH + 14 +1]; /* see WIN32_FIND_DATA */ + char * pbuff = buff; + size_t len, len1; LPITEMIDLIST pidl; PIDLTYPE type; @@ -1578,25 +1675,30 @@ LPITEMIDLIST _ILCreateFromFindDataA(WIN32_FIND_DATAA * stffile ) len1 = strlen (stffile->cAlternateFileName)+1; memcpy (pbuff, stffile->cAlternateFileName, len1); - type = (stffile->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? PT_FOLDER : - PT_VALUE; - /* FileStruct already has one byte for the first name, so use len - 1 in + type = (stffile->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? PT_FOLDER : PT_VALUE; + + /* + * FileStruct already has one byte for the first name, so use len - 1 in * size calculation */ - if ((pidl = _ILAlloc(type, sizeof(FileStruct) + (len - 1) + len1))) + pidl = _ILAlloc(type, sizeof(FileStruct) + (len - 1) + len1); + if (pidl) { LPPIDLDATA pData; LPSTR pszDest; /* set attributes */ - if ((pData = _ILGetDataPointer(pidl))) + pData = _ILGetDataPointer(pidl); + if (pData) { pData->type = type; - FileTimeToDosDateTime(&(stffile->ftLastWriteTime),&pData->u.file.uFileDate,&pData->u.file.uFileTime); + FileTimeToDosDateTime( &(stffile->ftLastWriteTime), + &pData->u.file.uFileDate, &pData->u.file.uFileTime); pData->u.file.dwFileSize = stffile->nFileSizeLow; pData->u.file.uFileAttribs = (WORD)stffile->dwFileAttributes; } - if ((pszDest = _ILGetTextPointer(pidl))) + pszDest = _ILGetTextPointer(pidl); + if (pszDest) { memcpy(pszDest, buff, len + len1); TRACE("-- create Value: %s\n",debugstr_a(pszDest)); @@ -1607,42 +1709,58 @@ LPITEMIDLIST _ILCreateFromFindDataA(WIN32_FIND_DATAA * stffile ) HRESULT _ILCreateFromPathA(LPCSTR szPath, LPITEMIDLIST* ppidl) { - HANDLE hFile; - WIN32_FIND_DATAA stffile; + HANDLE hFile; + WIN32_FIND_DATAA stffile; if (!ppidl) return E_INVALIDARG; - hFile = FindFirstFileA(szPath, &stffile); + hFile = FindFirstFileA(szPath, &stffile); + if (hFile == INVALID_HANDLE_VALUE) + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - if (hFile == INVALID_HANDLE_VALUE) - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + FindClose(hFile); - FindClose(hFile); + *ppidl = _ILCreateFromFindDataA(&stffile); - *ppidl = _ILCreateFromFindDataA(&stffile); + return *ppidl ? S_OK : E_OUTOFMEMORY; +} - return *ppidl ? S_OK : E_OUTOFMEMORY; +HRESULT _ILCreateFromPathW(LPCWSTR szPath, LPITEMIDLIST* ppidl) +{ + HANDLE hFile; + WIN32_FIND_DATAW stffile; + + if (!ppidl) + return E_INVALIDARG; + + hFile = FindFirstFileW(szPath, &stffile); + if (hFile == INVALID_HANDLE_VALUE) + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + FindClose(hFile); + + *ppidl = _ILCreateFromFindDataW(&stffile); + + return *ppidl ? S_OK : E_OUTOFMEMORY; } LPITEMIDLIST _ILCreateDrive(LPCWSTR lpszNew) { - WCHAR sTemp[4]; LPITEMIDLIST pidlOut; - sTemp[0]=toupperW(lpszNew[0]); - sTemp[1]=':'; - sTemp[2]='\\'; - sTemp[3]=0x00; - TRACE("(%s)\n",debugstr_w(sTemp)); + TRACE("(%s)\n",debugstr_w(lpszNew)); - if ((pidlOut = _ILAlloc(PT_DRIVE, sizeof(DriveStruct)))) + pidlOut = _ILAlloc(PT_DRIVE, sizeof(DriveStruct)); + if (pidlOut) { LPSTR pszDest; - if ((pszDest = _ILGetTextPointer(pidlOut))) + pszDest = _ILGetTextPointer(pidlOut); + if (pszDest) { - WideCharToMultiByte(CP_ACP, 0, sTemp, sizeof(sTemp)/sizeof(WCHAR), pszDest, sizeof(sTemp)/sizeof(WCHAR), NULL, NULL); + strcpy(pszDest, "x:\\"); + pszDest[0]=toupperW(lpszNew[0]); TRACE("-- create Drive: %s\n", debugstr_a(pszDest)); } } @@ -1658,20 +1776,21 @@ LPITEMIDLIST _ILCreateDrive(LPCWSTR lpszNew) * strlen (lpszText) */ DWORD _ILGetDrive(LPCITEMIDLIST pidl,LPSTR pOut, UINT uSize) -{ TRACE("(%p,%p,%u)\n",pidl,pOut,uSize); +{ + TRACE("(%p,%p,%u)\n",pidl,pOut,uSize); - if(_ILIsMyComputer(pidl)) - pidl = ILGetNext(pidl); + if(_ILIsMyComputer(pidl)) + pidl = ILGetNext(pidl); - if (pidl && _ILIsDrive(pidl)) - return _ILSimpleGetText(pidl, pOut, uSize); + if (pidl && _ILIsDrive(pidl)) + return _ILSimpleGetText(pidl, pOut, uSize); - return 0; + return 0; } /************************************************************************** * - * ### 2. section testing pidls ### + * ### 2. section testing pidls ### * ************************************************************************** * _ILIsDesktop() @@ -1683,79 +1802,96 @@ DWORD _ILGetDrive(LPCITEMIDLIST pidl,LPSTR pOut, UINT uSize) * _ILIsPidlSimple() */ BOOL _ILIsDesktop(LPCITEMIDLIST pidl) -{ TRACE("(%p)\n",pidl); - return pidl && pidl->mkid.cb ? 0 : 1; +{ + TRACE("(%p)\n",pidl); + + return pidl && pidl->mkid.cb ? 0 : 1; } BOOL _ILIsMyComputer(LPCITEMIDLIST pidl) { - REFIID iid = _ILGetGUIDPointer(pidl); + REFIID iid = _ILGetGUIDPointer(pidl); - TRACE("(%p)\n",pidl); + TRACE("(%p)\n",pidl); - if (iid) - return IsEqualIID(iid, &CLSID_MyComputer); - return FALSE; + if (iid) + return IsEqualIID(iid, &CLSID_MyComputer); + return FALSE; } BOOL _ILIsSpecialFolder (LPCITEMIDLIST pidl) { - LPPIDLDATA lpPData = _ILGetDataPointer(pidl); - TRACE("(%p)\n",pidl); - return (pidl && ( (lpPData && (PT_GUID== lpPData->type || PT_SHELLEXT== lpPData->type)) || - (pidl && pidl->mkid.cb == 0x00) - )); + LPPIDLDATA lpPData = _ILGetDataPointer(pidl); + + TRACE("(%p)\n",pidl); + + return (pidl && ( (lpPData && (PT_GUID== lpPData->type || PT_SHELLEXT== lpPData->type)) || + (pidl && pidl->mkid.cb == 0x00) + )); } BOOL _ILIsDrive(LPCITEMIDLIST pidl) -{ LPPIDLDATA lpPData = _ILGetDataPointer(pidl); - TRACE("(%p)\n",pidl); - return (pidl && lpPData && (PT_DRIVE == lpPData->type || - PT_DRIVE1 == lpPData->type || - PT_DRIVE2 == lpPData->type || - PT_DRIVE3 == lpPData->type)); +{ + LPPIDLDATA lpPData = _ILGetDataPointer(pidl); + + TRACE("(%p)\n",pidl); + + return (pidl && lpPData && (PT_DRIVE == lpPData->type || + PT_DRIVE1 == lpPData->type || + PT_DRIVE2 == lpPData->type || + PT_DRIVE3 == lpPData->type)); } BOOL _ILIsFolder(LPCITEMIDLIST pidl) -{ LPPIDLDATA lpPData = _ILGetDataPointer(pidl); - TRACE("(%p)\n",pidl); - return (pidl && lpPData && (PT_FOLDER == lpPData->type || PT_FOLDER1 == lpPData->type)); +{ + LPPIDLDATA lpPData = _ILGetDataPointer(pidl); + + TRACE("(%p)\n",pidl); + + return (pidl && lpPData && (PT_FOLDER == lpPData->type || PT_FOLDER1 == lpPData->type)); } BOOL _ILIsValue(LPCITEMIDLIST pidl) -{ LPPIDLDATA lpPData = _ILGetDataPointer(pidl); - TRACE("(%p)\n",pidl); - return (pidl && lpPData && PT_VALUE == lpPData->type); +{ + LPPIDLDATA lpPData = _ILGetDataPointer(pidl); + + TRACE("(%p)\n",pidl); + + return (pidl && lpPData && PT_VALUE == lpPData->type); } BOOL _ILIsCPanelStruct(LPCITEMIDLIST pidl) -{ LPPIDLDATA lpPData = _ILGetDataPointer(pidl); - TRACE("(%p)\n",pidl); - return (pidl && lpPData && (lpPData->type == 0)); +{ + LPPIDLDATA lpPData = _ILGetDataPointer(pidl); + + TRACE("(%p)\n",pidl); + + return (pidl && lpPData && (lpPData->type == 0)); } /************************************************************************** - * _ILIsPidlSimple + * _ILIsPidlSimple */ -BOOL _ILIsPidlSimple ( LPCITEMIDLIST pidl) +BOOL _ILIsPidlSimple(LPCITEMIDLIST pidl) { - BOOL ret = TRUE; + BOOL ret = TRUE; - if(! _ILIsDesktop(pidl)) /* pidl=NULL or mkid.cb=0 */ - { - WORD len = pidl->mkid.cb; - LPCITEMIDLIST pidlnext = (LPCITEMIDLIST) (((const BYTE*)pidl) + len ); - if (pidlnext->mkid.cb) - ret = FALSE; - } + if(! _ILIsDesktop(pidl)) /* pidl=NULL or mkid.cb=0 */ + { + WORD len = pidl->mkid.cb; + LPCITEMIDLIST pidlnext = (LPCITEMIDLIST) (((const BYTE*)pidl) + len ); - TRACE("%s\n", ret ? "Yes" : "No"); - return ret; + if (pidlnext->mkid.cb) + ret = FALSE; + } + + TRACE("%s\n", ret ? "Yes" : "No"); + return ret; } /************************************************************************** * - * ### 3. section getting values from pidls ### + * ### 3. section getting values from pidls ### */ /************************************************************************** @@ -1767,55 +1903,56 @@ BOOL _ILIsPidlSimple ( LPCITEMIDLIST pidl) */ DWORD _ILSimpleGetText (LPCITEMIDLIST pidl, LPSTR szOut, UINT uOutSize) { - DWORD dwReturn=0; - LPSTR szSrc; - GUID const * riid; - char szTemp[MAX_PATH]; + DWORD dwReturn=0; + LPSTR szSrc; + GUID const * riid; + char szTemp[MAX_PATH]; - TRACE("(%p %p %x)\n",pidl,szOut,uOutSize); + TRACE("(%p %p %x)\n",pidl,szOut,uOutSize); - if (!pidl) return 0; + if (!pidl) + return 0; - if (szOut) - *szOut = 0; + if (szOut) + *szOut = 0; - if (_ILIsDesktop(pidl)) - { - /* desktop */ - if (HCR_GetClassNameA(&CLSID_ShellDesktop, szTemp, MAX_PATH)) - { - if (szOut) - lstrcpynA(szOut, szTemp, uOutSize); + if (_ILIsDesktop(pidl)) + { + /* desktop */ + if (HCR_GetClassNameA(&CLSID_ShellDesktop, szTemp, MAX_PATH)) + { + if (szOut) + lstrcpynA(szOut, szTemp, uOutSize); - dwReturn = strlen (szTemp); - } - } - else if (( szSrc = _ILGetTextPointer(pidl) )) - { - /* filesystem */ - if (szOut) - lstrcpynA(szOut, szSrc, uOutSize); + dwReturn = strlen (szTemp); + } + } + else if (( szSrc = _ILGetTextPointer(pidl) )) + { + /* filesystem */ + if (szOut) + lstrcpynA(szOut, szSrc, uOutSize); - dwReturn = strlen(szSrc); - } - else if (( riid = _ILGetGUIDPointer(pidl) )) - { - /* special folder */ - if ( HCR_GetClassNameA(riid, szTemp, MAX_PATH) ) - { - if (szOut) - lstrcpynA(szOut, szTemp, uOutSize); + dwReturn = strlen(szSrc); + } + else if (( riid = _ILGetGUIDPointer(pidl) )) + { + /* special folder */ + if ( HCR_GetClassNameA(riid, szTemp, MAX_PATH) ) + { + if (szOut) + lstrcpynA(szOut, szTemp, uOutSize); - dwReturn = strlen (szTemp); - } - } - else - { - ERR("-- no text\n"); - } + dwReturn = strlen (szTemp); + } + } + else + { + ERR("-- no text\n"); + } - TRACE("-- (%p=%s 0x%08lx)\n",szOut,debugstr_a(szOut),dwReturn); - return dwReturn; + TRACE("-- (%p=%s 0x%08lx)\n",szOut,debugstr_a(szOut),dwReturn); + return dwReturn; } /************************************************************************** @@ -1827,32 +1964,32 @@ DWORD _ILSimpleGetText (LPCITEMIDLIST pidl, LPSTR szOut, UINT uOutSize) */ DWORD _ILSimpleGetTextW (LPCITEMIDLIST pidl, LPWSTR szOut, UINT uOutSize) { - DWORD dwReturn; - char szTemp[MAX_PATH]; + DWORD dwReturn; + char szTemp[MAX_PATH]; - TRACE("(%p %p %x)\n",pidl,szOut,uOutSize); + TRACE("(%p %p %x)\n",pidl,szOut,uOutSize); - dwReturn = _ILSimpleGetText(pidl, szTemp, uOutSize); + dwReturn = _ILSimpleGetText(pidl, szTemp, uOutSize); - if (!MultiByteToWideChar(CP_ACP, 0, szTemp, -1, szOut, MAX_PATH)) - *szOut = 0; + if (!MultiByteToWideChar(CP_ACP, 0, szTemp, -1, szOut, MAX_PATH)) + *szOut = 0; - TRACE("-- (%p=%s 0x%08lx)\n",szOut,debugstr_w(szOut),dwReturn); - return dwReturn; + TRACE("-- (%p=%s 0x%08lx)\n",szOut,debugstr_w(szOut),dwReturn); + return dwReturn; } /************************************************************************** * - * ### 4. getting pointers to parts of pidls ### + * ### 4. getting pointers to parts of pidls ### * ************************************************************************** * _ILGetDataPointer() */ LPPIDLDATA _ILGetDataPointer(LPCITEMIDLIST pidl) { - if(pidl && pidl->mkid.cb != 0x00) - return (LPPIDLDATA) &(pidl->mkid.abID); - return NULL; + if(pidl && pidl->mkid.cb != 0x00) + return (LPPIDLDATA) &(pidl->mkid.abID); + return NULL; } /************************************************************************** @@ -1860,41 +1997,42 @@ LPPIDLDATA _ILGetDataPointer(LPCITEMIDLIST pidl) * gets a pointer to the long filename string stored in the pidl */ LPSTR _ILGetTextPointer(LPCITEMIDLIST pidl) -{/* TRACE(pidl,"(pidl%p)\n", pidl);*/ +{ + /* TRACE(pidl,"(pidl%p)\n", pidl);*/ - LPPIDLDATA pdata = _ILGetDataPointer(pidl); + LPPIDLDATA pdata = _ILGetDataPointer(pidl); - if (pdata) - { - switch (pdata->type) - { - case PT_GUID: - case PT_SHELLEXT: - case PT_YAGUID: - return NULL; + if (!pdata) + return NULL; - case PT_DRIVE: - case PT_DRIVE1: - case PT_DRIVE2: - case PT_DRIVE3: - return (LPSTR)&(pdata->u.drive.szDriveName); + switch (pdata->type) + { + case PT_GUID: + case PT_SHELLEXT: + case PT_YAGUID: + return NULL; - case PT_FOLDER: - case PT_FOLDER1: - case PT_VALUE: - case PT_IESPECIAL1: - case PT_IESPECIAL2: - return (LPSTR)&(pdata->u.file.szNames); + case PT_DRIVE: + case PT_DRIVE1: + case PT_DRIVE2: + case PT_DRIVE3: + return (LPSTR)&(pdata->u.drive.szDriveName); - case PT_WORKGRP: - case PT_COMP: - case PT_NETWORK: - case PT_NETPROVIDER: - case PT_SHARE: - return (LPSTR)&(pdata->u.network.szNames); - } - } - return NULL; + case PT_FOLDER: + case PT_FOLDER1: + case PT_VALUE: + case PT_IESPECIAL1: + case PT_IESPECIAL2: + return (LPSTR)&(pdata->u.file.szNames); + + case PT_WORKGRP: + case PT_COMP: + case PT_NETWORK: + case PT_NETPROVIDER: + case PT_SHARE: + return (LPSTR)&(pdata->u.network.szNames); + } + return NULL; } /************************************************************************** @@ -1902,25 +2040,26 @@ LPSTR _ILGetTextPointer(LPCITEMIDLIST pidl) * gets a pointer to the short filename string stored in the pidl */ LPSTR _ILGetSTextPointer(LPCITEMIDLIST pidl) -{/* TRACE(pidl,"(pidl%p)\n", pidl);*/ +{ + /* TRACE(pidl,"(pidl%p)\n", pidl); */ - LPPIDLDATA pdata =_ILGetDataPointer(pidl); + LPPIDLDATA pdata =_ILGetDataPointer(pidl); - if (pdata) - { - switch (pdata->type) - { - case PT_FOLDER: - case PT_VALUE: - case PT_IESPECIAL1: - case PT_IESPECIAL2: - return (LPSTR)(pdata->u.file.szNames + strlen (pdata->u.file.szNames) + 1); + if (!pdata) + return NULL; - case PT_WORKGRP: - return (LPSTR)(pdata->u.network.szNames + strlen (pdata->u.network.szNames) + 1); - } - } - return NULL; + switch (pdata->type) + { + case PT_FOLDER: + case PT_VALUE: + case PT_IESPECIAL1: + case PT_IESPECIAL2: + return (LPSTR)(pdata->u.file.szNames + strlen (pdata->u.file.szNames) + 1); + + case PT_WORKGRP: + return (LPSTR)(pdata->u.network.szNames + strlen (pdata->u.network.szNames) + 1); + } + return NULL; } /************************************************************************** @@ -1930,25 +2069,25 @@ LPSTR _ILGetSTextPointer(LPCITEMIDLIST pidl) */ IID* _ILGetGUIDPointer(LPCITEMIDLIST pidl) { - LPPIDLDATA pdata =_ILGetDataPointer(pidl); + LPPIDLDATA pdata =_ILGetDataPointer(pidl); - TRACE("%p\n", pidl); + TRACE("%p\n", pidl); - if (pdata) - { - TRACE("pdata->type 0x%04x\n", pdata->type); - switch (pdata->type) - { - case PT_SHELLEXT: - case PT_GUID: - return &(pdata->u.guid.guid); + if (!pdata) + return NULL; - default: - TRACE("Unknown pidl type 0x%04x\n", pdata->type); - break; - } - } - return NULL; + TRACE("pdata->type 0x%04x\n", pdata->type); + switch (pdata->type) + { + case PT_SHELLEXT: + case PT_GUID: + return &(pdata->u.guid.guid); + + default: + TRACE("Unknown pidl type 0x%04x\n", pdata->type); + break; + } + return NULL; } /************************************************************************* @@ -1970,36 +2109,39 @@ BOOL _ILGetFileDateTime(LPCITEMIDLIST pidl, FILETIME *pFt) { LPPIDLDATA pdata = _ILGetDataPointer(pidl); - if(! pdata) return FALSE; + if (!pdata) + return FALSE; switch (pdata->type) { - case PT_FOLDER: - case PT_VALUE: - DosDateTimeToFileTime(pdata->u.file.uFileDate, pdata->u.file.uFileTime, pFt); - break; - default: - return FALSE; + case PT_FOLDER: + case PT_VALUE: + DosDateTimeToFileTime(pdata->u.file.uFileDate, pdata->u.file.uFileTime, pFt); + break; + default: + return FALSE; } return TRUE; } BOOL _ILGetFileDate (LPCITEMIDLIST pidl, LPSTR pOut, UINT uOutSize) { - FILETIME ft,lft; - SYSTEMTIME time; - BOOL ret; - - if (_ILGetFileDateTime( pidl, &ft )) { - FileTimeToLocalFileTime(&ft, &lft); - FileTimeToSystemTime (&lft, &time); - ret = GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&time, NULL, pOut, uOutSize); - } else { - pOut[0] = '\0'; - ret = FALSE; - } - return ret; + FILETIME ft,lft; + SYSTEMTIME time; + BOOL ret; + if (_ILGetFileDateTime( pidl, &ft )) + { + FileTimeToLocalFileTime(&ft, &lft); + FileTimeToSystemTime (&lft, &time); + ret = GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&time, NULL, pOut, uOutSize); + } + else + { + pOut[0] = '\0'; + ret = FALSE; + } + return ret; } /************************************************************************* @@ -2008,59 +2150,66 @@ BOOL _ILGetFileDate (LPCITEMIDLIST pidl, LPSTR pOut, UINT uOutSize) * Given the ItemIdList, get the FileSize * * PARAMS - * pidl [I] The ItemIDList - * pOut [I] The buffer to save the result - * uOutsize [I] The size of the buffer + * pidl [I] The ItemIDList + * pOut [I] The buffer to save the result + * uOutsize [I] The size of the buffer * * RETURNS - * The FileSize + * The FileSize * * NOTES - * pOut can be null when no string is needed + * pOut can be null when no string is needed * */ DWORD _ILGetFileSize (LPCITEMIDLIST pidl, LPSTR pOut, UINT uOutSize) { - LPPIDLDATA pdata = _ILGetDataPointer(pidl); - DWORD dwSize; + LPPIDLDATA pdata = _ILGetDataPointer(pidl); + DWORD dwSize; - if(! pdata) return 0; + if (!pdata) + return 0; - switch (pdata->type) - { - case PT_VALUE: - dwSize = pdata->u.file.dwFileSize; - if (pOut) StrFormatByteSizeA(dwSize, pOut, uOutSize); - return dwSize; - } - if (pOut) *pOut = 0x00; - return 0; + switch (pdata->type) + { + case PT_VALUE: + dwSize = pdata->u.file.dwFileSize; + if (pOut) + StrFormatByteSizeA(dwSize, pOut, uOutSize); + return dwSize; + } + if (pOut) + *pOut = 0x00; + return 0; } BOOL _ILGetExtension (LPCITEMIDLIST pidl, LPSTR pOut, UINT uOutSize) { - char szTemp[MAX_PATH]; - const char * pPoint; - LPCITEMIDLIST pidlTemp=pidl; + char szTemp[MAX_PATH]; + const char * pPoint; + LPCITEMIDLIST pidlTemp=pidl; - TRACE("pidl=%p\n",pidl); + TRACE("pidl=%p\n",pidl); - if (!pidl) return FALSE; + if (!pidl) + return FALSE; - pidlTemp = ILFindLastID(pidl); + pidlTemp = ILFindLastID(pidl); - if (!_ILIsValue(pidlTemp)) return FALSE; - if (!_ILSimpleGetText(pidlTemp, szTemp, MAX_PATH)) return FALSE; + if (!_ILIsValue(pidlTemp)) + return FALSE; + if (!_ILSimpleGetText(pidlTemp, szTemp, MAX_PATH)) + return FALSE; - pPoint = PathFindExtensionA(szTemp); + pPoint = PathFindExtensionA(szTemp); - if (! *pPoint) return FALSE; + if (!*pPoint) + return FALSE; - pPoint++; - lstrcpynA(pOut, pPoint, uOutSize); - TRACE("%s\n",pOut); + pPoint++; + lstrcpynA(pOut, pPoint, uOutSize); + TRACE("%s\n",pOut); - return TRUE; + return TRUE; } /************************************************************************* @@ -2074,34 +2223,31 @@ BOOL _ILGetExtension (LPCITEMIDLIST pidl, LPSTR pOut, UINT uOutSize) * uOutsize [I] The size of the buffer * * RETURNS - * nothing + * nothing * * NOTES - * This function copies as much as possible into the buffer. + * This function copies as much as possible into the buffer. */ void _ILGetFileType(LPCITEMIDLIST pidl, LPSTR pOut, UINT uOutSize) { - if(_ILIsValue(pidl)) - { - char sTemp[64]; - if(uOutSize > 0) - { + if(_ILIsValue(pidl)) + { + char sTemp[64]; + + if(uOutSize > 0) pOut[0] = 0; - } - if (_ILGetExtension (pidl, sTemp, 64)) - { - if (!( HCR_MapTypeToValueA(sTemp, sTemp, 64, TRUE) - && HCR_MapTypeToValueA(sTemp, pOut, uOutSize, FALSE ))) - { - lstrcpynA (pOut, sTemp, uOutSize - 6); - strcat (pOut, "-file"); - } - } - } - else - { - lstrcpynA(pOut, "Folder", uOutSize); - } + if (_ILGetExtension (pidl, sTemp, 64)) + { + if (!( HCR_MapTypeToValueA(sTemp, sTemp, 64, TRUE) + && HCR_MapTypeToValueA(sTemp, pOut, uOutSize, FALSE ))) + { + lstrcpynA (pOut, sTemp, uOutSize - 6); + strcat (pOut, "-file"); + } + } + } + else + lstrcpynA(pOut, "Folder", uOutSize); } /************************************************************************* @@ -2123,98 +2269,95 @@ void _ILGetFileType(LPCITEMIDLIST pidl, LPSTR pOut, UINT uOutSize) */ DWORD _ILGetFileAttributes(LPCITEMIDLIST pidl, LPSTR pOut, UINT uOutSize) { - LPPIDLDATA pData = _ILGetDataPointer(pidl); - WORD wAttrib = 0; - int i; + LPPIDLDATA pData = _ILGetDataPointer(pidl); + WORD wAttrib = 0; + int i; - if(! pData) return 0; + if (!pData) + return 0; - switch(pData->type) - { - case PT_FOLDER: - case PT_VALUE: - wAttrib = pData->u.file.uFileAttribs; - break; - } + switch(pData->type) + { + case PT_FOLDER: + case PT_VALUE: + wAttrib = pData->u.file.uFileAttribs; + break; + } - if(uOutSize >= 6) - { - i=0; - if(wAttrib & FILE_ATTRIBUTE_READONLY) - { - pOut[i++] = 'R'; - } - if(wAttrib & FILE_ATTRIBUTE_HIDDEN) - { - pOut[i++] = 'H'; - } - if(wAttrib & FILE_ATTRIBUTE_SYSTEM) - { - pOut[i++] = 'S'; - } - if(wAttrib & FILE_ATTRIBUTE_ARCHIVE) - { - pOut[i++] = 'A'; - } - if(wAttrib & FILE_ATTRIBUTE_COMPRESSED) - { - pOut[i++] = 'C'; - } - pOut[i] = 0x00; - } - return wAttrib; + if(uOutSize >= 6) + { + i=0; + if(wAttrib & FILE_ATTRIBUTE_READONLY) + pOut[i++] = 'R'; + if(wAttrib & FILE_ATTRIBUTE_HIDDEN) + pOut[i++] = 'H'; + if(wAttrib & FILE_ATTRIBUTE_SYSTEM) + pOut[i++] = 'S'; + if(wAttrib & FILE_ATTRIBUTE_ARCHIVE) + pOut[i++] = 'A'; + if(wAttrib & FILE_ATTRIBUTE_COMPRESSED) + pOut[i++] = 'C'; + pOut[i] = 0x00; + } + return wAttrib; } /************************************************************************* -* ILFreeaPidl -* -* free a aPidl struct -*/ + * ILFreeaPidl + * + * free a aPidl struct + */ void _ILFreeaPidl(LPITEMIDLIST * apidl, UINT cidl) { - UINT i; + UINT i; - if (apidl) - { - for (i = 0; i < cidl; i++) SHFree(apidl[i]); - SHFree(apidl); - } + if (apidl) + { + for (i = 0; i < cidl; i++) + SHFree(apidl[i]); + SHFree(apidl); + } } /************************************************************************* -* ILCopyaPidl -* -* copies an aPidl struct -*/ + * ILCopyaPidl + * + * copies an aPidl struct + */ LPITEMIDLIST* _ILCopyaPidl(LPCITEMIDLIST * apidlsrc, UINT cidl) { - UINT i; - LPITEMIDLIST * apidldest = (LPITEMIDLIST*)SHAlloc(cidl * sizeof(LPITEMIDLIST)); - if(!apidlsrc) return NULL; + UINT i; + LPITEMIDLIST *apidldest; - for (i = 0; i < cidl; i++) - apidldest[i] = ILClone(apidlsrc[i]); + apidldest = SHAlloc(cidl * sizeof(LPITEMIDLIST)); + if (!apidlsrc) + return NULL; - return apidldest; + for (i = 0; i < cidl; i++) + apidldest[i] = ILClone(apidlsrc[i]); + + return apidldest; } /************************************************************************* -* _ILCopyCidaToaPidl -* -* creates aPidl from CIDA -*/ + * _ILCopyCidaToaPidl + * + * creates aPidl from CIDA + */ LPITEMIDLIST* _ILCopyCidaToaPidl(LPITEMIDLIST* pidl, LPIDA cida) { - UINT i; - LPITEMIDLIST * dst = (LPITEMIDLIST*)SHAlloc(cida->cidl * sizeof(LPITEMIDLIST)); + UINT i; + LPITEMIDLIST *dst; - if(!dst) return NULL; + dst = SHAlloc(cida->cidl * sizeof(LPITEMIDLIST)); + if (!dst) + return NULL; - if (pidl) - *pidl = ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[0]])); + if (pidl) + *pidl = ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[0]])); - for (i = 0; i < cida->cidl; i++) - dst[i] = ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[i + 1]])); + for (i = 0; i < cida->cidl; i++) + dst[i] = ILClone((LPITEMIDLIST)(&((LPBYTE)cida)[cida->aoffset[i + 1]])); - return dst; + return dst; } diff --git a/reactos/lib/shell32/pidl.h b/reactos/lib/shell32/pidl.h index b28d217ff6b..1bf1828beba 100644 --- a/reactos/lib/shell32/pidl.h +++ b/reactos/lib/shell32/pidl.h @@ -208,11 +208,14 @@ LPITEMIDLIST _ILCreateGuid(PIDLTYPE type, REFIID guid); /* Like _ILCreateGuid, but using the string szGUID. */ LPITEMIDLIST _ILCreateGuidFromStrA(LPCSTR szGUID); +LPITEMIDLIST _ILCreateGuidFromStrW(LPCWSTR szGUID); /* Commonly used PIDLs representing file system objects. */ LPITEMIDLIST _ILCreateDesktop (void); LPITEMIDLIST _ILCreateFromFindDataA(WIN32_FIND_DATAA *stffile); +LPITEMIDLIST _ILCreateFromFindDataW(WIN32_FIND_DATAW *stffile); HRESULT _ILCreateFromPathA (LPCSTR szPath, LPITEMIDLIST* ppidl); +HRESULT _ILCreateFromPathW (LPCWSTR szPath, LPITEMIDLIST* ppidl); /* Other helpers */ LPITEMIDLIST _ILCreateMyComputer (void); diff --git a/reactos/lib/shell32/shell32.spec b/reactos/lib/shell32/shell32.spec index 17e70984602..b038862cd3f 100644 --- a/reactos/lib/shell32/shell32.spec +++ b/reactos/lib/shell32/shell32.spec @@ -3,193 +3,186 @@ # win95 and winNT dlls import shell32.dll by ordinal) # This list was updated to dll version 4.72 - 2 stdcall SHChangeNotifyRegister(long long long long long ptr) - 4 stdcall SHChangeNotifyDeregister (long) - 5 stdcall SHChangeNotifyUpdateEntryList (long long long long) - 9 stub PifMgr_OpenProperties - 10 stub PifMgr_GetProperties - 11 stub PifMgr_SetProperties - 13 stub PifMgr_CloseProperties - 15 stdcall ILGetDisplayName(ptr ptr) - 16 stdcall ILFindLastID(ptr) - 17 stdcall ILRemoveLastID(ptr) - 18 stdcall ILClone(ptr) - 19 stdcall ILCloneFirst (ptr) - 20 stdcall ILGlobalClone (ptr) - 21 stdcall ILIsEqual (ptr ptr) - 23 stdcall ILIsParent (ptr ptr long) - 24 stdcall ILFindChild (ptr ptr) - 25 stdcall ILCombine(ptr ptr) - 26 stdcall ILLoadFromStream (ptr ptr) - 27 stdcall ILSaveToStream(ptr ptr) - 28 stdcall SHILCreateFromPath(ptr ptr ptr) SHILCreateFromPathAW - 29 stdcall PathIsRoot(ptr) PathIsRootAW - 30 stdcall PathBuildRoot(ptr long) PathBuildRootAW - 31 stdcall PathFindExtension(ptr) PathFindExtensionAW - 32 stdcall PathAddBackslash(ptr) PathAddBackslashAW - 33 stdcall PathRemoveBlanks(ptr) PathRemoveBlanksAW - 34 stdcall PathFindFileName(ptr) PathFindFileNameAW - 35 stdcall PathRemoveFileSpec(ptr) PathRemoveFileSpecAW - 36 stdcall PathAppend(ptr ptr) PathAppendAW - 37 stdcall PathCombine(ptr ptr ptr) PathCombineAW - 38 stdcall PathStripPath(ptr)PathStripPathAW - 39 stdcall PathIsUNC (ptr) PathIsUNCAW - 40 stdcall PathIsRelative (ptr) PathIsRelativeAW - 41 stdcall IsLFNDriveA(str) - 42 stdcall IsLFNDriveW(wstr) - 43 stdcall PathIsExe (ptr) PathIsExeAW - 45 stdcall PathFileExists(ptr) PathFileExistsAW - 46 stdcall PathMatchSpec (ptr ptr) PathMatchSpecAW - 47 stdcall PathMakeUniqueName (ptr long ptr ptr ptr)PathMakeUniqueNameAW - 48 stdcall PathSetDlgItemPath (long long ptr) PathSetDlgItemPathAW - 49 stdcall PathQualify (ptr) PathQualifyAW - 50 stdcall PathStripToRoot (ptr) PathStripToRootAW - 51 stdcall PathResolve(str long long) PathResolveAW - 52 stdcall PathGetArgs(str) PathGetArgsAW - 53 stdcall DoEnvironmentSubst (long long) DoEnvironmentSubstAW - 54 stdcall DragAcceptFiles(long long) - 55 stdcall PathQuoteSpaces (ptr) PathQuoteSpacesAW - 56 stdcall PathUnquoteSpaces(str) PathUnquoteSpacesAW - 57 stdcall PathGetDriveNumber (str) PathGetDriveNumberAW - 58 stdcall ParseField(str long ptr long) ParseFieldAW - 59 stdcall RestartDialog(long wstr long) - 60 stdcall ExitWindowsDialog(long) - 61 stdcall RunFileDlg(long long long str str long) - 62 stdcall PickIconDlg(long long long long) - 63 stdcall GetFileNameFromBrowse(long long long long str str str) - 64 stdcall DriveType (long) - 65 stub InvalidateDriveType - 66 stdcall IsNetDrive(long) - 67 stdcall Shell_MergeMenus (long long long long long long) - 68 stdcall SHGetSetSettings(ptr long long) - 69 stub SHGetNetResource - 70 stdcall SHCreateDefClassObject(long long long long long) - 71 stdcall Shell_GetImageList(ptr ptr) - 72 stdcall Shell_GetCachedImageIndex(ptr ptr long) Shell_GetCachedImageIndexAW - 73 stdcall SHShellFolderView_Message(long long long) - 74 stdcall SHCreateStdEnumFmtEtc(long ptr ptr) - 75 stdcall PathYetAnotherMakeUniqueName(ptr wstr wstr wstr) + 2 stdcall -noname SHChangeNotifyRegister(long long long long long ptr) + 4 stdcall -noname SHChangeNotifyDeregister(long) + 5 stdcall -noname SHChangeNotifyUpdateEntryList(long long long long) + 9 stub -noname PifMgr_OpenProperties + 10 stub -noname PifMgr_GetProperties + 11 stub -noname PifMgr_SetProperties + 13 stub -noname PifMgr_CloseProperties + 15 stdcall -noname ILGetDisplayName(ptr ptr) + 16 stdcall -noname ILFindLastID(ptr) + 17 stdcall -noname ILRemoveLastID(ptr) + 18 stdcall -noname ILClone(ptr) + 19 stdcall -noname ILCloneFirst(ptr) + 20 stdcall -noname ILGlobalClone(ptr) + 21 stdcall -noname ILIsEqual(ptr ptr) + 23 stdcall -noname ILIsParent(ptr ptr long) + 24 stdcall -noname ILFindChild(ptr ptr) + 25 stdcall -noname ILCombine(ptr ptr) + 26 stdcall -noname ILLoadFromStream(ptr ptr) + 27 stdcall -noname ILSaveToStream(ptr ptr) + 28 stdcall -noname SHILCreateFromPath(ptr ptr ptr) SHILCreateFromPathAW + 29 stdcall -noname PathIsRoot(ptr) PathIsRootAW + 30 stdcall -noname PathBuildRoot(ptr long) PathBuildRootAW + 31 stdcall -noname PathFindExtension(ptr) PathFindExtensionAW + 32 stdcall -noname PathAddBackslash(ptr) PathAddBackslashAW + 33 stdcall -noname PathRemoveBlanks(ptr) PathRemoveBlanksAW + 34 stdcall -noname PathFindFileName(ptr) PathFindFileNameAW + 35 stdcall -noname PathRemoveFileSpec(ptr) PathRemoveFileSpecAW + 36 stdcall -noname PathAppend(ptr ptr) PathAppendAW + 37 stdcall -noname PathCombine(ptr ptr ptr) PathCombineAW + 38 stdcall -noname PathStripPath(ptr)PathStripPathAW + 39 stdcall -noname PathIsUNC(ptr) PathIsUNCAW + 40 stdcall -noname PathIsRelative(ptr) PathIsRelativeAW + 41 stdcall -noname IsLFNDriveA(str) + 42 stdcall -noname IsLFNDriveW(wstr) + 43 stdcall -noname PathIsExe(ptr) PathIsExeAW + 45 stdcall -noname PathFileExists(ptr) PathFileExistsAW + 46 stdcall -noname PathMatchSpec(ptr ptr) PathMatchSpecAW + 47 stdcall -noname PathMakeUniqueName(ptr long ptr ptr ptr)PathMakeUniqueNameAW + 48 stdcall -noname PathSetDlgItemPath(long long ptr) PathSetDlgItemPathAW + 49 stdcall -noname PathQualify(ptr) PathQualifyAW + 50 stdcall -noname PathStripToRoot(ptr) PathStripToRootAW + 51 stdcall -noname PathResolve(str long long) PathResolveAW + 52 stdcall -noname PathGetArgs(str) PathGetArgsAW + 53 stdcall DoEnvironmentSubst(long long) DoEnvironmentSubstAW + 55 stdcall -noname PathQuoteSpaces(ptr) PathQuoteSpacesAW + 56 stdcall -noname PathUnquoteSpaces(str) PathUnquoteSpacesAW + 57 stdcall -noname PathGetDriveNumber(str) PathGetDriveNumberAW + 58 stdcall -noname ParseField(str long ptr long) ParseFieldAW + 59 stdcall -noname RestartDialog(long wstr long) + 60 stdcall -noname ExitWindowsDialog(long) + 61 stdcall -noname RunFileDlg(long long long str str long) + 62 stdcall -noname PickIconDlg(long long long long) + 63 stdcall -noname GetFileNameFromBrowse(long long long long str str str) + 64 stdcall -noname DriveType(long) + 65 stub -noname InvalidateDriveType + 66 stdcall -noname IsNetDrive(long) + 67 stdcall -noname Shell_MergeMenus(long long long long long long) + 68 stdcall -noname SHGetSetSettings(ptr long long) + 69 stub -noname SHGetNetResource + 70 stdcall -noname SHCreateDefClassObject(long long long long long) + 71 stdcall -noname Shell_GetImageList(ptr ptr) + 72 stdcall -noname Shell_GetCachedImageIndex(ptr ptr long) Shell_GetCachedImageIndexAW + 73 stdcall -noname SHShellFolderView_Message(long long long) + 74 stdcall -noname SHCreateStdEnumFmtEtc(long ptr ptr) + 75 stdcall -noname PathYetAnotherMakeUniqueName(ptr wstr wstr wstr) 76 stub DragQueryInfo - 77 stdcall SHMapPIDLToSystemImageListIndex(ptr ptr ptr) - 78 stdcall OleStrToStrN(str long wstr long) OleStrToStrNAW - 79 stdcall StrToOleStrN(wstr long str long) StrToOleStrNAW - 80 stdcall DragFinish(long) - 81 stdcall DragQueryFile(long long ptr long) DragQueryFileA - 82 stdcall DragQueryFileA(long long ptr long) - 83 stdcall CIDLData_CreateFromIDArray(ptr long ptr ptr) + 77 stdcall -noname SHMapPIDLToSystemImageListIndex(ptr ptr ptr) + 78 stdcall -noname OleStrToStrN(str long wstr long) OleStrToStrNAW + 79 stdcall -noname StrToOleStrN(wstr long str long) StrToOleStrNAW + 83 stdcall -noname CIDLData_CreateFromIDArray(ptr long ptr ptr) 84 stub SHIsBadInterfacePtr - 85 stdcall OpenRegStream(long str str long) shlwapi.SHOpenRegStreamA - 86 stdcall SHRegisterDragDrop(long ptr) - 87 stdcall SHRevokeDragDrop(long) - 88 stdcall SHDoDragDrop(long ptr ptr long ptr) - 89 stdcall SHCloneSpecialIDList(long long long) - 90 stdcall SHFindFiles(ptr ptr) + 85 stdcall -noname OpenRegStream(long str str long) shlwapi.SHOpenRegStreamA + 86 stdcall -noname SHRegisterDragDrop(long ptr) + 87 stdcall -noname SHRevokeDragDrop(long) + 88 stdcall -noname SHDoDragDrop(long ptr ptr long ptr) + 89 stdcall -noname SHCloneSpecialIDList(long long long) + 90 stdcall -noname SHFindFiles(ptr ptr) 91 stub SHFindComputer - 92 stdcall PathGetShortPath (ptr) PathGetShortPathAW - 93 stdcall Win32CreateDirectory(wstr ptr) Win32CreateDirectoryAW - 94 stdcall Win32RemoveDirectory(wstr) Win32RemoveDirectoryAW - 95 stdcall SHLogILFromFSIL (ptr) - 96 stdcall StrRetToStrN (ptr long ptr ptr) StrRetToStrNAW - 97 stdcall SHWaitForFileToOpen (long long long) - 98 stdcall SHGetRealIDL (ptr ptr ptr) - 99 stdcall SetAppStartingCursor (long long) - 100 stdcall SHRestricted(long) + 92 stdcall -noname PathGetShortPath(ptr) PathGetShortPathAW + 93 stdcall -noname Win32CreateDirectory(wstr ptr) Win32CreateDirectoryAW + 94 stdcall -noname Win32RemoveDirectory(wstr) Win32RemoveDirectoryAW + 95 stdcall -noname SHLogILFromFSIL(ptr) + 96 stdcall -noname StrRetToStrN(ptr long ptr ptr) StrRetToStrNAW + 97 stdcall -noname SHWaitForFileToOpen (long long long) + 98 stdcall -noname SHGetRealIDL(ptr ptr ptr) + 99 stdcall -noname SetAppStartingCursor(long long) + 100 stdcall -noname SHRestricted(long) - 102 stdcall SHCoCreateInstance(wstr ptr long ptr ptr) - 103 stdcall SignalFileOpen(long) - 104 stdcall FileMenu_DeleteAllItems(long) - 105 stdcall FileMenu_DrawItem(long ptr) - 106 stdcall FileMenu_FindSubMenuByPidl(long ptr) - 107 stdcall FileMenu_GetLastSelectedItemPidls(long ptr ptr) - 108 stdcall FileMenu_HandleMenuChar(long long) - 109 stdcall FileMenu_InitMenuPopup (long) - 110 stdcall FileMenu_InsertUsingPidl (long long ptr long long ptr) - 111 stdcall FileMenu_Invalidate (long) - 112 stdcall FileMenu_MeasureItem(long ptr) - 113 stdcall FileMenu_ReplaceUsingPidl (long long ptr long ptr) - 114 stdcall FileMenu_Create (long long long long long) - 115 stdcall FileMenu_AppendItem (long ptr long long long long) FileMenu_AppendItemAW - 116 stdcall FileMenu_TrackPopupMenuEx (long long long long long long) - 117 stdcall FileMenu_DeleteItemByCmd(long long) - 118 stdcall FileMenu_Destroy (long) - 119 stdcall IsLFNDrive(ptr) IsLFNDriveAW - 120 stdcall FileMenu_AbortInitMenu () - 121 stdcall SHFlushClipboard () + 102 stdcall -noname SHCoCreateInstance(wstr ptr long ptr ptr) + 103 stdcall -noname SignalFileOpen(long) + 104 stdcall -noname FileMenu_DeleteAllItems(long) + 105 stdcall -noname FileMenu_DrawItem(long ptr) + 106 stdcall -noname FileMenu_FindSubMenuByPidl(long ptr) + 107 stdcall -noname FileMenu_GetLastSelectedItemPidls(long ptr ptr) + 108 stdcall -noname FileMenu_HandleMenuChar(long long) + 109 stdcall -noname FileMenu_InitMenuPopup(long) + 110 stdcall -noname FileMenu_InsertUsingPidl (long long ptr long long ptr) + 111 stdcall -noname FileMenu_Invalidate(long) + 112 stdcall -noname FileMenu_MeasureItem(long ptr) + 113 stdcall -noname FileMenu_ReplaceUsingPidl(long long ptr long ptr) + 114 stdcall -noname FileMenu_Create(long long long long long) + 115 stdcall -noname FileMenu_AppendItem(long ptr long long long long) FileMenu_AppendItemAW + 116 stdcall -noname FileMenu_TrackPopupMenuEx(long long long long long long) + 117 stdcall -noname FileMenu_DeleteItemByCmd(long long) + 118 stdcall -noname FileMenu_Destroy(long) + 119 stdcall -noname IsLFNDrive(ptr) IsLFNDriveAW + 120 stdcall -noname FileMenu_AbortInitMenu() + 121 stdcall -noname SHFlushClipboard() 122 stdcall -noname RunDLL_CallEntry16(long long long str long) #name wrong? - 123 stdcall SHFreeUnusedLibraries () - 124 stdcall FileMenu_AppendFilesForPidl(long ptr long) - 125 stdcall FileMenu_AddFilesForPidl(long long long ptr long long ptr) - 126 stdcall SHOutOfMemoryMessageBox (long long long) - 127 stdcall SHWinHelp (long long long long) - 128 stdcall -private DllGetClassObject(long long ptr) SHELL32_DllGetClassObject - 129 stdcall DAD_AutoScroll(long ptr ptr) - 130 stdcall DAD_DragEnter(long) - 131 stdcall DAD_DragEnterEx(long long long) - 132 stdcall DAD_DragLeave() - 133 stdcall DragQueryFileW(long long ptr long) - 134 stdcall DAD_DragMove(long long) - 135 stdcall DragQueryPoint(long ptr) - 136 stdcall DAD_SetDragImage(long long) - 137 stdcall DAD_ShowDragImage (long) + 123 stdcall -noname SHFreeUnusedLibraries() + 124 stdcall -noname FileMenu_AppendFilesForPidl(long ptr long) + 125 stdcall -noname FileMenu_AddFilesForPidl(long long long ptr long long ptr) + 126 stdcall -noname SHOutOfMemoryMessageBox(long long long) + 127 stdcall -noname SHWinHelp(long long long long) + 129 stdcall -noname DAD_AutoScroll(long ptr ptr) + 130 stdcall -noname DAD_DragEnter(long) + 131 stdcall -noname DAD_DragEnterEx(long long long) + 132 stdcall -noname DAD_DragLeave() + 134 stdcall -noname DAD_DragMove(long long) + 136 stdcall -noname DAD_SetDragImage(long long) + 137 stdcall -noname DAD_ShowDragImage(long) 139 stub Desktop_UpdateBriefcaseOnEvent - 140 stdcall FileMenu_DeleteItemByIndex(long long) - 141 stdcall FileMenu_DeleteItemByFirstID(long long) - 142 stdcall FileMenu_DeleteSeparator(long) - 143 stdcall FileMenu_EnableItemByCmd(long long long) - 144 stdcall FileMenu_GetItemExtent (long long) - 145 stdcall PathFindOnPath (ptr ptr) PathFindOnPathAW - 146 stdcall RLBuildListOfPaths() - 147 stdcall SHCLSIDFromString(long long) SHCLSIDFromStringAW - 149 stdcall SHFind_InitMenuPopup(long long long long) + 140 stdcall -noname FileMenu_DeleteItemByIndex(long long) + 141 stdcall -noname FileMenu_DeleteItemByFirstID(long long) + 142 stdcall -noname FileMenu_DeleteSeparator(long) + 143 stdcall -noname FileMenu_EnableItemByCmd(long long long) + 144 stdcall -noname FileMenu_GetItemExtent(long long) + 145 stdcall -noname PathFindOnPath(ptr ptr) PathFindOnPathAW + 146 stdcall -noname RLBuildListOfPaths() + 147 stdcall -noname SHCLSIDFromString(long long) SHCLSIDFromStringAW + 149 stdcall -noname SHFind_InitMenuPopup(long long long long) - 151 stdcall SHLoadOLE (long) - 152 stdcall ILGetSize(ptr) - 153 stdcall ILGetNext(ptr) - 154 stdcall ILAppend (long long long) - 155 stdcall ILFree (ptr) - 156 stdcall ILGlobalFree (ptr) - 157 stdcall ILCreateFromPath (ptr) ILCreateFromPathAW - 158 stdcall PathGetExtension(str long long) PathGetExtensionAW - 159 stdcall PathIsDirectory(ptr)PathIsDirectoryAW + 151 stdcall -noname SHLoadOLE(long) + 152 stdcall -noname ILGetSize(ptr) + 153 stdcall -noname ILGetNext(ptr) + 154 stdcall -noname ILAppend(long long long) + 155 stdcall -noname ILFree(ptr) + 156 stdcall -noname ILGlobalFree(ptr) + 157 stdcall -noname ILCreateFromPath(ptr) ILCreateFromPathAW + 158 stdcall -noname PathGetExtension(str long long) PathGetExtensionAW + 159 stdcall -noname PathIsDirectory(ptr) PathIsDirectoryAW 160 stub SHNetConnectionDialog - 161 stdcall SHRunControlPanel (long long) - 162 stdcall SHSimpleIDListFromPath (ptr) SHSimpleIDListFromPathAW - 163 stdcall StrToOleStr (wstr str) StrToOleStrAW - 164 stdcall Win32DeleteFile(str) Win32DeleteFileAW - 165 stdcall SHCreateDirectory(long ptr) - 166 stdcall CallCPLEntry16(long long long long long long) - 167 stdcall SHAddFromPropSheetExtArray(long long long) - 168 stdcall SHCreatePropSheetExtArray(long str long) - 169 stdcall SHDestroyPropSheetExtArray(long) - 170 stdcall SHReplaceFromPropSheetExtArray(long long long long) - 171 stdcall PathCleanupSpec(ptr ptr) - 172 stdcall SHCreateLinks(long str ptr long ptr) - 173 stdcall SHValidateUNC(long long long) - 174 stdcall SHCreateShellFolderViewEx (ptr ptr) - 175 stdcall SHGetSpecialFolderPath(long long long long) SHGetSpecialFolderPathAW - 176 stdcall SHSetInstanceExplorer (long) + 161 stdcall -noname SHRunControlPanel(long long) + 162 stdcall -noname SHSimpleIDListFromPath(ptr) SHSimpleIDListFromPathAW + 163 stdcall -noname StrToOleStr(wstr str) StrToOleStrAW + 164 stdcall -noname Win32DeleteFile(str) Win32DeleteFileAW + 165 stdcall -noname SHCreateDirectory(long ptr) + 166 stdcall -noname CallCPLEntry16(long long long long long long) + 167 stdcall -noname SHAddFromPropSheetExtArray(long long long) + 168 stdcall -noname SHCreatePropSheetExtArray(long str long) + 169 stdcall -noname SHDestroyPropSheetExtArray(long) + 170 stdcall -noname SHReplaceFromPropSheetExtArray(long long long long) + 171 stdcall -noname PathCleanupSpec(ptr ptr) + 172 stdcall -noname SHCreateLinks(long str ptr long ptr) + 173 stdcall -noname SHValidateUNC(long long long) + 174 stdcall -noname SHCreateShellFolderViewEx(ptr ptr) + 175 stdcall -noname SHGetSpecialFolderPath(long long long long) SHGetSpecialFolderPathAW + 176 stdcall -noname SHSetInstanceExplorer(long) 177 stub DAD_SetDragImageFromListView - 178 stdcall SHObjectProperties(long long wstr wstr) - 179 stdcall SHGetNewLinkInfoA(str str ptr long long) - 180 stdcall SHGetNewLinkInfoW(wstr wstr ptr long long) - 181 stdcall RegisterShellHook(long long) - 182 varargs ShellMessageBoxW(long long long str long) - 183 varargs ShellMessageBoxA(long long long str long) - 184 stdcall ArrangeWindows(long long long long long) + 178 stdcall -noname SHObjectProperties(long long wstr wstr) + 179 stdcall -noname SHGetNewLinkInfoA(str str ptr long long) + 180 stdcall -noname SHGetNewLinkInfoW(wstr wstr ptr long long) + 181 stdcall -noname RegisterShellHook(long long) + 182 varargs -noname ShellMessageBoxW(long long long str long) + 183 varargs -noname ShellMessageBoxA(long long long str long) + 184 stdcall -noname ArrangeWindows(long long long long long) 185 stub SHHandleDiskFull - 186 stdcall ILGetDisplayNameEx(ptr ptr ptr long) + 186 stdcall -noname ILGetDisplayNameEx(ptr ptr ptr long) 187 stub ILGetPseudoNameW - 188 stdcall ShellDDEInit(long) - 189 stdcall ILCreateFromPathA(str) - 190 stdcall ILCreateFromPathW(wstr) - 191 stdcall SHUpdateImageA(str long long long) - 192 stdcall SHUpdateImageW(wstr long long long) - 193 stdcall SHHandleUpdateImage(ptr) + 188 stdcall -noname ShellDDEInit(long) + 189 stdcall -noname ILCreateFromPathA(str) + 190 stdcall -noname ILCreateFromPathW(wstr) + 191 stdcall -noname SHUpdateImageA(str long long long) + 192 stdcall -noname SHUpdateImageW(wstr long long long) + 193 stdcall -noname SHHandleUpdateImage(ptr) 194 stub SHCreatePropSheetExtArrayEx - 195 stdcall SHFree(ptr) - 196 stdcall SHAlloc(long) + 195 stdcall -noname SHFree(ptr) + 196 stdcall -noname SHAlloc(long) 197 stub SHGlobalDefect - 198 stdcall SHAbortInvokeCommand () + 198 stdcall -noname SHAbortInvokeCommand() 199 stub SHGetFileIcon 200 stub SHLocalAlloc 201 stub SHLocalFree @@ -206,72 +199,15 @@ 212 stub Printers_AddPrinterPropPages 213 stub Printers_RegisterWindowW 214 stub Printers_UnregisterWindow - 215 stdcall SHStartNetConnectionDialog(long str long) + 215 stdcall -noname SHStartNetConnectionDialog(long str long) 243 stdcall @(long long) shell32_243 - 244 stdcall SHInitRestricted(ptr ptr) - 247 stdcall SHGetDataFromIDListA (ptr ptr long ptr long) - 248 stdcall SHGetDataFromIDListW (ptr ptr long ptr long) - 249 stdcall PathParseIconLocation (ptr) PathParseIconLocationAW - 250 stdcall PathRemoveExtension (ptr) PathRemoveExtensionAW - 251 stdcall PathRemoveArgs (ptr) PathRemoveArgsAW + 244 stdcall -noname SHInitRestricted(ptr ptr) + 249 stdcall -noname PathParseIconLocation(ptr) PathParseIconLocationAW + 250 stdcall -noname PathRemoveExtension(ptr) PathRemoveExtensionAW + 251 stdcall -noname PathRemoveArgs(ptr) PathRemoveArgsAW 256 stdcall @(ptr ptr) SHELL32_256 - 271 stub SheChangeDirA - 272 stub SheChangeDirExA - 273 stub SheChangeDirExW - 274 stdcall SheChangeDirW(wstr) - 275 stub SheConvertPathW - 276 stub SheFullPathA - 277 stub SheFullPathW - 278 stub SheGetCurDrive - 279 stub SheGetDirA - 280 stub SheGetDirExW - 281 stdcall SheGetDirW (long long) - 282 stub SheGetPathOffsetW - 283 stub SheRemoveQuotesA - 284 stub SheRemoveQuotesW - 285 stub SheSetCurDrive - 286 stub SheShortenPathA - 287 stub SheShortenPathW - 288 stdcall ShellAboutA(long str str long) - 289 stdcall ShellAboutW(long wstr wstr long) - 290 stdcall ShellExecuteA(long str str str str long) - 291 stdcall ShellExecuteEx (long) ShellExecuteExA - 292 stdcall ShellExecuteExA (long) - 293 stdcall ShellExecuteExW (long) - 294 stdcall ShellExecuteW (long wstr wstr wstr wstr long) - 296 stdcall Shell_NotifyIcon(long ptr) Shell_NotifyIconA - 297 stdcall Shell_NotifyIconA(long ptr) - 298 stdcall Shell_NotifyIconW(long ptr) #299 stub Shl1632_ThunkData32 #300 stub Shl3216_ThunkData32 - 301 stdcall StrChrA(str long) shlwapi.StrChrA - 302 stdcall StrChrIA(str long) shlwapi.StrChrIA - 303 stdcall StrChrIW(wstr long) shlwapi.StrChrIW - 304 stdcall StrChrW(wstr long) shlwapi.StrChrW - 305 stdcall StrCmpNA(str str long) shlwapi.StrCmpNA - 306 stdcall StrCmpNIA(str str long) shlwapi.StrCmpNIA - 307 stdcall StrCmpNIW(wstr wstr long) shlwapi.StrCmpNIW - 308 stdcall StrCmpNW(wstr wstr long) shlwapi.StrCmpNW - 309 stdcall StrCpyNA (ptr str long) lstrcpynA - 310 stdcall StrCpyNW(wstr wstr long) shlwapi.StrCpyNW - 311 stdcall StrNCmpA(str str long) shlwapi.StrCmpNA - 312 stdcall StrNCmpIA(str str long) shlwapi.StrCmpNIA - 313 stdcall StrNCmpIW(wstr wstr long) shlwapi.StrCmpNIW - 314 stdcall StrNCmpW(wstr wstr long) shlwapi.StrCmpNW - 315 stdcall StrNCpyA (ptr str long) lstrcpynA - 316 stdcall StrNCpyW(wstr wstr long) shlwapi.StrCpyNW - 317 stdcall StrRChrA(str str long) shlwapi.StrRChrA - 318 stdcall StrRChrIA(str str long) shlwapi.StrRChrIA - 319 stdcall StrRChrIW(str str long) shlwapi.StrRChrIW - 320 stdcall StrRChrW(wstr wstr long) shlwapi.StrRChrW - 321 stub StrRStrA - 322 stdcall StrRStrIA(str str str) shlwapi.StrRStrIA - 323 stdcall StrRStrIW(wstr wstr wstr) shlwapi.StrRStrIW - 324 stub StrRStrW - 325 stdcall StrStrA(str str) shlwapi.StrStrA - 326 stdcall StrStrIA(str str) shlwapi.StrStrIA - 327 stdcall StrStrIW(wstr wstr) shlwapi.StrStrIW - 328 stdcall StrStrW(wstr wstr) shlwapi.StrStrW 505 stdcall SHRegCloseKey (long) 506 stdcall SHRegOpenKeyA (long str long) @@ -282,38 +218,38 @@ 511 stdcall SHRegQueryValueExW (long wstr ptr ptr ptr ptr) 512 stdcall SHRegDeleteKeyW (long wstr) - 520 stdcall -noname SHAllocShared (ptr long long) - 521 stdcall -noname SHLockShared (long long) - 522 stdcall -noname SHUnlockShared (ptr) - 523 stdcall -noname SHFreeShared (long long) - 524 stdcall RealDriveType (long long) + 520 stdcall -noname SHAllocShared(ptr long long) + 521 stdcall -noname SHLockShared(long long) + 522 stdcall -noname SHUnlockShared(ptr) + 523 stdcall -noname SHFreeShared(long long) + 524 stdcall -noname RealDriveType(long long) 525 stub RealDriveTypeFlags - 640 stdcall NTSHChangeNotifyRegister (long long long long long long) - 641 stdcall NTSHChangeNotifyDeregister (long) + 640 stdcall -noname NTSHChangeNotifyRegister(long long long long long long) + 641 stdcall -noname NTSHChangeNotifyDeregister(long) 643 stub SHChangeNotifyReceive - 644 stdcall SHChangeNotification_Lock(long long ptr ptr) - 645 stdcall SHChangeNotification_Unlock(long) + 644 stdcall -noname SHChangeNotification_Lock(long long ptr ptr) + 645 stdcall -noname SHChangeNotification_Unlock(long) 646 stub SHChangeRegistrationReceive 647 stub ReceiveAddToRecentDocs 648 stub SHWaitOp_Operate - 650 stdcall PathIsSameRoot(ptr ptr)PathIsSameRootAW + 650 stdcall -noname PathIsSameRoot(ptr ptr) PathIsSameRootAW # nt40/win98 - 651 stdcall ReadCabinetState (long long) # OldReadCabinetState - 652 stdcall WriteCabinetState (long) - 653 stdcall PathProcessCommand (long long long long) PathProcessCommandAW + 651 stdcall -noname ReadCabinetState(long long) # OldReadCabinetState + 652 stdcall -noname WriteCabinetState(long) + 653 stdcall -noname PathProcessCommand(long long long long) PathProcessCommandAW # win98 - 654 stdcall @(long long)shell32_654 # ReadCabinetState@8 - 660 stdcall FileIconInit(long) - 680 stdcall IsUserAdmin() + 654 stdcall @(long long) shell32_654 # ReadCabinetState@8 + 660 stdcall -noname FileIconInit(long) + 680 stdcall -noname IsUserAdmin() # >= NT5 - 714 stdcall @(ptr)SHELL32_714 # PathIsTemporaryW - 730 stdcall RestartDialogEx(long wstr long long) + 714 stdcall @(ptr) SHELL32_714 # PathIsTemporaryW + 730 stdcall -noname RestartDialogEx(long wstr long long) 1217 stub FOOBAR1217 # no joke! This is the real name!! @@ -329,14 +265,22 @@ @ stdcall Control_FillCache_RunDLLW(long long long long) @ stdcall Control_RunDLL(ptr ptr str long) Control_RunDLLA @ stdcall Control_RunDLLA(ptr ptr str long) +@ stub Control_RunDLLAsUserW @ stdcall Control_RunDLLW(ptr ptr wstr long) @ stdcall -private DllCanUnloadNow() SHELL32_DllCanUnloadNow +@ stdcall -private DllGetClassObject(long long ptr) SHELL32_DllGetClassObject @ stdcall DllInstall(long wstr)SHELL32_DllInstall @ stdcall -private DllRegisterServer() SHELL32_DllRegisterServer @ stdcall -private DllUnregisterServer() SHELL32_DllUnregisterServer @ stdcall DoEnvironmentSubstA(str str) @ stdcall DoEnvironmentSubstW(wstr wstr) +@ stdcall DragAcceptFiles(long long) +@ stdcall DragFinish(long) +@ stdcall DragQueryFile(long long ptr long) DragQueryFileA +@ stdcall DragQueryFileA(long long ptr long) @ stub DragQueryFileAorW +@ stdcall DragQueryFileW(long long ptr long) +@ stdcall DragQueryPoint(long ptr) @ stdcall DuplicateIcon(long long) @ stdcall ExtractAssociatedIconA(long str ptr) @ stdcall ExtractAssociatedIconExA(long str long long) @@ -353,9 +297,11 @@ @ stub FindExeDlgProc @ stdcall FindExecutableA(ptr ptr ptr) @ stdcall FindExecutableW(wstr wstr wstr) +@ stub FixupOptionalComponents @ stdcall FreeIconList(long) @ stub InternalExtractIconListA @ stub InternalExtractIconListW +@ stub OCInstall @ stub OpenAs_RunDLL @ stub OpenAs_RunDLLA @ stub OpenAs_RunDLLW @@ -373,16 +319,48 @@ @ stdcall SHBrowseForFolderA(ptr) @ stdcall SHBrowseForFolderW(ptr) @ stdcall SHChangeNotify (long long ptr ptr) +@ stub SHChangeNotifySuspendResume @ stdcall SHCreateDirectoryExA(long str ptr) @ stdcall SHCreateDirectoryExW(long wstr ptr) +@ stub SHCreateProcessAsUserW +@ stdcall SheChangeDirA(str) +@ stub SheChangeDirExA +@ stub SheChangeDirExW +@ stdcall SheChangeDirW(wstr) +@ stub SheConvertPathW +@ stub SheFullPathA +@ stub SheFullPathW +@ stub SheGetCurDrive +@ stdcall SheGetDirA(long long) +@ stub SheGetDirExW +@ stdcall SheGetDirW (long long) +@ stub SheGetPathOffsetW +@ stdcall ShellAboutA(long str str long) +@ stdcall ShellAboutW(long wstr wstr long) +@ stdcall ShellExecuteA(long str str str str long) +@ stdcall ShellExecuteEx (long) ShellExecuteExA +@ stdcall ShellExecuteExA (long) +@ stdcall ShellExecuteExW (long) +@ stdcall ShellExecuteW (long wstr wstr wstr wstr long) @ stub ShellHookProc +@ stdcall Shell_NotifyIcon(long ptr) Shell_NotifyIconA +@ stdcall Shell_NotifyIconA(long ptr) +@ stdcall Shell_NotifyIconW(long ptr) @ stdcall SHEmptyRecycleBinA(long str long) @ stdcall SHEmptyRecycleBinW(long wstr long) +@ stub SheRemoveQuotesA +@ stub SheRemoveQuotesW +@ stub SheSetCurDrive +@ stub SheShortenPathA +@ stub SheShortenPathW +@ stub SHExtractIconsW @ stdcall SHFileOperation(ptr) SHFileOperationA @ stdcall SHFileOperationA(ptr) @ stdcall SHFileOperationW(ptr) @ stdcall SHFormatDrive(long long long long) @ stdcall SHFreeNameMappings(ptr) +@ stdcall SHGetDataFromIDListA(ptr ptr long ptr long) +@ stdcall SHGetDataFromIDListW(ptr ptr long ptr long) @ stdcall SHGetDesktopFolder(ptr) @ stdcall SHGetFileInfo(ptr long ptr long long) SHGetFileInfoA @ stdcall SHGetFileInfoA(ptr long ptr long long) @@ -395,13 +373,47 @@ @ stdcall SHGetPathFromIDListW(ptr ptr) @ stdcall SHGetSettings(ptr long) @ stdcall SHGetSpecialFolderLocation(long long ptr) -@ stdcall SHHelpShortcuts_RunDLL(long long long long) -@ stub SHHelpShortcuts_RunDLLA -@ stub SHHelpShortcuts_RunDLLW +@ stdcall SHHelpShortcuts_RunDLL(long long long long) SHHelpShortcuts_RunDLLA +@ stdcall SHHelpShortcuts_RunDLLA(long long long long) +@ stdcall SHHelpShortcuts_RunDLLW(long long long long) +@ stub SHInvokePrinterCommandA +@ stub SHInvokePrinterCommandW +@ stub SHIsFileAvailableOffline @ stdcall SHLoadInProc(long) +@ stub SHLoadNonloadedIconOverlayIdentifiers +@ stub SHPathPrepareForWriteA +@ stub SHPathPrepareForWriteW @ stdcall SHQueryRecycleBinA(str ptr) @ stdcall SHQueryRecycleBinW(wstr ptr) @ stub SHUpdateRecycleBinIcon +@ stdcall StrChrA(str long) shlwapi.StrChrA +@ stdcall StrChrIA(str long) shlwapi.StrChrIA +@ stdcall StrChrIW(wstr long) shlwapi.StrChrIW +@ stdcall StrChrW(wstr long) shlwapi.StrChrW +@ stdcall StrCmpNA(str str long) shlwapi.StrCmpNA +@ stdcall StrCmpNIA(str str long) shlwapi.StrCmpNIA +@ stdcall StrCmpNIW(wstr wstr long) shlwapi.StrCmpNIW +@ stdcall StrCmpNW(wstr wstr long) shlwapi.StrCmpNW +@ stdcall StrCpyNA (ptr str long) kernel32.lstrcpynA +@ stdcall StrCpyNW(wstr wstr long) shlwapi.StrCpyNW +@ stdcall StrNCmpA(str str long) shlwapi.StrCmpNA +@ stdcall StrNCmpIA(str str long) shlwapi.StrCmpNIA +@ stdcall StrNCmpIW(wstr wstr long) shlwapi.StrCmpNIW +@ stdcall StrNCmpW(wstr wstr long) shlwapi.StrCmpNW +@ stdcall StrNCpyA (ptr str long) kernel32.lstrcpynA +@ stdcall StrNCpyW(wstr wstr long) shlwapi.StrCpyNW +@ stdcall StrRChrA(str str long) shlwapi.StrRChrA +@ stdcall StrRChrIA(str str long) shlwapi.StrRChrIA +@ stdcall StrRChrIW(str str long) shlwapi.StrRChrIW +@ stdcall StrRChrW(wstr wstr long) shlwapi.StrRChrW +@ stub StrRStrA +@ stdcall StrRStrIA(str str str) shlwapi.StrRStrIA +@ stdcall StrRStrIW(wstr wstr wstr) shlwapi.StrRStrIW +@ stub StrRStrW +@ stdcall StrStrA(str str) shlwapi.StrStrA +@ stdcall StrStrIA(str str) shlwapi.StrStrIA +@ stdcall StrStrIW(wstr wstr) shlwapi.StrStrIW +@ stdcall StrStrW(wstr wstr) shlwapi.StrStrW @ stub WOWShellExecute # @@ -427,6 +439,9 @@ # version 5.00 (Win2K) # _WIN32_IE >= 0x0500 # +@ stub ShellExec_RunDLL +@ stub ShellExec_RunDLLA +@ stub ShellExec_RunDLLW @ stdcall SHBindToParent(ptr ptr ptr ptr) @ stdcall SHGetDiskFreeSpaceA(str ptr ptr ptr) kernel32.GetDiskFreeSpaceExA @ stdcall SHGetDiskFreeSpaceExA(str ptr ptr ptr) kernel32.GetDiskFreeSpaceExA @@ -434,6 +449,8 @@ @ stdcall SHGetFolderPathA(long long long long ptr) @ stdcall SHGetFolderPathW(long long long long ptr) @ stdcall SHGetFolderLocation(long long long long ptr) +@ stub SHGetIconOverlayIndexA +@ stub SHGetIconOverlayIndexW # version 6.0 (WinXP) # _WIN32_IE >= 0x600 diff --git a/reactos/lib/shell32/shell32_Es.rc b/reactos/lib/shell32/shell32_Es.rc index ec7bd2d9cf7..3b4dbe8c630 100644 --- a/reactos/lib/shell32/shell32_Es.rc +++ b/reactos/lib/shell32/shell32_Es.rc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -LANGUAGE LANG_SPANISH, SUBLANG_DEFAULT +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL MENU_001 MENU DISCARDABLE BEGIN diff --git a/reactos/lib/shell32/shell32_Pt.rc b/reactos/lib/shell32/shell32_Pt.rc index 3ebdf5c22ce..da1600677fd 100644 --- a/reactos/lib/shell32/shell32_Pt.rc +++ b/reactos/lib/shell32/shell32_Pt.rc @@ -188,3 +188,35 @@ STRINGTABLE DISCARDABLE IDS_SHUTDOWN_TITLE "Desligar" IDS_SHUTDOWN_PROMPT "Você quer finalizar a sessão no ReactOS?" } + +/* shell folder path default values - */ +STRINGTABLE DISCARDABLE +{ + IDS_PROGRAMS "Menu Iniciar\\Programas" + IDS_PERSONAL "Meus Documentos" + IDS_FAVORITES "Favoritos" + IDS_STARTUP "Menu Iniciar\\Programas\\Iniciar" + IDS_RECENT "Recent" + IDS_SENDTO "SendTo" + IDS_STARTMENU "Menu Iniciar" + IDS_MYMUSIC "Meus Documentos\\Minhas Músicas" + IDS_MYVIDEO "Meus Documentos\\Meus Vídeos" + IDS_DESKTOPDIRECTORY "Desktop" + IDS_NETHOOD "NetHood" + IDS_TEMPLATES "Templates" + IDS_APPDATA "Application Data" + IDS_PRINTHOOD "PrintHood" + IDS_LOCAL_APPDATA "Configurações locais\\Dados de aplicativos" + IDS_INTERNET_CACHE "Temporary Internet Files" + IDS_COOKIES "Cookies" + IDS_HISTORY "Histórico" + IDS_PROGRAM_FILES "Arquivos de programas" + IDS_MYPICTURES "Meus Documentos\\Minhas Imagens" + IDS_PROGRAM_FILES_COMMON "Arquivos de programas\\Arquivos comuns" + IDS_COMMON_DOCUMENTS "Documentos" + IDS_ADMINTOOLS "Menu Iniciar\\Programas\\Ferramentas Administrativas" + IDS_COMMON_MUSIC "Documentos\\Minhas Músicas" + IDS_COMMON_PICTURES "Documentos\\Minhas Imagens" + IDS_COMMON_VIDEO "Documentos\\Meus Vídeos" + IDS_CDBURN_AREA "Configurações locais\\Dados de aplicativos\\Microsoft\\CD Burning" +} diff --git a/reactos/lib/shell32/shell32_Sv.rc b/reactos/lib/shell32/shell32_Sv.rc index 86102cc1055..dd21543c6d0 100644 --- a/reactos/lib/shell32/shell32_Sv.rc +++ b/reactos/lib/shell32/shell32_Sv.rc @@ -1,5 +1,5 @@ /* - * Copyright 1998 Juergen Schmied + * Copyright 2005 David Nordenberg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,6 +18,92 @@ LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT +MENU_001 MENU DISCARDABLE +BEGIN + MENUITEM "&Stora ikoner", FCIDM_SHVIEW_BIGICON + MENUITEM "S&må ikoner", FCIDM_SHVIEW_SMALLICON + MENUITEM "&Lista", FCIDM_SHVIEW_LISTVIEW + MENUITEM "&Detaljer", FCIDM_SHVIEW_REPORTVIEW +END + +/* + shellview background menu +*/ +MENU_002 MENU DISCARDABLE +BEGIN + POPUP "" + BEGIN + POPUP "&Visa" + BEGIN + MENUITEM "&Stora ikoner", FCIDM_SHVIEW_BIGICON + MENUITEM "S&må ikoner", FCIDM_SHVIEW_SMALLICON + MENUITEM "&Lista", FCIDM_SHVIEW_LISTVIEW + MENUITEM "&Detaljer", FCIDM_SHVIEW_REPORTVIEW + END + MENUITEM SEPARATOR + POPUP "Ordna &ikoner efter" + BEGIN + MENUITEM "&Namn", 0x30 /* column 0 */ + MENUITEM "&Typ", 0x32 /* column 2 */ + MENUITEM "&Storlek", 0x31 /* ... */ + MENUITEM "&Datum", 0x33 + MENUITEM SEPARATOR + MENUITEM "&Ordna automatiskt", FCIDM_SHVIEW_AUTOARRANGE + END + MENUITEM "Rada upp ikoner", FCIDM_SHVIEW_SNAPTOGRID + MENUITEM SEPARATOR + MENUITEM "&Uppdatera", FCIDM_SHVIEW_REFRESH + MENUITEM SEPARATOR + MENUITEM "K&listra in", FCIDM_SHVIEW_INSERT + MENUITEM "Klistra in som genväg", FCIDM_SHVIEW_INSERTLINK + MENUITEM SEPARATOR + POPUP "&Ny" + BEGIN + MENUITEM "Ny &mapp", FCIDM_SHVIEW_NEWFOLDER + MENUITEM "Ny &genväg", FCIDM_SHVIEW_NEWLINK + MENUITEM SEPARATOR + END + MENUITEM SEPARATOR + MENUITEM "&Egenskaper", FCIDM_SHVIEW_PROPERTIES + END +END + +/* + shellview item menu +*/ +MENU_SHV_FILE MENU DISCARDABLE +BEGIN + POPUP "" + BEGIN + MENUITEM "&Utforska", FCIDM_SHVIEW_EXPLORE + MENUITEM "&Öppna", FCIDM_SHVIEW_OPEN + MENUITEM SEPARATOR + MENUITEM "&Klipp ut", FCIDM_SHVIEW_CUT + MENUITEM "K&opiera", FCIDM_SHVIEW_COPY + MENUITEM SEPARATOR + MENUITEM "Skapa &genväg", FCIDM_SHVIEW_CREATELINK + MENUITEM "&Ta bort", FCIDM_SHVIEW_DELETE + MENUITEM "&Byt namn", FCIDM_SHVIEW_RENAME + MENUITEM SEPARATOR + MENUITEM "&Egenskaper", FCIDM_SHVIEW_PROPERTIES + END +END + +SHBRSFORFOLDER_MSGBOX DIALOG LOADONCALL MOVEABLE DISCARDABLE 15, 40, 188, 192 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_SETFONT | DS_3DLOOK +CAPTION "Bläddra efter mapp" +FONT 8, "MS Shell Dlg" +{ + DEFPUSHBUTTON "OK", 1, 80, 176, 50, 12, BS_DEFPUSHBUTTON | WS_GROUP | WS_TABSTOP + PUSHBUTTON "Avbryt", 2, 134, 176, 50, 12, WS_GROUP | WS_TABSTOP + LTEXT "", IDD_TITLE, 4, 4, 180, 12 + LTEXT "", IDD_STATUS, 4, 25, 180, 12 + CONTROL "", IDD_TREEVIEW, "SysTreeView32", + TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | + WS_BORDER | WS_TABSTOP, + 4, 40, 180, 120 +} + SHELL_ABOUT_MSGBOX DIALOG LOADONCALL MOVEABLE DISCARDABLE 15, 40, 210, 152 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Om %s" @@ -37,10 +123,99 @@ CAPTION "" FONT 8, "MS Shell Dlg" { ICON "", 12297, 7, 11, 18, 20, WS_VISIBLE - LTEXT "Skriv namnet på ett program, en mapp eller ett dokument och ReactOS kommer att öppna det för dig.", 12289, 36, 11, 182, 18 +LTEXT "Skriv namnet på ett program, en mapp eller ett dokument för att öppna det.", 12289, 36, 11, 182, 18 LTEXT "&Öppna:", 12305, 7, 39, 24, 10 CONTROL "", 12298, "COMBOBOX", WS_TABSTOP | WS_GROUP | WS_VSCROLL | WS_VISIBLE | CBS_DISABLENOSCROLL | CBS_AUTOHSCROLL | CBS_DROPDOWN, 36, 37, 183, 100 DEFPUSHBUTTON "OK", IDOK, 62, 63, 50, 14, WS_TABSTOP PUSHBUTTON "Avbryt", IDCANCEL, 116, 63, 50, 14, WS_TABSTOP PUSHBUTTON "&Bläddra...", 12288, 170, 63, 50, 14, WS_TABSTOP } + +/* + special folders +*/ +STRINGTABLE DISCARDABLE +{ + IDS_DESKTOP "Skrivbord" + IDS_MYCOMPUTER "Den här datorn" +} + +/* + context menus +*/ +STRINGTABLE DISCARDABLE +{ + IDS_VIEW_LARGE "&Stora ikoner" + IDS_VIEW_SMALL "S&må ikoner" + IDS_VIEW_LIST "&Lista" + IDS_VIEW_DETAILS "&Detaljer" + IDS_SELECT "Välj" + IDS_OPEN "Öppna" +} + +STRINGTABLE DISCARDABLE +{ + IDS_CREATEFOLDER_DENIED "Kan inte skapa ny mapp: Åtkomst nekad." + IDS_CREATEFOLDER_CAPTION "Fel vid försök att skapa ny mapp" + IDS_DELETEITEM_CAPTION "Bekräfta borttagning av fil" + IDS_DELETEFOLDER_CAPTION "Bekräfta borttagning av mapp" + IDS_DELETEITEM_TEXT "Är du säker på att du vill ta bort '%1'?" + IDS_DELETEMULTIPLE_TEXT "Är du säker på att du vill ta bort de här %1 objekten?" + IDS_OVERWRITEFILE_TEXT "Skriv över fil %1?" + IDS_OVERWRITEFILE_CAPTION "Bekräfta filöverskrivning" +} + +/* columns in the shellview */ +STRINGTABLE +BEGIN + IDS_SHV_COLUMN1 "Fil" + IDS_SHV_COLUMN2 "Storlek" + IDS_SHV_COLUMN3 "Typ" + IDS_SHV_COLUMN4 "Modifierad" + IDS_SHV_COLUMN5 "Attribut" + IDS_SHV_COLUMN6 "Storlek" + IDS_SHV_COLUMN7 "Storlek tillgänglig" + IDS_SHV_COLUMN8 "Namn" + IDS_SHV_COLUMN9 "Kommentarer" +END + +/* message box strings */ +STRINGTABLE DISCARDABLE +{ + IDS_RESTART_TITLE "Starta om" + IDS_RESTART_PROMPT "Vill du starta om datorn?" + IDS_SHUTDOWN_TITLE "Stäng av" + IDS_SHUTDOWN_PROMPT "Vill du stänga av datorn?" +} + +/* shell folder path default values */ +STRINGTABLE DISCARDABLE +{ + IDS_PROGRAMS "Start-meny\\Program" + IDS_PERSONAL "Mina dokument" + IDS_FAVORITES "Favoriter" + IDS_STARTUP "Start-meny\\Program\\Autostart" + IDS_RECENT "Tidigare" + IDS_SENDTO "Skicka till" + IDS_STARTMENU "Start-meny" + IDS_MYMUSIC "Mina dokument\\Min musik" + IDS_MYVIDEO "Mina dokument\\Mina videoklipp" + IDS_DESKTOPDIRECTORY "Skrivbord" + IDS_NETHOOD "Nätverk" + IDS_TEMPLATES "Mallar" + IDS_APPDATA "Application Data" + IDS_PRINTHOOD "Skrivare" + IDS_LOCAL_APPDATA "Lokala inställningar\\Application Data" + IDS_INTERNET_CACHE "Temporary Internet Files" + IDS_COOKIES "Cookies" + IDS_HISTORY "Tidigare" + IDS_PROGRAM_FILES "Program" + IDS_MYPICTURES "Mina dokument\\Mina bilder" + IDS_PROGRAM_FILES_COMMON "Program\\Delade filer" + IDS_COMMON_DOCUMENTS "Delade dokument" + IDS_ADMINTOOLS "Start-meny\\Program\\Administrativa verktyg" + IDS_COMMON_MUSIC "Delade dokument\\Min musik" + IDS_COMMON_PICTURES "Delade dokument\\Mina bilder" + IDS_COMMON_VIDEO "Delade dokument\\Mina videoklipp" + IDS_CDBURN_AREA "Lokala inställningar\\Application Data\\Microsoft\\CD Burning" +} diff --git a/reactos/lib/shell32/shell32_main.c b/reactos/lib/shell32/shell32_main.c index f107146a5ac..44d3e0139ea 100644 --- a/reactos/lib/shell32/shell32_main.c +++ b/reactos/lib/shell32/shell32_main.c @@ -1,5 +1,5 @@ /* - * Shell basics + * Shell basics * * Copyright 1998 Marcus Meissner * Copyright 1998 Juergen Schmied (jsch) * @@ -54,7 +54,7 @@ extern const char * const SHELL_Authors[]; #define MORE_DEBUG 1 /************************************************************************* - * CommandLineToArgvW [SHELL32.@] + * CommandLineToArgvW [SHELL32.@] * * We must interpret the quotes in the command line to rebuild the argv * array correctly: @@ -92,15 +92,18 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs) LPWSTR cmdline; int in_quotes,bcount; - if (*lpCmdline==0) { + if (*lpCmdline==0) + { /* Return the path to the executable */ DWORD len, size=16; hargv=GlobalAlloc(size, 0); - argv=GlobalLock(hargv); - for (;;) { + argv=GlobalLock(hargv); + for (;;) + { len = GetModuleFileNameW(0, (LPWSTR)(argv+1), size-sizeof(LPWSTR)); - if (!len) { + if (!len) + { GlobalFree(hargv); return NULL; } @@ -121,8 +124,10 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs) bcount=0; in_quotes=0; cs=lpCmdline; - while (1) { - if (*cs==0 || ((*cs==0x0009 || *cs==0x0020) && !in_quotes)) { + while (1) + { + if (*cs==0 || ((*cs==0x0009 || *cs==0x0020) && !in_quotes)) + { /* space */ argc++; /* skip the remaining spaces */ @@ -133,14 +138,20 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs) break; bcount=0; continue; - } else if (*cs==0x005c) { + } + else if (*cs==0x005c) + { /* '\', count them */ bcount++; - } else if ((*cs==0x0022) && ((bcount & 1)==0)) { + } + else if ((*cs==0x0022) && ((bcount & 1)==0)) + { /* unescaped '"' */ in_quotes=!in_quotes; bcount=0; - } else { + } + else + { /* a regular character */ bcount=0; } @@ -160,8 +171,10 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs) bcount=0; in_quotes=0; arg=d=s=cmdline; - while (*s) { - if ((*s==0x0009 || *s==0x0020) && !in_quotes) { + while (*s) + { + if ((*s==0x0009 || *s==0x0020) && !in_quotes) + { /* Close the argument and copy it */ *d=0; argv[argc++]=arg; @@ -174,21 +187,28 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs) /* Start with a new argument */ arg=d=s; bcount=0; - } else if (*s==0x005c) { + } + else if (*s==0x005c) + { /* '\\' */ *d++=*s++; bcount++; - } else if (*s==0x0022) { + } + else if (*s==0x0022) + { /* '"' */ - if ((bcount & 1)==0) { - /* Preceeded by an even number of '\', this is half that + if ((bcount & 1)==0) + { + /* Preceded by an even number of '\', this is half that * number of '\', plus a quote which we erase. */ d-=bcount/2; in_quotes=!in_quotes; s++; - } else { - /* Preceeded by an odd number of '\', this is half that + } + else + { + /* Preceded by an odd number of '\', this is half that * number of '\' followed by a '"' */ d=d-bcount/2-1; @@ -196,13 +216,16 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs) s++; } bcount=0; - } else { + } + else + { /* a regular character */ *d++=*s++; bcount=0; } } - if (*arg) { + if (*arg) + { *d='\0'; argv[argc++]=arg; } @@ -212,347 +235,426 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs) return argv; } +#define SHGFI_KNOWN_FLAGS \ + (SHGFI_SMALLICON | SHGFI_OPENICON | SHGFI_SHELLICONSIZE | SHGFI_PIDL | \ + SHGFI_USEFILEATTRIBUTES | SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX | \ + SHGFI_ICON | SHGFI_DISPLAYNAME | SHGFI_TYPENAME | SHGFI_ATTRIBUTES | \ + SHGFI_ICONLOCATION | SHGFI_EXETYPE | SHGFI_SYSICONINDEX | \ + SHGFI_LINKOVERLAY | SHGFI_SELECTED | SHGFI_ATTR_SPECIFIED) + /************************************************************************* - * SHGetFileInfoA [SHELL32.@] + * SHGetFileInfoW [SHELL32.@] * */ - DWORD WINAPI SHGetFileInfoW(LPCWSTR path,DWORD dwFileAttributes, - SHFILEINFOW *psfi, UINT sizeofpsfi, - UINT flags ) + SHFILEINFOW *psfi, UINT sizeofpsfi, UINT flags ) { - WCHAR szLocation[MAX_PATH], szFullPath[MAX_PATH]; - int iIndex; - DWORD ret = TRUE, dwAttributes = 0; - IShellFolder * psfParent = NULL; - IExtractIconW * pei = NULL; - LPITEMIDLIST pidlLast = NULL, pidl = NULL; - HRESULT hr = S_OK; - BOOL IconNotYetLoaded=TRUE; + WCHAR szLocation[MAX_PATH], szFullPath[MAX_PATH]; + int iIndex; + DWORD ret = TRUE, dwAttributes = 0; + IShellFolder * psfParent = NULL; + IExtractIconW * pei = NULL; + LPITEMIDLIST pidlLast = NULL, pidl = NULL; + HRESULT hr = S_OK; + BOOL IconNotYetLoaded=TRUE; - TRACE("(%s fattr=0x%lx sfi=%p(attr=0x%08lx) size=0x%x flags=0x%x)\n", - (flags & SHGFI_PIDL)? "pidl" : debugstr_w(path), dwFileAttributes, psfi, psfi->dwAttributes, sizeofpsfi, flags); + TRACE("%s fattr=0x%lx sfi=%p(attr=0x%08lx) size=0x%x flags=0x%x\n", + (flags & SHGFI_PIDL)? "pidl" : debugstr_w(path), dwFileAttributes, + psfi, psfi->dwAttributes, sizeofpsfi, flags); - if ((flags & SHGFI_USEFILEATTRIBUTES) && (flags & (SHGFI_ATTRIBUTES|SHGFI_EXETYPE|SHGFI_PIDL))) - return FALSE; + if ( (flags & SHGFI_USEFILEATTRIBUTES) && + (flags & (SHGFI_ATTRIBUTES|SHGFI_EXETYPE|SHGFI_PIDL))) + return FALSE; - /* windows initializes this values regardless of the flags */ - if (psfi != NULL) { - psfi->szDisplayName[0] = '\0'; - psfi->szTypeName[0] = '\0'; - psfi->iIcon = 0; - } + /* windows initializes this values regardless of the flags */ + if (psfi != NULL) + { + psfi->szDisplayName[0] = '\0'; + psfi->szTypeName[0] = '\0'; + psfi->iIcon = 0; + } - if (!(flags & SHGFI_PIDL)){ - /* SHGitFileInfo should work with absolute and relative paths */ - if (PathIsRelativeW(path)){ - GetCurrentDirectoryW(MAX_PATH, szLocation); - PathCombineW(szFullPath, szLocation, path); - } else { - lstrcpynW(szFullPath, path, MAX_PATH); - } - } - - if (flags & SHGFI_EXETYPE) { - BOOL status = FALSE; - HANDLE hfile; - DWORD BinaryType; - IMAGE_DOS_HEADER mz_header; - IMAGE_NT_HEADERS nt; - DWORD len; - char magic[4]; - - if (flags != SHGFI_EXETYPE) return 0; - - status = GetBinaryTypeW (szFullPath, &BinaryType); - if (!status) return 0; - if ((BinaryType == SCS_DOS_BINARY) - || (BinaryType == SCS_PIF_BINARY)) return 0x4d5a; - - hfile = CreateFileW( szFullPath, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, 0, 0 ); - if ( hfile == INVALID_HANDLE_VALUE ) return 0; - - /* The next section is adapted from MODULE_GetBinaryType, as we need - * to examine the image header to get OS and version information. We - * know from calling GetBinaryTypeA that the image is valid and either - * an NE or PE, so much error handling can be omitted. - * Seek to the start of the file and read the header information. - */ - - SetFilePointer( hfile, 0, NULL, SEEK_SET ); - ReadFile( hfile, &mz_header, sizeof(mz_header), &len, NULL ); - - SetFilePointer( hfile, mz_header.e_lfanew, NULL, SEEK_SET ); - ReadFile( hfile, magic, sizeof(magic), &len, NULL ); - if ( *(DWORD*)magic == IMAGE_NT_SIGNATURE ) - { - SetFilePointer( hfile, mz_header.e_lfanew, NULL, SEEK_SET ); - ReadFile( hfile, &nt, sizeof(nt), &len, NULL ); - CloseHandle( hfile ); - if (nt.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { - return IMAGE_NT_SIGNATURE - | (nt.OptionalHeader.MajorSubsystemVersion << 24) - | (nt.OptionalHeader.MinorSubsystemVersion << 16); - } - return IMAGE_NT_SIGNATURE; - } - else if ( *(WORD*)magic == IMAGE_OS2_SIGNATURE ) - { - IMAGE_OS2_HEADER ne; - SetFilePointer( hfile, mz_header.e_lfanew, NULL, SEEK_SET ); - ReadFile( hfile, &ne, sizeof(ne), &len, NULL ); - CloseHandle( hfile ); - if (ne.ne_exetyp == 2) return IMAGE_OS2_SIGNATURE - | (ne.ne_expver << 16); - return 0; - } - CloseHandle( hfile ); - return 0; - } - - /* psfi is NULL normally to query EXE type. If it is NULL, none of the - * below makes sense anyway. Windows allows this and just returns FALSE */ - if (psfi == NULL) return FALSE; - - /* translate the path into a pidl only when SHGFI_USEFILEATTRIBUTES - * is not specified. - The pidl functions fail on not existing file names */ - - if (flags & SHGFI_PIDL) { - pidl = ILClone((LPCITEMIDLIST)path); - } else if (!(flags & SHGFI_USEFILEATTRIBUTES)) { - hr = SHILCreateFromPathW(szFullPath, &pidl, &dwAttributes); - } - - if ((flags & SHGFI_PIDL) || !(flags & SHGFI_USEFILEATTRIBUTES)) + if (!(flags & SHGFI_PIDL)) + { + /* SHGitFileInfo should work with absolute and relative paths */ + if (PathIsRelativeW(path)) { - /* get the parent shellfolder */ - if (pidl) { - hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psfParent, (LPCITEMIDLIST*)&pidlLast); - ILFree(pidl); - } else { - ERR("pidl is null!\n"); - return FALSE; - } + GetCurrentDirectoryW(MAX_PATH, szLocation); + PathCombineW(szFullPath, szLocation, path); } - - /* get the attributes of the child */ - if (SUCCEEDED(hr) && (flags & SHGFI_ATTRIBUTES)) - { - if (!(flags & SHGFI_ATTR_SPECIFIED)) - { - psfi->dwAttributes = 0xffffffff; - } - IShellFolder_GetAttributesOf(psfParent, 1, (LPCITEMIDLIST*)&pidlLast, &(psfi->dwAttributes)); - } - - /* get the displayname */ - if (SUCCEEDED(hr) && (flags & SHGFI_DISPLAYNAME)) - { - if (flags & SHGFI_USEFILEATTRIBUTES) - { - lstrcpyW (psfi->szDisplayName, PathFindFileNameW(szFullPath)); - } - else - { - STRRET str; - hr = IShellFolder_GetDisplayNameOf(psfParent, pidlLast, SHGDN_INFOLDER, &str); - StrRetToStrNW (psfi->szDisplayName, MAX_PATH, &str, pidlLast); - } - } - - /* get the type name */ - if (SUCCEEDED(hr) && (flags & SHGFI_TYPENAME)) + else { - static const WCHAR szFile[] = { 'F','i','l','e',0 }; - static const WCHAR szDashFile[] = { '-','f','i','l','e',0 }; - if (!(flags & SHGFI_USEFILEATTRIBUTES)) + lstrcpynW(szFullPath, path, MAX_PATH); + } + } + + if (flags & SHGFI_EXETYPE) + { + BOOL status = FALSE; + HANDLE hfile; + DWORD BinaryType; + IMAGE_DOS_HEADER mz_header; + IMAGE_NT_HEADERS nt; + DWORD len; + char magic[4]; + + if (flags != SHGFI_EXETYPE) + return 0; + + status = GetBinaryTypeW (szFullPath, &BinaryType); + if (!status) + return 0; + if ((BinaryType == SCS_DOS_BINARY) || (BinaryType == SCS_PIF_BINARY)) + return 0x4d5a; + + hfile = CreateFileW( szFullPath, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, 0 ); + if ( hfile == INVALID_HANDLE_VALUE ) + return 0; + + /* + * The next section is adapted from MODULE_GetBinaryType, as we need + * to examine the image header to get OS and version information. We + * know from calling GetBinaryTypeA that the image is valid and either + * an NE or PE, so much error handling can be omitted. + * Seek to the start of the file and read the header information. + */ + + SetFilePointer( hfile, 0, NULL, SEEK_SET ); + ReadFile( hfile, &mz_header, sizeof(mz_header), &len, NULL ); + + SetFilePointer( hfile, mz_header.e_lfanew, NULL, SEEK_SET ); + ReadFile( hfile, magic, sizeof(magic), &len, NULL ); + if ( *(DWORD*)magic == IMAGE_NT_SIGNATURE ) + { + SetFilePointer( hfile, mz_header.e_lfanew, NULL, SEEK_SET ); + ReadFile( hfile, &nt, sizeof(nt), &len, NULL ); + CloseHandle( hfile ); + if (nt.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { - char ftype[80]; - _ILGetFileType(pidlLast, ftype, 80); - MultiByteToWideChar(CP_ACP, 0, ftype, -1, psfi->szTypeName, 80 ); + return IMAGE_NT_SIGNATURE | + (nt.OptionalHeader.MajorSubsystemVersion << 24) | + (nt.OptionalHeader.MinorSubsystemVersion << 16); } - else + return IMAGE_NT_SIGNATURE; + } + else if ( *(WORD*)magic == IMAGE_OS2_SIGNATURE ) + { + IMAGE_OS2_HEADER ne; + SetFilePointer( hfile, mz_header.e_lfanew, NULL, SEEK_SET ); + ReadFile( hfile, &ne, sizeof(ne), &len, NULL ); + CloseHandle( hfile ); + if (ne.ne_exetyp == 2) + return IMAGE_OS2_SIGNATURE | (ne.ne_expver << 16); + return 0; + } + CloseHandle( hfile ); + return 0; + } + + /* + * psfi is NULL normally to query EXE type. If it is NULL, none of the + * below makes sense anyway. Windows allows this and just returns FALSE + */ + if (psfi == NULL) + return FALSE; + + /* + * translate the path into a pidl only when SHGFI_USEFILEATTRIBUTES + * is not specified. + * The pidl functions fail on not existing file names + */ + + if (flags & SHGFI_PIDL) + { + pidl = ILClone((LPCITEMIDLIST)path); + } + else if (!(flags & SHGFI_USEFILEATTRIBUTES)) + { + hr = SHILCreateFromPathW(szFullPath, &pidl, &dwAttributes); + } + + if ((flags & SHGFI_PIDL) || !(flags & SHGFI_USEFILEATTRIBUTES)) + { + /* get the parent shellfolder */ + if (pidl) + { + hr = SHBindToParent( pidl, &IID_IShellFolder, (LPVOID*)&psfParent, + (LPCITEMIDLIST*)&pidlLast ); + ILFree(pidl); + } + else + { + ERR("pidl is null!\n"); + return FALSE; + } + } + + /* get the attributes of the child */ + if (SUCCEEDED(hr) && (flags & SHGFI_ATTRIBUTES)) + { + if (!(flags & SHGFI_ATTR_SPECIFIED)) + { + psfi->dwAttributes = 0xffffffff; + } + IShellFolder_GetAttributesOf( psfParent, 1, (LPCITEMIDLIST*)&pidlLast, + &(psfi->dwAttributes) ); + } + + /* get the displayname */ + if (SUCCEEDED(hr) && (flags & SHGFI_DISPLAYNAME)) + { + if (flags & SHGFI_USEFILEATTRIBUTES) + { + lstrcpyW (psfi->szDisplayName, PathFindFileNameW(szFullPath)); + } + else + { + STRRET str; + hr = IShellFolder_GetDisplayNameOf( psfParent, pidlLast, + SHGDN_INFOLDER, &str); + StrRetToStrNW (psfi->szDisplayName, MAX_PATH, &str, pidlLast); + } + } + + /* get the type name */ + if (SUCCEEDED(hr) && (flags & SHGFI_TYPENAME)) + { + static const WCHAR szFile[] = { 'F','i','l','e',0 }; + static const WCHAR szDashFile[] = { '-','f','i','l','e',0 }; + + if (!(flags & SHGFI_USEFILEATTRIBUTES)) + { + char ftype[80]; + + _ILGetFileType(pidlLast, ftype, 80); + MultiByteToWideChar(CP_ACP, 0, ftype, -1, psfi->szTypeName, 80 ); + } + else + { + if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + strcatW (psfi->szTypeName, szFile); + else { - if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - strcatW (psfi->szTypeName, szFile); - else + WCHAR sTemp[64]; + + lstrcpyW(sTemp,PathFindExtensionW(szFullPath)); + if (!( HCR_MapTypeToValueW(sTemp, sTemp, 64, TRUE) && + HCR_MapTypeToValueW(sTemp, psfi->szTypeName, 80, FALSE ))) { - WCHAR sTemp[64]; - lstrcpyW(sTemp,PathFindExtensionW(szFullPath)); - if (!( HCR_MapTypeToValueW(sTemp, sTemp, 64, TRUE) - && HCR_MapTypeToValueW(sTemp, psfi->szTypeName, 80, FALSE ))) - { - lstrcpynW (psfi->szTypeName, sTemp, 64); - strcatW (psfi->szTypeName, szDashFile); - } + lstrcpynW (psfi->szTypeName, sTemp, 64); + strcatW (psfi->szTypeName, szDashFile); } } } + } - /* ### icons ###*/ - if (flags & SHGFI_LINKOVERLAY) - FIXME("set icon to link, stub\n"); + /* ### icons ###*/ + if (flags & SHGFI_ADDOVERLAYS) + FIXME("SHGFI_ADDOVERLAYS unhandled\n"); - if (flags & SHGFI_SELECTED) - FIXME("set icon to selected, stub\n"); + if (flags & SHGFI_OVERLAYINDEX) + FIXME("SHGFI_OVERLAYINDEX unhandled\n"); - if (flags & SHGFI_SHELLICONSIZE) - FIXME("set icon to shell size, stub\n"); + if (flags & SHGFI_LINKOVERLAY) + FIXME("set icon to link, stub\n"); - /* get the iconlocation */ - if (SUCCEEDED(hr) && (flags & SHGFI_ICONLOCATION )) - { - UINT uDummy,uFlags; - hr = IShellFolder_GetUIObjectOf(psfParent, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, &uDummy, (LPVOID*)&pei); + if (flags & SHGFI_SELECTED) + FIXME("set icon to selected, stub\n"); - if (SUCCEEDED(hr)) - { - hr = IExtractIconW_GetIconLocation(pei, (flags & SHGFI_OPENICON)? GIL_OPENICON : 0,szLocation, MAX_PATH, &iIndex, &uFlags); - psfi->iIcon = iIndex; + if (flags & SHGFI_SHELLICONSIZE) + FIXME("set icon to shell size, stub\n"); - if(uFlags != GIL_NOTFILENAME) - lstrcpyW (psfi->szDisplayName, szLocation); - else - ret = FALSE; + /* get the iconlocation */ + if (SUCCEEDED(hr) && (flags & SHGFI_ICONLOCATION )) + { + UINT uDummy,uFlags; - IExtractIconA_Release(pei); - } - } + hr = IShellFolder_GetUIObjectOf(psfParent, 0, 1, + (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, + &uDummy, (LPVOID*)&pei); + if (SUCCEEDED(hr)) + { + hr = IExtractIconW_GetIconLocation(pei, + (flags & SHGFI_OPENICON)? GIL_OPENICON : 0, + szLocation, MAX_PATH, &iIndex, &uFlags); + psfi->iIcon = iIndex; - /* get icon index (or load icon)*/ - if (SUCCEEDED(hr) && (flags & (SHGFI_ICON | SHGFI_SYSICONINDEX))) - { + if (uFlags != GIL_NOTFILENAME) + lstrcpyW (psfi->szDisplayName, szLocation); + else + ret = FALSE; - if (flags & SHGFI_USEFILEATTRIBUTES) - { - WCHAR sTemp [MAX_PATH]; - WCHAR * szExt; - DWORD dwNr=0; + IExtractIconA_Release(pei); + } + } - lstrcpynW(sTemp, szFullPath, MAX_PATH); + /* get icon index (or load icon)*/ + if (SUCCEEDED(hr) && (flags & (SHGFI_ICON | SHGFI_SYSICONINDEX))) + { + if (flags & SHGFI_USEFILEATTRIBUTES) + { + WCHAR sTemp [MAX_PATH]; + WCHAR * szExt; + DWORD dwNr=0; + + lstrcpynW(sTemp, szFullPath, MAX_PATH); if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - psfi->iIcon = 2; + psfi->iIcon = 2; else { - static const WCHAR p1W[] = {'%','1',0}; - psfi->iIcon = 0; - szExt = (LPWSTR) PathFindExtensionW(sTemp); - if ( szExt && HCR_MapTypeToValueW(szExt, sTemp, MAX_PATH, TRUE) - && HCR_GetDefaultIconW(sTemp, sTemp, MAX_PATH, &dwNr)) - { - if (!lstrcmpW(p1W,sTemp)) /* icon is in the file */ - strcpyW(sTemp, szFullPath); + static const WCHAR p1W[] = {'%','1',0}; - if (flags & SHGFI_SYSICONINDEX) - { - psfi->iIcon = SIC_GetIconIndex(sTemp,dwNr); - if (psfi->iIcon == -1) psfi->iIcon = 0; - } - else - { - IconNotYetLoaded=FALSE; - PrivateExtractIconsW(sTemp,dwNr,(flags & SHGFI_SMALLICON) ? - GetSystemMetrics(SM_CXSMICON) : GetSystemMetrics(SM_CXICON), - (flags & SHGFI_SMALLICON) ? GetSystemMetrics(SM_CYSMICON) : - GetSystemMetrics(SM_CYICON), &psfi->hIcon,0,1,0); - psfi->iIcon = dwNr; - } - } + psfi->iIcon = 0; + szExt = (LPWSTR) PathFindExtensionW(sTemp); + if ( szExt && + HCR_MapTypeToValueW(szExt, sTemp, MAX_PATH, TRUE) && + HCR_GetDefaultIconW(sTemp, sTemp, MAX_PATH, &dwNr)) + { + if (!lstrcmpW(p1W,sTemp)) /* icon is in the file */ + strcpyW(sTemp, szFullPath); + + if (flags & SHGFI_SYSICONINDEX) + { + psfi->iIcon = SIC_GetIconIndex(sTemp,dwNr); + if (psfi->iIcon == -1) + psfi->iIcon = 0; + } + else + { + IconNotYetLoaded=FALSE; + if (flags & SHGFI_SMALLICON) + PrivateExtractIconsW( sTemp,dwNr, + GetSystemMetrics( SM_CXSMICON ), + GetSystemMetrics( SM_CYSMICON ), + &psfi->hIcon, 0, 1, 0); + else + PrivateExtractIconsW( sTemp, dwNr, + GetSystemMetrics( SM_CXICON), + GetSystemMetrics( SM_CYICON), + &psfi->hIcon, 0, 1, 0); + psfi->iIcon = dwNr; + } + } } - } - else - { - if (!(PidlToSicIndex(psfParent, pidlLast, !(flags & SHGFI_SMALLICON), - (flags & SHGFI_OPENICON)? GIL_OPENICON : 0, &(psfi->iIcon)))) - { - ret = FALSE; - } - } - if (ret) - { - ret = (DWORD) ((flags & SHGFI_SMALLICON) ? ShellSmallIconList : ShellBigIconList); - } - } + } + else + { + if (!(PidlToSicIndex(psfParent, pidlLast, !(flags & SHGFI_SMALLICON), + (flags & SHGFI_OPENICON)? GIL_OPENICON : 0, &(psfi->iIcon)))) + { + ret = FALSE; + } + } + if (ret) + { + if (flags & SHGFI_SMALLICON) + ret = (DWORD) ShellSmallIconList; + else + ret = (DWORD) ShellBigIconList; + } + } - /* icon handle */ - if (SUCCEEDED(hr) && (flags & SHGFI_ICON) && IconNotYetLoaded) - psfi->hIcon = ImageList_GetIcon((flags & SHGFI_SMALLICON) ? ShellSmallIconList:ShellBigIconList, psfi->iIcon, ILD_NORMAL); + /* icon handle */ + if (SUCCEEDED(hr) && (flags & SHGFI_ICON) && IconNotYetLoaded) + { + if (flags & SHGFI_SMALLICON) + psfi->hIcon = ImageList_GetIcon( ShellSmallIconList, psfi->iIcon, ILD_NORMAL); + else + psfi->hIcon = ImageList_GetIcon( ShellBigIconList, psfi->iIcon, ILD_NORMAL); + } - if (flags & (SHGFI_UNKNOWN1 | SHGFI_UNKNOWN2 | SHGFI_UNKNOWN3)) - FIXME("unknown attribute!\n"); + if (flags & ~SHGFI_KNOWN_FLAGS) + FIXME("unknown flags %08x\n", flags & ~SHGFI_KNOWN_FLAGS); - if (psfParent) - IShellFolder_Release(psfParent); + if (psfParent) + IShellFolder_Release(psfParent); - if (hr != S_OK) - ret = FALSE; + if (hr != S_OK) + ret = FALSE; + + if (pidlLast) + SHFree(pidlLast); - if(pidlLast) SHFree(pidlLast); #ifdef MORE_DEBUG - TRACE ("icon=%p index=0x%08x attr=0x%08lx name=%s type=%s ret=0x%08lx\n", - psfi->hIcon, psfi->iIcon, psfi->dwAttributes, debugstr_w(psfi->szDisplayName), debugstr_w(psfi->szTypeName), ret); + TRACE ("icon=%p index=0x%08x attr=0x%08lx name=%s type=%s ret=0x%08lx\n", + psfi->hIcon, psfi->iIcon, psfi->dwAttributes, + debugstr_w(psfi->szDisplayName), debugstr_w(psfi->szTypeName), ret); #endif - return ret; + + return ret; } /************************************************************************* - * SHGetFileInfoW [SHELL32.@] + * SHGetFileInfoA [SHELL32.@] */ - DWORD WINAPI SHGetFileInfoA(LPCSTR path,DWORD dwFileAttributes, SHFILEINFOA *psfi, UINT sizeofpsfi, UINT flags ) { - INT len; - LPWSTR temppath; - DWORD ret; - SHFILEINFOW temppsfi; + INT len; + LPWSTR temppath; + DWORD ret; + SHFILEINFOW temppsfi; - if (flags & SHGFI_PIDL) { - /* path contains a pidl */ - temppath = (LPWSTR) path; - } else { - len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0); - temppath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); - MultiByteToWideChar(CP_ACP, 0, path, -1, temppath, len); - } + if (flags & SHGFI_PIDL) + { + /* path contains a pidl */ + temppath = (LPWSTR) path; + } + else + { + len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0); + temppath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, path, -1, temppath, len); + } - if(psfi && (flags & SHGFI_ATTR_SPECIFIED)) - temppsfi.dwAttributes=psfi->dwAttributes; + if (psfi && (flags & SHGFI_ATTR_SPECIFIED)) + temppsfi.dwAttributes=psfi->dwAttributes; - ret = SHGetFileInfoW(temppath, dwFileAttributes, (psfi == NULL)? NULL : &temppsfi, sizeof(temppsfi), flags); + if (psfi == NULL) + ret = SHGetFileInfoW(temppath, dwFileAttributes, NULL, sizeof(temppsfi), flags); + else + ret = SHGetFileInfoW(temppath, dwFileAttributes, &temppsfi, sizeof(temppsfi), flags); - if (psfi) + if (psfi) + { + if(flags & SHGFI_ICON) + psfi->hIcon=temppsfi.hIcon; + if(flags & (SHGFI_SYSICONINDEX|SHGFI_ICON|SHGFI_ICONLOCATION)) + psfi->iIcon=temppsfi.iIcon; + if(flags & SHGFI_ATTRIBUTES) + psfi->dwAttributes=temppsfi.dwAttributes; + if(flags & (SHGFI_DISPLAYNAME|SHGFI_ICONLOCATION)) { - if(flags & SHGFI_ICON) - psfi->hIcon=temppsfi.hIcon; - if(flags & (SHGFI_SYSICONINDEX|SHGFI_ICON|SHGFI_ICONLOCATION)) - psfi->iIcon=temppsfi.iIcon; - if(flags & SHGFI_ATTRIBUTES) - psfi->dwAttributes=temppsfi.dwAttributes; - if(flags & (SHGFI_DISPLAYNAME|SHGFI_ICONLOCATION)) - WideCharToMultiByte(CP_ACP, 0, temppsfi.szDisplayName, -1, psfi->szDisplayName, sizeof(psfi->szDisplayName), NULL, NULL); - if(flags & SHGFI_TYPENAME) - WideCharToMultiByte(CP_ACP, 0, temppsfi.szTypeName, -1, psfi->szTypeName, sizeof(psfi->szTypeName), NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, temppsfi.szDisplayName, -1, + psfi->szDisplayName, sizeof(psfi->szDisplayName), NULL, NULL); } - if(!(flags & SHGFI_PIDL)) HeapFree(GetProcessHeap(), 0, temppath); - return ret; + if(flags & SHGFI_TYPENAME) + { + WideCharToMultiByte(CP_ACP, 0, temppsfi.szTypeName, -1, + psfi->szTypeName, sizeof(psfi->szTypeName), NULL, NULL); + } + } + + if (!(flags & SHGFI_PIDL)) + HeapFree(GetProcessHeap(), 0, temppath); + + return ret; } /************************************************************************* - * DuplicateIcon [SHELL32.@] + * DuplicateIcon [SHELL32.@] */ HICON WINAPI DuplicateIcon( HINSTANCE hInstance, HICON hIcon) { ICONINFO IconInfo; HICON hDupIcon = 0; - TRACE("(%p, %p)\n", hInstance, hIcon); + TRACE("%p %p\n", hInstance, hIcon); - if(GetIconInfo(hIcon, &IconInfo)) + if (GetIconInfo(hIcon, &IconInfo)) { hDupIcon = CreateIconIndirect(&IconInfo); @@ -565,47 +667,50 @@ HICON WINAPI DuplicateIcon( HINSTANCE hInstance, HICON hIcon) } /************************************************************************* - * ExtractIconA [SHELL32.@] + * ExtractIconA [SHELL32.@] */ HICON WINAPI ExtractIconA(HINSTANCE hInstance, LPCSTR lpszFile, UINT nIconIndex) { - HICON ret; - INT len = MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, NULL, 0); - LPWSTR lpwstrFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + HICON ret; + INT len = MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, NULL, 0); + LPWSTR lpwstrFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); - TRACE("%p %s %d\n", hInstance, lpszFile, nIconIndex); + TRACE("%p %s %d\n", hInstance, lpszFile, nIconIndex); - MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, lpwstrFile, len); - ret = ExtractIconW(hInstance, lpwstrFile, nIconIndex); - HeapFree(GetProcessHeap(), 0, lpwstrFile); - return ret; + MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, lpwstrFile, len); + ret = ExtractIconW(hInstance, lpwstrFile, nIconIndex); + HeapFree(GetProcessHeap(), 0, lpwstrFile); + + return ret; } /************************************************************************* - * ExtractIconW [SHELL32.@] + * ExtractIconW [SHELL32.@] */ HICON WINAPI ExtractIconW(HINSTANCE hInstance, LPCWSTR lpszFile, UINT nIconIndex) { - HICON hIcon = NULL; - UINT ret; - UINT cx = GetSystemMetrics(SM_CXICON), cy = GetSystemMetrics(SM_CYICON); + HICON hIcon = NULL; + UINT ret; + UINT cx = GetSystemMetrics(SM_CXICON), cy = GetSystemMetrics(SM_CYICON); - TRACE("%p %s %d\n", hInstance, debugstr_w(lpszFile), nIconIndex); + TRACE("%p %s %d\n", hInstance, debugstr_w(lpszFile), nIconIndex); - if (nIconIndex == 0xFFFFFFFF) { - ret = PrivateExtractIconsW(lpszFile, 0, cx, cy, NULL, NULL, 0, LR_DEFAULTCOLOR); - if (ret != 0xFFFFFFFF && ret) - return (HICON)ret; - return NULL; - } - else - ret = PrivateExtractIconsW(lpszFile, nIconIndex, cx, cy, &hIcon, NULL, 1, LR_DEFAULTCOLOR); + if (nIconIndex == 0xFFFFFFFF) + { + ret = PrivateExtractIconsW(lpszFile, 0, cx, cy, NULL, NULL, 0, LR_DEFAULTCOLOR); + if (ret != 0xFFFFFFFF && ret) + return (HICON)ret; + return NULL; + } + else + ret = PrivateExtractIconsW(lpszFile, nIconIndex, cx, cy, &hIcon, NULL, 1, LR_DEFAULTCOLOR); - if (ret == 0xFFFFFFFF) - return (HICON)1; - else if (ret > 0 && hIcon) - return hIcon; - return NULL; + if (ret == 0xFFFFFFFF) + return (HICON)1; + else if (ret > 0 && hIcon) + return hIcon; + + return NULL; } typedef struct @@ -616,90 +721,103 @@ typedef struct HFONT hFont; } ABOUT_INFO; -#define IDC_STATIC_TEXT1 100 -#define IDC_STATIC_TEXT2 101 -#define IDC_LISTBOX 99 -#define IDC_WINE_TEXT 98 +#define IDC_STATIC_TEXT1 100 +#define IDC_STATIC_TEXT2 101 +#define IDC_LISTBOX 99 +#define IDC_WINE_TEXT 98 -#define DROP_FIELD_TOP (-15) -#define DROP_FIELD_HEIGHT 15 +#define DROP_FIELD_TOP (-15) +#define DROP_FIELD_HEIGHT 15 static BOOL __get_dropline( HWND hWnd, LPRECT lprect ) -{ HWND hWndCtl = GetDlgItem(hWnd, IDC_WINE_TEXT); +{ + HWND hWndCtl = GetDlgItem(hWnd, IDC_WINE_TEXT); + if( hWndCtl ) - { GetWindowRect( hWndCtl, lprect ); - MapWindowPoints( 0, hWnd, (LPPOINT)lprect, 2 ); - lprect->bottom = (lprect->top += DROP_FIELD_TOP); - return TRUE; + { + GetWindowRect( hWndCtl, lprect ); + MapWindowPoints( 0, hWnd, (LPPOINT)lprect, 2 ); + lprect->bottom = (lprect->top += DROP_FIELD_TOP); + return TRUE; } return FALSE; } /************************************************************************* - * SHAppBarMessage [SHELL32.@] + * SHAppBarMessage [SHELL32.@] */ UINT WINAPI SHAppBarMessage(DWORD msg, PAPPBARDATA data) { - int width=data->rc.right - data->rc.left; - int height=data->rc.bottom - data->rc.top; - RECT rec=data->rc; - switch (msg) - { case ABM_GETSTATE: - return ABS_ALWAYSONTOP | ABS_AUTOHIDE; - case ABM_GETTASKBARPOS: - GetWindowRect(data->hWnd, &rec); - data->rc=rec; - return TRUE; - case ABM_ACTIVATE: - SetActiveWindow(data->hWnd); - return TRUE; - case ABM_GETAUTOHIDEBAR: - data->hWnd=GetActiveWindow(); - return TRUE; - case ABM_NEW: - SetWindowPos(data->hWnd,HWND_TOP,rec.left,rec.top, - width,height,SWP_SHOWWINDOW); - return TRUE; - case ABM_QUERYPOS: - GetWindowRect(data->hWnd, &(data->rc)); - return TRUE; - case ABM_REMOVE: - FIXME("ABM_REMOVE broken\n"); - /* FIXME: this is wrong; should it be DestroyWindow instead? */ - /*CloseHandle(data->hWnd);*/ - return TRUE; - case ABM_SETAUTOHIDEBAR: - SetWindowPos(data->hWnd,HWND_TOP,rec.left+1000,rec.top, - width,height,SWP_SHOWWINDOW); - return TRUE; - case ABM_SETPOS: - data->uEdge=(ABE_RIGHT | ABE_LEFT); - SetWindowPos(data->hWnd,HWND_TOP,data->rc.left,data->rc.top, - width,height,SWP_SHOWWINDOW); - return TRUE; - case ABM_WINDOWPOSCHANGED: - return TRUE; - } - return FALSE; + int width=data->rc.right - data->rc.left; + int height=data->rc.bottom - data->rc.top; + RECT rec=data->rc; + + switch (msg) + { + case ABM_GETSTATE: + return ABS_ALWAYSONTOP | ABS_AUTOHIDE; + case ABM_GETTASKBARPOS: + GetWindowRect(data->hWnd, &rec); + data->rc=rec; + return TRUE; + case ABM_ACTIVATE: + SetActiveWindow(data->hWnd); + return TRUE; + case ABM_GETAUTOHIDEBAR: + data->hWnd=GetActiveWindow(); + return TRUE; + case ABM_NEW: + SetWindowPos(data->hWnd,HWND_TOP,rec.left,rec.top, + width,height,SWP_SHOWWINDOW); + return TRUE; + case ABM_QUERYPOS: + GetWindowRect(data->hWnd, &(data->rc)); + return TRUE; + case ABM_REMOVE: + FIXME("ABM_REMOVE broken\n"); + /* FIXME: this is wrong; should it be DestroyWindow instead? */ + /*CloseHandle(data->hWnd);*/ + return TRUE; + case ABM_SETAUTOHIDEBAR: + SetWindowPos(data->hWnd,HWND_TOP,rec.left+1000,rec.top, + width,height,SWP_SHOWWINDOW); + return TRUE; + case ABM_SETPOS: + data->uEdge=(ABE_RIGHT | ABE_LEFT); + SetWindowPos(data->hWnd,HWND_TOP,data->rc.left,data->rc.top, + width,height,SWP_SHOWWINDOW); + return TRUE; + case ABM_WINDOWPOSCHANGED: + return TRUE; + } + return FALSE; } /************************************************************************* - * SHHelpShortcuts_RunDLL [SHELL32.@] + * SHHelpShortcuts_RunDLLA [SHELL32.@] * */ -DWORD WINAPI SHHelpShortcuts_RunDLL (DWORD dwArg1, DWORD dwArg2, DWORD dwArg3, DWORD dwArg4) -{ FIXME("(%lx, %lx, %lx, %lx) empty stub!\n", - dwArg1, dwArg2, dwArg3, dwArg4); - - return 0; +DWORD WINAPI SHHelpShortcuts_RunDLLA(DWORD dwArg1, DWORD dwArg2, DWORD dwArg3, DWORD dwArg4) +{ + FIXME("(%lx, %lx, %lx, %lx) stub!\n", dwArg1, dwArg2, dwArg3, dwArg4); + return 0; } /************************************************************************* - * SHLoadInProc [SHELL32.@] + * SHHelpShortcuts_RunDLLA [SHELL32.@] + * + */ +DWORD WINAPI SHHelpShortcuts_RunDLLW(DWORD dwArg1, DWORD dwArg2, DWORD dwArg3, DWORD dwArg4) +{ + FIXME("(%lx, %lx, %lx, %lx) stub!\n", dwArg1, dwArg2, dwArg3, dwArg4); + return 0; +} + +/************************************************************************* + * SHLoadInProc [SHELL32.@] * Create an instance of specified object class from within * the shell process and release it immediately */ - HRESULT WINAPI SHLoadInProc (REFCLSID rclsid) { void *ptr = NULL; @@ -717,7 +835,7 @@ HRESULT WINAPI SHLoadInProc (REFCLSID rclsid) } /************************************************************************* - * AboutDlgProc (internal) + * AboutDlgProc (internal) */ INT_PTR CALLBACK AboutDlgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) @@ -759,18 +877,20 @@ INT_PTR CALLBACK AboutDlgProc( HWND hWnd, UINT msg, WPARAM wParam, return 1; case WM_PAINT: - { RECT rect; - PAINTSTRUCT ps; - HDC hDC = BeginPaint( hWnd, &ps ); + { + RECT rect; + PAINTSTRUCT ps; + HDC hDC = BeginPaint( hWnd, &ps ); - if( __get_dropline( hWnd, &rect ) ) { - SelectObject( hDC, GetStockObject( BLACK_PEN ) ); - MoveToEx( hDC, rect.left, rect.top, NULL ); - LineTo( hDC, rect.right, rect.bottom ); - } - EndPaint( hWnd, &ps ); - } - break; + if (__get_dropline( hWnd, &rect )) + { + SelectObject( hDC, GetStockObject( BLACK_PEN ) ); + MoveToEx( hDC, rect.left, rect.top, NULL ); + LineTo( hDC, rect.right, rect.bottom ); + } + EndPaint( hWnd, &ps ); + } + break; case WM_COMMAND: if (wParam == IDOK || wParam == IDCANCEL) @@ -789,7 +909,7 @@ INT_PTR CALLBACK AboutDlgProc( HWND hWnd, UINT msg, WPARAM wParam, /************************************************************************* - * ShellAboutA [SHELL32.288] + * ShellAboutA [SHELL32.288] */ BOOL WINAPI ShellAboutA( HWND hWnd, LPCSTR szApp, LPCSTR szOtherStuff, HICON hIcon ) { @@ -819,7 +939,7 @@ BOOL WINAPI ShellAboutA( HWND hWnd, LPCSTR szApp, LPCSTR szOtherStuff, HICON hIc /************************************************************************* - * ShellAboutW [SHELL32.289] + * ShellAboutW [SHELL32.289] */ BOOL WINAPI ShellAboutW( HWND hWnd, LPCWSTR szApp, LPCWSTR szOtherStuff, HICON hIcon ) @@ -855,7 +975,8 @@ BOOL WINAPI ShellAboutW( HWND hWnd, LPCWSTR szApp, LPCWSTR szOtherStuff, * FreeIconList (SHELL32.@) */ void WINAPI FreeIconList( DWORD dw ) -{ FIXME("(%lx): stub\n",dw); +{ + FIXME("%lx: stub\n",dw); } @@ -887,7 +1008,7 @@ HRESULT WINAPI SHELL32_DllGetVersion (DLLVERSIONINFO *pdvi) { /* FIXME: shouldn't these values come from the version resource? */ if (pdvi->cbSize == sizeof(DLLVERSIONINFO) || - pdvi->cbSize == sizeof(DLLVERSIONINFO2)) + pdvi->cbSize == sizeof(DLLVERSIONINFO2)) { pdvi->dwMajorVersion = WINE_FILEVERSION_MAJOR; pdvi->dwMinorVersion = WINE_FILEVERSION_MINOR; @@ -904,24 +1025,25 @@ HRESULT WINAPI SHELL32_DllGetVersion (DLLVERSIONINFO *pdvi) WINE_FILEVERSION_PLATFORMID); } TRACE("%lu.%lu.%lu.%lu\n", - pdvi->dwMajorVersion, pdvi->dwMinorVersion, - pdvi->dwBuildNumber, pdvi->dwPlatformID); + pdvi->dwMajorVersion, pdvi->dwMinorVersion, + pdvi->dwBuildNumber, pdvi->dwPlatformID); return S_OK; } else - { + { WARN("wrong DLLVERSIONINFO size from app\n"); return E_INVALIDARG; } } + /************************************************************************* * global variables of the shell32.dll * all are once per process * */ -HINSTANCE shell32_hInstance = 0; -HIMAGELIST ShellSmallIconList = 0; -HIMAGELIST ShellBigIconList = 0; +HINSTANCE shell32_hInstance = 0; +HIMAGELIST ShellSmallIconList = 0; +HIMAGELIST ShellBigIconList = 0; /************************************************************************* @@ -930,34 +1052,33 @@ HIMAGELIST ShellBigIconList = 0; * NOTES * calling oleinitialize here breaks sone apps. */ - BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) { - TRACE("%p 0x%lx %p\n", hinstDLL, fdwReason, fImpLoad); + TRACE("%p 0x%lx %p\n", hinstDLL, fdwReason, fImpLoad); - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - shell32_hInstance = hinstDLL; - DisableThreadLibraryCalls(shell32_hInstance); + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + shell32_hInstance = hinstDLL; + DisableThreadLibraryCalls(shell32_hInstance); - /* get full path to this DLL for IExtractIconW_fnGetIconLocation() */ - GetModuleFileNameW(hinstDLL, swShell32Name, MAX_PATH); - swShell32Name[MAX_PATH - 1] = '\0'; + /* get full path to this DLL for IExtractIconW_fnGetIconLocation() */ + GetModuleFileNameW(hinstDLL, swShell32Name, MAX_PATH); + swShell32Name[MAX_PATH - 1] = '\0'; - InitCommonControlsEx(NULL); + InitCommonControlsEx(NULL); - SIC_Initialize(); - InitChangeNotifications(); - break; + SIC_Initialize(); + InitChangeNotifications(); + break; - case DLL_PROCESS_DETACH: - shell32_hInstance = 0; - SIC_Destroy(); - FreeChangeNotifications(); - break; - } - return TRUE; + case DLL_PROCESS_DETACH: + shell32_hInstance = 0; + SIC_Destroy(); + FreeChangeNotifications(); + break; + } + return TRUE; } /************************************************************************* @@ -971,9 +1092,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) HRESULT WINAPI SHELL32_DllInstall(BOOL bInstall, LPCWSTR cmdline) { - FIXME("(%s, %s): stub!\n", bInstall ? "TRUE":"FALSE", debugstr_w(cmdline)); - - return S_OK; /* indicate success */ + FIXME("%s %s: stub\n", bInstall ? "TRUE":"FALSE", debugstr_w(cmdline)); + return S_OK; /* indicate success */ } /*********************************************************************** @@ -981,7 +1101,6 @@ HRESULT WINAPI SHELL32_DllInstall(BOOL bInstall, LPCWSTR cmdline) */ HRESULT WINAPI SHELL32_DllCanUnloadNow(void) { - FIXME("(void): stub\n"); - + FIXME("stub\n"); return S_FALSE; } diff --git a/reactos/lib/shell32/shell32_main.h b/reactos/lib/shell32/shell32_main.h index f2e013e613d..97ba2dc003c 100644 --- a/reactos/lib/shell32/shell32_main.h +++ b/reactos/lib/shell32/shell32_main.h @@ -81,7 +81,7 @@ LPENUMFORMATETC IEnumFORMATETC_Constructor(UINT, const FORMATETC []); LPCLASSFACTORY IClassFactory_Constructor(REFCLSID); IContextMenu2 * ISvItemCm_Constructor(LPSHELLFOLDER pSFParent, LPCITEMIDLIST pidl, LPCITEMIDLIST *aPidls, UINT uItemCount); -IContextMenu2 * ISvBgCm_Constructor(LPSHELLFOLDER pSFParent); +IContextMenu2 * ISvBgCm_Constructor(LPSHELLFOLDER pSFParent, BOOL bDesktop); LPSHELLVIEW IShellView_Constructor(LPSHELLFOLDER); HRESULT WINAPI IFSFolder_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv); @@ -92,6 +92,7 @@ HRESULT WINAPI ISF_MyComputer_Constructor(IUnknown * pUnkOuter, REFIID riid, LPV HRESULT WINAPI IDropTargetHelper_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv); HRESULT WINAPI IFileSystemBindData_Constructor(const WIN32_FIND_DATAW *pfd, LPBC *ppV); HRESULT WINAPI IControlPanel_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv); +HRESULT WINAPI UnixFolder_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv); HRESULT WINAPI CPanel_GetIconLocationA(LPITEMIDLIST pidl, LPSTR szIconFile, UINT cchMax, int* piIndex); HRESULT WINAPI CPanel_GetIconLocationW(LPITEMIDLIST pidl, LPWSTR szIconFile, UINT cchMax, int* piIndex); HRESULT WINAPI CPanel_ExtractIconA(LPITEMIDLIST pidl, LPCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize); @@ -220,6 +221,8 @@ UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOperation, extern WCHAR swShell32Name[MAX_PATH]; +extern const GUID CLSID_UnixFolder; + /* Default shell folder value registration */ HRESULT SHELL_RegisterShellFolders(void); diff --git a/reactos/lib/shell32/shelllink.c b/reactos/lib/shell32/shelllink.c index b7b379f81c5..70f26907f40 100644 --- a/reactos/lib/shell32/shelllink.c +++ b/reactos/lib/shell32/shelllink.c @@ -1,7 +1,8 @@ /* * - * Copyright 1997 Marcus Meissner - * Copyright 1998 Juergen Schmied + * Copyright 1997 Marcus Meissner + * Copyright 1998 Juergen Schmied + * Copyright 2005 Mike McCormack * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,6 +22,14 @@ * Nearly complete informations about the binary formats * of .lnk files available at http://www.wotsit.org * + * You can use winedump to examine the contents of a link file: + * winedump lnk sc.lnk + * + * MSI advertised shortcuts are totally undocumented. They provide an + * icon for a program that is not yet installed, and invoke MSI to + * install the program when the shortcut is clicked on. They are + * created by passing a special string to SetPath, and the information + * in that string is parsed an stored. */ #include "config.h" @@ -58,19 +67,28 @@ #include "shlguid.h" #include "shlwapi.h" +#include "initguid.h" + WINE_DEFAULT_DEBUG_CHANNEL(shell); +DEFINE_GUID( SHELL32_AdvtShortcutProduct, + 0x9db1186f,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63); +DEFINE_GUID( SHELL32_AdvtShortcutComponent, + 0x9db1186e,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63); + /* link file formats */ /* flag1: lnk elements: simple link has 0x0B */ -#define SCF_PIDL 1 -#define SCF_NORMAL 2 +#define SCF_PIDL 1 +#define SCF_LOCATION 2 #define SCF_DESCRIPTION 4 #define SCF_RELATIVE 8 #define SCF_WORKDIR 0x10 #define SCF_ARGS 0x20 #define SCF_CUSTOMICON 0x40 #define SCF_UNICODE 0x80 +#define SCF_PRODUCT 0x800 +#define SCF_COMPONENT 0x1000 #include "pshpack1.h" @@ -113,6 +131,21 @@ typedef struct _LOCAL_VOLUME_INFO DWORD dwVolLabelOfs; } LOCAL_VOLUME_INFO; +typedef struct tagLINK_ADVERTISEINFO +{ + DWORD size; + DWORD magic; + CHAR bufA[MAX_PATH]; + WCHAR bufW[MAX_PATH]; +} LINK_ADVERTISEINFO; + +typedef struct volume_info_t +{ + DWORD type; + DWORD serial; + WCHAR label[12]; /* assume 8.3 */ +} volume_info; + #include "poppack.h" static IShellLinkAVtbl slvt; @@ -146,6 +179,9 @@ typedef struct LPWSTR sWorkDir; LPWSTR sDescription; LPWSTR sPathRel; + LPWSTR sProduct; + LPWSTR sComponent; + volume_info volume; BOOL bDirty; } IShellLinkImpl; @@ -172,7 +208,6 @@ inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str) return p; } - /************************************************************************** * IPersistFile_QueryInterface */ @@ -251,8 +286,9 @@ static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFile static BOOL StartLinkProcessor( LPCOLESTR szLink ) { - static const WCHAR szFormat[] = {'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e', - ' ','-','r',' ','"','%','s','"',0 }; + static const WCHAR szFormat[] = { + 'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e', + ' ','-','r',' ','"','%','s','"',0 }; LONG len; LPWSTR buffer; STARTUPINFOW si; @@ -290,7 +326,7 @@ static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFile TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName)); - if (!pszFileName || !This->sPath) + if (!pszFileName) return E_FAIL; r = CreateStreamOnFile(pszFileName, STGM_READWRITE | STGM_CREATE, &stm); @@ -469,44 +505,175 @@ static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr ) return S_OK; } -static HRESULT Stream_LoadLocation( IStream* stm ) +static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data ) { DWORD size; ULONG count; HRESULT r; - LOCATION_INFO *loc; + struct sized_chunk { + DWORD size; + unsigned char data[1]; + } *chunk; TRACE("%p\n",stm); r = IStream_Read( stm, &size, sizeof(size), &count ); - if( FAILED( r ) ) - return r; - if( count != sizeof(loc->dwTotalSize) ) + if( FAILED( r ) || count != sizeof(size) ) return E_FAIL; - loc = HeapAlloc( GetProcessHeap(), 0, size ); - if( ! loc ) + chunk = HeapAlloc( GetProcessHeap(), 0, size ); + if( !chunk ) return E_OUTOFMEMORY; - r = IStream_Read( stm, &loc->dwHeaderSize, size-sizeof(size), &count ); - if( FAILED( r ) ) - goto end; - if( count != (size - sizeof(size)) ) + chunk->size = size; + r = IStream_Read( stm, chunk->data, size - sizeof(size), &count ); + if( FAILED( r ) || count != (size - sizeof(size)) ) { - r = E_FAIL; - goto end; + HeapFree( GetProcessHeap(), 0, chunk ); + return E_FAIL; } - loc->dwTotalSize = size; - TRACE("Read %ld bytes\n",count); + TRACE("Read %ld bytes\n",chunk->size); - /* FIXME: do something useful with it */ - HeapFree( GetProcessHeap(), 0, loc ); + *data = (LPVOID) chunk; + + return S_OK; +} + +static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume ) +{ + const int label_sz = sizeof volume->label/sizeof volume->label[0]; + LPSTR label; + int len; + + volume->serial = vol->dwVolSerial; + volume->type = vol->dwType; + + if( !vol->dwVolLabelOfs ) + return FALSE; + if( vol->dwSize <= vol->dwVolLabelOfs ) + return FALSE; + len = vol->dwSize - vol->dwVolLabelOfs; + + label = (LPSTR) vol; + label += vol->dwVolLabelOfs; + MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1); + + return TRUE; +} + +static LPWSTR Stream_LoadPath( LPSTR p, DWORD maxlen ) +{ + int len = 0, wlen; + LPWSTR path; + + while( p[len] && (len < maxlen) ) + len++; + + wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0); + path = HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen); + path[wlen] = 0; + + return path; +} + +static HRESULT Stream_LoadLocation( IStream *stm, + volume_info *volume, LPWSTR *path ) +{ + unsigned char *p = NULL; + LOCATION_INFO *loc; + HRESULT r; + int n; + + r = Stream_ReadChunk( stm, (LPVOID*) &p ); + if( FAILED(r) ) + return r; + + loc = (LOCATION_INFO*) p; + if (loc->dwTotalSize < sizeof(LOCATION_INFO) - sizeof(DWORD)) + { + HeapFree( GetProcessHeap(), 0, p ); + return E_FAIL; + } + + /* if there's valid local volume information, load it */ + if( loc->dwVolTableOfs && + ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) ) + { + LOCAL_VOLUME_INFO *volume_info; + + volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs]; + Stream_LoadVolume( volume_info, volume ); + } + + /* if there's a local path, load it */ + n = loc->dwLocalPathOfs; + if( n && (n < loc->dwTotalSize) ) + *path = Stream_LoadPath( &p[n], loc->dwTotalSize - n ); + + TRACE("type %ld serial %08lx name %s path %s\n", volume->type, + volume->serial, debugstr_w(volume->label), debugstr_w(*path)); + + HeapFree( GetProcessHeap(), 0, p ); + return S_OK; +} + +/* + * The format of the advertised shortcut info seems to be: + * + * Offset Description + * ------ ----------- + * + * 0 Length of the block (4 bytes, usually 0x314) + * 4 tag (dword) + * 8 string data in ASCII + * 8+0x104 string data in UNICODE + * + * In the original Win32 implementation the buffers are not initialized + * to zero, so data trailing the string is random garbage. + */ +static HRESULT Stream_LoadAdvertiseInfo( IStream* stm, LPWSTR *str ) +{ + DWORD size; + ULONG count; + HRESULT r; + LINK_ADVERTISEINFO buffer; + + TRACE("%p\n",stm); + + r = IStream_Read( stm, &buffer.size, sizeof (DWORD), &count ); + if( FAILED( r ) ) + return r; + + /* make sure that we read the size of the structure even on error */ + size = sizeof buffer - sizeof (DWORD); + if( buffer.size != sizeof buffer ) + { + ERR("Ooops. This structure is not as expected...\n"); + return E_FAIL; + } + + r = IStream_Read( stm, &buffer.magic, size, &count ); + if( FAILED( r ) ) + return r; + + if( count != size ) + return E_FAIL; + + TRACE("magic %08lx string = %s\n", buffer.magic, debugstr_w(buffer.bufW)); + + if( (buffer.magic&0xffff0000) != 0xa0000000 ) + { + ERR("Unknown magic number %08lx in advertised shortcut\n", buffer.magic); + return E_FAIL; + } + + *str = HeapAlloc( GetProcessHeap(), 0, + (strlenW(buffer.bufW)+1) * sizeof(WCHAR) ); + strcpyW( *str, buffer.bufW ); return S_OK; -end: - HeapFree( GetProcessHeap(), 0, loc ); - return r; } /************************************************************************ @@ -519,12 +686,12 @@ static HRESULT WINAPI IPersistStream_fnLoad( LINK_HEADER hdr; ULONG dwBytesRead; BOOL unicode; - WCHAR sTemp[MAX_PATH]; HRESULT r; + DWORD zero; _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface); - TRACE("(%p)(%p)\n", This, stm); + TRACE("%p %p\n", This, stm); if( !stm ) return STG_E_INVALIDPOINTER; @@ -541,30 +708,61 @@ static HRESULT WINAPI IPersistStream_fnLoad( if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) ) return E_FAIL; - /* if( hdr.dwFlags & SCF_PIDL ) */ /* FIXME: seems to always have a PIDL */ - { - r = ILLoadFromStream( stm, &This->pPidl ); - if( FAILED( r ) ) - return r; - } + /* free all the old stuff */ + ILFree(This->pPidl); + This->pPidl = NULL; + memset( &This->volume, 0, sizeof This->volume ); + HeapFree(GetProcessHeap(), 0, This->sPath); + This->sPath = NULL; + HeapFree(GetProcessHeap(), 0, This->sDescription); + This->sDescription = NULL; + HeapFree(GetProcessHeap(), 0, This->sPathRel); + This->sPathRel = NULL; + HeapFree(GetProcessHeap(), 0, This->sWorkDir); + This->sWorkDir = NULL; + HeapFree(GetProcessHeap(), 0, This->sArgs); + This->sArgs = NULL; + HeapFree(GetProcessHeap(), 0, This->sIcoPath); + This->sIcoPath = NULL; + HeapFree(GetProcessHeap(), 0, This->sProduct); + This->sProduct = NULL; + HeapFree(GetProcessHeap(), 0, This->sComponent); + This->sComponent = NULL; + This->wHotKey = (WORD)hdr.wHotKey; This->iIcoNdx = hdr.nIcon; FileTimeToSystemTime (&hdr.Time1, &This->time1); FileTimeToSystemTime (&hdr.Time2, &This->time2); FileTimeToSystemTime (&hdr.Time3, &This->time3); -#if 1 - GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256); - TRACE("-- time1: %s\n", debugstr_w(sTemp) ); - GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256); - TRACE("-- time1: %s\n", debugstr_w(sTemp) ); - GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256); - TRACE("-- time1: %s\n", debugstr_w(sTemp) ); - pdump (This->pPidl); -#endif - if( hdr.dwFlags & SCF_NORMAL ) - r = Stream_LoadLocation( stm ); + if (TRACE_ON(shell)) + { + WCHAR sTemp[MAX_PATH]; + GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1, + NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp)); + TRACE("-- time1: %s\n", debugstr_w(sTemp) ); + GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time2, + NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp)); + TRACE("-- time2: %s\n", debugstr_w(sTemp) ); + GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time3, + NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp)); + TRACE("-- time3: %s\n", debugstr_w(sTemp) ); + } + + /* load all the new stuff */ + if( hdr.dwFlags & SCF_PIDL ) + { + r = ILLoadFromStream( stm, &This->pPidl ); + if( FAILED( r ) ) + return r; + } + pdump(This->pPidl); + + /* load the location information */ + if( hdr.dwFlags & SCF_LOCATION ) + r = Stream_LoadLocation( stm, &This->volume, &This->sPath ); if( FAILED( r ) ) goto end; + unicode = hdr.dwFlags & SCF_UNICODE; if( hdr.dwFlags & SCF_DESCRIPTION ) { @@ -583,7 +781,7 @@ static HRESULT WINAPI IPersistStream_fnLoad( goto end; if( hdr.dwFlags & SCF_WORKDIR ) - { + { r = Stream_LoadString( stm, unicode, &This->sWorkDir ); TRACE("Working Dir -> %s\n",debugstr_w(This->sWorkDir)); } @@ -606,6 +804,26 @@ static HRESULT WINAPI IPersistStream_fnLoad( if( FAILED( r ) ) goto end; + if( hdr.dwFlags & SCF_PRODUCT ) + { + r = Stream_LoadAdvertiseInfo( stm, &This->sProduct ); + TRACE("Product -> %s\n",debugstr_w(This->sProduct)); + } + if( FAILED( r ) ) + goto end; + + if( hdr.dwFlags & SCF_COMPONENT ) + { + r = Stream_LoadAdvertiseInfo( stm, &This->sComponent ); + TRACE("Component -> %s\n",debugstr_w(This->sComponent)); + } + if( FAILED( r ) ) + goto end; + + r = IStream_Read(stm, &zero, sizeof zero, &dwBytesRead); + if( FAILED( r ) || zero || dwBytesRead != sizeof zero ) + ERR("Last word was not zero\n"); + TRACE("OK\n"); pdump (This->pPidl); @@ -640,19 +858,81 @@ static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str ) return S_OK; } -static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR filename ) +/************************************************************************ + * Stream_WriteLocationInfo + * + * Writes the location info to a stream + * + * FIXME: One day we might want to write the network volume information + * and the final path. + * Figure out how Windows deals with unicode paths here. + */ +static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path, + volume_info *volume ) +{ + DWORD total_size, path_size, volume_info_size, label_size, final_path_size; + LOCAL_VOLUME_INFO *vol; + LOCATION_INFO *loc; + LPSTR szLabel, szPath, szFinalPath; + ULONG count = 0; + + TRACE("%p %s %p\n", stm, debugstr_w(path), volume); + + /* figure out the size of everything */ + label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1, + NULL, 0, NULL, NULL ); + path_size = WideCharToMultiByte( CP_ACP, 0, path, -1, + NULL, 0, NULL, NULL ); + volume_info_size = sizeof *vol + label_size; + final_path_size = 1; + total_size = sizeof *loc + volume_info_size + path_size + final_path_size; + + /* create pointers to everything */ + loc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size); + vol = (LOCAL_VOLUME_INFO*) &loc[1]; + szLabel = (LPSTR) &vol[1]; + szPath = &szLabel[label_size]; + szFinalPath = &szPath[path_size]; + + /* fill in the location information header */ + loc->dwTotalSize = total_size; + loc->dwHeaderSize = sizeof (*loc); + loc->dwFlags = 1; + loc->dwVolTableOfs = sizeof (*loc); + loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size; + loc->dwNetworkVolTableOfs = 0; + loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size; + + /* fill in the volume information */ + vol->dwSize = volume_info_size; + vol->dwType = volume->type; + vol->dwVolSerial = volume->serial; + vol->dwVolLabelOfs = sizeof (*vol); + + /* copy in the strings */ + WideCharToMultiByte( CP_ACP, 0, volume->label, -1, + szLabel, label_size, NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, path, -1, + szPath, path_size, NULL, NULL ); + szFinalPath[0] = 0; + + return IStream_Write( stm, loc, total_size, &count ); +} + +static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic ) { - LOCATION_INFO loc; ULONG count; + LINK_ADVERTISEINFO buffer; + + TRACE("%p\n",stm); - FIXME("writing empty location info\n"); + memset( &buffer, 0, sizeof buffer ); + buffer.size = sizeof buffer; + buffer.magic = magic; + strncpyW( buffer.bufW, string, MAX_PATH ); + WideCharToMultiByte(CP_ACP, 0, string, -1, buffer.bufA, MAX_PATH, NULL, NULL ); - memset( &loc, 0, sizeof(loc) ); - loc.dwTotalSize = sizeof(loc) - sizeof(loc.dwTotalSize); - - /* FIXME: fill this in */ - - return IStream_Write( stm, &loc, loc.dwTotalSize, &count ); + return IStream_Write( stm, &buffer, buffer.size, &count ); } /************************************************************************ @@ -670,38 +950,39 @@ static HRESULT WINAPI IPersistStream_fnSave( LINK_HEADER header; WCHAR exePath[MAX_PATH]; ULONG count; + DWORD zero; HRESULT r; _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface); - TRACE("(%p) %p %x\n", This, stm, fClearDirty); + TRACE("%p %p %x\n", This, stm, fClearDirty); *exePath = '\0'; if (This->sPath) { - SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH, NULL, NULL, NULL, NULL); + SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH, + NULL, NULL, NULL, NULL); /* * windows can create lnk files to executables that do not exist yet * so if the executable does not exist the just trust the path they * gave us */ - if( !*exePath ) strcpyW(exePath,This->sPath); + if (!*exePath) strcpyW(exePath,This->sPath); } - /* if there's no PIDL, generate one */ - if( ! This->pPidl ) This->pPidl = ILCreateFromPathW(exePath); - memset(&header, 0, sizeof(header)); header.dwSize = sizeof(header); + header.fStartup = This->iShowCmd; memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) ); header.wHotKey = This->wHotKey; header.nIcon = This->iIcoNdx; header.dwFlags = SCF_UNICODE; /* strings are in unicode */ - header.dwFlags |= SCF_NORMAL; /* how do we determine this ? */ if( This->pPidl ) header.dwFlags |= SCF_PIDL; + if( This->sPath ) + header.dwFlags |= SCF_LOCATION; if( This->sDescription ) header.dwFlags |= SCF_DESCRIPTION; if( This->sWorkDir ) @@ -710,6 +991,10 @@ static HRESULT WINAPI IPersistStream_fnSave( header.dwFlags |= SCF_ARGS; if( This->sIcoPath ) header.dwFlags |= SCF_CUSTOMICON; + if( This->sProduct ) + header.dwFlags |= SCF_PRODUCT; + if( This->sComponent ) + header.dwFlags |= SCF_COMPONENT; SystemTimeToFileTime ( &This->time1, &header.Time1 ); SystemTimeToFileTime ( &This->time2, &header.Time2 ); @@ -736,9 +1021,9 @@ static HRESULT WINAPI IPersistStream_fnSave( } } - Stream_WriteLocationInfo( stm, exePath ); + if( This->sPath ) + Stream_WriteLocationInfo( stm, exePath, &This->volume ); - TRACE("Description = %s\n", debugstr_w(This->sDescription)); if( This->sDescription ) r = Stream_WriteString( stm, This->sDescription ); @@ -754,6 +1039,16 @@ static HRESULT WINAPI IPersistStream_fnSave( if( This->sIcoPath ) r = Stream_WriteString( stm, This->sIcoPath ); + if( This->sProduct ) + r = Stream_WriteAdvertiseInfo( stm, This->sProduct, 0xa0000007 ); + + if( This->sComponent ) + r = Stream_WriteAdvertiseInfo( stm, This->sComponent, 0xa0000006 ); + + /* the last field is a single zero dword */ + zero = 0; + r = IStream_Write( stm, &zero, sizeof zero, &count ); + return S_OK; } @@ -828,13 +1123,9 @@ HRESULT WINAPI IShellLink_Constructor ( static BOOL SHELL_ExistsFileW(LPCWSTR path) { - HANDLE hfile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - - if (hfile != INVALID_HANDLE_VALUE) { - CloseHandle(hfile); - return TRUE; - } else + if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path)) return FALSE; + return TRUE; } /************************************************************************** @@ -1017,7 +1308,10 @@ static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile, TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n", This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath)); - if( cchMaxPath ) + if (This->sComponent || This->sProduct) + return S_FALSE; + + if (cchMaxPath) pszFile[0] = 0; if (This->sPath) WideCharToMultiByte( CP_ACP, 0, This->sPath, -1, @@ -1025,18 +1319,16 @@ static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile, if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This); - return NOERROR; + return S_OK; } static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl) { - IShellLinkImpl *This = (IShellLinkImpl *)iface; + IShellLinkImpl *This = (IShellLinkImpl *)iface; - TRACE("(%p)->(ppidl=%p)\n",This, ppidl); + TRACE("(%p)->(ppidl=%p)\n",This, ppidl); - *ppidl = ILClone(This->pPidl); - - return NOERROR; + return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl); } static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl) @@ -1218,21 +1510,23 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon); - if (cchIconPath) - pszIconPath[0] = 0; + pszIconPath[0] = 0; + *piIcon = This->iIcoNdx; - if (This->sIcoPath) { + if (This->sIcoPath) + { WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL); - *piIcon = This->iIcoNdx; return S_OK; } - if (This->pPidl || This->sPath) { + if (This->pPidl || This->sPath) + { IShellFolder* pdsk; HRESULT hr = SHGetDesktopFolder(&pdsk); - if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) + { /* first look for an icon using the PIDL (if present) */ if (This->pPidl) hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon); @@ -1240,7 +1534,8 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p hr = E_FAIL; /* if we couldn't find an icon yet, look for it using the file system path */ - if (FAILED(hr) && This->sPath) { + if (FAILED(hr) && This->sPath) + { LPITEMIDLIST pidl; hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL); @@ -1256,8 +1551,8 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p } return hr; - } else - return E_FAIL; + } + return S_OK; } static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon) @@ -1281,7 +1576,7 @@ static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR { IShellLinkImpl *This = (IShellLinkImpl *)iface; - FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved); + TRACE("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved); HeapFree(GetProcessHeap(), 0, This->sPathRel); This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel); @@ -1296,7 +1591,7 @@ static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWOR IShellLinkImpl *This = (IShellLinkImpl *)iface; - FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags); + TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags); /*FIXME: use IResolveShellLink interface */ @@ -1333,23 +1628,20 @@ static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWOR static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile) { + HRESULT r; + LPWSTR str; IShellLinkImpl *This = (IShellLinkImpl *)iface; - char buffer[MAX_PATH]; - LPSTR fname; TRACE("(%p)->(path=%s)\n",This, pszFile); - if (!GetFullPathNameA(pszFile, MAX_PATH, buffer, &fname)) - return E_FAIL; - - HeapFree(GetProcessHeap(), 0, This->sPath); - This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, buffer); - if( !This->sPath ) + str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile); + if( !str ) return E_OUTOFMEMORY; - This->bDirty = TRUE; + r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str); + HeapFree( GetProcessHeap(), 0, str ); - return S_OK; + return r; } /************************************************************************** @@ -1421,17 +1713,20 @@ static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile, { _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface); - TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n", - This, pszFile, cchMaxPath, pfd, fFlags); + TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n", + This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath)); - if( cchMaxPath ) + if (This->sComponent || This->sProduct) + return S_FALSE; + + if (cchMaxPath) pszFile[0] = 0; - if( This->sPath ) + if (This->sPath) lstrcpynW( pszFile, This->sPath, cchMaxPath ); if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This); - return NOERROR; + return S_OK; } static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl) @@ -1440,11 +1735,9 @@ static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST TRACE("(%p)->(ppidl=%p)\n",This, ppidl); - if( This->pPidl) - *ppidl = ILClone( This->pPidl ); - else - *ppidl = NULL; - + if (!This->pPidl) + return S_FALSE; + *ppidl = ILClone(This->pPidl); return S_OK; } @@ -1471,8 +1764,7 @@ static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR p TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName); - if( cchMaxName ) - pszName[0] = 0; + pszName[0] = 0; if( This->sDescription ) lstrcpynW( pszName, This->sDescription, cchMaxName ); @@ -1632,21 +1924,23 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon); - if (cchIconPath) - pszIconPath[0] = 0; + pszIconPath[0] = 0; + *piIcon = This->iIcoNdx; - if (This->sIcoPath) { + if (This->sIcoPath) + { lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath); - *piIcon = This->iIcoNdx; return S_OK; } - if (This->pPidl || This->sPath) { + if (This->pPidl || This->sPath) + { IShellFolder* pdsk; HRESULT hr = SHGetDesktopFolder(&pdsk); - if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) + { /* first look for an icon using the PIDL (if present) */ if (This->pPidl) hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon); @@ -1654,12 +1948,14 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR hr = E_FAIL; /* if we couldn't find an icon yet, look for it using the file system path */ - if (FAILED(hr) && This->sPath) { + if (FAILED(hr) && This->sPath) + { LPITEMIDLIST pidl; hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL); - if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) + { hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon); SHFree(pidl); @@ -1668,10 +1964,9 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR IShellFolder_Release(pdsk); } - return hr; - } else - return E_FAIL; + } + return S_OK; } static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon) @@ -1716,7 +2011,7 @@ static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWOR _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface); - FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags); + TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags); /*FIXME: use IResolveShellLink interface */ @@ -1751,27 +2046,141 @@ static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWOR return hr; } +static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str) +{ + LPWSTR ret; + LPCWSTR p; + DWORD len; + + p = strchrW( str, ':' ); + if( !p ) + return NULL; + len = p - str; + ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1)); + if( !ret ) + return ret; + memcpy( ret, str, sizeof(WCHAR)*len ); + ret[len] = 0; + return ret; +} + +static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str) +{ + LPCWSTR szComponent = NULL, szProduct = NULL, p; + WCHAR szGuid[39]; + HRESULT r; + GUID guid; + int len; + + while( str[0] ) + { + /* each segment must start with two colons */ + if( str[0] != ':' || str[1] != ':' ) + return E_FAIL; + + /* the last segment is just two colons */ + if( !str[2] ) + break; + str += 2; + + /* there must be a colon straight after a guid */ + p = strchrW( str, ':' ); + if( !p ) + return E_FAIL; + len = p - str; + if( len != 38 ) + return E_FAIL; + + /* get the guid, and check it's validly formatted */ + memcpy( szGuid, str, sizeof(WCHAR)*len ); + szGuid[len] = 0; + r = CLSIDFromString( szGuid, &guid ); + if( r != S_OK ) + return r; + str = p + 1; + + /* match it up to a guid that we care about */ + if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent ) + szComponent = str; + else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct ) + szProduct = str; + else + return E_FAIL; + + /* skip to the next field */ + str = strchrW( str, ':' ); + if( !str ) + return E_FAIL; + } + + /* we have to have at least one of these two for an advertised shortcut */ + if( !szComponent && !szProduct ) + return E_FAIL; + + This->sComponent = ShellLink_GetAdvertisedArg( szComponent ); + This->sProduct = ShellLink_GetAdvertisedArg( szProduct ); + + TRACE("Component = %s\n", debugstr_w(This->sComponent)); + TRACE("Product = %s\n", debugstr_w(This->sProduct)); + + return S_OK; +} + +static BOOL ShellLink_GetVolumeInfo(LPWSTR path, volume_info *volume) +{ + const int label_sz = sizeof volume->label/sizeof volume->label[0]; + WCHAR drive[4] = { path[0], ':', '\\', 0 }; + BOOL r; + + volume->type = GetDriveTypeW(drive); + r = GetVolumeInformationW(drive, volume->label, label_sz, + &volume->serial, NULL, NULL, NULL, 0); + TRACE("r = %d type %ld serial %08lx name %s\n", r, + volume->type, volume->serial, debugstr_w(volume->label)); + return r; +} + static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile) { _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface); WCHAR buffer[MAX_PATH]; LPWSTR fname; + HRESULT hr = S_OK; TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile)); - if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname)) - return E_FAIL; - HeapFree(GetProcessHeap(), 0, This->sPath); - This->sPath = HeapAlloc( GetProcessHeap(), 0, - (lstrlenW( buffer )+1) * sizeof (WCHAR) ); - if (!This->sPath) - return E_OUTOFMEMORY; + This->sPath = NULL; - lstrcpyW(This->sPath, buffer); + HeapFree(GetProcessHeap(), 0, This->sComponent); + This->sComponent = NULL; + + if (This->pPidl) + ILFree(This->pPidl); + This->pPidl = NULL; + + if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile )) + { + if (*pszFile == '\0') + *buffer = '\0'; + else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname)) + return E_FAIL; + else if(!PathFileExistsW(buffer)) + hr = S_FALSE; + + This->pPidl = SHSimpleIDListFromPathW(pszFile); + ShellLink_GetVolumeInfo(buffer, &This->volume); + + This->sPath = HeapAlloc( GetProcessHeap(), 0, + (lstrlenW( buffer )+1) * sizeof (WCHAR) ); + if (!This->sPath) + return E_OUTOFMEMORY; + + lstrcpyW(This->sPath, buffer); + } This->bDirty = TRUE; - return S_OK; + return hr; } /************************************************************************** diff --git a/reactos/lib/shell32/shellpath.c b/reactos/lib/shell32/shellpath.c index 718bfc216e6..3201c64fb11 100644 --- a/reactos/lib/shell32/shellpath.c +++ b/reactos/lib/shell32/shellpath.c @@ -365,7 +365,7 @@ static BOOL PathIsExeA (LPCSTR lpszPath) TRACE("path=%s\n",lpszPath); for(i=0; lpszExtensions[i]; i++) - if (!strcasecmp(lpszExtension,lpszExtensions[i])) return TRUE; + if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE; return FALSE; } @@ -1267,7 +1267,7 @@ static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath) } else { - FIXME("LoadString failed, missing translation?\n"); + FIXME("(%d,%s), LoadString failed, missing translation?\n", folder, debugstr_w(pszPath)); hr = E_FAIL; } } @@ -1522,7 +1522,7 @@ static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName, */ static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest) { - HRESULT hr; + HRESULT hr = S_OK; WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 }; TRACE("%s, %p\n", debugstr_w(szSrc), szDest); diff --git a/reactos/lib/shell32/shfldr.h b/reactos/lib/shell32/shfldr.h index 5666f0d7cc3..bc35caf02e6 100644 --- a/reactos/lib/shell32/shfldr.h +++ b/reactos/lib/shell32/shfldr.h @@ -46,6 +46,7 @@ HRESULT SHELL32_BindToChild (LPCITEMIDLIST pidlRoot, LPCSTR pathRoot, LPCITEMIDLIST pidlComplete, REFIID riid, LPVOID * ppvOut); HRESULT SHELL32_CompareIDs (IShellFolder * iface, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); +LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path); static inline int SHELL32_GUIDToStringA (REFGUID guid, LPSTR str) { @@ -55,4 +56,17 @@ static inline int SHELL32_GUIDToStringA (REFGUID guid, LPSTR str) guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); } +static inline int SHELL32_GUIDToStringW (REFGUID guid, LPWSTR str) +{ + static const WCHAR fmtW[] = + { '{','%','0','8','l','x','-','%','0','4','x','-','%','0','4','x','-', + '%','0','2','x','%','0','2','x','-', + '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x', + '%','0','2','x','%','0','2','x','}',0 }; + return sprintfW(str, fmtW, + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); +} + void SHELL_FS_ProcessDisplayFilename(LPSTR szPath, DWORD dwFlags); diff --git a/reactos/lib/shell32/shfldr_desktop.c b/reactos/lib/shell32/shfldr_desktop.c index e14dd087735..5e8b13d696d 100644 --- a/reactos/lib/shell32/shfldr_desktop.c +++ b/reactos/lib/shell32/shfldr_desktop.c @@ -1,9 +1,9 @@ /* - * Virtual Desktop Folder + * Virtual Desktop Folder * - * Copyright 1997 Marcus Meissner - * Copyright 1998, 1999, 2002 Juergen Schmied + * Copyright 1997 Marcus Meissner + * Copyright 1998, 1999, 2002 Juergen Schmied * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -56,7 +56,7 @@ WINE_DEFAULT_DEBUG_CHANNEL (shell); /*********************************************************************** -* Desktopfolder implementation +* Desktopfolder implementation */ typedef struct { @@ -66,17 +66,17 @@ typedef struct { CLSID *pclsid; /* both paths are parsible from the desktop */ - LPSTR sPathTarget; /* complete path to target used for enumeration and ChangeNotify */ - LPITEMIDLIST pidlRoot; /* absolute pidl */ + LPSTR sPathTarget; /* complete path to target used for enumeration and ChangeNotify */ + LPITEMIDLIST pidlRoot; /* absolute pidl */ - int dwAttributes; /* attributes returned by GetAttributesOf FIXME: use it */ + int dwAttributes; /* attributes returned by GetAttributesOf FIXME: use it */ - UINT cfShellIDList; /* clipboardformat for IDropTarget */ - BOOL fAcceptFmt; /* flag for pending Drop */ + UINT cfShellIDList; /* clipboardformat for IDropTarget */ + BOOL fAcceptFmt; /* flag for pending Drop */ } IGenericSFImpl; -#define _IUnknown_(This) (IShellFolder*)&(This->lpVtbl) -#define _IShellFolder_(This) (IShellFolder*)&(This->lpVtbl) +#define _IUnknown_(This) (IShellFolder*)&(This->lpVtbl) +#define _IShellFolder_(This) (IShellFolder*)&(This->lpVtbl) static struct IShellFolder2Vtbl vt_MCFldr_ShellFolder2; @@ -91,9 +91,10 @@ static shvheader DesktopSFHeader[] = { #define DESKTOPSHELLVIEWCOLUMNS 5 /************************************************************************** -* ISF_Desktop_Constructor -*/ -HRESULT WINAPI ISF_Desktop_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv) + * ISF_Desktop_Constructor + */ +HRESULT WINAPI ISF_Desktop_Constructor ( + IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv) { IGenericSFImpl *sf; char szMyPath[MAX_PATH]; @@ -101,26 +102,27 @@ HRESULT WINAPI ISF_Desktop_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOI TRACE ("unkOut=%p %s\n", pUnkOuter, shdebugstr_guid (riid)); if (!ppv) - return E_POINTER; + return E_POINTER; if (pUnkOuter) - return CLASS_E_NOAGGREGATION; + return CLASS_E_NOAGGREGATION; if (!SHGetSpecialFolderPathA (0, szMyPath, CSIDL_DESKTOPDIRECTORY, TRUE)) - return E_UNEXPECTED; + return E_UNEXPECTED; sf = (IGenericSFImpl *) LocalAlloc (GMEM_ZEROINIT, sizeof (IGenericSFImpl)); if (!sf) - return E_OUTOFMEMORY; + return E_OUTOFMEMORY; sf->ref = 0; sf->lpVtbl = &vt_MCFldr_ShellFolder2; - sf->pidlRoot = _ILCreateDesktop (); /* my qualified pidl */ + sf->pidlRoot = _ILCreateDesktop (); /* my qualified pidl */ sf->sPathTarget = SHAlloc (strlen (szMyPath) + 1); lstrcpyA (sf->sPathTarget, szMyPath); - if (!SUCCEEDED (IUnknown_QueryInterface (_IUnknown_ (sf), riid, ppv))) { - IUnknown_Release (_IUnknown_ (sf)); - return E_NOINTERFACE; + if (!SUCCEEDED (IUnknown_QueryInterface (_IUnknown_ (sf), riid, ppv))) + { + IUnknown_Release (_IUnknown_ (sf)); + return E_NOINTERFACE; } TRACE ("--(%p)\n", sf); @@ -128,11 +130,12 @@ HRESULT WINAPI ISF_Desktop_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOI } /************************************************************************** - * ISF_Desktop_fnQueryInterface + * ISF_Desktop_fnQueryInterface * * NOTES supports not IPersist/IPersistFolder */ -static HRESULT WINAPI ISF_Desktop_fnQueryInterface (IShellFolder2 * iface, REFIID riid, LPVOID * ppvObj) +static HRESULT WINAPI ISF_Desktop_fnQueryInterface( + IShellFolder2 * iface, REFIID riid, LPVOID * ppvObj) { IGenericSFImpl *This = (IGenericSFImpl *)iface; @@ -140,15 +143,18 @@ static HRESULT WINAPI ISF_Desktop_fnQueryInterface (IShellFolder2 * iface, REFII *ppvObj = NULL; - if (IsEqualIID (riid, &IID_IUnknown) || IsEqualIID (riid, &IID_IShellFolder) - || IsEqualIID (riid, &IID_IShellFolder2)) { - *ppvObj = This; + if (IsEqualIID (riid, &IID_IUnknown) || + IsEqualIID (riid, &IID_IShellFolder) || + IsEqualIID (riid, &IID_IShellFolder2)) + { + *ppvObj = This; } - if (*ppvObj) { - IUnknown_AddRef ((IUnknown *) (*ppvObj)); - TRACE ("-- Interface: (%p)->(%p)\n", ppvObj, *ppvObj); - return S_OK; + if (*ppvObj) + { + IUnknown_AddRef ((IUnknown *) (*ppvObj)); + TRACE ("-- Interface: (%p)->(%p)\n", ppvObj, *ppvObj); + return S_OK; } TRACE ("-- Interface: E_NOINTERFACE\n"); return E_NOINTERFACE; @@ -171,89 +177,116 @@ static ULONG WINAPI ISF_Desktop_fnRelease (IShellFolder2 * iface) TRACE ("(%p)->(count=%lu)\n", This, refCount + 1); - if (!refCount) { - TRACE ("-- destroying IShellFolder(%p)\n", This); - if (This->pidlRoot) - SHFree (This->pidlRoot); - if (This->sPathTarget) - SHFree (This->sPathTarget); - LocalFree ((HLOCAL) This); + if (!refCount) + { + TRACE ("-- destroying IShellFolder(%p)\n", This); + if (This->pidlRoot) + SHFree (This->pidlRoot); + if (This->sPathTarget) + SHFree (This->sPathTarget); + LocalFree ((HLOCAL) This); return 0; } return refCount; } /************************************************************************** -* ISF_Desktop_fnParseDisplayName -* -* NOTES -* "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" and "" binds -* to MyComputer -*/ + * ISF_Desktop_fnParseDisplayName + * + * NOTES + * "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" and "" binds + * to MyComputer + */ static HRESULT WINAPI ISF_Desktop_fnParseDisplayName (IShellFolder2 * iface, - HWND hwndOwner, - LPBC pbc, - LPOLESTR lpszDisplayName, - DWORD * pchEaten, LPITEMIDLIST * ppidl, DWORD * pdwAttributes) + HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, + DWORD * pchEaten, LPITEMIDLIST * ppidl, DWORD * pdwAttributes) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - WCHAR szElement[MAX_PATH]; LPCWSTR szNext = NULL; LPITEMIDLIST pidlTemp = NULL; HRESULT hr = S_OK; - char szPath[MAX_PATH]; - DWORD len; CLSID clsid; TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", - This, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName), pchEaten, ppidl, pdwAttributes); + This, hwndOwner, pbc, lpszDisplayName, debugstr_w(lpszDisplayName), + pchEaten, ppidl, pdwAttributes); if (!lpszDisplayName || !ppidl) - return E_INVALIDARG; + return E_INVALIDARG; *ppidl = 0; if (pchEaten) - *pchEaten = 0; /* strange but like the original */ + *pchEaten = 0; /* strange but like the original */ - if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':') { - szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); - TRACE ("-- element: %s\n", debugstr_w (szElement)); - SHCLSIDFromStringW (szElement + 2, &clsid); - pidlTemp = _ILCreateGuid (PT_GUID, &clsid); - } else if (PathGetDriveNumberW (lpszDisplayName) >= 0) { - /* it's a filesystem path with a drive. Let MyComputer parse it */ - pidlTemp = _ILCreateMyComputer (); - szNext = lpszDisplayName; - } else if (PathIsUNCW(lpszDisplayName)) { - pidlTemp = _ILCreateNetwork(); - szNext = lpszDisplayName; - } else { - /* it's a filesystem path on the desktop. Let a FSFolder parse it */ + if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':') + { + szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); + TRACE ("-- element: %s\n", debugstr_w (szElement)); + SHCLSIDFromStringW (szElement + 2, &clsid); + pidlTemp = _ILCreateGuid (PT_GUID, &clsid); + } + else if (PathGetDriveNumberW (lpszDisplayName) >= 0) + { + /* it's a filesystem path with a drive. Let MyComputer parse it */ + pidlTemp = _ILCreateMyComputer (); + szNext = lpszDisplayName; + } + else if (PathIsUNCW(lpszDisplayName)) + { + pidlTemp = _ILCreateNetwork(); + szNext = lpszDisplayName; + } + else if( (pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, lpszDisplayName)) ) + { + *ppidl = pidlTemp; + return S_OK; + } + else + { + /* it's a filesystem path on the desktop. Let a FSFolder parse it */ - if (*lpszDisplayName) { - /* build a complete path to create a simple pidl */ - lstrcpyA(szPath, This->sPathTarget); - PathAddBackslashA(szPath); - len = lstrlenA(szPath); - WideCharToMultiByte(CP_ACP, 0, lpszDisplayName, -1, szPath + len, MAX_PATH - len, NULL, NULL); - hr = _ILCreateFromPathA(szPath, &pidlTemp); - } else { - pidlTemp = _ILCreateMyComputer(); - } + if (*lpszDisplayName) + { + WCHAR szPath[MAX_PATH]; + LPWSTR pathPtr; - szNext = NULL; + /* build a complete path to create a simple pidl */ + MultiByteToWideChar(CP_ACP, 0, This->sPathTarget, -1, szPath, + sizeof(szPath) / sizeof(szPath[0])); + pathPtr = PathAddBackslashW(szPath); + if (pathPtr) + { + lstrcpynW(pathPtr, lpszDisplayName, + sizeof(szPath)/sizeof(szPath[0]) - (pathPtr - szPath)); + hr = _ILCreateFromPathW(szPath, &pidlTemp); + } + else + { + /* should never reach here, but for completeness */ + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + } + else + pidlTemp = _ILCreateMyComputer(); + + szNext = NULL; } - if (SUCCEEDED(hr) && pidlTemp) { - if (szNext && *szNext) { - hr = SHELL32_ParseNextElement (iface, hwndOwner, pbc, &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes); - } else { - if (pdwAttributes && *pdwAttributes) { - hr = SHELL32_GetItemAttributes (_IShellFolder_ (This), pidlTemp, pdwAttributes); - } - } + if (SUCCEEDED(hr) && pidlTemp) + { + if (szNext && *szNext) + { + hr = SHELL32_ParseNextElement(iface, hwndOwner, pbc, + &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes); + } + else + { + if (pdwAttributes && *pdwAttributes) + hr = SHELL32_GetItemAttributes(_IShellFolder_ (This), + pidlTemp, pdwAttributes); + } } *ppidl = pidlTemp; @@ -266,41 +299,48 @@ static HRESULT WINAPI ISF_Desktop_fnParseDisplayName (IShellFolder2 * iface, /************************************************************************** * CreateDesktopEnumList() */ +static const WCHAR Desktop_NameSpaceW[] = { 'S','O','F','T','W','A','R','E', + '\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l', + 'o','r','e','r','\\','D','e','s','k','t','o','p','\\','N','a','m','e','s','p', + 'a','c','e','\0' }; + static BOOL CreateDesktopEnumList(IEnumIDList *list, DWORD dwFlags) { BOOL ret = TRUE; - char szPath[MAX_PATH]; + WCHAR szPath[MAX_PATH]; TRACE("(%p)->(flags=0x%08lx) \n",list,dwFlags); - /*enumerate the root folders */ - if(dwFlags & SHCONTF_FOLDERS) + /* enumerate the root folders */ + if (dwFlags & SHCONTF_FOLDERS) { HKEY hkey; + LONG r; - /*create the pidl for This item */ + /* create the pidl for This item */ ret = AddToEnumList(list, _ILCreateMyComputer()); - if (ret && !RegOpenKeyExA(HKEY_LOCAL_MACHINE, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\desktop\\NameSpace", - 0, KEY_READ, &hkey)) + r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, Desktop_NameSpaceW, + 0, KEY_READ, &hkey); + if (ret && ERROR_SUCCESS == r) { - char iid[50]; + WCHAR iid[50]; int i=0; BOOL moreKeys = TRUE; while (ret && moreKeys) { - DWORD size = sizeof (iid); - LONG apiRet = RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, - NULL); + DWORD size; - if (ERROR_SUCCESS == apiRet) + size = sizeof (iid); + r = RegEnumKeyExW(hkey, i, iid, &size, 0, NULL, NULL, NULL); + if (ERROR_SUCCESS == r) { - ret = AddToEnumList(list, _ILCreateGuidFromStrA(iid)); + ret = AddToEnumList(list, _ILCreateGuidFromStrW(iid)); i++; } - else if (ERROR_NO_MORE_ITEMS == apiRet) + else if (ERROR_NO_MORE_ITEMS == r) moreKeys = FALSE; else ret = FALSE; @@ -309,22 +349,23 @@ static BOOL CreateDesktopEnumList(IEnumIDList *list, DWORD dwFlags) } } - /*enumerate the elements in %windir%\desktop */ - SHGetSpecialFolderPathA(0, szPath, CSIDL_DESKTOPDIRECTORY, FALSE); + /* enumerate the elements in %windir%\desktop */ + SHGetSpecialFolderPathW(0, szPath, CSIDL_DESKTOPDIRECTORY, FALSE); ret = ret && CreateFolderEnumList(list, szPath, dwFlags); return ret; } /************************************************************************** -* ISF_Desktop_fnEnumObjects -*/ + * ISF_Desktop_fnEnumObjects + */ static HRESULT WINAPI ISF_Desktop_fnEnumObjects (IShellFolder2 * iface, - HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST * ppEnumIDList) + HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST * ppEnumIDList) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - TRACE ("(%p)->(HWND=%p flags=0x%08lx pplist=%p)\n", This, hwndOwner, dwFlags, ppEnumIDList); + TRACE ("(%p)->(HWND=%p flags=0x%08lx pplist=%p)\n", + This, hwndOwner, dwFlags, ppEnumIDList); *ppEnumIDList = IEnumIDList_Constructor(); if (*ppEnumIDList) @@ -336,41 +377,41 @@ static HRESULT WINAPI ISF_Desktop_fnEnumObjects (IShellFolder2 * iface, } /************************************************************************** -* ISF_Desktop_fnBindToObject -*/ + * ISF_Desktop_fnBindToObject + */ static HRESULT WINAPI ISF_Desktop_fnBindToObject (IShellFolder2 * iface, - LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) + LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n", This, pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); + TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n", + This, pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); return SHELL32_BindToChild (This->pidlRoot, This->sPathTarget, pidl, riid, ppvOut); } /************************************************************************** -* ISF_Desktop_fnBindToStorage -*/ + * ISF_Desktop_fnBindToStorage + */ static HRESULT WINAPI ISF_Desktop_fnBindToStorage (IShellFolder2 * iface, - LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) + LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n", This, pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); + FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n", + This, pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); *ppvOut = NULL; return E_NOTIMPL; } /************************************************************************** -* ISF_Desktop_fnCompareIDs -*/ - + * ISF_Desktop_fnCompareIDs + */ static HRESULT WINAPI ISF_Desktop_fnCompareIDs (IShellFolder2 * iface, - LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) + LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - int nReturn; TRACE ("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", This, lParam, pidl1, pidl2); @@ -380,62 +421,70 @@ static HRESULT WINAPI ISF_Desktop_fnCompareIDs (IShellFolder2 * iface, } /************************************************************************** -* ISF_Desktop_fnCreateViewObject -*/ + * ISF_Desktop_fnCreateViewObject + */ static HRESULT WINAPI ISF_Desktop_fnCreateViewObject (IShellFolder2 * iface, - HWND hwndOwner, REFIID riid, LPVOID * ppvOut) + HWND hwndOwner, REFIID riid, LPVOID * ppvOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - LPSHELLVIEW pShellView; HRESULT hr = E_INVALIDARG; - TRACE ("(%p)->(hwnd=%p,%s,%p)\n", This, hwndOwner, shdebugstr_guid (riid), ppvOut); + TRACE ("(%p)->(hwnd=%p,%s,%p)\n", + This, hwndOwner, shdebugstr_guid (riid), ppvOut); - if (ppvOut) { - *ppvOut = NULL; + if (!ppvOut) + return hr; - if (IsEqualIID (riid, &IID_IDropTarget)) { - WARN ("IDropTarget not implemented\n"); - hr = E_NOTIMPL; - } else if (IsEqualIID (riid, &IID_IContextMenu)) { - WARN ("IContextMenu not implemented\n"); - hr = E_NOTIMPL; - } else if (IsEqualIID (riid, &IID_IShellView)) { - pShellView = IShellView_Constructor ((IShellFolder *) iface); - if (pShellView) { - hr = IShellView_QueryInterface (pShellView, riid, ppvOut); - IShellView_Release (pShellView); - } - } + *ppvOut = NULL; + + if (IsEqualIID (riid, &IID_IDropTarget)) + { + WARN ("IDropTarget not implemented\n"); + hr = E_NOTIMPL; + } + else if (IsEqualIID (riid, &IID_IContextMenu)) + { + WARN ("IContextMenu not implemented\n"); + hr = E_NOTIMPL; + } + else if (IsEqualIID (riid, &IID_IShellView)) + { + pShellView = IShellView_Constructor ((IShellFolder *) iface); + if (pShellView) + { + hr = IShellView_QueryInterface (pShellView, riid, ppvOut); + IShellView_Release (pShellView); + } } TRACE ("-- (%p)->(interface=%p)\n", This, ppvOut); return hr; } /************************************************************************** -* ISF_Desktop_fnGetAttributesOf -*/ + * ISF_Desktop_fnGetAttributesOf + */ static HRESULT WINAPI ISF_Desktop_fnGetAttributesOf (IShellFolder2 * iface, - UINT cidl, LPCITEMIDLIST * apidl, DWORD * rgfInOut) + UINT cidl, LPCITEMIDLIST * apidl, DWORD * rgfInOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - HRESULT hr = S_OK; - TRACE ("(%p)->(cidl=%d apidl=%p mask=0x%08lx)\n", This, cidl, apidl, *rgfInOut); + TRACE ("(%p)->(cidl=%d apidl=%p mask=0x%08lx)\n", + This, cidl, apidl, *rgfInOut); - if ((!cidl) || (!apidl) || (!rgfInOut)) - return E_INVALIDARG; + if (!cidl || !apidl || !rgfInOut) + return E_INVALIDARG; if (*rgfInOut == 0) - *rgfInOut = ~0; + *rgfInOut = ~0; - while (cidl > 0 && *apidl) { - pdump (*apidl); - SHELL32_GetItemAttributes (_IShellFolder_ (This), *apidl, rgfInOut); - apidl++; - cidl--; + while (cidl > 0 && *apidl) + { + pdump (*apidl); + SHELL32_GetItemAttributes (_IShellFolder_ (This), *apidl, rgfInOut); + apidl++; + cidl--; } TRACE ("-- result=0x%08lx\n", *rgfInOut); @@ -444,22 +493,20 @@ static HRESULT WINAPI ISF_Desktop_fnGetAttributesOf (IShellFolder2 * iface, } /************************************************************************** -* ISF_Desktop_fnGetUIObjectOf -* -* PARAMETERS -* HWND hwndOwner, //[in ] Parent window for any output -* UINT cidl, //[in ] array size -* LPCITEMIDLIST* apidl, //[in ] simple pidl array -* REFIID riid, //[in ] Requested Interface -* UINT* prgfInOut, //[ ] reserved -* LPVOID* ppvObject) //[out] Resulting Interface -* -*/ + * ISF_Desktop_fnGetUIObjectOf + * + * PARAMETERS + * HWND hwndOwner, //[in ] Parent window for any output + * UINT cidl, //[in ] array size + * LPCITEMIDLIST* apidl, //[in ] simple pidl array + * REFIID riid, //[in ] Requested Interface + * UINT* prgfInOut, //[ ] reserved + * LPVOID* ppvObject) //[out] Resulting Interface + * + */ static HRESULT WINAPI ISF_Desktop_fnGetUIObjectOf (IShellFolder2 * iface, - HWND hwndOwner, - UINT cidl, - LPCITEMIDLIST * apidl, - REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) + HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, + REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; @@ -468,58 +515,72 @@ static HRESULT WINAPI ISF_Desktop_fnGetUIObjectOf (IShellFolder2 * iface, HRESULT hr = E_INVALIDARG; TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", - This, hwndOwner, cidl, apidl, shdebugstr_guid (riid), prgfInOut, ppvOut); + This, hwndOwner, cidl, apidl, shdebugstr_guid (riid), prgfInOut, ppvOut); - if (ppvOut) { - *ppvOut = NULL; + if (!ppvOut) + return hr; - if (IsEqualIID (riid, &IID_IContextMenu)) { - pObj = (LPUNKNOWN) ISvItemCm_Constructor ((IShellFolder *) iface, This->pidlRoot, apidl, cidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IDataObject) && (cidl >= 1)) { - pObj = (LPUNKNOWN) IDataObject_Constructor (hwndOwner, This->pidlRoot, apidl, cidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IExtractIconA) && (cidl == 1)) { - pidl = ILCombine (This->pidlRoot, apidl[0]); - pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl); - SHFree (pidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IExtractIconW) && (cidl == 1)) { - pidl = ILCombine (This->pidlRoot, apidl[0]); - pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl); - SHFree (pidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) { - hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, (LPVOID *) & pObj); - } else if ((IsEqualIID(riid,&IID_IShellLinkW) || IsEqualIID(riid,&IID_IShellLinkA)) - && (cidl == 1)) { - pidl = ILCombine (This->pidlRoot, apidl[0]); - hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj); - SHFree (pidl); - } else { - hr = E_NOINTERFACE; - } - - if (SUCCEEDED(hr) && !pObj) - hr = E_OUTOFMEMORY; - - *ppvOut = pObj; + if (IsEqualIID (riid, &IID_IDataObject) && (cidl >= 1)) + { + pObj = (LPUNKNOWN) IDataObject_Constructor( hwndOwner, + This->pidlRoot, apidl, cidl); + hr = S_OK; } + else if ((IsEqualIID(riid,&IID_IShellLinkW) || + IsEqualIID(riid,&IID_IShellLinkA)) && (cidl == 1)) + { + pidl = ILCombine (This->pidlRoot, apidl[0]); + hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj); + SHFree (pidl); + } + else if (IsEqualIID (riid, &IID_IContextMenu)) + { + if (cidl > 0) + pObj = (LPUNKNOWN) ISvItemCm_Constructor( (IShellFolder *) iface, This->pidlRoot, apidl, cidl); + else + pObj = (LPUNKNOWN) ISvBgCm_Constructor( (IShellFolder *) iface, TRUE); + hr = S_OK; + } + else if (IsEqualIID (riid, &IID_IExtractIconA) && (cidl == 1)) + { + pidl = ILCombine (This->pidlRoot, apidl[0]); + pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl); + SHFree (pidl); + hr = S_OK; + } + else if (IsEqualIID (riid, &IID_IExtractIconW) && (cidl == 1)) + { + pidl = ILCombine (This->pidlRoot, apidl[0]); + pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl); + SHFree (pidl); + hr = S_OK; + } + else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) + { + hr = IShellFolder_QueryInterface (iface, + &IID_IDropTarget, (LPVOID *) & pObj); + } + else + hr = E_NOINTERFACE; + + if (SUCCEEDED(hr) && !pObj) + hr = E_OUTOFMEMORY; + + *ppvOut = pObj; TRACE ("(%p)->hr=0x%08lx\n", This, hr); return hr; } /************************************************************************** -* ISF_Desktop_fnGetDisplayNameOf -* -* NOTES -* special case: pidl = null gives desktop-name back -*/ + * ISF_Desktop_fnGetDisplayNameOf + * + * NOTES + * special case: pidl = null gives desktop-name back + */ static HRESULT WINAPI ISF_Desktop_fnGetDisplayNameOf (IShellFolder2 * iface, - LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet) + LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - CHAR szPath[MAX_PATH]; GUID const *clsid; HRESULT hr = S_OK; @@ -530,66 +591,103 @@ static HRESULT WINAPI ISF_Desktop_fnGetDisplayNameOf (IShellFolder2 * iface, pdump (pidl); if (!strRet) - return E_INVALIDARG; + return E_INVALIDARG; - if (_ILIsDesktop (pidl)) { - if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) && (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING)) { - lstrcpyA (szPath, This->sPathTarget); - } else { - HCR_GetClassNameA(&CLSID_ShellDesktop, szPath, MAX_PATH); - } - } else if (_ILIsPidlSimple (pidl)) { - if ((clsid = _ILGetGUIDPointer (pidl))) { - if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING) { - int bWantsForParsing; + if (_ILIsDesktop (pidl)) + { + if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) && + (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING)) + { + lstrcpyA (szPath, This->sPathTarget); + } + else + HCR_GetClassNameA(&CLSID_ShellDesktop, szPath, MAX_PATH); + } + else if (_ILIsPidlSimple (pidl)) + { + if ((clsid = _ILGetGUIDPointer (pidl))) + { + if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING) + { + int bWantsForParsing; - /* - * we can only get a filesystem path from a shellfolder if the value WantsFORPARSING in - * CLSID\\{...}\\shellfolder exists - * exception: the MyComputer folder has this keys not but like any filesystem backed - * folder it needs these behaviour - */ - if (IsEqualIID (clsid, &CLSID_MyComputer)) { - bWantsForParsing = 1; - } else { - /* get the "WantsFORPARSING" flag from the registry */ - char szRegPath[100]; + /* + * We can only get a filesystem path from a shellfolder if the + * value WantsFORPARSING in CLSID\\{...}\\shellfolder exists. + * + * Exception: The MyComputer folder doesn't have this key, + * but any other filesystem backed folder it needs it. + */ + if (IsEqualIID (clsid, &CLSID_MyComputer)) + { + bWantsForParsing = 1; + } + else + { + /* get the "WantsFORPARSING" flag from the registry */ + static const WCHAR clsidW[] = + { 'C','L','S','I','D','\\',0 }; + static const WCHAR shellfolderW[] = + { '\\','s','h','e','l','l','f','o','l','d','e','r',0 }; + static const WCHAR wantsForParsingW[] = + { 'W','a','n','t','s','F','o','r','P','a','r','s','i','n', + 'g',0 }; + WCHAR szRegPath[100]; + LONG r; - lstrcpyA (szRegPath, "CLSID\\"); - SHELL32_GUIDToStringA (clsid, &szRegPath[6]); - lstrcatA (szRegPath, "\\shellfolder"); - bWantsForParsing = - (ERROR_SUCCESS == - SHGetValueA (HKEY_CLASSES_ROOT, szRegPath, "WantsFORPARSING", NULL, NULL, NULL)); - } + lstrcpyW (szRegPath, clsidW); + SHELL32_GUIDToStringW (clsid, &szRegPath[6]); + lstrcatW (szRegPath, shellfolderW); + r = SHGetValueW(HKEY_CLASSES_ROOT, szRegPath, + wantsForParsingW, NULL, NULL, NULL); + if (r == ERROR_SUCCESS) + bWantsForParsing = TRUE; + else + bWantsForParsing = FALSE; + } - if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) && bWantsForParsing) { - /* we need the filesystem path to the destination folder. Only the folder itself can know it */ - hr = SHELL32_GetDisplayNameOfChild (iface, pidl, dwFlags, szPath, MAX_PATH); - } else { - /* parsing name like ::{...} */ - lstrcpyA (szPath, "::"); - SHELL32_GUIDToStringA (clsid, &szPath[2]); - } - } else { - /* user friendly name */ - HCR_GetClassNameA (clsid, szPath, MAX_PATH); - } - } else { - /* file system folder */ - _ILSimpleGetText (pidl, szPath, MAX_PATH); + if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) && + bWantsForParsing) + { + /* + * we need the filesystem path to the destination folder. + * Only the folder itself can know it + */ + hr = SHELL32_GetDisplayNameOfChild (iface, pidl, dwFlags, + szPath, MAX_PATH); + } + else + { + /* parsing name like ::{...} */ + lstrcpyA (szPath, "::"); + SHELL32_GUIDToStringA (clsid, &szPath[2]); + } + } + else + { + /* user friendly name */ + HCR_GetClassNameA (clsid, szPath, MAX_PATH); + } + } + else + { + /* file system folder */ + _ILSimpleGetText (pidl, szPath, MAX_PATH); - if (!_ILIsFolder(pidl)) - SHELL_FS_ProcessDisplayFilename(szPath, dwFlags); - } - } else { - /* a complex pidl, let the subfolder do the work */ - hr = SHELL32_GetDisplayNameOfChild (iface, pidl, dwFlags, szPath, MAX_PATH); + if (!_ILIsFolder(pidl)) + SHELL_FS_ProcessDisplayFilename(szPath, dwFlags); + } + } + else + { + /* a complex pidl, let the subfolder do the work */ + hr = SHELL32_GetDisplayNameOfChild (iface, pidl, dwFlags, szPath, MAX_PATH); } - if (SUCCEEDED (hr)) { - strRet->uType = STRRET_CSTR; - lstrcpynA (strRet->u.cStr, szPath, MAX_PATH); + if (SUCCEEDED (hr)) + { + strRet->uType = STRRET_CSTR; + lstrcpynA (strRet->u.cStr, szPath, MAX_PATH); } TRACE ("-- (%p)->(%s,0x%08lx)\n", This, szPath, hr); @@ -597,77 +695,86 @@ static HRESULT WINAPI ISF_Desktop_fnGetDisplayNameOf (IShellFolder2 * iface, } /************************************************************************** -* ISF_Desktop_fnSetNameOf -* Changes the name of a file object or subfolder, possibly changing its item -* identifier in the process. -* -* PARAMETERS -* HWND hwndOwner, //[in ] Owner window for output -* LPCITEMIDLIST pidl, //[in ] simple pidl of item to change -* LPCOLESTR lpszName, //[in ] the items new display name -* DWORD dwFlags, //[in ] SHGNO formatting flags -* LPITEMIDLIST* ppidlOut) //[out] simple pidl returned -*/ -static HRESULT WINAPI ISF_Desktop_fnSetNameOf (IShellFolder2 * iface, HWND hwndOwner, LPCITEMIDLIST pidl, /*simple pidl */ - LPCOLESTR lpName, DWORD dwFlags, LPITEMIDLIST * pPidlOut) + * ISF_Desktop_fnSetNameOf + * Changes the name of a file object or subfolder, possibly changing its item + * identifier in the process. + * + * PARAMETERS + * HWND hwndOwner, //[in ] Owner window for output + * LPCITEMIDLIST pidl, //[in ] simple pidl of item to change + * LPCOLESTR lpszName, //[in ] the items new display name + * DWORD dwFlags, //[in ] SHGNO formatting flags + * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned + */ +static HRESULT WINAPI ISF_Desktop_fnSetNameOf (IShellFolder2 * iface, + HWND hwndOwner, LPCITEMIDLIST pidl, /* simple pidl */ + LPCOLESTR lpName, DWORD dwFlags, LPITEMIDLIST * pPidlOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - FIXME ("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", This, hwndOwner, pidl, debugstr_w (lpName), dwFlags, pPidlOut); + FIXME ("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", This, hwndOwner, pidl, + debugstr_w (lpName), dwFlags, pPidlOut); return E_FAIL; } -static HRESULT WINAPI ISF_Desktop_fnGetDefaultSearchGUID (IShellFolder2 * iface, GUID * pguid) +static HRESULT WINAPI ISF_Desktop_fnGetDefaultSearchGUID(IShellFolder2 *iface, + GUID * pguid) { IGenericSFImpl *This = (IGenericSFImpl *)iface; FIXME ("(%p)\n", This); return E_NOTIMPL; } -static HRESULT WINAPI ISF_Desktop_fnEnumSearches (IShellFolder2 * iface, IEnumExtraSearch ** ppenum) + +static HRESULT WINAPI ISF_Desktop_fnEnumSearches (IShellFolder2 *iface, + IEnumExtraSearch ** ppenum) { IGenericSFImpl *This = (IGenericSFImpl *)iface; FIXME ("(%p)\n", This); return E_NOTIMPL; } + static HRESULT WINAPI ISF_Desktop_fnGetDefaultColumn (IShellFolder2 * iface, - DWORD dwRes, ULONG * pSort, ULONG * pDisplay) + DWORD dwRes, ULONG * pSort, ULONG * pDisplay) { IGenericSFImpl *This = (IGenericSFImpl *)iface; TRACE ("(%p)\n", This); if (pSort) - *pSort = 0; + *pSort = 0; if (pDisplay) - *pDisplay = 0; + *pDisplay = 0; return S_OK; } -static HRESULT WINAPI ISF_Desktop_fnGetDefaultColumnState (IShellFolder2 * iface, UINT iColumn, DWORD * pcsFlags) +static HRESULT WINAPI ISF_Desktop_fnGetDefaultColumnState ( + IShellFolder2 * iface, UINT iColumn, DWORD * pcsFlags) { IGenericSFImpl *This = (IGenericSFImpl *)iface; TRACE ("(%p)\n", This); if (!pcsFlags || iColumn >= DESKTOPSHELLVIEWCOLUMNS) - return E_INVALIDARG; + return E_INVALIDARG; *pcsFlags = DesktopSFHeader[iColumn].pcsFlags; return S_OK; } + static HRESULT WINAPI ISF_Desktop_fnGetDetailsEx (IShellFolder2 * iface, - LPCITEMIDLIST pidl, const SHCOLUMNID * pscid, VARIANT * pv) + LPCITEMIDLIST pidl, const SHCOLUMNID * pscid, VARIANT * pv) { IGenericSFImpl *This = (IGenericSFImpl *)iface; FIXME ("(%p)\n", This); return E_NOTIMPL; } + static HRESULT WINAPI ISF_Desktop_fnGetDetailsOf (IShellFolder2 * iface, - LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS * psd) + LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS * psd) { IGenericSFImpl *This = (IGenericSFImpl *)iface; @@ -676,40 +783,46 @@ static HRESULT WINAPI ISF_Desktop_fnGetDetailsOf (IShellFolder2 * iface, TRACE ("(%p)->(%p %i %p)\n", This, pidl, iColumn, psd); if (!psd || iColumn >= DESKTOPSHELLVIEWCOLUMNS) - return E_INVALIDARG; + return E_INVALIDARG; - if (!pidl) { - psd->fmt = DesktopSFHeader[iColumn].fmt; - psd->cxChar = DesktopSFHeader[iColumn].cxChar; - psd->str.uType = STRRET_CSTR; - LoadStringA (shell32_hInstance, DesktopSFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH); - return S_OK; - } else { - /* the data from the pidl */ - switch (iColumn) { - case 0: /* name */ - hr = IShellFolder_GetDisplayNameOf (iface, pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); - break; - case 1: /* size */ - _ILGetFileSize (pidl, psd->str.u.cStr, MAX_PATH); - break; - case 2: /* type */ - _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH); - break; - case 3: /* date */ - _ILGetFileDate (pidl, psd->str.u.cStr, MAX_PATH); - break; - case 4: /* attributes */ - _ILGetFileAttributes (pidl, psd->str.u.cStr, MAX_PATH); - break; - } - hr = S_OK; - psd->str.uType = STRRET_CSTR; + if (!pidl) + { + psd->fmt = DesktopSFHeader[iColumn].fmt; + psd->cxChar = DesktopSFHeader[iColumn].cxChar; + psd->str.uType = STRRET_CSTR; + LoadStringA (shell32_hInstance, DesktopSFHeader[iColumn].colnameid, + psd->str.u.cStr, MAX_PATH); + return S_OK; } + /* the data from the pidl */ + switch (iColumn) + { + case 0: /* name */ + hr = IShellFolder_GetDisplayNameOf(iface, pidl, + SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); + break; + case 1: /* size */ + _ILGetFileSize (pidl, psd->str.u.cStr, MAX_PATH); + break; + case 2: /* type */ + _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH); + break; + case 3: /* date */ + _ILGetFileDate (pidl, psd->str.u.cStr, MAX_PATH); + break; + case 4: /* attributes */ + _ILGetFileAttributes (pidl, psd->str.u.cStr, MAX_PATH); + break; + } + hr = S_OK; + psd->str.uType = STRRET_CSTR; + return hr; } -static HRESULT WINAPI ISF_Desktop_fnMapColumnToSCID (IShellFolder2 * iface, UINT column, SHCOLUMNID * pscid) + +static HRESULT WINAPI ISF_Desktop_fnMapColumnToSCID ( + IShellFolder2 * iface, UINT column, SHCOLUMNID * pscid) { IGenericSFImpl *This = (IGenericSFImpl *)iface; FIXME ("(%p)\n", This); @@ -718,24 +831,25 @@ static HRESULT WINAPI ISF_Desktop_fnMapColumnToSCID (IShellFolder2 * iface, UINT static IShellFolder2Vtbl vt_MCFldr_ShellFolder2 = { - ISF_Desktop_fnQueryInterface, - ISF_Desktop_fnAddRef, - ISF_Desktop_fnRelease, - ISF_Desktop_fnParseDisplayName, - ISF_Desktop_fnEnumObjects, - ISF_Desktop_fnBindToObject, - ISF_Desktop_fnBindToStorage, - ISF_Desktop_fnCompareIDs, - ISF_Desktop_fnCreateViewObject, - ISF_Desktop_fnGetAttributesOf, - ISF_Desktop_fnGetUIObjectOf, - ISF_Desktop_fnGetDisplayNameOf, - ISF_Desktop_fnSetNameOf, - /* ShellFolder2 */ - ISF_Desktop_fnGetDefaultSearchGUID, - ISF_Desktop_fnEnumSearches, - ISF_Desktop_fnGetDefaultColumn, - ISF_Desktop_fnGetDefaultColumnState, - ISF_Desktop_fnGetDetailsEx, - ISF_Desktop_fnGetDetailsOf, - ISF_Desktop_fnMapColumnToSCID}; + ISF_Desktop_fnQueryInterface, + ISF_Desktop_fnAddRef, + ISF_Desktop_fnRelease, + ISF_Desktop_fnParseDisplayName, + ISF_Desktop_fnEnumObjects, + ISF_Desktop_fnBindToObject, + ISF_Desktop_fnBindToStorage, + ISF_Desktop_fnCompareIDs, + ISF_Desktop_fnCreateViewObject, + ISF_Desktop_fnGetAttributesOf, + ISF_Desktop_fnGetUIObjectOf, + ISF_Desktop_fnGetDisplayNameOf, + ISF_Desktop_fnSetNameOf, + /* ShellFolder2 */ + ISF_Desktop_fnGetDefaultSearchGUID, + ISF_Desktop_fnEnumSearches, + ISF_Desktop_fnGetDefaultColumn, + ISF_Desktop_fnGetDefaultColumnState, + ISF_Desktop_fnGetDetailsEx, + ISF_Desktop_fnGetDetailsOf, + ISF_Desktop_fnMapColumnToSCID +}; diff --git a/reactos/lib/shell32/shfldr_fs.c b/reactos/lib/shell32/shfldr_fs.c index e8fdca78b38..41c6e1d543e 100644 --- a/reactos/lib/shell32/shfldr_fs.c +++ b/reactos/lib/shell32/shfldr_fs.c @@ -1,9 +1,9 @@ /* - * file system folder + * file system folder * - * Copyright 1997 Marcus Meissner - * Copyright 1998, 1999, 2002 Juergen Schmied + * Copyright 1997 Marcus Meissner + * Copyright 1998, 1999, 2002 Juergen Schmied * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -67,24 +67,24 @@ typedef struct { IDropTargetVtbl *lpvtblDropTarget; ISFHelperVtbl *lpvtblSFHelper; - IUnknown *pUnkOuter; /* used for aggregation */ + IUnknown *pUnkOuter; /* used for aggregation */ CLSID *pclsid; /* both paths are parsible from the desktop */ - LPSTR sPathTarget; /* complete path to target used for enumeration and ChangeNotify */ + LPSTR sPathTarget; /* complete path to target used for enumeration and ChangeNotify */ - LPITEMIDLIST pidlRoot; /* absolute pidl */ + LPITEMIDLIST pidlRoot; /* absolute pidl */ - int dwAttributes; /* attributes returned by GetAttributesOf FIXME: use it */ + int dwAttributes; /* attributes returned by GetAttributesOf FIXME: use it */ - UINT cfShellIDList; /* clipboardformat for IDropTarget */ - BOOL fAcceptFmt; /* flag for pending Drop */ + UINT cfShellIDList; /* clipboardformat for IDropTarget */ + BOOL fAcceptFmt; /* flag for pending Drop */ } IGenericSFImpl; static struct IUnknownVtbl unkvt; static struct IShellFolder2Vtbl sfvt; -static struct IPersistFolder3Vtbl vt_FSFldr_PersistFolder3; /* IPersistFolder3 for a FS_Folder */ +static struct IPersistFolder3Vtbl vt_FSFldr_PersistFolder3; /* IPersistFolder3 for a FS_Folder */ static struct IDropTargetVtbl dtvt; static struct ISFHelperVtbl shvt; @@ -106,31 +106,31 @@ static struct ISFHelperVtbl shvt; /* converts This to a interface pointer */ -#define _IUnknown_(This) (IUnknown*)&(This->lpVtbl) -#define _IShellFolder_(This) (IShellFolder*)&(This->lpvtblShellFolder) -#define _IShellFolder2_(This) (IShellFolder2*)&(This->lpvtblShellFolder) -#define _IPersist_(This) (IPersist*)&(This->lpvtblPersistFolder3) -#define _IPersistFolder_(This) (IPersistFolder*)&(This->lpvtblPersistFolder3) -#define _IPersistFolder2_(This) (IPersistFolder2*)&(This->lpvtblPersistFolder3) -#define _IPersistFolder3_(This) (IPersistFolder3*)&(This->lpvtblPersistFolder3) -#define _IDropTarget_(This) (IDropTarget*)&(This->lpvtblDropTarget) -#define _ISFHelper_(This) (ISFHelper*)&(This->lpvtblSFHelper) +#define _IUnknown_(This) (IUnknown*)&(This->lpVtbl) +#define _IShellFolder_(This) (IShellFolder*)&(This->lpvtblShellFolder) +#define _IShellFolder2_(This) (IShellFolder2*)&(This->lpvtblShellFolder) +#define _IPersist_(This) (IPersist*)&(This->lpvtblPersistFolder3) +#define _IPersistFolder_(This) (IPersistFolder*)&(This->lpvtblPersistFolder3) +#define _IPersistFolder2_(This) (IPersistFolder2*)&(This->lpvtblPersistFolder3) +#define _IPersistFolder3_(This) (IPersistFolder3*)&(This->lpvtblPersistFolder3) +#define _IDropTarget_(This) (IDropTarget*)&(This->lpvtblDropTarget) +#define _ISFHelper_(This) (ISFHelper*)&(This->lpvtblSFHelper) /************************************************************************** -* registers clipboardformat once +* registers clipboardformat once */ static void SF_RegisterClipFmt (IGenericSFImpl * This) { TRACE ("(%p)\n", This); if (!This->cfShellIDList) { - This->cfShellIDList = RegisterClipboardFormatA (CFSTR_SHELLIDLIST); + This->cfShellIDList = RegisterClipboardFormatA (CFSTR_SHELLIDLIST); } } /************************************************************************** -* we need a separate IUnknown to handle aggregation -* (inner IUnknown) +* we need a separate IUnknown to handle aggregation +* (inner IUnknown) */ static HRESULT WINAPI IUnknown_fnQueryInterface (IUnknown * iface, REFIID riid, LPVOID * ppvObj) { @@ -141,30 +141,30 @@ static HRESULT WINAPI IUnknown_fnQueryInterface (IUnknown * iface, REFIID riid, *ppvObj = NULL; if (IsEqualIID (riid, &IID_IUnknown)) - *ppvObj = _IUnknown_ (This); + *ppvObj = _IUnknown_ (This); else if (IsEqualIID (riid, &IID_IShellFolder)) - *ppvObj = _IShellFolder_ (This); + *ppvObj = _IShellFolder_ (This); else if (IsEqualIID (riid, &IID_IShellFolder2)) - *ppvObj = _IShellFolder_ (This); + *ppvObj = _IShellFolder_ (This); else if (IsEqualIID (riid, &IID_IPersist)) - *ppvObj = _IPersist_ (This); + *ppvObj = _IPersist_ (This); else if (IsEqualIID (riid, &IID_IPersistFolder)) - *ppvObj = _IPersistFolder_ (This); + *ppvObj = _IPersistFolder_ (This); else if (IsEqualIID (riid, &IID_IPersistFolder2)) - *ppvObj = _IPersistFolder2_ (This); + *ppvObj = _IPersistFolder2_ (This); else if (IsEqualIID (riid, &IID_IPersistFolder3)) - *ppvObj = _IPersistFolder3_ (This); + *ppvObj = _IPersistFolder3_ (This); else if (IsEqualIID (riid, &IID_ISFHelper)) - *ppvObj = _ISFHelper_ (This); + *ppvObj = _ISFHelper_ (This); else if (IsEqualIID (riid, &IID_IDropTarget)) { - *ppvObj = _IDropTarget_ (This); - SF_RegisterClipFmt (This); + *ppvObj = _IDropTarget_ (This); + SF_RegisterClipFmt (This); } if (*ppvObj) { - IUnknown_AddRef ((IUnknown *) (*ppvObj)); - TRACE ("-- Interface = %p\n", *ppvObj); - return S_OK; + IUnknown_AddRef ((IUnknown *) (*ppvObj)); + TRACE ("-- Interface = %p\n", *ppvObj); + return S_OK; } TRACE ("-- Interface: E_NOINTERFACE\n"); return E_NOINTERFACE; @@ -188,13 +188,13 @@ static ULONG WINAPI IUnknown_fnRelease (IUnknown * iface) TRACE ("(%p)->(count=%lu)\n", This, refCount + 1); if (!refCount) { - TRACE ("-- destroying IShellFolder(%p)\n", This); + TRACE ("-- destroying IShellFolder(%p)\n", This); - if (This->pidlRoot) - SHFree (This->pidlRoot); - if (This->sPathTarget) - SHFree (This->sPathTarget); - LocalFree ((HLOCAL) This); + if (This->pidlRoot) + SHFree (This->pidlRoot); + if (This->sPathTarget) + SHFree (This->sPathTarget); + LocalFree ((HLOCAL) This); } return refCount; } @@ -217,24 +217,25 @@ static shvheader GenericSFHeader[] = { #define GENERICSHELLVIEWCOLUMNS 5 /************************************************************************** -* IFSFolder_Constructor +* IFSFolder_Constructor * * NOTES * creating undocumented ShellFS_Folder as part of an aggregation * {F3364BA0-65B9-11CE-A9BA-00AA004AE837} * */ -HRESULT WINAPI IFSFolder_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv) +HRESULT WINAPI +IFSFolder_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv) { IGenericSFImpl *sf; TRACE ("unkOut=%p %s\n", pUnkOuter, shdebugstr_guid (riid)); if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown)) - return CLASS_E_NOAGGREGATION; + return CLASS_E_NOAGGREGATION; sf = (IGenericSFImpl *) LocalAlloc (GMEM_ZEROINIT, sizeof (IGenericSFImpl)); if (!sf) - return E_OUTOFMEMORY; + return E_OUTOFMEMORY; sf->ref = 0; sf->lpVtbl = &unkvt; @@ -246,8 +247,8 @@ HRESULT WINAPI IFSFolder_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID sf->pUnkOuter = pUnkOuter ? pUnkOuter : _IUnknown_ (sf); if (!SUCCEEDED (IUnknown_QueryInterface (_IUnknown_ (sf), riid, ppv))) { - IUnknown_Release (_IUnknown_ (sf)); - return E_NOINTERFACE; + IUnknown_Release (_IUnknown_ (sf)); + return E_NOINTERFACE; } TRACE ("--%p\n", *ppv); @@ -258,14 +259,16 @@ HRESULT WINAPI IFSFolder_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID * IShellFolder_fnQueryInterface * * PARAMETERS - * REFIID riid [in ] Requested InterfaceID - * LPVOID* ppvObject [out] Interface* to hold the result + * REFIID riid [in ] Requested InterfaceID + * LPVOID* ppvObject [out] Interface* to hold the result */ -static HRESULT WINAPI IShellFolder_fnQueryInterface (IShellFolder2 * iface, REFIID riid, LPVOID * ppvObj) +static HRESULT WINAPI +IShellFolder_fnQueryInterface (IShellFolder2 * iface, REFIID riid, + LPVOID * ppvObj) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - TRACE ("(%p)->(%s,%p)\n", This, shdebugstr_guid (riid), ppvObj); + TRACE ("(%p)->(%s,%p)\n", This, shdebugstr_guid (riid), ppvObj); return IUnknown_QueryInterface (This->pUnkOuter, riid, ppvObj); } @@ -295,6 +298,50 @@ static ULONG WINAPI IShellFolder_fnRelease (IShellFolder2 * iface) return IUnknown_Release (This->pUnkOuter); } +/************************************************************************** + * SHELL32_CreatePidlFromBindCtx [internal] + * + * If the caller bound File System Bind Data, assume it is the + * find data for the path. + * This allows binding of paths that don't exist. + */ +LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path) +{ + static const WCHAR szfsbc[] = { + 'F','i','l','e',' ','S','y','s','t','e','m',' ', + 'B','i','n','d',' ','D','a','t','a',0 }; + IFileSystemBindData *fsbd = NULL; + LPITEMIDLIST pidl = NULL; + IUnknown *param = NULL; + WIN32_FIND_DATAW wfd; + HRESULT r; + + TRACE("%p %s\n", pbc, debugstr_w(path)); + + if (!pbc) + return NULL; + + /* see if the caller bound File System Bind Data */ + r = IBindCtx_GetObjectParam( pbc, (LPOLESTR) szfsbc, ¶m ); + if (FAILED(r)) + return NULL; + + r = IUnknown_QueryInterface( param, &IID_IFileSystemBindData, + (LPVOID*) &fsbd ); + if (SUCCEEDED(r)) + { + r = IFileSystemBindData_GetFindData( fsbd, &wfd ); + if (SUCCEEDED(r)) + { + lstrcpynW( &wfd.cFileName[0], path, MAX_PATH ); + pidl = _ILCreateFromFindDataW( &wfd ); + } + IFileSystemBindData_Release( fsbd ); + } + + return pidl; +} + /************************************************************************** * IShellFolder_ParseDisplayName {SHELL32} * @@ -313,8 +360,8 @@ static ULONG WINAPI IShellFolder_fnRelease (IShellFolder2 * iface) * subfolder to evaluate the remaining parts. * Now we can parse into namespaces implemented by shell extensions * -* Behaviour on win98: lpszDisplayName=NULL -> crash -* lpszDisplayName="" -> returns mycoputer-pidl +* Behaviour on win98: lpszDisplayName=NULL -> crash +* lpszDisplayName="" -> returns mycoputer-pidl * * FIXME * pdwAttributes is not set @@ -322,59 +369,66 @@ static ULONG WINAPI IShellFolder_fnRelease (IShellFolder2 * iface) */ static HRESULT WINAPI IShellFolder_fnParseDisplayName (IShellFolder2 * iface, - HWND hwndOwner, - LPBC pbc, - LPOLESTR lpszDisplayName, - DWORD * pchEaten, LPITEMIDLIST * ppidl, DWORD * pdwAttributes) + HWND hwndOwner, + LPBC pbc, + LPOLESTR lpszDisplayName, + DWORD * pchEaten, LPITEMIDLIST * ppidl, + DWORD * pdwAttributes) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) HRESULT hr = E_INVALIDARG; LPCWSTR szNext = NULL; WCHAR szElement[MAX_PATH]; - CHAR szPath[MAX_PATH]; + WCHAR szPath[MAX_PATH]; LPITEMIDLIST pidlTemp = NULL; DWORD len; TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", - This, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName), pchEaten, ppidl, pdwAttributes); + This, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName), + pchEaten, ppidl, pdwAttributes); if (!lpszDisplayName || !ppidl) - return E_INVALIDARG; + return E_INVALIDARG; if (pchEaten) - *pchEaten = 0; /* strange but like the original */ + *pchEaten = 0; /* strange but like the original */ - if (*lpszDisplayName) { - /* get the next element */ - szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); + pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, lpszDisplayName); + if (!pidlTemp && *lpszDisplayName) + { + /* get the next element */ + szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); - /* build the full pathname to the element */ - lstrcpyA(szPath, This->sPathTarget); - PathAddBackslashA(szPath); - len = lstrlenA(szPath); - WideCharToMultiByte(CP_ACP, 0, szElement, -1, szPath + len, MAX_PATH - len, NULL, NULL); + /* build the full pathname to the element */ + /* lstrcpyW(szPath, This->sPathTarget); */ + MultiByteToWideChar(CP_ACP, 0, This->sPathTarget, -1, szPath, MAX_PATH); + PathAddBackslashW(szPath); + len = lstrlenW(szPath); + lstrcpynW(szPath + len, szElement, MAX_PATH - len); - /* get the pidl */ - hr = _ILCreateFromPathA(szPath, &pidlTemp); + /* get the pidl */ + hr = _ILCreateFromPathW(szPath, &pidlTemp); - if (SUCCEEDED(hr)) { - if (szNext && *szNext) { - /* try to analyse the next element */ - hr = SHELL32_ParseNextElement (iface, hwndOwner, pbc, &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes); - } else { - /* it's the last element */ - if (pdwAttributes && *pdwAttributes) { - hr = SHELL32_GetItemAttributes (_IShellFolder_ (This), pidlTemp, pdwAttributes); - } - } - } + if (SUCCEEDED(hr)) { + if (szNext && *szNext) { + /* try to analyse the next element */ + hr = SHELL32_ParseNextElement (iface, hwndOwner, pbc, + &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes); + } else { + /* it's the last element */ + if (pdwAttributes && *pdwAttributes) { + hr = SHELL32_GetItemAttributes (_IShellFolder_ (This), + pidlTemp, pdwAttributes); + } + } + } } if (SUCCEEDED(hr)) - *ppidl = pidlTemp; + *ppidl = pidlTemp; else - *ppidl = NULL; + *ppidl = NULL; TRACE ("(%p)->(-- pidl=%p ret=0x%08lx)\n", This, ppidl ? *ppidl : 0, hr); @@ -382,22 +436,28 @@ IShellFolder_fnParseDisplayName (IShellFolder2 * iface, } /************************************************************************** -* IShellFolder_fnEnumObjects +* IShellFolder_fnEnumObjects * PARAMETERS * HWND hwndOwner, //[in ] Parent Window * DWORD grfFlags, //[in ] SHCONTF enumeration mask * LPENUMIDLIST* ppenumIDList //[out] IEnumIDList interface */ static HRESULT WINAPI -IShellFolder_fnEnumObjects (IShellFolder2 * iface, HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST * ppEnumIDList) +IShellFolder_fnEnumObjects (IShellFolder2 * iface, HWND hwndOwner, + DWORD dwFlags, LPENUMIDLIST * ppEnumIDList) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - TRACE ("(%p)->(HWND=%p flags=0x%08lx pplist=%p)\n", This, hwndOwner, dwFlags, ppEnumIDList); + TRACE ("(%p)->(HWND=%p flags=0x%08lx pplist=%p)\n", This, hwndOwner, + dwFlags, ppEnumIDList); *ppEnumIDList = IEnumIDList_Constructor(); if (*ppEnumIDList) - CreateFolderEnumList(*ppEnumIDList, This->sPathTarget, dwFlags); + { + WCHAR path[MAX_PATH]; + MultiByteToWideChar(CP_ACP, 0, This->sPathTarget, -1, path, MAX_PATH); + CreateFolderEnumList(*ppEnumIDList, path, dwFlags); + } TRACE ("-- (%p)->(new ID List: %p)\n", This, *ppEnumIDList); @@ -405,7 +465,7 @@ IShellFolder_fnEnumObjects (IShellFolder2 * iface, HWND hwndOwner, DWORD dwFlags } /************************************************************************** -* IShellFolder_fnBindToObject +* IShellFolder_fnBindToObject * PARAMETERS * LPCITEMIDLIST pidl, //[in ] relative pidl to open * LPBC pbc, //[in ] optional FileSystemBindData context @@ -413,13 +473,16 @@ IShellFolder_fnEnumObjects (IShellFolder2 * iface, HWND hwndOwner, DWORD dwFlags * LPVOID* ppvObject //[out] Interface* */ static HRESULT WINAPI -IShellFolder_fnBindToObject (IShellFolder2 * iface, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, LPVOID * ppvOut) +IShellFolder_fnBindToObject (IShellFolder2 * iface, LPCITEMIDLIST pidl, + LPBC pbc, REFIID riid, LPVOID * ppvOut) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n", This, pidl, pbc, shdebugstr_guid (riid), ppvOut); + TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n", This, pidl, pbc, + shdebugstr_guid (riid), ppvOut); - return SHELL32_BindToChild (This->pidlRoot, This->sPathTarget, pidl, riid, ppvOut); + return SHELL32_BindToChild (This->pidlRoot, This->sPathTarget, pidl, riid, + ppvOut); } /************************************************************************** @@ -431,11 +494,13 @@ IShellFolder_fnBindToObject (IShellFolder2 * iface, LPCITEMIDLIST pidl, LPBC pbc * LPVOID* ppvObject //[out] Interface* returned */ static HRESULT WINAPI -IShellFolder_fnBindToStorage (IShellFolder2 * iface, LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) +IShellFolder_fnBindToStorage (IShellFolder2 * iface, LPCITEMIDLIST pidl, + LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n", This, pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); + FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n", This, pidl, pbcReserved, + shdebugstr_guid (riid), ppvOut); *ppvOut = NULL; return E_NOTIMPL; @@ -446,7 +511,8 @@ IShellFolder_fnBindToStorage (IShellFolder2 * iface, LPCITEMIDLIST pidl, LPBC pb */ static HRESULT WINAPI -IShellFolder_fnCompareIDs (IShellFolder2 * iface, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) +IShellFolder_fnCompareIDs (IShellFolder2 * iface, LPARAM lParam, + LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) @@ -459,33 +525,35 @@ IShellFolder_fnCompareIDs (IShellFolder2 * iface, LPARAM lParam, LPCITEMIDLIST p } /************************************************************************** -* IShellFolder_fnCreateViewObject +* IShellFolder_fnCreateViewObject */ static HRESULT WINAPI -IShellFolder_fnCreateViewObject (IShellFolder2 * iface, HWND hwndOwner, REFIID riid, LPVOID * ppvOut) +IShellFolder_fnCreateViewObject (IShellFolder2 * iface, HWND hwndOwner, + REFIID riid, LPVOID * ppvOut) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) LPSHELLVIEW pShellView; HRESULT hr = E_INVALIDARG; - TRACE ("(%p)->(hwnd=%p,%s,%p)\n", This, hwndOwner, shdebugstr_guid (riid), ppvOut); + TRACE ("(%p)->(hwnd=%p,%s,%p)\n", This, hwndOwner, shdebugstr_guid (riid), + ppvOut); if (ppvOut) { - *ppvOut = NULL; + *ppvOut = NULL; - if (IsEqualIID (riid, &IID_IDropTarget)) { - hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, ppvOut); - } else if (IsEqualIID (riid, &IID_IContextMenu)) { - FIXME ("IContextMenu not implemented\n"); - hr = E_NOTIMPL; - } else if (IsEqualIID (riid, &IID_IShellView)) { - pShellView = IShellView_Constructor ((IShellFolder *) iface); - if (pShellView) { - hr = IShellView_QueryInterface (pShellView, riid, ppvOut); - IShellView_Release (pShellView); - } - } + if (IsEqualIID (riid, &IID_IDropTarget)) { + hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, ppvOut); + } else if (IsEqualIID (riid, &IID_IContextMenu)) { + FIXME ("IContextMenu not implemented\n"); + hr = E_NOTIMPL; + } else if (IsEqualIID (riid, &IID_IShellView)) { + pShellView = IShellView_Constructor ((IShellFolder *) iface); + if (pShellView) { + hr = IShellView_QueryInterface (pShellView, riid, ppvOut); + IShellView_Release (pShellView); + } + } } TRACE ("-- (%p)->(interface=%p)\n", This, ppvOut); return hr; @@ -501,25 +569,27 @@ IShellFolder_fnCreateViewObject (IShellFolder2 * iface, HWND hwndOwner, REFIID r * */ static HRESULT WINAPI -IShellFolder_fnGetAttributesOf (IShellFolder2 * iface, UINT cidl, LPCITEMIDLIST * apidl, DWORD * rgfInOut) +IShellFolder_fnGetAttributesOf (IShellFolder2 * iface, UINT cidl, + LPCITEMIDLIST * apidl, DWORD * rgfInOut) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) HRESULT hr = S_OK; - TRACE ("(%p)->(cidl=%d apidl=%p mask=0x%08lx)\n", This, cidl, apidl, *rgfInOut); + TRACE ("(%p)->(cidl=%d apidl=%p mask=0x%08lx)\n", This, cidl, apidl, + *rgfInOut); if ((!cidl) || (!apidl) || (!rgfInOut)) - return E_INVALIDARG; + return E_INVALIDARG; if (*rgfInOut == 0) - *rgfInOut = ~0; + *rgfInOut = ~0; while (cidl > 0 && *apidl) { - pdump (*apidl); - SHELL32_GetItemAttributes (_IShellFolder_ (This), *apidl, rgfInOut); - apidl++; - cidl--; + pdump (*apidl); + SHELL32_GetItemAttributes (_IShellFolder_ (This), *apidl, rgfInOut); + apidl++; + cidl--; } TRACE ("-- result=0x%08lx\n", *rgfInOut); @@ -539,19 +609,22 @@ IShellFolder_fnGetAttributesOf (IShellFolder2 * iface, UINT cidl, LPCITEMIDLIST * LPVOID* ppvObject) //[out] Resulting Interface * * NOTES -* This function gets asked to return "view objects" for one or more (multiple select) -* items: -* The viewobject typically is an COM object with one of the following interfaces: +* This function gets asked to return "view objects" for one or more (multiple +* select) items: +* The viewobject typically is an COM object with one of the following +* interfaces: * IExtractIcon,IDataObject,IContextMenu * In order to support icon positions in the default Listview your DataObject -* must implement the SetData method (in addition to GetData :) - the shell passes -* a barely documented "Icon positions" structure to SetData when the drag starts, -* and GetData's it if the drop is in another explorer window that needs the positions. +* must implement the SetData method (in addition to GetData :) - the shell +* passes a barely documented "Icon positions" structure to SetData when the +* drag starts, and GetData's it if the drop is in another explorer window that +* needs the positions. */ static HRESULT WINAPI IShellFolder_fnGetUIObjectOf (IShellFolder2 * iface, - HWND hwndOwner, - UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) + HWND hwndOwner, + UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, + UINT * prgfInOut, LPVOID * ppvOut) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) @@ -560,86 +633,99 @@ IShellFolder_fnGetUIObjectOf (IShellFolder2 * iface, HRESULT hr = E_INVALIDARG; TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", - This, hwndOwner, cidl, apidl, shdebugstr_guid (riid), prgfInOut, ppvOut); + This, hwndOwner, cidl, apidl, shdebugstr_guid (riid), prgfInOut, ppvOut); if (ppvOut) { - *ppvOut = NULL; + *ppvOut = NULL; - if (IsEqualIID (riid, &IID_IContextMenu) && (cidl >= 1)) { - pObj = (LPUNKNOWN) ISvItemCm_Constructor ((IShellFolder *) iface, This->pidlRoot, apidl, cidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IDataObject) && (cidl >= 1)) { - pObj = (LPUNKNOWN) IDataObject_Constructor (hwndOwner, This->pidlRoot, apidl, cidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IExtractIconA) && (cidl == 1)) { - pidl = ILCombine (This->pidlRoot, apidl[0]); - pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl); - SHFree (pidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IExtractIconW) && (cidl == 1)) { - pidl = ILCombine (This->pidlRoot, apidl[0]); - pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl); - SHFree (pidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) { - hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, (LPVOID *) & pObj); - } else if ((IsEqualIID(riid,&IID_IShellLinkW) || IsEqualIID(riid,&IID_IShellLinkA)) - && (cidl == 1)) { - pidl = ILCombine (This->pidlRoot, apidl[0]); - hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj); - SHFree (pidl); - } else { - hr = E_NOINTERFACE; - } + if (IsEqualIID (riid, &IID_IContextMenu) && (cidl >= 1)) { + pObj = (LPUNKNOWN) ISvItemCm_Constructor ((IShellFolder *) iface, + This->pidlRoot, apidl, cidl); + hr = S_OK; + } else if (IsEqualIID (riid, &IID_IDataObject) && (cidl >= 1)) { + pObj = (LPUNKNOWN) IDataObject_Constructor (hwndOwner, + This->pidlRoot, apidl, cidl); + hr = S_OK; + } else if (IsEqualIID (riid, &IID_IExtractIconA) && (cidl == 1)) { + pidl = ILCombine (This->pidlRoot, apidl[0]); + pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl); + SHFree (pidl); + hr = S_OK; + } else if (IsEqualIID (riid, &IID_IExtractIconW) && (cidl == 1)) { + pidl = ILCombine (This->pidlRoot, apidl[0]); + pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl); + SHFree (pidl); + hr = S_OK; + } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) { + hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, + (LPVOID *) & pObj); + } else if ((IsEqualIID(riid,&IID_IShellLinkW) || + IsEqualIID(riid,&IID_IShellLinkA)) && (cidl == 1)) { + pidl = ILCombine (This->pidlRoot, apidl[0]); + hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj); + SHFree (pidl); + } else { + hr = E_NOINTERFACE; + } - if (SUCCEEDED(hr) && !pObj) - hr = E_OUTOFMEMORY; + if (SUCCEEDED(hr) && !pObj) + hr = E_OUTOFMEMORY; - *ppvOut = pObj; + *ppvOut = pObj; } TRACE ("(%p)->hr=0x%08lx\n", This, hr); return hr; } +static const WCHAR AdvancedW[] = { 'S','O','F','T','W','A','R','E', + '\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l', + 'o','r','e','r','\\','A','d','v','a','n','c','e','d',0 }; +static const WCHAR HideFileExtW[] = { 'H','i','d','e','F','i','l','e','E','x', + 't',0 }; +static const WCHAR NeverShowExtW[] = { 'N','e','v','e','r','S','h','o','w','E', + 'x','t',0 }; + void SHELL_FS_ProcessDisplayFilename(LPSTR szPath, DWORD dwFlags) { /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */ if (!(dwFlags & SHGDN_FORPARSING) && - ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) { - HKEY hKey; - DWORD dwData; - DWORD dwDataSize = sizeof (DWORD); - BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */ + ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) { + HKEY hKey; + DWORD dwData; + DWORD dwDataSize = sizeof (DWORD); + BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */ - if (!RegCreateKeyExA (HKEY_CURRENT_USER, - "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced", - 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) { - if (!RegQueryValueExA (hKey, "HideFileExt", 0, 0, (LPBYTE) & dwData, &dwDataSize)) - doHide = dwData; + if (!RegCreateKeyExW (HKEY_CURRENT_USER, AdvancedW, + 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) { + if (!RegQueryValueExW (hKey, HideFileExtW, 0, 0, (LPBYTE) &dwData, + &dwDataSize)) + doHide = dwData; - RegCloseKey (hKey); - } + RegCloseKey (hKey); + } - if (!doHide) { - LPSTR ext = PathFindExtensionA(szPath); + if (!doHide) { + LPSTR ext = PathFindExtensionA(szPath); - if (ext) { - HKEY hkey; - char classname[MAX_PATH]; - LONG classlen = MAX_PATH; + if (ext) { + char classname[MAX_PATH]; + LONG classlen = MAX_PATH; - if (!RegQueryValueA(HKEY_CLASSES_ROOT, ext, classname, &classlen)) - if (!RegOpenKeyA(HKEY_CLASSES_ROOT, classname, &hkey)) { - if (!RegQueryValueExA(hkey, "NeverShowExt", 0, NULL, NULL, NULL)) - doHide = TRUE; + if (!RegQueryValueA(HKEY_CLASSES_ROOT, ext, classname, + &classlen)) + if (!RegOpenKeyA(HKEY_CLASSES_ROOT, classname, &hKey)) { + if (!RegQueryValueExW(hKey, NeverShowExtW, 0, NULL, + NULL, NULL)) + doHide = TRUE; - RegCloseKey(hkey); - } - } - } + RegCloseKey(hKey); + } + } + } - if (doHide && szPath[0] != '.') - PathRemoveExtensionA (szPath); + if (doHide && szPath[0] != '.') + PathRemoveExtensionA (szPath); } } @@ -657,7 +743,8 @@ void SHELL_FS_ProcessDisplayFilename(LPSTR szPath, DWORD dwFlags) */ static HRESULT WINAPI -IShellFolder_fnGetDisplayNameOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet) +IShellFolder_fnGetDisplayNameOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, + DWORD dwFlags, LPSTRRET strRet) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) @@ -671,36 +758,40 @@ IShellFolder_fnGetDisplayNameOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, DWOR pdump (pidl); if (!pidl || !strRet) - return E_INVALIDARG; + return E_INVALIDARG; bSimplePidl = _ILIsPidlSimple (pidl); /* take names of special folders only if its only this folder */ if (_ILIsSpecialFolder (pidl)) { - if (bSimplePidl) { - _ILSimpleGetText (pidl, szPath, MAX_PATH); /* append my own path */ - } else { - FIXME ("special pidl\n"); - } + if (bSimplePidl) { + _ILSimpleGetText (pidl, szPath, MAX_PATH); /* append my own path */ + } else { + FIXME ("special pidl\n"); + } } else { - if (!(dwFlags & SHGDN_INFOLDER) && (dwFlags & SHGDN_FORPARSING) && This->sPathTarget) { - lstrcpyA (szPath, This->sPathTarget); /* get path to root */ - PathAddBackslashA (szPath); - len = lstrlenA (szPath); - } - _ILSimpleGetText (pidl, szPath + len, MAX_PATH - len); /* append my own path */ + if (!(dwFlags & SHGDN_INFOLDER) && (dwFlags & SHGDN_FORPARSING) && + This->sPathTarget) { + /* get path to root */ + lstrcpyA (szPath, This->sPathTarget); + PathAddBackslashA (szPath); + len = lstrlenA (szPath); + } + /* append my own path */ + _ILSimpleGetText (pidl, szPath + len, MAX_PATH - len); - if (!_ILIsFolder(pidl)) - SHELL_FS_ProcessDisplayFilename(szPath, dwFlags); + if (!_ILIsFolder(pidl)) + SHELL_FS_ProcessDisplayFilename(szPath, dwFlags); } - if ((dwFlags & SHGDN_FORPARSING) && !bSimplePidl) { /* go deeper if needed */ - PathAddBackslashA (szPath); - len = lstrlenA (szPath); + /* go deeper if needed */ + if ((dwFlags & SHGDN_FORPARSING) && !bSimplePidl) { + PathAddBackslashA (szPath); + len = lstrlenA (szPath); - if (!SUCCEEDED - (SHELL32_GetDisplayNameOfChild (iface, pidl, dwFlags | SHGDN_INFOLDER, szPath + len, MAX_PATH - len))) - return E_OUTOFMEMORY; + if (!SUCCEEDED(SHELL32_GetDisplayNameOfChild (iface, pidl, + dwFlags | SHGDN_INFOLDER, szPath + len, MAX_PATH - len))) + return E_OUTOFMEMORY; } strRet->uType = STRRET_CSTR; lstrcpynA (strRet->u.cStr, szPath, MAX_PATH); @@ -721,98 +812,113 @@ IShellFolder_fnGetDisplayNameOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, DWOR * DWORD dwFlags, //[in ] SHGNO formatting flags * LPITEMIDLIST* ppidlOut) //[out] simple pidl returned */ -static HRESULT WINAPI IShellFolder_fnSetNameOf (IShellFolder2 * iface, HWND hwndOwner, LPCITEMIDLIST pidl, /*simple pidl */ - LPCOLESTR lpName, DWORD dwFlags, LPITEMIDLIST * pPidlOut) +static HRESULT WINAPI IShellFolder_fnSetNameOf (IShellFolder2 * iface, + HWND hwndOwner, + LPCITEMIDLIST pidl, + LPCOLESTR lpName, + DWORD dwFlags, + LPITEMIDLIST * pPidlOut) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - char szSrc[MAX_PATH], - szDest[MAX_PATH]; - int len; + WCHAR szSrc[MAX_PATH], szDest[MAX_PATH]; + LPWSTR ptr; BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl)); - TRACE ("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", This, hwndOwner, pidl, debugstr_w (lpName), dwFlags, pPidlOut); + TRACE ("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", This, hwndOwner, pidl, + debugstr_w (lpName), dwFlags, pPidlOut); /* build source path */ if (dwFlags & SHGDN_INFOLDER) { - strcpy (szSrc, This->sPathTarget); - PathAddBackslashA (szSrc); - len = strlen (szSrc); - _ILSimpleGetText (pidl, szSrc + len, MAX_PATH - len); + MultiByteToWideChar(CP_ACP, 0, This->sPathTarget, -1, szSrc, MAX_PATH); + ptr = PathAddBackslashW (szSrc); + if (ptr) + _ILSimpleGetTextW (pidl, ptr, MAX_PATH - (ptr - szSrc)); } else { - /* FIXME: Can this work with a simple PIDL? */ - SHGetPathFromIDListA (pidl, szSrc); + /* FIXME: Can this work with a simple PIDL? */ + SHGetPathFromIDListW (pidl, szSrc); } /* build destination path */ - strcpy (szDest, This->sPathTarget); - PathAddBackslashA (szDest); - len = strlen (szDest); - WideCharToMultiByte (CP_ACP, 0, lpName, -1, szDest + len, MAX_PATH - len, NULL, NULL); - szDest[MAX_PATH - 1] = 0; - TRACE ("src=%s dest=%s\n", szSrc, szDest); - if (MoveFileA (szSrc, szDest)) { - HRESULT hr = S_OK; + MultiByteToWideChar(CP_ACP, 0, This->sPathTarget, -1, szDest, MAX_PATH); + ptr = PathAddBackslashW (szDest); + if (ptr) + lstrcpynW(ptr, lpName, MAX_PATH - (ptr - szDest)); + TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest)); + if (MoveFileW (szSrc, szDest)) { + HRESULT hr = S_OK; - if (pPidlOut) - hr = _ILCreateFromPathA(szDest, pPidlOut); + if (pPidlOut) + hr = _ILCreateFromPathW(szDest, pPidlOut); - SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, SHCNF_PATHA, szSrc, szDest); + SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, + SHCNF_PATHW, szSrc, szDest); - return hr; + return hr; } return E_FAIL; } -static HRESULT WINAPI IShellFolder_fnGetDefaultSearchGUID (IShellFolder2 * iface, GUID * pguid) +static HRESULT WINAPI IShellFolder_fnGetDefaultSearchGUID (IShellFolder2 *iface, + GUID * pguid) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - FIXME ("(%p)\n", This); + FIXME ("(%p)\n", This); return E_NOTIMPL; } -static HRESULT WINAPI IShellFolder_fnEnumSearches (IShellFolder2 * iface, IEnumExtraSearch ** ppenum) +static HRESULT WINAPI IShellFolder_fnEnumSearches (IShellFolder2 * iface, + IEnumExtraSearch ** ppenum) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - FIXME ("(%p)\n", This); + FIXME ("(%p)\n", This); return E_NOTIMPL; } + static HRESULT WINAPI -IShellFolder_fnGetDefaultColumn (IShellFolder2 * iface, DWORD dwRes, ULONG * pSort, ULONG * pDisplay) +IShellFolder_fnGetDefaultColumn (IShellFolder2 * iface, DWORD dwRes, + ULONG * pSort, ULONG * pDisplay) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - TRACE ("(%p)\n", This); + TRACE ("(%p)\n", This); if (pSort) - *pSort = 0; + *pSort = 0; if (pDisplay) - *pDisplay = 0; + *pDisplay = 0; return S_OK; } -static HRESULT WINAPI IShellFolder_fnGetDefaultColumnState (IShellFolder2 * iface, UINT iColumn, DWORD * pcsFlags) + +static HRESULT WINAPI +IShellFolder_fnGetDefaultColumnState (IShellFolder2 * iface, UINT iColumn, + DWORD * pcsFlags) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - TRACE ("(%p)\n", This); + TRACE ("(%p)\n", This); if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS) - return E_INVALIDARG; + return E_INVALIDARG; *pcsFlags = GenericSFHeader[iColumn].pcsFlags; return S_OK; } + static HRESULT WINAPI -IShellFolder_fnGetDetailsEx (IShellFolder2 * iface, LPCITEMIDLIST pidl, const SHCOLUMNID * pscid, VARIANT * pv) +IShellFolder_fnGetDetailsEx (IShellFolder2 * iface, LPCITEMIDLIST pidl, + const SHCOLUMNID * pscid, VARIANT * pv) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) - FIXME ("(%p)\n", This); + FIXME ("(%p)\n", This); return E_NOTIMPL; } + static HRESULT WINAPI -IShellFolder_fnGetDetailsOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS * psd) +IShellFolder_fnGetDetailsOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, + UINT iColumn, SHELLDETAILS * psd) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) HRESULT hr = E_FAIL; @@ -820,41 +926,46 @@ IShellFolder_fnGetDetailsOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, UINT iCo TRACE ("(%p)->(%p %i %p)\n", This, pidl, iColumn, psd); if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS) - return E_INVALIDARG; + return E_INVALIDARG; if (!pidl) { - /* the header titles */ - psd->fmt = GenericSFHeader[iColumn].fmt; - psd->cxChar = GenericSFHeader[iColumn].cxChar; - psd->str.uType = STRRET_CSTR; - LoadStringA (shell32_hInstance, GenericSFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH); - return S_OK; + /* the header titles */ + psd->fmt = GenericSFHeader[iColumn].fmt; + psd->cxChar = GenericSFHeader[iColumn].cxChar; + psd->str.uType = STRRET_CSTR; + LoadStringA (shell32_hInstance, GenericSFHeader[iColumn].colnameid, + psd->str.u.cStr, MAX_PATH); + return S_OK; } else { - /* the data from the pidl */ - switch (iColumn) { - case 0: /* name */ - hr = IShellFolder_GetDisplayNameOf (iface, pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); - break; - case 1: /* size */ - _ILGetFileSize (pidl, psd->str.u.cStr, MAX_PATH); - break; - case 2: /* type */ - _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH); - break; - case 3: /* date */ - _ILGetFileDate (pidl, psd->str.u.cStr, MAX_PATH); - break; - case 4: /* attributes */ - _ILGetFileAttributes (pidl, psd->str.u.cStr, MAX_PATH); - break; - } - hr = S_OK; - psd->str.uType = STRRET_CSTR; + /* the data from the pidl */ + switch (iColumn) { + case 0: /* name */ + hr = IShellFolder_GetDisplayNameOf (iface, pidl, + SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); + break; + case 1: /* size */ + _ILGetFileSize (pidl, psd->str.u.cStr, MAX_PATH); + break; + case 2: /* type */ + _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH); + break; + case 3: /* date */ + _ILGetFileDate (pidl, psd->str.u.cStr, MAX_PATH); + break; + case 4: /* attributes */ + _ILGetFileAttributes (pidl, psd->str.u.cStr, MAX_PATH); + break; + } + hr = S_OK; + psd->str.uType = STRRET_CSTR; } return hr; } -static HRESULT WINAPI IShellFolder_fnMapColumnToSCID (IShellFolder2 * iface, UINT column, SHCOLUMNID * pscid) + +static HRESULT WINAPI +IShellFolder_fnMapColumnToSCID (IShellFolder2 * iface, UINT column, + SHCOLUMNID * pscid) { _ICOM_THIS_From_IShellFolder2 (IGenericSFImpl, iface) FIXME ("(%p)\n", This); @@ -863,34 +974,35 @@ static HRESULT WINAPI IShellFolder_fnMapColumnToSCID (IShellFolder2 * iface, UIN static IShellFolder2Vtbl sfvt = { - IShellFolder_fnQueryInterface, - IShellFolder_fnAddRef, - IShellFolder_fnRelease, - IShellFolder_fnParseDisplayName, - IShellFolder_fnEnumObjects, - IShellFolder_fnBindToObject, - IShellFolder_fnBindToStorage, - IShellFolder_fnCompareIDs, - IShellFolder_fnCreateViewObject, - IShellFolder_fnGetAttributesOf, - IShellFolder_fnGetUIObjectOf, - IShellFolder_fnGetDisplayNameOf, - IShellFolder_fnSetNameOf, - /* ShellFolder2 */ - IShellFolder_fnGetDefaultSearchGUID, - IShellFolder_fnEnumSearches, - IShellFolder_fnGetDefaultColumn, - IShellFolder_fnGetDefaultColumnState, - IShellFolder_fnGetDetailsEx, - IShellFolder_fnGetDetailsOf, - IShellFolder_fnMapColumnToSCID + IShellFolder_fnQueryInterface, + IShellFolder_fnAddRef, + IShellFolder_fnRelease, + IShellFolder_fnParseDisplayName, + IShellFolder_fnEnumObjects, + IShellFolder_fnBindToObject, + IShellFolder_fnBindToStorage, + IShellFolder_fnCompareIDs, + IShellFolder_fnCreateViewObject, + IShellFolder_fnGetAttributesOf, + IShellFolder_fnGetUIObjectOf, + IShellFolder_fnGetDisplayNameOf, + IShellFolder_fnSetNameOf, + /* ShellFolder2 */ + IShellFolder_fnGetDefaultSearchGUID, + IShellFolder_fnEnumSearches, + IShellFolder_fnGetDefaultColumn, + IShellFolder_fnGetDefaultColumnState, + IShellFolder_fnGetDetailsEx, + IShellFolder_fnGetDetailsOf, + IShellFolder_fnMapColumnToSCID }; /**************************************************************************** * ISFHelper for IShellFolder implementation */ -static HRESULT WINAPI ISFHelper_fnQueryInterface (ISFHelper * iface, REFIID riid, LPVOID * ppvObj) +static HRESULT WINAPI +ISFHelper_fnQueryInterface (ISFHelper * iface, REFIID riid, LPVOID * ppvObj) { _ICOM_THIS_From_ISFHelper (IGenericSFImpl, iface); @@ -923,7 +1035,8 @@ static ULONG WINAPI ISFHelper_fnRelease (ISFHelper * iface) * creates a unique folder name */ -static HRESULT WINAPI ISFHelper_fnGetUniqueName (ISFHelper * iface, LPSTR lpName, UINT uLen) +static HRESULT WINAPI +ISFHelper_fnGetUniqueName (ISFHelper * iface, LPSTR lpName, UINT uLen) { _ICOM_THIS_From_ISFHelper (IGenericSFImpl, iface) IEnumIDList *penum; @@ -934,31 +1047,33 @@ static HRESULT WINAPI ISFHelper_fnGetUniqueName (ISFHelper * iface, LPSTR lpName TRACE ("(%p)(%s %u)\n", This, lpName, uLen); if (uLen < strlen (szNewFolder) + 4) - return E_POINTER; + return E_POINTER; strcpy (lpName, szNewFolder); hr = IShellFolder_fnEnumObjects (_IShellFolder2_ (This), 0, - SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penum); + SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penum); if (penum) { - LPITEMIDLIST pidl; - DWORD dwFetched; - int i = 1; + LPITEMIDLIST pidl; + DWORD dwFetched; + int i = 1; - next:IEnumIDList_Reset (penum); - while (S_OK == IEnumIDList_Next (penum, 1, &pidl, &dwFetched) && dwFetched) { - _ILSimpleGetText (pidl, szText, MAX_PATH); - if (0 == strcasecmp (szText, lpName)) { - sprintf (lpName, "%s %d", szNewFolder, i++); - if (i > 99) { - hr = E_FAIL; - break; - } - goto next; - } - } +next: + IEnumIDList_Reset (penum); + while (S_OK == IEnumIDList_Next (penum, 1, &pidl, &dwFetched) && + dwFetched) { + _ILSimpleGetText (pidl, szText, MAX_PATH); + if (0 == strcasecmp (szText, lpName)) { + sprintf (lpName, "%s %d", szNewFolder, i++); + if (i > 99) { + hr = E_FAIL; + break; + } + goto next; + } + } - IEnumIDList_Release (penum); + IEnumIDList_Release (penum); } return hr; } @@ -969,7 +1084,9 @@ static HRESULT WINAPI ISFHelper_fnGetUniqueName (ISFHelper * iface, LPSTR lpName * adds a new folder. */ -static HRESULT WINAPI ISFHelper_fnAddFolder (ISFHelper * iface, HWND hwnd, LPCSTR lpName, LPITEMIDLIST * ppidlOut) +static HRESULT WINAPI +ISFHelper_fnAddFolder (ISFHelper * iface, HWND hwnd, LPCSTR lpName, + LPITEMIDLIST * ppidlOut) { _ICOM_THIS_From_ISFHelper (IGenericSFImpl, iface) char lpstrNewDir[MAX_PATH]; @@ -983,22 +1100,24 @@ static HRESULT WINAPI ISFHelper_fnAddFolder (ISFHelper * iface, HWND hwnd, LPCST bRes = CreateDirectoryA (lpstrNewDir, NULL); if (bRes) { - SHChangeNotify (SHCNE_MKDIR, SHCNF_PATHA, lpstrNewDir, NULL); + SHChangeNotify (SHCNE_MKDIR, SHCNF_PATHA, lpstrNewDir, NULL); - hres = S_OK; + hres = S_OK; - if (ppidlOut) - hres = _ILCreateFromPathA(lpstrNewDir, ppidlOut); + if (ppidlOut) + hres = _ILCreateFromPathA(lpstrNewDir, ppidlOut); } else { - char lpstrText[128 + MAX_PATH]; - char lpstrTempText[128]; - char lpstrCaption[256]; + char lpstrText[128 + MAX_PATH]; + char lpstrTempText[128]; + char lpstrCaption[256]; - /* Cannot Create folder because of permissions */ - LoadStringA (shell32_hInstance, IDS_CREATEFOLDER_DENIED, lpstrTempText, sizeof (lpstrTempText)); - LoadStringA (shell32_hInstance, IDS_CREATEFOLDER_CAPTION, lpstrCaption, sizeof (lpstrCaption)); - sprintf (lpstrText, lpstrTempText, lpstrNewDir); - MessageBoxA (hwnd, lpstrText, lpstrCaption, MB_OK | MB_ICONEXCLAMATION); + /* Cannot Create folder because of permissions */ + LoadStringA (shell32_hInstance, IDS_CREATEFOLDER_DENIED, lpstrTempText, + sizeof (lpstrTempText)); + LoadStringA (shell32_hInstance, IDS_CREATEFOLDER_CAPTION, lpstrCaption, + sizeof (lpstrCaption)); + sprintf (lpstrText, lpstrTempText, lpstrNewDir); + MessageBoxA (hwnd, lpstrText, lpstrCaption, MB_OK | MB_ICONEXCLAMATION); } return hres; @@ -1009,7 +1128,8 @@ static HRESULT WINAPI ISFHelper_fnAddFolder (ISFHelper * iface, HWND hwnd, LPCST * * deletes items in folder */ -static HRESULT WINAPI ISFHelper_fnDeleteItems (ISFHelper * iface, UINT cidl, LPCITEMIDLIST * apidl) +static HRESULT WINAPI +ISFHelper_fnDeleteItems (ISFHelper * iface, UINT cidl, LPCITEMIDLIST * apidl) { _ICOM_THIS_From_ISFHelper (IGenericSFImpl, iface) UINT i; @@ -1020,42 +1140,42 @@ static HRESULT WINAPI ISFHelper_fnDeleteItems (ISFHelper * iface, UINT cidl, LPC /* deleting multiple items so give a slightly different warning */ if (cidl != 1) { - char tmp[8]; + char tmp[8]; - snprintf (tmp, sizeof (tmp), "%d", cidl); - if (!SHELL_ConfirmDialog(ASK_DELETE_MULTIPLE_ITEM, tmp)) - return E_FAIL; - bConfirm = FALSE; + snprintf (tmp, sizeof (tmp), "%d", cidl); + if (!SHELL_ConfirmDialog(ASK_DELETE_MULTIPLE_ITEM, tmp)) + return E_FAIL; + bConfirm = FALSE; } for (i = 0; i < cidl; i++) { - strcpy (szPath, This->sPathTarget); - PathAddBackslashA (szPath); - _ILSimpleGetText (apidl[i], szPath + strlen (szPath), MAX_PATH); + strcpy (szPath, This->sPathTarget); + PathAddBackslashA (szPath); + _ILSimpleGetText (apidl[i], szPath + strlen (szPath), MAX_PATH); - if (_ILIsFolder (apidl[i])) { - LPITEMIDLIST pidl; + if (_ILIsFolder (apidl[i])) { + LPITEMIDLIST pidl; - TRACE ("delete %s\n", szPath); - if (!SHELL_DeleteDirectoryA (szPath, bConfirm)) { - TRACE ("delete %s failed, bConfirm=%d\n", szPath, bConfirm); - return E_FAIL; - } - pidl = ILCombine (This->pidlRoot, apidl[i]); - SHChangeNotify (SHCNE_RMDIR, SHCNF_IDLIST, pidl, NULL); - SHFree (pidl); - } else if (_ILIsValue (apidl[i])) { - LPITEMIDLIST pidl; + TRACE ("delete %s\n", szPath); + if (!SHELL_DeleteDirectoryA (szPath, bConfirm)) { + TRACE ("delete %s failed, bConfirm=%d\n", szPath, bConfirm); + return E_FAIL; + } + pidl = ILCombine (This->pidlRoot, apidl[i]); + SHChangeNotify (SHCNE_RMDIR, SHCNF_IDLIST, pidl, NULL); + SHFree (pidl); + } else if (_ILIsValue (apidl[i])) { + LPITEMIDLIST pidl; - TRACE ("delete %s\n", szPath); - if (!SHELL_DeleteFileA (szPath, bConfirm)) { - TRACE ("delete %s failed, bConfirm=%d\n", szPath, bConfirm); - return E_FAIL; - } - pidl = ILCombine (This->pidlRoot, apidl[i]); - SHChangeNotify (SHCNE_DELETE, SHCNF_IDLIST, pidl, NULL); - SHFree (pidl); - } + TRACE ("delete %s\n", szPath); + if (!SHELL_DeleteFileA (szPath, bConfirm)) { + TRACE ("delete %s failed, bConfirm=%d\n", szPath, bConfirm); + return E_FAIL; + } + pidl = ILCombine (This->pidlRoot, apidl[i]); + SHChangeNotify (SHCNE_DELETE, SHCNF_IDLIST, pidl, NULL); + SHFree (pidl); + } } return S_OK; @@ -1067,7 +1187,8 @@ static HRESULT WINAPI ISFHelper_fnDeleteItems (ISFHelper * iface, UINT cidl, LPC * copies items to this folder */ static HRESULT WINAPI -ISFHelper_fnCopyItems (ISFHelper * iface, IShellFolder * pSFFrom, UINT cidl, LPCITEMIDLIST * apidl) +ISFHelper_fnCopyItems (ISFHelper * iface, IShellFolder * pSFFrom, UINT cidl, + LPCITEMIDLIST * apidl) { UINT i; IPersistFolder2 *ppf2 = NULL; @@ -1078,44 +1199,49 @@ ISFHelper_fnCopyItems (ISFHelper * iface, IShellFolder * pSFFrom, UINT cidl, LPC TRACE ("(%p)->(%p,%u,%p)\n", This, pSFFrom, cidl, apidl); - IShellFolder_QueryInterface (pSFFrom, &IID_IPersistFolder2, (LPVOID *) & ppf2); + IShellFolder_QueryInterface (pSFFrom, &IID_IPersistFolder2, + (LPVOID *) & ppf2); if (ppf2) { - LPITEMIDLIST pidl; + LPITEMIDLIST pidl; - if (SUCCEEDED (IPersistFolder2_GetCurFolder (ppf2, &pidl))) { - for (i = 0; i < cidl; i++) { - SHGetPathFromIDListA (pidl, szSrcPath); - PathAddBackslashA (szSrcPath); - _ILSimpleGetText (apidl[i], szSrcPath + strlen (szSrcPath), MAX_PATH); + if (SUCCEEDED (IPersistFolder2_GetCurFolder (ppf2, &pidl))) { + for (i = 0; i < cidl; i++) { + SHGetPathFromIDListA (pidl, szSrcPath); + PathAddBackslashA (szSrcPath); + _ILSimpleGetText (apidl[i], szSrcPath + strlen (szSrcPath), + MAX_PATH); - strcpy (szDstPath, This->sPathTarget); - PathAddBackslashA (szDstPath); - _ILSimpleGetText (apidl[i], szDstPath + strlen (szDstPath), MAX_PATH); - MESSAGE ("would copy %s to %s\n", szSrcPath, szDstPath); - } - SHFree (pidl); - } - IPersistFolder2_Release (ppf2); + strcpy (szDstPath, This->sPathTarget); + PathAddBackslashA (szDstPath); + _ILSimpleGetText (apidl[i], szDstPath + strlen (szDstPath), + MAX_PATH); + MESSAGE ("would copy %s to %s\n", szSrcPath, szDstPath); + } + SHFree (pidl); + } + IPersistFolder2_Release (ppf2); } return S_OK; } static ISFHelperVtbl shvt = { - ISFHelper_fnQueryInterface, - ISFHelper_fnAddRef, - ISFHelper_fnRelease, - ISFHelper_fnGetUniqueName, - ISFHelper_fnAddFolder, - ISFHelper_fnDeleteItems, - ISFHelper_fnCopyItems + ISFHelper_fnQueryInterface, + ISFHelper_fnAddRef, + ISFHelper_fnRelease, + ISFHelper_fnGetUniqueName, + ISFHelper_fnAddFolder, + ISFHelper_fnDeleteItems, + ISFHelper_fnCopyItems }; /************************************************************************ - * IFSFldr_PersistFolder3_QueryInterface + * IFSFldr_PersistFolder3_QueryInterface * */ -static HRESULT WINAPI IFSFldr_PersistFolder3_QueryInterface (IPersistFolder3 * iface, REFIID iid, LPVOID * ppvObj) +static HRESULT WINAPI +IFSFldr_PersistFolder3_QueryInterface (IPersistFolder3 * iface, REFIID iid, + LPVOID * ppvObj) { _ICOM_THIS_From_IPersistFolder3 (IGenericSFImpl, iface); @@ -1125,10 +1251,11 @@ static HRESULT WINAPI IFSFldr_PersistFolder3_QueryInterface (IPersistFolder3 * i } /************************************************************************ - * IFSFldr_PersistFolder3_AddRef + * IFSFldr_PersistFolder3_AddRef * */ -static ULONG WINAPI IFSFldr_PersistFolder3_AddRef (IPersistFolder3 * iface) +static ULONG WINAPI +IFSFldr_PersistFolder3_AddRef (IPersistFolder3 * iface) { _ICOM_THIS_From_IPersistFolder3 (IGenericSFImpl, iface); @@ -1138,10 +1265,11 @@ static ULONG WINAPI IFSFldr_PersistFolder3_AddRef (IPersistFolder3 * iface) } /************************************************************************ - * IFSFldr_PersistFolder3_Release + * IFSFldr_PersistFolder3_Release * */ -static ULONG WINAPI IFSFldr_PersistFolder3_Release (IPersistFolder3 * iface) +static ULONG WINAPI +IFSFldr_PersistFolder3_Release (IPersistFolder3 * iface) { _ICOM_THIS_From_IPersistFolder3 (IGenericSFImpl, iface); @@ -1151,28 +1279,30 @@ static ULONG WINAPI IFSFldr_PersistFolder3_Release (IPersistFolder3 * iface) } /************************************************************************ - * IFSFldr_PersistFolder3_GetClassID + * IFSFldr_PersistFolder3_GetClassID */ -static HRESULT WINAPI IFSFldr_PersistFolder3_GetClassID (IPersistFolder3 * iface, CLSID * lpClassId) +static HRESULT WINAPI +IFSFldr_PersistFolder3_GetClassID (IPersistFolder3 * iface, CLSID * lpClassId) { _ICOM_THIS_From_IPersistFolder3 (IGenericSFImpl, iface); TRACE ("(%p)\n", This); if (!lpClassId) - return E_POINTER; + return E_POINTER; *lpClassId = *This->pclsid; return S_OK; } /************************************************************************ - * IFSFldr_PersistFolder3_Initialize + * IFSFldr_PersistFolder3_Initialize * * NOTES * sPathTarget is not set. Don't know how to handle in a non rooted environment. */ -static HRESULT WINAPI IFSFldr_PersistFolder3_Initialize (IPersistFolder3 * iface, LPCITEMIDLIST pidl) +static HRESULT WINAPI +IFSFldr_PersistFolder3_Initialize (IPersistFolder3 * iface, LPCITEMIDLIST pidl) { char sTemp[MAX_PATH]; @@ -1181,16 +1311,16 @@ static HRESULT WINAPI IFSFldr_PersistFolder3_Initialize (IPersistFolder3 * iface TRACE ("(%p)->(%p)\n", This, pidl); if (This->pidlRoot) - SHFree (This->pidlRoot); /* free the old pidl */ - This->pidlRoot = ILClone (pidl); /* set my pidl */ + SHFree (This->pidlRoot); /* free the old pidl */ + This->pidlRoot = ILClone (pidl); /* set my pidl */ if (This->sPathTarget) - SHFree (This->sPathTarget); + SHFree (This->sPathTarget); /* set my path */ if (SHGetPathFromIDListA (pidl, sTemp)) { - This->sPathTarget = SHAlloc (strlen (sTemp) + 1); - strcpy (This->sPathTarget, sTemp); + This->sPathTarget = SHAlloc (strlen (sTemp) + 1); + strcpy (This->sPathTarget, sTemp); } TRACE ("--(%p)->(%s)\n", This, This->sPathTarget); @@ -1198,9 +1328,11 @@ static HRESULT WINAPI IFSFldr_PersistFolder3_Initialize (IPersistFolder3 * iface } /************************************************************************** - * IFSFldr_PersistFolder3_GetCurFolder + * IFSFldr_PersistFolder3_GetCurFolder */ -static HRESULT WINAPI IFSFldr_PersistFolder3_fnGetCurFolder (IPersistFolder3 * iface, LPITEMIDLIST * pidl) +static HRESULT WINAPI +IFSFldr_PersistFolder3_fnGetCurFolder (IPersistFolder3 * iface, + LPITEMIDLIST * pidl) { _ICOM_THIS_From_IPersistFolder3 (IGenericSFImpl, iface); @@ -1212,13 +1344,14 @@ static HRESULT WINAPI IFSFldr_PersistFolder3_fnGetCurFolder (IPersistFolder3 * i } /************************************************************************** - * IFSFldr_PersistFolder3_InitializeEx + * IFSFldr_PersistFolder3_InitializeEx * - * FIXME: errorhandling + * FIXME: error handling */ static HRESULT WINAPI IFSFldr_PersistFolder3_InitializeEx (IPersistFolder3 * iface, - IBindCtx * pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO * ppfti) + IBindCtx * pbc, LPCITEMIDLIST pidlRoot, + const PERSIST_FOLDER_TARGET_INFO * ppfti) { char sTemp[MAX_PATH]; @@ -1226,18 +1359,19 @@ IFSFldr_PersistFolder3_InitializeEx (IPersistFolder3 * iface, TRACE ("(%p)->(%p,%p,%p)\n", This, pbc, pidlRoot, ppfti); if (ppfti) - TRACE ("--%p %s %s 0x%08lx 0x%08x\n", - ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName), - debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes, ppfti->csidl); + TRACE ("--%p %s %s 0x%08lx 0x%08x\n", + ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName), + debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes, + ppfti->csidl); pdump (pidlRoot); if (ppfti && ppfti->pidlTargetFolder) - pdump (ppfti->pidlTargetFolder); + pdump (ppfti->pidlTargetFolder); if (This->pidlRoot) - __SHFreeAndNil (&This->pidlRoot); /* free the old */ + __SHFreeAndNil (&This->pidlRoot); /* free the old */ if (This->sPathTarget) - __SHFreeAndNil (&This->sPathTarget); + __SHFreeAndNil (&This->sPathTarget); /* * Root path and pidl @@ -1245,20 +1379,22 @@ IFSFldr_PersistFolder3_InitializeEx (IPersistFolder3 * iface, This->pidlRoot = ILClone (pidlRoot); /* - * the target folder is spezified in csidl OR pidlTargetFolder OR szTargetParsingName + * the target folder is spezified in csidl OR pidlTargetFolder OR + * szTargetParsingName */ if (ppfti) { - if (ppfti->csidl != -1) { - if (SHGetSpecialFolderPathA (0, sTemp, ppfti->csidl, ppfti->csidl & CSIDL_FLAG_CREATE)) { - __SHCloneStrA (&This->sPathTarget, sTemp); - } - } else if (ppfti->szTargetParsingName[0]) { - __SHCloneStrWtoA (&This->sPathTarget, ppfti->szTargetParsingName); - } else if (ppfti->pidlTargetFolder) { - if (SHGetPathFromIDListA (ppfti->pidlTargetFolder, sTemp)) { - __SHCloneStrA (&This->sPathTarget, sTemp); - } - } + if (ppfti->csidl != -1) { + if (SHGetSpecialFolderPathA (0, sTemp, ppfti->csidl, + ppfti->csidl & CSIDL_FLAG_CREATE)) { + __SHCloneStrA (&This->sPathTarget, sTemp); + } + } else if (ppfti->szTargetParsingName[0]) { + __SHCloneStrWtoA (&This->sPathTarget, ppfti->szTargetParsingName); + } else if (ppfti->pidlTargetFolder) { + if (SHGetPathFromIDListA (ppfti->pidlTargetFolder, sTemp)) { + __SHCloneStrA (&This->sPathTarget, sTemp); + } + } } TRACE ("--(%p)->(target=%s)\n", This, debugstr_a (This->sPathTarget)); @@ -1267,7 +1403,8 @@ IFSFldr_PersistFolder3_InitializeEx (IPersistFolder3 * iface, } static HRESULT WINAPI -IFSFldr_PersistFolder3_GetFolderTargetInfo (IPersistFolder3 * iface, PERSIST_FOLDER_TARGET_INFO * ppfti) +IFSFldr_PersistFolder3_GetFolderTargetInfo (IPersistFolder3 * iface, + PERSIST_FOLDER_TARGET_INFO * ppfti) { _ICOM_THIS_From_IPersistFolder3 (IGenericSFImpl, iface); FIXME ("(%p)->(%p)\n", This, ppfti); @@ -1277,20 +1414,22 @@ IFSFldr_PersistFolder3_GetFolderTargetInfo (IPersistFolder3 * iface, PERSIST_FOL static IPersistFolder3Vtbl vt_FSFldr_PersistFolder3 = { - IFSFldr_PersistFolder3_QueryInterface, - IFSFldr_PersistFolder3_AddRef, - IFSFldr_PersistFolder3_Release, - IFSFldr_PersistFolder3_GetClassID, - IFSFldr_PersistFolder3_Initialize, - IFSFldr_PersistFolder3_fnGetCurFolder, - IFSFldr_PersistFolder3_InitializeEx, - IFSFldr_PersistFolder3_GetFolderTargetInfo + IFSFldr_PersistFolder3_QueryInterface, + IFSFldr_PersistFolder3_AddRef, + IFSFldr_PersistFolder3_Release, + IFSFldr_PersistFolder3_GetClassID, + IFSFldr_PersistFolder3_Initialize, + IFSFldr_PersistFolder3_fnGetCurFolder, + IFSFldr_PersistFolder3_InitializeEx, + IFSFldr_PersistFolder3_GetFolderTargetInfo }; /**************************************************************************** * ISFDropTarget implementation */ -static BOOL ISFDropTarget_QueryDrop (IDropTarget * iface, DWORD dwKeyState, LPDWORD pdwEffect) +static BOOL +ISFDropTarget_QueryDrop (IDropTarget * iface, DWORD dwKeyState, + LPDWORD pdwEffect) { DWORD dwEffect = *pdwEffect; @@ -1298,18 +1437,19 @@ static BOOL ISFDropTarget_QueryDrop (IDropTarget * iface, DWORD dwKeyState, LPDW *pdwEffect = DROPEFFECT_NONE; - if (This->fAcceptFmt) { /* Does our interpretation of the keystate ... */ - *pdwEffect = KeyStateToDropEffect (dwKeyState); + if (This->fAcceptFmt) { /* Does our interpretation of the keystate ... */ + *pdwEffect = KeyStateToDropEffect (dwKeyState); - /* ... matches the desired effect ? */ - if (dwEffect & *pdwEffect) { - return TRUE; - } + /* ... matches the desired effect ? */ + if (dwEffect & *pdwEffect) { + return TRUE; + } } return FALSE; } -static HRESULT WINAPI ISFDropTarget_QueryInterface (IDropTarget * iface, REFIID riid, LPVOID * ppvObj) +static HRESULT WINAPI +ISFDropTarget_QueryInterface (IDropTarget * iface, REFIID riid, LPVOID * ppvObj) { _ICOM_THIS_From_IDropTarget (IGenericSFImpl, iface); @@ -1337,7 +1477,8 @@ static ULONG WINAPI ISFDropTarget_Release (IDropTarget * iface) } static HRESULT WINAPI -ISFDropTarget_DragEnter (IDropTarget * iface, IDataObject * pDataObject, DWORD dwKeyState, POINTL pt, DWORD * pdwEffect) +ISFDropTarget_DragEnter (IDropTarget * iface, IDataObject * pDataObject, + DWORD dwKeyState, POINTL pt, DWORD * pdwEffect) { FORMATETC fmt; @@ -1347,21 +1488,24 @@ ISFDropTarget_DragEnter (IDropTarget * iface, IDataObject * pDataObject, DWORD d InitFormatEtc (fmt, This->cfShellIDList, TYMED_HGLOBAL); - This->fAcceptFmt = (S_OK == IDataObject_QueryGetData (pDataObject, &fmt)) ? TRUE : FALSE; + This->fAcceptFmt = (S_OK == IDataObject_QueryGetData (pDataObject, &fmt)) ? + TRUE : FALSE; ISFDropTarget_QueryDrop (iface, dwKeyState, pdwEffect); return S_OK; } -static HRESULT WINAPI ISFDropTarget_DragOver (IDropTarget * iface, DWORD dwKeyState, POINTL pt, DWORD * pdwEffect) +static HRESULT WINAPI +ISFDropTarget_DragOver (IDropTarget * iface, DWORD dwKeyState, POINTL pt, + DWORD * pdwEffect) { _ICOM_THIS_From_IDropTarget (IGenericSFImpl, iface); TRACE ("(%p)\n", This); if (!pdwEffect) - return E_INVALIDARG; + return E_INVALIDARG; ISFDropTarget_QueryDrop (iface, dwKeyState, pdwEffect); @@ -1380,7 +1524,8 @@ static HRESULT WINAPI ISFDropTarget_DragLeave (IDropTarget * iface) } static HRESULT WINAPI -ISFDropTarget_Drop (IDropTarget * iface, IDataObject * pDataObject, DWORD dwKeyState, POINTL pt, DWORD * pdwEffect) +ISFDropTarget_Drop (IDropTarget * iface, IDataObject * pDataObject, + DWORD dwKeyState, POINTL pt, DWORD * pdwEffect) { _ICOM_THIS_From_IDropTarget (IGenericSFImpl, iface); @@ -1390,11 +1535,11 @@ ISFDropTarget_Drop (IDropTarget * iface, IDataObject * pDataObject, DWORD dwKeyS } static struct IDropTargetVtbl dtvt = { - ISFDropTarget_QueryInterface, - ISFDropTarget_AddRef, - ISFDropTarget_Release, - ISFDropTarget_DragEnter, - ISFDropTarget_DragOver, - ISFDropTarget_DragLeave, - ISFDropTarget_Drop + ISFDropTarget_QueryInterface, + ISFDropTarget_AddRef, + ISFDropTarget_Release, + ISFDropTarget_DragEnter, + ISFDropTarget_DragOver, + ISFDropTarget_DragLeave, + ISFDropTarget_Drop }; diff --git a/reactos/lib/shell32/shfldr_mycomp.c b/reactos/lib/shell32/shfldr_mycomp.c index cacfcfbd68c..ef6a23eb206 100644 --- a/reactos/lib/shell32/shfldr_mycomp.c +++ b/reactos/lib/shell32/shfldr_mycomp.c @@ -1,9 +1,8 @@ - /* - * Virtual Workplace folder + * Virtual Workplace folder * - * Copyright 1997 Marcus Meissner - * Copyright 1998, 1999, 2002 Juergen Schmied + * Copyright 1997 Marcus Meissner + * Copyright 1998, 1999, 2002 Juergen Schmied * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -61,8 +60,8 @@ typedef struct { IPersistFolder2Vtbl *lpVtblPersistFolder2; /* both paths are parsible from the desktop */ - LPITEMIDLIST pidlRoot; /* absolute pidl */ - int dwAttributes; /* attributes returned by GetAttributesOf FIXME: use it */ + LPITEMIDLIST pidlRoot; /* absolute pidl */ + int dwAttributes; /* attributes returned by GetAttributesOf FIXME: use it */ } IGenericSFImpl; static struct IShellFolder2Vtbl vt_ShellFolder2; @@ -74,13 +73,13 @@ static struct IPersistFolder2Vtbl vt_PersistFolder2; /* converts This to a interface pointer */ -#define _IUnknown_(This) (IUnknown*)&(This->lpVtbl) -#define _IShellFolder_(This) (IShellFolder*)&(This->lpVtbl) -#define _IShellFolder2_(This) (IShellFolder2*)&(This->lpVtbl) +#define _IUnknown_(This) (IUnknown*)&(This->lpVtbl) +#define _IShellFolder_(This) (IShellFolder*)&(This->lpVtbl) +#define _IShellFolder2_(This) (IShellFolder2*)&(This->lpVtbl) -#define _IPersist_(This) (IPersist*)&(This->lpVtblPersistFolder2) -#define _IPersistFolder_(This) (IPersistFolder*)&(This->lpVtblPersistFolder2) -#define _IPersistFolder2_(This) (IPersistFolder2*)&(This->lpVtblPersistFolder2) +#define _IPersist_(This) (IPersist*)&(This->lpVtblPersistFolder2) +#define _IPersistFolder_(This) (IPersistFolder*)&(This->lpVtblPersistFolder2) +#define _IPersistFolder2_(This) (IPersistFolder2*)&(This->lpVtblPersistFolder2) /*********************************************************************** * IShellFolder [MyComputer] implementation @@ -96,7 +95,7 @@ static shvheader MyComputerSFHeader[] = { #define MYCOMPUTERSHELLVIEWCOLUMNS 4 /************************************************************************** -* ISF_MyComputer_Constructor +* ISF_MyComputer_Constructor */ HRESULT WINAPI ISF_MyComputer_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv) { @@ -105,22 +104,23 @@ HRESULT WINAPI ISF_MyComputer_Constructor (IUnknown * pUnkOuter, REFIID riid, LP TRACE ("unkOut=%p %s\n", pUnkOuter, shdebugstr_guid (riid)); if (!ppv) - return E_POINTER; + return E_POINTER; if (pUnkOuter) - return CLASS_E_NOAGGREGATION; + return CLASS_E_NOAGGREGATION; - sf = (IGenericSFImpl *) LocalAlloc (GMEM_ZEROINIT, sizeof (IGenericSFImpl)); + sf = LocalAlloc (GMEM_ZEROINIT, sizeof (IGenericSFImpl)); if (!sf) - return E_OUTOFMEMORY; + return E_OUTOFMEMORY; sf->ref = 0; sf->lpVtbl = &vt_ShellFolder2; sf->lpVtblPersistFolder2 = &vt_PersistFolder2; - sf->pidlRoot = _ILCreateMyComputer (); /* my qualified pidl */ + sf->pidlRoot = _ILCreateMyComputer (); /* my qualified pidl */ - if (!SUCCEEDED (IUnknown_QueryInterface (_IUnknown_ (sf), riid, ppv))) { - IUnknown_Release (_IUnknown_ (sf)); - return E_NOINTERFACE; + if (!SUCCEEDED (IUnknown_QueryInterface (_IUnknown_ (sf), riid, ppv))) + { + IUnknown_Release (_IUnknown_ (sf)); + return E_NOINTERFACE; } TRACE ("--(%p)\n", sf); @@ -128,11 +128,12 @@ HRESULT WINAPI ISF_MyComputer_Constructor (IUnknown * pUnkOuter, REFIID riid, LP } /************************************************************************** - * ISF_MyComputer_fnQueryInterface + * ISF_MyComputer_fnQueryInterface * * NOTES supports not IPersist/IPersistFolder */ -static HRESULT WINAPI ISF_MyComputer_fnQueryInterface (IShellFolder2 * iface, REFIID riid, LPVOID * ppvObj) +static HRESULT WINAPI ISF_MyComputer_fnQueryInterface (IShellFolder2 *iface, + REFIID riid, LPVOID *ppvObj) { IGenericSFImpl *This = (IGenericSFImpl *)iface; @@ -141,17 +142,23 @@ static HRESULT WINAPI ISF_MyComputer_fnQueryInterface (IShellFolder2 * iface, RE *ppvObj = NULL; if (IsEqualIID (riid, &IID_IUnknown) || - IsEqualIID (riid, &IID_IShellFolder) || IsEqualIID (riid, &IID_IShellFolder2)) { - *ppvObj = This; - } else if (IsEqualIID (riid, &IID_IPersist) || - IsEqualIID (riid, &IID_IPersistFolder) || IsEqualIID (riid, &IID_IPersistFolder2)) { - *ppvObj = _IPersistFolder2_ (This); + IsEqualIID (riid, &IID_IShellFolder) || + IsEqualIID (riid, &IID_IShellFolder2)) + { + *ppvObj = This; + } + else if (IsEqualIID (riid, &IID_IPersist) || + IsEqualIID (riid, &IID_IPersistFolder) || + IsEqualIID (riid, &IID_IPersistFolder2)) + { + *ppvObj = _IPersistFolder2_ (This); } - if (*ppvObj) { - IUnknown_AddRef ((IUnknown *) (*ppvObj)); - TRACE ("-- Interface: (%p)->(%p)\n", ppvObj, *ppvObj); - return S_OK; + if (*ppvObj) + { + IUnknown_AddRef ((IUnknown *) (*ppvObj)); + TRACE ("-- Interface: (%p)->(%p)\n", ppvObj, *ppvObj); + return S_OK; } TRACE ("-- Interface: E_NOINTERFACE\n"); return E_NOINTERFACE; @@ -174,7 +181,8 @@ static ULONG WINAPI ISF_MyComputer_fnRelease (IShellFolder2 * iface) TRACE ("(%p)->(count=%lu)\n", This, refCount + 1); - if (!refCount) { + if (!refCount) + { TRACE ("-- destroying IShellFolder(%p)\n", This); if (This->pidlRoot) SHFree (This->pidlRoot); @@ -184,51 +192,56 @@ static ULONG WINAPI ISF_MyComputer_fnRelease (IShellFolder2 * iface) } /************************************************************************** -* ISF_MyComputer_fnParseDisplayName +* ISF_MyComputer_fnParseDisplayName */ -static HRESULT WINAPI -ISF_MyComputer_fnParseDisplayName (IShellFolder2 * iface, - HWND hwndOwner, - LPBC pbc, - LPOLESTR lpszDisplayName, - DWORD * pchEaten, LPITEMIDLIST * ppidl, DWORD * pdwAttributes) +static HRESULT WINAPI ISF_MyComputer_fnParseDisplayName (IShellFolder2 *iface, + HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, + DWORD * pchEaten, LPITEMIDLIST * ppidl, DWORD * pdwAttributes) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - HRESULT hr = E_INVALIDARG; LPCWSTR szNext = NULL; WCHAR szElement[MAX_PATH]; LPITEMIDLIST pidlTemp = NULL; CLSID clsid; - TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", - This, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName), pchEaten, ppidl, pdwAttributes); + TRACE("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", This, + hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName), + pchEaten, ppidl, pdwAttributes); *ppidl = 0; if (pchEaten) - *pchEaten = 0; /* strange but like the original */ + *pchEaten = 0; /* strange but like the original */ /* handle CLSID paths */ - if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':') { - szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); - TRACE ("-- element: %s\n", debugstr_w (szElement)); - SHCLSIDFromStringW (szElement + 2, &clsid); - pidlTemp = _ILCreateGuid (PT_GUID, &clsid); + if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':') + { + szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); + TRACE ("-- element: %s\n", debugstr_w (szElement)); + SHCLSIDFromStringW (szElement + 2, &clsid); + pidlTemp = _ILCreateGuid (PT_GUID, &clsid); } /* do we have an absolute path name ? */ - else if (PathGetDriveNumberW (lpszDisplayName) >= 0 && lpszDisplayName[2] == (WCHAR) '\\') { - szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); - szElement[0] = toupper(szElement[0]); /* make drive letter uppercase to enable PIDL comparison */ - pidlTemp = _ILCreateDrive (szElement); + else if (PathGetDriveNumberW (lpszDisplayName) >= 0 && + lpszDisplayName[2] == (WCHAR) '\\') + { + szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH); + /* make drive letter uppercase to enable PIDL comparison */ + szElement[0] = toupper(szElement[0]); + pidlTemp = _ILCreateDrive (szElement); } - if (szNext && *szNext) { - hr = SHELL32_ParseNextElement (iface, hwndOwner, pbc, &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes); - } else { - if (pdwAttributes && *pdwAttributes) { - SHELL32_GetItemAttributes (_IShellFolder_ (This), pidlTemp, pdwAttributes); - } - hr = S_OK; + if (szNext && *szNext) + { + hr = SHELL32_ParseNextElement (iface, hwndOwner, pbc, &pidlTemp, + (LPOLESTR) szNext, pchEaten, pdwAttributes); + } + else + { + if (pdwAttributes && *pdwAttributes) + SHELL32_GetItemAttributes (_IShellFolder_ (This), + pidlTemp, pdwAttributes); + hr = S_OK; } *ppidl = pidlTemp; @@ -241,14 +254,20 @@ ISF_MyComputer_fnParseDisplayName (IShellFolder2 * iface, /************************************************************************** * CreateMyCompEnumList() */ +static const WCHAR MyComputer_NameSpaceW[] = { 'S','O','F','T','W','A','R','E', + '\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l', + 'o','r','e','r','\\','M','y','C','o','m','p','u','t','e','r','\\','N','a','m', + 'e','s','p','a','c','e','\0' }; + static BOOL CreateMyCompEnumList(IEnumIDList *list, DWORD dwFlags) { BOOL ret = TRUE; TRACE("(%p)->(flags=0x%08lx) \n",list,dwFlags); - /*enumerate the folders*/ - if(dwFlags & SHCONTF_FOLDERS) + /* enumerate the folders */ + if (dwFlags & SHCONTF_FOLDERS) { WCHAR wszDriveName[] = {'A', ':', '\\', '\0'}; DWORD dwDrivemap = GetLogicalDrives(); @@ -263,27 +282,27 @@ static BOOL CreateMyCompEnumList(IEnumIDList *list, DWORD dwFlags) } TRACE("-- (%p)-> enumerate (mycomputer shell extensions)\n",list); - if (ret && !RegOpenKeyExA(HKEY_LOCAL_MACHINE, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\mycomputer\\NameSpace", + if (ret && !RegOpenKeyExW(HKEY_LOCAL_MACHINE, MyComputer_NameSpaceW, 0, KEY_READ, &hkey)) { - char iid[50]; + WCHAR iid[50]; int i=0; while (ret) { - DWORD size = sizeof (iid); - LONG apiRet = RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, - NULL); + DWORD size; + LONG r; - if (ERROR_SUCCESS == apiRet) + size = sizeof(iid) / sizeof(iid[0]); + r = RegEnumKeyExW(hkey, i, iid, &size, 0, NULL, NULL, NULL); + if (ERROR_SUCCESS == r) { /* FIXME: shell extensions, shouldn't the type be * PT_SHELLEXT? */ - ret = AddToEnumList(list, _ILCreateGuidFromStrA(iid)); + ret = AddToEnumList(list, _ILCreateGuidFromStrW(iid)); i++; } - else if (ERROR_NO_MORE_ITEMS == apiRet) + else if (ERROR_NO_MORE_ITEMS == r) break; else ret = FALSE; @@ -295,14 +314,15 @@ static BOOL CreateMyCompEnumList(IEnumIDList *list, DWORD dwFlags) } /************************************************************************** -* ISF_MyComputer_fnEnumObjects +* ISF_MyComputer_fnEnumObjects */ -static HRESULT WINAPI -ISF_MyComputer_fnEnumObjects (IShellFolder2 * iface, HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST * ppEnumIDList) +static HRESULT WINAPI ISF_MyComputer_fnEnumObjects (IShellFolder2 *iface, + HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - TRACE ("(%p)->(HWND=%p flags=0x%08lx pplist=%p)\n", This, hwndOwner, dwFlags, ppEnumIDList); + TRACE("(%p)->(HWND=%p flags=0x%08lx pplist=%p)\n", This, + hwndOwner, dwFlags, ppEnumIDList); *ppEnumIDList = IEnumIDList_Constructor(); if (*ppEnumIDList) @@ -314,43 +334,42 @@ ISF_MyComputer_fnEnumObjects (IShellFolder2 * iface, HWND hwndOwner, DWORD dwFla } /************************************************************************** -* ISF_MyComputer_fnBindToObject +* ISF_MyComputer_fnBindToObject */ -static HRESULT WINAPI -ISF_MyComputer_fnBindToObject (IShellFolder2 * iface, LPCITEMIDLIST pidl, - LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) +static HRESULT WINAPI ISF_MyComputer_fnBindToObject (IShellFolder2 *iface, + LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n", This, pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); + TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", This, + pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); return SHELL32_BindToChild (This->pidlRoot, NULL, pidl, riid, ppvOut); } /************************************************************************** -* ISF_MyComputer_fnBindToStorage +* ISF_MyComputer_fnBindToStorage */ -static HRESULT WINAPI -ISF_MyComputer_fnBindToStorage (IShellFolder2 * iface, - LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) +static HRESULT WINAPI ISF_MyComputer_fnBindToStorage (IShellFolder2 * iface, + LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n", This, pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); + FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", This, + pidl, pbcReserved, shdebugstr_guid (riid), ppvOut); *ppvOut = NULL; return E_NOTIMPL; } /************************************************************************** -* ISF_MyComputer_fnCompareIDs +* ISF_MyComputer_fnCompareIDs */ -static HRESULT WINAPI -ISF_MyComputer_fnCompareIDs (IShellFolder2 * iface, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) +static HRESULT WINAPI ISF_MyComputer_fnCompareIDs (IShellFolder2 *iface, + LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - int nReturn; TRACE ("(%p)->(0x%08lx,pidl1=%p,pidl2=%p)\n", This, lParam, pidl1, pidl2); @@ -360,34 +379,41 @@ ISF_MyComputer_fnCompareIDs (IShellFolder2 * iface, LPARAM lParam, LPCITEMIDLIST } /************************************************************************** -* ISF_MyComputer_fnCreateViewObject +* ISF_MyComputer_fnCreateViewObject */ -static HRESULT WINAPI -ISF_MyComputer_fnCreateViewObject (IShellFolder2 * iface, HWND hwndOwner, REFIID riid, LPVOID * ppvOut) +static HRESULT WINAPI ISF_MyComputer_fnCreateViewObject (IShellFolder2 *iface, + HWND hwndOwner, REFIID riid, LPVOID * ppvOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - LPSHELLVIEW pShellView; HRESULT hr = E_INVALIDARG; - TRACE ("(%p)->(hwnd=%p,%s,%p)\n", This, hwndOwner, shdebugstr_guid (riid), ppvOut); + TRACE("(%p)->(hwnd=%p,%s,%p)\n", This, + hwndOwner, shdebugstr_guid (riid), ppvOut); - if (ppvOut) { - *ppvOut = NULL; + if (!ppvOut) + return hr; - if (IsEqualIID (riid, &IID_IDropTarget)) { - WARN ("IDropTarget not implemented\n"); - hr = E_NOTIMPL; - } else if (IsEqualIID (riid, &IID_IContextMenu)) { - WARN ("IContextMenu not implemented\n"); - hr = E_NOTIMPL; - } else if (IsEqualIID (riid, &IID_IShellView)) { - pShellView = IShellView_Constructor ((IShellFolder *) iface); - if (pShellView) { - hr = IShellView_QueryInterface (pShellView, riid, ppvOut); - IShellView_Release (pShellView); - } - } + *ppvOut = NULL; + + if (IsEqualIID (riid, &IID_IDropTarget)) + { + WARN ("IDropTarget not implemented\n"); + hr = E_NOTIMPL; + } + else if (IsEqualIID (riid, &IID_IContextMenu)) + { + WARN ("IContextMenu not implemented\n"); + hr = E_NOTIMPL; + } + else if (IsEqualIID (riid, &IID_IShellView)) + { + pShellView = IShellView_Constructor ((IShellFolder *) iface); + if (pShellView) + { + hr = IShellView_QueryInterface (pShellView, riid, ppvOut); + IShellView_Release (pShellView); + } } TRACE ("-- (%p)->(interface=%p)\n", This, ppvOut); return hr; @@ -396,26 +422,26 @@ ISF_MyComputer_fnCreateViewObject (IShellFolder2 * iface, HWND hwndOwner, REFIID /************************************************************************** * ISF_MyComputer_fnGetAttributesOf */ -static HRESULT WINAPI -ISF_MyComputer_fnGetAttributesOf (IShellFolder2 * iface, UINT cidl, LPCITEMIDLIST * apidl, DWORD * rgfInOut) +static HRESULT WINAPI ISF_MyComputer_fnGetAttributesOf (IShellFolder2 * iface, + UINT cidl, LPCITEMIDLIST * apidl, DWORD * rgfInOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - HRESULT hr = S_OK; TRACE ("(%p)->(cidl=%d apidl=%p mask=0x%08lx)\n", This, cidl, apidl, *rgfInOut); - if ((!cidl) || (!apidl) || (!rgfInOut)) - return E_INVALIDARG; + if (!cidl || !apidl || !rgfInOut) + return E_INVALIDARG; if (*rgfInOut == 0) - *rgfInOut = ~0; + *rgfInOut = ~0; - while (cidl > 0 && *apidl) { - pdump (*apidl); - SHELL32_GetItemAttributes (_IShellFolder_ (This), *apidl, rgfInOut); - apidl++; - cidl--; + while (cidl > 0 && *apidl) + { + pdump (*apidl); + SHELL32_GetItemAttributes (_IShellFolder_ (This), *apidl, rgfInOut); + apidl++; + cidl--; } TRACE ("-- result=0x%08lx\n", *rgfInOut); @@ -423,21 +449,20 @@ ISF_MyComputer_fnGetAttributesOf (IShellFolder2 * iface, UINT cidl, LPCITEMIDLIS } /************************************************************************** -* ISF_MyComputer_fnGetUIObjectOf +* ISF_MyComputer_fnGetUIObjectOf * * PARAMETERS -* HWND hwndOwner, //[in ] Parent window for any output -* UINT cidl, //[in ] array size -* LPCITEMIDLIST* apidl, //[in ] simple pidl array -* REFIID riid, //[in ] Requested Interface -* UINT* prgfInOut, //[ ] reserved -* LPVOID* ppvObject) //[out] Resulting Interface +* hwndOwner [in] Parent window for any output +* cidl [in] array size +* apidl [in] simple pidl array +* riid [in] Requested Interface +* prgfInOut [ ] reserved +* ppvObject [out] Resulting Interface * */ -static HRESULT WINAPI -ISF_MyComputer_fnGetUIObjectOf (IShellFolder2 * iface, - HWND hwndOwner, - UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) +static HRESULT WINAPI ISF_MyComputer_fnGetUIObjectOf (IShellFolder2 * iface, + HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, + UINT * prgfInOut, LPVOID * ppvOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; @@ -445,57 +470,72 @@ ISF_MyComputer_fnGetUIObjectOf (IShellFolder2 * iface, IUnknown *pObj = NULL; HRESULT hr = E_INVALIDARG; - TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", - This, hwndOwner, cidl, apidl, shdebugstr_guid (riid), prgfInOut, ppvOut); + TRACE("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n", This, + hwndOwner, cidl, apidl, shdebugstr_guid (riid), prgfInOut, ppvOut); - if (ppvOut) { - *ppvOut = NULL; + if (!ppvOut) + return hr; - if (IsEqualIID (riid, &IID_IContextMenu) && (cidl >= 1)) { - pObj = (LPUNKNOWN) ISvItemCm_Constructor ((IShellFolder *) iface, This->pidlRoot, apidl, cidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IDataObject) && (cidl >= 1)) { - pObj = (LPUNKNOWN) IDataObject_Constructor (hwndOwner, This->pidlRoot, apidl, cidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IExtractIconA) && (cidl == 1)) { - pidl = ILCombine (This->pidlRoot, apidl[0]); - pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl); - SHFree (pidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IExtractIconW) && (cidl == 1)) { - pidl = ILCombine (This->pidlRoot, apidl[0]); - pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl); - SHFree (pidl); - hr = S_OK; - } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) { - hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, (LPVOID *) & pObj); - } else if ((IsEqualIID(riid,&IID_IShellLinkW) || IsEqualIID(riid,&IID_IShellLinkA)) - && (cidl == 1)) { - pidl = ILCombine (This->pidlRoot, apidl[0]); - hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj); - SHFree (pidl); - } else { - hr = E_NOINTERFACE; - } + *ppvOut = NULL; - if (SUCCEEDED(hr) && !pObj) - hr = E_OUTOFMEMORY; - - *ppvOut = pObj; + if (IsEqualIID (riid, &IID_IContextMenu) && (cidl >= 1)) + { + pObj = (LPUNKNOWN) ISvItemCm_Constructor ((IShellFolder *) iface, + This->pidlRoot, apidl, cidl); + hr = S_OK; } + else if (IsEqualIID (riid, &IID_IDataObject) && (cidl >= 1)) + { + pObj = (LPUNKNOWN) IDataObject_Constructor (hwndOwner, + This->pidlRoot, apidl, cidl); + hr = S_OK; + } + else if (IsEqualIID (riid, &IID_IExtractIconA) && (cidl == 1)) + { + pidl = ILCombine (This->pidlRoot, apidl[0]); + pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl); + SHFree (pidl); + hr = S_OK; + } + else if (IsEqualIID (riid, &IID_IExtractIconW) && (cidl == 1)) + { + pidl = ILCombine (This->pidlRoot, apidl[0]); + pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl); + SHFree (pidl); + hr = S_OK; + } + else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) + { + hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, + (LPVOID *) &pObj); + } + else if ((IsEqualIID(riid,&IID_IShellLinkW) || + IsEqualIID(riid,&IID_IShellLinkA)) && (cidl == 1)) + { + pidl = ILCombine (This->pidlRoot, apidl[0]); + hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*) &pObj); + SHFree (pidl); + } + else + hr = E_NOINTERFACE; + + if (SUCCEEDED(hr) && !pObj) + hr = E_OUTOFMEMORY; + + *ppvOut = pObj; TRACE ("(%p)->hr=0x%08lx\n", This, hr); return hr; } /************************************************************************** -* ISF_MyComputer_fnGetDisplayNameOf +* ISF_MyComputer_fnGetDisplayNameOf */ -static HRESULT WINAPI ISF_MyComputer_fnGetDisplayNameOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet) +static HRESULT WINAPI ISF_MyComputer_fnGetDisplayNameOf (IShellFolder2 *iface, + LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - char szPath[MAX_PATH], - szDrive[18]; + char szPath[MAX_PATH], szDrive[18]; int len = 0; BOOL bSimplePidl; HRESULT hr = S_OK; @@ -504,98 +544,138 @@ static HRESULT WINAPI ISF_MyComputer_fnGetDisplayNameOf (IShellFolder2 * iface, pdump (pidl); if (!strRet) - return E_INVALIDARG; + return E_INVALIDARG; szPath[0] = 0x00; szDrive[0] = 0x00; bSimplePidl = _ILIsPidlSimple (pidl); - if (!pidl->mkid.cb) { - /* parsing name like ::{...} */ - lstrcpyA (szPath, "::"); - SHELL32_GUIDToStringA(&CLSID_MyComputer, &szPath[2]); - } else if (_ILIsSpecialFolder (pidl)) { - /* take names of special folders only if its only this folder */ - if (bSimplePidl) { - GUID const *clsid; + if (!pidl->mkid.cb) + { + /* parsing name like ::{...} */ + lstrcpyA (szPath, "::"); + SHELL32_GUIDToStringA(&CLSID_MyComputer, &szPath[2]); + } + else if (_ILIsSpecialFolder (pidl)) + { + /* take names of special folders only if its only this folder */ + if (bSimplePidl) + { + GUID const *clsid; - if ((clsid = _ILGetGUIDPointer (pidl))) { - if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING) { - int bWantsForParsing; + clsid = _ILGetGUIDPointer (pidl); + if (clsid) + { + if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING) + { + static const WCHAR clsidW[] = + { 'C','L','S','I','D','\\',0 }; + static const WCHAR shellfolderW[] = + { '\\','s','h','e','l','l','f','o','l','d','e','r',0 }; + static const WCHAR wantsForParsingW[] = + { 'W','a','n','t','s','F','o','r','P','a','r','s','i','n', + 'g',0 }; + int bWantsForParsing = FALSE; + WCHAR szRegPath[100]; + LONG r; - /* - * we can only get a filesystem path from a shellfolder if the value WantsFORPARSING in - * CLSID\\{...}\\shellfolder exists - * exception: the MyComputer folder has this keys not but like any filesystem backed - * folder it needs these behaviour - */ - /* get the "WantsFORPARSING" flag from the registry */ - char szRegPath[100]; + /* + * We can only get a filesystem path from a shellfolder + * if the value WantsFORPARSING exists in + * CLSID\\{...}\\shellfolder + * exception: the MyComputer folder has this keys not + * but like any filesystem backed + * folder it needs these behaviour + * + * Get the "WantsFORPARSING" flag from the registry + */ - lstrcpyA (szRegPath, "CLSID\\"); - SHELL32_GUIDToStringA (clsid, &szRegPath[6]); - lstrcatA (szRegPath, "\\shellfolder"); - bWantsForParsing = - (ERROR_SUCCESS == - SHGetValueA (HKEY_CLASSES_ROOT, szRegPath, "WantsFORPARSING", NULL, NULL, NULL)); + lstrcpyW (szRegPath, clsidW); + SHELL32_GUIDToStringW (clsid, &szRegPath[6]); + lstrcatW (szRegPath, shellfolderW); + r = SHGetValueW (HKEY_CLASSES_ROOT, szRegPath, + wantsForParsingW, NULL, NULL, NULL); + if (r == ERROR_SUCCESS) + bWantsForParsing = TRUE; - if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) && bWantsForParsing) { - /* we need the filesystem path to the destination folder. Only the folder itself can know it */ - hr = SHELL32_GetDisplayNameOfChild (iface, pidl, dwFlags, szPath, MAX_PATH); - } else { - LPSTR p; + if ((GET_SHGDN_RELATION (dwFlags) == SHGDN_NORMAL) && + bWantsForParsing) + { + /* + * We need the filesystem path to the destination folder + * Only the folder itself can know it + */ + hr = SHELL32_GetDisplayNameOfChild (iface, pidl, + dwFlags, szPath, MAX_PATH); + } + else + { + LPSTR p; - /* parsing name like ::{...} */ - p = lstrcpyA(szPath, "::") + 2; - p += SHELL32_GUIDToStringA(&CLSID_MyComputer, p); + /* parsing name like ::{...} */ + p = lstrcpyA(szPath, "::") + 2; + p += SHELL32_GUIDToStringA(&CLSID_MyComputer, p); - lstrcatA(p, "\\::"); - p += 3; - SHELL32_GUIDToStringA(clsid, p); - } - } else { - /* user friendly name */ - HCR_GetClassNameA (clsid, szPath, MAX_PATH); - } - } else - _ILSimpleGetText (pidl, szPath, MAX_PATH); /* append my own path */ - } else { - FIXME ("special folder\n"); - } - } else { - if (!_ILIsDrive (pidl)) { - ERR ("Wrong pidl type\n"); - return E_INVALIDARG; - } + lstrcatA(p, "\\::"); + p += 3; + SHELL32_GUIDToStringA(clsid, p); + } + } + else + { + /* user friendly name */ + HCR_GetClassNameA (clsid, szPath, MAX_PATH); + } + } + else + { + /* append my own path */ + _ILSimpleGetText (pidl, szPath, MAX_PATH); + } + } + else + FIXME ("special folder\n"); + } + else + { + if (!_ILIsDrive (pidl)) + { + ERR ("Wrong pidl type\n"); + return E_INVALIDARG; + } - _ILSimpleGetText (pidl, szPath, MAX_PATH); /* append my own path */ + _ILSimpleGetText (pidl, szPath, MAX_PATH); /* append my own path */ - /* long view "lw_name (C:)" */ - if (bSimplePidl && !(dwFlags & SHGDN_FORPARSING)) { - DWORD dwVolumeSerialNumber, - dwMaximumComponetLength, - dwFileSystemFlags; + /* long view "lw_name (C:)" */ + if (bSimplePidl && !(dwFlags & SHGDN_FORPARSING)) + { + DWORD dwVolumeSerialNumber, dwMaximumComponetLength, dwFileSystemFlags; - GetVolumeInformationA (szPath, szDrive, sizeof (szDrive) - 6, &dwVolumeSerialNumber, - &dwMaximumComponetLength, &dwFileSystemFlags, NULL, 0); - strcat (szDrive, " ("); - strncat (szDrive, szPath, 2); - strcat (szDrive, ")"); - strcpy (szPath, szDrive); - } + GetVolumeInformationA (szPath, szDrive, sizeof (szDrive) - 6, + &dwVolumeSerialNumber, + &dwMaximumComponetLength, &dwFileSystemFlags, NULL, 0); + strcat (szDrive, " ("); + strncat (szDrive, szPath, 2); + strcat (szDrive, ")"); + strcpy (szPath, szDrive); + } } - if (!bSimplePidl) { /* go deeper if needed */ - PathAddBackslashA (szPath); - len = strlen (szPath); + if (!bSimplePidl) + { + /* go deeper if needed */ + PathAddBackslashA (szPath); + len = strlen (szPath); - hr = SHELL32_GetDisplayNameOfChild (iface, pidl, dwFlags | SHGDN_INFOLDER, szPath + len, MAX_PATH - len); + hr = SHELL32_GetDisplayNameOfChild (iface, pidl, + dwFlags | SHGDN_INFOLDER, szPath + len, MAX_PATH - len); } - if (SUCCEEDED (hr)) { - strRet->uType = STRRET_CSTR; - lstrcpynA (strRet->u.cStr, szPath, MAX_PATH); + if (SUCCEEDED (hr)) + { + strRet->uType = STRRET_CSTR; + lstrcpynA (strRet->u.cStr, szPath, MAX_PATH); } TRACE ("-- (%p)->(%s)\n", This, szPath); @@ -608,53 +688,64 @@ static HRESULT WINAPI ISF_MyComputer_fnGetDisplayNameOf (IShellFolder2 * iface, * identifier in the process. * * PARAMETERS -* HWND hwndOwner, //[in ] Owner window for output -* LPCITEMIDLIST pidl, //[in ] simple pidl of item to change -* LPCOLESTR lpszName, //[in ] the items new display name -* DWORD dwFlags, //[in ] SHGNO formatting flags -* LPITEMIDLIST* ppidlOut) //[out] simple pidl returned +* hwndOwner [in] Owner window for output +* pidl [in] simple pidl of item to change +* lpszName [in] the items new display name +* dwFlags [in] SHGNO formatting flags +* ppidlOut [out] simple pidl returned */ -static HRESULT WINAPI ISF_MyComputer_fnSetNameOf (IShellFolder2 * iface, HWND hwndOwner, LPCITEMIDLIST pidl, /*simple pidl */ - LPCOLESTR lpName, DWORD dwFlags, LPITEMIDLIST * pPidlOut) +static HRESULT WINAPI ISF_MyComputer_fnSetNameOf ( + IShellFolder2 * iface, HWND hwndOwner, LPCITEMIDLIST pidl, + LPCOLESTR lpName, DWORD dwFlags, LPITEMIDLIST * pPidlOut) { IGenericSFImpl *This = (IGenericSFImpl *)iface; - FIXME ("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", This, hwndOwner, pidl, debugstr_w (lpName), dwFlags, pPidlOut); + FIXME ("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", This, + hwndOwner, pidl, debugstr_w (lpName), dwFlags, pPidlOut); return E_FAIL; } -static HRESULT WINAPI ISF_MyComputer_fnGetDefaultSearchGUID (IShellFolder2 * iface, GUID * pguid) +static HRESULT WINAPI ISF_MyComputer_fnGetDefaultSearchGUID ( + IShellFolder2 * iface, GUID * pguid) { IGenericSFImpl *This = (IGenericSFImpl *)iface; FIXME ("(%p)\n", This); return E_NOTIMPL; } -static HRESULT WINAPI ISF_MyComputer_fnEnumSearches (IShellFolder2 * iface, IEnumExtraSearch ** ppenum) +static HRESULT WINAPI ISF_MyComputer_fnEnumSearches ( + IShellFolder2 * iface, IEnumExtraSearch ** ppenum) { IGenericSFImpl *This = (IGenericSFImpl *)iface; FIXME ("(%p)\n", This); return E_NOTIMPL; } -static HRESULT WINAPI ISF_MyComputer_fnGetDefaultColumn (IShellFolder2 * iface, DWORD dwRes, ULONG * pSort, ULONG * pDisplay) +static HRESULT WINAPI ISF_MyComputer_fnGetDefaultColumn ( + IShellFolder2 *iface, DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { IGenericSFImpl *This = (IGenericSFImpl *)iface; TRACE ("(%p)\n", This); - if (pSort) *pSort = 0; - if (pDisplay) *pDisplay = 0; + if (pSort) + *pSort = 0; + if (pDisplay) + *pDisplay = 0; return S_OK; } -static HRESULT WINAPI ISF_MyComputer_fnGetDefaultColumnState (IShellFolder2 * iface, UINT iColumn, DWORD * pcsFlags) +static HRESULT WINAPI ISF_MyComputer_fnGetDefaultColumnState ( + IShellFolder2 * iface, UINT iColumn, DWORD * pcsFlags) { IGenericSFImpl *This = (IGenericSFImpl *)iface; TRACE ("(%p)\n", This); - if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS) return E_INVALIDARG; + if (!pcsFlags || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS) + return E_INVALIDARG; *pcsFlags = MyComputerSFHeader[iColumn].pcsFlags; return S_OK; } -static HRESULT WINAPI ISF_MyComputer_fnGetDetailsEx (IShellFolder2 * iface, LPCITEMIDLIST pidl, const SHCOLUMNID * pscid, VARIANT * pv) + +static HRESULT WINAPI ISF_MyComputer_fnGetDetailsEx (IShellFolder2 * iface, + LPCITEMIDLIST pidl, const SHCOLUMNID * pscid, VARIANT * pv) { IGenericSFImpl *This = (IGenericSFImpl *)iface; FIXME ("(%p)\n", This); @@ -662,7 +753,8 @@ static HRESULT WINAPI ISF_MyComputer_fnGetDetailsEx (IShellFolder2 * iface, LPCI } /* FIXME: drive size >4GB is rolling over */ -static HRESULT WINAPI ISF_MyComputer_fnGetDetailsOf (IShellFolder2 * iface, LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS * psd) +static HRESULT WINAPI ISF_MyComputer_fnGetDetailsOf (IShellFolder2 * iface, + LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS * psd) { IGenericSFImpl *This = (IGenericSFImpl *)iface; HRESULT hr; @@ -670,48 +762,58 @@ static HRESULT WINAPI ISF_MyComputer_fnGetDetailsOf (IShellFolder2 * iface, LPCI TRACE ("(%p)->(%p %i %p)\n", This, pidl, iColumn, psd); if (!psd || iColumn >= MYCOMPUTERSHELLVIEWCOLUMNS) - return E_INVALIDARG; + return E_INVALIDARG; - if (!pidl) { - psd->fmt = MyComputerSFHeader[iColumn].fmt; - psd->cxChar = MyComputerSFHeader[iColumn].cxChar; - psd->str.uType = STRRET_CSTR; - LoadStringA (shell32_hInstance, MyComputerSFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH); - return S_OK; - } else { - char szPath[MAX_PATH]; - ULARGE_INTEGER ulBytes; + if (!pidl) + { + psd->fmt = MyComputerSFHeader[iColumn].fmt; + psd->cxChar = MyComputerSFHeader[iColumn].cxChar; + psd->str.uType = STRRET_CSTR; + LoadStringA (shell32_hInstance, MyComputerSFHeader[iColumn].colnameid, + psd->str.u.cStr, MAX_PATH); + return S_OK; + } + else + { + char szPath[MAX_PATH]; + ULARGE_INTEGER ulBytes; - psd->str.u.cStr[0] = 0x00; - psd->str.uType = STRRET_CSTR; - switch (iColumn) { - case 0: /* name */ - hr = IShellFolder_GetDisplayNameOf (iface, pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); - break; - case 1: /* type */ - _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH); - break; - case 2: /* total size */ - if (_ILIsDrive (pidl)) { - _ILSimpleGetText (pidl, szPath, MAX_PATH); - GetDiskFreeSpaceExA (szPath, NULL, &ulBytes, NULL); - StrFormatByteSizeA (ulBytes.u.LowPart, psd->str.u.cStr, MAX_PATH); - } - break; - case 3: /* free size */ - if (_ILIsDrive (pidl)) { - _ILSimpleGetText (pidl, szPath, MAX_PATH); - GetDiskFreeSpaceExA (szPath, &ulBytes, NULL, NULL); - StrFormatByteSizeA (ulBytes.u.LowPart, psd->str.u.cStr, MAX_PATH); - } - break; - } - hr = S_OK; + psd->str.u.cStr[0] = 0x00; + psd->str.uType = STRRET_CSTR; + switch (iColumn) + { + case 0: /* name */ + hr = IShellFolder_GetDisplayNameOf (iface, pidl, + SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str); + break; + case 1: /* type */ + _ILGetFileType (pidl, psd->str.u.cStr, MAX_PATH); + break; + case 2: /* total size */ + if (_ILIsDrive (pidl)) + { + _ILSimpleGetText (pidl, szPath, MAX_PATH); + GetDiskFreeSpaceExA (szPath, NULL, &ulBytes, NULL); + StrFormatByteSizeA (ulBytes.u.LowPart, psd->str.u.cStr, MAX_PATH); + } + break; + case 3: /* free size */ + if (_ILIsDrive (pidl)) + { + _ILSimpleGetText (pidl, szPath, MAX_PATH); + GetDiskFreeSpaceExA (szPath, &ulBytes, NULL, NULL); + StrFormatByteSizeA (ulBytes.u.LowPart, psd->str.u.cStr, MAX_PATH); + } + break; + } + hr = S_OK; } return hr; } -static HRESULT WINAPI ISF_MyComputer_fnMapColumnToSCID (IShellFolder2 * iface, UINT column, SHCOLUMNID * pscid) + +static HRESULT WINAPI ISF_MyComputer_fnMapColumnToSCID ( + IShellFolder2 * iface, UINT column, SHCOLUMNID * pscid) { IGenericSFImpl *This = (IGenericSFImpl *)iface; FIXME ("(%p)\n", This); @@ -720,33 +822,34 @@ static HRESULT WINAPI ISF_MyComputer_fnMapColumnToSCID (IShellFolder2 * iface, U static IShellFolder2Vtbl vt_ShellFolder2 = { - ISF_MyComputer_fnQueryInterface, - ISF_MyComputer_fnAddRef, - ISF_MyComputer_fnRelease, - ISF_MyComputer_fnParseDisplayName, - ISF_MyComputer_fnEnumObjects, - ISF_MyComputer_fnBindToObject, - ISF_MyComputer_fnBindToStorage, - ISF_MyComputer_fnCompareIDs, - ISF_MyComputer_fnCreateViewObject, - ISF_MyComputer_fnGetAttributesOf, - ISF_MyComputer_fnGetUIObjectOf, - ISF_MyComputer_fnGetDisplayNameOf, - ISF_MyComputer_fnSetNameOf, - /* ShellFolder2 */ - ISF_MyComputer_fnGetDefaultSearchGUID, - ISF_MyComputer_fnEnumSearches, - ISF_MyComputer_fnGetDefaultColumn, - ISF_MyComputer_fnGetDefaultColumnState, - ISF_MyComputer_fnGetDetailsEx, - ISF_MyComputer_fnGetDetailsOf, - ISF_MyComputer_fnMapColumnToSCID + ISF_MyComputer_fnQueryInterface, + ISF_MyComputer_fnAddRef, + ISF_MyComputer_fnRelease, + ISF_MyComputer_fnParseDisplayName, + ISF_MyComputer_fnEnumObjects, + ISF_MyComputer_fnBindToObject, + ISF_MyComputer_fnBindToStorage, + ISF_MyComputer_fnCompareIDs, + ISF_MyComputer_fnCreateViewObject, + ISF_MyComputer_fnGetAttributesOf, + ISF_MyComputer_fnGetUIObjectOf, + ISF_MyComputer_fnGetDisplayNameOf, + ISF_MyComputer_fnSetNameOf, + /* ShellFolder2 */ + ISF_MyComputer_fnGetDefaultSearchGUID, + ISF_MyComputer_fnEnumSearches, + ISF_MyComputer_fnGetDefaultColumn, + ISF_MyComputer_fnGetDefaultColumnState, + ISF_MyComputer_fnGetDetailsEx, + ISF_MyComputer_fnGetDetailsOf, + ISF_MyComputer_fnMapColumnToSCID }; /************************************************************************ - * IMCFldr_PersistFolder2_QueryInterface + * IMCFldr_PersistFolder2_QueryInterface */ -static HRESULT WINAPI IMCFldr_PersistFolder2_QueryInterface (IPersistFolder2 * iface, REFIID iid, LPVOID * ppvObj) +static HRESULT WINAPI IMCFldr_PersistFolder2_QueryInterface ( + IPersistFolder2 * iface, REFIID iid, LPVOID * ppvObj) { _ICOM_THIS_From_IPersistFolder2 (IGenericSFImpl, iface); @@ -756,7 +859,7 @@ static HRESULT WINAPI IMCFldr_PersistFolder2_QueryInterface (IPersistFolder2 * i } /************************************************************************ - * IMCFldr_PersistFolder2_AddRef + * IMCFldr_PersistFolder2_AddRef */ static ULONG WINAPI IMCFldr_PersistFolder2_AddRef (IPersistFolder2 * iface) { @@ -768,7 +871,7 @@ static ULONG WINAPI IMCFldr_PersistFolder2_AddRef (IPersistFolder2 * iface) } /************************************************************************ - * ISFPersistFolder_Release + * ISFPersistFolder_Release */ static ULONG WINAPI IMCFldr_PersistFolder2_Release (IPersistFolder2 * iface) { @@ -780,27 +883,29 @@ static ULONG WINAPI IMCFldr_PersistFolder2_Release (IPersistFolder2 * iface) } /************************************************************************ - * IMCFldr_PersistFolder2_GetClassID + * IMCFldr_PersistFolder2_GetClassID */ -static HRESULT WINAPI IMCFldr_PersistFolder2_GetClassID (IPersistFolder2 * iface, CLSID * lpClassId) +static HRESULT WINAPI IMCFldr_PersistFolder2_GetClassID ( + IPersistFolder2 * iface, CLSID * lpClassId) { _ICOM_THIS_From_IPersistFolder2 (IGenericSFImpl, iface); TRACE ("(%p)\n", This); if (!lpClassId) - return E_POINTER; + return E_POINTER; *lpClassId = CLSID_MyComputer; return S_OK; } /************************************************************************ - * IMCFldr_PersistFolder2_Initialize + * IMCFldr_PersistFolder2_Initialize * * NOTES: it makes no sense to change the pidl */ -static HRESULT WINAPI IMCFldr_PersistFolder2_Initialize (IPersistFolder2 * iface, LPCITEMIDLIST pidl) +static HRESULT WINAPI IMCFldr_PersistFolder2_Initialize ( + IPersistFolder2 * iface, LPCITEMIDLIST pidl) { _ICOM_THIS_From_IPersistFolder2 (IGenericSFImpl, iface); TRACE ("(%p)->(%p)\n", This, pidl); @@ -808,26 +913,27 @@ static HRESULT WINAPI IMCFldr_PersistFolder2_Initialize (IPersistFolder2 * iface } /************************************************************************** - * IPersistFolder2_fnGetCurFolder + * IPersistFolder2_fnGetCurFolder */ -static HRESULT WINAPI IMCFldr_PersistFolder2_GetCurFolder (IPersistFolder2 * iface, LPITEMIDLIST * pidl) +static HRESULT WINAPI IMCFldr_PersistFolder2_GetCurFolder ( + IPersistFolder2 * iface, LPITEMIDLIST * pidl) { _ICOM_THIS_From_IPersistFolder2 (IGenericSFImpl, iface); TRACE ("(%p)->(%p)\n", This, pidl); if (!pidl) - return E_POINTER; + return E_POINTER; *pidl = ILClone (This->pidlRoot); return S_OK; } static IPersistFolder2Vtbl vt_PersistFolder2 = { - IMCFldr_PersistFolder2_QueryInterface, - IMCFldr_PersistFolder2_AddRef, - IMCFldr_PersistFolder2_Release, - IMCFldr_PersistFolder2_GetClassID, - IMCFldr_PersistFolder2_Initialize, - IMCFldr_PersistFolder2_GetCurFolder + IMCFldr_PersistFolder2_QueryInterface, + IMCFldr_PersistFolder2_AddRef, + IMCFldr_PersistFolder2_Release, + IMCFldr_PersistFolder2_GetClassID, + IMCFldr_PersistFolder2_Initialize, + IMCFldr_PersistFolder2_GetCurFolder }; diff --git a/reactos/lib/shell32/shfldr_unixfs.c b/reactos/lib/shell32/shfldr_unixfs.c new file mode 100644 index 00000000000..7a49b5e5e68 --- /dev/null +++ b/reactos/lib/shell32/shfldr_unixfs.c @@ -0,0 +1,21 @@ +/* + * UNIXFS - Shell namespace extension for the unix filesystem + * + * Copyright (C) 2005 Michael Jung + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Placeholder in ReactOS, we don't need this */ diff --git a/reactos/lib/shell32/shlexec.c b/reactos/lib/shell32/shlexec.c index 14bab166d5f..8385b12d886 100644 --- a/reactos/lib/shell32/shlexec.c +++ b/reactos/lib/shell32/shlexec.c @@ -365,7 +365,7 @@ static UINT SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, } else if ((retval = GetLastError()) >= 32) { - FIXME("Strange error set by CreateProcess: %d\n", retval); + TRACE("CreateProcess returned error %d\n", retval); retval = ERROR_BAD_FORMAT; } diff --git a/reactos/lib/shell32/shlfileop.c b/reactos/lib/shell32/shlfileop.c index 811a5a9b854..dfa6fb30863 100644 --- a/reactos/lib/shell32/shlfileop.c +++ b/reactos/lib/shell32/shlfileop.c @@ -861,7 +861,8 @@ static const char * debug_shfileops_action( DWORD op ) #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026 #define HIGH_ADR (LPWSTR)0xffffffff -static int file_operation_delete( WIN32_FIND_DATAW *wfd,SHFILEOPSTRUCTW nFileOp, LPWSTR pFromFile,LPWSTR pTempFrom,HANDLE *hFind) +/* handle the complete deletion of `pTempFrom` */ +static int shfileops_delete(WIN32_FIND_DATAW *wfd,SHFILEOPSTRUCTW nFileOp, LPWSTR pFromFile,LPWSTR pTempFrom,HANDLE *hFind) { LPWSTR lpFileName; @@ -914,7 +915,7 @@ static int file_operation_delete( WIN32_FIND_DATAW *wfd,SHFILEOPSTRUCTW nFileOp, * FOF_ALLOWUNDO, FOF_WANTMAPPINGHANDLE */ -static int file_operation_checkFlags(SHFILEOPSTRUCTW nFileOp) +static int shfileops_check_flags(SHFILEOPSTRUCTW nFileOp) { FILEOP_FLAGS OFl = ((FILEOP_FLAGS)nFileOp.fFlags & 0xfff); long FuncSwitch = (nFileOp.wFunc & FO_MASK); @@ -945,6 +946,53 @@ static int file_operation_checkFlags(SHFILEOPSTRUCTW nFileOp) return 0; } +static int shfileops_do_operation(WIN32_FIND_DATAW wfd,SHFILEOPSTRUCTW *nFileOp, LPWSTR pToFile, LPWSTR pFromFile) +{ + LPWSTR lpFileName = wfd.cAlternateFileName; + if (!lpFileName[0]) + lpFileName = wfd.cFileName; + if (IsDotDir(lpFileName) || + (IsAttribDir(wfd.dwFileAttributes) && (nFileOp->fFlags & FOF_FILESONLY))) + return 0; /* next name in pTempFrom(dir) */ + SHFileStrCpyCatW(&pToFile[1], lpFileName, NULL); + SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL); + return SHFileOperationW (nFileOp); +} + +/* get attributes of the parent dir of pTemp and create the directory if it does not exists */ +static DWORD shfileops_get_parent_attr2(LPWSTR pFile,LPWSTR pTemp,int flag,int *retCode) +{ + DWORD PathAttr; + pFile[0] = '\0'; + PathAttr = GetFileAttributesW(pTemp); + if ((PathAttr == INVALID_FILE_ATTRIBUTES) && flag) + { + /* create dir must be here, sample target D:\y\ *.* create with RC=10003 */ + if (SHNotifyCreateDirectoryW(pTemp, NULL)) + { + *retCode = 0x73;/* value unknown */ + /*goto shfileop_end;*/ + return PathAttr; + } + PathAttr = GetFileAttributesW(pTemp); + } + pFile[0] = '\\'; + return PathAttr; +} + +/* get attributes of the parent dir of pTemp without creating the directory if it does not exists */ +static DWORD shfileops_get_parent_attr(LPWSTR pFile,LPWSTR pTemp) +{ + /* less efficient: + return shfileops_get_parent_attr2(pFile,pTemp,0,NULL); + */ + DWORD PathAttr; + pFile[0] = '\0'; + PathAttr = GetFileAttributesW(pTemp); + pFile[0] = '\\'; + return PathAttr; +} + /************************************************************************* * SHFileOperationW [SHELL32.@] * @@ -964,7 +1012,6 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) LPWSTR pTempTo = NULL; LPWSTR pFromFile; LPWSTR pToFile = NULL; - LPWSTR lpFileName; int retCode = 0; DWORD ToAttr; DWORD ToPathAttr; @@ -986,8 +1033,6 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) long FuncSwitch = (nFileOp.wFunc & FO_MASK); long level= nFileOp.wFunc>>4; - int ret; - /* default no error */ nFileOp.fAnyOperationsAborted = FALSE; @@ -1016,12 +1061,9 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) * create dir 0 0 0 0 0 0 1 0 */ - ret = file_operation_checkFlags(nFileOp); - if (ret != 0) - { - retCode = ret; + retCode = shfileops_check_flags(nFileOp); + if (retCode) goto shfileop_end; - } if ((pNextFrom) && (!(b_MultiTo) || (pNextTo))) { @@ -1108,17 +1150,10 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) hFind = FindFirstFileW(pFrom, &wfd); if (INVALID_HANDLE_VALUE == hFind) { - if ((FO_DELETE == FuncSwitch) && (b_Mask)) + if ((FO_DELETE == FuncSwitch) && (b_Mask) && IsAttribDir(shfileops_get_parent_attr(pFromFile,pTempFrom))) { - DWORD FromPathAttr; - pFromFile[0] = '\0'; - FromPathAttr = GetFileAttributesW(pTempFrom); - pFromFile[0] = '\\'; - if (IsAttribDir(FromPathAttr)) - { - /* FO_DELETE with mask and without found is valid */ - goto shfileop_end; - } + /* FO_DELETE with mask and without found is valid */ + goto shfileop_end; } /* root (without mask) is also not allowed as source, tested in W98 */ retCode = ERROR_SHELL_INTERNAL_FILE_NOT_FOUND; @@ -1130,13 +1165,8 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) /* ??? b_Mask = (!SHFileStrICmpA(&pFromFile[1], &wfd.cFileName[0], HIGH_ADR, HIGH_ADR)); */ if (!pTo) /* FO_DELETE */ { - ret = file_operation_delete(&wfd,nFileOp,pFromFile,pTempFrom,&hFind); - /* if ret is not 0, nFileOp.fAnyOperationsAborted is TRUE */ - if (ret != 0) - { - retCode = ret; - goto shfileop_end; - } + retCode = shfileops_delete(&wfd,nFileOp,pFromFile,pTempFrom,&hFind); + /* if ret is not 0, nFileOp.fAnyOperationsAborted is TRUE and the loop will end */ continue; } /* FO_DELETE ends, pTo must be always valid from here */ @@ -1146,9 +1176,7 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) ToPathAttr = ToAttr = GetFileAttributesW(pTempTo); if (!b_Mask && (ToAttr == INVALID_FILE_ATTRIBUTES) && (pToFile)) { - pToFile[0] = '\0'; - ToPathAttr = GetFileAttributesW(pTempTo); - pToFile[0] = '\\'; + ToPathAttr = shfileops_get_parent_attr(pToFile,pTempTo); } if (FO_RENAME == FuncSwitch) @@ -1202,15 +1230,7 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) nFileOp.fFlags = (nFileOp.fFlags | FOF_MULTIDESTFILES); do { - lpFileName = wfd.cAlternateFileName; - if (!lpFileName[0]) - lpFileName = wfd.cFileName; - if (IsDotDir(lpFileName) || - (IsAttribDir(wfd.dwFileAttributes) && (nFileOp.fFlags & FOF_FILESONLY))) - continue; /* next name in pTempFrom(dir) */ - SHFileStrCpyCatW(&pToFile[1], lpFileName, NULL); - SHFileStrCpyCatW(&pFromFile[1], lpFileName, NULL); - retCode = SHFileOperationW (&nFileOp); + retCode = shfileops_do_operation(wfd,&nFileOp,pToFile,pFromFile); } while(!nFileOp.fAnyOperationsAborted && FindNextFileW(hFind, &wfd)); } FindClose(hFind); @@ -1224,19 +1244,9 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) { if (pToFile) { - pToFile[0] = '\0'; - ToPathAttr = GetFileAttributesW(pTempTo); - if ((ToPathAttr == INVALID_FILE_ATTRIBUTES) && b_ToValid) - { - /* create dir must be here, sample target D:\y\ *.* create with RC=10003 */ - if (SHNotifyCreateDirectoryW(pTempTo, NULL)) - { - retCode = 0x73;/* value unknown */ - goto shfileop_end; - } - ToPathAttr = GetFileAttributesW(pTempTo); - } - pToFile[0] = '\\'; + ToPathAttr = shfileops_get_parent_attr2(pToFile,pTempTo,b_ToValid,&retCode); + if (retCode) + goto shfileop_end; if (b_ToInvalidTail) { retCode = 0x10003; @@ -1270,9 +1280,7 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp) } else { - pToFile[0] = '\0'; - ToPathAttr = GetFileAttributesW(pTempTo); - pToFile[0] = '\\'; + ToPathAttr = shfileops_get_parent_attr(pToFile,pTempTo); if (IsAttribFile(ToPathAttr)) { /* error, is this tested ? */ @@ -1403,7 +1411,16 @@ void WINAPI SHFreeNameMappings(HANDLE hNameMapping) } /************************************************************************* - * SheGetDirW [SHELL32.281] + * SheGetDirA [SHELL32.@] + * + */ +HRESULT WINAPI SheGetDirA(LPSTR u, LPSTR v) +{ FIXME("%p %p stub\n",u,v); + return 0; +} + +/************************************************************************* + * SheGetDirW [SHELL32.@] * */ HRESULT WINAPI SheGetDirW(LPWSTR u, LPWSTR v) @@ -1412,7 +1429,16 @@ HRESULT WINAPI SheGetDirW(LPWSTR u, LPWSTR v) } /************************************************************************* - * SheChangeDirW [SHELL32.274] + * SheChangeDirA [SHELL32.@] + * + */ +HRESULT WINAPI SheChangeDirA(LPSTR u) +{ FIXME("(%s),stub\n",debugstr_a(u)); + return 0; +} + +/************************************************************************* + * SheChangeDirW [SHELL32.@] * */ HRESULT WINAPI SheChangeDirW(LPWSTR u) diff --git a/reactos/lib/shell32/shlfolder.c b/reactos/lib/shell32/shlfolder.c index 1f5841906d6..e6734ebcc60 100644 --- a/reactos/lib/shell32/shlfolder.c +++ b/reactos/lib/shell32/shlfolder.c @@ -422,7 +422,7 @@ HRESULT SHELL32_GetItemAttributes (IShellFolder * psf, LPCITEMIDLIST pidl, LPDWO if (SFGAO_LINK & *pdwAttributes) { char ext[MAX_PATH]; - if (!_ILGetExtension(pidl, ext, MAX_PATH) || strcasecmp(ext, "lnk")) + if (!_ILGetExtension(pidl, ext, MAX_PATH) || lstrcmpiA(ext, "lnk")) *pdwAttributes &= ~SFGAO_LINK; } } else { @@ -469,7 +469,7 @@ HRESULT SHELL32_CompareIDs (IShellFolder * iface, LPARAM lParam, LPCITEMIDLIST p /* test for name of pidl */ _ILSimpleGetText (pidl1, szTemp1, MAX_PATH); _ILSimpleGetText (pidl2, szTemp2, MAX_PATH); - nReturn = strcasecmp (szTemp1, szTemp2); + nReturn = lstrcmpiA (szTemp1, szTemp2); if (nReturn < 0) return MAKE_HRESULT( SEVERITY_SUCCESS, 0, (WORD)-1 ); else if (nReturn > 0) diff --git a/reactos/lib/shell32/shlfsbind.c b/reactos/lib/shell32/shlfsbind.c index b558aeab3e1..17ff2301948 100644 --- a/reactos/lib/shell32/shlfsbind.c +++ b/reactos/lib/shell32/shlfsbind.c @@ -42,16 +42,16 @@ WINE_DEFAULT_DEBUG_CHANNEL(pidl); */ typedef struct { - IFileSystemBindDataVtbl *lpVtbl; - DWORD ref; - WIN32_FIND_DATAW findFile; + IFileSystemBindDataVtbl *lpVtbl; + DWORD ref; + WIN32_FIND_DATAW findFile; } IFileSystemBindDataImpl; -static HRESULT WINAPI IFileSystemBindData_fnQueryInterface(IFileSystemBindData *iface, REFIID riid, LPVOID* ppvObj); -static ULONG WINAPI IFileSystemBindData_fnAddRef(IFileSystemBindData *iface); -static ULONG WINAPI IFileSystemBindData_fnRelease(IFileSystemBindData *iface); -static HRESULT WINAPI IFileSystemBindData_fnGetFindData(IFileSystemBindData *iface, WIN32_FIND_DATAW *pfd); -static HRESULT WINAPI IFileSystemBindData_fnSetFindData(IFileSystemBindData *iface, const WIN32_FIND_DATAW *pfd); +static HRESULT WINAPI IFileSystemBindData_fnQueryInterface(IFileSystemBindData *, REFIID, LPVOID*); +static ULONG WINAPI IFileSystemBindData_fnAddRef(IFileSystemBindData *); +static ULONG WINAPI IFileSystemBindData_fnRelease(IFileSystemBindData *); +static HRESULT WINAPI IFileSystemBindData_fnGetFindData(IFileSystemBindData *, WIN32_FIND_DATAW *); +static HRESULT WINAPI IFileSystemBindData_fnSetFindData(IFileSystemBindData *, const WIN32_FIND_DATAW *); static struct IFileSystemBindDataVtbl sbvt = { @@ -62,165 +62,165 @@ static struct IFileSystemBindDataVtbl sbvt = IFileSystemBindData_fnGetFindData, }; -static const WCHAR wFileSystemBindData[] = {'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d','D','a','t','a',0}; +static const WCHAR wFileSystemBindData[] = { + 'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d','D','a','t','a',0}; HRESULT WINAPI IFileSystemBindData_Constructor(const WIN32_FIND_DATAW *pfd, LPBC *ppV) { - IFileSystemBindDataImpl *sb; - HRESULT ret = E_OUTOFMEMORY; + IFileSystemBindDataImpl *sb; + HRESULT ret = E_OUTOFMEMORY; - TRACE("%p, %p\n", pfd, ppV); + TRACE("%p, %p\n", pfd, ppV); - if (!ppV) - return E_INVALIDARG; + if (!ppV) + return E_INVALIDARG; - *ppV = NULL; + *ppV = NULL; - sb = (IFileSystemBindDataImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IFileSystemBindDataImpl)); - if (!sb) - return ret; + sb = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IFileSystemBindDataImpl)); + if (!sb) + return ret; - sb->lpVtbl = &sbvt; - sb->ref = 1; - IFileSystemBindData_fnSetFindData((IFileSystemBindData*)sb, pfd); + sb->lpVtbl = &sbvt; + sb->ref = 1; + IFileSystemBindData_fnSetFindData((IFileSystemBindData*)sb, pfd); - ret = CreateBindCtx(0, ppV); - if (SUCCEEDED(ret)) - { - BIND_OPTS bindOpts; - bindOpts.cbStruct = sizeof(BIND_OPTS); - bindOpts.grfFlags = 0; - bindOpts.grfMode = STGM_CREATE; - bindOpts.dwTickCountDeadline = 0; - IBindCtx_SetBindOptions(*ppV, &bindOpts); - IBindCtx_RegisterObjectParam(*ppV, (LPOLESTR)wFileSystemBindData, (LPUNKNOWN)sb); + ret = CreateBindCtx(0, ppV); + if (SUCCEEDED(ret)) + { + BIND_OPTS bindOpts; - IFileSystemBindData_Release((IFileSystemBindData*)sb); - } - else - HeapFree(GetProcessHeap(), 0, sb); - return ret; + bindOpts.cbStruct = sizeof(BIND_OPTS); + bindOpts.grfFlags = 0; + bindOpts.grfMode = STGM_CREATE; + bindOpts.dwTickCountDeadline = 0; + IBindCtx_SetBindOptions(*ppV, &bindOpts); + IBindCtx_RegisterObjectParam(*ppV, (LPOLESTR)wFileSystemBindData, (LPUNKNOWN)sb); + + IFileSystemBindData_Release((IFileSystemBindData*)sb); + } + else + HeapFree(GetProcessHeap(), 0, sb); + return ret; } HRESULT WINAPI FileSystemBindData_GetFindData(LPBC pbc, WIN32_FIND_DATAW *pfd) { - LPUNKNOWN pUnk; - IFileSystemBindData *pfsbd = NULL; - HRESULT ret; + LPUNKNOWN pUnk; + IFileSystemBindData *pfsbd = NULL; + HRESULT ret; - TRACE("%p, %p\n", pbc, pfd); + TRACE("%p, %p\n", pbc, pfd); - if (!pfd) - return E_INVALIDARG; + if (!pfd) + return E_INVALIDARG; - ret = IBindCtx_GetObjectParam(pbc, (LPOLESTR)wFileSystemBindData, &pUnk); - if (SUCCEEDED(ret)) - { - ret = IUnknown_QueryInterface(pUnk, &IID_IFileSystemBindData, (LPVOID *)&pfsbd); - if (SUCCEEDED(ret)) - { - ret = IFileSystemBindData_GetFindData(pfsbd, pfd); - IFileSystemBindData_Release(pfsbd); - } - IUnknown_Release(pUnk); - } - return ret; + ret = IBindCtx_GetObjectParam(pbc, (LPOLESTR)wFileSystemBindData, &pUnk); + if (SUCCEEDED(ret)) + { + ret = IUnknown_QueryInterface(pUnk, &IID_IFileSystemBindData, (LPVOID *)&pfsbd); + if (SUCCEEDED(ret)) + { + ret = IFileSystemBindData_GetFindData(pfsbd, pfd); + IFileSystemBindData_Release(pfsbd); + } + IUnknown_Release(pUnk); + } + return ret; } HRESULT WINAPI FileSystemBindData_SetFindData(LPBC pbc, const WIN32_FIND_DATAW *pfd) { - LPUNKNOWN pUnk; - IFileSystemBindData *pfsbd = NULL; - HRESULT ret; - - TRACE("%p, %p\n", pbc, pfd); + LPUNKNOWN pUnk; + IFileSystemBindData *pfsbd = NULL; + HRESULT ret; + + TRACE("%p, %p\n", pbc, pfd); - ret = IBindCtx_GetObjectParam(pbc, (LPOLESTR)wFileSystemBindData, &pUnk); - if (SUCCEEDED(ret)) - { - ret = IUnknown_QueryInterface(pUnk, &IID_IFileSystemBindData, (LPVOID *)&pfsbd); - if (SUCCEEDED(ret)) - { - ret = IFileSystemBindData_SetFindData(pfsbd, pfd); - IFileSystemBindData_Release(pfsbd); - } - IUnknown_Release(pUnk); - } - return ret;} + ret = IBindCtx_GetObjectParam(pbc, (LPOLESTR)wFileSystemBindData, &pUnk); + if (SUCCEEDED(ret)) + { + ret = IUnknown_QueryInterface(pUnk, &IID_IFileSystemBindData, (LPVOID *)&pfsbd); + if (SUCCEEDED(ret)) + { + ret = IFileSystemBindData_SetFindData(pfsbd, pfd); + IFileSystemBindData_Release(pfsbd); + } + IUnknown_Release(pUnk); + } + return ret; +} - - -static HRESULT WINAPI IFileSystemBindData_fnQueryInterface(IFileSystemBindData *iface, REFIID riid, LPVOID *ppV) +static HRESULT WINAPI IFileSystemBindData_fnQueryInterface( + IFileSystemBindData *iface, REFIID riid, LPVOID *ppV) { - IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; - TRACE("(%p)->(\n\tIID:\t%s, %p)\n", This, debugstr_guid(riid), ppV); + IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; + TRACE("(%p)->(\n\tIID:\t%s, %p)\n", This, debugstr_guid(riid), ppV); - *ppV = NULL; + *ppV = NULL; - if (IsEqualIID(riid, &IID_IUnknown)) - { - *ppV = This; - } - else if (IsEqualIID(riid, &IID_IFileSystemBindData)) - { - *ppV = (IFileSystemBindData*)This; - } + if (IsEqualIID(riid, &IID_IUnknown)) + *ppV = This; + else if (IsEqualIID(riid, &IID_IFileSystemBindData)) + *ppV = (IFileSystemBindData*)This; - if (*ppV) - { - IUnknown_AddRef((IUnknown*)(*ppV)); - TRACE("-- Interface: (%p)->(%p)\n", ppV, *ppV); - return S_OK; - } - TRACE("-- Interface: E_NOINTERFACE\n"); - return E_NOINTERFACE; + if (*ppV) + { + IUnknown_AddRef((IUnknown*)(*ppV)); + TRACE("-- Interface: (%p)->(%p)\n", ppV, *ppV); + return S_OK; + } + TRACE("-- Interface: E_NOINTERFACE\n"); + return E_NOINTERFACE; } static ULONG WINAPI IFileSystemBindData_fnAddRef(IFileSystemBindData *iface) { - IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; - ULONG refCount = InterlockedIncrement(&This->ref); + IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; + ULONG refCount = InterlockedIncrement(&This->ref); - TRACE("(%p)->(count=%li)\n", This, refCount - 1); + TRACE("(%p)->(count=%li)\n", This, refCount - 1); - return refCount; + return refCount; } static ULONG WINAPI IFileSystemBindData_fnRelease(IFileSystemBindData *iface) { - IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; - ULONG refCount = InterlockedDecrement(&This->ref); - - TRACE("(%p)->(count=%li)\n", This, refCount + 1); + IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; + ULONG refCount = InterlockedDecrement(&This->ref); + + TRACE("(%p)->(count=%li)\n", This, refCount + 1); - if (!refCount) - { - TRACE(" destroying ISFBindPidl(%p)\n",This); - HeapFree(GetProcessHeap(), 0, This); - } - return refCount; + if (!refCount) + { + TRACE(" destroying ISFBindPidl(%p)\n",This); + HeapFree(GetProcessHeap(), 0, This); + } + return refCount; } -static HRESULT WINAPI IFileSystemBindData_fnGetFindData(IFileSystemBindData *iface, WIN32_FIND_DATAW *pfd) +static HRESULT WINAPI IFileSystemBindData_fnGetFindData( + IFileSystemBindData *iface, WIN32_FIND_DATAW *pfd) { - IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; - TRACE("(%p), %p\n", This, pfd); + IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; + TRACE("(%p), %p\n", This, pfd); - if (!pfd) - return E_INVALIDARG; + if (!pfd) + return E_INVALIDARG; - memcpy(pfd, &This->findFile, sizeof(WIN32_FIND_DATAW)); - return NOERROR; + memcpy(pfd, &This->findFile, sizeof(WIN32_FIND_DATAW)); + return NOERROR; } -static HRESULT WINAPI IFileSystemBindData_fnSetFindData(IFileSystemBindData *iface, const WIN32_FIND_DATAW *pfd) +static HRESULT WINAPI IFileSystemBindData_fnSetFindData( + IFileSystemBindData *iface, const WIN32_FIND_DATAW *pfd) { - IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; - TRACE("(%p), %p\n", This, pfd); + IFileSystemBindDataImpl *This = (IFileSystemBindDataImpl *)iface; + TRACE("(%p), %p\n", This, pfd); - if (pfd) - memcpy(&This->findFile, pfd, sizeof(WIN32_FIND_DATAW)); - else - memset(&This->findFile, 0, sizeof(WIN32_FIND_DATAW)); - return NOERROR; + if (pfd) + memcpy(&This->findFile, pfd, sizeof(WIN32_FIND_DATAW)); + else + memset(&This->findFile, 0, sizeof(WIN32_FIND_DATAW)); + return NOERROR; } diff --git a/reactos/lib/shell32/shlview.c b/reactos/lib/shell32/shlview.c index 6dc5e5f92ca..06f30c6c7b1 100644 --- a/reactos/lib/shell32/shlview.c +++ b/reactos/lib/shell32/shlview.c @@ -454,7 +454,7 @@ static INT CALLBACK ShellView_ListViewCompareItems(LPVOID lParam1, LPVOID lParam { _ILGetFileAttributes(pItemIdList1, strName1, MAX_PATH); _ILGetFileAttributes(pItemIdList2, strName2, MAX_PATH); - nDiff = strcasecmp(strName1, strName2); + nDiff = lstrcmpiA(strName1, strName2); } /* Sort by FileName: Folder or Files can be sorted */ else if(pSortInfo->nHeaderID == LISTVIEW_COLUMN_NAME || bIsBothFolder) @@ -462,7 +462,7 @@ static INT CALLBACK ShellView_ListViewCompareItems(LPVOID lParam1, LPVOID lParam /* Sort by Text */ _ILSimpleGetText(pItemIdList1, strName1, MAX_PATH); _ILSimpleGetText(pItemIdList2, strName2, MAX_PATH); - nDiff = strcasecmp(strName1, strName2); + nDiff = lstrcmpiA(strName1, strName2); } /* Sort by File Size, Only valid for Files */ else if(pSortInfo->nHeaderID == LISTVIEW_COLUMN_SIZE) @@ -475,7 +475,7 @@ static INT CALLBACK ShellView_ListViewCompareItems(LPVOID lParam1, LPVOID lParam /* Sort by Type */ _ILGetFileType(pItemIdList1, strName1, MAX_PATH); _ILGetFileType(pItemIdList2, strName2, MAX_PATH); - nDiff = strcasecmp(strName1, strName2); + nDiff = lstrcmpiA(strName1, strName2); } } /* If the Date, FileSize, FileType, Attrib was the same, sort by FileName */ @@ -484,7 +484,7 @@ static INT CALLBACK ShellView_ListViewCompareItems(LPVOID lParam1, LPVOID lParam { _ILSimpleGetText(pItemIdList1, strName1, MAX_PATH); _ILSimpleGetText(pItemIdList2, strName2, MAX_PATH); - nDiff = strcasecmp(strName1, strName2); + nDiff = lstrcmpiA(strName1, strName2); } if(!pSortInfo->bIsAscending) @@ -988,7 +988,7 @@ static void ShellView_DoContextMenu(IShellViewImpl * This, WORD x, WORD y, BOOL { hMenu = CreatePopupMenu(); - pCM = ISvBgCm_Constructor(This->pSFParent); + pCM = ISvBgCm_Constructor(This->pSFParent, FALSE); IContextMenu2_QueryContextMenu(pCM, hMenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, 0); uCommand = TrackPopupMenu( hMenu, TPM_LEFTALIGN | TPM_RETURNCMD,x,y,0,This->hWnd,NULL); @@ -1977,7 +1977,7 @@ static HRESULT WINAPI IShellView_fnGetItemObject(IShellView * iface, UINT uItem, switch(uItem) { case SVGIO_BACKGROUND: - *ppvOut = ISvBgCm_Constructor(This->pSFParent); + *ppvOut = ISvBgCm_Constructor(This->pSFParent, FALSE); break; case SVGIO_SELECTION: diff --git a/reactos/lib/shell32/shres.rc b/reactos/lib/shell32/shres.rc index 66a6f3b81fb..81ce37786a1 100644 --- a/reactos/lib/shell32/shres.rc +++ b/reactos/lib/shell32/shres.rc @@ -38,1644 +38,775 @@ END /* BINRES document.ico */ 1 ICON document.ico /* { - '00 00 01 00 08 00 20 20 00 00 01 00 08 00 A8 08' - '00 00 86 00 00 00 10 10 00 00 01 00 08 00 68 05' - '00 00 2E 09 00 00 20 20 00 00 01 00 04 00 E8 02' - '00 00 96 0E 00 00 10 10 00 00 01 00 04 00 28 01' - '00 00 7E 11 00 00 30 30 00 00 01 00 08 00 A8 0E' - '00 00 A6 12 00 00 30 30 00 00 01 00 20 00 A8 25' - '00 00 4E 21 00 00 20 20 00 00 01 00 20 00 A8 10' - '00 00 F6 46 00 00 10 10 00 00 01 00 20 00 68 04' - '00 00 9E 57 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' + '00 00 01 00 0C 00 10 10 10 00 01 00 04 00 28 01' + '00 00 C6 00 00 00 10 10 00 00 01 00 08 00 68 05' + '00 00 EE 01 00 00 10 10 00 00 01 00 20 00 68 04' + '00 00 56 07 00 00 20 20 10 00 01 00 04 00 E8 02' + '00 00 BE 0B 00 00 20 20 00 00 01 00 08 00 A8 08' + '00 00 A6 0E 00 00 20 20 00 00 01 00 20 00 A8 10' + '00 00 4E 17 00 00 30 30 10 00 01 00 04 00 68 06' + '00 00 F6 27 00 00 30 30 00 00 01 00 08 00 A8 0E' + '00 00 5E 2E 00 00 30 30 00 00 01 00 20 00 A8 25' + '00 00 06 3D 00 00 40 40 10 00 01 00 04 00 68 0A' + '00 00 AE 62 00 00 40 40 00 00 01 00 08 00 28 16' + '00 00 16 6D 00 00 40 40 00 00 01 00 20 00 28 42' + '00 00 3E 83 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 04 00 00 00 00 00 80 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 11 11 11 00 22 22 22 00 33 33 33 00 55 55' - '55 00 66 66 66 00 77 77 77 00 7F 7F 7F 00 88 88' - '88 00 99 99 99 00 AA AA AA 00 BB BB BB 00 CC CC' - 'CC 00 DD DD DD 00 EE EE EE 00 FF FF FF 00 00 00' - '00 00 33 00 32 00 5C 00 64 00 6F 00 63 00 75 00' - '6D 00 65 00 6E 00 74 00 2E 00 69 00 63 00 6F 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 C0 17 95 00 00 00 38 00 A8 44' - 'F9 77 13 00 00 00 18 0A 38 00 00 00 38 00 18 6C' - '38 00 98 17 95 00 00 00 00 00 E0 19 95 00 F0 88' - 'FA 77 70 38 F5 77 FF FF FF FF A8 44 F9 77 70 7D' - 'F5 77 3A 8A F5 77 86 00 00 00 86 00 00 00 08 00' - '00 00 B0 18 95 00 00 00 00 00 CB 44 F9 77 38 9F' - '07 00 CD 8B F5 77 78 13 05 00 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00' - '00 00 00 00 00 00 5A 00 5C 00 00 EC FD 7F 1A 02' - '00 00 4C 16 95 00 40 9F 07 00 FC 15 95 00 FF FF' - 'FF FF B4 1A 95 00 45 00 00 00 28 02 00 00 FF FF' - 'FF FF E2 D8 F5 77 7D 9B F5 77 94 B6 01 00 00 00' - '05 00 F4 17 95 00 80 00 10 C0 B4 1A 95 00 F0 88' - 'FA 77 88 1C F5 77 FF FF FF FF 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 9B B2 E7 77 B7 00 00 00 02 00' - '00 00 A4 1A 95 00 01 00 00 00 18 00 00 00 00 00' - '00 00 10 19 95 00 42 00 00 00 00 00 00 00 F4 18' - '95 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00' - '00 00 02 00 00 00 01 01 F5 77 00 EC FD 7F 5A 00' - '00 00 00 00 00 00 03 00 00 00 62 00 1A 02 40 9F' - '07 00 00 00 00 00 40 9F 07 00 05 00 00 00 BE B3' - 'E7 77 4C 19 95 00 A3 B4 E7 77 F8 00 00 00 00 00' - '00 C0 00 00 00 00 00 00 00 00 02 00 00 00 80 00' - '00 00 00 00 00 00 8C 1A 95 00 7F E9 4B 00 08 6C' - '0C 01 00 00 00 C0 00 00 00 00 80 1A 95 00 02 00' - '00 00 80 00 00 00 00 00 00 00 C0 27 95 00 C4 F5' - 'AF 00 02 00 00 00 44 3A 5C 6F 73 65 78 70 65 72' - '74 73 5C 72 65 61 63 74 6F 73 5C 6C 69 62 5C 73' - '68 65 6C 6C 33 32 5C 64 6F 63 75 6D 65 6E 74 2E' - '69 63 6F 00 4B 00 14 1A 95 00 1F 3B D4 77 13 00' - '00 00 98 00 00 00 4F 3B D4 77 E0 19 95 00 33 3B' - 'D4 77 64 C5 F5 77 A9 F1 E7 77 F8 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 1A 95 00 18 6C' - '38 00 86 00 00 00 00 00 00 00 C9 F1 E7 77 86 00' - '00 00 A4 1A 95 00 08 00 00 00 00 00 00 00 86 00' - '00 00 86 00 00 00 08 00 00 00 F4 19 95 00 33 3B' - 'D4 77 B4 1A 95 00 09 48 E9 77 B8 10 E9 77 FF FF' - 'FF FF C9 F1 E7 77 16 EA 4B 00 F8 00 00 00 18 6C' - '38 00 86 00 00 00 58 1A 95 00 00 00 00 00 00 00' - '00 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 00 00 00 00 00 00 00 00 00 00 00 00 00' - '10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 10 00 00 00 00 00 00 00 00 00 00 00 00' - '10 01 08 09 09 09 09 09 09 08 09 09 08 09 09 08' - '06 03 10 10 10 00 00 00 00 00 00 00 00 00 00 00' - '10 02 0C 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0F' - '0B 08 03 10 10 10 00 00 00 00 00 00 00 00 00 00' - '10 01 0C 0E 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D' - '0A 0C 0A 02 10 10 10 00 00 00 00 00 00 00 00 00' - '10 02 0B 0E 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0E' - '0A 0D 0D 0A 02 10 10 10 00 00 00 00 00 00 00 00' - '10 01 0C 0E 0D 0D 0D 0D 0D 0D 0E 0D 0D 0E 0D 0D' - '0B 0D 0E 0E 0A 03 10 10 10 00 00 00 00 00 00 00' - '10 02 0C 0E 0D 0D 0D 0E 0D 0D 0D 0D 0E 0D 0E 0D' - '0A 0D 0F 0E 0E 09 02 10 10 00 00 00 00 00 00 00' - '10 01 0C 0E 0D 0E 0D 0D 0D 0E 0D 0E 0D 0D 0E 0D' - '0B 0D 0E 0F 0E 0F 0A 02 10 10 00 00 00 00 00 00' - '10 02 0C 0E 0D 0D 0E 0D 0E 0D 0D 0E 0E 0D 0D 0E' - '0A 0E 0F 0E 0F 0F 0F 0A 02 10 10 10 00 00 00 00' - '10 01 0C 0E 0D 0E 0E 0D 0D 0E 0E 0D 0D 0E 0E 0E' - '0A 0D 0E 0E 0E 0E 0E 0E 09 03 10 10 00 00 00 00' - '10 02 0C 0E 0E 0D 0D 0E 0E 0D 0E 0E 0E 0D 0E 0E' - '0C 0A 0B 0B 0B 0A 0B 0B 0B 06 10 10 00 00 00 00' - '10 01 0C 0F 0D 0E 0E 0D 0E 0E 0D 0E 0D 0E 0D 0E' - '0E 0E 0E 0E 0E 0E 0E 0E 0F 09 10 10 00 00 00 00' - '10 02 0C 0E 0E 0E 0E 0E 0D 0E 0E 0E 0E 0E 0E 0E' - '0E 0E 0E 0E 0E 0E 0E 0E 0E 08 10 10 00 00 00 00' - '10 01 0C 0F 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E' - '0E 0E 0E 0E 0E 0E 0E 0E 0F 09 10 10 00 00 00 00' - '10 02 0C 0F 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E' - '0E 0F 0E 0E 0E 0E 0E 0F 0E 09 10 10 00 00 00 00' - '10 01 0C 0F 0E 0E 0E 0E 0F 0E 0F 0E 0E 0E 0F 0E' - '0E 0E 0E 0E 0F 0E 0F 0E 0F 09 10 10 00 00 00 00' - '10 02 0C 0F 0E 0F 0E 0F 0E 0F 0E 0F 0F 0F 0E 0F' - '0F 0E 0F 0E 0E 0E 0E 0F 0F 08 10 10 00 00 00 00' - '10 02 0C 0F 0E 0E 0F 0E 0F 0E 0F 0F 0F 0E 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0E 0F 09 10 10 00 00 00 00' - '10 02 0C 0F 0F 0E 0E 0F 0E 0F 0E 0F 0E 0F 0F 0E' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 09 10 10 00 00 00 00' - '10 02 0C 0F 0E 0F 0F 0E 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 09 10 10 00 00 00 00' - '10 02 0C 0F 0F 0E 0F 0F 0E 0F 0F 0E 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 09 10 10 00 00 00 00' - '10 02 0C 0F 0F 0F 0E 0F 0F 0E 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 09 10 10 00 00 00 00' - '10 02 0D 0F 0E 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 09 10 10 00 00 00 00' - '10 02 0C 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 09 10 10 00 00 00 00' - '10 02 0D 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 09 10 10 00 00 00 00' - '10 01 0D 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0A 10 10 00 00 00 00' - '10 02 0C 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 09 10 10 00 00 00 00' - '10 02 0D 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 09 10 10 00 00 00 00' - '10 02 0D 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0A 10 10 00 00 00 00' - '10 01 06 08 06 06 06 06 06 06 06 06 06 06 06 06' - '06 06 05 06 05 06 05 05 06 04 10 10 00 00 00 00' - '10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 10 10 10 10 10 10 10 10 10 00 00 E0 00' - '07 FF C0 00 03 FF C0 00 01 FF C0 00 00 FF C0 00' - '00 7F C0 00 00 3F C0 00 00 1F C0 00 00 1F C0 00' - '00 0F C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 28 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 88' + '88 88 88 88 88 00 00 8F FF FF FF FF F8 00 00 8F' + 'FF FF FF FF F8 00 00 8F FF FF FF FF F8 00 00 8F' + 'FF FF FF FF F8 00 00 8F FF FF FF FF F8 00 00 8F' + 'FF FF FF FF F8 00 00 8F FF FF FF FF F8 00 00 8F' + 'FF FF FF FF F8 00 00 8F FF FF FF FF F8 00 00 8F' + 'FF FF FF F8 88 00 00 8F FF FF F8 77 80 00 00 8F' + 'FF FF F8 F8 80 00 00 8F FF FF F8 F8 00 00 00 8F' + 'FF FF F8 80 00 00 00 88 88 88 88 00 00 00 C0 03' + '00 00 C0 03 00 00 C0 03 00 00 C0 03 00 00 C0 03' + '00 00 C0 03 00 00 C0 03 00 00 C0 03 00 00 C0 03' + '00 00 C0 03 00 00 C0 03 00 00 C0 07 00 00 C0 07' + '00 00 C0 0F 00 00 C0 1F 00 00 C0 3F 00 00 28 00' '00 00 10 00 00 00 20 00 00 00 01 00 08 00 00 00' - '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 11 11 11 00 33 33' - '33 00 44 44 44 00 77 77 77 00 7F 7F 7F 00 99 99' - '99 00 AA AA AA 00 BB BB BB 00 CC CC CC 00 DD DD' - 'DD 00 EE EE EE 00 FF FF FF 00 00 00 00 00 EE EE' - 'EE 00 FF FF FF 00 00 00 00 00 33 00 32 00 5C 00' - '64 00 6F 00 63 00 75 00 6D 00 65 00 6E 00 74 00' - '2E 00 69 00 63 00 6F 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 17' - '95 00 00 00 38 00 A8 44 F9 77 13 00 00 00 18 0A' - '38 00 00 00 38 00 18 6C 38 00 98 17 95 00 00 00' - '00 00 E0 19 95 00 F0 88 FA 77 70 38 F5 77 FF FF' - 'FF FF A8 44 F9 77 70 7D F5 77 3A 8A F5 77 86 00' - '00 00 86 00 00 00 08 00 00 00 B0 18 95 00 00 00' - '00 00 CB 44 F9 77 38 9F 07 00 CD 8B F5 77 78 13' - '05 00 37 90 F5 77 00 00 00 00 3E 8A F5 77 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 10 00 00 00 00 00 00 00 5A 00' - '5C 00 00 EC FD 7F 1A 02 00 00 4C 16 95 00 40 9F' - '07 00 FC 15 95 00 FF FF FF FF B4 1A 95 00 45 00' - '00 00 28 02 00 00 FF FF FF FF E2 D8 F5 77 7D 9B' - 'F5 77 94 B6 01 00 00 00 05 00 F4 17 95 00 80 00' - '10 C0 B4 1A 95 00 F0 88 FA 77 88 1C F5 77 FF FF' - 'FF FF 37 90 F5 77 00 00 00 00 3E 8A F5 77 9B B2' - 'E7 77 B7 00 00 00 02 00 00 00 A4 1A 95 00 01 00' - '00 00 18 00 00 00 00 00 00 00 10 19 95 00 42 00' - '00 00 00 00 00 00 F4 18 95 00 00 00 00 00 00 00' - '00 00 00 00 00 00 0C 00 00 00 02 00 00 00 01 01' - 'F5 77 00 EC FD 7F 5A 00 00 00 00 00 00 00 03 00' - '00 00 62 00 1A 02 40 9F 07 00 00 00 00 00 40 9F' - '07 00 05 00 00 00 BE B3 E7 77 4C 19 95 00 A3 B4' - 'E7 77 F8 00 00 00 00 00 00 C0 00 00 00 00 00 00' - '00 00 02 00 00 00 80 00 00 00 00 00 00 00 8C 1A' - '95 00 7F E9 4B 00 08 6C 0C 01 00 00 00 C0 00 00' - '00 00 80 1A 95 00 02 00 00 00 80 00 00 00 00 00' - '00 00 C0 27 95 00 C4 F5 AF 00 02 00 00 00 44 3A' - '5C 6F 73 65 78 70 65 72 74 73 5C 72 65 61 63 74' - '6F 73 5C 6C 69 62 5C 73 68 65 6C 6C 33 32 5C 64' - '6F 63 75 6D 65 6E 74 2E 69 63 6F 00 4B 00 14 1A' - '95 00 1F 3B D4 77 13 00 00 00 98 00 00 00 4F 3B' - 'D4 77 E0 19 95 00 33 3B D4 77 64 C5 F5 77 A9 F1' - 'E7 77 F8 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 1A 95 00 18 6C 38 00 86 00 00 00 00 00' - '00 00 C9 F1 E7 77 86 00 00 00 A4 1A 95 00 08 00' - '00 00 00 00 00 00 86 00 00 00 86 00 00 00 08 00' - '00 00 F4 19 95 00 33 3B D4 77 B4 1A 95 00 09 48' - 'E9 77 B8 10 E9 77 FF FF FF FF C9 F1 E7 77 16 EA' - '4B 00 F8 00 00 00 18 6C 38 00 86 00 00 00 58 1A' - '95 00 00 00 00 00 00 0D 0D 0D 0D 0D 0D 0D 0D 0D' - '0D 00 00 00 00 00 00 01 07 08 08 07 08 07 08 04' - '0D 0D 00 00 00 00 00 01 09 0B 0A 0B 0B 0B 0A 09' - '06 0D 0D 00 00 00 00 01 0A 0A 0A 0A 0A 0B 0A 09' - '0C 06 0D 0D 00 00 00 01 09 0A 0B 0A 0B 0A 0A 09' - '0C 0C 06 01 0D 00 00 01 0A 0B 0A 0B 0A 0B 0A 09' - '0A 09 0A 04 0D 00 00 01 09 0B 0B 0A 0B 0B 0B 0B' - '0A 0B 0B 08 0D 00 00 01 0A 0C 0B 0B 0B 0B 0B 0B' - '0B 0B 0C 08 0D 00 00 01 0A 0B 0B 0C 0B 0C 0B 0B' - '0C 0B 0C 09 0D 00 00 01 0B 0C 0B 0C 0B 0C 0C 0C' - '0C 0C 0C 08 0D 00 00 01 09 0C 0C 0B 0C 0C 0C 0C' - '0C 0C 0C 09 0D 00 00 01 0B 0C 0C 0C 0C 0C 0C 0C' - '0C 0C 0C 08 0D 00 00 01 0A 0C 0C 0C 0C 0C 0C 0C' - '0C 0C 0C 09 0D 00 00 01 0B 0C 0C 0C 0C 0C 0C 0C' - '0C 0C 0C 09 0D 00 00 01 0B 0C 0C 0C 0C 0C 0C 0C' - '0C 0C 0C 09 0D 00 00 0D 03 03 03 03 03 03 03 02' - '03 02 03 02 0D 00 80 1F 00 00 80 0F 00 00 80 07' - '00 00 80 03 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 04 00 00 00 00 00 00 02 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 80 00 00 00 00 80 00 00 80 80 00 00 00 00' - '80 00 80 00 80 00 00 80 80 00 C0 C0 C0 00 80 80' - '80 00 FF 00 00 00 00 FF 00 00 FF FF 00 00 00 00' - 'FF 00 FF 00 FF 00 00 FF FF 00 FF FF FF 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08' - '88 87 88 78 88 78 88 88 00 00 00 00 00 00 00 00' - '7F F7 FF 7F FF 7F F7 78 80 00 00 00 00 00 00 04' - '7F 7F 77 F7 F7 F7 FF 8F 88 00 00 00 00 00 00 00' - '7F F7 FF 7F 7F 7F 7F 77 F8 80 00 00 00 00 00 08' - '7F 77 F7 F7 F7 F7 F7 77 FF 88 00 00 00 00 00 00' - '7F F7 7F 7F 7F 7F 7F 7F 7F F8 80 00 00 00 00 08' - '7F 7F F7 F7 F7 F7 F7 77 FF FF 88 00 00 00 00 00' - '7F F7 FF 7F 7F F7 FF 8F FF FF F8 80 00 00 00 08' - '7F 7F 77 F7 FF 7F F7 77 F7 F7 FF 84 00 00 00 00' - '7F F7 FF 7F 7F F7 FF 77 77 77 87 78 00 00 00 08' - '7F 7F F7 FF F7 FF 7F FF 7F F7 FF F7 00 00 00 00' - '7F F7 FF 7F 7F 7F F7 FF F7 FF F7 F8 00 00 00 08' - '7F FF F7 F7 FF F7 FF 7F 7F FF 7F F8 00 00 00 00' - '7F 7F FF FF 7F FF 7F FF F7 FF FF F8 00 00 00 04' - '7F FF 7F F7 FF 7F FF F7 FF FF F7 F7 00 00 00 00' - 'FF FF FF FF FF F7 FF FF 7F F7 FF F8 00 00 00 08' - '7F 7F F7 FF FF FF FF FF FF FF FF F8 00 00 00 00' - '7F FF FF F7 FF FF FF FF FF FF FF F8 00 00 00 08' - '7F FF FF FF FF 7F FF FF FF FF FF F7 00 00 00 00' - 'FF F7 FF FF FF FF FF FF FF FF FF F8 00 00 00 08' - '7F FF FF 7F FF FF FF FF FF FF FF F7 00 00 00 00' - '7F FF FF FF FF FF FF FF FF FF FF F8 00 00 00 08' - '7F FF FF FF FF FF FF FF FF FF FF F8 00 00 00 00' - 'FF FF FF FF FF FF FF FF FF FF FF F7 00 00 00 08' - '7F FF FF FF FF FF FF FF FF FF FF F8 00 00 00 00' - 'FF FF FF FF FF FF FF FF FF FF FF F7 00 00 00 04' - '7F FF FF FF FF FF FF FF FF FF FF F8 00 00 00 08' - '7F FF FF FF FF FF FF F7 FF FF FF F7 00 00 00 00' - '88 88 88 88 88 88 88 88 88 88 88 88 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 00' - '07 FF C0 00 03 FF C0 00 01 FF C0 00 00 FF C0 00' - '00 7F C0 00 00 3F C0 00 00 1F C0 00 00 1F C0 00' - '00 0F C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 28 00' - '00 00 10 00 00 00 20 00 00 00 01 00 04 00 00 00' - '00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 80' - '00 00 80 80 00 00 00 00 80 00 80 00 80 00 00 80' - '80 00 C0 C0 C0 00 80 80 80 00 FF 00 00 00 00 FF' - '00 00 FF FF 00 00 00 00 FF 00 FF 00 FF 00 00 FF' - 'FF 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 77' - '77 77 78 00 00 00 00 7F F7 FF 7F 80 00 00 04 7F' - '7F 7F 77 F8 00 00 00 77 F7 F7 F7 FF 70 00 00 FF' - '7F 7F 7F 7F 78 00 08 77 FF 7F F7 F7 F7 00 00 FF' - '7F FF 7F 7F F7 00 00 7F FF F7 FF FF F7 00 04 7F' - 'FF FF FF FF F7 00 00 FF FF FF FF FF F7 00 00 7F' - 'FF FF FF FF F7 00 00 FF FF FF FF FF F7 00 04 7F' - 'FF FF FF FF F7 00 00 FF FF FF FF FF F7 00 00 88' - '88 88 88 88 88 00 80 1F 00 00 80 0F 00 00 80 07' - '00 00 80 03 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 28 00 00 00 30 00 00 00 60 00' - '00 00 01 00 08 00 00 00 00 00 00 09 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 11 11 11 00 22 22 22 00 33 33 33 00 44 44' - '44 00 66 66 66 00 77 77 77 00 7F 7F 7F 00 88 88' - '88 00 99 99 99 00 AA AA AA 00 BB BB BB 00 CC CC' - 'CC 00 DD DD DD 00 EE EE EE 00 FF FF FF 00 00 00' - '00 00 33 00 32 00 5C 00 64 00 6F 00 63 00 75 00' - '6D 00 65 00 6E 00 74 00 2E 00 69 00 63 00 6F 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 C0 17 95 00 00 00 38 00 A8 44' - 'F9 77 13 00 00 00 18 0A 38 00 00 00 38 00 18 6C' - '38 00 98 17 95 00 00 00 00 00 E0 19 95 00 F0 88' - 'FA 77 70 38 F5 77 FF FF FF FF A8 44 F9 77 70 7D' - 'F5 77 3A 8A F5 77 86 00 00 00 86 00 00 00 08 00' - '00 00 B0 18 95 00 00 00 00 00 CB 44 F9 77 38 9F' - '07 00 CD 8B F5 77 78 13 05 00 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00' - '00 00 00 00 00 00 5A 00 5C 00 00 EC FD 7F 1A 02' - '00 00 4C 16 95 00 40 9F 07 00 FC 15 95 00 FF FF' - 'FF FF B4 1A 95 00 45 00 00 00 28 02 00 00 FF FF' - 'FF FF E2 D8 F5 77 7D 9B F5 77 94 B6 01 00 00 00' - '05 00 F4 17 95 00 80 00 10 C0 B4 1A 95 00 F0 88' - 'FA 77 88 1C F5 77 FF FF FF FF 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 9B B2 E7 77 B7 00 00 00 02 00' - '00 00 A4 1A 95 00 01 00 00 00 18 00 00 00 00 00' - '00 00 10 19 95 00 42 00 00 00 00 00 00 00 F4 18' - '95 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00' - '00 00 02 00 00 00 01 01 F5 77 00 EC FD 7F 5A 00' - '00 00 00 00 00 00 03 00 00 00 62 00 1A 02 40 9F' - '07 00 00 00 00 00 40 9F 07 00 05 00 00 00 BE B3' - 'E7 77 4C 19 95 00 A3 B4 E7 77 F8 00 00 00 00 00' - '00 C0 00 00 00 00 00 00 00 00 02 00 00 00 80 00' - '00 00 00 00 00 00 8C 1A 95 00 7F E9 4B 00 08 6C' - '0C 01 00 00 00 C0 00 00 00 00 80 1A 95 00 02 00' - '00 00 80 00 00 00 00 00 00 00 C0 27 95 00 C4 F5' - 'AF 00 02 00 00 00 44 3A 5C 6F 73 65 78 70 65 72' - '74 73 5C 72 65 61 63 74 6F 73 5C 6C 69 62 5C 73' - '68 65 6C 6C 33 32 5C 64 6F 63 75 6D 65 6E 74 2E' - '69 63 6F 00 4B 00 14 1A 95 00 1F 3B D4 77 13 00' - '00 00 98 00 00 00 4F 3B D4 77 E0 19 95 00 33 3B' - 'D4 77 64 C5 F5 77 A9 F1 E7 77 F8 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 1A 95 00 18 6C' - '38 00 86 00 00 00 00 00 00 00 C9 F1 E7 77 86 00' - '00 00 A4 1A 95 00 08 00 00 00 00 00 00 00 86 00' - '00 00 86 00 00 00 08 00 00 00 F4 19 95 00 33 3B' - 'D4 77 B4 1A 95 00 09 48 E9 77 B8 10 E9 77 FF FF' - 'FF FF C9 F1 E7 77 16 EA 4B 00 F8 00 00 00 18 6C' - '38 00 86 00 00 00 58 1A 95 00 00 00 00 00 00 00' - '00 00 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 10 10 10 10 10 10 10 10 10 10 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 10 10 10 10 10 10 10 10 10 10 10 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 10 10 03 08 08 06 08 08 06 08 06 06 08 06 08' - '06 06 06 06 06 06 06 05 06 05 05 01 10 10 10 10' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 10 10 03 0C 0E 0E 0E 0D 0E 0E 0E 0E 0E 0E 0D' - '0E 0E 0E 0E 0E 0E 0E 0E 0E 0C 08 05 01 10 10 10' - '10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0D 0D 0D 0D 0D 0D 0D 0E 0D' - '0D 0D 0D 0E 0D 0D 0D 0E 0E 0B 09 0A 05 01 10 10' - '10 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 10 10 03 0B 0F 0C 0D 0D 0D 0D 0D 0D 0D 0D 0D' - '0D 0D 0D 0D 0D 0D 0D 0D 0D 0B 0A 0D 0A 05 02 10' - '10 10 10 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D' - '0E 0D 0D 0D 0D 0E 0D 0D 0E 0A 0A 0E 0D 09 06 01' - '10 10 10 10 00 00 00 00 00 00 00 00 00 00 00 00' - '00 10 10 03 0C 0E 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D' - '0D 0D 0D 0D 0D 0D 0D 0E 0D 0B 0A 0E 0D 0E 0A 06' - '01 10 10 10 10 00 00 00 00 00 00 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D' - '0D 0D 0D 0E 0D 0D 0D 0D 0E 0B 0A 0E 0E 0E 0E 0A' - '05 01 10 10 10 10 00 00 00 00 00 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0D 0D 0D 0D 0D 0D 0D 0E 0D' - '0D 0E 0D 0D 0D 0D 0E 0D 0E 0B 0A 0E 0E 0E 0E 0F' - '0A 06 01 10 10 10 10 00 00 00 00 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0E 0D 0D 0D 0E 0D 0D 0D 0D' - '0E 0D 0E 0D 0E 0D 0D 0E 0D 0B 0A 0E 0F 0E 0E 0E' - '0F 0A 05 01 10 10 10 10 00 00 00 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0D 0D 0E 0D 0D 0D 0E 0D 0E' - '0D 0D 0D 0E 0D 0D 0E 0D 0E 0B 0A 0F 0E 0E 0F 0E' - '0F 0E 0B 05 02 10 10 10 10 00 00 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0D 0E 0D 0D 0E 0D 0D 0E 0D' - '0E 0D 0E 0D 0E 0E 0D 0E 0E 0B 0B 0E 0F 0E 0E 0F' - '0E 0F 0F 0B 05 01 10 10 10 10 00 00 00 00 00 00' - '00 10 10 03 0C 0F 0E 0D 0D 0D 0E 0D 0E 0D 0D 0E' - '0D 0E 0D 0E 0D 0D 0E 0D 0E 0B 0A 0F 0E 0F 0F 0F' - '0F 0F 0F 0F 0B 06 01 10 10 10 10 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0E 0D 0E 0D 0D 0E 0E 0D 0E' - '0D 0D 0E 0D 0E 0E 0D 0E 0E 0B 0B 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0B 05 02 10 10 10 00 00 00 00 00' - '00 10 10 03 0C 0F 0E 0D 0E 0D 0E 0E 0D 0D 0E 0D' - '0E 0E 0D 0E 0E 0D 0E 0E 0E 0B 09 0B 0B 0B 0A 0B' - '0B 0A 0B 0A 0B 0B 09 05 10 10 10 00 00 00 00 00' - '00 10 10 03 0C 0F 0E 0D 0D 0E 0D 0D 0E 0E 0D 0E' - '0D 0E 0E 0D 0E 0E 0E 0D 0E 0D 0C 0C 0C 0C 0C 0C' - '0C 0C 0C 0C 0B 0C 0C 06 10 10 10 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0E 0E 0D 0E 0E 0E 0D 0E 0E' - '0E 0E 0D 0E 0E 0E 0D 0E 0E 0E 0E 0E 0E 0E 0E 0E' - '0E 0E 0E 0E 0F 0E 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0C 0F 0E 0D 0E 0E 0D 0E 0E 0E 0D 0E' - '0E 0D 0E 0E 0D 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E' - '0E 0E 0E 0E 0E 0F 0D 06 10 10 10 00 00 00 00 00' - '00 10 10 03 0C 0F 0D 0E 0E 0E 0E 0D 0E 0D 0E 0E' - '0D 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E' - '0F 0E 0E 0E 0E 0E 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 03 0D 0F 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E' - '0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E' - '0E 0E 0E 0E 0E 0F 0D 06 10 10 10 00 00 00 00 00' - '00 10 10 03 0C 0F 0E 0E 0E 0E 0E 0F 0E 0E 0E 0E' - '0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0F 0E' - '0E 0E 0E 0F 0E 0E 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0C 0F 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E' - '0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0F 0E 0E 0E 0E' - '0E 0F 0E 0E 0E 0F 0E 06 10 10 10 00 00 00 00 00' - '00 10 10 03 0C 0F 0F 0E 0E 0E 0F 0E 0F 0E 0F 0E' - '0F 0E 0E 0E 0E 0E 0E 0F 0E 0E 0E 0E 0E 0E 0E 0F' - '0E 0E 0F 0E 0F 0E 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 03 0D 0F 0E 0E 0E 0F 0E 0E 0E 0F 0E 0F' - '0E 0F 0E 0F 0F 0E 0F 0E 0F 0E 0F 0E 0F 0E 0E 0E' - '0F 0E 0E 0E 0E 0F 0E 06 10 10 10 00 00 00 00 00' - '00 10 10 04 0C 0F 0E 0E 0F 0E 0E 0E 0F 0E 0F 0E' - '0F 0E 0F 0F 0E 0F 0F 0F 0E 0F 0F 0F 0E 0F 0E 0F' - '0E 0F 0E 0F 0F 0E 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 03 0D 0F 0E 0E 0E 0F 0E 0F 0E 0E 0F 0F' - '0E 0F 0F 0E 0F 0E 0F 0F 0F 0F 0E 0F 0F 0F 0F 0E' - '0F 0E 0E 0E 0F 0F 0E 06 10 10 10 00 00 00 00 00' - '00 10 10 04 0C 0F 0F 0E 0F 0E 0F 0E 0F 0F 0F 0E' - '0F 0F 0E 0F 0F 0F 0F 0E 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0E 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 03 0D 0F 0E 0F 0E 0F 0E 0F 0E 0F 0E 0F' - '0F 0E 0F 0E 0F 0F 0F 0F 0F 0E 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 06 01 10 10 00 00 00 00 00' - '00 10 10 04 0C 0F 0F 0E 0E 0F 0F 0E 0F 0E 0F 0E' - '0F 0F 0F 0F 0F 0E 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 03 0D 0F 0E 0F 0F 0E 0E 0F 0F 0F 0F 0F' - '0E 0F 0F 0F 0F 0F 0F 0E 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 06 10 10 10 00 00 00 00 00' - '00 10 10 04 0C 0F 0F 0F 0E 0F 0F 0F 0E 0F 0F 0E' - '0F 0F 0E 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0D 0F 0E 0F 0F 0E 0F 0F 0F 0E 0F 0F' - '0F 0F 0F 0F 0E 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 03 0C 0F 0F 0E 0F 0F 0F 0E 0F 0F 0F 0F' - '0F 0E 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0D 0F 0F 0F 0E 0F 0F 0F 0F 0F 0E 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 03 0D 0F 0F 0E 0F 0F 0F 0F 0E 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0C 0F 0F 0F 0F 0F 0E 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0D 0F 0F 0F 0E 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 03 0D 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0C 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0D 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0C 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0D 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0E 08 10 10 10 00 00 00 00 00' - '00 10 10 04 0D 0F 0F 0F 0F 0F 0F 0F 0F 0E 0F 0F' - '0E 0F 0E 0F 0E 0F 0F 0E 0F 0E 0F 0F 0F 0F 0E 0F' - '0F 0F 0E 0F 0E 0F 0E 09 10 10 10 00 00 00 00 00' - '00 10 10 02 0A 09 09 09 09 09 09 09 09 09 09 09' - '09 09 09 08 09 09 08 09 08 09 08 08 08 08 08 08' - '08 08 08 08 08 08 08 05 10 10 10 00 00 00 00 00' - '00 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 10 10 10 10 10 10 10 10 00 00 00 00 00' - '00 00 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10' - '10 10 10 10 10 10 10 10 10 10 00 00 00 00 F0 00' - '00 01 FF FF 00 00 E0 00 00 00 FF FF 00 00 E0 00' - '00 00 7F FF 00 00 E0 00 00 00 3F FF 00 00 E0 00' - '00 00 1F FF 00 00 E0 00 00 00 0F FF 00 00 E0 00' - '00 00 07 FF 00 00 E0 00 00 00 03 FF 00 00 E0 00' - '00 00 01 FF 00 00 E0 00 00 00 00 FF 00 00 E0 00' - '00 00 00 7F 00 00 E0 00 00 00 00 3F 00 00 E0 00' - '00 00 00 1F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 F0 00 00 00 00 0F 00 00 28 00' - '00 00 30 00 00 00 60 00 00 00 01 00 20 00 00 00' - '00 00 00 24 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 04 00 00 00 29 00 00' - '00 38 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 38 00 00 00 34 00 00 00 19 00 00' - '00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 01 00 00 00 76 00 00 00 F1 00 00' - '00 F9 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F8 00 00 00 F2 00 00 00 B2 00 00' - '00 49 00 00 00 05 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 5A 00 00 00 FF 01 01 01 FF 06 06' - '06 FF 05 05 05 FF 05 05 05 FF 05 05 05 FF 05 05' - '05 FF 05 05 05 FF 05 05 05 FF 05 05 05 FF 05 05' - '05 FF 05 05 05 FF 05 05 05 FF 05 05 05 FF 05 05' - '05 FF 05 05 05 FF 05 05 05 FF 05 05 05 FF 04 04' - '04 FF 04 04 04 FF 04 04 04 FF 03 03 03 FF 02 02' - '02 FF 03 03 03 FF 02 02 02 FF 00 00 00 FF 00 00' - '00 D9 00 00 00 39 00 00 00 06 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 2E 2E 2E FF 88 88' - '88 FF 87 87 87 FF 86 86 86 FF 86 86 86 FF 85 85' - '85 FF 84 84 84 FF 84 84 84 FF 83 83 83 FF 82 82' - '82 FF 82 82 82 FF 81 81 81 FF 80 80 80 FF 7F 7F' - '7F FF 7F 7F 7F FF 7E 7E 7E FF 7D 7D 7D FF 7C 7C' - '7C FF 7C 7C 7C FF 7B 7B 7B FF 74 74 74 FF 72 72' - '72 FF 73 73 73 FF 67 67 67 FF 18 18 18 FF 00 00' - '00 FF 00 00 00 D9 00 00 00 58 00 00 00 03 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 38 38 38 FF C1 C1' - 'C1 FF F2 F2 F2 FF EA EA EA FF EA EA EA FF EA EA' - 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E9 E9' - 'E9 FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' - 'E8 FF E8 E8 E8 FF E9 E9 E9 FF EB EB EB FF EA EA' - 'EA FF EA EA EA FF EA EA EA FF EB EB EB FF F1 F1' - 'F1 FF C4 C4 C4 FF 8C 8C 8C FF 67 67 67 FF 15 15' - '15 FF 00 00 00 FF 00 00 00 DC 00 00 00 4C 00 00' - '00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 35 35 35 FF C8 C8' - 'C8 FF FA FA FA FF DD DD DD FF DE DE DE FF DE DE' - 'DE FF DF DF DF FF DF DF DF FF DF DF DF FF DF DF' - 'DF FF E0 E0 E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E1 E1 E1 FF E1 E1 E1 FF E2 E2 E2 FF E2 E2' - 'E2 FF E2 E2 E2 FF E2 E2 E2 FF E3 E3 E3 FF E8 E8' - 'E8 FF BA BA BA FF A3 A3 A3 FF A6 A6 A6 FF 6B 6B' - '6B FF 0B 0B 0B FF 00 00 00 FF 00 00 00 EB 00 00' - '00 4B 00 00 00 03 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 35 35 35 FF C7 C7' - 'C7 FF F9 F9 F9 FF DA DA DA FF DA DA DA FF DA DA' - 'DA FF DA DA DA FF DB DB DB FF DB DB DB FF DB DB' - 'DB FF DC DC DC FF DC DC DC FF DC DC DC FF DC DC' - 'DC FF DD DD DD FF DD DD DD FF DD DD DD FF DD DD' - 'DD FF DD DD DD FF DD DD DD FF DE DE DE FF E3 E3' - 'E3 FF B7 B7 B7 FF A8 A8 A8 FF DE DE DE FF A8 A8' - 'A8 FF 6C 6C 6C FF 25 25 25 FF 00 00 00 FF 00 00' - '00 DE 00 00 00 57 00 00 00 03 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 35 35 35 FF C7 C7' - 'C7 FF F9 F9 F9 FF DC DC DC FF DB DB DB FF DB DB' - 'DB FF DC DC DC FF DD DD DD FF DD DD DD FF DD DD' - 'DD FF DD DD DD FF DE DE DE FF DE DE DE FF DE DE' - 'DE FF DE DE DE FF DE DE DE FF DF DF DF FF DE DE' - 'DE FF DF DF DF FF DF DF DF FF E0 E0 E0 FF E5 E5' - 'E5 FF B8 B8 B8 FF A7 A7 A7 FF EB EB EB FF DE DE' - 'DE FF A4 A4 A4 FF 72 72 72 FF 17 17 17 FF 00 00' - '00 FF 00 00 00 DC 00 00 00 4C 00 00 00 06 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 36 36 36 FF C8 C8' - 'C8 FF FA FA FA FF DC DC DC FF DD DD DD FF DD DD' - 'DD FF DD DD DD FF DD DD DD FF DE DE DE FF DE DE' - 'DE FF DE DE DE FF DE DE DE FF DF DF DF FF DF DF' - 'DF FF DF DF DF FF DF DF DF FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF E0 E0 E0 FF E1 E1 E1 FF E6 E6' - 'E6 FF B9 B9 B9 FF A9 A9 A9 FF EA EA EA FF EA EA' - 'EA FF E9 E9 E9 FF B1 B1 B1 FF 72 72 72 FF 14 14' - '14 FF 00 00 00 FF 00 00 00 EB 00 00 00 4B 00 00' - '00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 37 37 37 FF C9 C9' - 'C9 FF FA FA FA FF DD DD DD FF DE DE DE FF DE DE' - 'DE FF DE DE DE FF DE DE DE FF DF DF DF FF DF DF' - 'DF FF DF DF DF FF DF DF DF FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF E0 E0 E0 FF E1 E1 E1 FF E1 E1' - 'E1 FF E1 E1 E1 FF E1 E1 E1 FF E2 E2 E2 FF E7 E7' - 'E7 FF BA BA BA FF AA AA AA FF EC EC EC FF EA EA' - 'EA FF ED ED ED FF EA EA EA FF B2 B2 B2 FF 6A 6A' - '6A FF 1C 1C 1C FF 00 00 00 FF 00 00 00 DE 00 00' - '00 57 00 00 00 04 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 37 37 37 FF C9 C9' - 'C9 FF FA FA FA FF DF DF DF FF DE DE DE FF DF DF' - 'DF FF DF DF DF FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF E1 E1 E1 FF E1 E1 E1 FF E1 E1' - 'E1 FF E1 E1 E1 FF E2 E2 E2 FF E2 E2 E2 FF E2 E2' - 'E2 FF E2 E2 E2 FF E3 E3 E3 FF E3 E3 E3 FF E8 E8' - 'E8 FF BA BA BA FF AC AC AC FF EF EF EF FF ED ED' - 'ED FF ED ED ED FF F1 F1 F1 FF F1 F1 F1 FF B1 B1' - 'B1 FF 6B 6B 6B FF 13 13 13 FF 00 00 00 FF 00 00' - '00 DC 00 00 00 46 00 00 00 05 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 37 37 37 FF C9 C9' - 'C9 FF FA FA FA FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF E1 E1 E1 FF E1 E1 E1 FF E1 E1' - 'E1 FF E1 E1 E1 FF E2 E2 E2 FF E2 E2 E2 FF E2 E2' - 'E2 FF E2 E2 E2 FF E2 E2 E2 FF E3 E3 E3 FF E3 E3' - 'E3 FF E3 E3 E3 FF E3 E3 E3 FF E4 E4 E4 FF E9 E9' - 'E9 FF BB BB BB FF AD AD AD FF F3 F3 F3 FF F1 F1' - 'F1 FF F1 F1 F1 FF F1 F1 F1 FF F4 F4 F4 FF F5 F5' - 'F5 FF B5 B5 B5 FF 70 70 70 FF 14 14 14 FF 00 00' - '00 FF 00 00 00 E0 00 00 00 3F 00 00 00 02 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 37 37 37 FF CA CA' - 'CA FF FB FB FB FF E1 E1 E1 FF E1 E1 E1 FF E1 E1' - 'E1 FF E1 E1 E1 FF E2 E2 E2 FF E2 E2 E2 FF E2 E2' - 'E2 FF E2 E2 E2 FF E3 E3 E3 FF E3 E3 E3 FF E3 E3' - 'E3 FF E3 E3 E3 FF E3 E3 E3 FF E4 E4 E4 FF E4 E4' - 'E4 FF E4 E4 E4 FF E4 E4 E4 FF E5 E5 E5 FF EA EA' - 'EA FF BC BC BC FF AF AF AF FF F5 F5 F5 FF F3 F3' - 'F3 FF F3 F3 F3 FF F3 F3 F3 FF F4 F4 F4 FF F8 F8' - 'F8 FF F4 F4 F4 FF B6 B6 B6 FF 6C 6C 6C FF 22 22' - '22 FF 00 00 00 FF 00 00 00 D4 00 00 00 49 00 00' - '00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 38 38 38 FF CA CA' - 'CA FF FB FB FB FF E2 E2 E2 FF E1 E1 E1 FF E2 E2' - 'E2 FF E2 E2 E2 FF E3 E3 E3 FF E3 E3 E3 FF E3 E3' - 'E3 FF E3 E3 E3 FF E4 E4 E4 FF E3 E3 E3 FF E3 E3' - 'E3 FF E4 E4 E4 FF E5 E5 E5 FF E5 E5 E5 FF E5 E5' - 'E5 FF E5 E5 E5 FF E6 E6 E6 FF E6 E6 E6 FF EB EB' - 'EB FF BC BC BC FF B1 B1 B1 FF F8 F8 F8 FF F6 F6' - 'F6 FF F6 F6 F6 FF F7 F7 F7 FF F8 F8 F8 FF F8 F8' - 'F8 FF FC FC FC FF FC FC FC FF B5 B5 B5 FF 70 70' - '70 FF 18 18 18 FF 00 00 00 FF 00 00 00 D4 00 00' - '00 3F 00 00 00 05 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 38 38 38 FF CB CB' - 'CB FF FC FC FC FF E3 E3 E3 FF E3 E3 E3 FF E3 E3' - 'E3 FF E3 E3 E3 FF E4 E4 E4 FF E4 E4 E4 FF E4 E4' - 'E4 FF E4 E4 E4 FF E5 E5 E5 FF E5 E5 E5 FF E5 E5' - 'E5 FF E5 E5 E5 FF E6 E6 E6 FF E6 E6 E6 FF E6 E6' - 'E6 FF E6 E6 E6 FF E6 E6 E6 FF E6 E6 E6 FF EC EC' - 'EC FF BD BD BD FF B3 B3 B3 FF FC FC FC FF F9 F9' - 'F9 FF FA FA FA FF FA FA FA FF FB FB FB FF FC FC' - 'FC FF FC FC FC FF FF FF FF FF FF FF FF FF BA BA' - 'BA FF 75 75 75 FF 18 18 18 FF 00 00 00 FF 00 00' - '00 E0 00 00 00 3E 00 00 00 03 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 38 38 38 FF CB CB' - 'CB FF FC FC FC FF E4 E4 E4 FF E4 E4 E4 FF E4 E4' - 'E4 FF E4 E4 E4 FF E5 E5 E5 FF E5 E5 E5 FF E5 E5' - 'E5 FF E5 E5 E5 FF E6 E6 E6 FF E6 E6 E6 FF E6 E6' - 'E6 FF E6 E6 E6 FF E6 E6 E6 FF E7 E7 E7 FF E7 E7' - 'E7 FF E7 E7 E7 FF E7 E7 E7 FF E7 E7 E7 FF ED ED' - 'ED FF BE BE BE FF B5 B5 B5 FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FB FB' - 'FB FF BE BE BE FF 6E 6E 6E FF 1C 1C 1C FF 00 00' - '00 FF 00 00 00 BB 00 00 00 1F 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 38 38 38 FF CC CC' - 'CC FF FC FC FC FF E4 E4 E4 FF E4 E4 E4 FF E5 E5' - 'E5 FF E5 E5 E5 FF E6 E6 E6 FF E5 E5 E5 FF E5 E5' - 'E5 FF E6 E6 E6 FF E7 E7 E7 FF E6 E6 E6 FF E6 E6' - 'E6 FF E7 E7 E7 FF E7 E7 E7 FF E8 E8 E8 FF E8 E8' - 'E8 FF E8 E8 E8 FF E8 E8 E8 FF E9 E9 E9 FF EE EE' - 'EE FF C3 C3 C3 FF 9A 9A 9A FF B9 B9 B9 FF B8 B8' - 'B8 FF B7 B7 B7 FF B7 B7 B7 FF B7 B7 B7 FF B7 B7' - 'B7 FF B6 B6 B6 FF B5 B5 B5 FF B5 B5 B5 FF B8 B8' - 'B8 FF B5 B5 B5 FF 91 91 91 FF 67 67 67 FF 01 01' - '01 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 39 39 39 FF CC CC' - 'CC FF FC FC FC FF E6 E6 E6 FF E7 E7 E7 FF E6 E6' - 'E6 FF E6 E6 E6 FF E6 E6 E6 FF E7 E7 E7 FF E7 E7' - 'E7 FF E7 E7 E7 FF E7 E7 E7 FF E8 E8 E8 FF E8 E8' - 'E8 FF E8 E8 E8 FF E8 E8 E8 FF E9 E9 E9 FF E9 E9' - 'E9 FF E9 E9 E9 FF E9 E9 E9 FF EA EA EA FF EB EB' - 'EB FF E2 E2 E2 FF CD CD CD FF CA CA CA FF CA CA' - 'CA FF CA CA CA FF CA CA CA FF C9 C9 C9 FF C9 C9' - 'C9 FF C9 C9 C9 FF C9 C9 C9 FF C9 C9 C9 FF C9 C9' - 'C9 FF CD CD CD FF CC CC CC FF 7A 7A 7A FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 39 39 39 FF CC CC' - 'CC FF FC FC FC FF E8 E8 E8 FF E8 E8 E8 FF E8 E8' - 'E8 FF E7 E7 E7 FF E7 E7 E7 FF E8 E8 E8 FF E8 E8' - 'E8 FF E8 E8 E8 FF E8 E8 E8 FF E9 E9 E9 FF E9 E9' - 'E9 FF E9 E9 E9 FF E9 E9 E9 FF EA EA EA FF EA EA' - 'EA FF EA EA EA FF EA EA EA FF EB EB EB FF EB EB' - 'EB FF ED ED ED FF EF EF EF FF F0 F0 F0 FF F0 F0' - 'F0 FF F0 F0 F0 FF F0 F0 F0 FF F1 F1 F1 FF F1 F1' - 'F1 FF F1 F1 F1 FF F1 F1 F1 FF F2 F2 F2 FF F2 F2' - 'F2 FF F6 F6 F6 FF EC EC EC FF 7A 7A 7A FF 04 04' - '04 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3A 3A 3A FF CE CE' - 'CE FF FD FD FD FF E9 E9 E9 FF E9 E9 E9 FF EA EA' - 'EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E9 E9' - 'E9 FF E9 E9 E9 FF EA EA EA FF EA EA EA FF EA EA' - 'EA FF EA EA EA FF EB EB EB FF EB EB EB FF EA EA' - 'EA FF EB EB EB FF EC EC EC FF EC EC EC FF EC EC' - 'EC FF EC EC EC FF EC EC EC FF ED ED ED FF ED ED' - 'ED FF ED ED ED FF ED ED ED FF EE EE EE FF ED ED' - 'ED FF ED ED ED FF EE EE EE FF EF EF EF FF EF EF' - 'EF FF F2 F2 F2 FF E9 E9 E9 FF 7B 7B 7B FF 04 04' - '04 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3A 3A 3A FF CD CD' - 'CD FF FD FD FD FF EA EA EA FF EB EB EB FF EC EC' - 'EC FF ED ED ED FF EC EC EC FF E9 E9 E9 FF EA EA' - 'EA FF EA EA EA FF EB EB EB FF EB EB EB FF EB EB' - 'EB FF EB EB EB FF EB EB EB FF EC EC EC FF EC EC' - 'EC FF EC EC EC FF EC EC EC FF ED ED ED FF ED ED' - 'ED FF ED ED ED FF ED ED ED FF EE EE EE FF EE EE' - 'EE FF EE EE EE FF EE EE EE FF EF EF EF FF EF EF' - 'EF FF EF EF EF FF EF EF EF FF F0 F0 F0 FF F0 F0' - 'F0 FF F4 F4 F4 FF E9 E9 E9 FF 7B 7B 7B FF 04 04' - '04 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3A 3A 3A FF CE CE' - 'CE FF FE FE FE FF EC EC EC FF EC EC EC FF ED ED' - 'ED FF EE EE EE FF EF EF EF FF EE EE EE FF EB EB' - 'EB FF EB EB EB FF EC EC EC FF EC EC EC FF EC EC' - 'EC FF EC EC EC FF EC EC EC FF ED ED ED FF ED ED' - 'ED FF ED ED ED FF ED ED ED FF EE EE EE FF EE EE' - 'EE FF EE EE EE FF EE EE EE FF EF EF EF FF EF EF' - 'EF FF EF EF EF FF EF EF EF FF F0 F0 F0 FF F0 F0' - 'F0 FF F0 F0 F0 FF F0 F0 F0 FF F1 F1 F1 FF F1 F1' - 'F1 FF F4 F4 F4 FF EA EA EA FF 7C 7C 7C FF 04 04' - '04 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3B 3B 3B FF CE CE' - 'CE FF FE FE FE FF ED ED ED FF ED ED ED FF EE EE' - 'EE FF EF EF EF FF F0 F0 F0 FF F1 F1 F1 FF F0 F0' - 'F0 FF EE EE EE FF EC EC EC FF EC EC EC FF ED ED' - 'ED FF ED ED ED FF EE EE EE FF EE EE EE FF EE EE' - 'EE FF EE EE EE FF EF EF EF FF EF EF EF FF EF EF' - 'EF FF EF EF EF FF EF EF EF FF F0 F0 F0 FF F0 F0' - 'F0 FF F0 F0 F0 FF F0 F0 F0 FF F1 F1 F1 FF F1 F1' - 'F1 FF F1 F1 F1 FF F1 F1 F1 FF F2 F2 F2 FF F1 F1' - 'F1 FF F5 F5 F5 FF EA EA EA FF 7D 7D 7D FF 04 04' - '04 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3B 3B 3B FF CF CF' - 'CF FF FE FE FE FF EE EE EE FF EF EF EF FF F0 F0' - 'F0 FF F0 F0 F0 FF F1 F1 F1 FF F2 F2 F2 FF F2 F2' - 'F2 FF F3 F3 F3 FF F1 F1 F1 FF EE EE EE FF EE EE' - 'EE FF EE EE EE FF EE EE EE FF EE EE EE FF EF EF' - 'EF FF EF EF EF FF EF EF EF FF EF EF EF FF F0 F0' - 'F0 FF F0 F0 F0 FF F0 F0 F0 FF F1 F1 F1 FF F1 F1' - 'F1 FF F1 F1 F1 FF F1 F1 F1 FF F2 F2 F2 FF F2 F2' - 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F3 F3 F3 FF F3 F3' - 'F3 FF F6 F6 F6 FF EB EB EB FF 7D 7D 7D FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3B 3B 3B FF CF CF' - 'CF FF FF FF FF FF F0 F0 F0 FF F0 F0 F0 FF F0 F0' - 'F0 FF F1 F1 F1 FF F2 F2 F2 FF F3 F3 F3 FF F3 F3' - 'F3 FF F4 F4 F4 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' - 'F4 FF F3 F3 F3 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' - 'F1 FF F0 F0 F0 FF F0 F0 F0 FF F1 F1 F1 FF F1 F1' - 'F1 FF F0 F0 F0 FF F0 F0 F0 FF F1 F1 F1 FF F2 F2' - 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F3 F3 F3 FF F3 F3' - 'F3 FF F3 F3 F3 FF F3 F3 F3 FF F4 F4 F4 FF F4 F4' - 'F4 FF F7 F7 F7 FF EB EB EB FF 7E 7E 7E FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3C 3C 3C FF D0 D0' - 'D0 FF FF FF FF FF F1 F1 F1 FF F1 F1 F1 FF F2 F2' - 'F2 FF F2 F2 F2 FF F3 F3 F3 FF F4 F4 F4 FF F4 F4' - 'F4 FF F5 F5 F5 FF F6 F6 F6 FF F6 F6 F6 FF F7 F7' - 'F7 FF F7 F7 F7 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8' - 'F8 FF F9 F9 F9 FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' - 'F8 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F3 F3' - 'F3 FF F3 F3 F3 FF F3 F3 F3 FF F3 F3 F3 FF F4 F4' - 'F4 FF F4 F4 F4 FF F4 F4 F4 FF F5 F5 F5 FF F5 F5' - 'F5 FF F8 F8 F8 FF EB EB EB FF 7E 7E 7E FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3D 3D 3D FF D1 D1' - 'D1 FF FF FF FF FF F2 F2 F2 FF F2 F2 F2 FF F3 F3' - 'F3 FF F3 F3 F3 FF F4 F4 F4 FF F5 F5 F5 FF F5 F5' - 'F5 FF F6 F6 F6 FF F7 F7 F7 FF F7 F7 F7 FF F8 F8' - 'F8 FF F8 F8 F8 FF F9 F9 F9 FF F9 F9 F9 FF F9 F9' - 'F9 FF FA FA FA FF FB FB FB FF FB FB FB FF FC FC' - 'FC FF FC FC FC FF FC FC FC FF FC FC FC FF FC FC' - 'FC FF FA FA FA FF F7 F7 F7 FF F5 F5 F5 FF F5 F5' - 'F5 FF F5 F5 F5 FF F5 F5 F5 FF F6 F6 F6 FF F6 F6' - 'F6 FF F9 F9 F9 FF EC EC EC FF 7F 7F 7F FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3D 3D 3D FF D1 D1' - 'D1 FF FF FF FF FF F3 F3 F3 FF F3 F3 F3 FF F4 F4' - 'F4 FF F4 F4 F4 FF F5 F5 F5 FF F6 F6 F6 FF F6 F6' - 'F6 FF F7 F7 F7 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8' - 'F8 FF F9 F9 F9 FF F9 F9 F9 FF FA FA FA FF FA FA' - 'FA FF FA FA FA FF FA FA FA FF FB FB FB FF FC FC' - 'FC FF FC FC FC FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FD FD FD FF FE FE FE FF FD FD FD FF FA FA' - 'FA FF F7 F7 F7 FF F5 F5 F5 FF F6 F6 F6 FF F7 F7' - 'F7 FF FA FA FA FF EC EC EC FF 80 80 80 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3E 3E 3E FF D2 D2' - 'D2 FF FF FF FF FF F4 F4 F4 FF F5 F5 F5 FF F5 F5' - 'F5 FF F5 F5 F5 FF F7 F7 F7 FF F7 F7 F7 FF F7 F7' - 'F7 FF F8 F8 F8 FF F9 F9 F9 FF F9 F9 F9 FF F9 F9' - 'F9 FF FA FA FA FF FA FA FA FF FB FB FB FF FB FB' - 'FB FF FB FB FB FF FC FC FC FF FC FC FC FF FC FC' - 'FC FF FD FD FD FF FD FD FD FF FD FD FD FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FF FF' - 'FF FF FE FE FE FF FB FB FB FF F8 F8 F8 FF F7 F7' - 'F7 FF FB FB FB FF ED ED ED FF 81 81 81 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3E 3E 3E FF D2 D2' - 'D2 FF FF FF FF FF F5 F5 F5 FF F6 F6 F6 FF F6 F6' - 'F6 FF F6 F6 F6 FF F7 F7 F7 FF F8 F8 F8 FF F8 F8' - 'F8 FF F8 F8 F8 FF F8 F8 F8 FF F9 F9 F9 FF FA FA' - 'FA FF FA FA FA FF FA FA FA FF FB FB FB FF FB FB' - 'FB FF FC FC FC FF FC FC FC FF FC FC FC FF FD FD' - 'FD FF FD FD FD FF FD FD FD FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FA FA' - 'FA FF FC FC FC FF ED ED ED FF 80 80 80 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3E 3E 3E FF D3 D3' - 'D3 FF FF FF FF FF F6 F6 F6 FF F7 F7 F7 FF F7 F7' - 'F7 FF F7 F7 F7 FF F8 F8 F8 FF F9 F9 F9 FF F9 F9' - 'F9 FF F9 F9 F9 FF FA FA FA FF FA FA FA FF FA FA' - 'FA FF FB FB FB FF FB FB FB FF FC FC FC FF FC FC' - 'FC FF FC FC FC FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FD FD FD FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF EE EE EE FF 82 82 82 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3E 3E 3E FF D3 D3' - 'D3 FF FF FF FF FF F7 F7 F7 FF F7 F7 F7 FF F8 F8' - 'F8 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF FA FA' - 'FA FF FA FA FA FF FB FB FB FF FB FB FB FF FB FB' - 'FB FF FC FC FC FF FC FC FC FF FC FC FC FF FC FC' - 'FC FF FD FD FD FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F0 F0 F0 FF 82 82 82 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3F 3F 3F FF D4 D4' - 'D4 FF FF FF FF FF F8 F8 F8 FF F8 F8 F8 FF F9 F9' - 'F9 FF F9 F9 F9 FF FA FA FA FF FA FA FA FF FA FA' - 'FA FF FA FA FA FF FB FB FB FF FB FB FB FF FC FC' - 'FC FF FC FC FC FF FC FC FC FF FD FD FD FF FD FD' - 'FD FF FD FD FD FF FD FD FD FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF EF EF EF FF 82 82 82 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3F 3F 3F FF D5 D5' - 'D5 FF FF FF FF FF F9 F9 F9 FF F9 F9 F9 FF F9 F9' - 'F9 FF FA FA FA FF FB FB FB FF FB FB FB FF FB FB' - 'FB FF FC FC FC FF FC FC FC FF FC FC FC FF FC FC' - 'FC FF FD FD FD FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F0 F0 F0 FF 83 83 83 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 3F 3F 3F FF D5 D5' - 'D5 FF FF FF FF FF FA FA FA FF FA FA FA FF FB FB' - 'FB FF FB FB FB FF FB FB FB FF FB FB FB FF FC FC' - 'FC FF FC FC FC FF FC FC FC FF FD FD FD FF FD FD' - 'FD FF FD FD FD FF FD FD FD FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F0 F0 F0 FF 84 84 84 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 40 40 40 FF D5 D5' - 'D5 FF FF FF FF FF FB FB FB FF FB FB FB FF FB FB' - 'FB FF FC FC FC FF FC FC FC FF FC FC FC FF FC FC' - 'FC FF FD FD FD FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FD FD FD FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F0 F0 F0 FF 85 85 85 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 40 40 40 FF D6 D6' - 'D6 FF FF FF FF FF FB FB FB FF FB FB FB FF FC FC' - 'FC FF FC FC FC FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FD FD FD FF FD FD FD FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F0 F0 F0 FF 85 85 85 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 41 41 41 FF D6 D6' - 'D6 FF FF FF FF FF FB FB FB FF FC FC FC FF FD FD' - 'FD FF FD FD FD FF FC FC FC FF FD FD FD FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F0 F0 F0 FF 86 86 86 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 41 41 41 FF D6 D6' - 'D6 FF FF FF FF FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F1 F1 F1 FF 87 87 87 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 41 41 41 FF D7 D7' - 'D7 FF FF FF FF FF FD FD FD FF FD FD FD FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F1 F1 F1 FF 87 87 87 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 41 41 41 FF D7 D7' - 'D7 FF FF FF FF FF FE FE FE FF FE FE FE FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F1 F1 F1 FF 88 88 88 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 41 41 41 FF D7 D7' - 'D7 FF FF FF FF FF FE FE FE FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F1 F1 F1 FF 88 88 88 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 42 42 42 FF D8 D8' - 'D8 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F1 F1 F1 FF 89 89 89 FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 42 42 42 FF D9 D9' - 'D9 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F2 F2 F2 FF 8A 8A 8A FF 05 05' - '05 FF 00 00 00 F7 00 00 00 37 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 84 00 00 00 FF 47 47 47 FF D9 D9' - 'D9 FF FB FB FB FF F8 F8 F8 FF F8 F8 F8 FF F8 F8' - 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F7 F7 F7 FF F7 F7' - 'F7 FF F7 F7 F7 FF F7 F7 F7 FF F7 F7 F7 FF F7 F7' - 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6' - 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F5 F5' - 'F5 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' - 'F4 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4' - 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F3 F3' - 'F3 FF F6 F6 F6 FF E8 E8 E8 FF 8F 8F 8F FF 06 06' - '06 FF 00 00 00 F7 00 00 00 38 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 83 00 00 00 FF 2D 2D 2D FF 98 98' - '98 FF 98 98 98 FF 97 97 97 FF 97 97 97 FF 97 97' - '97 FF 96 96 96 FF 95 95 95 FF 95 95 95 FF 95 95' - '95 FF 93 93 93 FF 93 93 93 FF 93 93 93 FF 92 92' - '92 FF 91 91 91 FF 90 90 90 FF 90 90 90 FF 90 90' - '90 FF 8F 8F 8F FF 8F 8F 8F FF 8E 8E 8E FF 8C 8C' - '8C FF 8C 8C 8C FF 8C 8C 8C FF 8B 8B 8B FF 89 89' - '89 FF 89 89 89 FF 88 88 88 FF 88 88 88 FF 86 86' - '86 FF 86 86 86 FF 86 86 86 FF 86 86 86 FF 84 84' - '84 FF 84 84 84 FF 87 87 87 FF 6C 6C 6C FF 04 04' - '04 FF 00 00 00 F7 00 00 00 33 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 47 00 00 00 F5 00 00 00 FF 04 04' - '04 FF 04 04 04 FF 04 04 04 FF 04 04 04 FF 04 04' - '04 FF 04 04 04 FF 04 04 04 FF 04 04 04 FF 04 04' - '04 FF 04 04 04 FF 04 04 04 FF 04 04 04 FF 04 04' - '04 FF 04 04 04 FF 04 04 04 FF 04 04 04 FF 04 04' - '04 FF 04 04 04 FF 04 04 04 FF 04 04 04 FF 04 04' - '04 FF 03 03 03 FF 03 03 03 FF 03 03 03 FF 03 03' - '03 FF 03 03 03 FF 03 03 03 FF 03 03 03 FF 03 03' - '03 FF 03 03 03 FF 03 03 03 FF 03 03 03 FF 03 03' - '03 FF 03 03 03 FF 03 03 03 FF 03 03 03 FF 00 00' - '00 FF 00 00 00 C1 00 00 00 14 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 52 00 00 00 EA 00 00' - '00 F3 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F4 00 00' - '00 C7 00 00 00 29 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 F0 00 00 01 FF FF 00 00 E0 00' - '00 00 FF FF 00 00 E0 00 00 00 7F FF 00 00 E0 00' - '00 00 3F FF 00 00 E0 00 00 00 1F FF 00 00 E0 00' - '00 00 0F FF 00 00 E0 00 00 00 07 FF 00 00 E0 00' - '00 00 03 FF 00 00 E0 00 00 00 01 FF 00 00 E0 00' - '00 00 00 FF 00 00 E0 00 00 00 00 7F 00 00 E0 00' - '00 00 00 3F 00 00 E0 00 00 00 00 1F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 F0 00' - '00 00 00 0F 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 20 00 00 00 00 00 00 10 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 3C 00 00' - '00 64 00 00 00 61 00 00 00 61 00 00 00 61 00 00' - '00 61 00 00 00 61 00 00 00 61 00 00 00 61 00 00' - '00 61 00 00 00 61 00 00 00 61 00 00 00 61 00 00' - '00 61 00 00 00 61 00 00 00 62 00 00 00 41 00 00' - '00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 4D 01 01 01 FB 05 05' - '05 FF 03 03 03 FF 03 03 03 FF 02 02 02 FF 02 02' - '02 FF 02 02 02 FF 02 02 02 FF 02 02 02 FF 02 02' - '02 FF 02 02 02 FF 02 02 02 FF 02 02 02 FF 01 01' - '01 FF 00 00 00 FF 03 03 03 FF 01 01 01 FD 00 00' - '00 7F 00 00 00 02 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1A 1A 1A FF 8C 8C' - '8C FF 9F 9F 9F FF 9C 9C 9C FF 9A 9A 9A FF 9A 9A' - '9A FF 99 99 99 FF 99 99 99 FF 97 97 97 FF 97 97' - '97 FF 96 96 96 FF 96 96 96 FF 95 95 95 FF 93 93' - '93 FF 8E 8E 8E FF 7D 7D 7D FF 33 33 33 FF 00 00' - '00 FD 00 00 00 79 00 00 00 02 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1D 1D 1D FF C6 C6' - 'C6 FF F7 F7 F7 FF EA EA EA FF EB EB EB FF EB EB' - 'EB FF EB EB EB FF EB EB EB FF EC EC EC FF EC EC' - 'EC FF ED ED ED FF EE EE EE FF EE EE EE FF EF EF' - 'EF FF F3 F3 F3 FF B7 B7 B7 FF 94 94 94 FF 34 34' - '34 FF 00 00 00 FD 00 00 00 7A 00 00 00 02 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1C 1C 1C FF C4 C4' - 'C4 FF EB EB EB FF D8 D8 D8 FF DA DA DA FF DB DB' - 'DB FF DB DB DB FF DC DC DC FF DC DC DC FF DC DC' - 'DC FF DD DD DD FF DD DD DD FF DD DD DD FF DE DE' - 'DE FF E0 E0 E0 FF AE AE AE FF D1 D1 D1 FF A4 A4' - 'A4 FF 32 32 32 FF 00 00 00 FD 00 00 00 7A 00 00' - '00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1C 1C 1C FF C4 C4' - 'C4 FF ED ED ED FF DB DB DB FF DD DD DD FF DD DD' - 'DD FF DE DE DE FF DE DE DE FF DE DE DE FF DF DF' - 'DF FF DF DF DF FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E2 E2 E2 FF AE AE AE FF D9 D9 D9 FF E9 E9' - 'E9 FF A3 A3 A3 FF 2E 2E 2E FF 00 00 00 FD 00 00' - '00 7A 00 00 00 02 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1D 1D 1D FF C6 C6' - 'C6 FF EF EF EF FF DD DD DD FF DF DF DF FF DF DF' - 'DF FF DF DF DF FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E1 E1 E1 FF E1 E1 E1 FF E1 E1 E1 FF E2 E2' - 'E2 FF E4 E4 E4 FF B0 B0 B0 FF DA DA DA FF F1 F1' - 'F1 FF EC EC EC FF A3 A3 A3 FF 2B 2B 2B FF 00 00' - '00 FD 00 00 00 79 00 00 00 01 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1D 1D 1D FF C6 C6' - 'C6 FF EF EF EF FF DE DE DE FF E0 E0 E0 FF E0 E0' - 'E0 FF E1 E1 E1 FF E1 E1 E1 FF E1 E1 E1 FF E2 E2' - 'E2 FF E2 E2 E2 FF E3 E3 E3 FF E3 E3 E3 FF E3 E3' - 'E3 FF E5 E5 E5 FF B1 B1 B1 FF DE DE DE FF F2 F2' - 'F2 FF F4 F4 F4 FF F1 F1 F1 FF A4 A4 A4 FF 2B 2B' - '2B FF 00 00 00 FC 00 00 00 72 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1D 1D 1D FF C7 C7' - 'C7 FF F1 F1 F1 FF E0 E0 E0 FF E2 E2 E2 FF E2 E2' - 'E2 FF E2 E2 E2 FF E3 E3 E3 FF E3 E3 E3 FF E3 E3' - 'E3 FF E4 E4 E4 FF E4 E4 E4 FF E4 E4 E4 FF E5 E5' - 'E5 FF E7 E7 E7 FF B3 B3 B3 FF E1 E1 E1 FF F6 F6' - 'F6 FF F4 F4 F4 FF F9 F9 F9 FF F6 F6 F6 FF A6 A6' - 'A6 FF 2D 2D 2D FF 00 00 00 F9 00 00 00 6F 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1E 1E 1E FF C7 C7' - 'C7 FF F1 F1 F1 FF E2 E2 E2 FF E3 E3 E3 FF E3 E3' - 'E3 FF E4 E4 E4 FF E4 E4 E4 FF E5 E5 E5 FF E5 E5' - 'E5 FF E5 E5 E5 FF E5 E5 E5 FF E6 E6 E6 FF E6 E6' - 'E6 FF E8 E8 E8 FF B5 B5 B5 FF E8 E8 E8 FF FE FE' - 'FE FF FC FC FC FF FD FD FD FF FF FF FF FF FC FC' - 'FC FF AD AD AD FF 31 31 31 FF 00 00 00 F9 00 00' - '00 6F 00 00 00 01 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1E 1E 1E FF C8 C8' - 'C8 FF F3 F3 F3 FF E3 E3 E3 FF E5 E5 E5 FF E5 E5' - 'E5 FF E5 E5 E5 FF E6 E6 E6 FF E6 E6 E6 FF E6 E6' - 'E6 FF E7 E7 E7 FF E7 E7 E7 FF E7 E7 E7 FF E8 E8' - 'E8 FF EA EA EA FF B2 B2 B2 FF D8 D8 D8 FF EC EC' - 'EC FF EA EA EA FF EB EB EB FF EB EB EB FF EF EF' - 'EF FF EA EA EA FF 98 98 98 FF 2C 2C 2C FF 00 00' - '00 F5 00 00 00 42 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1E 1E 1E FF C8 C8' - 'C8 FF F3 F3 F3 FF E5 E5 E5 FF E6 E6 E6 FF E6 E6' - 'E6 FF E7 E7 E7 FF E7 E7 E7 FF E7 E7 E7 FF E8 E8' - 'E8 FF E8 E8 E8 FF E8 E8 E8 FF E9 E9 E9 FF E9 E9' - 'E9 FF EC EC EC FF C9 C9 C9 FF B3 B3 B3 FF B6 B6' - 'B6 FF B5 B5 B5 FF B5 B5 B5 FF B5 B5 B5 FF B4 B4' - 'B4 FF B6 B6 B6 FF BA BA BA FF 80 80 80 FF 03 03' - '03 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1F 1F 1F FF C9 C9' - 'C9 FF F5 F5 F5 FF E8 E8 E8 FF E8 E8 E8 FF E8 E8' - 'E8 FF E8 E8 E8 FF E9 E9 E9 FF E9 E9 E9 FF E9 E9' - 'E9 FF EA EA EA FF EA EA EA FF EA EA EA FF EB EB' - 'EB FF EB EB EB FF EE EE EE FF EE EE EE FF ED ED' - 'ED FF EE EE EE FF EE EE EE FF EF EF EF FF EF EF' - 'EF FF F0 F0 F0 FF F7 F7 F7 FF 93 93 93 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1F 1F 1F FF CA CA' - 'CA FF F7 F7 F7 FF EA EA EA FF EC EC EC FF EA EA' - 'EA FF E9 E9 E9 FF EA EA EA FF EA EA EA FF EB EB' - 'EB FF EB EB EB FF EC EC EC FF EC EC EC FF EC EC' - 'EC FF ED ED ED FF ED ED ED FF EE EE EE FF EE EE' - 'EE FF EE EE EE FF EF EF EF FF EF EF EF FF EF EF' - 'EF FF F1 F1 F1 FF F6 F6 F6 FF 93 93 93 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 1F 1F 1F FF CA CA' - 'CA FF F8 F8 F8 FF EC EC EC FF EE EE EE FF EF EF' - 'EF FF EE EE EE FF EB EB EB FF EB EB EB FF EC EC' - 'EC FF ED ED ED FF ED ED ED FF ED ED ED FF EE EE' - 'EE FF EE EE EE FF EF EF EF FF EF EF EF FF EF EF' - 'EF FF F0 F0 F0 FF F0 F0 F0 FF F0 F0 F0 FF F1 F1' - 'F1 FF F2 F2 F2 FF F7 F7 F7 FF 93 93 93 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 20 20 20 FF CB CB' - 'CB FF F9 F9 F9 FF EE EE EE FF F0 F0 F0 FF F1 F1' - 'F1 FF F2 F2 F2 FF F1 F1 F1 FF EE EE EE FF ED ED' - 'ED FF EE EE EE FF EE EE EE FF EE EE EE FF EF EF' - 'EF FF EF EF EF FF EF EF EF FF F0 F0 F0 FF F1 F1' - 'F1 FF F1 F1 F1 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' - 'F2 FF F3 F3 F3 FF F8 F8 F8 FF 94 94 94 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 20 20 20 FF CC CC' - 'CC FF FB FB FB FF F0 F0 F0 FF F2 F2 F2 FF F2 F2' - 'F2 FF F4 F4 F4 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 C0 DC C0 00 F0 CA A6 00 FF 00' + 'FF 00 8C 8C 8C 00 A5 A5 A5 00 A9 A9 A9 00 B5 B5' + 'B5 00 BA BA BA 00 C0 C0 C0 00 C2 C2 C2 00 C6 C6' + 'C6 00 C9 C9 C9 00 CA CA CA 00 CB CB CB 00 CC CC' + 'CC 00 CD CD CD 00 CE CE CE 00 CF CF CF 00 D0 D0' + 'D0 00 D1 D1 D1 00 D2 D2 D2 00 D3 D3 D3 00 D4 D4' + 'D4 00 D5 D5 D5 00 D6 D6 D6 00 D7 D7 D7 00 D8 D8' + 'D8 00 D9 D9 D9 00 DA DA DA 00 DE DE DE 00 E0 E0' + 'E0 00 E1 E1 E1 00 E2 E2 E2 00 E3 E3 E3 00 E5 E5' + 'E5 00 E6 E6 E6 00 E7 E7 E7 00 E8 E8 E8 00 E9 E9' + 'E9 00 EA EA EA 00 EB EB EB 00 EC EC EC 00 ED ED' + 'ED 00 EE EE EE 00 EF EF EF 00 F0 F0 F0 00 F1 F1' + 'F1 00 F2 F2 F2 00 F3 F3 F3 00 F4 F4 F4 00 F5 F5' + 'F5 00 F6 F6 F6 00 F7 F7 F7 00 F8 F8 F8 00 F9 F9' + 'F9 00 FA FA FA 00 FB FB FB 00 FC FC FC 00 FD FD' + 'FD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 FB' + 'FF 00 A4 A0 A0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 21 21 20 1F 1F 1D 1C 1B' + '19 17 16 1F 00 00 00 00 21 35 34 32 31 30 2E 2B' + '29 26 25 17 00 00 00 00 22 36 34 33 32 31 2F 2D' + '2A 29 27 18 00 00 00 00 23 37 34 34 33 32 31 2F' + '2E 2A 29 19 00 00 00 00 23 38 36 35 34 33 32 31' + '2F 2E 2C 1C 00 00 00 00 23 39 36 36 35 34 33 32' + '31 2F 2F 1D 00 00 00 00 23 39 38 37 36 35 34 33' + '32 31 31 1E 00 00 00 00 23 3B 38 38 37 36 35 34' + '33 32 32 1F 00 00 00 00 23 3C 3A 39 38 37 36 35' + '34 33 30 13 00 00 00 00 23 3C 3B 3A 39 38 37 36' + '35 35 13 1E 00 00 00 00 23 3D 3C 3B 3A 39 38 38' + '38 1B 0D 1B 00 00 00 00 23 3F 3D 3C 3B 3A 36 12' + '0B 0C 0F 00 00 00 00 00 23 40 3E 3D 3C 3C 1A 1A' + '2B 07 18 00 00 00 00 00 23 41 40 3E 3D 3C 28 15' + '14 18 00 00 00 00 00 00 24 42 41 40 3E 3D 29 0E' + '11 00 00 00 00 00 00 00 1F 21 20 20 20 1E 21 17' + '00 00 00 00 00 00 C0 03 00 00 C0 03 00 00 C0 03' + '00 00 C0 03 00 00 C0 03 00 00 C0 03 00 00 C0 03' + '00 00 C0 03 00 00 C0 03 00 00 C0 03 00 00 C0 03' + '00 00 C0 07 00 00 C0 07 00 00 C0 0F 00 00 C0 1F' + '00 00 C0 3F 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 20 00 00 00 00 00 40 04 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 A1 A1 A1 6C BF BF BF 9F BE BE' + 'BE A0 BC BC BC A0 BC BC BC A0 B9 B9 B9 A0 B7 B7' + 'B7 A0 B5 B5 B5 A0 B3 B3 B3 A0 B0 B0 B0 A0 AE AE' + 'AE 9F 94 94 94 64 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C4 C4 C4 AD F0 F0 F0 FF EF EF' + 'EF FF ED ED ED FF EC EC EC FF EB EB EB FF E9 E9' + 'E9 FF E6 E6 E6 FF E3 E3 E3 FF E0 E0 E0 FF DE DE' + 'DE FE AE AE AE 9E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C6 C6 C6 AD F1 F1 F1 FF EF EF' + 'EF FF EE EE EE FF ED ED ED FF EC EC EC FF EA EA' + 'EA FF E8 E8 E8 FF E5 E5 E5 FF E3 E3 E3 FF E1 E1' + 'E1 FE B0 B0 B0 9E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD F2 F2 F2 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF ED ED ED FF EC EC' + 'EC FF EA EA EA FF E9 E9 E9 FF E5 E5 E5 FF E3 E3' + 'E3 FE B2 B2 B2 9E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD F3 F3 F3 FF F1 F1' + 'F1 FF F0 F0 F0 FF EF EF EF FF EE EE EE FF ED ED' + 'ED FF EC EC EC FF EA EA EA FF E9 E9 E9 FF E7 E7' + 'E7 FE B6 B6 B6 9E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD F4 F4 F4 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF EF EF EF FF EE EE' + 'EE FF ED ED ED FF EC EC EC FF EA EA EA FF EA EA' + 'EA FE B8 B8 B8 9E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD F4 F4 F4 FF F3 F3' + 'F3 FF F2 F2 F2 FF F1 F1 F1 FF F0 F0 F0 FF EF EF' + 'EF FF EE EE EE FF ED ED ED FF EC EC EC FF EC EC' + 'EC FE B9 B9 B9 9E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD F6 F6 F6 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F1 F1 F1 FF F0 F0' + 'F0 FF EF EF EF FF EE EE EE FF ED ED ED FF ED ED' + 'ED FE BB BB BB 9E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD F7 F7 F7 FF F5 F5' + 'F5 FF F4 F4 F4 FF F3 F3 F3 FF F2 F2 F2 FF F1 F1' + 'F1 FF F0 F0 F0 FF EF EF EF FF EE EE EE FF EB EB' + 'EB FE A8 A8 A8 9E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD F7 F7 F7 FF F6 F6' + 'F6 FF F5 F5 F5 FF F4 F4 F4 FF F3 F3 F3 FF F2 F2' + 'F2 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF C9 C9' + 'C9 FE B2 B2 B2 8F 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD F8 F8 F8 FF F7 F7' + 'F7 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F3 F3 F3 FF D1 D1 D1 FE A7 A7' + 'A7 F9 87 87 87 62 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD FA FA FA FF F8 F8' + 'F8 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5 F5 FF F1 F1' + 'F1 FF C6 C6 C6 FF 8C 8C 8C FF A4 A4 A4 FD 9A 9A' + '9A AD 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD FB FB FB FF F9 F9' + 'F9 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF D0 D0' + 'D0 FF D0 D0 D0 FF E6 E6 E6 FE B3 B3 B3 D5 37 37' + '37 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C7 C7 C7 AD FC FC FC FF FB FB' + 'FB FF F9 F9 F9 FF F8 F8 F8 FF F7 F7 F7 FF E2 E2' + 'E2 FF CB CB CB FE C6 C6 C6 ED 5C 5C 5C 4D 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C9 C9 C9 AD FD FD FD FF FC FC' + 'FC FF FB FB FB FF F9 F9 F9 FF F8 F8 F8 FF E3 E3' + 'E3 FF B3 B3 B3 F8 89 89 89 83 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 A6 A6 A6 77 C5 C5 C5 AF C3 C3' + 'C3 B0 C3 C3 C3 B0 C3 C3 C3 B0 C1 C1 C1 B0 C2 C2' + 'C2 A6 9D 9D 9D 81 0F 0F 0F 10 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 03' + '00 00 C0 03 00 00 C0 03 00 00 C0 03 00 00 C0 03' + '00 00 C0 03 00 00 C0 03 00 00 C0 03 00 00 C0 03' + '00 00 C0 03 00 00 C0 03 00 00 C0 07 00 00 C0 07' + '00 00 C0 0F 00 00 C0 1F 00 00 C0 1F 00 00 28 00' + '00 00 20 00 00 00 40 00 00 00 01 00 04 00 00 00' + '00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 88 88 88 88 88 88 88' + '88 88 88 88 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF F8 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF 88 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF 88 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF FF 88 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF F8 88 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF FF F8 78 00 00 00 00 00 8F FF FF FF FF FF FF' + 'FF 88 87 80 00 00 00 00 00 8F FF FF FF FF FF FF' + '87 87 88 00 00 00 00 00 00 8F FF FF FF FF FF FF' + '7F 8F 88 00 00 00 00 00 00 8F FF FF FF FF FF FF' + '88 F8 80 00 00 00 00 00 00 8F FF FF FF FF FF FF' + 'F7 F7 00 00 00 00 00 00 00 8F FF FF FF FF FF FF' + '88 88 00 00 00 00 00 00 00 88 88 88 88 88 88 88' + '88 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 1F FC 00 00 3F FC 00' + '00 3F FC 00 00 7F FC 00 00 FF FC 00 00 FF FC 00' + '01 FF FF FF FF FF 28 00 00 00 20 00 00 00 40 00' + '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 C0 C0 C0 00 C0 DC' + 'C0 00 F0 CA A6 00 77 77 77 00 FF 00 FF 00 82 82' + '82 00 A0 A0 A0 00 A1 A1 A1 00 A3 A3 A3 00 A7 A7' + 'A7 00 A9 A9 A9 00 AE AE AE 00 B2 B2 B2 00 B3 B3' + 'B3 00 B4 B4 B4 00 B5 B5 B5 00 B8 B8 B8 00 BA BA' + 'BA 00 BB BB BB 00 BC BC BC 00 BD BD BD 00 BE BE' + 'BE 00 BF BF BF 00 C0 C0 C0 00 C5 C5 C5 00 C6 C6' + 'C6 00 C7 C7 C7 00 C8 C8 C8 00 C9 C9 C9 00 CB CB' + 'CB 00 CD CD CD 00 CF CF CF 00 D0 D0 D0 00 D1 D1' + 'D1 00 D2 D2 D2 00 D3 D3 D3 00 D4 D4 D4 00 D5 D5' + 'D5 00 D6 D6 D6 00 D7 D7 D7 00 D8 D8 D8 00 D9 D9' + 'D9 00 DA DA DA 00 DB DB DB 00 DC DC DC 00 DD DD' + 'DD 00 DE DE DE 00 DF DF DF 00 E0 E0 E0 00 E1 E1' + 'E1 00 E2 E2 E2 00 E3 E3 E3 00 E4 E4 E4 00 E5 E5' + 'E5 00 E6 E6 E6 00 E7 E7 E7 00 E8 E8 E8 00 E9 E9' + 'E9 00 EA EA EA 00 EB EB EB 00 EC EC EC 00 ED ED' + 'ED 00 EE EE EE 00 EF EF EF 00 F0 F0 F0 00 F1 F1' + 'F1 00 F2 F2 F2 00 F3 F3 F3 00 F4 F4 F4 00 F5 F5' + 'F5 00 F6 F6 F6 00 F7 F7 F7 00 F8 F8 F8 00 F9 F9' + 'F9 00 FA FA FA 00 FB FB FB 00 FC FC FC 00 FD FD' + 'FD 00 FE FE FE 00 FF FF FF 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 F0 FB FF 00 A4 A0 A0 00 80 80' + '80 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 22 1B 1B 1B 1B 1B 1B 1B 1B 1B 1B 1B' + '1B 1B 1B 1B 1B 1B 1B 1B 20 21 00 00 00 00 00 00' + '00 00 00 00 26 35 35 35 35 35 35 35 35 35 35 33' + '32 31 30 2D 2C 2B 2A 28 29 1F 00 00 00 00 00 00' + '00 00 00 00 34 43 42 42 41 40 3F 3F 3D 3D 3B 39' + '38 36 35 33 30 2F 2E 2C 2A 1A 00 00 00 00 00 00' + '00 00 00 00 34 44 43 42 42 41 40 3F 3F 3D 3C 3A' + '39 38 36 35 32 30 2F 2E 2A 1A 00 00 00 00 00 00' + '00 00 00 00 35 44 44 43 42 42 41 40 3F 3F 3E 3C' + '3B 39 38 36 35 32 30 2F 2B 1A 00 00 00 00 00 00' + '00 00 00 00 35 45 44 44 43 42 42 41 40 3F 3F 3E' + '3C 3B 39 38 36 35 32 30 2D 1A 00 00 00 00 00 00' + '00 00 00 00 35 46 45 44 44 43 42 42 41 40 3F 3F' + '3E 3C 3B 39 38 36 35 33 2F 1A 00 00 00 00 00 00' + '00 00 00 00 36 46 46 45 44 44 43 42 42 41 40 3F' + '3F 3E 3C 3B 39 38 36 35 30 1A 00 00 00 00 00 00' + '00 00 00 00 37 47 46 46 45 44 43 43 42 42 41 40' + '3F 3E 3E 3C 3B 39 38 36 30 1A 00 00 00 00 00 00' + '00 00 00 00 37 48 47 46 46 45 44 43 43 42 42 41' + '40 3F 3E 3E 3C 3B 39 38 33 1B 00 00 00 00 00 00' + '00 00 00 00 37 49 48 47 46 46 45 44 43 43 42 42' + '41 40 3F 3E 3E 3C 3B 39 34 1A 00 00 00 00 00 00' + '00 00 00 00 37 49 49 48 47 46 46 45 44 43 43 42' + '42 41 40 3F 3E 3E 3C 3A 36 1B 00 00 00 00 00 00' + '00 00 00 00 37 4A 49 49 48 47 46 46 45 44 43 43' + '42 42 41 40 3F 3E 3D 3C 36 1B 00 00 00 00 00 00' + '00 00 00 00 38 4C 4A 49 49 48 47 46 46 45 44 43' + '43 42 42 41 40 3F 3E 3D 36 1B 00 00 00 00 00 00' + '00 00 00 00 38 4C 4C 4A 49 49 48 47 46 46 45 44' + '43 43 42 42 41 40 3F 3E 36 1B 00 00 00 00 00 00' + '00 00 00 00 38 4D 4C 4C 4A 49 49 48 47 46 46 45' + '44 43 43 42 42 41 40 3F 36 1B 00 00 00 00 00 00' + '00 00 00 00 39 4E 4D 4C 4C 4A 49 49 48 47 46 46' + '45 44 43 43 42 42 41 40 36 1B 00 00 00 00 00 00' + '00 00 00 00 39 4E 4E 4D 4C 4C 4A 49 49 48 47 46' + '46 45 44 43 43 42 42 41 36 1B 00 00 00 00 00 00' + '00 00 00 00 38 4F 4E 4E 4D 4C 4C 4A 49 49 48 47' + '46 46 45 44 43 43 42 42 36 1B 00 00 00 00 00 00' + '00 00 00 00 39 51 4F 4E 4E 4D 4C 4C 4A 49 49 48' + '47 46 46 45 44 43 43 42 36 1B 00 00 00 00 00 00' + '00 00 00 00 39 51 51 4F 4E 4E 4D 4C 4C 4A 49 49' + '48 47 46 46 45 44 43 43 35 1B 00 00 00 00 00 00' + '00 00 00 00 3A 52 52 51 4F 4E 4E 4D 4C 4C 4A 49' + '49 48 47 46 46 45 44 41 28 19 00 00 00 00 00 00' + '00 00 00 00 3A 54 53 52 51 4F 4E 4E 4D 4C 4B 4A' + '49 49 48 47 46 46 47 31 16 1C 00 00 00 00 00 00' + '00 00 00 00 3A 54 54 52 52 51 4F 4E 4E 4D 4C 4B' + '4A 49 49 48 47 47 45 14 17 07 00 00 00 00 00 00' + '00 00 00 00 3A 56 55 54 53 52 51 4F 4E 4E 4D 4C' + '4B 4A 4A 4A 4A 3F 11 0D 17 00 00 00 00 00 00 00' + '00 00 00 00 3A 56 55 54 54 52 51 51 4F 4E 4E 4D' + '4C 4B 4B 42 16 0A 0E 15 00 00 00 00 00 00 00 00' + '00 00 00 00 3A 56 56 56 55 54 53 51 51 4F 4E 4E' + '4D 4E 27 0C 10 31 07 18 00 00 00 00 00 00 00 00' + '00 00 00 00 3A 56 56 56 55 54 54 53 51 51 4F 4E' + '4E 4E 29 23 3B 24 16 00 00 00 00 00 00 00 00 00' + '00 00 00 00 3A 56 56 56 56 56 54 54 53 51 51 4F' + '4E 4F 3C 12 26 13 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 3A 56 56 56 56 56 55 54 54 53 51 50' + '4F 50 3F 0F 1B 07 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 3D 39 39 39 39 39 39 39 39 39 39 39' + '38 33 25 1A 1D 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 0F FC 00 00 0F FC 00 00 0F FC 00 00 0F FC 00' + '00 1F FC 00 00 3F FC 00 00 3F FC 00 00 7F FC 00' + '00 FF FC 00 00 FF FC 00 01 FF FF FF FF FF 28 00' + '00 00 20 00 00 00 40 00 00 00 01 00 20 00 00 00' + '00 00 80 10 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 2C 2C' + '2C 43 26 26 26 4D 26 26 26 4D 26 26 26 4D 26 26' + '26 4D 26 26 26 4D 26 26 26 4D 26 26 26 4D 26 26' + '26 4D 26 26 26 4D 26 26 26 4D 26 26 26 4D 26 26' + '26 4D 26 26 26 4D 26 26 26 4D 23 23 23 4D 26 26' + '26 4D 23 23 23 4D 23 23 23 4D 23 23 23 4D 23 23' + '23 42 00 00 00 38 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C4 C4 C4 1F C2 C2' + 'C2 C9 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8' + 'D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D8' + 'D8 D8 D8 D8 D8 D8 D8 D8 D8 D8 D6 D6 D6 D8 D4 D4' + 'D4 D8 D3 D3 D3 D8 D2 D2 D2 D8 CF CF CF D8 CE CE' + 'CE D8 CC CC CC D8 CB CB CB D8 C9 C9 C9 D8 BF BF' + 'BF B4 20 20 20 42 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C0 C0 C0 27 DA DA' + 'DA ED EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E6 E6' + 'E6 FF E6 E6 E6 FF E4 E4 E4 FF E2 E2 E2 FF E1 E1' + 'E1 FF DF DF DF FF DE DE DE FF DC DC DC FF D9 D9' + 'D9 FF D8 D8 D8 FF D7 D7 D7 FF D5 D5 D5 FF CB CB' + 'CB DA 23 23 23 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C0 C0 C0 27 DA DA' + 'DA ED ED ED ED FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E6 E6 E6 FF E5 E5 E5 FF E3 E3 E3 FF E2 E2' + 'E2 FF E1 E1 E1 FF DF DF DF FF DE DE DE FF DB DB' + 'DB FF D9 D9 D9 FF D8 D8 D8 FF D7 D7 D7 FF CB CB' + 'CB DA 23 23 23 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C0 C0 C0 27 DB DB' + 'DB ED ED ED ED FF ED ED ED FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E5 E5 E5 FF E4 E4' + 'E4 FF E2 E2 E2 FF E1 E1 E1 FF DF DF DF FF DE DE' + 'DE FF DB DB DB FF D9 D9 D9 FF D8 D8 D8 FF CD CD' + 'CD DA 23 23 23 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C0 C0 C0 27 DC DC' + 'DC ED EE EE EE FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF E9 E9' + 'E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7 E7 FF E5 E5' + 'E5 FF E4 E4 E4 FF E2 E2 E2 FF E1 E1 E1 FF DF DF' + 'DF FF DE DE DE FF DB DB DB FF D9 D9 D9 FF CF CF' + 'CF DA 23 23 23 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C0 C0 C0 27 DC DC' + 'DC ED EF EF EF FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7' + 'E7 FF E5 E5 E5 FF E4 E4 E4 FF E2 E2 E2 FF E1 E1' + 'E1 FF DF DF DF FF DE DE DE FF DC DC DC FF D1 D1' + 'D1 DA 23 23 23 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C0 C0 C0 27 DD DD' + 'DD ED EF EF EF FF EF EF EF FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E5 E5 E5 FF E4 E4 E4 FF E2 E2' + 'E2 FF E1 E1 E1 FF DF DF DF FF DE DE DE FF D2 D2' + 'D2 DA 23 23 23 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C4 C4 C4 27 DE DE' + 'DE ED F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF E9 E9 E9 FF E8 E8' + 'E8 FF E7 E7 E7 FF E7 E7 E7 FF E5 E5 E5 FF E4 E4' + 'E4 FF E2 E2 E2 FF E1 E1 E1 FF DF DF DF FF D3 D3' + 'D3 DA 23 23 23 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C4 C4 C4 27 DE DE' + 'DE ED F1 F1 F1 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF E9 E9' + 'E9 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7 E7 FF E5 E5' + 'E5 FF E4 E4 E4 FF E2 E2 E2 FF E1 E1 E1 FF D6 D6' + 'D6 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C4 C4 C4 27 DE DE' + 'DE ED F2 F2 F2 FF F1 F1 F1 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF E9 E9 E9 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7' + 'E7 FF E5 E5 E5 FF E4 E4 E4 FF E2 E2 E2 FF D7 D7' + 'D7 DA 23 23 23 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C4 C4 C4 27 DE DE' + 'DE ED F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EE EE EE FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF E9 E9 E9 FF E8 E8 E8 FF E7 E7' + 'E7 FF E7 E7 E7 FF E5 E5 E5 FF E3 E3 E3 FF D9 D9' + 'D9 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C4 C4 C4 27 DE DE' + 'DE ED F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF E9 E9 E9 FF E8 E8' + 'E8 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF D9 D9' + 'D9 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C7 C7 C7 27 DF DF' + 'DF ED F5 F5 F5 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF E9 E9' + 'E9 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6 E6 FF D9 D9' + 'D9 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C7 C7 C7 27 DF DF' + 'DF ED F5 F5 F5 FF F5 F5 F5 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF E9 E9 E9 FF E8 E8 E8 FF E7 E7 E7 FF D9 D9' + 'D9 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C7 C7 C7 27 DF DF' + 'DF ED F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EE EE EE FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF E9 E9 E9 FF E8 E8 E8 FF D9 D9' + 'D9 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C7 C7 C7 27 E0 E0' + 'E0 ED F7 F7 F7 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF E9 E9 E9 FF D9 D9' + 'D9 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CA CA CA 27 E0 E0' + 'E0 ED F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF D9 D9' + 'D9 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C7 C7 C7 27 DF DF' + 'DF ED F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F5 F5 F5 FF F5 F5 F5 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF D9 D9' + 'D9 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 C7 C7 C7 27 E0 E0' + 'E0 ED FA FA FA FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EE EE EE FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF D9 D9' + 'D9 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CA CA CA 27 E0 E0' + 'E0 ED FA FA FA FF FA FA FA FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF ED ED ED FF EC EC EC FF EC EC EC FF D8 D8' + 'D8 DA 26 26 26 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CA CA CA 27 E1 E1' + 'E1 ED FB FB FB FF FB FB FB FF FA FA FA FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF ED ED ED FF EA EA EA FF C9 C9' + 'C9 DA 20 20 20 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CD CD CD 27 E1 E1' + 'E1 ED FD FD FD FF FC FC FC FF FB FB FB FF FA FA' + 'FA FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F5 F5 F5 FF F4 F4 F4 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF F0 F0 F0 FF DA DA DA FF A8 A8' + 'A8 D9 24 24 24 4C 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CD CD CD 27 E1 E1' + 'E1 ED FD FD FD FF FD FD FD FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4 F4 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EE EE EE FF B3 B3 B3 FF A9 A9' + 'A9 D3 1D 1D 1D 47 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CD CD CD 27 E1 E1' + 'E1 ED FF FF FF FF FE FE FE FF FD FD FD FF FC FC' + 'FC FF FB FB FB FF FA FA FA FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4' 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F3 F3 F3 FF F3 F3' - 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' - 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F3 F3 F3 FF F4 F4' - 'F4 FF F5 F5 F5 FF F9 F9 F9 FF 94 94 94 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 20 20 20 FF CD CD' - 'CD FF FB FB FB FF F2 F2 F2 FF F3 F3 F3 FF F4 F4' - 'F4 FF F5 F5 F5 FF F6 F6 F6 FF F7 F7 F7 FF F8 F8' - 'F8 FF F9 F9 F9 FF F9 F9 F9 FF FA FA FA FF FB FB' - 'FB FF FB FB FB FF FB FB FB FF FA FA FA FF F8 F8' - 'F8 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4 F4 FF F5 F5' - 'F5 FF F6 F6 F6 FF FA FA FA FF 95 95 95 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 21 21 21 FF CE CE' - 'CE FF FD FD FD FF F4 F4 F4 FF F4 F4 F4 FF F6 F6' - 'F6 FF F6 F6 F6 FF F7 F7 F7 FF F8 F8 F8 FF F8 F8' - 'F8 FF FA FA FA FF FA FA FA FF FB FB FB FF FB FB' - 'FB FF FC FC FC FF FD FD FD FF FD FD FD FF FE FE' - 'FE FF FE FE FE FF FC FC FC FF F9 F9 F9 FF F6 F6' - 'F6 FF F7 F7 F7 FF FB FB FB FF 96 96 96 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 21 21 21 FF CE CE' - 'CE FF FE FE FE FF F5 F5 F5 FF F6 F6 F6 FF F7 F7' - 'F7 FF F8 F8 F8 FF F9 F9 F9 FF F9 F9 F9 FF FA FA' - 'FA FF FB FB FB FF FB FB FB FF FC FC FC FF FC FC' - 'FC FF FD FD FD FF FD FD FD FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FF FF FF FF FF FF FF FF FD FD' - 'FD FF FB FB FB FF FC FC FC FF 97 97 97 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 21 21 21 FF CF CF' - 'CF FF FF FF FF FF F7 F7 F7 FF F8 F8 F8 FF F8 F8' - 'F8 FF F9 F9 F9 FF FA FA FA FF FA FA FA FF FB FB' - 'FB FF FB FB FB FF FC FC FC FF FC FC FC FF FD FD' - 'FD FF FD FD FD FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 98 98 98 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 22 22 22 FF CF CF' - 'CF FF FF FF FF FF F8 F8 F8 FF F9 F9 F9 FF FA FA' - 'FA FF FA FA FA FF FA FA FA FF FB FB FB FF FC FC' - 'FC FF FC FC FC FF FD FD FD FF FD FD FD FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 99 99 99 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 22 22 22 FF D0 D0' - 'D0 FF FF FF FF FF F9 F9 F9 FF FA FA FA FF FB FB' - 'FB FF FB FB FB FF FB FB FB FF FC FC FC FF FD FD' - 'FD FF FD FD FD FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 99 99 99 FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 23 23 23 FF D1 D1' - 'D1 FF FF FF FF FF FA FA FA FF FB FB FB FF FC FC' - 'FC FF FC FC FC FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 9A 9A 9A FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 23 23 23 FF D1 D1' - 'D1 FF FF FF FF FF FC FC FC FF FC FC FC FF FD FD' - 'FD FF FD FD FD FF FD FD FD FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 9B 9B 9B FF 02 02' - '02 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 23 23 23 FF D2 D2' - 'D2 FF FF FF FF FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FE FE FE FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 9C 9C 9C FF 03 03' - '03 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 23 23 23 FF D3 D3' - 'D3 FF FF FF FF FF FE FE FE FF FE FE FE FF FE FE' - 'FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 9D 9D 9D FF 03 03' - '03 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 23 23 23 FF D3 D3' - 'D3 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 9D 9D 9D FF 03 03' - '03 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 24 24 24 FF D4 D4' - 'D4 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 9E 9E 9E FF 03 03' - '03 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 84 27 27 27 FF D8 D8' - 'D8 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FE FE' - 'FE FF FE FE FE FF FE FE FE FF FE FE FE FF FD FD' - 'FD FF FD FD FD FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FD FD FD FF FC FC FC FF FC FC FC FF FC FC' - 'FC FF FD FD FD FF FF FF FF FF A3 A3 A3 FF 04 04' - '04 FF 00 00 00 61 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 7F 18 18 18 FF 77 77' - '77 FF 7D 7D 7D FF 7B 7B 7B FF 7A 7A 7A FF 79 79' - '79 FF 79 79 79 FF 78 78 78 FF 78 78 78 FF 77 77' - '77 FF 76 76 76 FF 75 75 75 FF 75 75 75 FF 74 74' - '74 FF 73 73 73 FF 72 72 72 FF 71 71 71 FF 70 70' - '70 FF 70 70 70 FF 6F 6F 6F FF 6E 6E 6E FF 6E 6E' - '6E FF 6D 6D 6D FF 6E 6E 6E FF 59 59 59 FF 03 03' - '03 FF 00 00 00 59 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 2A 00 00 00 D3 00 00' - '00 F5 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F6 00 00' - '00 B6 00 00 00 14 00 00 00 00 00 00 00 00 E0 00' - '07 FF C0 00 03 FF C0 00 01 FF C0 00 00 FF C0 00' - '00 7F C0 00 00 3F C0 00 00 1F C0 00 00 1F C0 00' - '00 0F C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 28 00' - '00 00 10 00 00 00 20 00 00 00 01 00 20 00 00 00' - '00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 01 01 01 63 05 05' - '05 AF 04 04 04 A8 04 04 04 A8 04 04 04 A8 04 04' - '04 A8 03 03 03 A8 03 03 03 AA 02 02 02 A0 00 00' - '00 2C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 13 13 13 CB A8 A8' - 'A8 FF BC BC BC FF B7 B7 B7 FF B7 B7 B7 FF B6 B6' - 'B6 FF B7 B7 B7 FF B6 B6 B6 FF 7A 7A 7A FF 0F 0F' - '0F D5 00 00 00 28 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 17 17 17 CC D1 D1' - 'D1 FF EA EA EA FF E5 E5 E5 FF E6 E6 E6 FF E7 E7' - 'E7 FF E9 E9 E9 FF E3 E3 E3 FF D0 D0 D0 FF 95 95' - '95 FF 0B 0B 0B D5 00 00 00 28 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 17 17 17 CC CD CD' - 'CD FF E4 E4 E4 FF DF DF DF FF E0 E0 E0 FF E1 E1' - 'E1 FF E2 E2 E2 FF DA DA DA FF CE CE CE FF F9 F9' - 'F9 FF 95 95 95 FF 0A 0A 0A D4 00 00 00 25 00 00' - '00 00 00 00 00 00 00 00 00 00 17 17 17 CC D0 D0' - 'D0 FF E7 E7 E7 FF E3 E3 E3 FF E4 E4 E4 FF E4 E4' - 'E4 FF E6 E6 E6 FF DD DD DD FF D2 D2 D2 FF FF FF' - 'FF FF FF FF FF FF 9E 9E 9E FF 0D 0D 0D D1 00 00' - '00 24 00 00 00 00 00 00 00 00 17 17 17 CC D1 D1' - 'D1 FF EB EB EB FF E5 E5 E5 FF E7 E7 E7 FF E7 E7' - 'E7 FF E9 E9 E9 FF E4 E4 E4 FF CB CB CB FF D7 D7' - 'D7 FF D9 D9 D9 FF DF DF DF FF 7C 7C 7C FF 00 00' - '00 9D 00 00 00 00 00 00 00 00 17 17 17 CC D4 D4' - 'D4 FF F0 F0 F0 FF E9 E9 E9 FF E9 E9 E9 FF EA EA' - 'EA FF EB EB EB FF EC EC EC FF E8 E8 E8 FF E4 E4' - 'E4 FF E5 E5 E5 FF EF EF EF FF BC BC BC FF 03 03' - '03 A8 00 00 00 00 00 00 00 00 17 17 17 CC D6 D6' - 'D6 FF F4 F4 F4 FF EF EF EF FF EE EE EE FF ED ED' - 'ED FF ED ED ED FF EE EE EE FF F0 F0 F0 FF F1 F1' - 'F1 FF F1 F1 F1 FF FC FC FC FF BD BD BD FF 02 02' - '02 A8 00 00 00 00 00 00 00 00 18 18 18 CC D8 D8' - 'D8 FF F8 F8 F8 FF F4 F4 F4 FF F5 F5 F5 FF F5 F5' - 'F5 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' - 'F5 FF F4 F4 F4 FF FE FE FE FF BE BE BE FF 03 03' - '03 A8 00 00 00 00 00 00 00 00 18 18 18 CC DB DB' - 'DB FF FB FB FB FF F6 F6 F6 FF F8 F8 F8 FF FA FA' - 'FA FF FB FB FB FF FC FC FC FF FD FD FD FF FE FE' - 'FE FF FD FD FD FF FF FF FF FF C0 C0 C0 FF 02 02' - '02 A8 00 00 00 00 00 00 00 00 18 18 18 CC DC DC' - 'DC FF FE FE FE FF F9 F9 F9 FF FB FB FB FF FC FC' - 'FC FF FC FC FC FF FD FD FD FF FE FE FE FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF C3 C3 C3 FF 02 02' - '02 A8 00 00 00 00 00 00 00 00 18 18 18 CC DE DE' - 'DE FF FF FF FF FF FC FC FC FF FC FC FC FF FD FD' - 'FD FF FE FE FE FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF C4 C4 C4 FF 03 03' - '03 A8 00 00 00 00 00 00 00 00 18 18 18 CC DF DF' - 'DF FF FF FF FF FF FD FD FD FF FE FE FE FF FE FE' - 'FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF C5 C5 C5 FF 03 03' - '03 A8 00 00 00 00 00 00 00 00 19 19 19 CC E2 E2' - 'E2 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF C7 C7 C7 FF 03 03' - '03 A8 00 00 00 00 00 00 00 00 19 19 19 CC E2 E2' - 'E2 FF FF FF FF FF FD FD FD FF FD FD FD FF FD FD' - 'FD FF FD FD FD FF FD FD FD FF FC FC FC FF FC FC' - 'FC FF FB FB FB FF FF FF FF FF C6 C6 C6 FF 04 04' - '04 A8 00 00 00 00 00 00 00 00 09 09 09 A0 41 41' - '41 F9 46 46 46 F0 44 44 44 F0 44 44 44 F0 43 43' - '43 F0 42 42 42 F0 42 42 42 F0 41 41 41 F0 3F 3F' - '3F F0 3F 3F 3F F0 40 40 40 F0 36 36 36 FB 02 02' - '02 82 00 00 00 00 80 1F 00 00 80 0F 00 00 80 07' - '00 00 80 03 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00' -} */ - - -/* BINRES folder.ico */ -3 ICON folder.ico -/* { - '00 00 01 00 08 00 20 20 00 00 01 00 08 00 A8 08' - '00 00 86 00 00 00 10 10 00 00 01 00 08 00 68 05' - '00 00 2E 09 00 00 20 20 00 00 01 00 04 00 E8 02' - '00 00 96 0E 00 00 10 10 00 00 01 00 04 00 28 01' - '00 00 7E 11 00 00 30 30 00 00 01 00 08 00 A8 0E' - '00 00 A6 12 00 00 30 30 00 00 01 00 20 00 A8 25' - '00 00 4E 21 00 00 20 20 00 00 01 00 20 00 A8 10' - '00 00 F6 46 00 00 10 10 00 00 01 00 20 00 68 04' - '00 00 9E 57 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' + 'F3 FF E8 E8 E8 FF A9 A9 A9 FE 9E 9E 9E FB 8B 8B' + '8B 9B 00 00 00 2D 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 11 00 00 00 00 11 00 00 00 00 11 00 22 00' - '00 00 33 00 00 00 33 33 00 00 33 33 33 00 44 00' - '00 00 66 33 00 00 66 33 33 00 66 66 33 00 7F 7F' - '7F 00 99 33 00 00 99 33 33 00 99 66 33 00 99 66' - '66 00 CC 66 66 00 FF 99 33 00 FF CC 33 00 99 99' - '66 00 CC 99 66 00 FF 99 66 00 CC CC 66 00 FF CC' - '66 00 BB BB BB 00 CC 99 99 00 FF 99 99 00 CC CC' - '99 00 FF CC 99 00 FF FF 99 00 CC 99 CC 00 CC CC' - 'CC 00 DD DD DD 00 FF CC CC 00 FF FF CC 00 FF CC' - 'FF 00 EE EE EE 00 FF FF FF 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CD CD CD 27 E1 E1' + 'E1 ED FF FF FF FF FE FE FE FF FD FD FD FF FD FD' + 'FD FF FB FB FB FF FA FA FA FF FA FA FA FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF EB EB EB FF B5 B5' + 'B5 FF 77 77 77 FF A0 A0 A0 FD A9 A9 A9 DD 3B 3B' + '3B 3C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CD CD CD 27 E1 E1' + 'E1 ED FF FF FF FF FF FF FF FF FF FF FF FF FE FE' + 'FE FF FD FD FD FF FC FC FC FF FA FA FA FF FA FA' + 'FA FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F7 F7 F7 FF D0 D0 D0 FF 82 82 82 FF A7 A7' + 'A7 FF DA DA DA FE B9 B9 B9 E5 57 57 57 69 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CD CD CD 27 E1 E1' + 'E1 ED FF FF FF FF FF FF FF FF FF FF FF FF FE FE' + 'FE FF FD FD FD FF FD FD FD FF FC FC FC FF FA FA' + 'FA FF FA FA FA FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F7 F7 F7 FF D2 D2 D2 FF C9 C9 C9 FF E4 E4' + 'E4 FE CA CA CA FB 86 86 86 9C 1B 1B 1B 1C 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CD CD CD 27 E1 E1' + 'E1 ED FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FD FD FD FF FD FD FD FF FC FC' + 'FC FF FA FA FA FF FA FA FA FF F8 F8 F8 FF F7 F7' + 'F7 FF F8 F8 F8 FF E5 E5 E5 FF AE AE AE FF CE CE' + 'CE FC 9C 9C 9C C7 18 18 18 29 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CD CD CD 27 E1 E1' + 'E1 ED FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FE FE FE FF FD FD FD FF FD FD' + 'FD FF FC FC FC FF FA FA FA FF F9 F9 F9 FF F8 F8' + 'F8 FF F9 F9 F9 FF E8 E8 E8 FF A3 A3 A3 FE B5 B5' + 'B5 E4 5E 5E 5E 64 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 DC DC DC 22 E2 E2' + 'E2 DA E0 E0 E0 ED E0 E0 E0 ED E0 E0 E0 ED E0 E0' + 'E0 ED E0 E0 E0 ED E0 E0 E0 ED E0 E0 E0 ED E0 E0' + 'E0 ED E0 E0 E0 ED E0 E0 E0 ED E0 E0 E0 EC DF DF' + 'DF EC D9 D9 D9 ED C8 C8 C8 EA B3 B3 B3 E2 7A 7A' + '7A 7B 00 00 00 10 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 C0 17 95 00 00 00 38 00 A8 44' - 'F9 77 13 00 00 00 18 0A 38 00 00 00 38 00 18 6C' - '38 00 98 17 95 00 00 00 00 00 E0 19 95 00 F0 88' - 'FA 77 70 38 F5 77 FF FF FF FF A8 44 F9 77 70 7D' - 'F5 77 3A 8A F5 77 86 00 00 00 86 00 00 00 08 00' - '00 00 B0 18 95 00 00 00 00 00 CB 44 F9 77 38 9F' - '07 00 CD 8B F5 77 78 13 05 00 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D8 D8' + 'D8 22 CA CA CA 27 CA CA CA 27 CA CA CA 27 CA CA' + 'CA 27 CA CA CA 27 CA CA CA 27 CA CA CA 27 CA CA' + 'CA 27 CA CA CA 27 CA CA CA 27 C9 C9 C9 26 C5 C5' + 'C5 26 BC BC BC 27 A7 A7 A7 26 99 99 99 22 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FC 00 00 0F F8 00 00 0F F8 00' + '00 0F F8 00 00 0F F8 00 00 0F F8 00 00 0F F8 00' + '00 0F F8 00 00 0F F8 00 00 0F F8 00 00 0F F8 00' + '00 0F F8 00 00 0F F8 00 00 0F F8 00 00 0F F8 00' + '00 0F F8 00 00 0F F8 00 00 0F F8 00 00 0F F8 00' + '00 0F F8 00 00 0F F8 00 00 0F F8 00 00 0F F8 00' + '00 0F F8 00 00 0F F8 00 00 0F F8 00 00 1F F8 00' + '00 3F F8 00 00 3F F8 00 00 7F F8 00 00 FF F8 00' + '00 FF FC 00 03 FF 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 04 00 00 00 00 00 80 04 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00' - '00 00 00 00 00 00 56 00 58 00 00 EC FD 7F 1A 02' - '00 00 4C 16 95 00 40 9F 07 00 FC 15 95 00 FF FF' - 'FF FF B4 1A 95 00 45 00 00 00 28 02 00 00 FF FF' - 'FF FF E2 D8 F5 77 7D 9B F5 77 94 B6 01 00 00 00' - '05 00 F4 17 95 00 80 00 10 C0 B4 1A 95 00 F0 88' - 'FA 77 88 1C F5 77 FF FF FF FF 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 9B B2 E7 77 B7 00 00 00 02 00' - '00 00 A4 1A 95 00 01 00 00 00 18 00 00 00 00 00' - '00 00 10 19 95 00 42 00 00 00 00 00 00 00 F4 18' - '95 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00' - '00 00 02 00 00 00 01 01 F5 77 00 EC FD 7F 56 00' - '00 00 00 00 00 00 03 00 00 00 5E 00 1A 02 40 9F' - '07 00 00 00 00 00 40 9F 07 00 05 00 00 00 BE B3' - 'E7 77 4C 19 95 00 A3 B4 E7 77 F8 00 00 00 00 00' - '00 C0 00 00 00 00 00 00 00 00 02 00 00 00 80 00' - '00 00 00 00 00 00 8C 1A 95 00 7F E9 4B 00 08 6C' - '0C 01 00 00 00 C0 00 00 00 00 80 1A 95 00 02 00' - '00 00 80 00 00 00 00 00 00 00 C0 27 95 00 C4 F5' - 'AF 00 02 00 00 00 44 3A 5C 6F 73 65 78 70 65 72' - '74 73 5C 72 65 61 63 74 6F 73 5C 6C 69 62 5C 73' - '68 65 6C 6C 33 32 5C 66 6F 6C 64 65 72 2E 69 63' - '6F 00 1A 93 4B 00 14 1A 95 00 1F 3B D4 77 13 00' - '00 00 98 00 00 00 4F 3B D4 77 E0 19 95 00 33 3B' - 'D4 77 64 C5 F5 77 A9 F1 E7 77 F8 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 1A 95 00 18 6C' - '38 00 86 00 00 00 00 00 00 00 C9 F1 E7 77 86 00' - '00 00 A4 1A 95 00 08 00 00 00 00 00 00 00 86 00' - '00 00 86 00 00 00 08 00 00 00 F4 19 95 00 33 3B' - 'D4 77 B4 1A 95 00 09 48 E9 77 B8 10 E9 77 FF FF' - 'FF FF C9 F1 E7 77 16 EA 4B 00 F8 00 00 00 18 6C' - '38 00 86 00 00 00 58 1A 95 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 27 27 27 00' + '00 00 00 00 00 00 00 00 00 00 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF F8 80 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF F8 70 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF F8 F8 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF 8F 70 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF F8 F8 F7 80 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF FF 8F' + '8F 78 00 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF F8 F8 FF 77 87 00 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF FF 8F 77' + '88 80 00 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF 87 77 8F 87 00 00 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF FF F7 F8 F8' + '80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF 87 8F 88 70 00 00 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF F8 F7 F8 88' + '00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF 87 88 70 00 00 00 00 00 00 00 00' + '00 00 8F FF FF FF FF FF FF FF FF FF F8 F7 88 80' + '00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF FF' + 'FF FF FF FF FF 87 87 00 00 00 00 00 00 00 00 00' + '00 00 88 88 88 88 88 88 88 88 88 88 88 87 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 27 02 03 27 27' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 27 03 04 0D 06 27' - '27 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 27 27 27 09 11 22 1A 06' - '27 27 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 27 27 27 0F 1B 22 26 26 1A' - '05 27 27 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 27 02 08 15 1B 1C 26 26 26 26' - '1A 04 27 27 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 27 27 27 08 1A 1D 1A 26 26 26 26 26' - '25 15 04 27 27 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 27 27 27 0A 1D 1D 1A 1D 1D 26 26 26 26' - '26 25 10 04 27 27 00 00 00 00 00 00 00 00 00 00' - '00 00 27 01 01 10 23 1D 15 23 16 12 18 26 26 26' - '26 26 25 10 01 27 27 00 00 00 00 00 00 00 00 00' - '27 27 02 06 1B 23 1D 1A 22 12 12 16 12 1D 26 26' - '26 25 25 25 10 01 27 27 00 00 00 00 00 00 00 27' - '27 27 0E 22 23 1D 1A 22 18 12 16 12 16 12 18 25' - '26 24 25 23 25 10 01 27 27 00 00 00 00 00 27 01' - '04 15 23 22 22 1C 1D 1B 12 16 12 12 18 16 16 1D' - '23 25 25 21 25 22 10 01 27 27 00 00 00 00 03 06' - '1D 25 25 23 1C 1B 1D 13 16 12 18 16 12 16 18 16' - '1D 25 25 25 22 21 21 0F 01 27 27 00 00 00 27 09' - '22 23 22 22 17 1D 16 12 16 16 12 16 16 18 16 18' - '1B 1D 25 21 21 21 20 22 09 02 27 27 00 00 27 27' - '06 22 23 15 1D 16 12 16 16 13 16 18 12 16 18 1B' - '18 1D 1D 21 21 20 22 20 22 0A 27 27 27 00 00 27' - '27 09 16 1D 16 13 16 16 12 16 12 16 16 18 16 18' - '1D 1D 1E 22 20 22 19 20 19 22 0F 27 27 27 00 00' - '27 27 09 16 12 16 16 12 18 16 16 12 18 16 18 1D' - '1D 1D 22 22 23 20 22 19 22 19 1C 0E 27 27 00 00' - '00 27 27 09 16 16 16 18 16 12 18 16 16 1D 1D 1D' - '22 1E 22 23 22 23 20 19 19 22 1F 14 01 27 00 00' - '00 00 27 27 09 12 18 16 12 16 16 1D 1D 1D 1D 23' - '1D 22 23 22 23 23 25 22 19 19 14 02 27 27 00 00' - '00 00 00 27 27 09 16 18 16 18 1D 1D 1D 1D 22 1D' - '23 22 23 23 23 24 23 25 20 1A 04 27 27 00 00 00' - '00 00 00 00 27 27 09 16 18 1D 22 1D 1D 22 1D 23' - '22 23 22 23 24 23 26 26 1D 07 27 27 00 00 00 00' - '00 00 00 00 00 27 27 09 1D 22 1D 22 1D 22 23 22' - '23 22 26 23 25 23 25 22 0A 27 27 00 00 00 00 00' - '00 00 00 00 00 00 27 27 10 1D 22 1E 22 23 22 23' - '22 23 23 24 23 26 25 0B 27 27 00 00 00 00 00 00' - '00 00 00 00 00 00 27 27 15 23 22 22 23 22 23 24' - '23 25 25 23 26 26 0F 27 27 00 00 00 00 00 00 00' - '00 00 00 00 00 00 27 27 05 1C 23 22 22 23 24 23' - '25 25 23 26 26 10 27 27 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 27 27 04 17 22 23 23 25 25' - '23 25 26 26 15 27 27 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 27 27 04 1D 26 26 25 23' - '26 26 25 22 04 27 27 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 27 27 06 22 25 26 26' - '25 26 22 06 27 27 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 27 27 04 1C 26 26' - '26 23 07 27 27 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 27 27 01 1A 26' - '23 0A 27 27 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 27 27 27 06' - '04 27 27 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 27 27' - '27 27 00 00 00 00 00 00 00 00 00 00 00 00 FF FC' - '7F FF FF F8 3F FF FF F0 1F FF FF C0 0F FF FF 80' - '07 FF FF 00 03 FF FC 00 01 FF F8 00 00 FF F0 00' - '00 7F C0 00 00 3F 80 00 00 1F 00 00 00 0F 00 00' - '00 07 00 00 00 03 00 00 00 01 80 00 00 00 C0 00' - '00 00 E0 00 00 00 F0 00 00 00 F8 00 00 01 FC 00' - '00 03 FE 00 00 07 FF 00 00 0F FF 00 00 1F FF 00' - '00 3F FF 80 00 7F FF C0 00 7F FF E0 00 FF FF F0' - '01 FF FF F8 03 FF FF FC 07 FF FF FF 0F FF 28 00' - '00 00 10 00 00 00 20 00 00 00 01 00 08 00 00 00' - '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 11 00 00 00 00 11' - '00 00 00 00 11 00 11 11 11 00 22 00 00 00 33 00' - '00 00 33 33 00 00 33 00 33 00 22 22 22 00 33 33' - '33 00 66 33 00 00 66 33 33 00 66 66 33 00 44 44' - '44 00 66 66 66 00 77 77 77 00 7F 7F 7F 00 99 66' - '66 00 FF 99 33 00 99 99 66 00 CC 99 66 00 FF 99' - '66 00 FF CC 66 00 CC 99 99 00 CC CC 99 00 FF CC' - '99 00 FF FF 99 00 CC 99 CC 00 CC CC CC 00 DD DD' - 'DD 00 FF CC CC 00 FF FF CC 00 EE EE EE 00 FF FF' - 'FF 00 00 00 00 00 FF CC FF 00 EE EE EE 00 FF FF' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00' + '00 00 01 FF 00 00 FF 00 00 00 03 FF 00 00 FF 00' + '00 00 07 FF 00 00 FF 00 00 00 07 FF 00 00 FF 00' + '00 00 0F FF 00 00 FF 00 00 00 1F FF 00 00 FF 00' + '00 00 1F FF 00 00 FF 00 00 00 3F FF 00 00 FF 00' + '00 00 FF FF 00 00 FF FF FF FF FF FF 00 00 28 00' + '00 00 30 00 00 00 60 00 00 00 01 00 08 00 00 00' + '00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 C0 DC C0 00 F0 CA A6 00 6C 6C' + '6C 00 6F 6F 6F 00 78 78 78 00 7E 7E 7E 00 FF 00' + 'FF 00 8A 8A 8A 00 96 96 96 00 99 99 99 00 9B 9B' + '9B 00 9D 9D 9D 00 A1 A1 A1 00 A2 A2 A2 00 A3 A3' + 'A3 00 A4 A4 A4 00 A5 A5 A5 00 A7 A7 A7 00 A8 A8' + 'A8 00 A9 A9 A9 00 AA AA AA 00 AB AB AB 00 AC AC' + 'AC 00 AD AD AD 00 AE AE AE 00 AF AF AF 00 B0 B0' + 'B0 00 B1 B1 B1 00 B2 B2 B2 00 B3 B3 B3 00 B5 B5' + 'B5 00 B6 B6 B6 00 B9 B9 B9 00 BA BA BA 00 BC BC' + 'BC 00 BD BD BD 00 BF BF BF 00 C0 C0 C0 00 C5 C5' + 'C5 00 C6 C6 C6 00 C8 C8 C8 00 CA CA CA 00 CC CC' + 'CC 00 CE CE CE 00 CF CF CF 00 D0 D0 D0 00 D3 D3' + 'D3 00 D4 D4 D4 00 D5 D5 D5 00 D6 D6 D6 00 D7 D7' + 'D7 00 D8 D8 D8 00 D9 D9 D9 00 DA DA DA 00 DB DB' + 'DB 00 DC DC DC 00 DD DD DD 00 DE DE DE 00 DF DF' + 'DF 00 E0 E0 E0 00 E1 E1 E1 00 E2 E2 E2 00 E3 E3' + 'E3 00 E4 E4 E4 00 E5 E5 E5 00 E6 E6 E6 00 E7 E7' + 'E7 00 E8 E8 E8 00 E9 E9 E9 00 EA EA EA 00 EB EB' + 'EB 00 EC EC EC 00 ED ED ED 00 EE EE EE 00 EF EF' + 'EF 00 F0 F0 F0 00 F1 F1 F1 00 F2 F2 F2 00 F3 F3' + 'F3 00 F4 F4 F4 00 F5 F5 F5 00 F6 F6 F6 00 F7 F7' + 'F7 00 F8 F8 F8 00 F9 F9 F9 00 FA FA FA 00 FB FB' + 'FB 00 FC FC FC 00 FD FD FD 00 FE FE FE 00 FF FF' 'FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -1686,150 +817,6 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 17' - '95 00 00 00 38 00 A8 44 F9 77 13 00 00 00 18 0A' - '38 00 00 00 38 00 18 6C 38 00 98 17 95 00 00 00' - '00 00 E0 19 95 00 F0 88 FA 77 70 38 F5 77 FF FF' - 'FF FF A8 44 F9 77 70 7D F5 77 3A 8A F5 77 86 00' - '00 00 86 00 00 00 08 00 00 00 B0 18 95 00 00 00' - '00 00 CB 44 F9 77 38 9F 07 00 CD 8B F5 77 78 13' - '05 00 37 90 F5 77 00 00 00 00 3E 8A F5 77 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 10 00 00 00 00 00 00 00 56 00' - '58 00 00 EC FD 7F 1A 02 00 00 4C 16 95 00 40 9F' - '07 00 FC 15 95 00 FF FF FF FF B4 1A 95 00 45 00' - '00 00 28 02 00 00 FF FF FF FF E2 D8 F5 77 7D 9B' - 'F5 77 94 B6 01 00 00 00 05 00 F4 17 95 00 80 00' - '10 C0 B4 1A 95 00 F0 88 FA 77 88 1C F5 77 FF FF' - 'FF FF 37 90 F5 77 00 00 00 00 3E 8A F5 77 9B B2' - 'E7 77 B7 00 00 00 02 00 00 00 A4 1A 95 00 01 00' - '00 00 18 00 00 00 00 00 00 00 10 19 95 00 42 00' - '00 00 00 00 00 00 F4 18 95 00 00 00 00 00 00 00' - '00 00 00 00 00 00 0C 00 00 00 02 00 00 00 01 01' - 'F5 77 00 EC FD 7F 56 00 00 00 00 00 00 00 03 00' - '00 00 5E 00 1A 02 40 9F 07 00 00 00 00 00 40 9F' - '07 00 05 00 00 00 BE B3 E7 77 4C 19 95 00 A3 B4' - 'E7 77 F8 00 00 00 00 00 00 C0 00 00 00 00 00 00' - '00 00 02 00 00 00 80 00 00 00 00 00 00 00 8C 1A' - '95 00 7F E9 4B 00 08 6C 0C 01 00 00 00 C0 00 00' - '00 00 80 1A 95 00 02 00 00 00 80 00 00 00 00 00' - '00 00 C0 27 95 00 C4 F5 AF 00 02 00 00 00 44 3A' - '5C 6F 73 65 78 70 65 72 74 73 5C 72 65 61 63 74' - '6F 73 5C 6C 69 62 5C 73 68 65 6C 6C 33 32 5C 66' - '6F 6C 64 65 72 2E 69 63 6F 00 1A 93 4B 00 14 1A' - '95 00 1F 3B D4 77 13 00 00 00 98 00 00 00 4F 3B' - 'D4 77 E0 19 95 00 33 3B D4 77 64 C5 F5 77 A9 F1' - 'E7 77 F8 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 1A 95 00 18 6C 38 00 86 00 00 00 00 00' - '00 00 C9 F1 E7 77 86 00 00 00 A4 1A 95 00 08 00' - '00 00 00 00 00 00 86 00 00 00 86 00 00 00 08 00' - '00 00 F4 19 95 00 33 3B D4 77 B4 1A 95 00 09 48' - 'E9 77 B8 10 E9 77 FF FF FF FF C9 F1 E7 77 16 EA' - '4B 00 F8 00 00 00 18 6C 38 00 86 00 00 00 58 1A' - '95 00 00 00 00 00 00 00 00 00 00 00 01 23 23 00' - '00 00 00 00 00 00 00 00 00 00 00 02 03 12 0C 23' - '00 00 00 00 00 00 00 00 00 00 02 08 15 22 20 0A' - '03 00 00 00 00 00 00 00 23 23 0C 1A 1F 22 22 1F' - '07 03 00 00 00 00 00 01 01 10 1A 1A 13 1A 22 22' - '1D 07 23 00 00 00 23 09 1A 1F 1A 13 16 13 1A 21' - '22 1C 06 02 00 00 05 1F 20 1A 16 16 13 17 16 1A' - '1E 21 19 05 23 00 23 0D 1A 16 13 17 16 16 17 1A' - '1A 1E 1D 18 04 01 00 23 0B 17 16 16 13 16 1A 1A' - '20 1F 1D 1F 14 23 00 00 23 0B 17 16 1A 1A 1F 20' - '1F 21 20 1C 0A 23 00 00 00 23 0C 17 1F 1B 1F 1F' - '20 20 21 0D 23 00 00 00 00 00 23 18 20 1F 20 1F' - '22 22 0F 23 00 00 00 00 00 00 23 0E 1A 21 20 22' - '22 14 23 00 00 00 00 00 00 00 00 23 0C 20 22 22' - '18 02 23 00 00 00 00 00 00 00 00 00 23 0E 1F 1D' - '02 23 00 00 00 00 00 00 00 00 00 00 00 23 02 01' - '23 00 00 00 00 00 FC 7F 00 00 F8 3F 00 00 F0 1F' - '00 00 C0 0F 00 00 80 07 00 00 00 03 00 00 00 01' - '00 00 00 00 00 00 80 00 00 00 C0 00 00 00 E0 01' - '00 00 F0 03 00 00 F0 07 00 00 F8 07 00 00 FC 0F' - '00 00 FE 1F 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 04 00 00 00 00 00 00 02 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 80 00 00 00 00 80 00 00 80 80 00 00 00 00' - '80 00 80 00 80 00 00 80 80 00 C0 C0 C0 00 80 80' - '80 00 FF 00 00 00 00 FF 00 00 FF FF 00 00 00 00' - 'FF 00 FF 00 FF 00 00 FF FF 00 FF FF FF 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 08 10 00 00 00 00 00 00 00 00 00' - '00 00 00 00 08 BF 81 00 00 00 00 00 00 00 00 00' - '00 00 00 04 1B 7F F8 00 00 00 00 00 00 00 00 00' - '00 00 00 9B 77 FF FF 89 00 00 00 00 00 00 00 00' - '00 00 00 78 7F FF FF F8 20 00 00 00 00 00 00 00' - '00 00 1F B7 78 FF FF F7 10 00 00 00 00 00 00 00' - '00 81 77 8F B7 BF FF FF F8 20 00 00 00 00 00 00' - '08 BF FB FB 8B 8B FF FF F7 18 00 00 00 00 00 00' - '17 7F B7 88 B1 B7 BF FF 7F F8 00 00 00 00 00 0B' - 'FF F8 77 BB 7B 7B 78 FF F7 F7 B0 00 00 00 09 7F' - '7F B7 7B 88 B7 B8 BF BF 7F 77 7B 00 00 00 08 7F' - '7F 87 B8 B7 B8 B8 7B FB F7 F7 F7 90 00 00 00 97' - 'FB 7B 7B 7B 7B 8F B7 BF BF 7F 77 78 00 00 00 08' - 'B7 B8 B7 B8 B8 BB 7B 7B F7 77 77 7F 90 00 00 00' - '1B 7B 8B 7B 87 B7 BF BF 7F F7 77 FB F1 00 00 00' - '08 B8 B7 B7 BB 7B 77 F7 FB FF 77 77 78 00 00 00' - '00 8B 7B 7B B7 77 7F BF BF FF 77 77 80 00 00 00' - '00 08 B8 B7 7B FB F7 F7 F7 FF FF B7 00 00 00 00' - '00 00 1B FB 77 7F BF 7F BF FF FF 78 00 00 00 00' - '00 00 08 BF 7F BF 7F F7 FF BF FF 80 00 00 00 00' - '00 00 00 87 FB F7 FF BF FF FF F8 00 00 00 00 00' - '00 00 00 BF 77 FF BF FF 7F FF 80 00 00 00 00 00' - '00 00 00 07 F7 F7 FF F7 FF F8 00 00 00 00 00 00' - '00 00 00 00 BF 7F 7F FF FF B0 00 00 00 00 00 00' - '00 00 00 00 8B FF FF FF F7 00 00 00 00 00 00 00' - '00 00 00 00 08 7F FF FF 78 00 00 00 00 00 00 00' - '00 00 00 00 00 07 FF F7 80 00 00 00 00 00 00 00' - '00 00 00 00 00 00 8F F8 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FC' - '7F FF FF F8 3F FF FF F0 1F FF FF C0 0F FF FF 80' - '07 FF FF 00 03 FF FC 00 01 FF F8 00 00 FF F0 00' - '00 7F C0 00 00 3F 80 00 00 1F 00 00 00 0F 00 00' - '00 07 00 00 00 03 00 00 00 01 80 00 00 00 C0 00' - '00 00 E0 00 00 00 F0 00 00 00 F8 00 00 01 FC 00' - '00 03 FE 00 00 07 FF 00 00 0F FF 00 00 1F FF 00' - '00 3F FF 80 00 7F FF C0 00 7F FF E0 00 FF FF F0' - '01 FF FF F8 03 FF FF FC 07 FF FF FF 0F FF 28 00' - '00 00 10 00 00 00 20 00 00 00 01 00 04 00 00 00' - '00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 80' - '00 00 80 80 00 00 00 00 80 00 80 00 80 00 00 80' - '80 00 C0 C0 C0 00 80 80 80 00 FF 00 00 00 00 FF' - '00 00 FF FF 00 00 00 00 FF 00 FF 00 FF 00 00 FF' - 'FF 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' - '00 01 80 00 00 00 00 00 01 8F 78 00 00 00 00 00' - '1B FF F7 10 00 00 00 4B 8F B7 FF 70 00 00 08 BF' - '7B 8B FF F7 90 00 07 F7 7B 87 B7 F7 70 00 08 7B' - 'B7 BB 7B 7F 77 00 00 8B 7B B7 BF F7 77 B0 00 08' - 'B7 B7 FB FF F7 00 00 00 17 7F BF 7F F8 00 00 00' - '0B F7 FF FF 80 00 00 00 08 7F 7F F8 00 00 00 00' - '00 8F FF 70 00 00 00 00 00 08 F7 00 00 00 00 00' - '00 00 00 00 00 00 FC 7F 00 00 F8 3F 00 00 F0 1F' - '00 00 C0 0F 00 00 80 07 00 00 00 03 00 00 00 01' - '00 00 00 00 00 00 80 00 00 00 C0 00 00 00 E0 01' - '00 00 F0 03 00 00 F0 07 00 00 F8 07 00 00 FC 0F' - '00 00 FE 1F 00 00 28 00 00 00 30 00 00 00 60 00' - '00 00 01 00 08 00 00 00 00 00 00 09 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 11 00 00 00 00 11 00 00 00 00 11 00 22 00' - '00 00 33 00 00 00 00 22 00 00 33 33 00 00 22 22' - '22 00 44 00 00 00 55 00 00 00 66 00 00 00 66 33' - '00 00 66 33 33 00 66 66 33 00 7F 7F 7F 00 99 33' - '00 00 99 33 33 00 99 66 00 00 99 66 33 00 CC 66' - '00 00 CC 66 33 00 99 66 66 00 CC 66 66 00 CC 99' - '33 00 FF 99 33 00 FF CC 33 00 99 99 66 00 CC 99' - '66 00 FF 99 66 00 CC CC 66 00 FF CC 66 00 BB BB' - 'BB 00 CC 99 99 00 FF 99 99 00 CC CC 99 00 FF CC' - '99 00 FF FF 99 00 CC 99 CC 00 CC CC CC 00 DD DD' - 'DD 00 FF CC CC 00 CC FF CC 00 FF FF CC 00 FF CC' - 'FF 00 EE EE EE 00 FF FF FF 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -1838,220 +825,1321 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 C0 17 95 00 00 00 38 00 A8 44' - 'F9 77 13 00 00 00 18 0A 38 00 00 00 38 00 18 6C' - '38 00 98 17 95 00 00 00 00 00 E0 19 95 00 F0 88' - 'FA 77 70 38 F5 77 FF FF FF FF A8 44 F9 77 70 7D' - 'F5 77 3A 8A F5 77 86 00 00 00 86 00 00 00 08 00' - '00 00 B0 18 95 00 00 00 00 00 CB 44 F9 77 38 9F' - '07 00 CD 8B F5 77 78 13 05 00 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00' - '00 00 00 00 00 00 56 00 58 00 00 EC FD 7F 1A 02' - '00 00 4C 16 95 00 40 9F 07 00 FC 15 95 00 FF FF' - 'FF FF B4 1A 95 00 45 00 00 00 28 02 00 00 FF FF' - 'FF FF E2 D8 F5 77 7D 9B F5 77 94 B6 01 00 00 00' - '05 00 F4 17 95 00 80 00 10 C0 B4 1A 95 00 F0 88' - 'FA 77 88 1C F5 77 FF FF FF FF 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 9B B2 E7 77 B7 00 00 00 02 00' - '00 00 A4 1A 95 00 01 00 00 00 18 00 00 00 00 00' - '00 00 10 19 95 00 42 00 00 00 00 00 00 00 F4 18' - '95 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00' - '00 00 02 00 00 00 01 01 F5 77 00 EC FD 7F 56 00' - '00 00 00 00 00 00 03 00 00 00 5E 00 1A 02 40 9F' - '07 00 00 00 00 00 40 9F 07 00 05 00 00 00 BE B3' - 'E7 77 4C 19 95 00 A3 B4 E7 77 F8 00 00 00 00 00' - '00 C0 00 00 00 00 00 00 00 00 02 00 00 00 80 00' - '00 00 00 00 00 00 8C 1A 95 00 7F E9 4B 00 08 6C' - '0C 01 00 00 00 C0 00 00 00 00 80 1A 95 00 02 00' - '00 00 80 00 00 00 00 00 00 00 C0 27 95 00 C4 F5' - 'AF 00 02 00 00 00 44 3A 5C 6F 73 65 78 70 65 72' - '74 73 5C 72 65 61 63 74 6F 73 5C 6C 69 62 5C 73' - '68 65 6C 6C 33 32 5C 66 6F 6C 64 65 72 2E 69 63' - '6F 00 1A 93 4B 00 14 1A 95 00 1F 3B D4 77 13 00' - '00 00 98 00 00 00 4F 3B D4 77 E0 19 95 00 33 3B' - 'D4 77 64 C5 F5 77 A9 F1 E7 77 F8 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 1A 95 00 18 6C' - '38 00 86 00 00 00 00 00 00 00 C9 F1 E7 77 86 00' - '00 00 A4 1A 95 00 08 00 00 00 00 00 00 00 86 00' - '00 00 86 00 00 00 08 00 00 00 F4 19 95 00 33 3B' - 'D4 77 B4 1A 95 00 09 48 E9 77 B8 10 E9 77 FF FF' - 'FF FF C9 F1 E7 77 16 EA 4B 00 F8 00 00 00 18 6C' - '38 00 86 00 00 00 58 1A 95 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 2F 2F 2F 2F 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 2F 2F 2F 2F 2F 2F 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 2F 2F 2F 01 2F 2F 2F 2F 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '2F 2F 2F 2F 09 10 0C 02 2F 2F 2F 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2F' - '2F 2F 04 11 15 1C 17 09 02 2F 2F 2F 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 2F 2F' - '2F 05 13 1C 23 2E 2D 1C 0B 2F 2F 2F 2F 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 2F 2F 2F 2F' - '0B 23 22 21 2E 2E 2E 28 1C 0A 2F 2F 2F 2F 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 2F 2F 2F 01 0C' - '23 22 18 2E 2E 2E 2E 2E 28 1C 05 2F 2F 2F 2F 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 2F 2F 2F 2F 04 13 29' - '24 17 2D 2E 2E 2E 2E 2E 2E 28 17 05 02 2F 2F 2F' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 2F 2F 2F 01 05 1C 2D 24' - '15 2D 2E 2E 2E 2E 2E 2E 2E 2E 27 1C 04 01 2F 2F' - '2F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 2F 2F 2F 01 0C 24 29 1F 17' - '2D 24 24 2E 2E 2E 2E 2E 2E 2E 2E 28 17 06 2F 2F' - '2F 2F 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 2F 2F 2F 2F 05 16 29 29 24 17 29' - '29 19 1D 24 2E 2E 2E 2E 2E 2E 2E 2D 28 17 09 2F' - '2F 2F 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 2F 2F 2F 2F 0C 21 2B 24 24 1D 27 2B' - '19 19 19 1D 29 2E 2E 2E 2E 2E 2D 2E 2E 28 1B 05' - '2F 2F 2F 2F 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 2F 2F 2F 03 10 29 2B 24 29 1D 20 2B 19' - '19 1D 19 19 19 29 2E 2E 2E 2E 2E 2D 2D 2D 29 17' - '04 2F 2F 2F 00 00 00 00 00 00 00 00 00 00 00 00' - '00 2F 2F 2F 2F 05 13 29 2B 24 29 1C 1C 2E 1D 19' - '19 19 1D 19 1D 19 2D 2E 2E 2E 2D 2E 2D 2D 2D 27' - '13 04 2F 2F 2F 00 00 00 00 00 00 00 00 00 00 00' - '2F 2F 2F 2F 0A 21 2B 2B 24 29 24 21 2E 1D 19 1D' - '19 1D 19 1D 19 1F 19 29 2E 2D 2E 2D 29 2D 28 2D' - '28 15 2F 2F 2F 2F 00 00 00 00 00 00 00 00 00 2F' - '2F 2F 04 13 24 28 29 2B 29 24 1C 2B 24 19 1D 19' - '1D 19 1D 1A 1D 1D 1F 1D 2B 2C 2E 2D 2D 2D 2D 28' - '29 27 15 04 2F 2F 2F 00 00 00 00 00 00 00 2F 2F' - '2F 05 13 29 2E 2B 2B 29 24 1C 2D 24 19 1D 1A 1D' - '19 1D 19 1D 19 1D 1D 1F 1D 25 28 2D 2D 2D 29 28' - '28 28 29 13 01 2F 2F 2F 00 00 00 00 00 00 2F 2F' - '0C 22 2E 2B 29 29 2D 2B 1C 29 1F 1D 1D 19 1D 19' - '1D 1A 1D 19 1D 1F 1D 1D 1F 22 2B 2D 2D 28 28 28' - '28 27 28 27 15 2F 2F 2F 2F 00 00 00 00 00 2F 2F' - '0C 2B 29 2B 29 2B 2B 17 24 24 1D 1D 19 1D 1D 1A' - '1D 1D 19 1D 1F 1D 1D 1F 22 1F 1F 29 28 2D 29 28' - '28 28 27 29 20 15 04 2F 2F 2F 2F 00 00 00 2F 2F' - '2F 10 2B 2D 2B 2D 1D 23 24 1D 19 1D 1F 19 1D 1D' - '1A 1D 1D 19 1D 1F 1F 1D 1F 24 22 1F 29 28 2D 28' - '29 28 29 27 29 20 10 2F 2F 2F 2F 00 00 00 2F 2F' - '2F 2F 10 24 2E 1D 1C 24 19 1D 1F 19 1D 1F 19 1D' - '1D 1D 19 1D 1F 1D 1D 1F 22 1F 1F 24 24 29 2A 29' - '28 27 27 27 27 20 29 13 2F 2F 2F 2F 00 00 00 00' - '2F 2F 2F 11 24 1C 24 1D 1D 19 1D 1D 19 1D 1D 1A' - '1D 19 1F 1D 19 1F 1D 24 1F 22 24 24 24 24 29 28' - '27 29 27 29 20 27 28 24 11 02 2F 2F 2F 2F 00 00' - '00 2F 2F 01 12 1D 1F 1D 1A 1D 19 1D 1F 19 1F 1D' - '19 1D 1D 1A 1D 1D 1D 1F 1D 24 24 24 25 29 2B 29' - '28 27 27 27 27 27 21 27 28 10 2F 2F 2F 2F 00 00' - '00 00 2F 2F 04 14 1D 1D 1D 1D 1F 1D 19 1D 19 1D' - '1F 1D 19 1D 1F 1D 1F 24 24 24 25 29 29 25 29 2B' - '29 28 29 20 29 23 27 26 23 29 13 01 2F 2F 00 00' - '00 00 2F 2F 2F 08 10 19 1D 1F 19 1F 1D 1D 1D 1F' - '1D 19 1D 1D 1D 24 24 24 24 24 29 2B 24 29 2B 2B' - '29 2B 20 20 27 27 26 23 26 27 1C 02 2F 2F 00 00' - '00 00 00 2F 2F 2F 01 14 1D 1D 1D 19 1D 1F 1D 19' - '1D 1F 1D 1F 24 24 24 24 2B 24 24 29 2B 29 29 2B' - '2B 29 2E 27 23 26 23 20 20 21 05 2F 2F 2F 00 00' - '00 00 00 00 2F 2F 2F 03 12 19 1D 1D 1F 19 1F 1D' - '1F 1D 24 24 24 24 29 24 24 29 29 2B 29 2B 2B 29' - '2B 2E 2B 29 27 23 26 27 21 07 2F 2F 2F 00 00 00' - '00 00 00 00 00 00 2F 2F 2F 12 1D 1F 1D 1D 1D 1D' - '24 24 24 24 24 24 24 2B 29 24 2B 29 2B 29 2B 2B' - '2C 2B 2D 2E 28 26 23 21 0D 2F 2F 2F 00 00 00 00' - '00 00 00 00 00 00 2F 2F 2F 03 10 18 1D 1D 1F 24' - '24 29 24 24 2B 24 29 29 24 2B 29 2B 29 2B 2B 2C' - '2B 2D 2D 2E 2B 2B 27 0D 2F 2F 2F 00 00 00 00 00' - '00 00 00 00 00 00 00 00 2F 2F 2F 10 1D 1F 24 29' - '24 24 29 24 24 29 25 29 2B 29 2B 29 2E 29 2B 2D' - '2B 2E 2B 29 2E 2B 13 2F 2F 2F 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 2F 2F 2F 0C 24 29 24' - '25 29 24 2B 29 25 29 29 2B 29 2B 2B 29 2B 2D 2B' - '2E 29 2B 2E 2E 13 2F 2F 2F 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 2F 2F 2F 0D 1F 29' - '29 24 29 24 29 29 2B 29 29 2B 29 2D 2B 2D 2D 2B' - '2D 2B 2E 2E 13 01 2F 2F 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 2F 2F 0C 1E 24' - '29 2B 29 2B 29 2B 29 2B 2B 2C 2B 2B 2D 2D 2B 2C' - '2B 2E 2E 1C 01 2F 2F 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 2F 2F 15 2D 29' - '1E 29 2B 29 2B 29 2B 29 2D 2B 2C 2B 2D 2B 2C 2B' - '2E 2D 22 02 01 2F 2F 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 2F 2F 07 22 2B' - '29 24 29 2B 29 2B 2C 2B 2B 2D 2B 2C 2B 2D 2B 2E' - '2E 29 07 2F 2F 2F 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 2F 2F 2F 07 1C' - '2E 23 24 2C 2B 2D 2B 2C 2B 2D 2D 2B 2D 2D 2E 2E' - '24 07 2F 2F 2F 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 2F 2F 2F 04' - '1B 1D 29 2B 2D 2B 2C 2B 2E 2D 2B 2E 2D 2E 2E 29' - '0C 2F 2F 2F 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 2F 2F 2F' - '01 18 2B 2D 2B 2C 2B 2E 2C 2B 2E 2D 2E 2E 29 0E' - '2F 2F 2F 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2F' - '2F 04 1C 2E 2E 2B 2E 2D 2B 2E 2D 2E 2E 2B 13 2F' - '2F 2F 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2F' - '2F 2F 04 1C 2D 2E 2D 2E 2E 2E 2E 2E 2E 1B 01 2F' - '2F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '2F 2F 2F 2F 1C 2D 2E 2E 2E 2E 2E 2E 1C 01 2F 2F' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 2F 2F 2F 2F 13 2B 2E 2E 2E 2E 1E 01 2F 2F 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 2F 2F 2F 0E 29 2E 2D 23 05 2F 2F 2F 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 FB' + 'FF 00 A4 A0 A0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 2F 2F 2F 07 13 07 04 2F 2F 2F 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 39 28' + '23 23 23 23 23 23 23 23 23 22 22 22 22 22 21 21' + '21 20 20 20 20 1F 1F 1F 1F 1E 1E 1E 1D 25 33 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 42 50' + '50 50 4F 4F 4E 4E 4D 4D 4C 4B 4B 4A 49 48 47 46' + '45 43 42 41 40 3F 3E 3D 3B 3A 39 38 37 36 24 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 51' + '50 50 4F 4F 4E 4E 4D 4D 4C 4C 4B 4A 4A 49 48 47' + '45 44 43 42 41 40 3F 3D 3C 3B 3A 39 38 37 1D 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 51' + '51 50 50 4F 4F 4E 4E 4D 4D 4C 4C 4B 4A 49 48 47' + '46 45 44 43 42 41 40 3E 3D 3C 3B 3A 39 38 1D 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 51' + '51 51 50 50 4F 4F 4E 4E 4D 4D 4C 4C 4B 4A 49 48' + '47 46 45 44 43 42 41 40 3E 3D 3C 3B 3A 39 1D 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 52' + '51 51 51 50 50 4F 4F 4E 4E 4D 4D 4C 4C 4B 4A 49' + '48 47 46 45 44 43 42 41 40 3E 3D 3C 3B 3A 1E 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 52' + '52 51 51 51 50 50 4F 4F 4E 4E 4D 4D 4C 4C 4B 4A' + '49 48 47 46 45 44 43 42 41 40 3E 3D 3C 3B 1E 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 53' + '52 52 51 51 51 50 50 4F 4F 4E 4E 4D 4D 4C 4C 4B' + '4A 49 48 47 46 45 44 43 42 41 40 3E 3D 3C 1E 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 53' + '53 52 52 51 51 51 50 50 4F 4F 4E 4E 4D 4D 4C 4C' + '4B 4A 49 48 47 46 45 44 43 42 41 40 3F 3E 1E 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 54' + '53 53 52 52 51 51 51 50 50 4F 4F 4E 4E 4D 4D 4C' + '4C 4B 4A 49 48 47 46 45 44 43 42 41 40 3F 1F 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 43 54' + '54 53 53 52 52 51 51 51 50 50 4F 4F 4E 4E 4D 4D' + '4C 4C 4B 4A 49 48 47 46 45 44 43 42 41 40 1F 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 43 55' + '54 54 53 53 52 52 51 51 50 50 50 4F 4F 4E 4E 4D' + '4D 4C 4B 4B 4A 49 48 47 46 45 44 43 42 41 1F 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 43 55' + '55 54 54 53 53 52 52 51 51 50 50 50 4F 4F 4E 4E' + '4D 4D 4C 4B 4B 4A 49 48 47 46 45 44 43 42 20 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 56' + '55 55 54 54 53 53 52 52 51 51 50 50 50 4F 4F 4E' + '4E 4D 4D 4C 4B 4B 4A 49 48 47 46 45 44 43 20 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 56' + '56 55 55 54 54 53 53 52 52 51 51 50 50 50 4F 4F' + '4E 4E 4D 4D 4C 4B 4B 4A 49 48 47 46 45 44 20 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 57' + '56 56 55 55 54 54 53 53 52 52 51 51 50 50 50 4F' + '4F 4E 4E 4D 4D 4C 4B 4B 4A 49 48 47 46 46 20 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 57' + '57 56 56 55 55 54 54 53 53 52 52 51 51 50 50 50' + '4F 4F 4E 4E 4D 4D 4C 4B 4B 4A 49 48 47 47 21 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 58' + '57 57 56 56 55 55 54 54 53 53 52 52 51 51 50 50' + '50 4F 4F 4E 4E 4D 4D 4C 4B 4B 4A 49 49 48 21 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 58' + '58 57 57 56 56 55 55 54 54 53 53 52 52 51 51 50' + '50 50 4F 4F 4E 4E 4D 4D 4C 4B 4B 4A 49 49 21 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 46 59' + '59 58 57 57 56 56 55 55 54 54 53 53 52 52 51 51' + '50 50 50 4F 4F 4E 4E 4D 4D 4C 4B 4B 4A 4A 21 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 46 5A' + '59 59 58 57 57 56 56 55 55 54 54 53 53 52 52 51' + '51 50 50 50 4F 4F 4E 4E 4D 4D 4C 4B 4B 4B 21 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 5A' + '5A 59 59 58 57 57 56 56 55 55 54 54 53 53 52 52' + '51 51 50 50 50 4F 4F 4E 4E 4D 4D 4C 4C 4B 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 5B' + '5A 5A 59 59 58 57 57 56 56 55 55 54 54 53 53 52' + '52 51 51 50 50 50 4F 4F 4E 4E 4D 4D 4C 4C 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 5B' + '5B 5A 5A 59 59 58 57 57 56 56 55 55 54 54 53 53' + '52 52 51 51 50 50 50 4F 4F 4E 4E 4D 4D 4C 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 48 5C' + '5B 5B 5A 5A 59 59 58 57 57 56 56 55 55 54 54 53' + '53 52 52 51 51 50 50 50 4F 4F 4E 4E 4D 4D 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 48 5C' + '5C 5B 5B 5A 5A 59 59 58 57 57 56 56 55 55 54 54' + '53 53 52 52 51 51 50 50 50 4F 4F 4E 4E 4D 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 48 5D' + '5C 5C 5B 5B 5A 5A 59 59 58 57 57 56 56 55 55 54' + '54 53 53 52 52 51 51 50 50 50 4F 4F 4E 4E 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 48 5E' + '5D 5C 5C 5B 5B 5A 5A 59 59 58 57 57 56 56 55 55' + '54 54 53 53 52 52 51 51 50 50 50 4F 4F 4F 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 48 5E' + '5E 5D 5C 5C 5B 5B 5A 5A 59 59 58 57 57 56 56 55' + '55 54 54 53 53 52 52 51 51 50 50 50 4F 4F 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4A 5F' + '5E 5E 5D 5C 5C 5B 5B 5A 5A 59 59 58 57 57 56 56' + '55 55 54 54 53 53 52 52 51 51 50 50 4F 4D 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4A 60' + '5F 5F 5E 5D 5C 5C 5B 5B 5A 5A 59 59 58 57 57 56' + '56 55 55 54 54 53 53 52 52 51 51 50 4D 43 1E 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 61' + '60 5F 5F 5E 5D 5C 5C 5B 5B 5A 5A 59 59 58 57 57' + '56 56 55 55 54 54 53 53 52 52 51 51 47 33 1B 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 61' + '61 60 5F 5F 5E 5D 5C 5C 5B 5B 5A 5A 59 58 58 57' + '57 56 56 55 55 54 54 53 53 52 52 51 38 27 1A 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '61 61 60 5F 5F 5E 5D 5C 5C 5B 5B 5A 5A 59 58 58' + '57 57 56 56 55 55 54 54 53 53 52 4C 29 1B 1C 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 61 61 60 5F 5F 5E 5D 5C 5C 5B 5B 5A 5A 59 58' + '58 57 57 56 56 55 55 54 54 53 50 36 11 20 22 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 62 61 61 60 5F 5F 5E 5D 5C 5C 5B 5B 5A 5A 59' + '58 58 57 57 56 56 55 55 54 4E 35 12 13 18 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 62 62 61 61 60 5F 5E 5E 5D 5C 5C 5B 5B 5A 5A' + '59 58 58 57 57 56 55 50 3E 25 0D 10 1D 20 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 62 62 62 61 61 60 5F 5E 5E 5D 5C 5C 5B 5B 5A' + '5A 59 58 58 55 49 34 1C 0C 0B 1F 26 18 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 62 62 62 62 61 61 60 5F 5E 5E 5D 5C 5C 5B 5B' + '5A 5A 59 58 43 12 0A 0F 2A 37 07 15 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 62 62 62 62 62 61 61 60 5F 5E 5E 5D 5C 5C 5B' + '5B 5A 5A 59 3A 18 56 4B 3C 30 1B 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 62 62 62 62 62 62 61 61 60 5F 5E 5E 5D 5C 5C' + '5B 5B 5A 59 49 11 50 41 33 24 1A 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 62 62 62 62 62 62 62 61 61 60 5F 5E 5E 5D 5C' + '5C 5B 5B 5A 53 16 31 36 2B 16 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 62 62 62 62 62 62 62 62 61 61 60 5F 5E 5E 5D' + '5C 5C 5B 5B 57 27 1A 2E 18 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 62' + '62 62 62 62 62 62 62 62 62 62 61 61 60 5F 5E 5E' + '5D 5C 5C 5A 50 22 17 21 1E 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4C 62' + '62 62 62 62 62 62 62 62 62 62 62 61 61 60 5F 5E' + '5D 5C 59 4D 32 14 22 19 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 4E 4D' + '4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 4A 49' + '47 47 43 39 2F 2C 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 FF 00 00 FF 00' + '00 00 00 FF 00 00 FF 00 00 00 01 FF 00 00 FF 00' + '00 00 03 FF 00 00 FF 00 00 00 07 FF 00 00 FF 00' + '00 00 07 FF 00 00 FF 00 00 00 0F FF 00 00 FF 00' + '00 00 1F FF 00 00 FF 00 00 00 1F FF 00 00 FF 00' + '00 00 3F FF 00 00 FF 00 00 00 FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 20 00 00 00 00 00 80 25 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 10 0E 0E 0E 12 0E 0E 0E 12 0E 0E' + '0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E' + '0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E' + '0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E' + '0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E' + '0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E' + '0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E 0E 12 0E 0E' + '0E 12 0E 0E 0E 12 0E 0E 0E 12 00 00 00 10 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 7D 7D' + '7D 51 76 76 76 83 6F 6F 6F 8B 6F 6F 6F 8B 6F 6F' + '6F 8B 6F 6F 6F 8B 6F 6F 6F 8B 6F 6F 6F 8B 6F 6F' + '6F 8B 6F 6F 6F 8B 6F 6F 6F 8B 6E 6E 6E 8B 6E 6E' + '6E 8B 6E 6E 6E 8B 6E 6E 6E 8B 6E 6E 6E 8B 6C 6C' + '6C 8B 6C 6C 6C 8B 6C 6C 6C 8B 6A 6A 6A 8B 6A 6A' + '6A 8B 6A 6A 6A 8B 6A 6A 6A 8B 68 68 68 8B 68 68' + '68 8B 68 68 68 8B 68 68 68 8B 66 66 66 8B 66 66' + '66 8B 66 66 66 8B 64 64 64 8B 6B 6B 6B 83 38 38' + '38 3F 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D4 D4' + 'D4 BF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4' + 'E4 FF E3 E3 E3 FF E2 E2 E2 FF E0 E0 E0 FF DF DF' + 'DF FF DE DE DE FF DD DD DD FF DC DC DC FF DB DB' + 'DB FF DA DA DA FF D8 D8 D8 FF D7 D7 D7 FF D6 D6' + 'D6 FF D5 D5 D5 FF D4 D4 D4 FF D3 D3 D3 FF 67 67' + '67 82 00 00 00 10 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D2 D2' + 'D2 C1 EE EE EE FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E4 E4 E4 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0' + 'E0 FF DF DF DF FF DE DE DE FF DD DD DD FF DC DC' + 'DC FF DA DA DA FF D9 D9 D9 FF D8 D8 D8 FF D7 D7' + 'D7 FF D6 D6 D6 FF D5 D5 D5 FF D4 D4 D4 FF 63 63' + '63 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D2 D2' + 'D2 C1 EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E9 E9' + 'E9 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1' + 'E1 FF E0 E0 E0 FF DF DF DF FF DE DE DE FF DD DD' + 'DD FF DB DB DB FF DA DA DA FF D9 D9 D9 FF D8 D8' + 'D8 FF D7 D7 D7 FF D6 D6 D6 FF D5 D5 D5 FF 63 63' + '63 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D3 D3' + 'D3 C1 EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6' + 'E6 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2' + 'E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF DF FF DE DE' + 'DE FF DD DD DD FF DB DB DB FF DA DA DA FF D9 D9' + 'D9 FF D8 D8 D8 FF D7 D7 D7 FF D6 D6 D6 FF 63 63' + '63 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D3 D3' + 'D3 C1 EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E7 E7' + 'E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3' + 'E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF' + 'DF FF DE DE DE FF DD DD DD FF DB DB DB FF DA DA' + 'DA FF D9 D9 D9 FF D8 D8 D8 FF D7 D7 D7 FF 65 65' + '65 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D3 D3' + 'D3 C1 EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4' + 'E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0' + 'E0 FF DF DF DF FF DE DE DE FF DD DD DD FF DB DB' + 'DB FF DA DA DA FF D9 D9 D9 FF D8 D8 D8 FF 65 65' + '65 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D4 D4' + 'D4 C1 F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E9 E9' + 'E9 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1' + 'E1 FF E0 E0 E0 FF DF DF DF FF DE DE DE FF DD DD' + 'DD FF DB DB DB FF DA DA DA FF D9 D9 D9 FF 65 65' + '65 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D4 D4' + 'D4 C1 F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6' + 'E6 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2' + 'E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF DF FF DE DE' + 'DE FF DD DD DD FF DC DC DC FF DB DB DB FF 65 65' + '65 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D4 D4' + 'D4 C1 F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E7 E7' + 'E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3' + 'E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF' + 'DF FF DE DE DE FF DD DD DD FF DC DC DC FF 67 67' + '67 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D6 D6' + 'D6 C1 F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4' + 'E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0' + 'E0 FF DF DF DF FF DE DE DE FF DD DD DD FF 67 67' + '67 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D6 D6' + 'D6 C1 F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1' + 'E1 FF E0 E0 E0 FF DF DF DF FF DE DE DE FF 67 67' + '67 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D6 D6' + 'D6 C1 F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6' + 'E6 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2' + 'E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF DF FF 69 69' + '69 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D7 D7' + 'D7 C1 F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7' + 'E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3' + 'E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0 E0 FF 69 69' + '69 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D7 D7' + 'D7 C1 F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4' + 'E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1 E1 FF 69 69' + '69 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D7 D7' + 'D7 C1 F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E4 E4 E4 FF E3 E3 E3 FF E3 E3 E3 FF 69 69' + '69 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D8 D8' + 'D8 C1 F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6' + 'E6 FF E5 E5 E5 FF E4 E4 E4 FF E4 E4 E4 FF 6B 6B' + '6B 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D8 D8' + 'D8 C1 F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7' + 'E7 FF E6 E6 E6 FF E6 E6 E6 FF E5 E5 E5 FF 6B 6B' + '6B 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D8 D8' + 'D8 C1 F5 F5 F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E6 E6 E6 FF E6 E6 E6 FF 6B 6B' + '6B 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DA DA' + 'DA C1 F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7 E7 FF 6B 6B' + '6B 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DA DA' + 'DA C1 F7 F7 F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E8 E8 E8 FF E8 E8 E8 FF E8 E8 E8 FF 6B 6B' + '6B 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DB DB' + 'DB C1 F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF 6D 6D' + '6D 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DB DB' + 'DB C1 F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF 6D 6D' + '6D 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DB DB' + 'DB C1 F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF 6D 6D' + '6D 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DC DC' + 'DC C1 F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF 6D 6D' + '6D 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DC DC' + 'DC C1 F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF 6D 6D' + '6D 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DC DC' + 'DC C1 FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF 6D 6D' + '6D 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DD DD' + 'DD C1 FB FB FB FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EC EC EC FF 6D 6D' + '6D 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DD DD' + 'DD C1 FB FB FB FF FB FB FB FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF 6D 6D' + '6D 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DF DF' + 'DF C1 FC FC FC FF FB FB FB FF FB FB FB FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EA EA EA FF 6D 6D' + '6D 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DF DF' + 'DF C1 FD FD FD FF FC FC FC FF FC FC FC FF FB FB' + 'FB FF FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF EA EA EA FF E0 E0 E0 FF 65 65' + '65 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FE FE FE FF FD FD FD FF FC FC FC FF FC FC' + 'FC FF FB FB FB FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF E4 E4 E4 FF CE CE CE FF 60 60' + '60 8A 0E 0E 0E 12 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FE FE FE FF FE FE FE FF FD FD FD FF FC FC' + 'FC FF FC FC FC FF FB FB FB FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF D5 D5 D5 FF B6 B6 B6 FF 5D 5D' + '5D 89 0F 0F 0F 11 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FE FE FE FF FE FE FE FF FD FD' + 'FD FF FC FC FC FF FC FC FC FF FB FB FB FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF E9 E9 E9 FF BA BA BA FF A9 A9 A9 FE 5D 5D' + '5D 86 00 00 00 10 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FE FE FE FF FE FE' + 'FE FF FD FD FD FF FC FC FC FF FC FC FC FF FB FB' + 'FB FF FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF ED ED' + 'ED FF D3 D3 D3 FF 99 99 99 FF AD AD AD FB 52 52' + '52 75 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FF FF FF FF FE FE' + 'FE FF FE FE FE FF FD FD FD FF FC FC FC FF FC FC' + 'FC FF FB FB FB FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF EB EB EB FF D0 D0' + 'D0 FF 9B 9B 9B FF 9D 9D 9D FE 9A 9A 9A E2 40 40' + '40 4B 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FE FE FE FF FE FE FE FF FD FD FD FF FC FC' + 'FC FF FB FB FB FF FB FB FB FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F2 F2' + 'F2 FF ED ED ED FF DB DB DB FF B3 B3 B3 FF 7E 7E' + '7E FF 96 96 96 FE A7 A7 A7 F2 6C 6C 6C 8D 0A 0A' + '0A 19 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FE FE FE FF FE FE FE FF FD FD' + 'FD FF FC FC FC FF FB FB FB FF FB FB FB FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F2 F2 F2 FF E6 E6 E6 FF CF CF' + 'CF FF AA AA AA FF 78 78 78 FF 6F 6F 6F FF AD AD' + 'AD FE B4 B4 B4 FA 82 82 82 B8 21 21 21 2E 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FE FE' + 'FE FF FD FD FD FF FC FC FC FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F5 F5 F5 FF E0 E0 E0 FF 9B 9B 9B FF 6C 6C' + '6C FF 8A 8A 8A FF BC BC BC FF D4 D4 D4 FF C0 C0' + 'C0 FD 92 92 92 D9 43 43 43 4F 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FE FE' + 'FE FF FE FE FE FF FD FD FD FF FC FC FC FF FB FB' + 'FB FF FB FB FB FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF D7 D7 D7 FF A5 A5 A5 FF F3 F3' + 'F3 FF E8 E8 E8 FF D9 D9 D9 FF C8 C8 C8 FE A2 A2' + 'A2 EC 60 60 60 79 0D 0D 0D 13 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FE FE FE FF FE FE FE FF FD FD FD FF FC FC' + 'FC FF FB FB FB FF FB FB FB FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F6 F6 F6 FF E6 E6 E6 FF 99 99 99 FF ED ED' + 'ED FF DE DE DE FF CE CE CE FE B0 B0 B0 F7 79 79' + '79 A6 15 15 15 23 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FE FE FE FF FE FE FE FF FD FD' + 'FD FF FC FC FC FF FB FB FB FF FB FB FB FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F0 F0 F0 FF A3 A3 A3 FF CA CA' + 'CA FF D3 D3 D3 FF BC BC BC FC 8C 8C 8C CC 35 35' + '35 3E 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FE FE' + 'FE FF FD FD FD FF FC FC FC FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F4 F4 F4 FF B6 B6 B6 FF A8 A8' + 'A8 FF C5 C5 C5 FE 9B 9B 9B E5 55 55 55 65 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 E0' + 'E0 C1 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FE FE' + 'FE FF FE FE FE FF FD FD FD FF FC FC FC FF FB FB' + 'FB FF FB FB FB FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F7 F7 F7 FF ED ED ED FF B0 B0 B0 FF A4 A4' + 'A4 FE AB AB AB F3 6F 6F 6F 93 12 12 12 1B 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E2 E2' + 'E2 BF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FE FE FE FF FE FE FE FF FD FD FD FF FC FC' + 'FC FF FB FB FB FF FA FA FA FF F9 F9 F9 FF F6 F6' + 'F6 FF EA EA EA FF CC CC CC FF A1 A1 A1 FE AE AE' + 'AE F9 86 86 86 BA 24 24 24 31 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D8 D8' + 'D8 86 E3 E3 E3 C1 E0 E0 E0 C3 E0 E0 E0 C3 E0 E0' + 'E0 C3 E0 E0 E0 C3 E0 E0 E0 C3 E0 E0 E0 C3 E0 E0' + 'E0 C3 E0 E0 E0 C3 E0 E0 E0 C3 E0 E0 E0 C3 E0 E0' + 'E0 C3 E0 E0 E0 C3 E0 E0 E0 C3 DF DF DF C3 DF DF' + 'DF C3 DE DE DE C3 DC DC DC C2 DB DB DB C2 D6 D6' + 'D6 C3 CA CA CA C3 B4 B4 B4 C3 AB AB AB C1 96 96' + '96 A3 51 51 51 42 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 80' + '00 00 00 FF 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 3F 00 00 FF 00' + '00 00 00 3F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' + '00 00 00 FF 00 00 FF 00 00 00 01 FF 00 00 FF 00' + '00 00 01 FF 00 00 FF 00 00 00 03 FF 00 00 FF 00' + '00 00 07 FF 00 00 FF 00 00 00 0F FF 00 00 FF 00' + '00 00 0F FF 00 00 FF 00 00 00 1F FF 00 00 FF 00' + '00 00 3F FF 00 00 FF FF FF FF FF FF 00 00 28 00' + '00 00 40 00 00 00 80 00 00 00 01 00 04 00 00 00' + '00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 08 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 00 00 00 00 00 00 00 00 00 00 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + '88 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + '88 80 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 88' + '88 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF F8 F8' + '78 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 87' + '88 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 88 87' + '80 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF 8F F8 78' + '80 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF F8 87 87' + '80 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF 8F 88 77 88' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF F8 F8 F8 77 78 78' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 8F 88 77 77 88 80' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF F7 77 77 88 87 00' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 87 88 F8 F8 88 00' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF F7 FF 88 87 80 00' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 87 F8 F8 88 00 00' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF F7 8F 88 70 00 00' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 87 88 88 80 00 00' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF F7 88 87 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 88 78 70 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 87 88 80 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 8F FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 88 87 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 08 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF E0 00 00 00 00 03 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 01 FF FF C0 00 00 00 00 01 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 07 FF FF C0' + '00 00 00 00 07 FF FF C0 00 00 00 00 07 FF FF C0' + '00 00 00 00 0F FF FF C0 00 00 00 00 0F FF FF C0' + '00 00 00 00 1F FF FF C0 00 00 00 00 3F FF FF C0' + '00 00 00 00 3F FF FF C0 00 00 00 00 7F FF FF C0' + '00 00 00 00 FF FF FF C0 00 00 00 01 FF FF FF C0' + '00 00 00 01 FF FF FF C0 00 00 00 03 FF FF FF C0' + '00 00 00 07 FF FF FF C0 00 00 00 07 FF FF FF C0' + '00 00 00 0F FF FF FF E0 00 00 00 3F FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 40 00 00 00 80 00' + '00 00 01 00 08 00 00 00 00 00 00 10 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 C0 C0 C0 00 C0 DC' + 'C0 00 F0 CA A6 00 5B 5B 5B 00 5D 5D 5D 00 65 65' + '65 00 6B 6B 6B 00 72 72 72 00 FF 00 FF 00 82 82' + '82 00 83 83 83 00 85 85 85 00 88 88 88 00 8B 8B' + '8B 00 90 90 90 00 91 91 91 00 92 92 92 00 93 93' + '93 00 96 96 96 00 97 97 97 00 9A 9A 9A 00 9D 9D' + '9D 00 A0 A0 A0 00 A1 A1 A1 00 A2 A2 A2 00 A3 A3' + 'A3 00 A4 A4 A4 00 A5 A5 A5 00 A6 A6 A6 00 A7 A7' + 'A7 00 A8 A8 A8 00 A9 A9 A9 00 AA AA AA 00 AB AB' + 'AB 00 AC AC AC 00 AD AD AD 00 AE AE AE 00 AF AF' + 'AF 00 B0 B0 B0 00 B1 B1 B1 00 B2 B2 B2 00 B3 B3' + 'B3 00 B4 B4 B4 00 B5 B5 B5 00 B6 B6 B6 00 B7 B7' + 'B7 00 B8 B8 B8 00 B9 B9 B9 00 BA BA BA 00 BB BB' + 'BB 00 BC BC BC 00 BD BD BD 00 BE BE BE 00 BF BF' + 'BF 00 C0 C0 C0 00 C1 C1 C1 00 C2 C2 C2 00 C3 C3' + 'C3 00 C4 C4 C4 00 C5 C5 C5 00 C6 C6 C6 00 C7 C7' + 'C7 00 C8 C8 C8 00 C9 C9 C9 00 CB CB CB 00 CC CC' + 'CC 00 CD CD CD 00 D0 D0 D0 00 D1 D1 D1 00 D2 D2' + 'D2 00 D3 D3 D3 00 D4 D4 D4 00 D5 D5 D5 00 D6 D6' + 'D6 00 D7 D7 D7 00 D8 D8 D8 00 D9 D9 D9 00 DA DA' + 'DA 00 DB DB DB 00 DC DC DC 00 DD DD DD 00 DE DE' + 'DE 00 DF DF DF 00 E0 E0 E0 00 E1 E1 E1 00 E2 E2' + 'E2 00 E3 E3 E3 00 E4 E4 E4 00 E5 E5 E5 00 E6 E6' + 'E6 00 E7 E7 E7 00 E8 E8 E8 00 E9 E9 E9 00 EA EA' + 'EA 00 EB EB EB 00 EC EC EC 00 ED ED ED 00 EE EE' + 'EE 00 EF EF EF 00 F0 F0 F0 00 F1 F1 F1 00 F2 F2' + 'F2 00 F3 F3 F3 00 F4 F4 F4 00 F5 F5 F5 00 F6 F6' + 'F6 00 F7 F7 F7 00 F8 F8 F8 00 F9 F9 F9 00 FA FA' + 'FA 00 FB FB FB 00 FC FC FC 00 FD FD FD 00 FE FE' + 'FE 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 F0 FB FF 00 A4 A0 A0 00 80 80' + '80 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 3F 3F 3B 3B 3B 3B 3B' + '3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B' + '3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B 3B' + '3B 3F 3F 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4D 50 46 43 43 43 43 43' + '42 42 42 41 41 41 41 40 40 40 40 40 3E 3E 07 07' + '3C 3B 3B 3B 3B 39 39 38 37 37 36 36 36 34 34 33' + '33 34 35 3E 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4D 68 67 67 67 67 66 66' + '65 65 65 64 64 63 63 62 62 61 61 60 5F 5E 5E 5D' + '5C 5B 5A 59 58 58 57 56 55 54 53 52 51 50 4F 4F' + '4E 4D 36 3E 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 68 68 67 67 67 66 66' + '66 65 65 64 64 64 63 63 62 62 61 60 60 5F 5E 5D' + '5C 5C 5B 5A 59 58 57 56 55 55 54 53 52 51 50 4F' + '4E 4E 35 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 68 68 68 67 67 67 66' + '66 66 65 65 64 64 63 63 63 62 61 61 60 60 5F 5E' + '5D 5C 5B 5B 5A 59 58 57 56 55 54 53 52 52 51 50' + '4F 4F 35 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 68 68 68 68 67 67 67' + '66 66 65 65 65 64 64 63 63 63 62 61 61 60 5F 5F' + '5E 5D 5C 5B 5A 5A 59 58 57 56 55 54 53 52 52 51' + '50 4F 35 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 69 68 68 68 67 67 67' + '67 66 66 65 65 65 64 64 63 63 62 62 61 61 60 5F' + '5F 5E 5D 5C 5B 5A 5A 59 58 57 56 55 54 53 52 52' + '51 50 36 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 69 69 68 68 68 67 67' + '67 67 66 66 65 65 65 64 64 63 63 62 62 61 61 60' + '5F 5F 5E 5D 5C 5B 5A 5A 59 58 57 56 55 54 53 52' + '52 51 37 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 69 69 69 68 68 68 67' + '67 67 67 66 66 65 65 65 64 64 63 63 62 62 61 61' + '60 5F 5F 5E 5D 5C 5B 5A 5A 59 58 57 56 55 54 53' + '53 52 37 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 6A 69 69 69 68 68 68' + '67 67 67 67 66 66 65 65 65 64 64 63 63 62 62 61' + '61 60 5F 5F 5E 5D 5C 5B 5A 59 59 58 57 56 55 54' + '53 53 38 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 6A 6A 69 69 69 68 68' + '68 67 67 67 67 66 66 65 65 65 64 64 63 63 62 62' + '61 61 60 5F 5F 5E 5D 5C 5B 5A 59 59 58 57 56 55' + '54 54 38 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 6B 6A 6A 69 69 69 68' + '68 68 67 67 67 67 66 66 65 65 65 64 64 63 63 62' + '62 61 61 60 5F 5F 5E 5D 5C 5B 5A 59 59 58 57 56' + '55 55 3A 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 6B 6B 6A 6A 69 69 69' + '68 68 68 67 67 67 67 66 66 65 65 65 64 64 63 63' + '62 62 61 61 60 5F 5F 5E 5D 5C 5B 5A 59 59 58 57' + '56 56 3A 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 6B 6B 6B 6A 6A 69 69' + '69 68 68 68 67 67 67 66 66 66 65 65 65 64 64 63' + '63 62 62 61 61 60 5F 5F 5E 5D 5C 5B 5A 59 59 58' + '57 56 3A 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4A 6C 6B 6B 6B 6A 6A 69' + '69 69 68 68 68 67 67 67 66 66 66 65 65 65 64 64' + '63 63 62 62 61 61 60 5F 5F 5E 5D 5C 5B 5A 59 59' + '58 57 3B 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 6C 6C 6B 6B 6B 6A 6A' + '69 69 69 68 68 68 67 67 67 66 66 66 65 65 65 64' + '64 63 63 62 62 61 61 60 5F 5E 5E 5D 5C 5B 5A 59' + '59 58 3B 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 6C 6C 6C 6B 6B 6B 6A' + '6A 69 69 69 68 68 68 67 67 67 66 66 66 65 65 65' + '64 64 63 63 62 62 61 61 60 5F 5E 5E 5D 5C 5B 5A' + '5A 59 3C 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 6D 6C 6C 6C 6B 6B 6B' + '6A 6A 69 69 69 68 68 68 67 67 67 66 66 66 65 65' + '65 64 64 63 63 62 62 61 61 60 5F 5E 5E 5D 5C 5B' + '5A 5A 07 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 6D 6D 6D 6C 6C 6B 6B' + '6B 6A 6A 69 69 69 68 68 68 67 67 67 66 66 66 65' + '65 65 64 64 63 63 62 62 61 61 60 5F 5E 5E 5D 5C' + '5B 5B 07 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 6E 6D 6D 6D 6C 6C 6B' + '6B 6B 6A 6A 69 69 69 68 68 68 67 67 67 66 66 66' + '65 65 65 64 64 63 63 62 62 61 61 60 5F 5E 5E 5D' + '5C 5B 3F 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 6E 6E 6D 6D 6D 6C 6C' + '6B 6B 6B 6A 6A 69 69 69 68 68 68 67 67 67 66 66' + '66 65 65 65 64 64 63 63 62 62 61 61 60 5F 5E 5E' + '5D 5C 3F 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 6E 6E 6E 6D 6D 6D 6C' + '6C 6B 6B 6B 6A 6A 69 69 69 68 68 68 67 67 67 66' + '66 66 65 65 64 64 64 63 63 62 62 61 61 60 5F 5E' + '5E 5D 40 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 6F 6E 6E 6E 6D 6D 6D' + '6C 6C 6B 6B 6B 6A 6A 69 69 69 68 68 68 67 67 67' + '66 66 66 65 65 64 64 64 63 63 62 62 61 61 60 5F' + '5F 5E 40 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 6F 6F 6F 6E 6E 6D 6D' + '6D 6C 6C 6B 6B 6B 6A 6A 69 69 69 68 68 68 67 67' + '67 66 66 66 65 65 64 64 64 63 63 62 62 61 61 60' + '5F 5F 40 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4B 70 6F 6F 6F 6E 6E 6D' + '6D 6D 6C 6C 6B 6B 6B 6A 6A 69 69 69 68 68 68 67' + '67 67 66 66 66 65 65 64 64 64 63 63 62 62 61 61' + '60 60 41 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 70 70 6F 6F 6F 6E 6E' + '6D 6D 6C 6C 6C 6B 6B 6B 6A 6A 69 69 69 68 68 68' + '67 67 67 66 66 66 65 65 64 64 64 63 63 62 62 61' + '61 60 41 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 70 70 70 6F 6F 6F 6E' + '6E 6D 6D 6C 6C 6C 6B 6B 6B 6A 6A 69 69 69 68 68' + '68 67 67 67 66 66 66 65 65 64 64 64 63 63 62 62' + '61 61 41 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 71 71 70 70 6F 6F 6F' + '6E 6E 6D 6D 6C 6C 6C 6B 6B 6B 6A 6A 69 69 69 68' + '68 68 67 67 67 66 66 66 65 65 64 64 64 63 63 62' + '62 62 42 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 71 71 71 70 70 6F 6F' + '6F 6E 6E 6D 6D 6C 6C 6C 6B 6B 6B 6A 6A 69 69 69' + '68 68 68 67 67 67 66 66 66 65 65 64 64 64 63 63' + '62 62 42 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 72 71 71 71 70 70 6F' + '6F 6E 6E 6E 6D 6D 6C 6C 6C 6B 6B 6B 6A 6A 69 69' + '69 68 68 68 67 67 67 66 66 66 65 65 64 64 64 63' + '63 63 43 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 72 72 71 71 71 70 70' + '6F 6F 6E 6E 6E 6D 6D 6C 6C 6C 6B 6B 6B 6A 6A 69' + '69 69 68 68 68 67 67 67 66 66 66 65 65 64 64 64' + '63 63 43 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 72 72 72 71 71 71 70' + '70 6F 6F 6E 6E 6E 6D 6D 6C 6C 6C 6B 6B 6B 6A 6A' + '69 69 69 68 68 68 67 67 67 66 66 66 65 65 64 64' + '64 63 43 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 73 73 72 72 71 71 71' + '70 70 6F 6F 6E 6E 6E 6D 6D 6C 6C 6C 6B 6B 6A 6A' + '6A 69 69 69 68 68 68 67 67 67 66 66 66 65 65 64' + '64 64 43 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 73 73 73 72 72 71 71' + '71 70 70 6F 6F 6E 6E 6E 6D 6D 6C 6C 6C 6B 6B 6A' + '6A 6A 69 69 69 68 68 68 67 67 67 66 66 66 65 65' + '64 64 45 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4C 74 73 73 73 72 72 71' + '71 71 70 70 6F 6F 6E 6E 6E 6D 6D 6C 6C 6C 6B 6B' + '6A 6A 6A 69 69 69 68 68 68 67 67 67 66 66 66 65' + '65 65 45 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4D 74 74 73 73 73 72 72' + '71 71 71 70 70 6F 6F 6E 6E 6E 6D 6D 6C 6C 6C 6B' + '6B 6A 6A 6A 69 69 69 68 68 68 67 67 67 66 66 66' + '65 65 45 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4D 75 74 74 73 73 73 72' + '72 71 71 70 70 70 6F 6F 6E 6E 6E 6D 6D 6C 6C 6C' + '6B 6B 6A 6A 6A 69 69 69 68 68 68 67 67 67 66 66' + '66 65 46 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4D 75 75 74 74 73 73 73' + '72 72 71 71 71 70 70 6F 6F 6E 6E 6E 6D 6D 6C 6C' + '6C 6B 6B 6A 6A 6A 69 69 69 68 68 68 67 67 67 66' + '66 66 46 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4D 76 75 75 74 74 73 73' + '73 72 72 71 71 70 70 70 6F 6F 6E 6E 6E 6D 6D 6C' + '6C 6C 6B 6B 6A 6A 6A 69 69 69 68 68 68 67 67 67' + '66 66 46 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4D 76 76 75 75 74 74 73' + '73 73 72 72 71 71 70 70 70 6F 6F 6E 6E 6E 6D 6D' + '6C 6C 6C 6B 6B 6A 6A 6A 69 69 69 68 68 68 67 67' + '66 65 45 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4D 77 76 76 75 75 74 74' + '73 73 73 72 72 71 71 70 70 70 6F 6F 6E 6E 6E 6D' + '6D 6C 6C 6C 6B 6B 6A 6A 6A 69 69 69 68 68 68 67' + '65 5F 40 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4D 77 77 77 76 75 75 74' + '74 73 73 73 72 72 71 71 70 70 70 6F 6F 6E 6E 6E' + '6D 6D 6C 6C 6C 6B 6B 6A 6A 6A 69 69 68 68 68 67' + '62 55 37 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4E 78 78 77 77 76 75 75' + '74 74 73 73 73 72 72 71 71 70 70 70 6F 6F 6E 6E' + '6E 6D 6D 6C 6C 6C 6B 6B 6A 6A 6A 69 69 68 68 67' + '5B 48 2F 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4E 79 78 78 77 77 76 75' + '75 74 74 73 73 72 72 72 71 71 70 70 70 6F 6F 6E' + '6E 6E 6D 6D 6C 6C 6C 6B 6B 6A 6A 6A 69 69 68 66' + '4E 33 29 39 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4E 79 79 79 78 77 77 76' + '75 75 74 74 73 73 72 72 72 71 71 70 70 70 6F 6F' + '6E 6E 6E 6D 6D 6C 6C 6C 6B 6B 6A 6A 6A 69 68 62' + '3E 1E 29 3B 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4E 79 79 79 79 78 77 77' + '76 75 75 74 74 73 73 72 72 72 71 71 70 70 70 6F' + '6F 6E 6E 6E 6D 6D 6C 6C 6C 6B 6B 6A 6A 6A 68 53' + '24 1E 28 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4E 79 79 79 79 79 78 77' + '77 76 75 75 74 74 73 73 72 72 72 71 71 70 70 70' + '6F 6F 6E 6E 6D 6D 6D 6C 6C 6C 6B 6B 6A 69 5D 38' + '16 2C 22 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4E 79 79 79 79 79 79 78' + '77 77 76 75 75 74 74 73 73 72 72 72 71 71 70 70' + '70 6F 6F 6E 6E 6D 6D 6D 6C 6C 6C 6B 68 5B 3A 17' + '1C 27 26 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4F 79 79 79 79 79 79 79' + '78 77 77 76 75 75 74 74 73 73 72 72 72 71 71 70' + '70 70 6F 6F 6E 6E 6D 6D 6D 6C 6A 62 4D 29 11 15' + '2D 1D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4F 79 79 79 79 79 79 79' + '79 78 77 77 76 75 75 74 74 73 73 72 72 72 71 71' + '70 70 70 6F 6F 6E 6E 6D 69 60 4E 31 13 0C 18 38' + '1E 2D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4F 79 79 79 79 79 79 79' + '79 79 78 77 77 76 75 75 74 74 73 73 72 72 72 71' + '71 70 70 70 6F 6E 67 57 44 25 10 0A 0E 34 3F 24' + '23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4F 79 79 79 79 79 79 79' + '79 79 78 78 77 77 76 75 75 74 74 73 73 72 72 72' + '71 71 70 70 6F 68 47 1A 0D 0B 14 42 4F 46 2D 1F' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4F 79 79 79 79 79 79 79' + '79 79 79 78 78 77 77 76 75 75 74 74 73 73 72 72' + '72 71 71 70 70 63 27 18 42 67 5E 53 49 35 1E 32' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 4F 79 79 79 79 79 79 79' + '79 79 79 79 78 78 77 77 76 75 75 74 74 73 73 72' + '72 72 71 71 70 66 2D 3C 6D 62 56 4B 07 22 27 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 50 79 79 79 79 79 79 79' + '79 79 79 79 79 78 78 77 77 76 75 75 74 74 73 73' + '72 72 72 71 71 6D 45 19 66 5A 4F 45 2B 20 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 50 79 79 79 79 79 79 79' + '79 79 79 79 79 79 78 78 77 76 76 75 75 74 74 73' + '73 72 72 72 71 6F 54 12 55 52 49 32 1E 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 50 79 79 79 79 79 79 79' + '79 79 79 79 79 79 79 78 78 77 76 76 75 75 74 74' + '73 73 72 72 72 71 5E 19 32 4B 07 21 2A 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 50 79 79 79 79 79 79 79' + '79 79 79 79 79 79 79 79 78 78 77 76 76 75 75 74' + '74 73 73 72 72 71 60 1D 1E 43 28 20 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 50 79 79 79 79 79 79 79' + '79 79 79 79 79 79 79 79 79 78 78 77 76 76 75 75' + '74 74 73 72 71 6B 51 1B 21 31 1F 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 53 79 79 79 79 79 79 79' + '79 79 79 79 79 79 79 79 79 79 78 77 77 76 76 75' + '75 74 73 6F 67 55 34 19 2F 21 2E 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 53 79 79 79 79 79 79 79' + '79 79 79 79 79 79 79 79 79 79 78 78 77 77 76 75' + '75 74 72 6D 61 4D 39 38 30 2F 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 52 52 4F 4F 4F 4F 4F' + '4E 4E 4E 4E 4E 4E 4E 4D 4D 4D 4D 4D 4D 4C 4C 4C' + '4B 4B 4B 4A 48 45 45 45 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 2F 2F 2F 2F 2F 2F 2F 2F 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 2F 2F 2F 2F 2F 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' - 'FF FF FF FF 00 00 FF FF FC 3F FF FF 00 00 FF FF' - 'F8 1F FF FF 00 00 FF FF F0 0F FF FF 00 00 FF FF' - 'C0 07 FF FF 00 00 FF FF 80 03 FF FF 00 00 FF FF' - '00 01 FF FF 00 00 FF FC 00 00 FF FF 00 00 FF F8' - '00 00 7F FF 00 00 FF E0 00 00 3F FF 00 00 FF C0' - '00 00 1F FF 00 00 FF 80 00 00 0F FF 00 00 FE 00' - '00 00 0F FF 00 00 FC 00 00 00 03 FF 00 00 F8 00' - '00 00 03 FF 00 00 E0 00 00 00 01 FF 00 00 C0 00' - '00 00 00 FF 00 00 80 00 00 00 00 7F 00 00 00 00' - '00 00 00 3F 00 00 00 00 00 00 00 1F 00 00 00 00' - '00 00 00 07 00 00 00 00 00 00 00 07 00 00 00 00' - '00 00 00 03 00 00 C0 00 00 00 00 00 00 00 E0 00' - '00 00 00 00 00 00 F0 00 00 00 00 00 00 00 F0 00' - '00 00 00 00 00 00 F8 00 00 00 00 00 00 00 FC 00' - '00 00 00 01 00 00 FF 00 00 00 00 03 00 00 FF 00' - '00 00 00 07 00 00 FF C0 00 00 00 0F 00 00 FF E0' - '00 00 00 1F 00 00 FF F0 00 00 00 3F 00 00 FF F8' - '00 00 00 7F 00 00 FF F8 00 00 00 7F 00 00 FF F8' - '00 00 00 FF 00 00 FF F8 00 00 01 FF 00 00 FF FC' - '00 00 03 FF 00 00 FF FE 00 00 07 FF 00 00 FF FF' - '80 00 0F FF 00 00 FF FF 80 00 1F FF 00 00 FF FF' - 'C0 00 3F FF 00 00 FF FF E0 00 7F FF 00 00 FF FF' - 'F8 00 7F FF 00 00 FF FF FC 00 FF FF 00 00 FF FF' - 'FE 01 FF FF 00 00 FF FF FF 07 FF FF 00 00 28 00' - '00 00 30 00 00 00 60 00 00 00 01 00 20 00 00 00' - '00 00 00 24 00 00 00 00 00 00 00 00 00 00 00 00' + 'FF FF FF FF FF FF FF E0 00 00 00 00 07 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 07 FF FF C0 00 00 00 00 07 FF FF C0' + '00 00 00 00 07 FF FF C0 00 00 00 00 0F FF FF C0' + '00 00 00 00 0F FF FF C0 00 00 00 00 1F FF FF C0' + '00 00 00 00 3F FF FF C0 00 00 00 00 3F FF FF C0' + '00 00 00 00 7F FF FF C0 00 00 00 00 FF FF FF C0' + '00 00 00 01 FF FF FF C0 00 00 00 01 FF FF FF C0' + '00 00 00 03 FF FF FF C0 00 00 00 07 FF FF FF C0' + '00 00 00 07 FF FF FF C0 00 00 00 0F FF FF FF E0' + '00 00 00 3F FF FF FF FF FF FF FF FF FF FF 28 00' + '00 00 40 00 00 00 80 00 00 00 01 00 20 00 00 00' + '00 00 00 42 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -2070,501 +2158,1120 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 0E 00 00 00 32 00 00 00 2D 00 00 00 0B 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F 0F' + '0F 13 0F 0F 0F 2F 0F 0F 0F 41 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F 0F 45 0F 0F' + '0F 45 0F 0F 0F 41 0F 0F 0F 2F 0F 0F 0F 13 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 86 86' + '86 4B CA CA CA C7 BE BE BE D4 BB BB BB D6 BB BB' + 'BB D6 BB BB BB D6 BB BB BB D6 BB BB BB D6 BA BA' + 'BA D6 BA BA BA D6 BA BA BA D6 B9 B9 B9 D6 B9 B9' + 'B9 D6 B9 B9 B9 D6 B9 B9 B9 D6 B8 B8 B8 D6 B8 B8' + 'B8 D6 B8 B8 B8 D6 B7 B7 B7 D6 B7 B7 B7 D6 B5 B5' + 'B5 D6 B5 B5 B5 D6 B4 B4 B4 D6 B4 B4 B4 D6 B3 B3' + 'B3 D6 B2 B2 B2 D6 B2 B2 B2 D6 B1 B1 B1 D6 B1 B1' + 'B1 D6 AF AF AF D6 AF AF AF D6 AE AE AE D6 AD AD' + 'AD D6 AD AD AD D6 AC AC AC D6 AC AC AC D6 AB AB' + 'AB D6 A9 A9 A9 D6 A9 A9 A9 D6 A8 A8 A8 D6 A8 A8' + 'A8 D6 A8 A8 A8 D4 93 93 93 A7 0F 0F 0F 2F 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 93 93' + '93 68 EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E4 E4 E4 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2' + 'E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF DF FF DE DE' + 'DE FF DE DE DE FF DD DD DD FF DC DC DC FF DB DB' + 'DB FF DA DA DA FF D9 D9 D9 FF D8 D8 D8 FF D7 D7' + 'D7 FF D6 D6 D6 FF D5 D5 D5 FF D5 D5 D5 FF D4 D4' + 'D4 FF D3 D3 D3 FF AC AC AC D7 0F 0F 0F 42 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 31 00 00' - '00 B1 00 00 00 F6 00 00 00 DF 00 00 00 65 00 00' - '00 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E6 E6 E6 FF E6 E6' + 'E6 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2' + 'E2 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF' + 'DF FF DE DE DE FF DD DD DD FF DC DC DC FF DB DB' + 'DB FF DB DB DB FF DA DA DA FF D9 D9 D9 FF D8 D8' + 'D8 FF D7 D7 D7 FF D6 D6 D6 FF D5 D5 D5 FF D4 D4' + 'D4 FF D4 D4 D4 FF AB AB AB D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E9 E9' + 'E9 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6' + 'E6 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3' + 'E3 FF E2 E2 E2 FF E1 E1 E1 FF E1 E1 E1 FF E0 E0' + 'E0 FF DF DF DF FF DE DE DE FF DD DD DD FF DC DC' + 'DC FF DB DB DB FF DA DA DA FF D9 D9 D9 FF D8 D8' + 'D8 FF D8 D8 D8 FF D7 D7 D7 FF D6 D6 D6 FF D5 D5' + 'D5 FF D5 D5 D5 FF AC AC AC D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 58 00 00 00 F2 00 00' - '00 FF 12 04 00 FF 02 00 00 FF 00 00 00 F6 00 00' - '00 71 00 00 00 08 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B EE EE EE FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E9 E9' + 'E9 FF E9 E9 E9 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7' + 'E7 FF E6 E6 E6 FF E5 E5 E5 FF E5 E5 E5 FF E4 E4' + 'E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0' + 'E0 FF E0 E0 E0 FF DF DF DF FF DE DE DE FF DD DD' + 'DD FF DC DC DC FF DB DB DB FF DA DA DA FF D9 D9' + 'D9 FF D8 D8 D8 FF D8 D8 D8 FF D7 D7 D7 FF D6 D6' + 'D6 FF D5 D5 D5 FF AC AC AC D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7' + 'E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF E5 E5' + 'E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1' + 'E1 FF E0 E0 E0 FF E0 E0 E0 FF DF DF DF FF DE DE' + 'DE FF DD DD DD FF DC DC DC FF DB DB DB FF DA DA' + 'DA FF D9 D9 D9 FF D8 D8 D8 FF D8 D8 D8 FF D7 D7' + 'D7 FF D6 D6 D6 FF AD AD AD D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 12 00 00 00 9D 00 00 00 F6 00 00 00 FF 59 17' - '00 FF 97 2E 01 FF 6E 20 00 FF 06 00 00 FF 00 00' - '00 F0 00 00 00 65 00 00 00 08 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2' + 'E2 FF E1 E1 E1 FF E0 E0 E0 FF E0 E0 E0 FF DF DF' + 'DF FF DE DE DE FF DD DD DD FF DC DC DC FF DB DB' + 'DB FF DA DA DA FF D9 D9 D9 FF D8 D8 D8 FF D8 D8' + 'D8 FF D7 D7 D7 FF AE AE AE D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B EF EF EF FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6' + 'E6 FF E5 E5 E5 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3' + 'E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0 E0 FF E0 E0' + 'E0 FF DF DF DF FF DE DE DE FF DD DD DD FF DC DC' + 'DC FF DB DB DB FF DA DA DA FF D9 D9 D9 FF D9 D9' + 'D9 FF D8 D8 D8 FF AE AE AE D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 3A 00 00' - '00 C2 00 00 00 FF 17 03 00 FF 7A 37 1A FF BE 59' - '2D FF CF 9C 89 FF BF 81 6D FF 5A 1E 08 FF 06 00' - '00 FF 00 00 00 EE 00 00 00 5C 00 00 00 08 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B F0 F0 F0 FF EF EF EF FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E9 E9' + 'E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7' + 'E7 FF E6 E6 E6 FF E5 E5 E5 FF E5 E5 E5 FF E4 E4' + 'E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0' + 'E0 FF DF DF DF FF DF DF DF FF DE DE DE FF DD DD' + 'DD FF DC DC DC FF DB DB DB FF DA DA DA FF D9 D9' + 'D9 FF D9 D9 D9 FF AF AF AF D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7' + 'E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF E5 E5' + 'E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1' + 'E1 FF E0 E0 E0 FF DF DF DF FF DF DF DF FF DE DE' + 'DE FF DD DD DD FF DC DC DC FF DB DB DB FF DA DA' + 'DA FF DA DA DA FF AF AF AF D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 70 00 00 00 F3 00 00' - '00 FF 32 07 00 FF B1 63 41 FF D5 8F 70 FF E3 BF' - 'AF FF FF FF FF FF EA E7 EA FF C0 82 6C FF 64 1B' - '01 FF 00 00 00 FF 00 00 00 EE 00 00 00 5E 00 00' - '00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2' + 'E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF DF FF DF DF' + 'DF FF DE DE DE FF DD DD DD FF DC DC DC FF DB DB' + 'DB FF DB DB DB FF B1 B1 B1 D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6' + 'E6 FF E5 E5 E5 FF E5 E5 E5 FF E4 E4 E4 FF E3 E3' + 'E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF' + 'DF FF DF DF DF FF DE DE DE FF DD DD DD FF DC DC' + 'DC FF DC DC DC FF B2 B2 B2 D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 20 00 00 00 AC 00 00 00 FD 06 00 00 FF 61 1B' - '00 FF D5 9F 86 FF E2 A3 84 FF DB AE 9A FF FD FB' - 'FA FF FF FF FF FF FF FF FF FF E9 E3 E4 FF BD 87' - '77 FF 5A 1A 01 FF 02 00 00 FF 00 00 00 E6 00 00' - '00 52 00 00 00 08 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B F1 F1 F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E9 E9' + 'E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7' + 'E7 FF E6 E6 E6 FF E5 E5 E5 FF E5 E5 E5 FF E4 E4' + 'E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0' + 'E0 FF DF DF DF FF DF DF DF FF DE DE DE FF DD DD' + 'DD FF DC DC DC FF B2 B2 B2 D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8F 8F' + '8F 6B F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7' + 'E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF E5 E5' + 'E5 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1' + 'E1 FF E0 E0 E0 FF DF DF DF FF DF DF DF FF DE DE' + 'DE FF DD DD DD FF B3 B3 B3 D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 2A 00 00' - '00 C4 00 00 00 FF 08 00 00 FF 73 29 0F FF EE BD' - 'A5 FF EB AE 8B FF CA 7F 5C FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF E6 DF' - 'E1 FF BE 7A 63 FF 3D 0E 00 FF 00 00 00 FF 00 00' - '00 E2 00 00 00 3D 00 00 00 01 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E4 E4 E4 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2' + 'E2 FF E1 E1 E1 FF E0 E0 E0 FF DF DF DF FF DF DF' + 'DF FF DE DE DE FF B3 B3 B3 D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 01 00 00 00 6C 00 00 00 E3 00 00' - '00 FF 2F 01 00 FF A7 66 50 FF F5 D0 B8 FF F3 B6' - '8D FF CF 89 67 FF F5 EB E8 FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FD FD' - 'FE FF E4 DA DC FF B6 78 61 FF 44 11 00 FF 00 00' - '00 FF 00 00 00 CB 00 00 00 3C 00 00 00 01 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F2 F2 F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6' + 'E6 FF E5 E5 E5 FF E4 E4 E4 FF E4 E4 E4 FF E3 E3' + 'E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0 E0 FF E0 E0' + 'E0 FF DF DF DF FF B4 B4 B4 D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E9 E9' + 'E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7' + 'E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4 E4 FF E4 E4' + 'E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1 E1 FF E0 E0' + 'E0 FF E0 E0 E0 FF B5 B5 B5 D9 0F 0F 0F 47 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 1C 00 00 00 8D 00 00 00 FF 07 00 00 FF 4D 1A' - '07 FF C9 81 63 FF FF E3 CD FF F7 BC 91 FF C7 71' - '47 FF F1 E5 E0 FF FF FF FF FF FF FF FF FF FF FF' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F3 F3 F3 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7' + 'E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4' + 'E4 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2 E2 FF E1 E1' + 'E1 FF E1 E1 E1 FF B5 B5 B5 D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E4 E4 E4 FF E4 E4 E4 FF E3 E3 E3 FF E2 E2' + 'E2 FF E1 E1 E1 FF B7 B7 B7 D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6' + 'E6 FF E5 E5 E5 FF E4 E4 E4 FF E4 E4 E4 FF E3 E3' + 'E3 FF E2 E2 E2 FF B7 B7 B7 D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F4 F4 F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E9 E9' + 'E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7' + 'E7 FF E6 E6 E6 FF E5 E5 E5 FF E4 E4 E4 FF E4 E4' + 'E4 FF E3 E3 E3 FF B8 B8 B8 D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7' + 'E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5 E5 FF E5 E5' + 'E5 FF E4 E4 E4 FF B8 B8 B8 D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F5 F5 F5 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E5' + 'E5 FF E5 E5 E5 FF B9 B9 B9 D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 92' + '92 6B F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6' + 'E6 FF E6 E6 E6 FF BA BA BA D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E9 E9' + 'E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7' + 'E7 FF E6 E6 E6 FF BA BA BA D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B F6 F6 F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7' + 'E7 FF E7 E7 E7 FF BA BA BA D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F5 F5 F5 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8 E8 FF E8 E8' + 'E8 FF E8 E8 E8 FF BB BB BB D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B F7 F7 F7 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF EA EA EA FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF BB BB BB D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF EA EA EA FF E9 E9 E9 FF E9 E9' + 'E9 FF E9 E9 E9 FF BC BC BC D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EA EA EA FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF BC BC BC D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B F8 F8 F8 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF EA EA EA FF EA EA EA FF EA EA' + 'EA FF E9 E9 E9 FF BC BC BC D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF EB EB EB FF EA EA EA FF EA EA' + 'EA FF EA EA EA FF BC BC BC D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B F9 F9 F9 FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EC EC EC FF EB EB EB FF EB EB EB FF EA EA' + 'EA FF EA EA EA FF BE BE BE D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 6B FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF EC EC EC FF EB EB EB FF EB EB' + 'EB FF EB EB EB FF BE BE BE D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 96' + '96 6B FA FA FA FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' + 'ED FF EC EC EC FF EC EC EC FF EC EC EC FF EB EB' + 'EB FF EB EB EB FF BE BE BE D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 96' + '96 6B FB FB FB FF FA FA FA FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF ED ED' + 'ED FF ED ED ED FF EC EC EC FF EC EC EC FF EC EC' + 'EC FF EB EB EB FF BF BF BF D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 96' + '96 6B FB FB FB FF FB FB FB FF FA FA FA FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF EE EE EE FF ED ED' + 'ED FF ED ED ED FF ED ED ED FF EC EC EC FF EC EC' + 'EC FF EC EC EC FF BF BF BF D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 96' + '96 6B FC FC FC FF FB FB FB FF FB FB FB FF FA FA' + 'FA FF FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EE EE EE FF EE EE' + 'EE FF ED ED ED FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EC EC EC FF BF BF BF D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 96' + '96 6B FC FC FC FF FC FC FC FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6' + 'F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EF EF EF FF EE EE EE FF EE EE' + 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF EC EC' + 'EC FF EB EB EB FF BE BE BE D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 96' + '96 6B FD FD FD FF FC FC FC FF FC FC FC FF FB FB' + 'FB FF FB FB FB FF FA FA FA FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF EB EB' + 'EB FF E5 E5 E5 FF B8 B8 B8 D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 96' + '96 6B FD FD FD FF FD FD FD FF FD FD FD FF FC FC' + 'FC FF FB FB FB FF FB FB FB FF FA FA FA FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' + 'EE FF EE EE EE FF EE EE EE FF ED ED ED FF E8 E8' + 'E8 FF DB DB DB FF AE AE AE D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 99 99' + '99 6B FE FE FE FF FE FE FE FF FD FD FD FF FD FD' + 'FD FF FC FC FC FF FB FB FB FF FB FB FB FF FA FA' + 'FA FF FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF F0 F0 F0 FF F0 F0 F0 FF EF EF EF FF EF EF' + 'EF FF EE EE EE FF EE EE EE FF ED ED ED FF E1 E1' + 'E1 FF CC CC CC FF A5 A5 A5 D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 99 99' + '99 6B FF FF FF FF FE FE FE FF FE FE FE FF FD FD' + 'FD FF FD FD FD FF FC FC FC FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6' + 'F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1' + 'F1 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0 F0 FF EF EF' + 'EF FF EF EF EF FF EE EE EE FF EC EC EC FF D4 D4' + 'D4 FF B6 B6 B6 FF 9D 9D 9D D9 0F 0F 0F 47 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 99 99' + '99 6B FF FF FF FF FF FF FF FF FF FF FF FF FE FE' + 'FE FF FD FD FD FF FD FD FD FF FC FC FC FF FB FB' + 'FB FF FB FB FB FF FA FA FA FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' + 'F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0' + 'F0 FF EF EF EF FF EE EE EE FF E8 E8 E8 FF C1 C1' + 'C1 FF A1 A1 A1 FF 9E 9E 9E D9 0F 0F 0F 45 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 99 99' + '99 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FE FE FE FF FD FD FD FF FD FD FD FF FC FC' + 'FC FF FB FB FB FF FB FB FB FF FA FA FA FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' + 'F0 FF F0 F0 F0 FF EE EE EE FF D9 D9 D9 FF A7 A7' + 'A7 FF A1 A1 A1 FE 99 99 99 D3 0F 0F 0F 40 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 99 99' + '99 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FE FE FE FF FD FD FD FF FD FD' + 'FD FF FC FC FC FF FB FB FB FF FB FB FB FF FA FA' + 'FA FF FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' + 'F3 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2' + 'F2 FF F2 F2 F2 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0' + 'F0 FF EF EF EF FF E3 E3 E3 FF BB BB BB FF 91 91' + '91 FF AE AE AE FD 89 89 89 C3 0F 0F 0F 35 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 99 99' + '99 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FD FD' + 'FD FF FD FD FD FF FC FC FC FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6' + 'F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4 F4 FF F4 F4' + 'F4 FF F3 F3 F3 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1 F1 FF EE EE' + 'EE FF E1 E1 E1 FF BD BD BD FF 92 92 92 FF 9D 9D' + '9D FE A5 A5 A5 F2 72 72 72 9C 0F 0F 0F 23 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9B 9B' + '9B 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FE FE' + 'FE FF FD FD FD FF FD FD FD FF FC FC FC FF FB FB' + 'FB FF FB FB FB FF FA FA FA FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F4 F4' + 'F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3 F3 FF F3 F3' + 'F3 FF F2 F2 F2 FF F0 F0 F0 FF E8 E8 E8 FF D3 D3' + 'D3 FF AC AC AC FF 83 83 83 FF 90 90 90 FE AE AE' + 'AE F9 89 89 89 CE 42 42 42 54 0F 0F 0F 11 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9B 9B' + '9B 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF E5 D8 DA FF B3 77 61 FF 33 0E' - '00 FF 00 00 00 FF 00 00 00 C8 00 00 00 33 00 00' - '00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + 'FF FF FE FE FE FF FD FD FD FF FD FD FD FF FC FC' + 'FC FF FB FB FB FF FB FB FB FF FA FA FA FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' + 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF EF EF' + 'EF FF E6 E6 E6 FF D4 D4 D4 FF B4 B4 B4 FF 88 88' + '88 FF 65 65 65 FF 93 93 93 FF BA BA BA FC 95 95' + '95 E3 5E 5E 5E 7D 0F 0F 0F 1F 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00' - '00 D3 00 00 00 FF 0B 00 00 FF 85 36 19 FF E9 B7' - '9F FF FF E6 CB FF F9 B8 88 FF CE 7B 50 FF F2 EE' - 'EF FF FF C8 98 FF FF D7 B6 FF FF FF FF FF FF FF' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9B 9B' + '9B 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FD FE FF FF E5 DA DB FF BE 7D' - '65 FF 30 0B 00 FF 00 00 00 FF 00 00 00 D2 00 00' - '00 33 00 00 00 01 00 00 00 00 00 00 00 00 00 00' + 'FF FF FF FF FF FF FE FE FE FF FD FD FD FF FD FD' + 'FD FF FC FC FC FF FB FB FB FF FB FB FB FF FA FA' + 'FA FF FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F6 F6 F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF F4 F4 F4 FF ED ED ED FF DD DD DD FF C7 C7' + 'C7 FF A8 A8 A8 FF 82 82 82 FF 5B 5B 5B FF 72 72' + '72 FF B7 B7 B7 FF C2 C2 C2 FE A1 A1 A1 F0 74 74' + '74 A4 19 19 19 30 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 04 00 00 00 72 00 00 00 EA 00 00' - '00 FF 37 0B 00 FF B1 71 56 FF F7 D4 BE FF FF DF' - 'C0 FF FD C3 95 FF D0 80 56 FF E9 D4 CC FF FF D4' - 'AD FF FF 92 39 FF FF 98 44 FF FF D6 B4 FF FF FF' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9B 9B' + '9B 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FE FE FE FF FC FC FB FF F8 F6 F7 FF E4 D9' - 'DA FF B5 80 6B FF 3A 12 02 FF 00 00 00 FF 00 00' - '00 C8 00 00 00 39 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 1F 00 00 00 9F 00 00 00 FF 08 00 00 FF 5F 28' - '11 FF D3 93 74 FF FF EF DE FF FF D9 B5 FF FC C5' - '9B FF D5 83 56 FF E6 CE C4 FF FF EB D2 FF FF 93' - '3A FF FF 94 3D FF FF 93 3B FF FF 95 3E FF FF DD' - 'BF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FD FB FB FF FA F8 F8 FF F8 F6 F6 FF F5 F3' - 'F4 FF E7 DA DA FF B6 82 6C FF 2A 0D 00 FF 00 00' - '00 FF 00 00 00 BF 00 00 00 29 00 00 00 02 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 3D 00 00' - '00 E1 00 00 00 FF 12 00 00 FF 94 3F 15 FF F3 CF' - 'BC FF FF EC D7 FF FF D1 AC FF FF D8 B4 FF DC 91' - '64 FF E0 C0 B3 FF FF E9 D3 FF FF 99 44 FF FF 96' - '40 FF FF 98 44 FF FF 98 43 FF FF 97 42 FF FF 9B' - '46 FF FF D3 AE FF FF FF FF FF FF FF FF FF FE FD' - 'FD FF FC FA FA FF F9 F6 F6 FF F6 F3 F3 FF F4 F0' - 'F0 FF F1 ED EF FF E3 D6 D6 FF BD 77 57 FF 27 08' - '00 FF 00 00 00 FF 00 00 00 C4 00 00 00 28 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 02 00 00 00 60 00 00 00 DD 00 00' - '00 FF 2F 0C 00 FF A1 5E 3C FF F7 D5 C0 FF FF EA' - 'D2 FF FF D4 B0 FF FF DB B8 FF E3 A2 76 FF D6 9E' - '83 FF FF F8 F1 FF FF AB 64 FF FF 95 3D FF FF 9A' - '46 FF FF 99 46 FF FF 99 45 FF FF 9A 47 FF FF 9C' - '49 FF FF A8 5D FF FF E3 CA FF FF FF FF FF FD FC' - 'FD FF FA F8 F8 FF F7 F5 F5 FF F4 F1 F1 FF F2 EE' - 'EE FF F1 EC EC FF EB E6 E8 FF E1 CF CC FF A2 62' - '43 FF 24 0A 00 FF 00 00 00 FF 00 00 00 A1 00 00' - '00 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 12 00 00 00 9E 00 00 00 FF 00 00 00 FF 59 1E' - '00 FF D5 99 7A FF FF F5 E8 FF FF E4 C7 FF FF D9' - 'B8 FF FF E0 C1 FF ED B6 8E FF D7 A1 86 FF FD FA' - 'F6 FF FF AC 66 FF FF 98 41 FF FF 9C 49 FF FF 9C' - '49 FF FF 9C 49 FF FF 9C 48 FF FF 9C 49 FF FF A0' - '51 FF FF A3 54 FF FF A7 59 FF FE DF C3 FF FB FF' - 'FF FF F9 F7 F8 FF F6 F3 F3 FF F4 F0 F0 FF F1 EC' - 'EC FF EF E9 E8 FF EC E6 E6 FF E9 E3 E5 FF E4 D0' - 'CA FF AE 6B 48 FF 16 02 00 FF 00 00 00 FF 00 00' - '00 B0 00 00 00 1C 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 48 00 00' - '00 C8 00 00 00 FF 21 04 00 FF 8E 4B 25 FF EF C7' - 'B0 FF FF F0 DE FF FF E3 C9 FF FF DE C0 FF FF E8' - 'CC FF EF BF 99 FF D2 93 71 FF F9 F0 E9 FF FF BF' - '86 FF FF 98 43 FF FF 9D 4C FF FF 9E 4C FF FF 9D' - '4B FF FF 9D 4B FF FF 9D 4B FF FF 9E 4B FF FF A2' - '53 FF FF A7 59 FF FF AA 5E FF FF B0 6A FF FD D7' - 'B5 FF F7 F8 FC FF F5 F3 F3 FF F2 ED ED FF F0 EA' - 'EA FF ED E7 E7 FF EA E3 E3 FF E9 E1 E1 FF E5 DE' - 'E1 FF E0 CA C3 FF B0 6B 44 FF 23 09 00 FF 00 00' - '00 FE 00 00 00 B1 00 00 00 24 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 4C 00 00 00 EB 00 00' - '00 FF 37 11 00 FF B5 72 4C FF FC DD C8 FF FF F8' - 'E8 FF FF E4 C8 FF FF E3 C8 FF FF E9 CF FF F2 CA' - 'A9 FF D4 92 69 FF F8 EC E3 FF FF C4 8E FF FF 98' - '42 FF FF 9F 4F FF FF A0 4F FF FF A0 4E FF FF 9F' - '4E FF FF 9F 4E FF FF 9F 4D FF FF 9F 4E FF FF A3' - '54 FF FF A8 5C FF FF AD 64 FF FF B0 67 FF FF B4' - '6F FF FA D9 BC FF F4 F0 F1 FF F1 ED EE FF EF E9' - 'E9 FF EC E5 E5 FF E9 E2 E2 FF E7 DE DE FF E5 DC' - 'DC FF E2 DA DD FF E3 CC C3 FF A4 5F 37 FF 25 0B' - '00 FF 00 00 00 FF 00 00 00 A1 00 00 00 25 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 EB 02 00 00 FF 6A 2F' - '0C FF E4 AC 8B FF FF FA EF FF FF F1 DC FF FF E7' - 'CE FF FF E8 D0 FF FF EC D5 FF FC E3 C8 FF D2 8B' - '5F FF F0 D2 BD FF FF C9 96 FF FF 9E 4A FF FF A1' - '51 FF FF A2 52 FF FF A1 51 FF FF A1 51 FF FF A1' - '51 FF FF A1 51 FF FF A1 50 FF FF A1 50 FF FF A5' - '57 FF FF AA 5E FF FF AE 66 FF FF B3 6D FF FF B7' - '72 FF FF B9 74 FF F8 D6 B7 FF EF EC EF FF ED E8' - 'E9 FF EB E4 E4 FF E8 E0 E0 FF E6 DD DD FF E3 DA' - 'D9 FF E0 D6 D6 FF DE D5 D8 FF E2 C7 BD FF AF 63' - '36 FF 16 03 00 FF 00 00 00 FF 00 00 00 B0 00 00' - '00 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 ED 06 01 00 FF 7A 3C' - '16 FF F7 CA AA FF FF F4 E0 FF FF E9 D2 FF FF E9' - 'D2 FF FF ED D7 FF FA E1 C7 FF D1 87 58 FF EF C6' - 'A8 FF FF CD 9E FF FF A0 4D FF FF A2 53 FF FF A3' - '54 FF FF A3 54 FF FF A3 54 FF FF A3 53 FF FF A3' - '53 FF FF A3 53 FF FF A3 53 FF FF A3 53 FF FF A7' - '59 FF FF AC 60 FF FF B0 68 FF FF B4 6F FF FF B9' - '76 FF FF BD 7C FF FF C1 81 FF F9 D4 B1 FF EC E6' - 'E8 FF E9 E3 E3 FF E7 DE DE FF E4 DB DB FF E2 D7' - 'D7 FF DF D4 D4 FF DD D1 D0 FF DB D2 D5 FF DF C3' - 'B8 FF A9 5E 30 FF 1D 08 00 FF 00 00 00 FE 00 00' - '00 B1 00 00 00 26 00 00 00 01 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 7D 00 00 00 FF 08 01' - '00 FF 96 46 13 FF FB CF AC FF FF F4 E1 FF FF EB' - 'D6 FF FF F1 DE FF DF A6 7E FF E2 A8 7F FF FF CF' - 'A1 FF FF A2 52 FF FF A2 52 FF FF A3 54 FF FF A3' - '54 FF FF A3 54 FF FF A3 54 FF FF A3 54 FF FF A3' - '54 FF FF A3 54 FF FF A3 54 FF FF A3 54 FF FF A7' - '5A FF FF AC 62 FF FF B1 69 FF FF B5 71 FF FF BA' - '78 FF FF BF 7F FF FF C3 85 FF FF C6 89 FF F5 D7' - 'B9 FF E8 E1 E3 FF E5 DD DE FF E4 DA DA FF E1 D6' - 'D6 FF DD D3 D3 FF DB CF CF FF D8 CB CB FF D9 CF' - 'D3 FF E6 C9 BA FF 95 50 23 FF 0C 00 00 FF 00 00' - '00 FF 00 00 00 91 00 00 00 17 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 01 00 00 00 7C 00 00' - '00 FF 0B 03 00 FF 89 49 1D FF F7 C4 9A FF FF FC' - 'EC FF DF A8 7F FF DE 98 67 FF FF CC 9D FF FF A8' - '5C FF FF A2 52 FF FF A4 56 FF FF A4 55 FF FF A4' - '55 FF FF A4 55 FF FF A4 55 FF FF A4 55 FF FF A3' - '55 FF FF A3 55 FF FF A3 54 FF FF A3 54 FF FF A7' - '5B FF FF AC 62 FF FF B0 69 FF FF B5 71 FF FF BA' - '78 FF FF BF 80 FF FF C4 87 FF FF C8 8C FF FF CC' - '91 FF F4 D7 B9 FF E4 DB DB FF E1 D8 D9 FF DF D5' - 'D5 FF DD D1 D1 FF DB CE CE FF D8 CA CA FF D4 C6' - 'C6 FF D5 CA CE FF E5 C3 B1 FF 9D 5D 34 FF 0A 00' - '00 FF 00 00 00 FA 00 00 00 99 00 00 00 13 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 76 00 00 00 FC 09 00 00 FF 87 4B 20 FF E7 AE' - '83 FF DF 95 60 FF FD C1 8C FF FF A9 5F FF FF A2' - '53 FF FF A4 57 FF FF A4 57 FF FF A4 57 FF FF A4' - '56 FF FF A4 56 FF FF A4 56 FF FF A4 56 FF FF A4' - '56 FF FF A4 56 FF FF A4 55 FF FF A3 55 FF FF A7' - '5B FF FF AC 62 FF FF B1 6A FF FF B5 71 FF FF BA' - '78 FF FF BE 7E FF FF C4 86 FF FF CA 92 FF FF D2' - '9E FF FF D9 A9 FF F4 DE C6 FF E0 D6 D7 FF DD D2' - 'D3 FF DB CF CF FF D9 CC CC FF D6 C8 C8 FF D3 C4' - 'C4 FF D0 BF BF FF D6 CC D0 FF E6 C5 B3 FF 96 54' - '29 FF 11 05 00 FF 00 00 00 F9 00 00 00 82 00 00' - '00 16 00 00 00 01 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 5F 00 00 00 FB 0D 03 00 FF B1 50' - '0F FF FD AB 6A FF FF B3 6D FF FF A3 55 FF FF A5' - '58 FF FF A5 58 FF FF A5 58 FF FF A4 58 FF FF A4' - '58 FF FF A4 58 FF FF A4 58 FF FF A4 57 FF FF A4' - '57 FF FF A4 57 FF FF A4 57 FF FF A4 57 FF FF A8' - '5C FF FF AC 63 FF FF B0 69 FF FF B5 70 FF FF BA' - '7A FF FF C3 88 FF FF CF 9D FF FF D7 AB FF FF DB' - 'B4 FF FF E0 BA FF FF E3 BE FF F4 E0 CA FF DD D1' - 'D1 FF D9 CD CE FF D8 CA CA FF D5 C6 C6 FF D2 C3' - 'C3 FF D0 C0 C0 FF CC BA BA FF D0 C3 C6 FF EA CC' - 'BD FF 97 55 26 FF 0D 02 00 FF 00 00 00 FF 00 00' - '00 7D 00 00 00 0E 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 9E 00 00 00 FF 26 12' - '04 FF B2 5F 23 FF FB 98 48 FF FF AA 5F FF FF A6' - '5A FF FF A6 59 FF FF A6 59 FF FF A6 59 FF FF A6' - '59 FF FF A5 59 FF FF A5 59 FF FF A5 59 FF FF A5' - '58 FF FF A5 58 FF FF A5 58 FF FF A5 58 FF FF A7' - '5B FF FF AC 62 FF FF B5 72 FF FF C2 89 FF FF CB' - '99 FF FF D2 A6 FF FF D7 AD FF FF DA B2 FF FF DD' - 'B8 FF FF E1 BD FF FF E4 C3 FF FF E9 C8 FF F4 E3' - 'CE FF DB CD CC FF D5 C7 C8 FF D4 C5 C5 FF D1 C2' - 'C2 FF CF BE BE FF CC BA BA FF C9 B5 B6 FF CC BD' - 'C0 FF E2 BE AD FF A2 65 3A FF 0E 05 00 FF 00 00' - '00 EC 00 00 00 30 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 14 00 00 00 AC 00 00' - '00 FF 27 12 03 FF A7 53 15 FF F9 93 40 FF FF A7' - '5B FF FF A7 5C FF FF A6 5B FF FF A6 5B FF FF A6' - '5B FF FF A6 5B FF FF A6 5B FF FF A6 5A FF FF A6' - '5A FF FF A6 5A FF FF A5 58 FF FF A5 58 FF FF AE' - '69 FF FF BB 7F FF FF C9 97 FF FF CF A2 FF FF D2' - 'A7 FF FF D5 AC FF FF D9 B1 FF FF DC B8 FF FF E0' - 'BD FF FF E3 C2 FF FF E7 C8 FF FF EA CD FF FF EF' - 'D2 FF F4 E5 D3 FF D7 C9 C8 FF D1 C2 C2 FF D0 C0' - 'C0 FF CE BD BD FF CB B9 B9 FF C8 B5 B5 FF C3 B0' - 'B0 FF D6 C7 CA FF BF 94 78 FF 18 09 00 FF 00 00' - '00 F4 00 00 00 2C 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00' - '00 A6 00 00 00 FF 11 08 02 FF A1 4F 12 FF FA 93' - '3F FF FF AC 61 FF FF A7 5D FF FF A7 5C FF FF A6' - '5C FF FF A6 5C FF FF A6 5C FF FF A7 5B FF FF A6' - '5B FF FF A4 57 FF FF A9 60 FF FF BA 7F FF FF C5' - '94 FF FF CB 9C FF FF CE A1 FF FF D1 A6 FF FF D4' - 'AC FF FF D8 B1 FF FF DC B7 FF FF DF BD FF FF E3' - 'C2 FF FF E5 C7 FF FF E8 CC FF FF EC D1 FF FF EE' - 'D6 FF FF F3 DB FF F6 EB D9 FF D3 C3 C3 FF CD BC' - 'BC FF CD BB BB FF CA B7 B7 FF C6 B3 B4 FF CB B9' - 'BC FF BD 92 7B FF 31 19 0B FF 00 00 00 FF 00 00' - '00 8D 00 00 00 03 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 05 00 00 00 A6 00 00 00 FF 16 0B 03 FF A6 54' - '16 FF FA 94 41 FF FF AB 61 FF FF A9 5F FF FF A8' - '5D FF FF A8 5E FF FF A8 5D FF FF A6 5B FF FF A7' - '5D FF FF B2 71 FF FF C2 8E FF FF C8 9A FF FF C9' - '9B FF FF CD A0 FF FF D0 A6 FF FF D4 AC FF FF D7' - 'B1 FF FF DB B7 FF FF DE BC FF FF E1 C1 FF FF E4' - 'C6 FF FF E7 CB FF FF EA D0 FF FF ED D4 FF FF EF' - 'D9 FF FF F2 DD FF FF F8 E3 FF EE E5 D9 FF D1 C1' - 'C0 FF CA B7 B8 FF C8 B5 B5 FF CA B7 B8 FF CB A8' - '9A FF 46 2D 1D FF 00 00 00 FF 00 00 00 C8 00 00' - '00 1F 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 87 00 00 00 FF 0D 06' - '02 FF 92 4B 14 FF FD 96 41 FF FF AB 63 FF FF A9' - '60 FF FF A8 5F FF FF A6 5B FF FF AB 64 FF FF C1' - '8C FF FF CB A0 FF FF CC A0 FF FF CB 9F FF FF CD' - 'A2 FF FF D1 A8 FF FF D4 AD FF FF D7 B2 FF FF DA' - 'B8 FF FF DE BD FF FF E1 C2 FF FF E4 C7 FF FF E7' - 'CB FF FF E9 D0 FF FF EC D4 FF FF EF D8 FF FF F1' - 'DD FF FF F4 E1 FF FF F7 E5 FF FF FD EC FF F2 EA' - 'E0 FF CA B8 B6 FF C8 B6 B9 FF CD AD A1 FF 57 35' - '1B FF 00 00 00 FF 00 00 00 D6 00 00 00 18 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 05 00 00 00 75 00 00' - '00 FD 0C 06 02 FF 7E 42 11 FF F3 91 3F FF FF AC' - '63 FF FF A7 5D FF FF B5 77 FF FF C8 9A FF FF CF' - 'A7 FF FF CE A5 FF FF CF A6 FF FF CF A7 FF FF D1' - 'AA FF FF D4 AF FF FF D8 B4 FF FF DA B9 FF FF DD' - 'BE FF FF E0 C2 FF FF E3 C7 FF FF E6 CC FF FF E9' - 'D0 FF FF EB D4 FF FF EE D8 FF FF F0 DC FF FF F2' - 'E0 FF FF F5 E4 FF FF F8 E9 FF FF F9 EB FF FD F3' - 'E0 FF F3 EC E4 FF DB BD B0 FF 68 46 30 FF 00 00' - '00 FF 00 00 00 D1 00 00 00 32 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 78 00 00 00 F1 00 00 00 FF 7F 43 12 FF EF 90' - '40 FF FF BC 80 FF FF D0 A8 FF FF D3 AD FF FF D2' - 'AC FF FF D2 AD FF FF D3 AD FF FF D3 AE FF FF D5' - 'B1 FF FF D8 B5 FF FF DB BA FF FF DD BF FF FF E0' - 'C4 FF FF E3 C8 FF FF E6 CC FF FF E8 D1 FF FF EB' - 'D5 FF FF ED D8 FF FF EF DC FF FF F2 E0 FF FF F4' - 'E3 FF FF F7 E8 FF FE F5 E6 FF FC EF DC FF FD F7' - 'EA FF FF F0 DA FF 83 5C 3B FF 00 00 00 FF 00 00' - '00 EB 00 00 00 3F 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 54 00 00 00 FD 00 00 00 FF 66 3A' - '14 FF F4 B8 85 FF FF D9 B8 FF FF D6 B3 FF FF D5' - 'B3 FF FF D6 B4 FF FF D6 B5 FF FF D7 B5 FF FF D9' - 'B8 FF FF DC BC FF FF DF C1 FF FF E1 C5 FF FF E4' - 'C9 FF FF E7 CE FF FF E8 D1 FF FF EB D5 FF FF EE' - 'D9 FF FF F0 DD FF FF F1 E0 FF FF F4 E4 FF FF F6' - 'E8 FF FE F2 E1 FF FB EB D7 FF FF FD F6 FF FF F6' - 'E4 FF 9C 66 38 FF 03 00 00 FF 00 00 00 F5 00 00' - '00 46 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + 'FF FF FF FF FF FF FE FE FE FF FE FE FE FF FD FD' + 'FD FF FD FD FD FF FC FC FC FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5' + 'F5 FF EE EE EE FF CB CB CB FF 97 97 97 FF 6B 6B' + '6B FF 5D 5D 5D FF 8B 8B 8B FF C5 C5 C5 FF D5 D5' + 'D5 FF C9 C9 C9 FE AD AD AD F7 86 86 86 C5 37 37' + '37 4B 0F 0F 0F 10 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 45 00 00 00 E0 01 00' - '00 FF 78 47 1B FF F2 B5 7F FF FF DC BE FF FF DA' - 'BB FF FF DA BB FF FF DB BC FF FF DB BD FF FF DC' - 'BF FF FF DF C3 FF FF E2 C7 FF FF E4 CB FF FF E6' - 'CF FF FF E9 D4 FF FF EB D6 FF FF ED DA FF FF F0' - 'DE FF FF F2 E1 FF FF F3 E5 FF FF F4 E6 FF FC EE' - 'DB FF FD F1 E0 FF FF FC F5 FF FF F5 E3 FF A7 7A' - '51 FF 07 00 00 FF 00 00 00 F3 00 00 00 60 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9B 9B' + '9B 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FE FE' + 'FE FF FD FD FD FF FD FD FD FF FC FC FC FF FB FB' + 'FB FF FB FB FB FF FA FA FA FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F6 F6' + 'F6 FF E9 E9 E9 FF AA AA AA FF 93 93 93 FF C5 C5' + 'C5 FF ED ED ED FF E4 E4 E4 FF D9 D9 D9 FF CD CD' + 'CD FE B7 B7 B7 FC 92 92 92 DD 56 56 56 6F 0F 0F' + '0F 1A 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 8B 00 00' - '00 FF 69 42 1C FF ED AC 6F FF EE BC 90 FF FF E2' - 'C7 FF FF DE C2 FF FF DE C2 FF FF DF C4 FF FF E0' - 'C5 FF FF E2 C9 FF FF E5 CD FF FF E7 D1 FF FF E9' - 'D4 FF FF EC D8 FF FF EE DB FF FF F0 DF FF FF F1' - 'E2 FF FF F4 E6 FF FE F3 E5 FF FC EB D9 FF FD F2' - 'E2 FF FF FC F4 FF FF FF FE FF CB 9A 6C FF 1A 09' - '00 FF 00 00 00 FF 00 00 00 7D 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9B 9B' + '9B 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FE FE' + 'FE FF FE FE FE FF FD FD FD FF FD FD FD FF FC FC' + 'FC FF FB FB FB FF FB FB FB FF FA FA FA FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' + 'F6 FF EC EC EC FF B0 B0 B0 FF BF BF BF FF F3 F3' + 'F3 FF E8 E8 E8 FF DC DC DC FF D1 D1 D1 FF C0 C0' + 'C0 FE 9E 9E 9E EC 6F 6F 6F 97 15 15 15 29 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 9C 02 00' - '00 FF AA 6B 2D FF FF FA EA FF F1 D6 BF FF EC B8' - '88 FF FF E1 C9 FF FF E3 CB FF FF E2 CA FF FF E3' - 'CB FF FF E5 CF FF FF E8 D3 FF FF EA D6 FF FF EC' - 'D9 FF FF ED DD FF FF EF E0 FF FF F2 E3 FF FF F4' - 'E6 FF FD F0 E0 FF FD EF DE FF FE F4 E8 FF FF FB' - 'F4 FF FF FF F7 FF DC B0 84 FF 29 17 09 FF 00 00' - '00 FF 00 00 00 94 00 00 00 0B 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9D 9D' + '9D 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FE FE FE FF FE FE FE FF FD FD FD FF FD FD' + 'FD FF FC FC FC FF FB FB FB FF FB FB FB FF FA FA' + 'FA FF FA FA FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8' + 'F8 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7' + 'F7 FF F3 F3 F3 FF C8 C8 C8 FF 96 96 96 FF EC EC' + 'EC FF E0 E0 E0 FF D5 D5 D5 FF C8 C8 C8 FE AB AB' + 'AB F5 81 81 81 BB 2E 2E 2E 41 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 94 01 00' - '00 FF 51 30 0F FF DF B0 7F FF FF FB F0 FF EF D0' - 'B3 FF F0 C5 9D FF FD E4 CD FF FF E6 D1 FF FF E7' - 'D2 FF FF E8 D5 FF FF EB D9 FF FF ED DC FF FF EF' - 'DF FF FF F0 E2 FF FF F2 E5 FF FF F3 E6 FF FE F0' - 'E1 FF FE F0 E1 FF FE F6 ED FF FF FA F3 FF FF FF' - 'FC FF E5 C3 A0 FF 3B 20 06 FF 00 00 00 FF 00 00' - '00 B0 00 00 00 07 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9D 9D' + '9D 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FE FE FE FF FE FE FE FF FD FD' + 'FD FF FC FC FC FF FC FC FC FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF FA FA FA FF F9 F9 F9 FF F9 F9' + 'F9 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8 F8 FF F7 F7' + 'F7 FF F5 F5 F5 FF DA DA DA FF 85 85 85 FF DB DB' + 'DB FF D8 D8 D8 FF CD CD CD FE B4 B4 B4 FB 8F 8F' + '8F D6 4D 4D 4D 62 0F 0F 0F 17 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 1F 00 00' - '00 CC 00 00 00 FF 35 1B 00 FF D4 99 5A FF FF FF' - 'FE FF EC C3 9B FF F4 CE AA FF FF ED DC FF FF EA' - 'D8 FF FF EC DB FF FF EE DF FF FF F0 E1 FF FF F1' - 'E4 FF FF F3 E7 FF FE F2 E6 FF FE EF E0 FF FE F3' - 'E7 FF FF F8 F0 FF FF FA F3 FF FF FF FF FF F3 CD' - 'A6 FF 52 2F 0D FF 00 00 00 FF 00 00 00 B8 00 00' - '00 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9D 9D' + '9D 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FE FE' + 'FE FF FD FD FD FF FC FC FC FF FC FC FC FF FB FB' + 'FB FF FB FB FB FF FA FA FA FF FA FA FA FF F9 F9' + 'F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF E4 E4 E4 FF 96 96 96 FF B5 B5' + 'B5 FF D1 D1 D1 FF BF BF BF FD 9B 9B 9B E8 67 67' + '67 8A 0F 0F 0F 24 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 1A 00 00 00 AE 00 00 00 FF 23 13 01 FF BF 8D' - '56 FF F3 B6 77 FF F6 D4 B6 FF FF F0 E3 FF FF ED' - 'DF FF FF EF E1 FF FF F1 E4 FF FF F3 E7 FF FF F3' - 'E9 FF FE F2 E5 FF FE F2 E6 FF FF F6 ED FF FF F9' - 'F2 FF FF F9 F4 FF FF FF FF FF FB DE BE FF 55 37' - '1A FF 00 00 00 FF 00 00 00 CB 00 00 00 1E 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9D 9D' + '9D 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FE FE' + 'FE FF FE FE FE FF FD FD FD FF FC FC FC FF FC FC' + 'FC FF FB FB FB FF FB FB FB FF FA FA FA FF FA FA' + 'FA FF F9 F9 F9 FF F9 F9 F9 FF F8 F8 F8 FF F8 F8' + 'F8 FF F7 F7 F7 FF E6 E6 E6 FF A0 A0 A0 FF A1 A1' + 'A1 FF C6 C6 C6 FE A7 A7 A7 F3 7B 7B 7B B1 25 25' + '25 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 04 00 00 00 A1 00 00 00 FF 11 07' - '00 FF B5 72 2D FF FF EB CF FF FF F4 EA FF FF F1' - 'E5 FF FF F2 E7 FF FF F4 EA FF FF F4 EB FF FE F3' - 'E9 FF FE F4 EA FF FF F8 F2 FF FF FA F5 FF FF FB' - 'F6 FF FF FF FF FF F7 E5 D0 FF 76 4D 25 FF 00 00' - '00 FF 00 00 00 DB 00 00 00 25 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9D 9D' + '9D 6B FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FE FE FE FF FE FE FE FF FD FD FD FF FC FC' + 'FC FF FC FC FC FF FB FB FB FF FB FB FB FF FA FA' + 'FA FF FA FA FA FF F9 F9 F9 FF F8 F8 F8 FF F7 F7' + 'F7 FF F1 F1 F1 FF D7 D7 D7 FF 9A 9A 9A FF A4 A4' + 'A4 FE B2 B2 B2 F9 8C 8C 8C CE 40 40 40 57 0F 0F' + '0F 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00' - '00 FF 23 14 04 FF C7 99 68 FF FF FF F2 FF FF F8' - 'F1 FF FF F5 ED FF FE F6 ED FF FE F4 EC FF FE F8' - 'F1 FF FF FA F6 FF FF FB F6 FF FF FC F8 FF FF FF' - 'FF FF FF F4 DE FF 8C 61 34 FF 00 00 00 FF 00 00' - '00 EA 00 00 00 3B 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 A2 A2' + 'A2 68 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FE FE FE FF FD FD FD FF FD FD' + 'FD FF FC FC FC FF FC FC FC FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF F9 F9 F9 FF F5 F5 F5 FF ED ED' + 'ED FF DB DB DB FF B7 B7 B7 FF 96 96 96 FE B1 B1' + 'B1 FC 98 98 98 E2 5F 5F 5F 7C 0F 0F 0F 1F 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00' - '00 BB 00 00 00 FF 28 17 05 FF C1 9C 74 FF FF F6' - 'E7 FF FF FB F8 FF FE F8 F1 FF FF FA F6 FF FF FC' - 'F8 FF FF FC FA FF FF FD FB FF FF FF FF FF FF FD' - 'F5 FF AA 82 56 FF 0E 05 00 FF 00 00 00 F9 00 00' - '00 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 B5 B5' + 'B5 5C FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FE FE FE FF FE FE FE FF FD FD' + 'FD FF FD FD FD FF FC FC FC FF FB FB FB FF FB FB' + 'FB FF FA FA FA FF F8 F8 F8 FF F3 F3 F3 FF E7 E7' + 'E7 FF D3 D3 D3 FF BC BC BC FF BA BA BA FD AB AB' + 'AB E7 7E 7E 7E 99 1A 1A 1A 2C 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 7D 7D' + '7D 1E AE AE AE 58 9B 9B 9B 64 97 97 97 67 97 97' + '97 67 97 97 97 67 97 97 97 67 97 97 97 67 94 94' + '94 67 94 94 94 67 94 94 94 67 94 94 94 67 94 94' + '94 67 94 94 94 67 94 94 94 67 92 92 92 67 92 92' + '92 67 92 92 92 67 92 92 92 67 92 92 92 67 92 92' + '92 67 8F 8F 8F 67 8F 8F 8F 67 8F 8F 8F 67 8C 8C' + '8C 66 8C 8C 8C 66 8C 8C 8C 66 89 89 89 66 80 80' + '80 67 76 76 76 67 75 75 75 66 71 71 71 62 62 62' + '62 4F 36 36 36 27 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 03 FF FF C0' + '00 00 00 00 03 FF FF C0 00 00 00 00 07 FF FF C0' + '00 00 00 00 0F FF FF C0 00 00 00 00 0F FF FF C0' + '00 00 00 00 1F FF FF C0 00 00 00 00 3F FF FF C0' + '00 00 00 00 7F FF FF C0 00 00 00 00 7F FF FF C0' + '00 00 00 00 FF FF FF C0 00 00 00 01 FF FF FF C0' + '00 00 00 01 FF FF FF C0 00 00 00 03 FF FF FF C0' + '00 00 00 07 FF FF FF C0 00 00 00 0F FF FF FF FF' + 'FF FF FF FF FF FF' +} */ + + +/* BINRES folder.ico */ +3 ICON folder.ico + /* { + '00 00 01 00 0C 00 10 10 10 00 01 00 04 00 28 01' + '00 00 C6 00 00 00 10 10 00 00 01 00 08 00 68 05' + '00 00 EE 01 00 00 10 10 00 00 01 00 20 00 68 04' + '00 00 56 07 00 00 20 20 10 00 01 00 04 00 E8 02' + '00 00 BE 0B 00 00 20 20 00 00 01 00 08 00 A8 08' + '00 00 A6 0E 00 00 20 20 00 00 01 00 20 00 A8 10' + '00 00 4E 17 00 00 30 30 10 00 01 00 04 00 68 06' + '00 00 F6 27 00 00 30 30 00 00 01 00 08 00 A8 0E' + '00 00 5E 2E 00 00 30 30 00 00 01 00 20 00 A8 25' + '00 00 06 3D 00 00 40 40 10 00 01 00 04 00 68 0A' + '00 00 AE 62 00 00 40 40 00 00 01 00 08 00 28 16' + '00 00 16 6D 00 00 40 40 00 00 01 00 20 00 28 42' + '00 00 3E 83 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 04 00 00 00 00 00 80 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 78' + '78 78 78 78 78 00 F8 FF FF FF FF F8 88 70 FF F4' + 'F4 F4 FF FF 88 70 FF 4F 4F 4F 4F FF 88 70 F8 FF' + 'FF FF FF F8 88 70 F8 88 88 88 88 88 88 70 F8 88' + '88 88 88 88 88 70 F8 88 88 88 88 88 88 70 F8 88' + '88 88 88 88 88 70 86 66 66 66 87 87 87 00 0E FE' + 'FE FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + '00 00 FF FF 00 00 80 03 00 00 00 01 00 00 00 01' + '00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01' + '00 00 00 01 00 00 00 01 00 00 00 03 00 00 80 FF' + '00 00 FF FF 00 00 FF FF 00 00 FF FF 00 00 28 00' + '00 00 10 00 00 00 20 00 00 00 01 00 08 00 00 00' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 C0 DC C0 00 F0 CA A6 00 75 72' + '6D 00 7A 75 6C 00 6F 70 70 00 74 74 75 00 7B 79' + '7B 00 7A 7B 7B 00 7D 7E 7E 00 8A 84 78 00 B7 97' + '7C 00 BD A5 77 00 FF 00 FF 00 82 81 85 00 84 84' + '84 00 8A 8A 8A 00 8C 8B 8C 00 8C 8C 8C 00 8D 8D' + '8D 00 8E 8E 8E 00 8F 8F 8F 00 90 90 90 00 90 90' + '91 00 92 92 92 00 93 93 93 00 96 96 96 00 97 97' + '97 00 98 98 98 00 99 99 99 00 9A 9A 9A 00 9A 9A' + '9E 00 9B 9C 9C 00 9C 9C 9C 00 9C 9D 9E 00 9E 9E' + '9E 00 9F 9F 9F 00 A0 99 8C 00 BD 9D 85 00 A0 A0' + 'A0 00 A1 A1 A1 00 A2 A3 A3 00 A3 A3 A3 00 A2 A3' + 'A4 00 A3 A4 A5 00 A5 A5 A5 00 A4 A5 A7 00 A6 A6' + 'A6 00 A7 A7 A7 00 A6 A7 A9 00 A7 A8 AA 00 A9 A9' + 'A9 00 A8 A9 AA 00 A9 A9 AA 00 AB AB AB 00 AA AB' + 'AC 00 AC AC AC 00 AC AE AF 00 AE AE AE 00 AF AF' + 'AF 00 AC AF B0 00 B1 B1 B1 00 B2 B2 B2 00 B3 B3' + 'B3 00 B0 B2 B4 00 B1 B3 B5 00 B3 B5 B6 00 B5 B7' + 'B7 00 B6 B6 B6 00 B7 B7 B7 00 B6 B7 B9 00 B7 B9' + 'BA 00 BA BA BA 00 BB BC BB 00 BA BC BC 00 BB BC' + 'BE 00 BC BC BC 00 BE BE BE 00 C7 B2 89 00 DF CC' + 'A6 00 D1 C5 B0 00 E4 D0 BE 00 F2 DE B8 00 FB E6' + 'BF 00 BE BF C0 00 C1 C1 C1 00 C1 C3 C5 00 C4 C4' + 'C4 00 C4 C6 C7 00 DA CA C2 00 DC D9 D2 00 D8 D8' + 'D9 00 DF DF DF 00 E3 D4 C6 00 FE EE C7 00 E1 E1' + 'E1 00 E4 E4 E4 00 F1 E9 E1 00 F0 F1 F1 00 F2 F2' + 'F2 00 F4 F4 F5 00 F5 F5 F6 00 F5 F6 F6 00 F6 F6' + 'F7 00 F6 F7 F8 00 F7 F7 F8 00 F6 F8 F9 00 F7 F8' + 'F9 00 F8 F8 F9 00 F9 FA FA 00 FB FC FD 00 FC FC' + 'FD 00 FF FF FD 00 FD FE FF 00 FF FF FF 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 04 00 00 00 A8 00 00 00 FF 06 00 00 FF AB 84' - '58 FF FF FA EB FF FF FF FF FF FF FE FD FF FF FF' - 'FD FF FF FF FF FF FF FF FF FF FF FF FC FF C9 9B' - '66 FF 0F 05 00 FF 00 00 00 FF 00 00 00 68 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -2573,10 +3280,6 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 0A 00 00 00 74 00 00 00 FF 0E 05' - '00 FF 79 5C 3A FF FF ED D6 FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FE FF C9 A5 7A FF 19 0D' - '00 FF 00 00 00 FF 00 00 00 82 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -2585,10 +3288,6 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 67 00 00' - '00 EE 00 00 00 FF 6E 4E 29 FF F0 DD C5 FF FF FF' - 'F4 FF FF F1 DC FF D1 AC 80 FF 36 1D 02 FF 00 00' - '00 FF 00 00 00 8F 00 00 00 03 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -2598,9 +3297,842 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 3F 00 00 00 E0 00 00 00 FF 48 2F 12 FF 87 5D' - '2D FF 53 38 19 FF 12 0A 00 FF 00 00 00 FF 00 00' - '00 AD 00 00 00 08 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 FB' + 'FF 00 A4 A0 A0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 4C 4C 4C 4A 48 40 3E 38 3B' + '35 32 3C 42 00 00 71 76 79 79 78 75 74 73 6E 70' + '6C 6B 29 1E 44 00 79 2D 60 12 60 68 12 60 2D 79' + '79 79 51 0C 28 00 79 79 58 79 64 79 79 64 79 79' + '79 77 5B 0D 2B 00 69 6F 74 73 72 72 72 72 72 6D' + '6B 6A 30 0F 34 00 62 5F 5D 52 4D 4E 49 47 43 39' + '33 29 10 16 3F 00 63 53 45 3D 31 2E 2E 23 22 1F' + '1C 1A 19 17 4C 00 66 5C 4B 41 37 34 2F 2B 28 25' + '21 1F 1F 18 50 00 67 5E 4F 46 3A 36 31 2E 2A 28' + '24 21 20 1D 54 00 61 2C 11 0B 0A 0E 15 26 27 25' + '24 21 1F 1B 00 00 00 65 5A 59 56 55 13 57 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF 00 00 FF FF 00 00 80 03' + '00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01' + '00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01' + '00 00 00 03 00 00 80 FF 00 00 FF FF 00 00 FF FF' + '00 00 FF FF 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 20 00 00 00 00 00 40 04 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C1 C1' + 'C1 84 B0 B0 B0 92 B0 B0 B0 92 B0 B0 B0 92 AC B0' + 'B0 92 A5 A9 AC 92 9D A0 A2 92 99 9B 9D 92 92 94' + '97 92 96 97 99 92 8F 90 94 92 8B 8D 8F 92 97 97' + '99 91 A2 A2 A2 90 A1 A1 A1 91 81 81 81 45 F6 F8' + 'F9 FE FC FC FD FF FF FF FF FF FF FF FF FF FD FE' + 'FF FF FB FC FD FF F9 FA FA FF F8 F8 F9 FF F6 F6' + 'F7 FF F7 F7 F8 FF F5 F5 F6 FF F4 F4 F5 FF 9C 9D' + '9E FF 90 90 91 FF B1 B1 B1 FB 94 94 94 7D FF FF' + 'FF FF BD 9D 85 FF DA CA C2 FF B7 97 7C FF DA CA' + 'C2 FF F1 E9 E1 FF B7 97 7C FF DA CA C2 FF BD 9D' + '85 FF FF FF FF FF FF FF FF FF FF FF FF FF BA BC' + 'BC FF 6F 70 70 FF 9B 9B 9B FB 82 82 82 81 FF FF' + 'FF FF FF FF FF FF E4 D0 BE FF FF FF FF FF E3 D4' + 'C6 FF FF FF FF FF FF FF FF FF E3 D4 C6 FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FD FF BE BF' + 'C0 FF 74 74 75 FF 9F 9F 9F FC 84 84 84 85 F0 F1' + 'F1 FF F6 F7 F8 FF F9 FA FA FF F8 F8 F9 FF F7 F8' + 'F9 FF F7 F8 F9 FF F7 F8 F9 FF F7 F8 F9 FF F7 F8' + 'F9 FF F5 F6 F6 FF F4 F4 F5 FF F2 F2 F2 FF A2 A3' + 'A3 FF 7A 7B 7B FF A5 A5 A5 FC 88 88 88 88 D8 D8' + 'D9 FF C4 C6 C7 FF C1 C3 C5 FF BB BC BE FF B6 B7' + 'B9 FF B7 B9 BA FF B3 B5 B6 FF B0 B2 B4 FF AC AF' + 'B0 FF A7 A8 AA FF A3 A4 A5 FF 9C 9D 9E FF 7D 7E' + '7E FF 84 84 84 FF AC AC AC FD 8F 8F 8F 8B DF DF' + 'DF FF BC BC BC FF B2 B2 B2 FF AB AB AB FF A3 A3' + 'A3 FF A0 A0 A0 FF A0 A0 A0 FF 98 98 98 FF 97 97' + '97 FF 92 92 92 FF 8F 8F 8F FF 8D 8D 8D FF 8C 8C' + '8C FF 8A 8A 8A FF B7 B7 B7 FD 95 95 95 8E E1 E1' + 'E1 FF C1 C1 C1 FF B6 B6 B6 FF AE AE AE FF A7 A7' + 'A7 FF A5 A5 A5 FF A1 A1 A1 FF 9F 9F 9F FF 9C 9C' + '9C FF 9A 9A 9A FF 96 96 96 FF 92 92 92 FF 92 92' + '92 FF 8C 8B 8C FF BB BC BB FD 98 98 98 90 E4 E4' + 'E4 FF C4 C4 C4 FF BA BA BA FF B3 B3 B3 FF A9 A9' + 'A9 FF A6 A6 A6 FF A3 A3 A3 FF A0 A0 A0 FF 9E 9E' + '9E FF 9C 9C 9C FF 99 99 99 FF 96 96 96 FF 93 93' + '93 FF 90 90 90 FF BE BE BE FD 99 99 99 92 DC D9' + 'D2 FF A0 99 8C FF 8A 84 78 FF 7A 75 6C FF 75 72' + '6D FF 7B 79 7B FF 82 81 85 FF 9A 9A 9E FF 9B 9C' + '9C FF 9A 9A 9A FF 99 99 99 FF 96 96 96 FF 92 92' + '92 FF 8E 8E 8E FF C1 C1 C1 FE 9D 9D 9D 93 F8 F7' + 'E9 7F FE EE C7 FF FB E6 BF FF F2 DE B8 FF DF CC' + 'A6 FF C7 B2 89 FF BD A5 77 FF D1 C5 B0 FE E9 E9' + 'E9 40 E9 E9 E9 40 E8 E8 E8 40 E8 E8 E8 40 E7 E7' + 'E7 40 E7 E7 E7 40 EE EE EE 3F B3 B3 B3 1E 00 00' + '00 00 FE EE C7 40 FB E6 BF 40 F2 DE B8 40 DF CC' + 'A6 40 C7 B2 89 40 BD A5 77 40 C4 B0 87 2C 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + '00 00 FF FF 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 80 FF 00 00 FF FF 00 00 FF FF 00 00 28 00' + '00 00 20 00 00 00 40 00 00 00 01 00 04 00 00 00' + '00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 08 77 77 77 77 77 77 77 77 77' + '77 77 77 77 70 00 0F 88 88 88 88 87 88 78 78 78' + '78 78 78 78 78 00 0F 88 FF FF FF FF FF FF FF FF' + 'FF F8 88 88 87 00 0F 8F 4F F4 44 44 44 4F FF F4' + '4F FF 88 88 87 00 0F 8F F4 4F F4 F4 FF F4 4F 4F' + 'FF FF 88 88 87 00 0F 8F F4 F4 4F 4F FF 4F F4 4F' + 'FF FF 88 88 87 00 0F 8F F4 4F FF 44 4F F4 4F FF' + 'FF FF 88 88 87 00 0F 88 FF FF FF FF FF FF FF FF' + 'FF F8 88 88 87 00 0F 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 0F 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 0F 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 0F 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 0F 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 0F 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 0F 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 0F 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 0F 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 00 00 F6 66 66 66 66 66 88 88 88' + '88 88 88 88 80 00 00 FE 6E 6E 6E 6E 6E 80 00 00' + '00 00 00 00 00 00 00 0F EF EF EF EF EF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 80 00' + '00 07 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 C0 00 00 07 C0 01 FF FF E0 03 FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 20 00 00 00 40 00' + '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 C0 C0 C0 00 C0 DC' + 'C0 00 F0 CA A6 00 73 73 73 00 76 76 76 00 79 79' + '79 00 7A 7A 7A 00 7C 7C 7C 00 7E 7E 7E 00 7F 7F' + '7F 00 90 67 4E 00 92 79 6A 00 9C 8A 66 00 A9 86' + '70 00 A8 9A 7E 00 B4 91 78 00 FF 00 FF 00 80 80' + '80 00 82 82 82 00 84 84 84 00 86 86 86 00 87 87' + '87 00 87 88 87 00 88 88 88 00 8A 8A 8A 00 8B 8B' + '8B 00 8C 8C 8C 00 8D 8D 8D 00 8E 8E 8E 00 8F 8F' + '8F 00 9F 83 82 00 96 92 8B 00 93 90 8C 00 95 92' + '8D 00 98 94 8A 00 98 94 8E 00 9C 97 8C 00 90 90' + '90 00 92 92 92 00 93 93 93 00 94 94 94 00 95 95' + '95 00 95 96 95 00 96 96 96 00 97 97 97 00 98 98' + '98 00 99 99 99 00 9A 9A 9A 00 9B 9A 9A 00 9B 9B' + '9B 00 9C 9C 9C 00 9D 9D 9D 00 9E 9E 9E 00 9F 9F' + '9E 00 9E 9F 9F 00 9F 9F 9F 00 AD 9F 83 00 BD 9E' + '8F 00 A2 9C 92 00 B2 A5 89 00 BF A0 88 00 AA A0' + '96 00 A9 A3 97 00 BD A7 94 00 BB AE 94 00 B1 AB' + '9F 00 A0 A0 A0 00 A1 A1 A1 00 A2 A2 A1 00 A2 A2' + 'A2 00 A4 A4 A4 00 A5 A5 A5 00 A6 A6 A6 00 A7 A7' + 'A6 00 A7 A7 A7 00 A8 A8 A8 00 AA AA AA 00 AC AB' + 'AA 00 AC AC AC 00 AE AE AE 00 B5 AE A6 00 B7 B1' + 'A6 00 B8 B2 A6 00 B1 B0 AF 00 AF AF B0 00 B1 B1' + 'B1 00 B2 B2 B2 00 B3 B4 B4 00 B4 B4 B4 00 B5 B5' + 'B5 00 B5 B6 B6 00 B6 B6 B6 00 B8 B8 B8 00 B8 B9' + 'BA 00 BA BA BA 00 BB BB BB 00 BB BC BC 00 BC BC' + 'BC 00 BC BD BE 00 BF BF BE 00 BF BF BF 00 C0 9F' + '80 00 C3 A4 8F 00 C3 A7 92 00 C5 AA 93 00 C5 A9' + '99 00 C6 AB 9E 00 C8 B0 9C 00 C5 B9 9F 00 C9 AD' + 'A7 00 C2 AA A8 00 CC B5 A2 00 C9 B0 A4 00 CC B7' + 'A4 00 CE B7 A4 00 CD B8 A5 00 CC BF A5 00 C6 B2' + 'AD 00 C9 B4 AE 00 CE B9 A8 00 CE BA A8 00 CE BA' + 'AB 00 D1 B8 A2 00 D0 B8 A6 00 D1 BA A7 00 D0 BC' + 'A7 00 D2 BE AB 00 D0 C3 AA 00 D3 C0 AE 00 D5 C8' + 'AE 00 D3 C2 B2 00 D5 C3 B2 00 D1 C0 B5 00 D6 C7' + 'B7 00 D7 CA B1 00 DA CC B3 00 DD CE B4 00 D7 C7' + 'B8 00 D5 C5 BD 00 D8 C6 BB 00 DB C9 BA 00 DC CA' + 'B9 00 D9 CB BC 00 DB C9 BE 00 EB DF BE 00 C0 C0' + 'C0 00 C2 C2 C1 00 C0 C1 C2 00 C2 C2 C2 00 C3 C3' + 'C3 00 C4 C3 C2 00 C3 C4 C5 00 C4 C4 C4 00 C5 C6' + 'C6 00 C6 C6 C6 00 C7 C7 C7 00 C8 C8 C7 00 C8 C8' + 'C8 00 C9 C9 C9 00 C9 C9 CA 00 CA CA CA 00 CB CB' + 'CB 00 CC CC CC 00 CC CD CC 00 CD CD CD 00 CE CE' + 'CE 00 DF D0 C1 00 D0 D0 D0 00 D1 D2 D2 00 D2 D2' + 'D2 00 D4 D4 D4 00 D7 D6 D5 00 D5 D6 D6 00 D6 D6' + 'D6 00 D8 D5 D0 00 D8 D8 D8 00 DA DA DA 00 DE DE' + 'DE 00 DF DF DF 00 E1 D2 C7 00 E5 D6 C9 00 E5 D7' + 'CC 00 E4 D6 CF 00 E5 DA D0 00 E7 DB D0 00 E8 D9' + 'D3 00 EE E5 C6 00 F2 E8 CB 00 F5 EC CE 00 ED E2' + 'D6 00 E2 E1 DF 00 EC E1 D9 00 F7 EE D5 00 FA F2' + 'DA 00 E0 E0 E0 00 E1 E1 E1 00 E2 E1 E1 00 E4 E3' + 'E3 00 E7 E5 E3 00 E8 E6 E4 00 EA E7 E5 00 EA E8' + 'E5 00 EC E8 E6 00 ED EA E7 00 EA EA EA 00 ED EA' + 'E8 00 EE EB E8 00 F1 EA E2 00 F2 EA E2 00 F6 ED' + 'E6 00 F6 EE E7 00 F6 EE E8 00 F5 F0 E4 00 FD F5' + 'E0 00 FD F8 E6 00 F6 F0 E9 00 F6 F0 EA 00 F1 F1' + 'EF 00 F7 F1 EC 00 F9 F0 EA 00 F8 F1 EC 00 F8 F2' + 'EC 00 F9 F2 EE 00 FB F4 EE 00 FD FA E8 00 FD FD' + 'ED 00 F9 F3 F0 00 F9 F4 F0 00 FA F5 F0 00 FA F6' + 'F1 00 F9 F5 F2 00 FA F5 F2 00 F9 F6 F2 00 FA F6' + 'F2 00 FA F6 F4 00 FD FD F3 00 FC F9 F5 00 FE FC' + 'F7 00 FC FB F9 00 F0 FB FF 00 A4 A0 A0 00 80 80' + '80 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07' + '4C 1F 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E' + '1E 1E 1E 1E 1E 1E 1F 20 20 1A 23 00 00 00 00 6B' + 'D3 B3 AC A6 A0 9E 9A 69 67 64 5E 5E 56 5B 53 53' + '53 52 4E 3B 3A 39 39 3B 36 31 32 49 00 00 00 6B' + 'B3 07 CF D5 D4 D4 D4 D2 D1 D2 D1 D0 D0 D0 D1 CF' + 'CF CE CE CD B2 2C 0E 0E 0D 0A 1B 38 00 00 00 6B' + 'B7 E0 77 81 81 94 78 7F 8C 8A 7F AD D6 DF E2 C6' + '87 7A 90 80 BB B8 18 0F 0F 0B 19 36 00 00 00 07' + 'CB DE 7D C0 44 57 8B 79 92 BE 93 EA 80 6F 70 96' + '71 D6 F5 F1 E4 F0 1D 10 0E 0B 19 35 00 00 00 6B' + 'CC E9 EF 12 F4 74 75 6D 76 43 89 EB 16 85 84 6C' + '7C BA BF DF DF F1 23 18 0F 0B 19 33 00 00 00 AB' + 'AC F0 C4 14 82 EE DE D8 72 11 40 EB 83 46 6E 25' + 'F0 DA DE D9 D9 E6 1F 19 18 0C 19 30 00 00 00 B6' + '9C C5 EB BD 91 E2 E2 DF DF E6 E4 E3 E4 95 BC D7' + 'E2 E3 E2 DE F3 9D 1F 19 19 0C 1B 2E 00 00 00 B7' + '99 66 AF B7 B6 B3 B1 B3 B0 B0 AE AE AB AC AB A8' + 'A7 A4 A1 A2 62 21 20 1E 19 0F 1F 2C 00 00 00 B9' + '99 68 63 62 5D 56 55 52 52 4F 4D 4A 4A 49 3A 35' + '34 30 2F 2E 2D 23 21 1E 1B 0E 21 2C 00 00 00 B8' + '07 99 68 65 62 5C 56 53 51 4D 4F 4D 4A 39 3B 36' + '34 30 30 2D 2D 23 22 20 1F 10 2D 23 00 00 00 C9' + '9B 9F 68 63 5F 5C 56 54 4F 4F 4F 4C 4A 3B 3A 39' + '35 35 2F 2F 2D 2C 23 1F 1E 19 2F 23 00 00 00 B8' + '9B A1 99 66 63 5D 56 53 52 4F 4D 49 4A 3E 3B 39' + '38 34 34 2F 2E 2E 2D 20 20 1A 30 22 00 00 00 B8' + 'A0 A1 9C 69 63 60 5C 55 53 52 4F 4C 4B 3E 3A 3B' + '39 36 34 34 30 2F 2C 2D 21 1C 32 21 00 00 00 B9' + 'A2 A8 9F 07 68 62 5F 55 52 4F 4E 4D 4A 4A 49 3B' + '3A 39 37 34 32 32 2D 2C 23 1B 33 20 00 00 00 CA' + 'A5 A9 A1 9C 6B 63 5C 56 54 4F 4D 4C 4C 3B 3E 3B' + '3A 3A 39 34 34 30 2F 2D 21 1C 34 20 00 00 00 B8' + 'A8 AA A3 99 6A 65 5F 5A 54 50 4D 4B 3C 49 3E 3A' + '3B 3A 38 38 34 32 2E 24 23 1B 35 1F 00 00 00 00' + 'B5 59 58 48 45 41 2B 29 26 2A 28 27 37 3D 38 39' + '35 36 35 33 30 2D 24 21 1C 10 60 00 00 00 00 00' + 'DB 8F 8E 8D 88 86 7B 73 47 42 3F 15 13 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 F2 E8 E7 DD DC C8 C7 C3 C2 C1 97 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 80 00 00 07 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 C0 00 00 07 C0 01' + 'FF FF E0 03 FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 28 00' + '00 00 20 00 00 00 40 00 00 00 01 00 20 00 00 00' + '00 00 80 10 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 27 00 00 00 49 00 00 00 4A 00 00 00 4A 00 00' + '00 4A 00 00 00 4A 00 00 00 4A 00 00 00 4A 00 00' + '00 4A 00 00 00 4A 00 00 00 4A 00 00 00 4A 00 00' + '00 4A 00 00 00 4A 00 00 00 4A 00 00 00 4A 00 00' + '00 4A 00 00 00 4A 00 00 00 4A 00 00 00 4A 00 00' + '00 4A 00 00 00 4A 00 00 00 48 00 00 00 47 00 00' + '00 47 00 00 00 50 00 00 00 43 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 EB EB' + 'EB F8 D5 D6 D6 FF CE CF CF FF C9 C9 CA FF C5 C6' + 'C6 FF C3 C4 C5 FF C0 C1 C2 FF BC BD BE FF BB BC' + 'BC FF B8 B9 BA FF B3 B4 B4 FF B2 B2 B3 FF AE AF' + 'AF FF AF AF B0 FF AA AA AB FF AA AA AA FF AB AB' + 'AB FF A8 A8 A8 FF A4 A4 A4 FF 9E 9E 9E FF 9D 9D' + '9D FF 9C 9C 9C FF 9C 9C 9C FF 9E 9E 9E FF 9A 9A' + '9A FF 96 96 96 FF 90 90 90 E0 00 00 00 2B 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D5 D5' + 'D5 FF C1 C1 C2 FF E9 E7 E5 FF EE EB E8 FF ED EB' + 'E8 FF ED EA E7 FF ED EA E8 FF ED E9 E7 FF EC E9' + 'E7 FF EC E9 E6 FF EC E8 E6 FF EB E8 E5 FF EA E8' + 'E5 FF EB E8 E5 FF EB E8 E5 FF EA E7 E5 FF E9 E7' + 'E5 FF E9 E6 E4 FF E8 E6 E4 FF E7 E5 E3 FF D7 D6' + 'D5 FF 90 90 90 FF 7C 7C 7C FF 7C 7C 7C FF 7A 7A' + '7A FF 73 73 73 FF 7F 7F 7F E6 00 00 00 30 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DA DA' + 'DA FF F1 F1 EF FF C9 B0 A4 FF D1 B8 A2 FF D1 B8' + 'A2 FF DC CA B9 FF CC B7 A4 FF CE BB A8 FF D6 C7' + 'B7 FF D5 C3 B2 FF CE B9 A8 FF DF D0 C1 FF F1 EA' + 'E2 FF F7 F0 EA FF F7 F0 EB FF EC E1 D9 FF D3 C0' + 'AE FF CD B8 A5 FF D7 C7 B8 FF CE BA A9 FF E5 D6' + 'C9 FF DF DF DF FF 80 80 80 FF 7E 7E 7E FF 7D 7D' + '7D FF 76 76 76 FF 7D 7D 7D E8 00 00 00 32 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E2 E1' + 'E1 FF F6 F0 E9 FF C9 B4 AE FF E8 D9 D3 FF AA A0' + '96 FF B5 AE A6 FF D1 C0 B5 FF CE B7 A4 FF D8 C6' + 'BB FF E5 DA D0 FF DB C9 BA FF F9 F4 F0 FF CE BA' + 'AB FF C5 AA 93 FF C5 A9 99 FF DB C9 BE FF C6 AB' + '9E FF F1 EB E2 FF FC FB F9 FF FA F6 F4 FF F8 F2' + 'ED FF FA F6 F2 FF 87 88 87 FF 7E 7E 7E FF 7C 7C' + '7C FF 76 76 76 FF 7C 7C 7C EA 00 00 00 34 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E4 E3' + 'E3 FF F9 F3 F0 FF F9 F6 F2 FF 92 79 6A FF FE FC' + 'F7 FF C9 AD A7 FF C2 AA A8 FF C3 A4 8F FF CC B5' + 'A2 FF BF A0 88 FF D3 C2 B2 FF FA F5 F0 FF B4 91' + '78 FF D2 BE AB FF D0 BC A7 FF C0 9F 80 FF C6 B2' + 'AD FF E1 D2 C7 FF E7 DB D0 FF F7 F0 EA FF F6 F0' + 'EA FF FB F7 F4 FF 8D 8D 8D FF 80 80 80 FF 7E 7E' + '7E FF 76 76 76 FF 7E 7E 7E EC 00 00 00 38 00 00' + '00 00 00 00 00 00 00 00 00 00 F0 F0 F0 47 CF CF' + 'CF FF F9 F5 F2 FF ED E2 D6 FF A9 86 70 FF D0 B8' + 'A6 FF FA F5 F2 FF F7 EF E8 FF F6 ED E6 FF C8 B0' + '9C FF 90 67 4E FF BD 9E 8F FF FA F6 F1 FF D1 BA' + 'A7 FF BD A7 94 FF C3 A7 92 FF 9F 83 82 FF FB F7' + 'F1 FF F6 EE E8 FF F7 EF E8 FF F7 EF E8 FF F6 EE' + 'E7 FF F9 F2 EE FF 8A 8A 8A FF 83 83 83 FF 80 80' + '80 FF 77 77 77 FF 80 80 80 EE 00 00 00 3A 00 00' + '00 00 00 00 00 00 00 00 00 00 FA FA FA 6F C2 C2' + 'C2 FF E2 E1 DF FF FB F5 F0 FF E4 D6 CF FF D5 C5' + 'BD FF F9 F0 EA FF F7 F1 EA FF F7 F1 EA FF F7 F0' + 'EA FF FB F4 EE FF F7 F1 EC FF F8 F1 EC FF F8 F2' + 'EC FF D9 CB BC FF E5 D7 CC FF F2 EA E2 FF F9 F1' + 'EB FF F7 F0 EA FF F7 F0 EA FF F7 F0 EA FF FC F9' + 'F5 FF C4 C3 C2 FF 89 89 89 FF 83 83 83 FF 81 81' + '81 FF 79 79 79 FF 83 83 83 EF 00 00 00 3C 00 00' + '00 00 00 00 00 00 00 00 00 00 F6 F6 F6 79 C1 C1' + 'C1 FF BC BC BC FF D1 D2 D2 FF D9 D9 D9 FF D8 D8' + 'D8 FF D6 D6 D6 FF D5 D5 D5 FF D4 D4 D4 FF D3 D3' + 'D3 FF D2 D2 D2 FF D0 D0 D0 FF CF CF CF FF CE CE' + 'CE FF CE CE CE FF CD CD CD FF CB CB CB FF CA CA' + 'CA FF C8 C8 C8 FF C6 C6 C6 FF C7 C7 C7 FF B5 B6' + 'B6 FF 8C 8C 8C FF 8B 8B 8B FF 88 88 88 FF 83 83' + '83 FF 7D 7D 7D FF 87 87 87 F1 00 00 00 3E 00 00' + '00 00 00 00 00 00 00 00 00 00 FB FB FB 84 C1 C1' + 'C1 FF BD BD BD FF B8 B8 B8 FF B6 B6 B6 FF B2 B2' + 'B2 FF AE AE AE FF AB AB AB FF A9 A9 A9 FF A8 A8' + 'A8 FF A6 A6 A6 FF A3 A3 A3 FF A2 A2 A2 FF A1 A1' + 'A1 FF A0 A0 A0 FF 9D 9D 9D FF 99 99 99 FF 98 98' + '98 FF 95 95 95 FF 94 94 94 FF 93 93 93 FF 92 92' + '92 FF 8E 8E 8E FF 8C 8C 8C FF 88 88 88 FF 85 85' + '85 FF 7D 7D 7D FF 89 89 89 F3 00 00 00 40 00 00' + '00 00 00 00 00 00 00 00 00 00 F9 F9 F9 88 C0 C0' + 'C0 FF C1 C1 C1 FF BD BD BD FF BA BA BA FF B6 B6' + 'B6 FF B1 B1 B1 FF AE AE AE FF AA AA AA FF A7 A7' + 'A7 FF A4 A4 A4 FF A6 A6 A6 FF A4 A4 A4 FF A1 A1' + 'A1 FF 9C 9C 9C FF 9E 9E 9E FF 9A 9A 9A FF 98 98' + '98 FF 95 95 95 FF 95 95 95 FF 92 92 92 FF 91 91' + '91 FF 8F 8F 8F FF 8D 8D 8D FF 8B 8B 8B FF 89 89' + '89 FF 80 80 80 FF 8F 8F 8F F4 00 00 00 41 00 00' + '00 00 00 00 00 00 00 00 00 00 FB FB FB 89 C2 C2' + 'C2 FF C4 C4 C4 FF BC BC BC FF B8 B8 B8 FF B4 B4' + 'B4 FF B1 B1 B1 FF AE AE AE FF AB AB AB FF A6 A6' + 'A6 FF A6 A6 A6 FF A6 A6 A6 FF A2 A2 A2 FF A1 A1' + 'A1 FF 9E 9E 9E FF 9D 9D 9D FF 9C 9C 9C FF 99 99' + '99 FF 99 99 99 FF 94 94 94 FF 94 94 94 FF 92 92' + '92 FF 90 90 90 FF 8E 8E 8E FF 8A 8A 8A FF 89 89' + '89 FF 82 82 82 FF 91 91 91 F5 00 00 00 43 00 00' + '00 00 00 00 00 00 00 00 00 00 F6 F6 F6 8F C2 C2' + 'C2 FF C6 C6 C6 FF C1 C1 C1 FF BC BC BC FF B8 B8' + 'B8 FF B2 B2 B2 FF AE AE AE FF AA AA AA FF A8 A8' + 'A8 FF A6 A6 A6 FF A3 A3 A3 FF A1 A1 A1 FF A1 A1' + 'A1 FF 9F 9F 9F FF 9E 9E 9E FF 9C 9C 9C FF 9B 9B' + '9B FF 98 98 98 FF 98 98 98 FF 94 94 94 FF 93 93' + '93 FF 93 93 93 FF 91 91 91 FF 8C 8C 8C FF 8A 8A' + '8A FF 84 84 84 FF 93 94 93 F6 00 00 00 44 00 00' + '00 00 00 00 00 00 00 00 00 00 F1 F1 F1 9C C5 C5' + 'C5 FF C7 C7 C7 FF C3 C3 C3 FF BD BD BD FF B9 B9' + 'B9 FF B4 B4 B4 FF B1 B1 B1 FF AC AC AC FF AA AA' + 'AA FF A8 A8 A8 FF A6 A6 A6 FF A3 A3 A3 FF A1 A1' + 'A1 FF 9F 9F 9F FF 9D 9D 9D FF 9E 9E 9E FF 9C 9C' + '9C FF 9A 9A 9A FF 98 98 98 FF 98 98 98 FF 95 95' + '95 FF 94 94 94 FF 91 91 91 FF 91 91 91 FF 8C 8C' + '8C FF 87 87 87 FF 95 95 95 F7 00 00 00 45 00 00' + '00 00 00 00 00 00 00 00 00 00 EF EF EF A8 C7 C7' + 'C7 FF CB CB CB FF C4 C4 C4 FF C0 C0 C0 FF BB BB' + 'BB FF B7 B7 B7 FF B3 B3 B3 FF AD AD AD FF A8 A8' + 'A8 FF A6 A6 A6 FF A5 A5 A5 FF A3 A3 A3 FF A2 A2' + 'A2 FF A1 A1 A1 FF A0 A0 A0 FF 9E 9E 9E FF 9D 9D' + '9D FF 9C 9C 9C FF 9A 9A 9A FF 98 98 98 FF 96 96' + '96 FF 96 96 96 FF 92 92 92 FF 91 91 91 FF 8D 8D' + '8D FF 86 86 86 FF 96 96 96 F8 00 00 00 47 00 00' + '00 00 00 00 00 00 00 00 00 00 EF EF EF B4 C9 C9' + 'C9 FF CC CC CC FF C6 C6 C6 FF C3 C3 C3 FF BF BF' + 'BF FF B7 B7 B7 FF B2 B2 B2 FF AE AE AE FF AB AB' + 'AB FF A6 A6 A6 FF A4 A4 A4 FF A2 A2 A2 FF A2 A2' + 'A2 FF 9E 9E 9E FF 9F 9F 9F FF 9E 9E 9E FF 9D 9D' + '9D FF 9D 9D 9D FF 9C 9C 9C FF 98 98 98 FF 98 98' + '98 FF 95 95 95 FF 94 94 94 FF 91 91 91 FF 8D 8D' + '8D FF 87 87 87 FF 97 97 97 F9 00 00 00 47 00 00' + '00 00 00 00 00 00 00 00 00 00 ED ED ED AB CB CB' + 'CB FF CC CD CC FF C8 C8 C7 FF C2 C2 C1 FF BF BF' + 'BE FF BA BA BA FF B4 B4 B4 FF B1 B0 AF FF AC AB' + 'AA FF A7 A7 A6 FF A4 A4 A4 FF A2 A2 A1 FF 9F 9F' + '9E FF A0 A0 A0 FF 9F 9F 9F FF 9D 9D 9D FF 9E 9E' + '9E FF 9D 9D 9D FF 9B 9B 9B FF 9B 9B 9B FF 98 98' + '98 FF 96 96 96 FF 93 93 93 FF 8F 8F 8F FF 8E 8E' + '8E FF 86 86 86 FF 99 99 99 FC 00 00 00 48 00 00' + '00 00 00 00 00 00 00 00 00 00 BE BE B9 3B D8 D5' + 'D0 FF B8 B2 A6 FF B7 B1 A6 FF B1 AB 9F FF A9 A3' + '97 FF A2 9C 92 FF 9C 97 8C FF 98 94 8A FF 96 92' + '8B FF 98 94 8E FF 95 92 8D FF 93 90 8C FF 9B 9A' + '9A FF 9E 9F 9F FF 9B 9B 9B FF 9C 9C 9C FF 99 99' + '99 FF 9A 9A 9A FF 99 99 99 FF 97 97 97 FF 95 95' + '95 FF 92 92 92 FF 8F 8F 8F FF 8C 8C 8C FF 87 87' + '87 FF 7F 7F 7F FF B5 B5 B5 FC 00 00 00 29 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F5 F0' + 'E4 FD DD CE B4 FF DA CC B3 FF D7 CA B1 FF D5 C8' + 'AE FF D0 C3 AA FF CC BF A5 FF C5 B9 9F FF BB AE' + '94 FF B2 A5 89 FF AD 9F 83 FF A8 9A 7E FF 9C 8A' + '66 FF BF BD BC FE D1 D1 D1 FC D0 D0 D0 FC D1 D1' + 'D1 FD D2 D2 D2 FD CE CE CE FD CD CD CD FD C8 C8' + 'C8 FD C4 C5 C4 FD BD BC BD FD B6 B6 B6 FE B0 B0' + 'B0 FD C2 C2 C2 FC 42 42 42 38 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E9 E6' + 'E3 53 FF FF F5 F6 FF FF EE F8 FF FC E9 F8 FF FA' + 'E7 F8 FF F6 E1 F8 FC F3 DB F8 F9 EF D6 F8 F6 ED' + 'CE F8 F3 E9 CB F8 EF E6 C6 F8 EC E0 BE F8 E5 DD' + 'CB E2 0B 0B 0B 17 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF C0 00' + '00 07 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' + '00 03 C0 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 C0 00 00 07 C0 00 FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 04 00 00 00 00 00 80 04 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 88 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 00 00 00 F8' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 80 00 00 F8 88 88 88 88 88 88 88 88' + '87 78 77 77 77 77 77 77 77 77 77 77 70 00 00 F8' + '88 FF FF FF FF FF FF FF FF FF FF FF FF FF FF 88' + '88 88 88 88 70 00 00 F8 8F 44 44 4F FF FF FF FF' + 'FF FF FF FF FF FF FF F8 88 88 88 88 70 00 00 F8' + 'FF 4F FF F4 44 44 44 44 4F FF FF 44 44 44 4F FF' + '88 88 88 88 70 00 00 F8 FF 4F F4 FF FF FF FF FF' + 'F4 44 44 FF FF FF FF FF 88 88 88 88 70 00 00 F8' + 'FF F4 F4 4F 44 4F 44 4F FF 4F F4 44 4F FF FF FF' + '88 88 88 88 70 00 00 F8 FF F4 4F F4 FF F4 4F 4F' + 'F4 FF FF 4F FF FF FF FF 88 88 88 88 70 00 00 F8' + 'FF F4 F4 FF FF F4 F4 FF F4 F4 F4 4F FF FF FF FF' + '88 88 88 88 70 00 00 F8 FF F4 44 FF FF FF 44 44' + 'F4 44 FF 4F FF FF FF FF 88 88 88 88 70 00 00 F8' + '8F FF FF FF FF FF FF FF FF FF FF FF FF FF FF F8' + '88 88 88 88 70 00 00 F8 88 FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF 88 88 88 88 88 70 00 00 F8' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 70 00 00 F8' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 70 00 00 F8' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 70 00 00 F8' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 70 00 00 F8' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 70 00 00 F8' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 70 00 00 F7' + '77 77 77 77 77 77 77 77 77 78 88 88 88 88 88 88' + '88 88 88 88 70 00 00 F6 66 66 66 66 66 66 66 66' + '66 67 77 77 77 77 77 77 77 77 77 77 00 00 00 FE' + '6E 6E 6E 6E 6E 6E 6E 6E 6E 68 88 88 88 88 88 88' + '88 88 88 80 00 00 00 0F EF EF EF EF EF EF EF EF' + 'EF 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '88 88 88 88 88 88 88 88 88 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 C0 00 00 00 00 0F 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 0F 00 00 C0 00' + '00 00 00 1F 00 00 E0 00 01 FF FF FF 00 00 F0 00' + '03 FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 28 00' + '00 00 30 00 00 00 60 00 00 00 01 00 08 00 00 00' + '00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 C0 DC C0 00 F0 CA A6 00 66 66' + '66 00 69 69 69 00 6D 6D 6D 00 72 72 72 00 75 75' + '75 00 79 79 79 00 7D 7D 7D 00 9F 73 58 00 91 6B' + '63 00 9E 77 6A 00 9D 76 6D 00 A1 7A 71 00 9B 8A' + '69 00 8E 88 7A 00 8F 89 7D 00 91 88 76 00 94 8B' + '77 00 92 8A 78 00 95 8C 78 00 92 8B 7D 00 95 8D' + '7D 00 99 8E 7A 00 A7 84 6F 00 AE 89 6D 00 AD 8C' + '7D 00 A2 93 75 00 AA 9C 7F 00 B6 96 7C 00 FF 00' + 'FF 00 81 81 81 00 83 83 84 00 85 85 85 00 89 89' + '89 00 8D 8D 8D 00 9F 95 80 00 96 93 8D 00 91 91' + '91 00 95 95 95 00 98 97 97 00 99 99 99 00 9D 9D' + '9D 00 A2 98 82 00 A6 9C 86 00 AC 9E 82 00 BE 9E' + '85 00 B7 99 8E 00 BA 9B 89 00 A4 9F 97 00 B6 9A' + '95 00 AD A0 84 00 AA A0 8A 00 AF A5 8F 00 BF A1' + '87 00 B3 A5 89 00 BF A2 8A 00 B8 AA 8F 00 AF A5' + '90 00 B0 A6 90 00 BC A3 93 00 BE B0 96 00 9F A0' + 'A3 00 A1 A1 A1 00 A2 A3 A4 00 A4 A3 A4 00 A3 A4' + 'A7 00 A5 A5 A5 00 A3 A6 A9 00 A5 A6 A9 00 A9 A9' + 'A9 00 A9 AB AF 00 AB AC AD 00 AD AD AD 00 BC B3' + 'A0 00 AC AE B2 00 B1 B1 B1 00 B1 B2 B4 00 B5 B5' + 'B5 00 B3 B5 B9 00 B6 B8 BC 00 B9 B9 B9 00 BB BB' + 'BC 00 BA BC BD 00 BD BD BD 00 C1 A4 8F 00 C2 A6' + '92 00 C1 A6 96 00 C6 AC 96 00 C3 AD 99 00 C6 AF' + '98 00 C0 AC 9E 00 C8 AF 9A 00 C0 B2 96 00 C7 B0' + '9A 00 C3 B5 99 00 C5 B7 9B 00 C9 B1 9B 00 C5 B8' + '9D 00 C9 BB 9F 00 C6 AF A4 00 CA B1 A0 00 CB B5' + 'A1 00 CD B6 A3 00 CA B3 A7 00 CD B7 A4 00 CA BC' + 'A2 00 CE BF A3 00 CF BA A7 00 CC BF A5 00 C8 B4' + 'AB 00 D0 BA A8 00 D0 BC AA 00 D3 BD B0 00 CE C3' + 'A3 00 CF C2 A7 00 D2 C4 A9 00 D6 C7 AB 00 D4 C1' + 'AF 00 D4 C6 AC 00 D6 C9 AF 00 D9 CB AF 00 CB C4' + 'B0 00 D4 C0 B0 00 D6 C3 B4 00 D6 C4 B5 00 D9 C6' + 'B5 00 D6 CD B5 00 D9 CB B2 00 DB CD B3 00 DC CD' + 'B2 00 D4 C2 B9 00 D6 C4 BC 00 D8 C7 B9 00 D9 C8' + 'B9 00 DD CB BB 00 DA C9 BC 00 DD CC BF 00 E0 D2' + 'B6 00 E0 D2 B8 00 E3 D5 B9 00 E7 D7 BE 00 E8 D9' + 'BD 00 BD BF C3 00 C1 C1 C1 00 C5 C5 C5 00 C8 C6' + 'C3 00 CB C7 C6 00 C3 C5 C8 00 C6 C8 CB 00 C9 C9' + 'C9 00 CD CC CB 00 CA CB CE 00 CD CD CD 00 D9 CB' + 'C3 00 DC CC C1 00 D9 C8 C6 00 DB CC C7 00 D0 CF' + 'CE 00 DF D1 C5 00 CC CE D2 00 CF D1 D4 00 D1 D2' + 'D2 00 D1 D3 D6 00 D6 D6 D6 00 DB D7 D4 00 DC D9' + 'D6 00 DA DA DA 00 DE DB D8 00 DF DC D9 00 D8 D9' + 'DD 00 DD DD DD 00 E1 D2 C6 00 EA DD C1 00 ED DF' + 'C3 00 E2 D5 CA 00 E5 D8 CC 00 E6 DB D1 00 E6 DD' + 'D2 00 E6 DA D5 00 E6 DC D6 00 EA DE D3 00 E9 DD' + 'D6 00 E0 DD DA 00 ED E2 C6 00 EE E6 C9 00 EB E3' + 'CF 00 EE E8 CC 00 F0 EA CF 00 E0 E0 D7 00 EA E0' + 'D7 00 EC E1 D7 00 E4 E1 DE 00 EA E1 DA 00 ED E2' + 'D9 00 EA E3 DC 00 EC E3 DD 00 EC E5 DD 00 F0 EA' + 'D2 00 F1 ED D6 00 F2 EF D8 00 F2 F0 DD 00 E2 E2' + 'E2 00 E7 E4 E2 00 E5 E5 E5 00 EB E4 E2 00 ED E6' + 'E0 00 EF E8 E2 00 EF EE E0 00 EA E8 E5 00 EC EA' + 'E6 00 E9 E9 E9 00 EB EC EC 00 ED ED EC 00 F0 E7' + 'E0 00 F2 E8 E1 00 F1 EA E5 00 F4 EB E4 00 F2 EC' + 'E6 00 F5 ED E6 00 F2 ED E8 00 F6 EE E8 00 F8 EF' + 'E9 00 F3 F3 E7 00 F7 F1 EB 00 F5 F1 EE 00 F8 F0' + 'EA 00 F8 F2 EE 00 F9 F4 EF 00 F4 F2 F1 00 F9 F5' + 'F1 00 F9 F6 F4 00 FB F8 F2 00 FB F8 F5 00 FC F9' + 'F6 00 FC FB F9 00 FE FC F9 00 FE FE FC 00 F0 FB' + 'FF 00 A4 A0 A0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 5A 4E 32 32 32 32 32 32' + '32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32' + '32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32' + '47 50 00 00 00 00 00 00 99 50 30 2F 2F 2F 2F 2E' + '2F 2E 2B 2B 2B 2B 2B 2B 2A 2B 2A 2A 2A 2A 2A 29' + '2A 29 29 29 27 27 F8 10 F8 10 10 F8 10 10 10 0F' + 'F8 0F 55 00 00 00 00 00 AF AC 99 98 07 5A 59 56' + '57 56 55 54 54 51 51 50 4D 4B 48 48 48 47 32 32' + '32 32 30 2F 2F 2F 2E 2B 2E 2B 2B 2E 2B 2B 2B 2B' + '31 0C 51 00 00 00 00 00 B3 9E 56 9F BF C8 C8 C8' + 'C8 C8 C8 C8 C8 BF BF BF BF BF BF BF BF BF B1 B1' + 'B0 B0 B0 AE AE AE AD 9A 32 29 F8 10 10 10 10 0F' + '2A 0B 50 00 00 00 00 00 AF 99 A6 D5 85 8D 8F 8F' + 'B8 EE F5 F5 F4 F4 F2 F4 F2 F2 F0 EC EC EC EC EC' + 'F0 F1 F2 F2 F2 F4 F4 EC D3 30 27 10 10 10 10 0F' + '2B 0B 4E 00 00 00 00 00 AC 9E DD EE C9 C9 CD CD' + 'CC 85 66 62 71 78 71 69 69 77 B7 DE EB EF EB D7' + 'A7 71 64 64 77 6F 6E B7 F5 A1 F8 F8 10 F8 0F 0F' + '2A 0A 50 00 00 00 00 00 AA A6 E9 EB 44 C9 E1 A5' + 'BB A4 CC CA C9 E0 C6 B9 E2 EC B9 8E 71 7E 77 8F' + 'B4 BE D7 F3 F5 F4 F3 EC E5 E2 2A 10 F8 10 10 0F' + '2A 0A 4D 00 00 00 00 00 A6 A6 EF F3 E2 12 86 22' + '11 15 20 40 21 6D B8 B4 60 E4 E9 8B 40 85 6D 5E' + '38 37 8B A3 D6 F1 EF F1 F1 ED 2B 28 10 F8 0F 10' + '2A 0A 4E 00 00 00 00 00 A1 A6 EC E3 EF 3A 8C EE' + '79 DE BC 90 3E 8F 6D 60 BA E0 DF 3E 91 84 8D 77' + '64 8B E3 CA DF E3 E3 E3 E6 E2 2E 28 F8 10 10 0F' + '2A 0B 4B 00 00 00 00 00 AA 99 F1 F3 E4 A2 25 B7' + 'F3 F1 F2 F2 E4 78 90 13 BB EE EF 83 61 74 78 D7' + '14 F2 F2 F2 F2 F2 F2 F2 F2 DD 2B 29 28 F8 10 0F' + '2A 0A 4B 00 00 00 00 00 B3 07 D9 F1 C7 76 5F 5D' + 'E8 EB EB EB F0 B9 6E 6F 70 DE EE BD 63 8D 90 CB' + '6C EB EB EB EC EB EB EB EE 9B 2A 29 29 F8 F8 0F' + '2A 0B 4D 00 00 00 00 00 D2 5A 9F EB EA EA D7 D6' + 'E5 E6 E3 E6 E3 E6 E6 E5 E5 E6 E5 E6 E3 CB E1 E6' + 'EA E6 E3 E5 E3 E5 E6 EC E0 32 2A 29 28 27 27 10' + '2A 0B 4B 00 00 00 00 00 D2 5C 5A 9E D4 DD DC DB' + 'DC DB DB DB DB DB DB DB D4 DB D4 DB D4 D4 D4 D4' + 'D4 D4 D4 D2 D4 D2 D3 A1 31 2B 2A 2B 29 29 28 F8' + '2B 0B 4B 00 00 00 00 00 D2 07 59 59 59 56 54 54' + '54 50 50 4E 4E 4E 4B 4B 4B 48 47 47 47 32 32 32' + '31 30 2F 2E 2E 2F 2E 2E 2B 2B 2B 2A 29 29 28 F8' + '2B 0C F7 00 00 00 00 00 D2 98 07 5A 59 59 54 54' + '51 50 50 4E 4E 4B 4B 4B 48 49 47 47 32 47 32 31' + '31 31 30 2F 2F 2F 2E 2E 2E 2B 2B 2A 2A 29 27 27' + '2E 0D 47 00 00 00 00 00 D2 99 98 98 5A 5A 59 56' + '54 51 51 51 4E 4E 4B 4B 4B 4B 48 F7 32 32 32 32' + '31 30 2F 2F 2F 2F 2E 2E 2E 2E 2B 2B 2A 2A 29 29' + '2F 0C F7 00 00 00 00 00 D2 99 99 07 5A 5A 56 56' + '56 54 51 51 4E 4B 4B 4B 4B 4B 4B 47 32 47 32 32' + '31 31 31 2F 2F 2F 2E 2E 2E 2E 2B 2B 2A 2A 2A 29' + '2E 0D 47 00 00 00 00 00 D2 9E 99 98 5C 59 59 56' + '54 54 54 4E 4E 4E 4B 4B 4B 49 48 47 47 32 32 32' + '31 31 31 30 2F 2F 2F 2E 2E 2B 2E 2B 2A 2A 2A 29' + '2F 0E 47 00 00 00 00 00 D4 99 9E 98 98 5C 59 59' + '56 51 51 50 4E 4E 4B 4B 4B 47 4B 47 47 32 32 47' + '31 31 31 31 30 2F 2F 2E 2F 2E 2E 2B 2B 2A 2A 29' + '2F 0E 32 00 00 00 00 00 D4 9E 99 99 99 5C 5A 59' + '56 56 54 50 4E 4E 4E 4B 49 47 48 47 47 47 32 47' + '32 32 31 31 31 30 2F 2F 2F 2E 2E 2E 2B 2B 2A 2A' + '30 0E 47 00 00 00 00 00 D4 9E 9E 99 98 07 5A 59' + '56 54 54 51 51 4E 4E 4E 4B 4B F7 48 47 32 32 32' + '32 32 31 32 31 31 30 2F 2F 2F 2E 2E 2E 2B 2B 29' + '31 0F 32 00 00 00 00 00 D4 9F A0 9A 99 07 5C 5A' + '59 56 54 54 4E 4E 4E 4B 4B 4B 48 49 47 F7 47 32' + '47 32 32 31 32 31 30 2F 2F 2F 2F 2E 2E 2B 2A 2A' + '30 0F 32 00 00 00 00 00 D4 9F A0 9E 99 98 98 5C' + '59 56 54 54 50 4E 4B 4B 4B 49 47 47 47 47 47 32' + '32 32 32 32 31 31 31 30 30 2F 2F 2E 2E 2B 2B 2A' + '2F 0F 32 00 00 00 00 00 D4 A1 A1 9E 99 99 98 07' + '59 56 55 54 51 51 4E 4B 49 47 49 47 47 32 47 32' + '32 32 32 32 32 31 31 31 30 2F 2F 2E 2E 2B 2B 29' + '31 0F 32 00 00 00 00 00 DB A1 A6 A0 9E 98 98 07' + '59 59 56 54 51 50 4B 4B 4B 4B 48 47 47 47 32 32' + '47 32 32 32 32 32 31 30 30 2F 2F 2E 2B 2B 2A 2A' + '2F 0F 47 00 00 00 00 00 DB B2 A9 A9 A8 A1 9D 9C' + '97 5B 58 57 55 53 4F 4E 4C 4A 4C 48 47 32 47 32' + '32 32 32 32 31 32 31 31 30 2E 2F 2E 2B 2B 2A 28' + '2F 28 47 00 00 00 00 00 C5 82 43 3D 42 3C 34 33' + '2C 1F 1C 1A 19 1B 1E 1E 1D 17 18 2D 46 32 32 32' + '32 32 32 31 31 31 30 30 2F 2E 2B 2E 2A 2A 28 29' + '4B 32 00 00 00 00 00 00 B3 C2 8A 88 80 7F 7F 7C' + '7B 75 72 6A 45 41 3F 3B 35 24 23 16 39 57 5A 59' + '56 59 56 56 56 54 54 51 50 4B 4B 47 32 31 30 4E' + '4E 00 00 00 00 00 00 00 00 E7 CE C1 C0 B6 B5 96' + '94 92 8A 81 7D 7C 73 72 68 67 65 7A 52 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 D8 D1 D1 D0 D0 CF' + 'C4 C4 C3 C3 B6 B6 B5 95 94 93 93 87 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -2610,9 +4142,6 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 26 00 00 00 BF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 EC 00 00 00 83 00 00' - '00 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -2622,612 +4151,2525 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 0A 00 00 00 75 00 00' - '00 84 00 00 00 60 00 00 00 24 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 FF FF FF FF FF FF 00 00 FF FF' - 'FC 3F FF FF 00 00 FF FF F8 1F FF FF 00 00 FF FF' - 'F0 0F FF FF 00 00 FF FF C0 07 FF FF 00 00 FF FF' - '80 03 FF FF 00 00 FF FF 00 01 FF FF 00 00 FF FC' - '00 00 FF FF 00 00 FF F8 00 00 7F FF 00 00 FF E0' - '00 00 3F FF 00 00 FF C0 00 00 1F FF 00 00 FF 80' - '00 00 0F FF 00 00 FE 00 00 00 0F FF 00 00 FC 00' - '00 00 03 FF 00 00 F8 00 00 00 03 FF 00 00 E0 00' - '00 00 01 FF 00 00 C0 00 00 00 00 FF 00 00 80 00' - '00 00 00 7F 00 00 00 00 00 00 00 3F 00 00 00 00' - '00 00 00 1F 00 00 00 00 00 00 00 07 00 00 00 00' - '00 00 00 07 00 00 00 00 00 00 00 03 00 00 C0 00' - '00 00 00 00 00 00 E0 00 00 00 00 00 00 00 F0 00' - '00 00 00 00 00 00 F0 00 00 00 00 00 00 00 F8 00' - '00 00 00 00 00 00 FC 00 00 00 00 01 00 00 FF 00' - '00 00 00 03 00 00 FF 00 00 00 00 07 00 00 FF C0' - '00 00 00 0F 00 00 FF E0 00 00 00 1F 00 00 FF F0' - '00 00 00 3F 00 00 FF F8 00 00 00 7F 00 00 FF F8' - '00 00 00 7F 00 00 FF F8 00 00 00 FF 00 00 FF F8' - '00 00 01 FF 00 00 FF FC 00 00 03 FF 00 00 FF FE' - '00 00 07 FF 00 00 FF FF 80 00 0F FF 00 00 FF FF' - '80 00 1F FF 00 00 FF FF C0 00 3F FF 00 00 FF FF' - 'E0 00 7F FF 00 00 FF FF F8 00 7F FF 00 00 FF FF' - 'FC 00 FF FF 00 00 FF FF FE 01 FF FF 00 00 FF FF' - 'FF 07 FF FF 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 20 00 00 00 00 00 00 10 00 00 00 00' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 C0 00' + '00 00 00 0F 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 0F 00 00 C0 00 00 00 00 1F 00 00 E0 00' + '01 FF FF FF 00 00 F0 00 03 FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 20 00 00 00 00 00 80 25 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 0E 00 00 00 58 00 00' - '00 3C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 30 00 00 00 C1 09 00 00 FF 00 00' - '00 ED 00 00 00 59 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 61 00 00 00 E9 33 0D 00 FF 97 3E 19 FF 4D 1D' - '0A FF 00 00 00 F0 00 00 00 52 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 14 00 00 00 9A 02 00' - '00 FF 63 32 1D FF C6 78 55 FF F2 DA D2 FF CC A1' - '95 FF 44 17 07 FF 00 00 00 EB 00 00 00 4C 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 3B 00 00 00 CD 18 03 00 FF 9A 62' - '4A FF E0 9D 7D FF F1 DB D2 FF FF FF FF FF FD FD' - 'FE FF CA 9D 8F FF 3D 15 08 FF 00 00 00 E5 00 00' - '00 45 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 6F 00 00 00 F1 3B 15 09 FF CB 95 7B FF EE AA' - '83 FF E9 C8 B8 FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FD FC FE FF C8 9B 8D FF 39 14 07 FF 00 00' - '00 E2 00 00 00 3C 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 03 00 00 00 80 00 00' - '00 FF 45 1B 0D FF E1 AC 91 FF FC BD 95 FF DB A5' - '8B FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FA F7 F9 FF B5 80 6E FF 1A 05' - '00 FF 00 00 00 CA 00 00 00 1E 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 1F 00 00 00 AC 07 00 00 FF 74 43' - '2F FF EF C3 A7 FF FC C4 9B FF DB A2 86 FF FA D0' - 'AF FF FF BE 87 FF FF FC FA FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF F4 EB EB FF A8 78' - '67 FF 16 05 00 FF 00 00 00 BB 00 00 00 1B 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 4A 00 00 00 DA 24 0A 00 FF AB 76 5E FF FF DE' - 'C1 FF FE CB A2 FF DC A0 80 FF F7 D7 BD FF FF 9D' - '49 FF FF 8E 31 FF FF BB 82 FF FF FB F8 FF FF FF' - 'FF FF FF FF FF FF FD FB FB FF FA FA FB FF F3 EA' - 'E9 FF A6 7B 6A FF 16 05 00 FF 00 00 00 B5 00 00' - '00 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 07 00 00 00 82 00 00' - '00 F9 50 28 15 FF D8 A9 8F FF FF E9 CE FF FE D1' - 'AA FF DE A2 81 FF F4 D5 C0 FF FF A6 58 FF FF 96' - '3E FF FF 98 44 FF FF 96 3E FF FF BD 85 FF FF FA' - 'F6 FF FE FF FF FF FA F8 F8 FF F6 F3 F3 FF F4 F2' - 'F3 FF F0 E4 E3 FF A5 74 5E FF 16 05 00 FF 00 00' - '00 B1 00 00 00 16 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 28 00 00 00 B8 0E 00 00 FF 86 55' - '3D FF F6 D3 BA FF FF EB D1 FF FE D9 B7 FF E2 A9' - '88 FF F1 D4 C1 FF FF B0 69 FF FF 98 40 FF FF 9C' - '49 FF FF 9C 48 FF FF 9D 4C FF FF A0 4E FF FF C1' - '89 FF FD F6 F1 FF F8 F9 FB FF F5 F0 F0 FF F1 EB' - 'EB FF EE EA EB FF ED E0 DD FF A6 74 5B FF 16 05' - '00 FF 00 00 00 B1 00 00 00 16 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 4F 00 00 00 E2 30 13 04 FF BB 8B 70 FF FF ED' - 'D8 FF FF EB D1 FF FF E3 C7 FF E5 B0 8C FF ED CB' - 'B6 FF FF B9 7A FF FF 99 44 FF FF 9F 4D FF FF 9E' - '4D FF FF 9E 4C FF FF A0 4F FF FF A6 5A FF FF AA' - '5D FF FE C3 8D FF F7 EE E8 FF F2 F0 F2 FF EE E8' - 'E8 FF EB E3 E3 FF E8 E2 E3 FF EA DA D7 FF A7 72' - '56 FF 18 06 00 FF 00 00 00 B1 00 00 00 17 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 EA 53 27 0F FF E8 BF A3 FF FF F9 E7 FF FF EB' - 'D2 FF FF EF D8 FF EA BC 9B FF E7 BB 9E FF FF C0' - '86 FF FF 9C 47 FF FF A2 51 FF FF A1 51 FF FF A1' - '51 FF FF A1 50 FF FF A2 53 FF FF A9 5D FF FF B0' - '68 FF FF B3 6B FF FD C8 94 FF F1 EA E7 FF EC E8' - 'EA FF E9 E1 E1 FF E5 DC DB FF E2 DA DB FF E7 D4' - 'D0 FF A2 65 42 FF 08 00 00 FF 00 00 00 A8 00 00' - '00 0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 EB 5F 32 18 FF F9 D2 B3 FF FF F4 DE FF FF EF' - 'DA FF F4 D4 B9 FF E1 A8 82 FF FD C5 93 FF FF A1' - '50 FF FF A2 53 FF FF A3 54 FF FF A3 54 FF FF A3' - '53 FF FF A3 53 FF FF A3 55 FF FF AB 60 FF FF B2' - '6B FF FF B9 76 FF FF BE 7A FF FA D1 A8 FF EC E4' - 'E2 FF E7 DF E2 FF E4 DA DA FF DF D4 D4 FF DD D4' - 'D6 FF E2 C5 B9 FF 7D 4A 2C FF 01 00 00 FF 00 00' - '00 86 00 00 00 05 00 00 00 00 00 00 00 00 00 00' - '00 51 00 00 00 F3 5A 2F 15 FF F1 C8 A5 FF FC E5' - 'CD FF E1 A2 77 FF FB BB 85 FF FF A5 56 FF FF A3' - '53 FF FF A4 55 FF FF A4 55 FF FF A4 55 FF FF A3' - '55 FF FF A3 55 FF FF A4 57 FF FF AB 61 FF FF B3' - '6C FF FF BA 77 FF FF C0 81 FF FF C6 87 FF F9 D4' - 'AC FF E7 DC DA FF E1 D7 D9 FF DE D2 D2 FF DA CD' - 'CD FF D8 CD D0 FF E1 C4 B8 FF 7F 4F 31 FF 01 00' - '00 FF 00 00 00 83 00 00 00 03 00 00 00 00 00 00' - '00 00 00 00 00 48 00 00 00 EB 56 2F 16 FF E3 94' - '5B FF FB B5 79 FF FF A8 5C FF FF A3 55 FF FF A4' - '58 FF FF A4 56 FF FF A4 56 FF FF A4 56 FF FF A4' - '56 FF FF A4 56 FF FF A5 58 FF FF AB 62 FF FF B2' - '6B FF FF B9 76 FF FF C2 85 FF FF CD 97 FF FF D7' - 'A7 FF F8 DF C1 FF E2 D6 D4 FF DB CF D0 FF D9 CB' - 'CB FF D4 C5 C4 FF D3 C6 C8 FF E2 C5 B8 FF 80 51' - '31 FF 02 00 00 FE 00 00 00 79 00 00 00 06 00 00' - '00 00 00 00 00 00 00 00 00 4E 00 00 00 FF 7A 40' - '16 FF FB 9E 51 FF FF AA 5C FF FF A6 5A FF FF A5' - '59 FF FF A5 59 FF FF A5 58 FF FF A5 59 FF FF A5' - '59 FF FF A4 57 FF FF A4 57 FF FF AB 62 FF FF B8' - '76 FF FF C6 8F FF FF D2 A3 FF FF DA B1 FF FF E0' - 'BA FF FF E5 C1 FF F8 E5 CB FF DE D0 CD FF D4 C6' - 'C7 FF D3 C3 C3 FF CE BC BC FF CD BE C0 FF DE C0' - 'B3 FF 85 56 37 FF 06 01 00 FE 00 00 00 4C 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 6B 00 00' - '00 FB 6D 37 0D FF F5 92 41 FF FF AC 5F FF FF A7' - '5C FF FF A6 5B FF FF A6 5B FF FF A6 5B FF FF A5' - '59 FF FF A5 58 FF FF AE 68 FF FF C0 87 FF FF CC' - '9D FF FF D3 A9 FF FF D8 B0 FF FF DD B8 FF FF E2' - 'C0 FF FF E7 C8 FF FF EE D0 FF F8 EA D5 FF DA CB' - 'C8 FF CF BE BF FF CD BB BB FF C8 B5 B5 FF D3 C0' - 'C1 FF 9B 77 61 FF 09 03 00 FF 00 00 00 4B 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 5B 00 00 00 F7 5D 2F 0C FF F1 90 41 FF FF AD' - '62 FF FF A8 5E FF FF A7 5D FF FF A6 5A FF FF AB' - '64 FF FF BC 83 FF FF C8 98 FF FF CE A1 FF FF D2' - 'A8 FF FF D7 B0 FF FF DC B8 FF FF E1 C0 FF FF E5' - 'C8 FF FF EA CF FF FF EE D6 FF FF F4 DE FF F8 EF' - 'DE FF D6 C6 C4 FF C8 B6 B7 FF D0 BC BD FF A0 80' - '70 FF 10 09 04 FF 00 00 00 8E 00 00 00 04 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 6B 00 00 00 FF 70 3A 0F FF FB 9C' - '4B FF FF AE 64 FF FF A6 5B FF FF AE 6A FF FF C6' - '95 FF FF CD A3 FF FF CD A1 FF FF D1 A8 FF FF D6' - 'B0 FF FF DB B8 FF FF DF C0 FF FF E4 C6 FF FF E8' - 'CD FF FF EC D4 FF FF F0 DB FF FF F3 E1 FF FF FC' - 'EA FF F5 EC E0 FF D3 C2 C3 FF BF A0 95 FF 29 19' - '0C FF 00 00 00 C7 00 00 00 0B 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 69 00 00 00 FB 6F 3C' - '12 FF F7 9B 4C FF FF BC 7F FF FF CD A3 FF FF D1' - 'AB FF FF D0 AA FF FF D2 AC FF FF D6 B1 FF FF DB' - 'B9 FF FF DF C0 FF FF E3 C7 FF FF E7 CD FF FF EB' - 'D4 FF FF EF DA FF FF F3 E0 FF FF F6 E6 FF FE F5' - 'E5 FF FF FF EF FF E9 D0 BA FF 43 2D 20 FF 00 00' - '00 D9 00 00 00 25 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 59 00 00' - '00 F2 60 36 14 FF ED BA 8B FF FF DB B9 FF FF D7' - 'B4 FF FF D6 B5 FF FF D8 B7 FF FF DC BC FF FF E0' - 'C2 FF FF E4 C9 FF FF E8 CF FF FF EB D5 FF FF EF' - 'DB FF FF F2 E1 FF FF F4 E5 FF FE F2 E1 FF FF FA' - 'EE FF F9 EA D3 FF 5B 41 2A FF 00 00 00 E4 00 00' - '00 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 4F 00 00 00 F9 A5 74 48 FF FB CF A6 FF FF DE' - 'C1 FF FF DD C0 FF FF DD C1 FF FF E1 C6 FF FF E5' - 'CD FF FF E8 D2 FF FF EC D7 FF FF EE DD FF FF F2' - 'E2 FF FF F3 E4 FF FE F0 DF FF FF F9 EF FF FF F3' - 'E2 FF 70 52 37 FF 00 00 00 F0 00 00 00 42 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 23 03 00 00 FD BF 97 6F FF FF EA D3 FF F4 CC' - 'A9 FF FF E3 CB FF FF E3 CC FF FF E6 CF FF FF EA' - 'D5 FF FF EC DA FF FF EF DF FF FF F2 E4 FF FE F2' - 'E3 FF FE F0 E1 FF FF FA F3 FF FF FA ED FF 83 65' - '48 FF 00 00 00 F6 00 00 00 52 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 12 00 00 00 CD 37 23 0E FF D6 B6 91 FF FD E7' - 'D0 FF F6 D3 B4 FF FF EB D9 FF FF EB D9 FF FF EE' - 'DE FF FF F0 E2 FF FF F2 E6 FF FF F1 E4 FF FE F3' - 'E6 FF FF FA F4 FF FF FE F4 FF 98 79 5A FF 01 00' - '00 FC 00 00 00 65 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 1C 00 00 00 D4 24 13 01 FF D7 A8' - '74 FF FE D8 B5 FF FF F1 E5 FF FF EF E1 FF FF F2' - 'E6 FF FF F4 E9 FF FE F2 E5 FF FF F6 EC FF FF FA' - 'F4 FF FF FF FF FF BB 9C 7B FF 06 01 00 FF 00 00' - '00 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 2A 00 00 00 D3 36 1F' - '07 FF EB C5 9C FF FF FF F6 FF FF F4 EA FF FF F5' - 'EB FF FF F4 EB FF FF F7 F0 FF FF FB F7 FF FF FF' - 'FF FF E0 C8 AD FF 29 1A 0B FF 00 00 00 B7 00 00' - '00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 22 00 00' - '00 DA 3E 2D 1A FF DE C5 A8 FF FF FF FD FF FF FA' - 'F5 FF FF FB F7 FF FF FD FB FF FF FF FF FF EC D9' - 'C2 FF 3A 27 14 FF 00 00 00 C7 00 00 00 16 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 22 00 00 00 C4 25 18 0A FF C6 B0 95 FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF F4 E5 D3 FF 4B 36' - '20 FF 00 00 00 D6 00 00 00 22 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 10 00 00 00 A6 12 09 01 FF AA 94' - '7A FF FF F6 EA FF F0 DE C8 FF 5E 46 2B FF 00 00' - '00 E3 00 00 00 2E 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 02 00 00 00 84 05 01' - '00 FF 44 31 1A FF 26 19 0A FF 00 00 00 E1 00 00' - '00 3D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 54 00 00 00 84 00 00 00 67 00 00 00 1F 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FC' - '7F FF FF F8 3F FF FF F0 1F FF FF C0 0F FF FF 80' - '07 FF FF 00 03 FF FC 00 01 FF F8 00 00 FF F0 00' - '00 7F C0 00 00 3F 80 00 00 1F 00 00 00 0F 00 00' - '00 07 00 00 00 03 00 00 00 01 80 00 00 00 C0 00' - '00 00 E0 00 00 00 F0 00 00 00 F8 00 00 01 FC 00' - '00 03 FE 00 00 07 FF 00 00 0F FF 00 00 1F FF 00' - '00 3F FF 80 00 7F FF C0 00 7F FF E0 00 FF FF F0' - '01 FF FF F8 03 FF FF FC 07 FF FF FF 0F FF 28 00' - '00 00 10 00 00 00 20 00 00 00 01 00 20 00 00 00' - '00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 10 00 00 00 8A 00 00 00 67 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 36 1E 0B' - '04 C9 9D 6B 56 FF 5D 3E 32 FE 00 00 00 67 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 69 4C 2F 21 EF CE 9D' - '87 FF FF FF FE FF F0 E3 DF FF 50 36 2D F9 00 00' - '00 5D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 0A 00 00 00 90 71 4F 3D FF EE B2 90 FF FD D2' - 'B5 FF FF FF FD FF FF FF FF FF E8 D7 D1 FF 3C 27' - '1F F3 00 00 00 47 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 2C 1A 0D' - '07 BF A6 83 6D FF FB CC AC FF F7 BA 8F FF FF 9A' - '44 FF FF C3 91 FF FF FD FC FF FF FF FF FF DC C7' - 'C1 FF 38 26 1D E9 00 00 00 40 00 00 00 00 00 00' - '00 00 00 00 00 00 02 01 00 5C 47 33 27 E8 D6 B9' - 'A1 FF FF E0 C2 FF F3 BF 98 FF FF A3 55 FF FF 9A' - '44 FF FF 9D 4A FF FF CB 9D FF F5 F2 F2 FF F6 F2' - 'F5 FF D5 BD B4 FF 39 24 19 E9 00 00 00 3E 00 00' - '00 00 00 00 00 00 29 16 0B F2 F3 D0 B4 FF FF F3' - 'DC FF EF BC 94 FF FE A9 60 FF FF 9F 4D FF FF A1' - '51 FF FF A5 57 FF FF B1 67 FF F9 D1 AA FF E9 E2' - 'E3 FF EA E2 E5 FF CA AB 9D FF 28 17 0D E2 00 00' - '00 2D 00 00 00 00 06 03 02 67 6D 53 3F FB F7 B8' - '88 FF FF AD 65 FF FF A3 52 FF FF A4 56 FF FF A3' - '54 FF FF A7 5A FF FF B5 6F FF FF C5 88 FF F5 D9' - 'BD FF DE D4 D4 FF DE D1 D3 FF BD 9F 92 FF 2D 1E' - '13 D8 00 00 00 2D 00 00 00 00 00 00 00 6E 76 43' - '1B FF FD A4 53 FF FF AA 5D FF FF A5 57 FF FF A4' - '56 FF FF B0 6C FF FF C6 91 FF FF D7 AE FF FF E4' - 'C0 FF F3 E2 D0 FF D3 C5 C5 FF D9 C7 C9 FF A5 85' - '75 FF 06 03 01 9C 00 00 00 00 00 00 00 00 00 00' - '00 6D 6E 3E 18 FF FF A6 58 FF FF AE 65 FF FF BD' - '85 FF FF CC A0 FF FF D7 B2 FF FF E0 C0 FF FF E9' - 'CE FF FF F3 DC FF F3 EA DF FF CB B4 AF FF 3C 30' - '2A EB 01 01 00 32 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 6F 71 43 1E F6 F7 C2 90 FF FF D8' - 'B5 FF FF D7 B4 FF FF DF C1 FF FF E7 CE FF FF EE' - 'DA FF FF F8 E8 FF FF F7 E4 FF 61 51 44 F8 00 00' - '00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 9F D0 AA 86 FF FF E6' - 'CA FF FF E1 C9 FF FF E8 D3 FF FF EE DD FF FF F4' - 'E7 FF FF FC ED FF 7C 6B 5A FD 00 00 00 64 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 43 53 43 32 F4 F3 D2' - 'B0 FF FF F1 E0 FF FF F0 E3 FF FF F4 E8 FF FF FF' - 'FB FF 98 86 75 FF 00 00 00 7D 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 4E 59 47' - '34 F4 F5 EA D9 FF FF FF FC FF FF FF FF FF BA AC' - '9C FF 0C 07 03 A2 00 00 00 03 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 45 47 3C 31 E3 E3 DC D4 FF C9 BE B0 FF 16 10' - '09 B4 00 00 00 0A 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 2B 19 12 0A BB 0D 09 03 A0 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 1F 00 00' + '00 2D 00 00 00 2E 00 00 00 2E 00 00 00 2E 00 00' + '00 2E 00 00 00 2E 00 00 00 2E 00 00 00 2E 00 00' + '00 2E 00 00 00 2E 00 00 00 2E 00 00 00 2E 00 00' + '00 2E 00 00 00 2E 00 00 00 2E 00 00 00 2E 00 00' + '00 2E 00 00 00 2E 00 00 00 2E 00 00 00 2E 00 00' + '00 2E 00 00 00 2E 00 00 00 2E 00 00 00 2E 00 00' + '00 2E 00 00 00 2E 00 00 00 2E 00 00 00 2E 00 00' + '00 2E 00 00 00 2E 00 00 00 2E 00 00 00 2E 00 00' + '00 2E 00 00 00 2E 00 00 00 2E 00 00 00 2E 00 00' + '00 2E 00 00 00 2E 00 00 00 2C 00 00 00 1A 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 DA DA DA 31 9E 9E 9E 97 86 86' + '86 B4 84 84 84 B5 84 84 84 B5 83 83 83 B5 81 81' + '81 B5 80 80 80 B5 80 80 80 B5 7E 7E 7E B5 7D 7D' + '7D B5 7A 7A 7A B5 7A 7A 7A B5 7A 7A 7A B5 7A 7A' + '7A B5 79 79 79 B5 74 74 74 B5 76 76 76 B5 74 74' + '74 B5 74 74 74 B5 74 74 74 B5 73 73 73 B5 70 70' + '70 B5 70 70 70 B5 70 70 70 B5 6F 6F 6F B5 6D 6D' + '6D B5 6B 6B 6B B5 69 69 69 B5 66 66 66 B5 65 65' + '65 B5 65 65 65 B5 65 65 65 B5 62 62 62 B5 62 62' + '62 B5 64 64 64 B5 62 62 62 B5 61 61 61 B5 61 61' + '61 B5 5F 5F 5F B5 5E 5E 5E AA 0F 0F 0F 63 00 00' '00 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 FC 7F 00 00 F8 3F 00 00 F0 1F' - '00 00 C0 0F 00 00 80 07 00 00 00 03 00 00 00 01' - '00 00 00 00 00 00 80 00 00 00 C0 00 00 00 E0 01' - '00 00 F0 03 00 00 F0 07 00 00 F8 07 00 00 FC 0F' - '00 00 FE 1F 00 00' + '00 00 00 00 00 00 FA FA FA 73 D6 D6 D6 F6 C5 C5' + 'C5 FC C2 C2 C2 FC BE BF BF FC BB BC BC FC B8 B9' + 'B9 FC B7 B7 B7 FC B5 B6 B7 FC B4 B4 B5 FC B2 B3' + 'B4 FC B0 B1 B2 FC AE AF AF FC AD AE AE FC AB AC' + 'AD FC AA AA AB FC A6 A7 A8 FC A6 A6 A6 FC A2 A3' + 'A4 FC A1 A2 A2 FC A2 A2 A2 FC A1 A1 A1 FC 9F 9F' + '9F FC 9E 9E 9E FC 9D 9D 9D FC 9B 9B 9B FC 98 98' + '98 FC 96 96 96 FC 94 94 94 FC 92 92 92 FC 90 90' + '90 FC 8E 8E 8E FC 8F 8F 8F FC 8C 8C 8C FC 8E 8E' + '8E FC 8F 8F 8F FC 8E 8E 8E FC 8D 8D 8D FC 8D 8D' + '8D FC 8B 8B 8B FC 96 96 96 EC 22 22 22 87 00 00' + '00 19 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FA FA FA 7D C9 C9 C9 FF B7 B7' + 'B7 FF CB CA CA FF E0 DD DB FF E5 E2 DF FF E5 E2' + 'DF FF E4 E1 DE FF E4 E1 DE FF E4 E1 DE FF E4 E0' + 'DE FF E3 E0 DD FF E2 DF DC FF E2 DF DC FF E1 DE' + 'DB FF E1 DE DB FF E1 DD DB FF E0 DD DA FF E0 DD' + 'DA FF DF DC D9 FF E0 DD DA FF E0 DC D9 FF DF DC' + 'D9 FF DF DB D8 FF DE DB D8 FF DE DB D8 FF DE DB' + 'D8 FF DD DA D7 FF DC D9 D6 FF DC D9 D6 FF DB D7' + 'D4 FF C8 C6 C3 FF 9D 9E 9D FF 84 84 84 FF 7F 7F' + '7F FF 7D 7D 7D FF 7D 7D 7D FF 7C 7C 7C FF 7B 7B' + '7B FF 79 79 79 FF 89 89 89 F0 24 24 24 8D 00 00' + '00 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 F6 F6 F6 7D C7 C7 C7 FF CE CD' + 'CC FF EB E4 E2 FF D6 C4 B7 FF DA C6 B6 FF DD CB' + 'BB FF DD CB BB FF E6 D9 CC FF F9 F4 F0 FF FF FF' + 'FD FF FF FE FC FF FF FC F9 FF FE FA F7 FF FD F9' + 'F6 FF FD FA F6 FF FD FA F6 FF FC F9 F5 FF FB F7' + 'F2 FF FA F5 F0 FF F9 F4 EF FF F9 F3 EF FF F9 F4' + 'EF FF FA F4 F0 FF FB F6 F3 FF FC F8 F5 FF FD F9' + 'F6 FF FD F9 F6 FF FE FA F7 FF FF FC FA FF FE FD' + 'FA FF F9 F3 EF FF E7 E4 E2 FF 98 97 97 FF 80 80' + '80 FF 7E 7E 7E FF 7D 7D 7D FF 7D 7D 7D FF 7C 7C' + '7C FF 7A 7A 7A FF 88 88 88 F1 23 23 23 90 00 00' + '00 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 EE EE EE 7E C7 C7 C7 FF ED ED' + 'EC FF F8 F4 F3 FF EA E1 DB FF EC E2 DA FF ED E5' + 'DD FF ED E5 DD FF EC E3 DD FF D5 C5 B4 FF C7 B0' + '9A FF C6 AF 98 FF CD B7 A4 FF D0 BC AA FF CE B7' + 'A4 FF C9 B1 9B FF C8 AF 99 FF D0 BB A8 FF E2 D5' + 'CA FF EE E7 E0 FF F6 F3 F1 FF FA F7 F5 FF F6 F2' + 'EF FF EF E8 E2 FF DF D1 C5 FF CE B7 A4 FF C7 AD' + '97 FF C8 B0 99 FF D0 BC A9 FF CC B6 A3 FF CB B5' + 'A1 FF E3 D6 CB FF FD FC FB FF CD CE CE FF 80 80' + '80 FF 7F 7F 7F FF 7F 7F 7F FF 7E 7E 7E FF 7C 7C' + '7C FF 79 79 79 FF 86 86 86 F1 22 22 22 92 00 00' + '00 1D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 E4 E6 E6 7E CE CD CC FF F6 F1' + 'ED FF F9 F3 ED FF BC A3 93 FF E9 E0 DB FF F4 EB' + 'E4 FF DB CC C7 FF E6 DA D5 FF D9 C8 C6 FF EC E4' + 'DF FF EB E2 DA FF EA E1 DA FF F1 EA E6 FF EA E0' + 'D7 FF E6 DB D1 FF F1 EB E5 FF F8 F4 F0 FF E7 DB' + 'D0 FF D9 C8 B9 FF CE B7 A3 FF D4 C1 AF FF D0 BA' + 'A8 FF DB C9 BC FF E3 D3 C6 FF E9 DD D6 FF ED E6' + 'E2 FF FC FB FB FF FE FD FD FF FD FC F9 FF FD FB' + 'F8 FF FA F3 EF FF F7 EF E7 FF F1 ED E8 FF 88 88' + '88 FF 7F 7F 7F FF 7E 7E 7E FF 7E 7E 7E FF 7C 7C' + '7C FF 7A 7A 7A FF 84 84 84 F2 25 25 25 95 00 00' + '00 1F 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 DE DE DE 7F D0 CF CE FF F9 F7' + 'F5 FF FC FA F7 FF F2 EC E6 FF 91 6B 63 FF D8 C6' + 'B5 FF AD 8C 7D FF 9F 73 58 FF A1 7A 71 FF A7 84' + '6F FF BF A2 8C FF AE 89 6D FF CA B3 9F FF E4 D8' + 'CD FF E0 D2 C6 FF C6 AD 95 FF F2 ED E7 FF F4 F0' + 'EF FF D4 C3 BB FF BF A2 89 FF D6 C3 B4 FF C9 B0' + 'A0 FF C2 A6 92 FF BA 9B 89 FF B7 99 8E FF D5 C3' + 'B8 FF DC CC C1 FF ED E6 DE FF FB F7 F5 FF FA F6' + 'F4 FF FA F7 F4 FF FB F8 F5 FF F3 F2 F1 FF 8F 8F' + '8F FF 81 81 81 FF 7F 7F 7F FF 7E 7E 7E FF 7C 7C' + '7C FF 7A 7A 7A FF 86 86 86 F3 2A 2A 2A 97 00 00' + '00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 DB DB DB 80 CF CE CD FF F8 F3' + 'EF FF F5 ED E6 FF FB F8 F2 FF B6 9A 95 FF D6 C4' + 'BC FF F8 F6 F2 FF D3 BD B0 FF F0 E7 E0 FF E6 DC' + 'D6 FF DB C8 B9 FF BF A1 87 FF DA CA BC FF CB B2' + 'A0 FF C5 AA 96 FF E6 DD D2 FF F3 EB E4 FF F3 E9' + 'E2 FF BE 9E 85 FF DD CC BF FF D7 C4 B4 FF D8 C7' + 'B8 FF D0 BB A8 FF C9 AF 9B FF D4 C2 BA FF F5 ED' + 'E6 FF EE E2 D8 FF F2 E8 E0 FF F5 EE E7 FF F5 ED' + 'E6 FF F5 ED E6 FF F7 ED E7 FF F2 ED E9 FF 91 90' + '90 FF 84 84 84 FF 7F 7F 7F FF 7D 7D 7D FF 7D 7D' + '7D FF 7A 7A 7A FF 86 86 86 F4 2C 2C 2C 9A 00 00' + '00 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 E1 E1 E1 1A D8 D9 D9 B3 C7 C7 C7 FF F8 F7' + 'F6 FF FD FB F9 FF F2 EE E8 FF D9 CB C3 FF B6 96' + '7C FF E3 D6 CA FF FC FA F7 FF FB F8 F6 FF FC F9' + 'F6 FF FC F8 F6 FF F3 EE E9 FF D0 BC AB FF D8 C7' + 'BB FF 9E 77 6A FF E7 DC D6 FF F8 F4 F1 FF FA F7' + 'F4 FF D4 C0 B0 FF C3 AD 99 FF CF BA A7 FF D0 BC' + 'AB FF EF E8 E2 FF 9D 76 6D FF FB F9 F6 FF FC F9' + 'F6 FF FC F8 F6 FF FC F9 F6 FF FC F9 F6 FF FC F9' + 'F6 FF FB F8 F6 FF FD F9 F7 FF EE ED EC FF 8D 8D' + '8D FF 85 85 85 FF 82 82 82 FF 80 80 80 FF 7D 7D' + '7D FF 7B 7B 7B FF 87 87 87 F4 2C 2C 2D 9C 00 00' + '00 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 FA FA FA 34 E2 E2 E2 EA BE BE BE FF EA E8' + 'E5 FF FB F7 F3 FF EC E1 D7 FF C8 B4 AB FF C1 A6' + '96 FF C1 A4 8F FF F7 F1 EB FF F8 F2 ED FF F9 F3' + 'EE FF F9 F2 EE FF FB F7 F3 FF E7 DB D2 FF CA B3' + 'A2 FF CE B7 A4 FF CA B3 A7 FF EF E6 E0 FF FA F5' + 'F1 FF EA DE D3 FF C0 AC 9E FF D8 C8 B8 FF D9 C9' + 'BB FF EA E3 DC FF C6 AF A4 FF F7 F2 EE FF F9 F3' + 'EE FF F9 F2 EE FF F9 F3 EE FF F9 F3 EE FF F9 F3' + 'EE FF F8 F2 EE FF FB F4 F0 FF CB C7 C6 FF 88 88' + '88 FF 86 86 86 FF 84 84 84 FF 81 81 81 FF 7F 7F' + '7F FF 7B 7B 7B FF 88 88 88 F5 34 34 34 9F 00 00' + '00 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 FA FA FA 36 E3 E3 E3 EE BC BC BC FF CD CC' + 'CB FF F8 F2 EE FF FA F1 EB FF F8 F0 EA FF ED E6' + 'E1 FF EE E6 E0 FF F7 EF E9 FF F7 EE E7 FF F6 EF' + 'E7 FF F6 EF E7 FF F6 EF E7 FF F7 EF E8 FF F7 EF' + 'E8 FF F7 EF E8 FF F6 EE E8 FF F7 EF E8 FF F7 EF' + 'E9 FF F7 EF E9 FF F5 EE E7 FF EB E3 DA FF F4 EC' + 'E5 FF F7 EF E7 FF F8 F1 EB FF F8 EF E9 FF F7 EF' + 'E7 FF F6 EE E7 FF F6 EE E7 FF F6 EE E7 FF F7 EF' + 'E8 FF FB F5 EF FF EC EA E6 FF 9F 9E 9D FF 8A 8A' + '8A FF 86 86 86 FF 83 83 83 FF 82 82 82 FF 80 80' + '80 FF 7C 7C 7C FF 89 89 89 F6 37 37 37 A1 00 00' + '00 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 F2 F2 F2 3B E2 E2 E2 F3 C0 C0 C0 FF BB BB' + 'BB FF C8 C8 C9 FF E5 E5 E5 FF EB EC EC FF EA EB' + 'EB FF EA EB EB FF EA EA EA FF EA EA EA FF E9 E9' + 'E9 FF E9 E9 E9 FF E9 E9 E9 FF E9 E9 E9 FF E8 E8' + 'E8 FF E8 E8 E8 FF E8 E8 E8 FF E7 E7 E7 FF E7 E7' + 'E7 FF E7 E7 E7 FF E7 E7 E7 FF E6 E6 E6 FF E5 E6' + 'E6 FF E5 E5 E5 FF E5 E5 E5 FF E4 E4 E4 FF E4 E4' + 'E4 FF E3 E3 E3 FF E3 E3 E3 FF E3 E3 E3 FF E3 E3' + 'E3 FF CD CE CF FF 99 9A 9A FF 8B 8B 8B FF 8B 8B' + '8B FF 8A 8A 8A FF 88 88 88 FF 85 85 85 FF 82 82' + '82 FF 80 80 80 FF 8B 8B 8B F6 38 38 38 A3 00 00' + '00 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 EE EE EE 3F E3 E3 E3 F6 BF BF BF FF BB BB' + 'BB FF B9 B9 B9 FF B7 B7 B7 FF B5 B5 B5 FF B3 B3' + 'B3 FF B1 B1 B1 FF AF AF AF FF AD AD AD FF AB AB' + 'AB FF AA AA AA FF A9 A9 A9 FF A8 A8 A8 FF A7 A7' + 'A7 FF A5 A5 A5 FF A4 A4 A4 FF A3 A3 A3 FF A1 A1' + 'A1 FF A0 A0 A0 FF A0 A0 A0 FF 9F 9F 9F FF 9D 9D' + '9D FF 9B 9B 9B FF 99 99 99 FF 97 97 97 FF 95 95' + '95 FF 93 93 93 FF 92 92 92 FF 92 92 92 FF 92 92' + '92 FF 90 90 90 FF 8E 8E 8E FF 8D 8D 8D FF 8B 8B' + '8B FF 89 89 89 FF 87 87 87 FF 84 84 84 FF 82 82' + '82 FF 7F 7F 7F FF 8D 8D 8D F7 3D 3D 3D A5 00 00' + '00 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 EB EB EB 43 E3 E3 E3 FA C2 C2 C2 FF BF BF' + 'BF FF BC BC BC FF B9 B9 B9 FF B7 B7 B7 FF B3 B3' + 'B3 FF B1 B1 B1 FF AE AE AE FF AB AB AB FF AB AB' + 'AB FF A9 A9 A9 FF A8 A8 A8 FF A7 A7 A7 FF A6 A6' + 'A6 FF A4 A4 A4 FF A3 A3 A3 FF A4 A4 A4 FF A1 A1' + 'A1 FF A1 A1 A1 FF 9F 9F 9F FF 9F 9F 9F FF 9D 9D' + '9D FF 9B 9B 9B FF 98 98 98 FF 99 99 99 FF 97 97' + '97 FF 95 95 95 FF 95 95 95 FF 94 94 94 FF 92 92' + '92 FF 92 92 92 FF 90 90 90 FF 8E 8E 8E FF 8C 8C' + '8C FF 8A 8A 8A FF 87 87 87 FF 85 85 85 FF 83 83' + '83 FF 81 81 81 FF 8F 8F 8F F7 43 43 43 A6 00 00' + '00 27 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 F4 F4 F4 48 E3 E3 E3 FE C5 C5 C5 FF C1 C1' + 'C1 FF C0 C0 C0 FF BC BC BC FF BB BB BB FF B7 B7' + 'B7 FF B5 B5 B5 FF B0 B0 B0 FF B0 B0 B0 FF AD AD' + 'AD FF AB AB AB FF A9 A9 A9 FF A8 A8 A8 FF A6 A6' + 'A6 FF A5 A5 A5 FF A6 A6 A6 FF A4 A4 A4 FF A3 A3' + 'A3 FF A2 A2 A2 FF 9E 9E 9E FF 9D 9D 9D FF 9D 9D' + '9D FF 9B 9B 9B FF 9A 9A 9A FF 98 98 98 FF 96 96' + '96 FF 95 95 95 FF 95 95 95 FF 93 93 93 FF 92 92' + '92 FF 92 92 92 FF 90 90 90 FF 90 90 90 FF 8E 8E' + '8E FF 8B 8B 8B FF 8B 8B 8B FF 89 89 89 FF 86 86' + '86 FF 83 83 83 FF 93 93 93 F8 44 44 44 A8 00 00' + '00 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 F8 F8 F8 4A E3 E3 E3 FF C5 C5 C5 FF C3 C3' + 'C3 FF C0 C0 C0 FF BD BD BD FF BA BA BA FF B7 B7' + 'B7 FF B5 B5 B5 FF B3 B3 B3 FF B1 B1 B1 FF AE AE' + 'AE FF AC AC AC FF A9 A9 A9 FF A7 A7 A7 FF A6 A6' + 'A6 FF A5 A5 A5 FF A6 A6 A6 FF A5 A5 A5 FF A3 A3' + 'A3 FF A1 A1 A1 FF 9E 9E 9E FF 9E 9E 9E FF 9F 9F' + '9F FF 9C 9C 9C FF 9A 9A 9A FF 99 99 99 FF 98 98' + '98 FF 96 96 96 FF 94 94 94 FF 94 94 94 FF 93 93' + '93 FF 92 92 92 FF 90 90 90 FF 90 90 90 FF 8D 8D' + '8D FF 8D 8D 8D FF 8A 8A 8A FF 8A 8A 8A FF 87 87' + '87 FF 85 85 85 FF 93 93 93 F8 48 48 48 A9 00 00' + '00 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 F8 F8 F8 4B E3 E3 E3 FF C7 C7 C7 FF C5 C5' + 'C5 FF C2 C2 C2 FF BD BD BD FF B9 B9 B9 FF B7 B7' + 'B7 FF B5 B5 B5 FF B4 B4 B4 FF B1 B1 B1 FF AE AE' + 'AE FF AB AB AB FF A9 A9 A9 FF A6 A6 A6 FF A7 A7' + 'A7 FF A5 A5 A5 FF A5 A5 A5 FF A3 A3 A3 FF A3 A3' + 'A3 FF A1 A1 A1 FF A0 A0 A0 FF 9D 9D 9D FF 9C 9C' + '9C FF 9D 9D 9D FF 9B 9B 9B FF 99 99 99 FF 98 98' + '98 FF 98 98 98 FF 95 95 95 FF 94 94 94 FF 94 94' + '94 FF 92 92 92 FF 91 91 91 FF 8F 8F 8F FF 8F 8F' + '8F FF 8D 8D 8D FF 8B 8B 8B FF 8A 8A 8A FF 88 88' + '88 FF 85 85 85 FF 94 94 94 F9 4D 4D 4D AB 00 00' + '00 29 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 EF EF EF 51 E4 E4 E4 FF C7 C7 C7 FF C7 C7' + 'C7 FF C3 C3 C3 FF C0 C0 C0 FF BD BD BD FF BA BA' + 'BA FF B7 B7 B7 FF B4 B4 B4 FF B0 B0 B0 FF AE AE' + 'AE FF AB AB AB FF A9 A9 A9 FF A8 A8 A8 FF A6 A6' + 'A6 FF A6 A6 A6 FF A4 A4 A4 FF A1 A1 A1 FF A3 A3' + 'A3 FF A2 A2 A2 FF A1 A1 A1 FF 9F 9F 9F FF 9E 9E' + '9E FF 9F 9F 9F FF 9A 9A 9A FF 9B 9B 9B FF 98 98' + '98 FF 99 99 99 FF 97 97 97 FF 96 96 96 FF 94 94' + '94 FF 93 93 93 FF 93 93 93 FF 92 92 92 FF 90 90' + '90 FF 8E 8E 8E FF 8B 8B 8B FF 89 89 89 FF 89 89' + '89 FF 87 87 87 FF 94 94 94 F9 4E 4E 4E AC 00 00' + '00 2A 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 EB EB EB 5C E4 E4 E4 FF C9 C9 C9 FF C8 C8' + 'C8 FF C5 C5 C5 FF C3 C3 C3 FF BE BE BE FF BB BB' + 'BB FF B9 B9 B9 FF B5 B5 B5 FF B3 B3 B3 FF B0 B0' + 'B0 FF AD AD AD FF AB AB AB FF A9 A9 A9 FF A7 A7' + 'A7 FF A6 A6 A6 FF A4 A4 A4 FF A2 A2 A2 FF A2 A2' + 'A2 FF A1 A1 A1 FF A0 A0 A0 FF 9F 9F 9F FF 9F 9F' + '9F FF 9F 9F 9F FF 9D 9D 9D FF 9C 9C 9C FF 99 99' + '99 FF 9A 9A 9A FF 99 99 99 FF 98 98 98 FF 95 95' + '95 FF 94 94 94 FF 94 94 94 FF 93 93 93 FF 91 91' + '91 FF 90 90 90 FF 8F 8F 8F FF 8C 8C 8C FF 8A 8A' + '8A FF 88 88 88 FF 95 96 95 FA 52 52 52 AE 00 00' + '00 2A 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 E6 E6 E6 68 E4 E4 E4 FF CA CA CA FF C8 C8' + 'C8 FF C5 C5 C5 FF C2 C2 C2 FF BE BE BE FF BC BC' + 'BC FF B9 B9 B9 FF B5 B5 B5 FF B3 B3 B3 FF B0 B0' + 'B0 FF AC AC AC FF AB AB AB FF AA AA AA FF A9 A9' + 'A9 FF A8 A8 A8 FF A6 A6 A6 FF A4 A4 A4 FF A3 A3' + 'A3 FF A2 A2 A2 FF A1 A1 A1 FF 9F 9F 9F FF 9E 9E' + '9E FF 9D 9D 9D FF 9E 9E 9E FF 9C 9C 9C FF 9B 9B' + '9B FF 9B 9B 9B FF 99 99 99 FF 99 99 99 FF 98 98' + '98 FF 96 96 96 FF 94 94 94 FF 94 94 94 FF 92 92' + '92 FF 92 92 92 FF 91 91 91 FF 8E 8E 8E FF 8B 8B' + '8B FF 88 88 88 FF 96 96 96 FA 57 57 57 AF 00 00' + '00 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 E2 E2 E2 76 E5 E5 E5 FF CC CC CC FF CB CB' + 'CB FF C7 C7 C7 FF C4 C4 C4 FF C1 C1 C1 FF BE BE' + 'BE FF BB BB BB FF B8 B8 B8 FF B5 B5 B5 FF B2 B2' + 'B2 FF AF AF AF FF AC AC AC FF A9 A9 A9 FF A8 A8' + 'A8 FF A5 A5 A5 FF A5 A5 A5 FF A4 A4 A4 FF A3 A3' + 'A3 FF A3 A3 A3 FF A2 A2 A2 FF A1 A1 A1 FF A0 A0' + 'A0 FF 9E 9E 9E FF 9E 9E 9E FF 9D 9D 9D FF 9C 9C' + '9C FF 9C 9C 9C FF 9A 9A 9A FF 9A 9A 9A FF 97 97' + '97 FF 96 96 96 FF 96 96 96 FF 95 95 95 FF 93 93' + '93 FF 92 92 92 FF 90 90 90 FF 8D 8D 8D FF 8B 8B' + '8B FF 88 88 88 FF 96 96 96 FA 58 58 58 B0 00 00' + '00 2C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 E3 E3 E3 81 E5 E5 E5 FF CC CC CC FF CC CC' + 'CC FF C8 C8 C8 FF C6 C6 C6 FF C1 C1 C1 FF C0 C0' + 'C0 FF BC BC BC FF B9 B9 B9 FF B5 B5 B5 FF B2 B2' + 'B2 FF AF AF AF FF AC AC AC FF A9 A9 A9 FF A7 A7' + 'A7 FF A5 A5 A5 FF A4 A4 A4 FF A3 A3 A3 FF A2 A2' + 'A2 FF A1 A1 A1 FF A1 A1 A1 FF A0 A0 A0 FF A0 A0' + 'A0 FF 9E 9E 9E FF 9D 9D 9D FF 9E 9E 9E FF 9E 9E' + '9E FF 9D 9D 9D FF 9B 9B 9B FF 99 99 99 FF 98 98' + '98 FF 98 98 98 FF 97 97 97 FF 95 95 95 FF 94 94' + '94 FF 92 92 92 FF 90 90 90 FF 8E 8E 8E FF 8B 8B' + '8B FF 89 89 89 FF 95 95 95 FB 59 59 59 B1 00 00' + '00 2D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 E3 E3 E3 8C E7 E7 E7 FF CD CD CD FF CD CD' + 'CD FF C9 C9 C9 FF C6 C6 C6 FF C5 C5 C5 FF C1 C1' + 'C1 FF BE BE BE FF BA BA BA FF B5 B5 B5 FF B3 B3' + 'B3 FF B0 B0 B0 FF AE AE AE FF AB AB AB FF A8 A8' + 'A8 FF A6 A6 A6 FF A4 A4 A4 FF A2 A2 A2 FF A3 A3' + 'A3 FF A2 A2 A2 FF A0 A0 A0 FF 9E 9E 9E FF 9F 9F' + '9F FF 9F 9F 9F FF 9E 9E 9E FF 9D 9D 9D FF 9D 9D' + '9D FF 9C 9C 9C FF 9C 9C 9C FF 9A 9A 9A FF 9A 9A' + '9A FF 99 99 99 FF 97 97 97 FF 95 95 95 FF 94 94' + '94 FF 92 92 92 FF 90 90 90 FF 8E 8E 8E FF 8B 8B' + '8B FF 88 88 88 FF 96 96 96 FB 5B 5B 5B B2 00 00' + '00 2D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 E9 E9 E9 81 E7 E7 E7 FE CF CF CF FF CE CE' + 'CE FF CB CB CB FF C8 C8 C8 FF C3 C3 C3 FF C1 C1' + 'C1 FF BE BE BE FF BB BB BC FF B7 B7 B7 FF B4 B4' + 'B4 FF B2 B2 B2 FF AE AD AD FF AB AB AB FF A8 A8' + 'A8 FF A6 A6 A6 FF A5 A5 A5 FF A4 A4 A4 FF A2 A2' + 'A2 FF A0 A0 A0 FF A0 A0 A0 FF A0 A0 A0 FF 9F 9F' + '9F FF 9D 9D 9D FF 9F 9F 9F FF 9D 9D 9D FF 9E 9E' + '9E FF 9D 9D 9D FF 9B 9B 9B FF 9C 9C 9C FF 99 99' + '99 FF 98 98 98 FF 97 97 97 FF 95 95 95 FF 94 94' + '94 FF 92 92 92 FF 8E 8E 8E FF 8E 8E 8E FF 8A 8A' + '8A FF 88 88 88 FF 93 93 93 FB 5D 5D 5D B2 00 00' + '00 2D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 EC EC EC 36 EE EE EE DD D8 D9 DD FF D1 D3' + 'D6 FF CF D1 D4 FF CC CE D2 FF CA CB CE FF C6 C8' + 'CB FF C3 C5 C8 FF BD BF C3 FF BA BC BF FF B6 B8' + 'BC FF B3 B5 B9 FF B0 B2 B5 FF AC AE B2 FF A9 AB' + 'AF FF A5 A6 AA FF A3 A6 A9 FF A3 A4 A7 FF A4 A6' + 'A9 FF A2 A3 A5 FF A0 A0 A0 FF 9F 9F 9F FF 9F 9F' + '9F FF 9F 9F 9F FF 9E 9E 9E FF 9D 9D 9D FF 9D 9D' + '9D FF 9B 9B 9B FF 9B 9B 9B FF 9B 9B 9B FF 9A 9A' + '9A FF 98 98 98 FF 97 97 97 FF 93 93 93 FF 93 93' + '93 FF 91 91 91 FF 8E 8E 8E FF 8C 8C 8C FF 88 88' + '88 FF 85 85 85 FF 93 93 94 FB 64 64 66 AA 00 00' + '00 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 F6 F6 E6 99 CB C4 B0 FF B0 A6' + '90 FF AF A5 8F FF AF A5 90 FF AA A0 8A FF A6 9C' + '86 FF A2 98 82 FF 9F 95 80 FF 99 8E 7A FF 95 8C' + '78 FF 94 8B 77 FF 91 88 76 FF 92 8A 78 FF 95 8D' + '7D FF 95 8E 7E FF 92 8B 7D FF 8E 88 7A FF 8F 89' + '7D FF 96 93 8D FF 9F A0 A3 FF 9E 9E 9E FF 9D 9D' + '9D FF 9D 9D 9D FF 9D 9D 9D FF 9B 9B 9B FF 9C 9C' + '9C FF 9B 9B 9B FF 9A 9A 9A FF 99 99 99 FF 97 97' + '97 FF 96 96 96 FF 94 94 94 FF 92 92 92 FF 8F 8F' + '8F FF 8E 8E 8E FF 8B 8B 8B FF 88 88 88 FF 84 84' + '84 FF 85 85 85 FF A4 A4 A4 F4 7C 7C 7C 83 00 00' + '00 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 F9 F9 F5 86 EC E3 CF FC DB CD' + 'B3 FF D9 CB B2 FF D6 C9 AF FF D4 C6 AC FF D2 C5' + 'AB FF D2 C5 AA FF CF C2 A7 FF CC BF A5 FF CA BC' + 'A2 FF C5 B8 9D FF BE B0 96 FF B8 AA 8F FF B3 A5' + '89 FF AD A0 84 FF AC 9E 82 FF AA 9C 7F FF A2 93' + '75 FF 9B 8A 69 FF A4 9F 97 FE B5 B6 B7 F7 BA BA' + 'BA F6 B9 B9 B9 F6 B7 B7 B7 F6 B7 B7 B7 F6 B7 B7' + 'B7 F6 B5 B5 B5 F7 B3 B3 B3 F7 B0 B0 B0 F7 AF AF' + 'AF F7 AD AD AD F7 AA AA AA F7 A6 A6 A6 F7 A3 A2' + 'A3 F8 A0 A0 A0 F8 9D 9D 9D F8 98 98 98 F8 96 96' + '96 F7 A8 A8 A8 F1 63 63 63 42 00 00 00 15 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 EF EB E7 40 FC FC EE D9 F0 E9' + 'D3 FE EF E6 C8 FF EE E3 C6 FF ED DF C3 FF EB DD' + 'C1 FF E8 D9 BD FF E3 D5 B8 FF E0 D2 B6 FF DC CD' + 'B2 FF D9 CB AF FF D6 C7 AB FF D2 C4 A8 FF CE BF' + 'A3 FF C9 BB 9F FF C5 B7 9B FF C3 B5 99 FF C0 B2' + '96 FF CE C3 A3 FF BA AD 91 AC AE B0 B5 72 C9 C9' + 'C9 6E CE CE CE 6E D2 D2 D2 6E D1 D1 D1 6F D3 D3' + 'D3 6F D7 D7 D7 6F D5 D5 D5 6F D5 D5 D5 6F D7 D7' + 'D7 6F CF CF CF 70 CB CB CB 72 CB CB CB 72 C5 C0' + 'C5 73 BD BD BD 74 B9 B9 B9 75 B5 B5 B5 73 B2 B2' + 'B2 71 BA BA BA 68 19 19 19 14 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 DE DE D6 46 FC FB' + 'E9 C6 FC FB E5 D4 FC FA E2 D4 FC F8 DD D4 FC F7' + 'DC D4 FB F6 D9 D4 FA F4 D4 D4 FA F2 D2 D4 F7 F0' + 'CE D4 F6 EF CC D4 F5 E8 C7 D4 F4 E5 C2 D4 F1 E2' + 'C0 D4 EF DC BE D4 EA DA BA D4 E8 D6 B5 D4 E6 D6' + 'B6 D4 DE D2 B1 BB A8 9E 84 32 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 E0 00 00 00 00 0F 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 0F 00 00 C0 00 00 00 00 1F 00 00 E0 00' + '01 FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 28 00' + '00 00 40 00 00 00 80 00 00 00 01 00 04 00 00 00' + '00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 88 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 70 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 87 78 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 8F 44 44 44 FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF F8 88 88 88' + '88 88 87 00 00 00 00 F8 88 FF FF FF FF 44 44 44' + '44 44 44 44 FF FF FF 44 44 44 44 44 FF 88 88 88' + '88 88 87 00 00 00 00 F8 88 FF 4F FF FF FF FF FF' + 'FF FF FF FF 44 44 44 4F FF FF FF FF FF 88 88 88' + '88 88 87 00 00 00 00 F8 8F FF F4 FF 4F 4F 44 44' + 'FF 44 FF F4 4F F4 FF 44 4F FF FF FF FF F8 88 88' + '88 88 87 00 00 00 00 F8 8F FF F4 F4 4F 44 4F 44' + '44 FF 4F FF 4F 44 44 F4 F4 4F FF FF FF F8 88 88' + '88 88 87 00 00 00 00 F8 8F FF FF 4F F4 FF FF F4' + 'FF 4F FF FF 4F FF FF 4F FF FF FF FF FF F8 88 88' + '88 88 87 00 00 00 00 F8 8F FF FF 44 FF FF FF FF' + '4F 4F FF FF 44 44 FF 4F FF FF FF FF FF F8 88 88' + '88 88 87 00 00 00 00 F8 88 FF FF 4F 4F FF FF FF' + '44 44 4F FF F4 FF FF 4F FF FF FF FF FF 88 88 88' + '88 88 87 00 00 00 00 F8 88 FF FF 44 4F FF FF FF' + 'FF FF FF FF F4 44 FF FF FF FF FF FF FF 88 88 88' + '88 88 87 00 00 00 00 F8 88 8F FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF F8 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 8F 77 77 77 77 77 77 77 77' + '77 77 77 77 77 78 88 88 88 88 88 88 88 88 88 88' + '88 88 87 00 00 00 00 0F 66 66 66 66 66 66 66 66' + '66 66 66 66 66 67 77 77 77 77 77 77 77 77 77 77' + '77 77 70 00 00 00 00 0F 66 66 66 66 66 66 66 66' + '66 66 66 66 66 68 88 88 88 88 88 88 88 88 88 88' + '88 88 00 00 00 00 00 08 FE 6E 6E 6E 6E 6E 6E 6E' + '6E 6E 6E 6E 6E 80 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 8F EF EF EF EF EF EF EF' + 'EF EF EF EF EF 80 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 08 88 88 88 88 88 88 88' + '88 88 88 88 88 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF C0 00' + '00 00 00 00 00 7F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F E0 00' + '00 00 00 00 00 7F E0 00 00 00 00 00 00 FF E0 00' + '00 01 FF FF FF FF F0 00 00 01 FF FF FF FF F8 00' + '00 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 40 00 00 00 80 00' + '00 00 01 00 08 00 00 00 00 00 00 10 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 78 3C' + '25 00 77 77 77 00 7A 7A 7A 00 7D 7D 7D 00 83 49' + '25 00 83 50 32 00 A7 7E 5E 00 97 6F 64 00 9F 75' + '62 00 9E 79 68 00 96 88 6B 00 8E 86 75 00 92 87' + '70 00 96 8A 72 00 91 88 76 00 95 8A 74 00 97 8C' + '77 00 99 8C 73 00 9C 8D 70 00 9C 8F 76 00 90 88' + '78 00 96 8C 78 00 A9 82 67 00 AB 84 69 00 A5 82' + '7A 00 B2 8E 74 00 A0 92 76 00 A1 94 7A 00 A3 96' + '7C 00 A4 97 7C 00 A6 98 7C 00 A8 9B 7F 00 B3 90' + '74 00 BA 99 7F 00 FF 00 FF 00 81 81 81 00 85 85' + '85 00 89 89 89 00 8D 8D 8D 00 91 91 91 00 95 95' + '95 00 9A 97 93 00 98 97 96 00 99 99 99 00 9D 9D' + '9D 00 AD 89 81 00 A9 9C 82 00 AB 9E 84 00 AD 90' + '88 00 B8 96 82 00 BB 9B 82 00 B2 96 8C 00 B5 9A' + '8A 00 BB 9C 89 00 AE A0 86 00 AE A2 88 00 B4 A5' + '87 00 B0 A3 89 00 B2 A5 8A 00 B4 A7 8D 00 BD A0' + '8A 00 B5 A8 8E 00 AD A2 91 00 A2 A0 9C 00 BA AC' + '92 00 BC AF 95 00 BE B2 98 00 9F 9F A0 00 9F A0' + 'A0 00 A1 A1 A1 00 A1 A2 A5 00 A4 A3 A4 00 A3 A4' + 'A7 00 A5 A5 A5 00 A3 A4 A8 00 A6 A7 A8 00 A7 A8' + 'A9 00 A7 A9 AC 00 A9 A9 A9 00 A8 AA AD 00 AB AC' + 'AC 00 AD AD AD 00 AB AD B0 00 AE AF B2 00 AF B0' + 'B1 00 B1 B1 B1 00 B1 B2 B5 00 B3 B4 B5 00 B5 B5' + 'B5 00 B6 B7 B8 00 B7 B8 B9 00 B9 B9 B9 00 BC BB' + 'BB 00 BD BC BB 00 B8 B9 BC 00 BB BC BE 00 BD BD' + 'BD 00 C2 A4 8C 00 C1 A4 93 00 C3 A8 92 00 C4 A9' + '92 00 C5 AB 95 00 C6 AD 96 00 C8 AA 91 00 C1 A5' + '99 00 C0 A9 9F 00 C8 AF 9A 00 C0 B2 98 00 C4 B6' + '9A 00 C4 B6 9C 00 CB B2 9C 00 C6 B8 9B 00 C6 B8' + '9D 00 C8 BA 9E 00 C6 AB A1 00 C7 AF A1 00 C5 AE' + 'A5 00 CC B5 A1 00 C7 BA A0 00 C8 BA A0 00 C9 BC' + 'A1 00 CD BF A3 00 CF B9 A6 00 CB BE A4 00 CC BF' + 'A4 00 CB B7 AC 00 CE B9 A9 00 D1 BA A4 00 D1 BC' + 'AA 00 C2 BD B5 00 D2 BE B3 00 D4 BE B8 00 CD C0' + 'A6 00 D1 C2 A6 00 D1 C3 A9 00 D2 C4 A9 00 D5 C7' + 'AA 00 D4 C0 AE 00 D3 C6 AC 00 D5 C7 AD 00 D8 C3' + 'AF 00 D6 C8 A9 00 D6 C8 AD 00 C8 C3 BF 00 D5 C2' + 'B1 00 D7 C4 B2 00 D7 C5 B5 00 D9 C7 B7 00 D9 CC' + 'B1 00 DC CE B1 00 DB CE B4 00 D7 C4 B9 00 D8 C7' + 'B8 00 D3 CA B9 00 D4 CB BA 00 D6 CE BD 00 D9 C8' + 'B9 00 DC CB BD 00 D9 CF BF 00 DE D0 B6 00 E0 D3' + 'B9 00 E2 D4 B9 00 E4 D6 B9 00 E6 D8 BD 00 E9 DC' + 'BF 00 C1 C1 C1 00 C4 C3 C2 00 C3 C4 C5 00 C5 C5' + 'C5 00 C6 C7 C9 00 C9 C9 C9 00 CC CC CB 00 C9 CA' + 'CC 00 CD CD CD 00 DA C9 C1 00 DE CE C1 00 D9 CD' + 'C4 00 D1 CD C9 00 D4 CF CB 00 DA D2 C0 00 DB D4' + 'C3 00 DF D2 C6 00 DB D5 C5 00 DC D8 C6 00 D5 D1' + 'CD 00 D8 D3 CF 00 D9 D7 CD 00 DC DA CA 00 DD DB' + 'CD 00 DD DC CE 00 CD CE D0 00 CF D0 D0 00 D1 D1' + 'D1 00 D4 D2 D0 00 D2 D3 D6 00 D5 D5 D5 00 DA D6' + 'D2 00 DD DD D2 00 DC D8 D4 00 DA DA DA 00 DD DD' + 'DD 00 E0 D1 C4 00 EB DE C2 00 E2 D4 CA 00 E6 D8' + 'CC 00 E6 DC D3 00 E8 DE D5 00 EE E3 C7 00 EE E5' + 'C8 00 F0 E8 CB 00 E1 E0 D7 00 EC E0 D4 00 EA E1' + 'D9 00 EB E3 DD 00 ED E6 DF 00 F4 EB D0 00 F6 EE' + 'D6 00 F0 E5 DD 00 F6 F3 D8 00 E2 E2 E2 00 E5 E5' + 'E5 00 EB E4 E1 00 EE E7 E0 00 EF E8 E2 00 EC E8' + 'E4 00 E9 E9 E9 00 ED ED ED 00 F2 E9 E0 00 F4 EB' + 'E2 00 F1 ED E0 00 F1 EA E5 00 F1 EC E6 00 F6 ED' + 'E6 00 F0 EB E8 00 F2 ED E9 00 F6 EE E8 00 F2 EF' + 'ED 00 F4 EF EC 00 F8 EF E8 00 F5 F0 EC 00 F8 F0' + 'EA 00 F9 F2 ED 00 FA F4 EE 00 F2 F2 F1 00 F4 F3' + 'F2 00 F6 F4 F1 00 F4 F4 F4 00 F9 F5 F0 00 FC F6' + 'F2 00 F8 F6 F4 00 FB F8 F5 00 FA F9 F8 00 FC FA' + 'F8 00 FE FD FB 00 FE FE FE 00 00 00 00 00 FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + '60 54 28 26 26 25 26 26 25 26 26 25 26 26 25 26' + '26 25 26 26 25 26 26 25 26 26 25 26 26 25 26 26' + '25 26 26 25 26 26 25 26 26 25 26 26 25 26 26 25' + '26 26 25 26 26 25 28 FF FF FF FF FF FF FF FF FF' + 'A6 C3 60 5C 5B 5B 59 58 58 57 55 58 55 54 55 50' + '50 51 51 50 51 4E 49 4E 4B 49 49 49 49 47 45 45' + '45 45 43 2C 2C 2C 2B 2A 2A 2A 28 28 27 27 27 2A' + '27 27 27 27 27 2B 23 45 FF FF FF FF FF FF FF FF' + 'A8 DC A9 A6 A5 A5 5F 5B 5A 58 58 57 57 56 54 54' + '51 50 50 4E 4C 4B 49 49 46 43 44 3F 43 2C 2C 2C' + '2B 2B 2B 28 28 28 27 27 26 26 26 26 25 25 25 25' + '25 24 25 24 24 2B 03 43 FF FF FF FF FF FF FF FF' + 'A8 C8 59 58 58 AA C4 C5 C6 C6 C6 C6 C4 C6 C4 C4' + 'C4 C4 B9 B9 B9 B9 B9 B9 B8 B8 B8 C1 B8 B8 B8 B8' + 'B8 B8 B2 B2 B1 B1 B1 B1 B1 8F 45 24 23 23 03 23' + '23 03 03 03 03 27 02 2C FF FF FF FF FF FF FF FF' + 'A7 C7 58 5B DB FD FE FE FE FE FE FE FE FE FE FE' + 'FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE' + 'FE FE FE FE FE FE FE FE FE FE FC 5F 24 23 03 23' + '03 03 03 03 02 27 02 2C FF FF FF FF FF FF FF FF' + 'A6 C3 58 C1 F2 83 61 64 6A 6E 6E 8C D3 F2 F2 F2' + 'F2 F0 EE EB E8 E8 E8 E8 E8 E8 E8 E8 E8 E8 E8 E8' + 'E8 E8 E8 E8 E8 EE F0 F2 F2 E4 E4 F1 2C 23 23 03' + '23 03 03 03 03 26 02 2C FF FF FF FF FF FF FF FF' + 'A5 C3 5B F3 FE FE FE FD F2 F2 F7 EA B5 75 66 66' + '75 80 7E 75 6A 6A 6E 80 B3 CE E6 EF F5 EF EA D4' + 'B5 89 6A 66 6A 80 84 6E 75 97 F9 FE C3 23 23 23' + '23 03 03 03 02 26 01 2B FF FF FF FF FF FF FF FF' + 'A5 C0 A6 F7 EB F0 34 F1 EE EB E8 EB EF F2 F8 F8' + 'F8 F2 FD CC CD F8 FA E8 D3 AF 9C 89 89 91 92 AF' + 'CC E3 F8 FC FC F8 F8 F8 F2 F1 F0 EE DF 26 23 03' + '23 03 23 02 03 25 01 2C FF FF FF FF FF FF FF FF' + 'A5 AB AA FA F7 FA 9D 68 DE 65 05 19 04 09 6A 32' + '32 32 B5 FD AF 61 CB F8 F9 7D CC 64 E9 97 68 AF' + '62 63 2D 91 E8 FA F8 F7 F7 F5 F7 F7 EC 28 23 23' + '23 03 03 03 02 26 02 2A FF FF FF FF FF FF FF FF' + 'A5 AA AD FC FC FC F9 00 21 CE 89 06 9C 69 17 82' + '16 90 75 9C ED CB 63 FC FC E7 35 66 61 7F 6E 20' + '61 30 E6 CD 7A 93 FC FC FC FA FC FA F4 2A 24 23' + '23 03 23 02 03 25 02 2B FF FF FF FF FF FF FF FF' + 'A5 AA AA F2 F0 E8 F2 83 72 F8 F7 B5 D9 F8 F1 AF' + '7F 67 F8 7A 19 CB F2 EE F0 91 20 EF CB 6E F1 90' + '6E 7D E6 EE F0 F0 EE EB E8 EE E8 F0 E9 2A 24 24' + '23 03 03 03 03 25 02 2B FF FF FF FF FF FF FF FF' + 'AA AA A8 FE FE FE F9 FB 20 91 FA FE FE FE FE FE' + 'FC B5 91 FB 07 D5 FE FE FE DF 3C 90 75 80 92 F9' + '18 FE FE FE FE FE FE FE FE FE FE FE F3 28 24 24' + '23 23 23 03 03 26 02 28 FF FF FF FF FF FF FF FF' + 'C7 A8 5C EA F1 EB 91 33 63 7D 6E F0 E8 E8 E8 E8' + 'E8 F2 61 19 17 31 35 E4 EB E8 61 34 FD E7 66 EB' + '08 D4 E8 E8 E8 E8 E8 E8 E8 E8 E8 EE C6 25 25 24' + '24 23 23 03 03 26 02 28 FF FF FF FF FF FF FF FF' + 'C8 A8 58 DB FD FE FE E9 73 74 80 FD FE FE FE FE' + 'FE FE FD E6 DF FB DD F9 FE FE E7 B0 66 80 E7 D6' + 'E9 F9 FE FE FE FE FE FE FE FE FE FE 54 25 25 24' + '24 24 03 23 03 25 03 28 FF FF FF FF FF FF FF FF' + 'C8 A8 5A 5D E0 F1 EE EE F2 F8 F2 F0 EE E8 EB E8' + 'EB EB E8 EE E8 EE EB EE EE EB EB F1 F2 F2 F0 F0' + 'F0 EB EE E8 EB E8 EB E8 EB EE F1 B1 26 26 25 24' + '24 24 23 23 03 26 03 28 FF FF FF FF FF FF FF FF' + 'C8 A8 60 5C 60 DB F3 F4 F6 F6 F6 F6 F9 F6 F6 F6' + 'F6 F6 F6 F6 F4 F6 F4 F6 F3 F3 F4 F6 F3 F3 F4 F3' + 'F3 F3 F3 F6 F9 FB F6 FB E2 E1 60 27 25 26 25 25' + '25 24 24 23 23 27 03 27 FF FF FF FF FF FF FF FF' + 'C8 A8 5C 60 5F 5B 5B 58 58 57 55 54 51 51 50 50' + '4E 4E 4E 49 49 49 49 47 45 45 44 45 44 2C 2C 2C' + '2B 2B 28 2A 27 27 27 27 28 27 27 26 26 26 25 25' + '25 24 23 23 23 27 03 28 FF FF FF FF FF FF FF FF' + 'C8 A8 A5 5C 60 5B 5B 59 58 58 55 55 51 51 51 50' + '4E 4E 4E 4E 49 49 49 49 45 47 45 45 45 2C 2C 2C' + '2B 2B 2A 28 2A 28 28 28 27 27 27 26 27 26 25 25' + '25 24 24 24 23 27 23 27 FF FF FF FF FF FF FF FF' + 'C8 AA A5 A5 A5 5C 5C 5A 58 58 55 54 51 50 50 4E' + '4E 4E 49 4B 49 49 49 49 45 45 45 45 44 44 2C 2B' + '2B 2B 2B 2B 28 28 28 28 28 27 28 27 26 26 26 26' + '25 24 24 24 24 26 24 27 FF FF FF FF FF FF FF FF' + 'DC AB A8 A5 A6 60 60 5C 5B 59 58 55 54 51 51 50' + '4E 4E 4E 4E 49 49 4B 47 47 47 45 44 2C 2C 43 2B' + '2C 2B 2B 2A 28 2B 28 28 27 28 27 27 27 27 26 25' + '26 25 25 24 24 27 24 27 FF FF FF FF FF FF FF FF' + 'E1 AB A7 A8 A5 A5 60 5F 5B 5B 58 55 57 55 51 51' + '50 4E 4E 49 49 49 49 49 49 47 45 2C 3F 44 43 2C' + '2C 2B 2A 2A 28 28 28 28 28 27 27 27 27 26 26 26' + '26 25 25 24 25 27 24 27 FF FF FF FF FF FF FF FF' + 'E2 AB AA A8 A7 60 60 5C 5B 5A 58 58 55 55 51 51' + '50 4B 4E 4E 49 4E 49 49 47 45 45 45 44 3F 2C 2C' + '2C 2B 2B 2C 2B 28 28 28 28 28 27 27 27 27 26 26' + '25 26 25 25 24 27 25 26 FF FF FF FF FF FF FF FF' + 'F3 AC AA A8 A8 A6 5F 5C 5B 59 58 58 55 57 51 51' + '4E 4E 49 4E 49 49 49 47 49 47 44 3F 43 2C 2C 2C' + '2C 2B 2C 28 2B 2A 28 28 28 28 27 27 27 27 26 26' + '26 26 25 24 25 27 25 26 FF FF FF FF FF FF FF FF' + 'F3 AD A9 AA A6 A6 A5 5F 60 5B 59 58 55 55 51 51' + '4E 4E 4E 4E 49 49 49 45 49 45 45 45 45 2C 44 43' + '2B 2C 2B 2B 2B 2B 2A 28 28 28 28 28 27 27 27 26' + '25 25 25 25 25 28 25 26 FF FF FF FF FF FF FF FF' + 'E2 AD AB AA A8 A8 A5 A5 5C 5B 5B 58 58 55 55 51' + '4E 4E 4E 49 4E 49 49 47 47 45 47 45 3F 44 44 43' + '2C 2C 2C 2B 2B 2B 2B 2B 2A 28 28 27 28 27 27 27' + '27 26 26 25 25 28 25 26 FF FF FF FF FF FF FF FF' + 'F3 AD AB AC A8 A8 A8 A5 60 5C 5B 58 58 58 55 54' + '51 4E 50 4E 4E 47 49 47 49 47 45 3F 45 43 43 2C' + '44 2C 2C 2B 2C 2B 2B 2B 2A 2A 28 28 28 27 27 27' + '27 26 26 25 25 28 26 26 FF FF FF FF FF FF FF FF' + 'E2 AD AD AA AA A8 A5 A5 5D 5F 5B 59 58 55 54 51' + '51 51 4E 4E 4E 4E 49 47 49 47 45 47 44 44 3F 2C' + '44 2C 2C 2C 2B 2C 2B 2B 2B 2A 28 28 28 28 27 28' + '27 27 26 26 25 28 27 26 FF FF FF FF FF FF FF FF' + 'E1 BF AD AD AA A8 A8 A5 A5 60 5C 5B 58 58 55 55' + '51 51 4E 4E 49 49 49 49 49 49 45 47 44 45 3F 44' + '44 2C 43 2C 2C 2B 2C 2B 2B 28 2A 2A 28 28 28 27' + '27 26 26 25 25 28 27 26 FF FF FF FF FF FF FF FF' + 'E2 BF AD AD AA AA A8 A5 A5 A5 5B 5B 5B 58 55 55' + '51 50 4E 4E 49 49 49 49 47 47 45 45 45 45 45 44' + '2C 43 2C 2C 2C 2C 2C 2B 2B 2B 2B 2B 28 28 27 27' + '27 27 26 26 25 27 26 26 FF FF FF FF FF FF FF FF' + 'E1 BF BF AD AB AA A8 A6 A5 A5 60 5C 58 58 55 55' + '51 51 50 4E 49 49 47 47 47 47 45 45 44 3F 44 43' + '2C 44 43 3F 43 2C 2B 2B 2B 2B 2B 2B 28 2A 28 27' + '27 27 26 25 25 28 26 26 FF FF FF FF FF FF FF FF' + 'E1 C0 BF BF AA AA A9 A8 A6 A5 60 5C 5A 58 57 55' + '51 51 50 4E 4E 49 47 47 47 47 47 45 44 44 3F 44' + '44 43 2C 2C 2C 43 2C 2B 2C 2B 2B 2A 28 28 28 27' + '27 26 26 26 25 27 27 26 FF FF FF FF FF FF FF FF' + 'E2 C0 C0 BF AD AB AA A6 A6 A5 60 60 5B 58 58 57' + '51 51 51 4E 49 49 49 49 47 45 45 45 3F 45 43 2C' + '44 43 2C 44 2C 2C 2C 2C 2B 2B 2B 2A 2A 28 28 27' + '27 27 25 26 25 26 27 26 FF FF FF FF FF FF FF FF' + 'E2 C7 C0 BF AD AB AA A9 A8 A6 A5 5F 5B 5A 58 58' + '55 51 50 4E 4E 49 49 47 49 45 45 45 45 45 44 44' + '3F 43 2C 2C 2C 2C 2C 2B 2C 2B 2B 2B 28 28 28 27' + '26 27 26 25 24 26 27 26 FF FF FF FF FF FF FF FF' + 'C0 E1 C2 C0 C0 BE BE AC A9 A7 A5 5F 5E 5A 56 56' + '53 53 4F 4E 4A 4A 46 48 48 4B 46 44 3F 44 44 44' + '43 2C 43 2C 2C 2B 2C 2C 2B 2B 2A 28 28 27 27 27' + '26 26 25 25 24 26 2B 2B FF FF FF FF FF FF FF FF' + 'FF D1 42 3A 39 39 37 2F 2E 1D 1C 1B 13 11 10 0D' + '0C 0C 0D 15 10 10 0E 0B 0B 14 29 46 45 44 45 43' + '2C 43 2C 2C 2C 2C 2C 2B 2B 2A 2A 28 28 27 27 26' + '26 25 25 24 24 2B 46 FF FF FF FF FF FF FF FF FF' + 'FF DA 9F 8E 8E 8A 86 84 84 84 7C 7B 77 70 6D 6B' + '40 3D 3A 36 2F 2E 1F 1E 1A 12 0A 46 50 50 50 4E' + '4E 49 4B 49 49 49 45 45 43 2C 2C 2B 2A 28 28 27' + '27 25 26 25 28 2B FF FF FF FF FF FF FF FF FF FF' + 'FF D2 D7 A3 A1 A0 9F 9F 96 94 8B 8A 86 84 7B 78' + '76 6D 42 41 40 3D 3D 3A 39 36 38 3E 56 FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF E5 D8 D1 D0 D0 CA CA A4 A4 A1 A2 9F 95 94' + '94 88 87 85 79 78 71 6F 6C 6F 8D 3F 5B FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF BA C5 C5 C5 BD BD BD BC BC BB BB B7 B7' + 'B6 B4 B3 B3 9E 9E 9B 99 9A 9A 81 5C FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF C0 00 00 00 00 00 00 7F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F E0 00 00 00 00 00 00 7F E0 00' + '00 00 00 00 00 FF E0 00 00 01 FF FF FF FF F0 00' + '00 01 FF FF FF FF F8 00 00 03 FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 28 00' + '00 00 40 00 00 00 80 00 00 00 01 00 20 00 00 00' + '00 00 00 42 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 16 00 00 00 39 00 00 00 44 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 45 00 00 00 45 00 00' + '00 45 00 00 00 45 00 00 00 3A 00 00 00 17 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DC DC' + 'DC 16 DF DF DF AC BE BE BE C3 B9 B9 B9 C9 B7 B7' + 'B7 C9 B6 B6 B6 C9 B5 B5 B5 C9 B4 B4 B4 C9 B1 B1' + 'B1 C9 B0 B0 B0 C9 AF AF AF C9 AF AF AF C9 AD AD' + 'AD C9 AC AC AC C9 AA AA AA C9 A8 A8 A8 C9 A7 A7' + 'A7 C9 A7 A7 A7 C9 A8 A8 A8 C9 A6 A6 A6 C9 A6 A6' + 'A6 C9 A2 A2 A2 C9 A1 A1 A1 C9 A1 A1 A1 C9 9F 9F' + '9F C9 9F 9F 9F C9 9E 9E 9E C9 9E 9E 9E C9 9D 9D' + '9D C9 9A 9A 9A C9 9A 9A 9A C9 99 99 99 C9 99 99' + '99 C9 98 98 98 C9 96 96 96 C9 94 94 94 C9 93 93' + '93 C9 91 91 91 C9 90 90 90 C9 8C 8C 8C C9 8C 8C' + '8C C9 8B 8B 8B C9 8A 8A 8A C9 8A 8A 8A C9 86 86' + '86 C9 86 86 86 C9 86 86 86 C9 89 89 89 C9 87 87' + '87 C9 85 85 85 C9 83 83 83 C9 85 85 85 C9 83 83' + '83 C9 8E 8E 8E C9 36 36 36 76 00 00 00 29 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 F0' + 'F0 22 E5 E5 E5 FE C5 C5 C5 FF C4 C4 C4 FF C1 C2' + 'C2 FF C0 C0 C0 FF BB BC BD FF B9 BA BB FF B6 B7' + 'B8 FF B5 B6 B6 FF B4 B5 B5 FF B3 B4 B5 FF B2 B3' + 'B4 FF B1 B2 B3 FF B0 B0 B1 FF AF B0 B1 FF AC AD' + 'AE FF AB AC AD FF AA AB AB FF A8 A9 AA FF A7 A8' + 'A9 FF A6 A7 A8 FF A4 A5 A6 FF A4 A4 A5 FF A1 A2' + 'A3 FF 9F A0 A0 FF 9E 9F 9F FF A0 A0 A0 FF 9F 9F' + 'A0 FF 9E 9E 9E FF 9D 9D 9D FF 9C 9C 9C FF 9B 9B' + '9B FF 9A 9A 9A FF 98 98 98 FF 96 96 96 FF 95 95' + '95 FF 93 93 93 FF 91 91 92 FF 90 90 90 FF 8F 8F' + '8F FF 8D 8D 8D FF 8C 8C 8C FF 8C 8C 8C FF 89 89' + '89 FF 89 89 89 FF 88 88 88 FF 89 89 89 FF 88 88' + '88 FF 87 87 87 FF 87 87 87 FF 86 86 86 FF 86 86' + '86 FF 99 99 99 FF 45 45 45 88 00 00 00 2D 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DF DF' + 'DF 28 DD DD DD FF B7 B7 B7 FF B4 B4 B4 FF B6 B6' + 'B6 FF CB C9 C8 FF DC D7 D3 FF DD D9 D5 FF DD D9' + 'D5 FF DC D8 D4 FF DC D8 D4 FF DC D7 D3 FF DB D7' + 'D3 FF DB D7 D3 FF DB D6 D3 FF DB D6 D3 FF DA D5' + 'D2 FF D9 D5 D1 FF D9 D5 D1 FF D8 D4 D0 FF D8 D3' + 'CF FF D8 D3 CF FF D7 D2 CF FF D7 D2 CE FF D6 D2' + 'CE FF D5 D1 CD FF D5 D1 CD FF D6 D2 CE FF D6 D1' + 'CD FF D6 D1 CD FF D5 D0 CD FF D5 D0 CC FF D4 D0' + 'CC FF D4 D0 CC FF D4 CF CB FF D3 CF CB FF D3 CE' + 'CA FF D2 CD C9 FF D1 CD C9 FF D1 CC C8 FF D0 CC' + 'C8 FF C8 C3 BF FF A3 A3 A2 FF 85 85 85 FF 82 82' + '82 FF 81 81 81 FF 7E 7E 7E FF 80 80 80 FF 7F 7F' + '7F FF 7E 7E 7E FF 7D 7D 7D FF 7D 7D 7D FF 7C 7C' + '7C FF 90 90 90 FF 43 43 43 8B 00 00 00 2E 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D6 D6' + 'D6 32 DA DA DA FF B6 B6 B6 FF B8 B8 B8 FF E2 E2' + 'E2 FF FD FD FD FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FE FE FE FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FE FE' + 'FE FF FF FF FF FF F9 F9 F9 FF BC BC BC FF 83 83' + '83 FF 81 81 81 FF 7F 7F 7F FF 80 80 80 FF 7D 7D' + '7D FF 7E 7E 7E FF 7D 7D 7D FF 7C 7C 7C FF 7B 7B' + '7B FF 8F 8F 8F FF 44 44 44 8D 00 00 00 30 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 CD CD' + 'CD 3E D6 D6 D6 FF B5 B5 B5 FF D4 D2 D0 FF F9 F3' + 'ED FF D4 BE B8 FF C0 A2 8B FF C8 AA 91 FF CC B1' + '98 FF CC B1 98 FF CD B2 9A FF D8 C3 AF FF EC E0' + 'D4 FF FA F4 EF FF FB F5 EF FF FB F4 EF FF FA F3' + 'ED FF F9 F1 EB FF F7 EF E8 FF F6 EE E7 FF F6 EE' + 'E6 FF F6 EE E6 FF F6 EE E6 FF F6 EE E6 FF F6 EE' + 'E6 FF F6 EE E6 FF F6 EE E6 FF F6 ED E6 FF F6 ED' + 'E6 FF F6 ED E6 FF F6 ED E6 FF F6 ED E6 FF F6 ED' + 'E6 FF F6 ED E6 FF F6 ED E6 FF F6 ED E6 FF F6 EE' + 'E6 FF F7 EF E8 FF F9 F2 EB FF FA F3 EE FF FA F4' + 'EE FF F4 EB E3 FF F3 EA E1 FF F8 F4 ED FF 9F 9D' + '9C FF 81 81 81 FF 80 80 80 FF 7E 7E 7E FF 7F 7F' + '7F FF 7E 7E 7E FF 7D 7D 7D FF 7D 7D 7D FF 7C 7C' + '7C FF 8E 8E 8E FF 43 43 43 8F 00 00 00 31 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C7 C7' + 'C7 4A D3 D3 D3 FF BA BA BA FF F2 F2 F2 FF FF FF' + 'FF FF FF FF FF FF FE FE FE FF FB FA F9 FF F8 F5' + 'F2 FF F7 F4 F1 FF F7 F4 F1 FF F4 EF EC FF DF D2' + 'C6 FF CB B5 A0 FF C6 AD 97 FF C6 AE 97 FF CB B4' + 'A0 FF CF BB A9 FF D0 BC AA FF CC B4 A0 FF C8 AF' + '99 FF C8 AF 9A FF C9 B1 9B FF D1 BD AB FF DE CF' + 'C2 FF E8 DE D6 FF EF E9 E3 FF F5 F1 EE FF F8 F4' + 'F1 FF F4 F1 ED FF F2 EC E7 FF EB E2 DA FF E0 D3' + 'C7 FF D3 BF AD FF C9 B0 9A FF C7 AE 97 FF C8 AF' + '99 FF CF BA A7 FF D0 BC A9 FF CA B3 9E FF CB B4' + '9F FF D8 C7 B8 FF F9 F8 F6 FF FE FE FE FF D5 D5' + 'D5 FF 82 82 82 FF 81 81 81 FF 80 80 80 FF 80 80' + '80 FF 7F 7F 7F FF 7E 7E 7E FF 7C 7C 7C FF 7B 7B' + '7B FF 8C 8C 8C FF 42 42 42 91 00 00 00 32 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C6 C6' + 'C6 55 D0 D0 D0 FF C4 C3 C2 FF F8 F3 EF FF F7 EF' + 'E9 FF F9 F1 EB FF B4 98 88 FF FA F3 ED FF F8 EF' + 'E9 FF F6 EE E7 FF F5 EC E5 FF F6 EE E9 FF F7 F1' + 'EC FF FA F4 EF FF FC F6 F2 FF FD F7 F3 FF FB F6' + 'F1 FF F9 F4 EF FF FF FB F8 FF E5 D8 CC FF E6 DB' + 'D2 FF FB F9 F5 FF FD F8 F4 FF F4 EC E5 FF EA DE' + 'D3 FF E0 D0 C2 FF DA CA B9 FF D5 C1 AF FF D4 C0' + 'AE FF D5 C2 B0 FF D9 C7 B7 FF DF CF C1 FF E7 D9' + 'CD FF F2 E8 E0 FF FC F7 F2 FF FE FA F6 FF FE F9' + 'F6 FF FE F8 F4 FF FC F7 F2 FF FB F5 F0 FF FB F5' + 'EF FF F9 F2 ED FF F8 F0 E9 FF F8 F0 E9 FF ED E9' + 'E4 FF 8B 8B 8B FF 81 81 81 FF 7F 7F 7F FF 7F 7F' + '7F FF 7F 7F 7F FF 7E 7E 7E FF 7C 7C 7C FF 7C 7C' + '7C FF 8B 8B 8B FF 43 43 43 94 00 00 00 33 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C2 C2' + 'C2 61 CC CC CC FF CA C9 C8 FF FB F8 F5 FF F9 F5' + 'F0 FF FB F7 F3 FF DC CB BD FF C1 A5 99 FF EF E7' + 'E1 FF C5 AB 95 FF 83 50 32 FF B1 8C 74 FF 83 49' + '25 FF 9E 79 68 FF C9 AF 9C FF BC 9C 83 FF BB 9B' + '81 FF BC 9C 84 FF E0 D1 C7 FF FD FD FC FF DD CE' + 'C0 FF C3 A6 8C FF E2 D4 C7 FF FB F8 F5 FF FA F6' + 'F3 FF CB B7 AE FF E3 D7 CF FF C4 A8 91 FF F2 EB' + 'E7 FF D7 C4 B9 FF C2 A5 99 FF DA C9 C1 FF C1 A4' + '94 FF C4 A7 8E FF AD 89 81 FF D7 C5 B5 FF F2 EB' + 'E7 FF FB F8 F5 FF FA F6 F2 FF F9 F4 F0 FF F9 F4' + 'F0 FF F9 F4 F0 FF F9 F4 F0 FF F9 F5 F0 FF F2 EF' + 'ED FF 95 95 94 FF 82 82 82 FF 80 80 80 FF 80 80' + '80 FF 7E 7E 7E FF 7D 7D 7D FF 7C 7C 7C FF 7B 7B' + '7B FF 8B 8B 8B FF 49 49 49 96 00 00 00 34 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 BF BF' + 'BF 6D CB CB CB FF CC CC CB FF FD FB F9 FF FC F9' + 'F7 FF FC FA F8 FF F8 F5 F4 FF 78 3C 25 FF BA 99' + '7F FF E8 DF D6 FF D4 C0 AE FF A7 7E 5E FF D9 C8' + 'BA FF C0 A9 9F FF A8 82 6A FF D3 BF B1 FF A9 82' + '67 FF D5 C2 B0 FF CD B6 A1 FF D9 C8 B8 FF F3 EE' + 'EA FF E2 D6 CA FF C2 A7 8E FF FD FB FA FF FC FA' + 'F8 FF F1 EA E5 FF BC 9D 8A FF C6 AC 94 FF C1 A3' + '8B FF CF B9 A5 FF CB B3 9D FF B3 90 71 FF C2 A5' + '92 FF AD 90 88 FF EF E8 E3 FF E7 DD D4 FF D0 B9' + 'A8 FF D9 C7 B7 FF FD FA F8 FF FC F9 F7 FF FC F9' + 'F7 FF FC F9 F7 FF FC F9 F7 FF FC F9 F7 FF F4 F3' + 'F1 FF 98 98 98 FF 84 84 84 FF 82 82 82 FF 7F 7F' + '7F FF 7F 7F 7F FF 7F 7F 7F FF 7C 7C 7C FF 7B 7B' + '7B FF 8B 8B 8B FF 4B 4B 4B 98 00 00 00 35 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 BF BF' + 'BF 79 C9 C9 C9 FF CB C9 C8 FF FA F5 F1 FF F7 EF' + 'E9 FF F6 EE E7 FF FA F4 EE FF D2 BD B6 FF C6 AB' + 'A1 FF FC F8 F4 FF FB F4 EF FF E1 D0 C8 FF F0 E5' + 'DD FF FB F4 EF FF F9 F2 EC FF E1 D0 C1 FF D1 BA' + 'A4 FF C5 AA 93 FF FC F7 F2 FF CE B6 A4 FF B2 8F' + '77 FF E2 D5 C8 FF FA F3 EE FF F7 EF E9 FF F8 F0' + 'EA FF D7 C4 B2 FF B4 8F 73 FF F5 F0 ED FF E4 D5' + 'CA FF CB B3 9E FF F6 F1 EB FF D5 C2 B1 FF CC B2' + '9F FF CC B7 AB FF F3 EA E4 FF F7 EE E8 FF F8 F0' + 'EA FF F8 F0 EA FF F7 EF E9 FF F6 EE E7 FF F6 EE' + 'E7 FF F6 EE E7 FF F6 EE E7 FF F7 EE E8 FF F1 EC' + 'E7 FF 98 97 96 FF 85 85 85 FF 84 84 84 FF 80 80' + '80 FF 7E 7E 7E FF 7E 7E 7E FF 7D 7D 7D FF 7C 7C' + '7C FF 8C 8C 8C FF 4D 4D 4D 9B 00 00 00 37 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D2 D2' + 'D2 8B C7 C7 C7 FF C6 C6 C7 FF FD FD FD FF FF FF' + 'FF FF FF FF FF FF F7 F5 F2 FF FB F9 F8 FF B3 91' + '77 FF D5 C2 B1 FF FA F8 F6 FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FB FA' + 'F8 FF DF D2 C6 FF D5 C2 B2 FF FB FA F8 FF 97 6F' + '64 FF EB E3 DD FF FF FF FF FF FF FF FF FF FE FF' + 'FF FF EE E7 E0 FF BD A0 8A FF D4 C3 B3 FF CB B4' + '9F FF D2 BE AD FF D7 C5 B5 FF F8 F6 F4 FF A5 82' + '7A FF FD FD FC FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF F1 F1' + 'F1 FF 93 93 93 FF 87 87 87 FF 85 85 85 FF 82 82' + '82 FF 81 81 81 FF 7F 7F 7F FF 7D 7D 7D FF 7D 7D' + '7D FF 8D 8D 8D FF 4D 4D 4D 9D 00 00 00 38 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 EE EE' + 'EE 98 C4 C4 C4 FF BC BB BB FF F1 ED E9 FF F9 F3' + 'EE FF F6 EE E7 FF D7 C3 B2 FF B2 96 8C FF C3 A8' + '92 FF CE B8 AA FF CD B2 9B FF F9 F2 EB FF F6 ED' + 'E6 FF F6 ED E6 FF F6 ED E6 FF F6 ED E6 FF F6 EE' + 'E6 FF F9 F3 ED FF C2 A3 8D FF B2 8E 74 FF AE 87' + '68 FF B8 96 82 FF BB 9B 89 FF F4 EB E2 FF F6 EE' + 'E6 FF F6 EE E7 FF C4 A5 8B FF B6 9D 8D FF FE FD' + 'FB FF F2 EC E4 FF C6 AB 96 FF F6 EF E9 FF 9F 75' + '62 FF EA E0 D8 FF F6 EE E6 FF F6 ED E6 FF F6 ED' + 'E6 FF F6 ED E6 FF F6 ED E6 FF F6 ED E6 FF F6 ED' + 'E6 FF F6 ED E6 FF F6 ED E6 FF F8 EF E8 FF DB D7' + 'D4 FF 8A 8A 8A FF 87 87 87 FF 86 86 86 FF 84 84' + '84 FF 82 82 82 FF 81 81 81 FF 7F 7F 7F FF 7C 7C' + '7C FF 8B 8B 8B FF 54 54 56 A0 00 00 00 39 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 F0' + 'F0 9D C4 C4 C4 FF B6 B6 B6 FF DF DF DF FF FE FE' + 'FE FF FF FF FF FF FE FE FE FF F1 EB E7 FF C7 AF' + 'A1 FF C5 AE A5 FF CF BA A9 FF FD FD FD FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FC FB FB FF F0 EA E5 FF EF E8' + 'E2 FF FB F9 F8 FF EB E4 E1 FF F9 F6 F4 FF FF FF' + 'FF FF FE FE FE FF F0 EA E5 FF D9 CD C4 FF C6 AD' + '96 FF CF BA A7 FF F1 EC E6 FF ED E6 DF FF F0 EB' + 'E8 FF F9 F7 F6 FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FC FC FC FF B1 B1' + 'B1 FF 8A 8A 8A FF 87 87 87 FF 87 87 87 FF 85 85' + '85 FF 83 83 83 FF 80 80 80 FF 7F 7F 7F FF 7D 7D' + '7D FF 8B 8B 8B FF 56 56 56 A3 00 00 00 3A 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 EE EE' + 'EE A6 C5 C5 C5 FF B7 B7 B7 FF BD BC BB FF EC E8' + 'E4 FF F9 F2 ED FF F7 EE E8 FF F8 F0 EA FF FB F4' + 'EF FF FC F6 F2 FF FA F3 EE FF F8 F0 EA FF F7 EE' + 'E7 FF F6 EE E7 FF F6 EE E7 FF F6 EE E7 FF F6 EE' + 'E7 FF F6 EE E7 FF F6 EE E7 FF F7 EE E7 FF F7 EE' + 'E7 FF F7 EE E8 FF F7 EF E8 FF F7 EF E8 FF F7 EE' + 'E8 FF F6 EE E7 FF F7 EF E8 FF F9 F2 EC FF FB F5' + 'F0 FF FA F3 EE FF F9 F1 EB FF F8 F0 E9 FF F7 EF' + 'E9 FF F7 EE E8 FF F7 EE E7 FF F6 EE E7 FF F6 EE' + 'E7 FF F6 EE E7 FF F6 EE E7 FF F6 EE E7 FF F6 EE' + 'E7 FF F7 EE E8 FF FA F3 ED FF D0 CC C9 FF 8F 8E' + '8E FF 8C 8C 8C FF 89 89 89 FF 86 86 86 FF 84 84' + '84 FF 84 84 84 FF 82 82 82 FF 81 81 81 FF 7E 7E' + '7E FF 8D 8D 8D FF 59 59 59 A5 00 00 00 3B 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E9 E9' + 'E9 B1 C6 C6 C6 FF BD BD BD FF BB BB BB FF BF BF' + 'BF FF DF DF DF FF F3 F2 F2 FF F4 F4 F4 FF F4 F4' + 'F4 FF F4 F4 F4 FF F5 F5 F5 FF F5 F5 F5 FF F5 F5' + 'F4 FF F5 F4 F4 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4' + 'F4 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4 F3 FF F4 F3' + 'F3 FF F4 F3 F3 FF F4 F3 F3 FF F3 F3 F3 FF F3 F3' + 'F3 FF F3 F3 F2 FF F3 F3 F2 FF F3 F3 F2 FF F3 F3' + 'F2 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2 F2 FF F2 F2' + 'F2 FF F2 F1 F1 FF F2 F2 F1 FF F4 F4 F4 FF F7 F7' + 'F7 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6 F6 FF F1 F1' + 'F1 FF E8 E8 E8 FF BF BF BF FF 8F 8F 8F FF 8C 8C' + '8C FF 8B 8B 8B FF 8C 8C 8C FF 89 89 89 FF 88 88' + '88 FF 87 87 87 FF 84 84 84 FF 82 82 82 FF 81 81' + '81 FF 8F 8F 8F FF 5A 5A 5A A7 00 00 00 3C 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E6 E6' + 'E6 BC C6 C6 C6 FF BC BC BC FF BC BC BC FF BB BB' + 'BB FF BA BA BA FF B9 B9 B9 FF B6 B6 B6 FF B4 B4' + 'B4 FF B3 B3 B3 FF B1 B1 B1 FF AF AF AF FF AF AF' + 'AF FF AD AD AD FF AB AB AB FF AB AB AB FF AA AA' + 'AA FF A9 A9 A9 FF A8 A8 A8 FF A7 A7 A7 FF A6 A6' + 'A6 FF A5 A5 A5 FF A4 A4 A4 FF A3 A3 A3 FF A1 A1' + 'A1 FF A1 A1 A1 FF A0 A0 A0 FF A0 A0 A0 FF 9F 9F' + '9F FF 9E 9E 9E FF 9D 9D 9D FF 9B 9B 9B FF 9A 9A' + '9A FF 98 98 98 FF 97 97 97 FF 96 96 96 FF 92 92' + '92 FF 91 91 91 FF 91 91 91 FF 92 92 92 FF 93 93' + '93 FF 92 92 92 FF 8F 8F 8F FF 8E 8E 8E FF 8D 8D' + '8D FF 8B 8B 8B FF 8B 8B 8B FF 8A 8A 8A FF 88 88' + '88 FF 84 84 84 FF 83 83 83 FF 82 82 82 FF 81 81' + '81 FF 8F 8F 8F FF 60 60 60 AA 00 00 00 3D 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 E5' + 'E5 C6 C6 C6 C6 FF BF BF BF FF BD BD BD FF BD BD' + 'BD FF B9 B9 B9 FF B8 B8 B8 FF B7 B7 B7 FF B6 B6' + 'B6 FF B4 B4 B4 FF B2 B2 B2 FF B0 B0 B0 FF AE AE' + 'AE FF AD AD AD FF AD AD AD FF AB AB AB FF AA AA' + 'AA FF A9 A9 A9 FF A9 A9 A9 FF A8 A8 A8 FF A6 A6' + 'A6 FF A5 A5 A5 FF A5 A5 A5 FF A4 A4 A4 FF A2 A2' + 'A2 FF A2 A2 A2 FF A2 A2 A2 FF A1 A1 A1 FF A1 A1' + 'A1 FF 9E 9E 9E FF 9D 9D 9D FF 9C 9C 9C FF 9A 9A' + '9A FF 98 98 98 FF 97 97 97 FF 96 96 96 FF 96 96' + '96 FF 94 94 94 FF 94 94 94 FF 94 94 94 FF 93 93' + '93 FF 92 92 92 FF 90 90 90 FF 8F 8F 8F FF 8F 8F' + '8F FF 8E 8E 8E FF 8A 8A 8A FF 89 89 89 FF 88 88' + '88 FF 87 87 87 FF 85 85 85 FF 84 84 84 FF 81 81' + '81 FF 8F 8F 8F FF 64 64 64 AC 00 00 00 3D 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E4 E4' + 'E4 D1 C9 C9 C9 FF C1 C1 C1 FF C1 C1 C1 FF BF BF' + 'BF FF BD BD BD FF BA BA BA FF B9 B9 B9 FF B5 B5' + 'B5 FF B4 B4 B4 FF B2 B2 B2 FF AF AF AF FF AD AD' + 'AD FF AB AB AB FF AC AC AC FF AA AA AA FF A9 A9' + 'A9 FF A8 A8 A8 FF A8 A8 A8 FF A6 A6 A6 FF A6 A6' + 'A6 FF A4 A4 A4 FF A5 A5 A5 FF A5 A5 A5 FF A2 A2' + 'A2 FF A2 A2 A2 FF A1 A1 A1 FF A0 A0 A0 FF A0 A0' + 'A0 FF 9F 9F 9F FF 9C 9C 9C FF 9B 9B 9B FF 99 99' + '99 FF 9A 9A 9A FF 99 99 99 FF 97 97 97 FF 96 96' + '96 FF 96 96 96 FF 95 95 95 FF 95 95 95 FF 93 93' + '93 FF 93 93 93 FF 93 93 93 FF 90 90 90 FF 8F 8F' + '8F FF 8E 8E 8E FF 8D 8D 8D FF 8B 8B 8B FF 88 88' + '88 FF 87 87 87 FF 85 85 85 FF 84 84 84 FF 83 83' + '83 FF 90 90 90 FF 67 67 67 AF 00 00 00 3E 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E9 E9' + 'E9 EB CB CB CB FF C5 C5 C5 FF C2 C2 C2 FF C2 C2' + 'C2 FF BF BF BF FF BC BC BC FF BC BC BC FF B8 B8' + 'B8 FF B7 B7 B7 FF B4 B4 B4 FF B1 B1 B1 FF B0 B0' + 'B0 FF AE AE AE FF AD AD AD FF AB AB AB FF AA AA' + 'AA FF A9 A9 A9 FF A9 A9 A9 FF A7 A7 A7 FF A6 A6' + 'A6 FF A7 A7 A7 FF A6 A6 A6 FF A4 A4 A4 FF A4 A4' + 'A4 FF A2 A2 A2 FF A3 A3 A3 FF 9F 9F 9F FF 9E 9E' + '9E FF 9E 9E 9E FF 9E 9E 9E FF 9B 9B 9B FF 9B 9B' + '9B FF 9A 9A 9A FF 98 98 98 FF 97 97 97 FF 96 96' + '96 FF 97 97 97 FF 95 95 95 FF 94 94 94 FF 93 93' + '93 FF 94 94 94 FF 92 92 92 FF 91 91 91 FF 90 90' + '90 FF 8F 8F 8F FF 8E 8E 8E FF 8B 8B 8B FF 8B 8B' + '8B FF 8A 8A 8A FF 87 87 87 FF 86 86 86 FF 84 84' + '84 FF 93 93 93 FF 69 69 69 B1 00 00 00 3F 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CF CF CF 10 ED ED' + 'ED F6 CB CB CB FF C4 C4 C4 FF C4 C4 C4 FF C1 C1' + 'C1 FF C0 C0 C0 FF BE BE BE FF BC BC BC FF B9 B9' + 'B9 FF B8 B8 B8 FF B5 B5 B5 FF B3 B3 B3 FF B3 B3' + 'B3 FF B0 B0 B0 FF AE AE AE FF AD AD AD FF AB AB' + 'AB FF A8 A8 A8 FF A8 A8 A8 FF A6 A6 A6 FF A6 A6' + 'A6 FF A6 A6 A6 FF A6 A6 A6 FF A5 A5 A5 FF A5 A5' + 'A5 FF A3 A3 A3 FF A1 A1 A1 FF 9E 9E 9E FF 9F 9F' + '9F FF 9F 9F 9F FF 9F 9F 9F FF 9D 9D 9D FF 9B 9B' + '9B FF 9A 9A 9A FF 98 98 98 FF 97 97 97 FF 96 96' + '96 FF 95 95 95 FF 95 95 95 FF 94 94 94 FF 93 93' + '93 FF 93 93 93 FF 92 92 92 FF 90 90 90 FF 91 91' + '91 FF 8F 8F 8F FF 8D 8D 8D FF 8D 8D 8D FF 8C 8C' + '8C FF 8B 8B 8B FF 89 89 89 FF 86 86 86 FF 87 87' + '87 FF 93 93 93 FF 6C 6C 6C B3 00 00 00 3F 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 E1 E1 E1 11 EE EE' + 'EE FB CB CB CB FF C9 C9 C9 FF C5 C5 C5 FF C3 C3' + 'C3 FF BF BF BF FF BD BD BD FF BB BB BB FF B9 B9' + 'B9 FF B7 B7 B7 FF B5 B5 B5 FF B4 B4 B4 FF B1 B1' + 'B1 FF B0 B0 B0 FF AE AE AE FF AD AD AD FF AC AC' + 'AC FF A8 A8 A8 FF A9 A9 A9 FF A8 A8 A8 FF A6 A6' + 'A6 FF A7 A7 A7 FF A6 A6 A6 FF A5 A5 A5 FF A3 A3' + 'A3 FF A3 A3 A3 FF A1 A1 A1 FF A0 A0 A0 FF 9F 9F' + '9F FF A0 A0 A0 FF 9E 9E 9E FF 9D 9D 9D FF 9C 9C' + '9C FF 9A 9A 9A FF 9A 9A 9A FF 9A 9A 9A FF 99 99' + '99 FF 96 96 96 FF 95 95 95 FF 94 94 94 FF 95 95' + '95 FF 94 94 94 FF 92 92 92 FF 92 92 92 FF 91 91' + '91 FF 8F 8F 8F FF 8E 8E 8E FF 8D 8D 8D FF 8A 8A' + '8A FF 8B 8B 8B FF 89 89 89 FF 88 88 88 FF 86 86' + '86 FF 92 92 92 FF 70 70 70 B6 00 00 00 40 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 E2 E2 E2 12 F0 F0' + 'F0 FE CC CC CC FF C8 C8 C8 FF C7 C7 C7 FF C5 C5' + 'C5 FF C1 C1 C1 FF BD BD BD FF BB BB BB FF B9 B9' + 'B9 FF B7 B7 B7 FF B6 B6 B6 FF B5 B5 B5 FF B2 B2' + 'B2 FF B2 B2 B2 FF AE AE AE FF AC AC AC FF AB AB' + 'AB FF A8 A8 A8 FF A7 A7 A7 FF A7 A7 A7 FF A6 A6' + 'A6 FF A6 A6 A6 FF A5 A5 A5 FF A3 A3 A3 FF A4 A4' + 'A4 FF A3 A3 A3 FF A1 A1 A1 FF A0 A0 A0 FF 9E 9E' + '9E FF 9D 9D 9D FF 9D 9D 9D FF 9E 9E 9E FF 9C 9C' + '9C FF 9A 9A 9A FF 9A 9A 9A FF 98 98 98 FF 99 99' + '99 FF 97 97 97 FF 95 95 95 FF 95 95 95 FF 95 95' + '95 FF 93 93 93 FF 93 93 93 FF 92 92 92 FF 90 90' + '90 FF 90 90 90 FF 8F 8F 8F FF 8D 8D 8D FF 8C 8C' + '8C FF 8B 8B 8B FF 89 89 89 FF 88 88 88 FF 86 86' + '86 FF 93 93 93 FF 73 73 73 B8 00 00 00 41 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 DA DA DA 15 F1 F1' + 'F1 FF CD CD CD FF C8 C8 C8 FF C8 C8 C8 FF C5 C5' + 'C5 FF C2 C2 C2 FF C0 C0 C0 FF BE BE BE FF BC BC' + 'BC FF B9 B9 B9 FF B7 B7 B7 FF B5 B5 B5 FF B1 B1' + 'B1 FF B1 B1 B1 FF AE AE AE FF AC AC AC FF AA AA' + 'AA FF A9 A9 A9 FF A8 A8 A8 FF A7 A7 A7 FF A7 A7' + 'A7 FF A6 A6 A6 FF A4 A4 A4 FF A2 A2 A2 FF A4 A4' + 'A4 FF A3 A3 A3 FF A2 A2 A2 FF A1 A1 A1 FF A0 A0' + 'A0 FF 9E 9E 9E FF 9F 9F 9F FF 9F 9F 9F FF 9A 9A' + '9A FF 9B 9B 9B FF 9B 9B 9B FF 98 98 98 FF 99 99' + '99 FF 98 98 98 FF 98 98 98 FF 96 96 96 FF 95 95' + '95 FF 95 95 95 FF 93 93 93 FF 94 94 94 FF 92 92' + '92 FF 91 91 91 FF 90 90 90 FF 8E 8E 8E FF 8B 8B' + '8B FF 8A 8A 8A FF 89 89 89 FF 89 89 89 FF 88 88' + '88 FF 93 93 93 FF 74 73 74 BA 00 00 00 41 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CF CF CF 20 EF EF' + 'EF FF CD CD CD FF CA CA CA FF C9 C9 C9 FF C6 C6' + 'C6 FF C4 C4 C4 FF C2 C2 C2 FF BF BF BF FF BC BC' + 'BC FF BA BA BA FF B8 B8 B8 FF B6 B6 B6 FF B3 B3' + 'B3 FF B1 B1 B1 FF AF AF AF FF AD AD AD FF AB AB' + 'AB FF AA AA AA FF A8 A8 A8 FF A7 A7 A7 FF A8 A8' + 'A8 FF A5 A5 A5 FF A4 A4 A4 FF A3 A3 A3 FF A3 A3' + 'A3 FF A2 A2 A2 FF A2 A2 A2 FF A1 A1 A1 FF 9F 9F' + '9F FF A0 A0 A0 FF A0 A0 A0 FF 9F 9F 9F FF 9E 9E' + '9E FF 9C 9C 9C FF 9C 9C 9C FF 9A 9A 9A FF 9B 9B' + '9B FF 9A 9A 9A FF 99 99 99 FF 98 98 98 FF 96 96' + '96 FF 95 95 95 FF 94 94 94 FF 94 94 94 FF 93 93' + '93 FF 93 93 93 FF 91 91 91 FF 8F 8F 8F FF 8F 8F' + '8F FF 8D 8D 8D FF 8C 8C 8C FF 8A 8A 8A FF 89 89' + '89 FF 95 96 95 FF 77 77 77 BC 00 00 00 42 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 D0 D0 D0 2C EF EF' + 'EF FF CE CE CE FF CC CC CC FF CA CA CA FF C7 C7' + 'C7 FF C5 C5 C5 FF C4 C4 C4 FF C0 C0 C0 FF BE BE' + 'BE FF BB BB BB FF B9 B9 B9 FF B6 B6 B6 FF B5 B5' + 'B5 FF B4 B4 B4 FF B1 B1 B1 FF AE AE AE FF AD AD' + 'AD FF AB AB AB FF AA AA AA FF A9 A9 A9 FF A8 A8' + 'A8 FF A6 A6 A6 FF A5 A5 A5 FF A4 A4 A4 FF A4 A4' + 'A4 FF A3 A3 A3 FF A2 A2 A2 FF A1 A1 A1 FF A0 A0' + 'A0 FF 9F 9F 9F FF 9F 9F 9F FF 9F 9F 9F FF 9F 9F' + '9F FF 9D 9D 9D FF 9C 9C 9C FF 9A 9A 9A FF 9B 9B' + '9B FF 9A 9A 9A FF 99 99 99 FF 99 99 99 FF 97 97' + '97 FF 96 96 96 FF 94 94 94 FF 95 95 95 FF 94 94' + '94 FF 92 92 92 FF 92 92 92 FF 91 91 91 FF 90 90' + '90 FF 8E 8E 8E FF 8C 8C 8C FF 8B 8B 8B FF 8A 8A' + '8A FF 94 94 94 FF 7B 7B 7B BE 00 00 00 42 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CC CC CC 38 ED ED' + 'ED FF CE CE CE FF CC CC CC FF CA CA CA FF C7 C7' + 'C7 FF C5 C5 C5 FF C2 C2 C2 FF C0 C0 C0 FF BD BD' + 'BD FF BC BC BC FF B9 B9 B9 FF B7 B7 B7 FF B5 B5' + 'B5 FF B3 B3 B3 FF AF AF AF FF AE AE AE FF AC AC' + 'AC FF AB AB AB FF AA AA AA FF AA AA AA FF A8 A8' + 'A8 FF A8 A8 A8 FF A6 A6 A6 FF A5 A5 A5 FF A4 A4' + 'A4 FF A3 A3 A3 FF A3 A3 A3 FF A2 A2 A2 FF A0 A0' + 'A0 FF A0 A0 A0 FF 9F 9F 9F FF 9E 9E 9E FF 9F 9F' + '9F FF 9D 9D 9D FF 9D 9D 9D FF 9C 9C 9C FF 9C 9C' + '9C FF 9A 9A 9A FF 9A 9A 9A FF 9A 9A 9A FF 99 99' + '99 FF 97 97 97 FF 96 96 96 FF 95 95 95 FF 95 95' + '95 FF 93 93 93 FF 93 93 93 FF 92 92 92 FF 92 92' + '92 FF 90 90 90 FF 8D 8D 8D FF 8C 8C 8C FF 89 89' + '89 FF 94 94 94 FF 7F 7F 7F C0 00 00 00 43 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CE CE CE 44 EB EB' + 'EB FF CF CF CF FF CE CE CE FF CC CC CC FF CA CA' + 'CA FF C5 C5 C5 FF C4 C4 C4 FF C2 C2 C2 FF C0 C0' + 'C0 FF BD BD BD FF BB BB BB FF B9 B9 B9 FF B6 B6' + 'B6 FF B4 B4 B4 FF B1 B1 B1 FF B0 B0 B0 FF AD AD' + 'AD FF AC AC AC FF A9 A9 A9 FF A9 A9 A9 FF A6 A6' + 'A6 FF A6 A6 A6 FF A6 A6 A6 FF A5 A5 A5 FF A4 A4' + 'A4 FF A4 A4 A4 FF A3 A3 A3 FF A2 A2 A2 FF A1 A1' + 'A1 FF A1 A1 A1 FF A0 A0 A0 FF 9F 9F 9F FF 9F 9F' + '9F FF 9E 9E 9E FF 9E 9E 9E FF 9D 9D 9D FF 9D 9D' + '9D FF 9B 9B 9B FF 9B 9B 9B FF 9A 9A 9A FF 98 98' + '98 FF 97 97 97 FF 96 96 96 FF 97 97 97 FF 96 96' + '96 FF 94 94 94 FF 93 93 93 FF 92 92 92 FF 91 91' + '91 FF 8F 8F 8F FF 8D 8D 8D FF 8B 8B 8B FF 89 89' + '89 FF 94 94 94 FF 80 80 80 C1 00 00 00 43 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CE CE CE 4F EC EC' + 'EC FF CF CF CF FF CE CE CE FF CD CD CD FF CA CA' + 'CA FF C7 C7 C7 FF C6 C6 C6 FF C2 C2 C2 FF C1 C1' + 'C1 FF BF BF BF FF BB BB BB FF B9 B9 B9 FF B7 B7' + 'B7 FF B5 B5 B5 FF B3 B3 B3 FF B0 B0 B0 FF AC AC' + 'AC FF AB AB AB FF A9 A9 A9 FF A8 A8 A8 FF A7 A7' + 'A7 FF A6 A6 A6 FF A5 A5 A5 FF A4 A4 A4 FF A3 A3' + 'A3 FF A3 A3 A3 FF A2 A2 A2 FF A2 A2 A2 FF A1 A1' + 'A1 FF A1 A1 A1 FF A0 A0 A0 FF A0 A0 A0 FF 9E 9E' + '9E FF 9E 9E 9E FF 9E 9E 9E FF 9E 9E 9E FF 9D 9D' + '9D FF 9C 9C 9C FF 9B 9B 9B FF 9B 9B 9B FF 99 99' + '99 FF 99 99 99 FF 98 98 98 FF 97 97 97 FF 96 96' + '96 FF 94 94 94 FF 94 94 94 FF 92 92 92 FF 91 91' + '91 FF 8F 8F 8F FF 8E 8E 8E FF 8B 8B 8B FF 8A 8A' + '8A FF 92 92 92 FF 7E 7E 7E C3 00 00 00 44 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 D1 D1 D1 5A E9 E9' + 'E9 FF D0 D0 D0 FF CE CE CE FF CE CE CE FF CB CB' + 'CB FF C8 C8 C8 FF C6 C6 C6 FF C3 C3 C3 FF C2 C2' + 'C2 FF C0 C0 C0 FF BD BD BD FF BB BB BB FF B6 B6' + 'B6 FF B4 B4 B4 FF B2 B2 B2 FF B0 B0 B0 FF AE AE' + 'AE FF AD AD AD FF AA AA AA FF A8 A8 A8 FF A6 A6' + 'A6 FF A5 A5 A5 FF A4 A4 A4 FF A4 A4 A4 FF A3 A3' + 'A3 FF A3 A3 A3 FF A2 A2 A2 FF A1 A1 A1 FF A0 A0' + 'A0 FF A0 A0 A0 FF A0 A0 A0 FF 9E 9E 9E FF 9E 9E' + '9E FF 9F 9F 9F FF 9F 9F 9F FF 9F 9F 9F FF 9E 9E' + '9E FF 9D 9D 9D FF 9C 9C 9C FF 99 99 99 FF 99 99' + '99 FF 99 99 99 FF 98 98 98 FF 98 98 98 FF 96 96' + '96 FF 96 96 96 FF 94 94 94 FF 93 93 93 FF 91 91' + '91 FF 8F 8F 8F FF 8E 8E 8E FF 8B 8B 8B FF 8A 8A' + '8A FF 92 92 92 FF 81 81 81 C5 00 00 00 44 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 D2 D2 D2 66 E9 E9' + 'E9 FF D2 D2 D2 FF CF CF CF FF CE CE CE FF CB CB' + 'CB FF C9 C9 C9 FF C7 C7 C7 FF C6 C6 C6 FF C3 C3' + 'C3 FF C1 C1 C1 FF BE BE BE FF BB BB BB FF B7 B7' + 'B7 FF B5 B5 B5 FF B3 B3 B3 FF B1 B1 B1 FF AF AF' + 'AF FF AD AD AD FF AB AB AB FF A9 A9 A9 FF A7 A7' + 'A7 FF A6 A6 A6 FF A5 A5 A5 FF A3 A3 A3 FF A4 A4' + 'A4 FF A3 A3 A3 FF A3 A3 A3 FF A1 A1 A1 FF A0 A0' + 'A0 FF 9F 9F 9F FF A0 A0 A0 FF A0 A0 A0 FF A0 A0' + 'A0 FF 9E 9E 9E FF 9E 9E 9E FF 9E 9E 9E FF 9D 9D' + '9D FF 9D 9D 9D FF 9C 9C 9C FF 9B 9B 9B FF 9B 9B' + '9B FF 9A 9A 9A FF 99 99 99 FF 97 97 97 FF 96 96' + '96 FF 95 95 95 FF 93 93 93 FF 93 93 93 FF 90 90' + '90 FF 8F 8F 8F FF 8D 8D 8D FF 8C 8C 8C FF 89 89' + '89 FF 91 91 91 FF 83 83 83 C6 00 00 00 45 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 D8 D8 D8 6B ED ED' + 'ED FE D2 D2 D2 FF D0 D0 D0 FF CF CF CF FF CD CD' + 'CD FF CB CB CB FF C8 C8 C8 FF C4 C4 C4 FF C3 C3' + 'C3 FF C1 C1 C1 FF BE BE BE FF BC BC BC FF B9 B9' + 'B9 FF B6 B6 B6 FF B4 B4 B4 FF B3 B3 B3 FF AF AF' + 'AF FF AD AD AD FF AB AB AB FF A8 A8 A8 FF A7 A7' + 'A7 FF A5 A5 A5 FF A6 A6 A6 FF A5 A5 A5 FF A3 A3' + 'A3 FF A2 A2 A2 FF A1 A1 A1 FF A1 A1 A1 FF A1 A1' + 'A1 FF A0 A0 A0 FF 9F 9F 9F FF 9E 9E 9E FF A0 A0' + 'A0 FF 9E 9E 9E FF 9E 9E 9E FF 9F 9F 9F FF 9E 9E' + '9E FF 9D 9D 9D FF 9C 9C 9C FF 9D 9D 9D FF 9A 9A' + '9A FF 99 99 99 FF 98 98 98 FF 98 98 98 FF 96 96' + '96 FF 95 95 95 FF 94 94 94 FF 92 92 92 FF 8F 8F' + '8F FF 90 90 90 FF 8C 8C 8C FF 8B 8B 8B FF 89 89' + '89 FF 8F 8F 8F FF 83 83 83 C8 00 00 00 45 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CE CE CE 15 F0 F0' + 'F0 EB DA DA DB FF D2 D2 D2 FF CF D0 D0 FF CD CD' + 'CD FF CB CB CB FF C9 C9 C9 FF C7 C7 C7 FF C5 C5' + 'C5 FF C3 C3 C3 FF C0 C0 C0 FF BD BD BE FF BA BA' + 'BA FF B7 B8 B8 FF B5 B5 B6 FF B3 B3 B3 FF B1 B1' + 'B1 FF AE AE AE FF AC AC AC FF AA AA AA FF A7 A7' + 'A7 FF A6 A6 A6 FF A5 A5 A5 FF A3 A3 A3 FF A4 A4' + 'A4 FF A2 A2 A2 FF A1 A1 A1 FF A1 A1 A1 FF A0 A0' + 'A0 FF A1 A1 A1 FF A0 A0 A0 FF 9F 9F 9F FF 9F 9F' + '9F FF 9F 9F 9F FF 9F 9F 9F FF 9D 9D 9D FF 9D 9D' + '9D FF 9C 9C 9C FF 9C 9C 9C FF 9C 9C 9C FF 9B 9B' + '9B FF 9A 9A 9A FF 99 99 99 FF 98 98 98 FF 95 95' + '95 FF 94 94 94 FF 94 94 94 FF 92 92 92 FF 8F 8F' + '8F FF 8F 8F 8F FF 8C 8C 8C FF 89 89 89 FF 87 87' + '87 FF 8E 8E 8E FF 83 83 84 C8 00 00 00 43 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E7 E7' + 'E5 6C EB EB E9 FE D2 D3 D6 FF D0 D1 D3 FF D0 D1' + 'D2 FF CD CE D0 FF CC CD CF FF C9 CA CC FF C6 C7' + 'C9 FF C3 C4 C5 FF C0 C1 C3 FF BB BC BF FF B8 B9' + 'BC FF B7 B8 BA FF B2 B3 B6 FF B0 B2 B5 FF AE AF' + 'B2 FF AB AD B0 FF A8 AA AD FF A7 A9 AC FF A3 A4' + 'A8 FF A2 A3 A7 FF A1 A3 A6 FF A2 A3 A6 FF A3 A4' + 'A7 FF A4 A5 A7 FF A2 A2 A4 FF A0 A0 A0 FF A0 A0' + 'A0 FF A0 A0 A0 FF 9F 9F 9F FF A0 A0 A0 FF 9F 9F' + '9F FF 9D 9D 9D FF 9E 9E 9E FF 9E 9E 9E FF 9C 9C' + '9C FF 9C 9C 9C FF 9C 9C 9C FF 9B 9B 9B FF 9A 9A' + '9A FF 99 99 99 FF 97 97 97 FF 96 96 96 FF 94 94' + '94 FF 93 93 93 FF 92 92 92 FF 90 90 90 FF 8E 8E' + '8E FF 8C 8C 8C FF 89 89 89 FF 88 88 88 FF 86 86' + '86 FF 8D 8D 8D FF 8A 8A 8A C3 00 00 00 34 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E7 E7' + 'C8 21 F2 E9 CB FB BE B2 98 FF B2 A5 8B FF AF A2' + '88 FF B0 A3 89 FF AE A2 88 FF AB 9E 84 FF A9 9C' + '82 FF A4 97 7C FF A3 96 7C FF A1 94 7A FF 9C 8F' + '76 FF 99 8C 73 FF 97 8B 73 FF 96 8A 72 FF 93 87' + '70 FF 92 87 71 FF 95 8A 74 FF 97 8C 77 FF 96 8D' + '78 FF 96 8C 78 FF 91 88 76 FF 8E 86 74 FF 8F 86' + '76 FF 90 88 78 FF 9A 97 93 FF A1 A2 A4 FF A1 A1' + 'A1 FF 9F 9F 9F FF A0 A0 A0 FF 9F 9F 9F FF 9F 9F' + '9F FF 9E 9E 9E FF 9D 9D 9D FF 9E 9E 9E FF 9D 9D' + '9D FF 9C 9C 9C FF 9C 9C 9C FF 9A 9A 9A FF 99 99' + '99 FF 98 98 98 FF 97 97 97 FF 95 95 95 FF 94 94' + '94 FF 91 91 91 FF 90 90 90 FF 8E 8E 8E FF 8C 8C' + '8C FF 8A 8A 8A FF 87 87 87 FF 85 85 85 FF 87 87' + '87 FF 96 96 96 F7 8F 8E 8F 93 00 00 00 14 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 EE EE' + 'D5 1F F8 F4 D9 F8 DE D1 B6 FF D6 C9 AE FF D5 C7' + 'AD FF D2 C5 AB FF D1 C3 A9 FF CF C1 A7 FF CD C0' + 'A6 FF CD C0 A5 FF CC BF A4 FF CA BD A2 FF C8 BA' + 'A0 FF C6 B8 9E FF C4 B6 9C FF C0 B2 98 FF BB AD' + '93 FF B5 A7 8D FF B2 A5 8A FF AF A1 86 FF AB 9E' + '83 FF A9 9C 81 FF A8 9B 7F FF A6 98 7C FF A0 92' + '76 FF 9C 8D 70 FF 96 88 6B FF A1 A2 A3 FF AB AC' + 'AC FF AB AB AB FF AC AC AC FF A9 A9 A9 FF A8 A8' + 'A8 FF A8 A8 A8 FF A6 A6 A6 FF A7 A7 A7 FF A5 A5' + 'A5 FF A4 A4 A4 FF A2 A2 A2 FF A0 A0 A0 FF 9F 9F' + '9F FF 9E 9E 9E FF 9C 9C 9C FF 9A 9A 9A FF 97 97' + '97 FF 95 95 95 FF 94 94 94 FF 92 92 92 FF 8F 8F' + '8F FF 8B 8B 8B FF 8B 8B 8B FF 89 89 89 FF 93 93' + '93 FC 7A 7A 7A 8D 12 12 12 1B 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 F5 F2 E4 A1 F4 EB D0 FE E5 D8 BE FF E2 D5' + 'BB FF E0 D3 B9 FF DE D1 B7 FF DD D0 B6 FF DB CE' + 'B4 FF D9 CC B2 FF D5 C8 AE FF D3 C6 AC FF D1 C4' + 'AA FF CE C1 A7 FF CB BE A4 FF C9 BC A2 FF C7 BA' + 'A0 FF C4 B7 9D FF C0 B3 99 FF BC AF 95 FF B9 AC' + '92 FF B5 A8 8E FF B4 A7 8D FF B2 A5 8B FF AF A2' + '88 FF AD A0 86 FF B4 A5 87 FF AA 9E 8A DF AB AC' + 'AC A7 BD BD BD A0 C0 C0 C0 A0 C1 C1 C1 A1 C3 C3' + 'C3 A2 C3 C3 C3 A3 C5 C5 C5 A3 C5 C5 C5 A4 C7 C7' + 'C7 A4 C4 C4 C4 A5 C7 C7 C7 A5 C5 C5 C5 A5 C4 C4' + 'C4 A6 C0 C0 C0 A6 BE BE BE A7 BE BE BE A9 BA BA' + 'BA AB B6 B4 B6 AC B2 B2 B2 AE AE AE AE AF AB AB' + 'AB B1 A9 A9 A9 AF A6 A6 A6 A8 A5 A5 A5 A8 A7 A7' + 'A7 94 51 51 51 1C 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 D7 CD BA 1A F9 F4 E5 DC F6 EE D6 FF F0 E8' + 'CB FF EE E5 C8 FF EE E3 C7 FF EB DF C3 FF EB DE' + 'C2 FF E9 DC BF FF E7 D9 BD FF E4 D6 B9 FF E3 D4' + 'B8 FF DF D1 B5 FF DC CE B1 FF DA CC B0 FF D7 C9' + 'AD FF D5 C7 AA FF D3 C5 A8 FF D1 C2 A6 FF CD BF' + 'A3 FF CA BC A0 FF C8 BA 9E FF C6 B8 9B FF C4 B6' + '9A FF C7 B9 9C FF D7 C8 A8 F7 5A 53 46 4C 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 D9 D2 C3 22 F6 F2 DC 76 F9 F7' + 'E4 86 F9 F7 E2 86 F7 F7 E0 86 F7 F5 DC 86 F7 F5' + 'DA 86 F7 F5 DA 86 F7 F3 D8 86 F7 F3 D5 86 F5 F3' + 'D3 86 F5 F1 D1 86 F5 EF CD 86 F5 ED CB 86 F3 E8' + 'C9 86 F3 E6 C5 86 F1 E4 C2 86 F1 E0 C0 86 EF DC' + 'BE 86 EB DA BC 86 EA DA BA 86 E6 D5 B4 86 E4 D3' + 'B4 86 E4 D5 B2 86 C5 B8 9C 50 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF E0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F 80 00' + '00 00 00 00 00 3F 80 00 00 00 00 00 00 3F 80 00' + '00 00 00 00 00 3F 80 00 00 00 00 00 00 3F 80 00' + '00 00 00 00 00 3F 80 00 00 00 00 00 00 3F 80 00' + '00 00 00 00 00 3F 80 00 00 00 00 00 00 3F 80 00' + '00 00 00 00 00 3F 80 00 00 00 00 00 00 3F 80 00' + '00 00 00 00 00 3F 80 00 00 00 00 00 00 3F 80 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 7F E0 00' + '00 00 00 00 00 FF E0 00 00 03 FF FF FF FF F0 00' + '00 07 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF' } */ + + /* BINRES folder_open.ico */ 4 ICON folder_open.ico -/* { - '00 00 01 00 06 00 20 20 00 00 01 00 08 00 A8 08' - '00 00 66 00 00 00 10 10 00 00 01 00 08 00 68 05' - '00 00 0E 09 00 00 30 30 00 00 01 00 20 00 A8 25' - '00 00 76 0E 00 00 20 20 00 00 01 00 20 00 A8 10' - '00 00 1E 34 00 00 10 10 00 00 01 00 20 00 68 04' - '00 00 C6 44 00 00 10 10 00 00 01 00 08 00 68 05' - '00 00 2E 49 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 B7 35 09 00 C4 3D 06 00 B5 44 16 00 B5 48' - '23 00 BE 50 2B 00 CE 48 0F 00 CC 4A 15 00 CB 53' - '1B 00 D4 58 1A 00 DC 66 1F 00 C5 53 26 00 D0 5A' - '22 00 D0 59 31 00 DE 69 20 00 DA 72 2B 00 CE 63' - '3C 00 D5 67 31 00 DF 6F 37 00 DC 72 30 00 E4 6C' - '24 00 EA 78 27 00 EB 6D 31 00 E8 75 36 00 F1 76' - '30 00 BB 6C 4F 00 C4 6D 55 00 C4 76 5D 00 CE 7D' - '5F 00 D2 74 50 00 D4 7A 5B 00 E6 72 41 00 C5 7F' - '6B 00 C8 7B 68 00 DB 7E 65 00 F8 8A 29 00 EF 88' - '35 00 F2 8A 34 00 FD 99 35 00 FC 9B 3B 00 FF A3' - '3A 00 BC 89 79 00 E9 88 46 00 E5 85 4B 00 F5 9A' - '44 00 FD 9D 45 00 F0 95 49 00 FA 9C 4D 00 EA 81' - '5B 00 FF A6 40 00 FF A9 45 00 F4 A0 49 00 FF AD' - '4E 00 FA A0 53 00 FE A9 53 00 FD A6 58 00 FA AA' - '5B 00 FF B2 55 00 FF B3 5C 00 C5 80 6C 00 CB 82' - '6C 00 D5 84 69 00 C3 85 77 00 CA 8B 77 00 D4 89' - '78 00 CD 90 7C 00 DA 9B 7E 00 EF 9D 65 00 ED 97' - '6F 00 F4 9E 63 00 F5 9C 6F 00 E3 8C 78 00 E0 9D' - '7E 00 E8 9A 7D 00 F0 9B 79 00 FA AF 65 00 F6 AB' - '6C 00 FD B2 62 00 FF BA 65 00 FE B6 6E 00 FF BF' - '6D 00 EC A1 76 00 EB A2 7A 00 F2 A6 75 00 F7 AC' - '70 00 F0 A3 7D 00 F5 AC 7F 00 FE B7 71 00 FC BC' - '72 00 F9 BA 7F 00 FE C4 78 00 92 8F 8D 00 91 90' - '8E 00 9F 93 8F 00 93 91 91 00 9B 96 94 00 9C 9B' - '9B 00 BC 8F 82 00 A1 93 8D 00 BC 92 85 00 B1 96' - '8A 00 A3 95 91 00 A5 98 94 00 A0 9E 9F 00 BC 9A' - '93 00 BE 9E 98 00 A8 A0 9C 00 B6 A1 9D 00 B9 A3' - '9F 00 A4 A2 A2 00 A9 A6 A6 00 AD AA A6 00 A9 A5' - 'A8 00 AE AB AC 00 B7 A7 A1 00 B9 A3 A0 00 B6 A8' - 'A3 00 BA AA A5 00 B3 AB AB 00 B5 B1 AD 00 B4 AE' - 'B0 00 BB B3 B7 00 C1 93 84 00 C2 93 89 00 CE 97' - '8B 00 C2 9A 8D 00 CF 99 8A 00 D6 98 87 00 C3 9F' - '92 00 CB 9D 92 00 DF 9D 90 00 E4 9A 88 00 C2 A1' - '97 00 CB A0 92 00 C3 A6 9C 00 C8 A4 99 00 DC A4' - '91 00 DB A9 91 00 D0 A3 99 00 E8 A7 80 00 EC AD' - '83 00 F0 AA 82 00 F3 AB 89 00 F0 B7 80 00 FF BF' - '85 00 F3 B2 8C 00 F8 B1 88 00 F5 BB 8C 00 F8 BE' - '8E 00 F1 AE 9A 00 EE B2 95 00 F7 B8 96 00 C1 AC' - 'A5 00 D7 AA A4 00 C4 B1 AD 00 D7 B0 A1 00 DA B5' - 'A6 00 DA B4 AA 00 D9 BB AF 00 C2 BC BD 00 D0 B3' - 'B2 00 DA BD B4 00 D0 BB BB 00 E0 B2 A5 00 EE BD' - 'A2 00 E3 B4 AA 00 E4 BB AF 00 F1 B6 A7 00 F6 BE' - 'A2 00 F0 BA AB 00 E5 BE B3 00 EC BC B0 00 E2 BF' - 'BB 00 FD C9 86 00 F4 C4 97 00 FF CB 95 00 FB C4' - '9C 00 FE CC 9D 00 FD D1 93 00 FE D5 9C 00 FE DB' - '9E 00 EC C5 AF 00 F4 C2 A5 00 FD CD A1 00 F5 C8' - 'AA 00 F9 CB AB 00 FD D4 A3 00 FF DE A4 00 FD D3' - 'AC 00 FD DB A9 00 E9 C2 B7 00 F1 C2 B5 00 F3 CA' - 'B3 00 FA D2 B3 00 FD DC B5 00 FB D6 BB 00 FB DC' - 'BC 00 FF E0 A5 00 FF E2 AE 00 FF E3 B2 00 FE E6' - 'BE 00 FF E8 BF 00 C3 BF C0 00 D0 C1 C1 00 DB C5' - 'C0 00 DD C6 CC 00 DF CA CB 00 DF CF D0 00 EB C5' - 'C0 00 EB CF CA 00 F9 CD C6 00 FC D3 C5 00 FC DC' - 'C5 00 F0 D3 CA 00 FF D7 CB 00 FA DD CA 00 E0 CF' - 'D0 00 E5 D7 DA 00 FA D8 D1 00 F0 DC D9 00 FE E4' - 'C1 00 FF E9 C3 00 FF EE CE 00 F0 E0 D4 00 FC E2' - 'D3 00 FF EE D0 00 F0 E1 DF 00 FD E5 DA 00 FE EB' - 'DC 00 FF F0 D5 00 FE F2 DD 00 ED DE E0 00 F0 DF' - 'E2 00 F1 E3 E3 00 FD E9 E5 00 F0 E7 EF 00 F1 EB' - 'EC 00 FE F5 E3 00 FF F3 EB 00 FF FA EE 00 F3 EE' - 'F0 00 FE F3 F2 00 FF FA F1 00 FF F7 FF 00 FF F9' - 'FC 00 7F 7F 7F 00 09 48 E9 77 B8 10 E9 77 FF FF' - 'FF FF C9 F1 E7 77 16 EA 4B 00 F8 00 00 00 40 B4' - '0C 01 66 00 00 00 58 1A 95 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 5F 5F 5F 5C 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 A5 99 98 76 70 60 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 7F 83 88 7E 68 74 71 67 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 65 47 A7 E3 A9 83 7E 68 75 71 6D 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 9D 8E 95 F4 F4 EA BF 82 7C 68 73 71 6D' - '5C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 9B B1 95 F4 F4 F3 F1 E9 D0 83 7F 7A 6B' - '76 6E 5E 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 89 BA 91 F1 F4 F3 F1 F1 EC E8 AB 83 41' - '63 69 76 70 5E 00 00 00 00 00 00 00 00 00 00 00' - '00 00 62 89 C5 93 E0 E3 F1 F1 F0 F0 EB E9 E2 AA' - '83 40 3E 68 6B 71 6D 00 00 00 00 00 00 00 00 00' - '00 00 BE B6 B4 93 B0 90 B7 C3 DF EC EB E9 E2 E7' - 'E7 AC 83 40 3E 7B 6C 71 70 5C 00 00 00 00 00 00' - '00 00 AB C1 C5 94 97 4F 36 37 8F C0 D5 DB E7 E7' - 'E7 D9 D8 AC 82 3D 20 63 6B 77 6D 00 00 00 00 00' - '00 00 95 C2 C7 B0 91 5A 4D 36 2C 2E 43 96 BE D1' - 'D9 D9 D8 CF CF A1 7E 3C 1B 69 CA 00 00 00 00 00' - '00 66 95 C2 C7 B7 8D AD 4E 3A 36 2C 24 18 2B 48' - 'A5 AC D8 CF CF CD CB A0 3D 3E 9F 60 00 00 00 00' - '00 9E C0 C4 C7 BD 55 B2 5A 4E 3A 36 2C 24 15 14' - '12 3D 88 9D CE CD CB A2 8A 20 9F 6D 00 00 00 00' - '00 9E D4 C8 C9 BD 4A BA AD 5A 4E 3A 34 2C 24 15' - '0E 0C 0C 1D 7F 99 A0 A2 A0 21 79 70 00 00 00 00' - '00 9C E1 DD C9 C2 8E BC AD AD 5A 4E 39 36 2C 25' - '15 0E 0C 08 07 10 3C 7B 8A 21 73 6E 00 00 00 00' - '5D A6 E5 DE DD C4 95 B9 B3 B2 AD 5A 4E 3A 36 2D' - '25 15 14 09 08 06 02 04 19 1A 63 6F 00 00 00 00' - 'D0 D3 E5 E1 DE DC B8 A8 B0 B0 AF AD 5A 4E 3A 39' - '32 26 25 15 0A 09 06 01 03 05 20 76 00 00 00 00' - 'D0 E0 E6 E5 E1 DE DD BC 97 97 B0 94 94 90 50 3A' - '34 32 31 26 23 14 09 01 08 0B 1A 76 00 00 00 00' - 'AB E4 E6 E6 E5 E1 DE DD C9 C2 B7 91 8E 97 AF 50' - '39 34 32 31 28 23 14 08 0C 0B 10 75 60 00 00 66' - 'BF ED ED ED E6 E5 E1 DE DD C9 C9 C7 BA 92 97 AD' - '50 39 34 32 31 26 18 17 0F 0C 0D 69 67 00 00 A1' - 'D6 EE F2 ED E6 E6 E5 E1 DE DD C9 C8 C7 B7 52 91' - '59 57 4D 39 32 27 16 2F 33 13 0D 61 6D 00 00 CC' - 'EA F2 F2 EF ED ED E6 E5 DE DE DD C9 C9 C7 B1 8D' - '8E 92 56 4C 38 35 1F 4B 4E 2E 11 3B 70 00 00 A1' - 'F1 F4 F2 F2 F2 ED E6 E6 E5 E1 DE DD C9 C8 C7 C6' - 'BA 94 52 51 53 46 30 54 58 4B 2A 1C 78 00 00 A1' - 'EA EA F1 EE F2 EF ED E6 D7 C3 C4 DC DC C9 C9 C7' - 'C7 C6 C5 B3 93 44 22 49 8E 53 45 1E 77 00 00 74' - '98 9D D2 DA E0 E3 E4 D4 A9 87 86 B5 C1 B9 C1 C2' - 'C7 C7 C7 C5 B4 8C 81 98 84 61 29 3F 00 00 00 00' - '00 5B 98 98 86 A5 A6 A3 98 00 00 72 86 80 86 A4' - 'B9 B7 BA BD BB 8B 9A 00 00 00 00 6A 00 00 00 00' - '00 00 00 00 00 66 5D 66 00 00 00 00 00 00 00 6B' - '7D 85 B6 B0 AE 42 76 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 9A 84 63 64 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' +/*{ + '00 00 01 00 0B 00 10 10 10 00 01 00 04 00 28 01' + '00 00 B6 00 00 00 10 10 00 00 01 00 08 00 68 05' + '00 00 DE 01 00 00 10 10 00 00 01 00 20 00 68 04' + '00 00 46 07 00 00 20 20 10 00 01 00 04 00 E8 02' + '00 00 AE 0B 00 00 20 20 00 00 01 00 08 00 A8 08' + '00 00 96 0E 00 00 20 20 00 00 01 00 20 00 A8 10' + '00 00 3E 17 00 00 30 30 10 00 01 00 04 00 68 06' + '00 00 E6 27 00 00 30 30 00 00 01 00 08 00 A8 0E' + '00 00 4E 2E 00 00 30 30 00 00 01 00 20 00 A8 25' + '00 00 F6 3C 00 00 40 40 10 00 01 00 04 00 68 0A' + '00 00 9E 62 00 00 40 40 00 00 01 00 08 00 28 16' + '00 00 06 6D 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 04 00 00 00 00 00 80 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 87 87' + '87 87 87 87 87 00 78 8F FF FF FF F8 88 70 88 FF' + '4F 4F 4F FF 88 70 78 F4 F4 F4 F4 FF 88 70 88 8F' + 'FF FF FF F8 88 70 77 88 88 88 88 88 88 87 87 88' + '88 88 88 88 88 87 77 77 77 77 88 88 88 87 87 77' + '77 77 77 77 77 77 86 66 66 66 87 77 77 70 0E FE' + 'FE FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' - 'FF FF FC 3F FF FF FC 0F FF FF FC 03 FF FF F8 00' - 'FF FF F8 00 1F FF F8 00 07 FF F8 00 01 FF F0 00' - '00 7F F0 00 00 0F F0 00 00 07 F0 00 00 07 E0 00' - '00 03 E0 00 00 03 E0 00 00 03 E0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 01 80 00' - '00 01 80 00 00 01 80 00 00 01 80 00 00 01 80 00' - '00 01 80 00 00 03 E0 18 00 7B FE 3F 80 7F FF FF' - 'F0 FF FF FF FF FF FF FF FF FF FF FF FF FF 28 00' + '00 00 FF FF 00 00 00 03 00 00 00 01 00 00 00 01' + '00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 01 00 00 80 FF' + '00 00 FF FF 00 00 FF FF 00 00 FF FF 00 00 28 00' '00 00 10 00 00 00 20 00 00 00 01 00 08 00 00 00' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 C0 DC C0 00 F0 CA A6 00 66 66' + '66 00 68 68 68 00 6C 6C 6C 00 6F 6F 6F 00 75 72' + '6D 00 7A 75 6C 00 6F 70 72 00 6F 71 74 00 71 71' + '71 00 72 72 72 00 73 73 73 00 76 76 76 00 73 75' + '78 00 78 79 79 00 7B 79 7B 00 7A 7A 7A 00 7B 7B' + '7B 00 79 7B 7E 00 7B 7D 7D 00 7C 7C 7C 00 7E 7E' + '7E 00 7F 7F 7F 00 8A 84 78 00 BD A5 77 00 7F 7F' + '80 00 FF 00 FF 00 80 80 80 00 80 80 82 00 82 82' + '82 00 83 83 83 00 82 81 85 00 80 82 85 00 84 84' + '84 00 85 85 85 00 86 86 86 00 87 87 89 00 86 88' + '8C 00 88 88 88 00 89 89 89 00 8A 8A 8A 00 8B 8B' + '8A 00 8B 8B 8B 00 8B 8C 8B 00 8C 8C 8C 00 8E 8E' + '8E 00 8E 8E 8F 00 8F 8F 8F 00 90 90 90 00 91 91' + '91 00 92 92 92 00 93 93 93 00 91 92 94 00 94 94' + '94 00 95 95 95 00 96 96 96 00 98 97 98 00 98 98' + '98 00 99 99 99 00 9A 99 99 00 9A 9A 9A 00 9B 9B' + '9B 00 9C 9C 9C 00 9D 9D 9D 00 9E 9E 9D 00 9E 9E' + '9E 00 A0 99 8C 00 A0 A0 A0 00 A0 A0 A1 00 A1 A1' + 'A1 00 A2 A2 A2 00 A3 A3 A3 00 A4 A4 A4 00 A6 A6' + 'A6 00 A7 A7 A7 00 A8 A8 A8 00 A9 A9 A9 00 AA AA' + 'AA 00 AD AD AD 00 AD AE AE 00 B0 B0 B0 00 B2 B2' + 'B1 00 B3 B3 B3 00 B6 B7 B7 00 B7 B7 B7 00 B9 BA' + 'BA 00 BB BB BB 00 BC BC BB 00 BE BD BB 00 C0 A4' + '8D 00 C7 B2 89 00 C4 A8 94 00 C5 AB 95 00 CD B5' + 'A3 00 D1 BD B1 00 D3 BF B3 00 DF CC A6 00 D1 C5' + 'B0 00 F2 DE B8 00 FB E6 BF 00 C1 C1 C1 00 C5 C5' + 'C5 00 CF CF CF 00 DC D9 D2 00 D8 D8 D7 00 EB DC' + 'CE 00 E5 DC D5 00 EA DF D4 00 FE EE C7 00 EB E1' + 'D9 00 EA E3 DE 00 EF E7 E1 00 EF E8 E1 00 ED E8' + 'E2 00 ED E9 E5 00 EA E9 E8 00 F0 EC E7 00 F1 ED' + 'E9 00 F3 EE EB 00 F1 F0 EE 00 F3 F1 EF 00 F8 F3' + 'EF 00 F8 F4 F1 00 FF FF FF 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 FB' + 'FF 00 A4 A0 A0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 54 3B 37 34 31 2D 2A 25 1F 1C' + '17 1A 24 2A 00 00 5E 73 79 7F 7E 7A 7B 80 81 7D' + '77 61 19 1A 30 00 5A 84 66 68 62 67 76 65 67 64' + '78 7C 2F 1A 1E 00 4F 84 84 72 82 74 83 84 74 83' + '84 71 2F 1F 26 00 4A 5F 5C 58 56 52 50 4D 47 41' + '3F 38 2F 27 35 00 46 6D 5D 59 55 50 4C 48 43 3F' + '3C 36 35 2C 4E 3B 40 6F 6E 60 5B 57 53 51 4C 45' + '42 3E 39 35 3C 3A 1E 49 44 3B 32 2B 26 2C 48 48' + '46 42 3C 38 33 46 3D 2E 29 1B 16 11 10 12 13 12' + '0D 0C 0B 0A 3B 56 70 4B 20 0F 0E 18 28 22 1E 1D' + '19 15 12 14 56 00 00 75 6C 6B 69 63 21 6A 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF 00 00 FF FF 00 00 00 03' + '00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 01 00 00 80 FF 00 00 FF FF 00 00 FF FF' + '00 00 FF FF 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 20 00 00 00 00 00 40 04 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 A8 A8' + 'A8 A6 7F 80 80 B6 7A 7A 7B B5 76 77 76 B5 74 74' + '74 B5 70 70 72 B5 6B 6B 6B B5 66 66 68 B5 65 65' + '65 B5 5F 61 62 B5 5B 5C 5C B5 5F 5F 60 B6 67 67' + '67 B6 6A 6A 6A B2 4B 4B 4B 51 00 00 00 00 CC CD' + 'CD F9 E5 DC D5 FE EF E8 E1 FD F3 EE EB FD F1 ED' + 'E9 FD ED E8 E2 FD ED E9 E5 FD F1 F0 EE FD F3 F1' + 'EF FD F0 EC E7 FD EA E3 DE FE BE BD BB FE 7A 7A' + '7A FE 7A 7A 7A FB 62 62 62 96 00 00 00 00 C4 C4' + 'C3 FD FF FF FF FF CD B5 A3 FF D3 BF B3 FF C0 A4' + '8D FF D1 BD B1 FF EB E1 D9 FF C5 AB 95 FF D1 BD' + 'B1 FF C4 A8 94 FF EF E7 E1 FF EA E9 E8 FF 88 88' + '88 FF 7B 7B 7B FE 67 67 67 BC 00 00 00 00 B2 B2' + 'B2 FE FF FF FF FF FF FF FF FF EB DC CE FF F8 F3' + 'EF FF EA DF D4 FF F8 F4 F1 FF FF FF FF FF EA DF' + 'D4 FF F8 F4 F1 FF FF FF FF FF D8 D8 D7 FF 88 88' + '88 FF 7F 7F 7F FE 76 76 76 D5 00 00 00 00 AE AE' + 'AE FE BB BB BB FF B6 B7 B7 FF AD AE AE FF AA AA' + 'AA FF A6 A6 A6 FF A3 A3 A3 FF A0 A0 A1 FF 9C 9C' + '9C FF 98 97 98 FF 95 95 95 FF 8F 8F 8F FF 88 88' + '88 FF 83 83 83 FF 88 88 88 EC 00 00 00 20 AB AB' + 'AB FF C1 C1 C1 FF B7 B7 B7 FF B0 B0 B0 FF A9 A9' + 'A9 FF A3 A3 A3 FF A0 A0 A0 FF 9D 9D 9D FF 99 99' + '99 FF 95 95 95 FF 93 93 93 FF 8E 8E 8E FF 8C 8C' + '8C FF 86 86 86 FF A0 A0 A0 F5 00 00 00 3D A5 A5' + 'A5 FF CF CF CF FF C5 C5 C5 FF BC BC BB FF B3 B3' + 'B3 FF AD AD AD FF A7 A7 A7 FF A4 A4 A4 FF A0 A0' + 'A0 FF 9A 9A 9A FF 98 98 98 FF 94 94 94 FF 90 90' + '90 FF 8C 8C 8C FF 92 92 92 FA 4C 4C 4C 67 8A 8A' + '8A FF 9E 9E 9D FF 9A 99 99 FF 92 92 92 FF 8B 8B' + '8A FF 85 85 85 FF 82 82 82 FF 86 86 86 FF 9D 9D' + '9D FF 9D 9D 9D FF 9B 9B 9B FF 98 98 98 FF 93 93' + '93 FF 8F 8F 8F FF 8A 8A 8A FC 77 77 77 82 98 99' + '9B FE 86 88 8C FF 80 82 85 FF 79 7B 7E FF 73 75' + '78 FF 6F 71 74 FF 6F 70 72 FF 71 71 71 FF 72 72' + '72 FF 71 71 71 FF 6F 6F 6F FF 6C 6C 6C FF 68 68' + '68 FF 65 65 65 FD 89 89 89 D5 6C 6C 6C 42 DC D9' + 'D2 FF A0 99 8C FF 8A 84 78 FF 7A 75 6C FF 75 72' + '6D FF 7B 79 7B FF 82 81 85 FF 7E 7E 7F FC 7D 7D' + '7D FA 7B 7B 7B FA 79 79 79 FA 75 75 75 FA 6F 6F' + '6F FA 6F 6F 6F F3 83 83 83 5B 00 00 00 00 F8 F7' + 'E9 7F FE EE C7 FF FB E6 BF FF F2 DE B8 FF DF CC' + 'A6 FF C7 B2 89 FF BD A5 77 FF D1 C5 B0 FE E9 E9' + 'E9 40 E9 E9 E9 40 E8 E8 E8 40 E8 E8 E8 40 E7 E7' + 'E7 40 E7 E7 E7 40 EE EE EE 3F B3 B3 B3 1E 00 00' + '00 00 FE EE C7 40 FB E6 BF 40 F2 DE B8 40 DF CC' + 'A6 40 C7 B2 89 40 BD A5 77 40 C4 B0 87 2C 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + '00 00 FF FF 00 00 00 01 00 00 00 01 00 00 00 01' '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 B8 47 1D 00 C9 45' - '0E 00 CB 55 1F 00 D7 5F 1F 00 DE 65 1E 00 D0 59' - '28 00 CE 68 3F 00 E2 69 24 00 E7 77 27 00 EB 7B' - '2F 00 F0 7F 39 00 C7 6B 4E 00 DE 7A 45 00 C9 7B' - '60 00 F3 8B 32 00 FD 9C 35 00 BE 80 70 00 BF 91' - '7F 00 F0 96 46 00 F6 99 44 00 F7 99 44 00 E8 8D' - '51 00 ED 8A 5E 00 FD A4 44 00 FF AA 46 00 FF AA' - '49 00 F7 A2 52 00 FF B2 59 00 FF B5 59 00 FF B2' - '5D 00 FF B3 5E 00 C4 84 72 00 EF A1 6B 00 FF B4' - '62 00 FF B5 68 00 F2 A7 73 00 F8 B3 71 00 F8 B5' - '7E 00 FF C3 73 00 FF C5 7A 00 FF C7 7A 00 96 8A' - '85 00 96 90 8E 00 99 91 8E 00 92 91 90 00 96 95' - '94 00 9F 97 94 00 9A 98 97 00 9C 99 98 00 9E 9C' - '9C 00 A6 96 91 00 AD 9C 95 00 B6 9D 93 00 A6 A0' - '9F 00 BB A0 94 00 B7 A0 9B 00 B1 A2 9F 00 BB A0' - '9A 00 A6 A2 A3 00 AE A7 A5 00 AC A7 A8 00 B3 A3' - 'A3 00 BF AB A6 00 BF AE AB 00 B7 AF B0 00 C9 8E' - '82 00 C4 95 8B 00 C9 94 88 00 CD 9A 8E 00 D9 9C' - '85 00 C3 9C 95 00 C2 9E 95 00 CE 9D 91 00 D1 9F' - '94 00 CD A0 90 00 C6 AA 9F 00 DA A3 98 00 DF AA' - '98 00 DF A9 9A 00 DC AB 9F 00 EC B5 8D 00 F1 B2' - '83 00 F3 B3 8B 00 F5 B6 8A 00 F7 B5 8E 00 F7 B8' - '8A 00 EE AC 93 00 F6 BB 99 00 CF B3 A9 00 D9 B0' - 'A0 00 D1 B7 B1 00 D9 BD B2 00 E9 BC A3 00 E5 BE' - 'AF 00 EA BC A8 00 FD C3 8C 00 FF D1 8F 00 FE C7' - '92 00 F8 C3 98 00 F9 C2 9B 00 FC C1 99 00 FF C8' - '9B 00 FA CF 9C 00 FB D0 97 00 FD CD A3 00 F8 CE' - 'AE 00 FC D0 A1 00 F7 D0 AE 00 FF DD AC 00 F4 CB' - 'B0 00 F2 CA B5 00 FF DF B2 00 FF E2 AD 00 FE E0' - 'B3 00 FF E1 B3 00 FF E4 B6 00 FE E0 BA 00 D3 C0' - 'C2 00 DE C1 C1 00 DF C4 C3 00 EF CA C3 00 EA D0' - 'CF 00 F6 D7 CE 00 F6 DA CF 00 F3 DD CD 00 E0 D0' - 'D1 00 EE D6 D4 00 E3 D4 D8 00 FA DF DB 00 FB E2' - 'C7 00 FF E8 C0 00 FF E8 C1 00 FF ED CB 00 FF EC' - 'CC 00 FF ED CC 00 EF E0 DF 00 FB E2 D3 00 FF EB' - 'D2 00 FF F0 D5 00 FF F1 D6 00 FC E5 E0 00 FC E7' - 'E4 00 F0 E6 EA 00 F7 ED ED 00 FB EB EA 00 FF F4' - 'E0 00 FF F9 ED 00 FF FA F6 00 FF F6 FB 00 7F 7F' - '7F 00 F7 B8 96 00 C1 AC A5 00 D7 AA A4 00 C4 B1' - 'AD 00 D7 B0 A1 00 DA B5 A6 00 DA B4 AA 00 D9 BB' - 'AF 00 C2 BC BD 00 D0 B3 B2 00 DA BD B4 00 D0 BB' - 'BB 00 E0 B2 A5 00 EE BD A2 00 E3 B4 AA 00 E4 BB' - 'AF 00 F1 B6 A7 00 F6 BE A2 00 F0 BA AB 00 E5 BE' - 'B3 00 EC BC B0 00 E2 BF BB 00 FD C9 86 00 F4 C4' - '97 00 FF CB 95 00 FB C4 9C 00 FE CC 9D 00 FD D1' - '93 00 FE D5 9C 00 FE DB 9E 00 EC C5 AF 00 F4 C2' - 'A5 00 FD CD A1 00 F5 C8 AA 00 F9 CB AB 00 FD D4' - 'A3 00 FF DE A4 00 FD D3 AC 00 FD DB A9 00 E9 C2' - 'B7 00 F1 C2 B5 00 F3 CA B3 00 FA D2 B3 00 FD DC' - 'B5 00 FB D6 BB 00 FB DC BC 00 FF E0 A5 00 FF E2' - 'AE 00 FF E3 B2 00 FE E6 BE 00 FF E8 BF 00 C3 BF' - 'C0 00 D0 C1 C1 00 DB C5 C0 00 DD C6 CC 00 DF CA' - 'CB 00 DF CF D0 00 EB C5 C0 00 EB CF CA 00 F9 CD' - 'C6 00 FC D3 C5 00 FC DC C5 00 F0 D3 CA 00 FF D7' - 'CB 00 FA DD CA 00 E0 CF D0 00 E5 D7 DA 00 FA D8' - 'D1 00 F0 DC D9 00 FE E4 C1 00 FF E9 C3 00 FF EE' - 'CE 00 F0 E0 D4 00 FC E2 D3 00 FF EE D0 00 F0 E1' - 'DF 00 FD E5 DA 00 FE EB DC 00 FF F0 D5 00 FE F2' - 'DD 00 ED DE E0 00 F0 DF E2 00 F1 E3 E3 00 FD E9' - 'E5 00 F0 E7 EF 00 F1 EB EC 00 FE F5 E3 00 FF F3' - 'EB 00 FF FA EE 00 F3 EE F0 00 FE F3 F2 00 FF FA' - 'F1 00 FF F7 FF 00 FF F9 FC 00 7F 7F 7F 00 09 48' - 'E9 77 B8 10 E9 77 FF FF FF FF C9 F1 E7 77 16 EA' - '4B 00 F8 00 00 00 40 B4 0C 01 66 00 00 00 58 1A' - '95 00 00 00 00 00 00 00 00 2F 31 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 45 4F 48 3D 2D 00 00' - '00 00 00 00 00 00 00 00 00 57 8E 81 50 47 3C 2E' - '00 00 00 00 00 00 00 00 00 63 8D 95 90 7F 4E 43' - '3E 30 00 00 00 00 00 00 59 67 65 66 7D 8F 88 7A' - '4D 42 38 3B 00 00 00 00 5A 6D 56 22 1B 21 5F 80' - '7E 78 4A 20 41 00 00 00 6F 74 54 29 1E 14 0A 0D' - '46 77 76 49 40 00 00 00 82 83 58 61 27 1C 15 09' - '04 06 0E 44 3A 00 00 5B 8A 87 6A 64 62 28 1F 18' - '0F 05 02 01 11 00 00 5C 92 8C 86 75 69 55 60 1D' - '19 10 08 03 0C 32 2B 7B 93 92 8C 87 84 70 53 26' - '23 1A 0B 13 07 36 2C 91 94 93 92 8B 85 84 72 6B' - '52 24 17 25 16 39 00 3F 79 7C 89 5E 4C 6E 6C 73' - '71 68 4B 35 12 2A 00 00 00 00 33 00 00 00 00 37' - '5D 51 34 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00' + '00 00 80 FF 00 00 FF FF 00 00 FF FF 00 00 28 00' + '00 00 20 00 00 00 40 00 00 00 01 00 04 00 00 00' + '00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 08 77 77 77 77 77 77 77 77 77' + '77 77 77 77 70 00 07 88 88 88 88 88 78 87 87 87' + '87 87 87 87 80 00 07 88 8F FF FF FF FF FF FF FF' + 'FF F8 88 88 70 00 07 88 FF F4 FF F4 F4 FF FF FF' + 'FF FF 88 88 87 00 07 88 FF F4 F4 4F 44 FF 44 4F' + '4F FF 88 88 87 00 08 78 FF FF 4F FF FF 4F 4F F4' + 'FF FF 88 88 87 00 08 78 8F FF FF FF FF FF FF FF' + 'FF F8 88 88 87 00 08 78 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 08 78 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 08 78 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 08 77 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 08 77 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 08 77 88 88 88 88 88 88 88 88' + '88 88 88 88 88 87 08 77 78 88 88 88 88 88 88 88' + '88 88 88 88 88 87 08 77 77 77 77 77 77 77 78 88' + '88 88 88 88 88 87 08 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 70 08 77 77 77 77 77 77 77 77 77' + '77 77 77 77 78 00 00 F6 66 66 66 66 66 88 88 88' + '88 88 88 88 80 00 00 FE 6E 6E 6E 6E 6E 80 00 00' + '00 00 00 00 00 00 00 0F EF EF EF EF EF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 80 00' + '00 07 80 00 00 07 80 00 00 07 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 01 80 00 00 01 80 00 00 01 80 00 00 01 80 00' + '00 00 80 00 00 00 80 00 00 00 80 00 00 01 80 00' + '00 03 C0 00 00 07 C0 01 FF FF E0 03 FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 20 00 00 00 40 00' + '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 C0 C0 C0 00 C0 DC' + 'C0 00 F0 CA A6 00 64 64 64 00 66 66 66 00 69 69' + '69 00 6A 6A 6A 00 6C 6C 6C 00 6F 6F 6F 00 70 70' + '70 00 70 71 71 00 71 71 71 00 72 72 72 00 73 73' + '73 00 74 74 74 00 76 76 76 00 77 77 77 00 78 78' + '78 00 79 79 79 00 79 7A 7B 00 7A 7A 7A 00 7B 7B' + '7B 00 7B 7B 7D 00 7A 7B 7E 00 7B 7C 7C 00 7C 7C' + '7C 00 7C 7D 7D 00 7D 7D 7D 00 7C 7C 7F 00 7C 7D' + '7F 00 7D 7E 7E 00 7E 7E 7E 00 7F 7F 7F 00 81 7F' + '7D 00 A5 7C 61 00 8B 87 7E 00 8D 88 7F 00 A8 82' + '68 00 B0 8C 6F 00 B1 8E 6F 00 B2 90 70 00 B8 97' + '7B 00 B8 98 7E 00 7F 7F 80 00 FF 00 FF 00 7F 80' + '80 00 80 80 80 00 81 81 81 00 80 81 83 00 81 82' + '82 00 82 82 82 00 82 82 83 00 83 83 83 00 83 83' + '84 00 83 84 84 00 84 84 84 00 85 85 84 00 85 85' + '85 00 86 86 85 00 85 85 86 00 84 85 87 00 86 85' + '86 00 86 86 86 00 87 87 87 00 87 88 8A 00 88 88' + '88 00 88 88 89 00 89 89 89 00 8A 8A 8A 00 8B 8B' + '8B 00 8B 8D 8F 00 8C 8C 8C 00 8E 8E 8E 00 93 8E' + '84 00 99 94 8A 00 9D 97 8C 00 8F 90 93 00 90 90' + '90 00 92 90 90 00 93 92 91 00 92 92 92 00 93 93' + '93 00 93 94 96 00 94 94 94 00 95 94 94 00 95 95' + '95 00 96 96 96 00 98 98 98 00 9B 99 99 00 98 99' + '9B 00 9A 9A 9A 00 9C 9B 9B 00 9A 9B 9D 00 9C 9C' + '9C 00 9D 9D 9D 00 9E 9E 9E 00 BB 9C 81 00 A0 9D' + '98 00 A0 9F 9F 00 B1 A3 86 00 B9 AB 8E 00 BE AD' + '8C 00 A6 A1 93 00 AA A3 96 00 B0 A9 9B 00 9F 9F' + 'A0 00 A0 A0 A0 00 A2 A2 A1 00 A2 A2 A2 00 A4 A4' + 'A3 00 A3 A3 A4 00 A4 A4 A4 00 A6 A6 A6 00 A8 A8' + 'A8 00 AA AA A9 00 AA AA AA 00 AC AB AB 00 AB AB' + 'AD 00 AB AC AC 00 AC AC AC 00 AE AE AE 00 B7 B1' + 'A2 00 B0 B0 B0 00 B2 B2 B2 00 B3 B3 B4 00 B4 B4' + 'B4 00 B7 B7 B7 00 B9 B9 B7 00 B8 B8 B8 00 BA BA' + 'BA 00 BC BC BC 00 BD BD BD 00 BE BD BC 00 BE BE' + 'BE 00 C1 A6 8C 00 C4 A8 91 00 C5 AA 93 00 C1 B2' + '96 00 C5 B7 99 00 CB B2 9E 00 CB BD A0 00 CF BA' + 'A7 00 C0 B7 A9 00 C0 B8 A9 00 CE B9 A8 00 D0 BB' + 'A7 00 D2 BF AD 00 D0 C2 A7 00 D5 C7 AB 00 D6 C3' + 'B3 00 DB CC B1 00 DE D0 B4 00 E5 DA B7 00 E1 D3' + 'B8 00 E4 D6 BB 00 E3 DB BC 00 E8 DF BE 00 C0 C0' + 'C0 00 C2 C1 C0 00 C2 C2 C2 00 C4 C4 C4 00 C7 C6' + 'C6 00 C7 C7 C8 00 CA CA CA 00 CC CC CC 00 CF CD' + 'CF 00 DD CF C2 00 D9 D5 CA 00 D3 D2 D0 00 D2 D2' + 'D2 00 D5 D5 D5 00 D6 D6 D6 00 D9 D9 D9 00 DF DD' + 'DA 00 DE DE DD 00 DE DE DE 00 E0 D2 C5 00 E1 D3' + 'C7 00 E3 D7 CC 00 E5 D8 CE 00 E2 D9 D1 00 E4 D9' + 'D0 00 E7 DB D1 00 E7 DE D5 00 E6 DE D7 00 E8 DE' + 'D5 00 E5 DE D9 00 E6 DF DC 00 EB E2 C1 00 EC E6' + 'C7 00 EF E8 CF 00 E9 E0 D7 00 E1 E0 DF 00 E6 E2' + 'DD 00 EB E3 DB 00 ED E5 DE 00 ED E6 DF 00 EF E6' + 'DF 00 F1 EB D6 00 F3 ED D9 00 E1 E1 E1 00 E7 E6' + 'E6 00 EC E6 E1 00 EE E6 E0 00 ED E7 E2 00 EE E7' + 'E2 00 EF E8 E1 00 EE E8 E2 00 EF EA E3 00 EE E8' + 'E4 00 EC E9 E6 00 F0 EA E5 00 F1 EC E6 00 F3 ED' + 'E6 00 F1 EC E8 00 F2 ED E8 00 F2 EE E9 00 F3 EE' + 'EA 00 F0 EF EC 00 F0 EF EE 00 F6 F0 E0 00 F5 F2' + 'E5 00 F7 F2 E4 00 F5 F0 EB 00 F0 F0 EE 00 F2 F0' + 'EE 00 F5 F0 EC 00 F6 F2 EE 00 F8 F3 E8 00 F9 F6' + 'EB 00 F1 F1 F1 00 F2 F1 F0 00 F3 F3 F3 00 F4 F4' + 'F4 00 F8 F4 F1 00 F8 F5 F2 00 F9 F7 F5 00 FB F8' + 'F5 00 FB F8 F6 00 F0 FB FF 00 A4 A0 A0 00 80 80' + '80 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 E7 FF 00 00 E0 FF 00 00 E0 3F' - '00 00 E0 0F 00 00 C0 03 00 00 C0 01 00 00 C0 01' - '00 00 C0 01 00 00 80 01 00 00 80 00 00 00 00 00' - '00 00 00 00 00 00 80 00 00 00 F7 87 00 00 FF FF' - '00 00 FF FF 00 00 28 00 00 00 30 00 00 00 60 00' - '00 00 01 00 20 00 00 00 00 00 00 24 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -3238,7 +6680,46 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 89' + '77 45 40 3D 3E 3A 39 39 39 38 36 35 34 32 27 27' + '26 25 21 1F 20 1C 1B 1B 1C 1C 46 00 00 00 00 07' + 'B6 88 81 7A 79 74 74 72 69 66 62 5F 5D 5B 56 55' + '4F 4E 4A 41 36 3B 48 48 46 44 49 00 00 00 00 69' + '88 A8 C1 C6 C6 CB D8 D8 DA DB DE DF E6 E0 E8 DF' + 'DC D7 D3 D1 C8 86 35 19 17 42 3C 00 00 00 00 4F' + '7D C7 E9 98 E6 DF D7 E0 D4 B9 C0 CA BE E0 D7 DE' + 'F1 E9 D4 C9 D5 EE 5F 1A 20 4A 35 26 00 00 00 39' + '70 B4 F5 97 8E 29 2C 2D 2F B8 9C BD CC 67 99 8D' + '2E 92 BA F1 F1 F4 8B 26 1B 41 3D 19 00 00 00 3B' + '75 AF F2 AD 31 D5 F1 F3 CB 94 2F B7 DC 8F BC B7' + '30 F1 EA EA EA F5 A5 39 22 3E 57 1B 00 00 00 39' + '61 88 B6 F0 C2 BF E8 E8 EA EF EF E7 ED E1 BB D9' + 'ED E2 E1 E2 E7 D0 66 40 39 26 5D 19 00 00 00 45' + '57 A6 89 A7 A6 8C 89 89 89 88 88 86 84 83 82 81' + '81 7F 7A 7A 7A 71 4C 46 3C 36 54 13 00 00 00 57' + '44 B0 8C 88 85 81 7E 7A 77 76 75 69 71 66 64 61' + '5D 5D 58 54 54 4F 4F 4B 46 3A 4C 49 22 00 00 5E' + '26 B3 A5 89 85 82 81 7C 79 76 75 73 72 66 64 61' + '5E 5D 5C 57 54 54 4E 4E 4B 42 3E 57 20 00 00 72' + '16 CF A7 07 89 85 83 7F 7A 77 76 75 66 66 66 61' + '62 5E 5E 5C 58 57 4F 54 4B 4A 3E 5E 12 00 00 76' + '17 B6 A9 A6 07 86 84 81 7E 79 77 75 72 70 66 64' + '64 62 5E 5E 5D 5C 54 54 4F 4F 42 4F 22 00 00 7B' + '21 B2 AB A9 A6 8C 88 84 7F 7B 76 73 73 70 66 65' + '65 61 62 61 5D 5D 5D 58 4F 4F 49 4B 61 22 00 7B' + '4B AA B1 AA A9 A6 8C 87 82 7F 78 77 73 70 66 65' + '64 62 64 61 61 5D 5D 5A 54 4E 4C 4C 70 22 00 81' + '62 4C 7E 7F 7B 77 75 66 62 5D 5A 54 4F 4F 58 66' + '69 66 66 66 64 61 5D 58 54 4F 46 54 7D 26 00 8A' + '65 58 4C 48 3F 27 20 19 17 15 14 14 14 11 10 35' + '46 46 41 40 3E 3B 36 27 1C 18 1C 5C 66 00 00 AC' + '7C 63 60 59 53 4D 47 43 37 24 23 1D 1A 1C 1C 19' + '18 17 16 16 14 12 0F 0E 0C 0A 14 57 00 00 00 00' + 'AE 96 95 80 6F 6E 6D 52 51 50 2B 2A 28 1E 1C 1C' + '1C 1B 19 18 16 15 12 0F 0D 0B 58 00 00 00 00 00' + 'E4 A1 A0 9E 9D 9B 9A 93 91 90 6B 6A 6C 68 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 EC EB E5 E3 CE CD C5 C4 C3 A3 9F A2 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -3251,13 +6732,20 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 80 00 00 07 80 00 00 07 80 00' + '00 07 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 01 80 00 00 01 80 00' + '00 01 80 00 00 01 80 00 00 00 80 00 00 00 80 00' + '00 00 80 00 00 01 80 00 00 03 C0 00 00 07 C0 00' + 'FF FF E0 01 FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 28 00' + '00 00 20 00 00 00 40 00 00 00 01 00 20 00 00 00' + '00 00 80 10 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D5 C9' - 'BE 1C DC C8 BE 54 D4 C6 BE 54 CF C4 BF 54 CB C4' - 'BF 54 CF C7 C2 41 D2 CA C6 2E D6 CE C9 1B DA D3' - 'CD 0C D8 D2 C9 03 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -3266,10 +6754,6 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 DC C8' - 'BE 54 F0 C5 BC FF DA BF BE FF CA BA BF FF BF B8' - 'BF FF C9 C2 C9 C3 D4 CC D4 8A DF D8 DF 54 EA E7' - 'EA 26 E6 E2 DF 0A 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -3278,11 +6762,6 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D8 BE' - 'B4 54 E5 A7 9E FF DE 9C 90 FF D5 96 89 FF CA 95' - '89 FF C6 A1 9B EB C2 AE AD D8 BF BD BF C6 CA C9' - 'CA 96 D3 D0 D1 6B DA D2 D4 43 E1 DA DB 24 DE D7' - 'D4 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -3290,11 +6769,6 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D6 B7' - 'AB 54 E0 91 84 FF E3 95 84 FF E3 9B 88 FF DF A2' - '8F FF D4 9B 8C FF C9 99 8E FF BF 9D 95 FF BF A9' - 'A3 DF C4 B8 B6 BD CF C9 CF 98 DA D4 DA 6C DA D5' - 'D6 47 D1 CC C4 27 D8 D1 CB 17 D8 D0 C9 0A 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -3302,11 +6776,6 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D6 B2' - 'A4 54 E0 84 6F FF EA AB 9A FF F5 C9 BA FF FF DF' - 'D0 FF F4 AF 9A FF E9 8C 75 FF DF 78 5F FF C9 88' - '74 FF BA 9A 8F FF B0 AF B0 FF BA B9 BA DB C5 C4' - 'C5 AE D0 D0 D0 78 E4 DF E4 46 E5 DD DF 1E 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -3314,446 +6783,1095 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 DF C7 BD 71 E7 B4' - 'A6 C6 EA 90 79 FF ED A1 8F FF F4 C1 B6 FF FF F2' - 'EF FF FB E2 DD FF F7 D2 CA FF F4 C1 B4 FF E6 AA' - '98 FF D6 98 84 FF C5 89 7A FF C1 94 8B F3 BD A4' - 'A1 E4 BA BA BA D1 C8 C3 C8 B1 CF C9 CD 89 D0 CD' - 'CA 5B D7 D4 D1 39 D8 D3 CD 1A 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 E5 C4 BA A9 F1 B9' - 'A8 FF F4 A9 8A FF F0 A0 8A FF F4 BC B0 FF FF FC' - 'FF FF FF FC FF FF FF F6 F8 FF FF EC EA FF F8 CE' - 'C3 FF EC B3 A2 FF DA 99 89 FF D2 96 89 FF C9 97' - '8D FF BF 9C 95 FF BF A3 9F EF C1 AF AD D5 C5 C1' - 'C0 B0 CF CC CD 8A D3 CF CF 60 D1 C9 C4 33 D4 CE' - 'C8 22 D5 CE C6 10 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 8C 8C 8C 14 77 77' + '77 5C 4E 4E 4E 82 4C 4C 4C 82 48 4A 4A 82 4A 4A' + '4A 82 46 46 48 82 46 46 46 82 46 46 46 82 46 46' + '46 82 44 46 46 82 44 44 44 82 42 42 42 82 40 42' + '42 82 40 40 42 82 40 40 40 82 40 40 40 82 3E 3E' + '3E 82 3C 3E 3E 82 3A 3C 3C 82 38 3A 3A 82 3A 3A' + '3A 82 38 38 38 82 36 36 36 82 36 36 36 82 38 38' + '38 82 38 38 38 82 2F 2F 2F 65 00 00 00 16 00 00' + '00 00 00 00 00 00 00 00 00 00 BC BC BC 50 E3 E3' + 'E3 E1 BB BB BB F7 AF AF AF F6 A9 A9 A8 F5 A8 A7' + 'A7 F5 A4 A3 A3 F5 A3 A3 A2 F5 A1 A1 A0 F5 9F 9E' + '9E F5 9D 9D 9D F5 9B 9A 9A F5 99 97 97 F5 95 94' + '94 F5 93 92 92 F5 91 90 8F F5 90 8E 8E F5 8D 8C' + '8C F5 8A 8A 8A F5 87 87 87 F5 84 84 83 F5 7F 7F' + '7F F6 81 81 81 F7 86 86 86 F7 86 86 86 F7 85 85' + '85 F7 84 84 85 FA 7D 7D 7D D1 0C 0C 0C 3F 00 00' + '00 00 00 00 00 00 00 00 00 00 73 73 73 6E BA BB' + 'BA F7 C7 C6 C6 FF E5 DE D9 FF E9 E0 D7 FF E9 E0' + 'D7 FF EE E6 E0 FF EE E8 E4 FF EE E8 E3 FF F0 EA' + 'E5 FF F1 EC E6 FF F2 ED E8 FF F2 EE E9 FF F3 EF' + 'EB FF F3 EF EB FF F3 EF EB FF F3 EF EA FF F1 EC' + 'E8 FF EF EA E3 FF ED E7 E2 FF EC E6 E1 FF E6 E2' + 'DD FF B9 B9 B7 FF 80 80 80 FF 79 79 79 FF 77 77' + '77 FF 85 85 85 FF 7E 7E 7F EA 2C 2C 2C 62 00 00' + '00 00 00 00 00 00 00 00 00 00 55 55 55 78 AB AC' + 'AC F9 E1 E0 DF FF F5 F0 EC FF D0 BB A7 FF F5 F0' + 'EB FF F2 EE E9 FF EF E9 E3 FF F3 EE EA FF EE E8' + 'E2 FF E3 D7 CC FF E8 DE D5 FF ED E5 DE FF E7 DE' + 'D5 FF F3 EE EA FF EF E9 E3 FF F3 ED E9 FF F8 F4' + 'F1 FF F5 F0 EB FF EE E7 E2 FF EB E3 DB FF EF E8' + 'E2 FF F2 F1 F0 FF 99 99 98 FF 7B 7B 7B FF 7C 7C' + '7C FF 89 89 89 FF 7E 7E 7E F7 3D 3D 3D 80 00 00' + '00 00 00 00 00 00 00 00 00 00 4C 4C 4C 88 9F 9F' + 'A0 FC DF DD DA FF FB F8 F6 FF CE B9 A8 FF C4 A8' + '91 FF A5 7C 61 FF A8 82 68 FF B0 8C 6F FF B2 91' + '70 FF E1 D3 C7 FF D6 C3 B3 FF E7 DB D1 FF EF E6' + 'DF FF BB 9C 81 FF D2 BF AD FF C1 A6 8C FF B1 8E' + '6F FF CB B2 9E FF E5 D8 CE FF F9 F4 F1 FF F6 F3' + 'EF FF FB F9 F5 FF BE BD BC FF 7F 7F 7F FF 7A 7A' + '7A FF 86 86 86 FF 83 83 84 FE 45 45 45 A5 00 00' + '00 00 00 00 00 00 00 00 00 00 54 54 54 91 A3 A3' + 'A4 FD D3 D2 D0 FF F8 F5 F2 FF DD CF C2 FF B8 98' + '7E FF EF E8 E1 FF F8 F4 F1 FF F9 F7 F5 FF ED E6' + 'DF FF CF BA A7 FF B3 90 71 FF E0 D2 C5 FF F3 ED' + 'E6 FF C5 AA 93 FF E4 D9 D0 FF E0 D2 C5 FF B8 97' + '7B FF F7 F3 EF FF F6 F2 EF FF F6 F3 EF FF F6 F2' + 'EE FF FB F8 F5 FF C2 C1 C0 FF 82 82 82 FF 7D 7D' + '7D FF 84 84 84 FF 92 92 92 FE 55 55 55 BA 00 00' + '00 00 00 00 00 00 00 00 00 00 58 58 58 99 9A 9A' + '9A FE BB BB BB FF DE DE DD FF F4 F4 F4 FF E6 DF' + 'DC FF E6 DE D7 FF F2 F1 EE FF F3 F0 EE FF F3 F1' + 'EE FF F3 F3 F3 FF F3 F3 F3 FF F1 F0 EF FF F1 F0' + 'EF FF F0 EF EE FF E2 D9 D1 FF EC E9 E6 FF F1 F1' + 'F1 FF F0 F0 EE FF F0 EF EC FF F0 EF ED FF F1 F0' + 'EE FF E7 E6 E6 FF 9E 9E 9E FF 85 85 85 FF 82 82' + '82 FF 7E 7E 7E FF 97 97 97 FF 63 63 63 D3 00 00' + '00 1F 00 00 00 00 00 00 00 00 61 61 61 9D 92 92' + '92 FE C2 C2 C2 FF BC BC BC FF C4 C4 C4 FF C2 C2' + 'C2 FF BE BE BE FF BC BC BC FF BB BB BB FF BC BC' + 'BC FF BB BB BB FF BA BA BA FF B8 B8 B8 FF B5 B4' + 'B5 FF B3 B3 B4 FF B2 B2 B2 FF B0 B0 B0 FF B0 B0' + 'B0 FF AC AD AD FF AA AA AA FF AB AB AB FF AA AA' + 'AA FF A0 A0 A0 FF 8B 8B 8B FF 87 87 87 FF 83 83' + '83 FF 81 81 81 FF 91 91 91 FF 69 69 69 E7 05 05' + '05 2F 00 00 00 00 00 00 00 00 77 77 77 A0 86 85' + '86 FF D2 D2 D2 FF BE BE BE FF BA BA BA FF B7 B7' + 'B7 FF B1 B1 B1 FF AC AC AC FF A9 A9 A9 FF A7 A7' + 'A7 FF A4 A4 A4 FF A2 A2 A2 FF A0 A0 A0 FF A0 A0' + 'A0 FF 9E 9E 9E FF 9B 9B 9B FF 9A 9A 9A FF 97 97' + '97 FF 95 95 95 FF 93 93 93 FF 91 91 91 FF 8F 8F' + '8F FF 8F 8F 8F FF 8D 8D 8D FF 8A 8A 8A FF 87 87' + '87 FF 83 83 83 FF 8B 8B 8B FF 88 88 89 FE 09 09' + '09 4D 00 00 00 00 00 00 00 00 84 84 84 A4 7D 7D' + '7D FF D9 D9 D9 FF C1 C1 C1 FF BC BC BC FF B7 B7' + 'B7 FF B2 B2 B2 FF B0 B0 B0 FF AC AC AC FF A9 A9' + 'A9 FF A5 A5 A5 FF A3 A3 A3 FF A2 A2 A2 FF A1 A1' + 'A1 FF 9F 9F 9F FF 9B 9B 9B FF 9A 9A 9A FF 99 99' + '99 FF 96 96 96 FF 94 94 94 FF 92 92 92 FF 91 91' + '91 FF 8F 8F 8F FF 8D 8D 8D FF 8C 8C 8C FF 89 89' + '89 FF 86 86 86 FF 84 84 84 FF 91 91 91 FF 0F 0F' + '0F 61 00 00 00 00 00 00 00 00 92 92 92 AC 76 76' + '76 FF E1 E1 E1 FF C5 C5 C5 FF C0 C0 C0 FF BC BC' + 'BC FF B7 B7 B7 FF B3 B3 B3 FF AE AE AE FF AA AA' + 'AA FF A6 A6 A6 FF A4 A4 A4 FF A3 A3 A3 FF 9E 9E' + '9E FF 9F 9F 9F FF 9D 9D 9D FF 9B 9B 9B FF 9B 9B' + '9B FF 98 98 98 FF 97 97 97 FF 96 96 96 FF 93 93' + '93 FF 91 91 91 FF 8F 8F 8F FF 8F 8F 8F FF 8B 8B' + '8B FF 89 89 89 FF 84 84 84 FF 97 97 97 FF 2D 2D' + '2D 88 00 00 00 12 00 00 00 00 98 98 98 B1 77 77' + '77 FF DE DE DE FF C7 C7 C7 FF C3 C3 C3 FF BF BF' + 'BF FF B9 B9 B9 FF B5 B5 B5 FF B0 B0 B0 FF AC AC' + 'AC FF A8 A8 A8 FF A6 A6 A6 FF A4 A4 A4 FF A1 A1' + 'A1 FF A0 A0 A0 FF 9E 9E 9E FF 9C 9C 9C FF 9C 9C' + '9C FF 9B 9B 9B FF 99 99 99 FF 98 98 98 FF 96 96' + '96 FF 94 94 94 FF 91 91 91 FF 90 90 90 FF 8E 8E' + '8E FF 8D 8D 8D FF 86 86 86 FF 8D 8D 8D FF 5E 5E' + '5E AB 00 00 00 1B 00 00 00 00 A3 A3 A3 B7 7D 7D' + '7D FF D6 D6 D6 FF CC CC CC FF C7 C7 C7 FF C3 C3' + 'C3 FF BE BE BE FF B9 B9 B9 FF B4 B4 B4 FF AF AF' + 'AF FF AA AA AA FF A5 A5 A5 FF A2 A2 A2 FF A1 A1' + 'A1 FF A0 A0 A0 FF 9E 9E 9E FF 9D 9D 9D FF 9D 9D' + '9D FF 9A 9A 9A FF 9B 9B 9B FF 9A 9A 9A FF 97 97' + '97 FF 96 96 96 FF 95 95 95 FF 93 93 93 FF 8F 8F' + '8F FF 8E 8E 8E FF 88 88 88 FF 8A 8A 8A FF 93 93' + '93 D7 00 00 00 2F 00 00 00 00 A4 A4 A4 BF 8A 8A' + '8A FF CA CA CA FF D5 D5 D5 FF CB CB CB FF C7 C7' + 'C8 FF C2 C2 C2 FF BE BE BE FF B7 B7 B7 FF B2 B2' + 'B2 FF AE AE AE FF A9 A9 A9 FF A5 A5 A5 FF A2 A2' + 'A2 FF A0 A0 A0 FF 9E 9E 9E FF 9D 9D 9D FF 9C 9C' + '9C FF 9B 9B 9B FF 9C 9C 9C FF 9B 9B 9B FF 99 99' + '99 FF 97 97 97 FF 96 96 96 FF 93 93 93 FF 90 90' + '90 FF 8D 8D 8D FF 8A 8A 8A FF 8B 8B 8B FF 9C 9C' + '9C E1 0F 0F 0F 40 00 00 00 00 AD AD AD D5 9B 9B' + '9B FF 8B 8B 8B FF AD AD AD FF AD AD AD FF AC AB' + 'AB FF A7 A7 A7 FF A3 A3 A3 FF 9E 9E 9E FF 9B 9B' + '9A FF 96 96 96 FF 94 94 94 FF 91 91 91 FF 8F 8F' + '8E FF 8F 8E 8E FF 92 92 92 FF 9F 9F 9F FF 9F 9F' + '9F FF 9E 9E 9E FF 9E 9E 9E FF 9D 9D 9D FF 9C 9C' + '9C FF 9A 9A 9A FF 97 97 97 FF 93 93 93 FF 90 90' + '90 FF 8D 8D 8D FF 88 88 88 FF 90 90 90 FF A7 A7' + 'A7 D6 43 43 43 35 00 00 00 00 BD BD BD EC 9D 9D' + '9D FF 93 93 93 FF 8B 8B 8B FF 88 88 88 FF 85 85' + '84 FF 7F 7F 7F FF 7C 7C 7C FF 79 79 79 FF 77 77' + '77 FF 74 74 74 FF 73 73 73 FF 73 73 73 FF 72 72' + '72 FF 70 71 71 FF 70 70 70 FF 80 80 80 FF 87 87' + '87 FF 87 87 87 FF 86 86 86 FF 85 85 85 FF 84 84' + '84 FF 82 82 82 FF 81 81 81 FF 7F 7F 7F FF 7B 7B' + '7B FF 78 78 78 FF 7A 7A 7A FA 90 90 90 E2 7F 7F' + '7F 85 00 00 00 00 00 00 00 00 D4 D2 D4 B9 AB AB' + 'AD FE 9A 9B 9D FF 98 99 9B FF 93 94 96 FF 8F 90' + '93 FF 8B 8D 8F FF 87 88 8A FF 84 85 87 FF 80 81' + '83 FF 7C 7D 7F FF 7C 7C 7F FF 7B 7B 7D FF 79 7A' + '7B FF 7B 7B 7B FF 7B 7B 7B FF 79 79 79 FF 78 78' + '78 FF 77 77 77 FF 76 76 76 FF 76 76 76 FF 73 73' + '73 FF 71 71 71 FF 6F 6F 6F FF 6C 6C 6C FF 69 69' + '69 FF 64 64 64 FF 68 68 68 DF 19 19 19 47 00 00' + '00 00 00 00 00 00 00 00 00 00 DE DB D6 65 DA D6' + 'CA F7 C0 B8 A9 FF C0 B7 A9 FF B7 B1 A2 FF B0 A9' + '9B FF AA A3 96 FF A6 A1 93 FF 9D 97 8C FF 99 94' + '8A FF 93 8E 84 FF 8D 88 7F FF 8B 87 7E FF 81 7F' + '7D FF 7A 7B 7E FF 7B 7B 7B FF 7B 7B 7B FF 7B 7B' + '7B FF 7A 7A 7A FF 79 79 79 FF 78 78 78 FF 76 76' + '76 FF 74 74 74 FF 71 71 71 FF 6F 6F 6F FF 6A 6A' + '6A FF 66 66 66 FF 8A 8A 8A D5 28 28 28 33 00 00' + '00 00 00 00 00 00 00 00 00 00 ED ED E9 3B FC F9' + 'EA E0 E4 D6 BB FF E1 D3 B8 FF DE D0 B4 FF DB CC' + 'B1 FF D5 C7 AB FF D0 C2 A7 FF CB BD A0 FF C5 B7' + '99 FF C1 B2 96 FF B9 AB 8E FF B1 A3 86 FF BE AD' + '8C FE 9E 9B 96 F1 A8 A8 A9 D7 AA AA AA D0 A9 A9' + 'A9 D0 A7 A7 A7 D0 A5 A5 A5 D1 A2 A2 A2 D1 9D 9D' + '9D D2 98 98 98 D3 92 92 92 D4 8D 8D 8D D5 83 83' + '83 D7 9B 9B 9B D5 2D 2D 2D 44 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E1 DD' + 'DA 69 FB F8 EC F8 F9 F4 E9 FA F8 F3 E5 FA F7 F1' + 'E1 FA F4 EE DA FA F2 EC D6 FA F0 E9 CF FA ED E7' + 'C7 FA EC E3 C1 FA E9 E0 BE FA E6 DB B7 FA E8 DF' + 'BC E1 8E 86 75 5D 9E 9E 9E 2D AE AE AE 26 B5 B5' + 'B5 26 B5 B5 B5 26 B7 B7 B7 27 B0 B0 B0 27 AC AC' + 'AC 28 9D 9D 9D 2A 8B 8B 8B 2C 85 85 85 2E 74 74' + '74 30 7C 7C 7C 2D 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 80 00' + '00 03 80 00 00 03 80 00 00 03 80 00 00 03 80 00' + '00 03 80 00 00 03 80 00 00 01 80 00 00 01 80 00' + '00 01 80 00 00 01 80 00 00 00 80 00 00 00 80 00' + '00 00 80 00 00 00 80 00 00 00 80 00 00 01 80 00' + '00 03 80 00 00 03 80 00 00 07 C0 00 00 0F FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 04 00 00 00 00 00 80 04 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 08 87 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 00 00 07 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 00 00 07 88 88 88 88 88 88 88 88 88' + '77 87 77 77 77 77 77 77 77 77 77 77 00 00 07 88' + '88 FF FF FF FF FF FF FF FF FF FF FF FF FF FF 88' + '88 88 88 88 70 00 08 78 8F F4 44 44 44 44 4F F4' + 'F4 F4 F4 44 44 44 4F F8 88 88 88 88 70 00 08 78' + '8F F4 FF FF FF FF F4 4F 4F 4F 4F FF FF FF FF FF' + '88 88 88 88 70 00 08 78 8F F4 F4 F4 4F 44 F4 F4' + 'FF 44 F4 4F 44 4F FF FF 88 88 88 88 70 00 08 78' + '8F FF 4F 4F F4 FF 4F 4F FF 4F FF F4 FF FF FF FF' + '88 88 88 88 70 00 08 78 88 F4 44 FF FF FF 44 44' + '4F F4 4F F4 4F FF FF FF 88 88 88 88 70 00 08 77' + '88 8F FF FF FF FF FF FF FF FF FF FF FF FF FF F8' + '88 88 88 88 70 00 08 77 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 87 00 08 77' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 08 77 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 87 00 08 77' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 08 77 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 87 00 08 77' + '78 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 08 77 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 70 08 77' + '78 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 08 77 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 70 08 77' + '78 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 08 77 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 70 08 77' + '77 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 08 77 77 77 77 77 77 77 77 77' + '77 77 77 88 88 88 88 88 88 88 88 88 88 70 08 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 00 08 87 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 78 00 00 00 87' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 78 00 00 00 86 66 66 66 66 66 66 66 66' + '66 67 77 77 77 77 77 77 77 77 77 80 00 00 00 8E' + '6E 6E 6E 6E 6E 6E 6E 6E 6E 68 88 88 88 88 88 88' + '88 88 88 00 00 00 00 0F EF EF EF EF EF EF EF EF' + 'EF 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '88 88 88 88 88 88 88 88 88 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 80 00 00 00 00 0F 00 00 80 00' + '00 00 00 0F 00 00 80 00 00 00 00 0F 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 03 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 03 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 03 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 03 00 00 80 00 00 00 00 0F 00 00 C0 00' + '00 00 00 0F 00 00 C0 00 00 00 00 1F 00 00 C0 00' + '00 00 00 3F 00 00 E0 00 01 FF FF FF 00 00 F0 00' + '03 FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 28 00' + '00 00 30 00 00 00 60 00 00 00 01 00 08 00 00 00' + '00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 C0 DC C0 00 F0 CA A6 00 58 58' + '58 00 5C 5C 5C 00 61 61 61 00 65 65 65 00 69 69' + '69 00 6D 6D 6D 00 71 71 71 00 75 75 75 00 79 79' + '79 00 7A 7C 7F 00 7D 7D 7D 00 98 69 4E 00 99 6C' + '4F 00 A5 7A 5A 00 A6 7D 58 00 89 85 7E 00 95 8E' + '7E 00 AB 84 61 00 AF 89 6A 00 B2 8E 6F 00 B1 8D' + '71 00 B3 91 70 00 B4 92 74 00 B5 94 75 00 7B 7D' + '80 00 7C 7E 82 00 FF 00 FF 00 7F 80 80 00 7F 81' + '84 00 81 81 81 00 82 83 86 00 85 85 85 00 85 86' + '88 00 87 89 8C 00 89 89 89 00 8C 8C 8B 00 89 8B' + '8E 00 8B 8C 8C 00 8C 8C 8C 00 8D 8D 8E 00 8D 8E' + '8E 00 8E 8E 8E 00 90 8F 8F 00 9A 91 81 00 9D 95' + '85 00 9A 96 8D 00 8D 8E 91 00 90 8F 90 00 90 90' + '90 00 92 91 92 00 92 92 92 00 95 94 93 00 91 92' + '95 00 94 94 94 00 96 95 95 00 96 96 96 00 98 97' + '97 00 93 95 98 00 96 97 99 00 97 98 9B 00 98 98' + '98 00 99 99 9A 00 9A 9A 9A 00 9C 9B 9B 00 99 9A' + '9D 00 9B 9C 9F 00 9C 9C 9C 00 9E 9D 9D 00 9E 9E' + '9D 00 9E 9E 9E 00 AA 9C 81 00 A4 9C 8B 00 A8 9F' + '8E 00 BD 9F 85 00 A0 9E 9E 00 AE A0 84 00 AA A0' + '8F 00 B2 A2 82 00 B2 A4 89 00 B7 AA 8F 00 AF A5' + '91 00 AE A5 93 00 BC AE 92 00 B6 AD 98 00 BE B1' + '94 00 BA B0 9C 00 9E 9E A0 00 A1 A1 A1 00 A4 A2' + 'A2 00 A3 A3 A4 00 A5 A5 A5 00 A6 A7 A9 00 A9 A9' + 'A9 00 AC AB AA 00 AD AC AB 00 AD AD AD 00 BF B4' + 'A0 00 B1 B1 B1 00 B5 B5 B5 00 B9 B7 B7 00 B9 B9' + 'B9 00 BD BC BB 00 BB BB BC 00 BD BD BD 00 C2 A5' + '8B 00 C1 A6 8D 00 C7 AD 96 00 C3 B5 99 00 CA B2' + '9D 00 CC B5 9F 00 C6 B9 9E 00 C8 B9 9D 00 CE BE' + '9F 00 CD B6 A2 00 CB B5 A6 00 C4 BA A4 00 CB BD' + 'A1 00 C9 BC A2 00 CF B9 A6 00 D0 B9 A5 00 D1 BD' + 'AA 00 D3 BF AD 00 C0 BF BE 00 CE C0 A3 00 CD C0' + 'A5 00 CB C0 AA 00 D0 C3 A8 00 D2 C4 A8 00 D5 C7' + 'AB 00 D3 C6 AC 00 D0 C4 AE 00 D7 C9 AE 00 D9 CB' + 'AF 00 D5 C2 B2 00 D2 C7 B0 00 D7 C5 B6 00 D9 C7' + 'B7 00 D7 C9 B3 00 D8 CB B0 00 DA CD B3 00 DD CF' + 'B3 00 DB CE B4 00 DA C9 B9 00 DB CC BE 00 DE D1' + 'B3 00 DF D0 B4 00 DE D0 B6 00 E0 D2 B5 00 E1 D4' + 'B9 00 E2 D5 BA 00 E4 D5 B9 00 E6 D8 BC 00 E8 DA' + 'BE 00 E9 DC BF 00 C1 C1 C1 00 C5 C5 C5 00 C8 C8' + 'C6 00 C7 C7 C8 00 C9 C9 C9 00 CC CB CB 00 CD CD' + 'CD 00 DD CF C3 00 D0 CF CE 00 DE D5 C1 00 D2 D0' + 'CE 00 DF D2 CA 00 D2 D2 D1 00 D6 D4 D3 00 D6 D5' + 'D5 00 D8 D7 D7 00 D9 D8 D3 00 D9 D9 D9 00 DD DD' + 'D9 00 DD DD DD 00 E0 D1 C4 00 E9 DB C1 00 EB DF' + 'C4 00 E2 D6 CA 00 E4 D7 CA 00 E3 D6 CC 00 E4 D7' + 'CC 00 E3 D8 CD 00 E4 D9 CE 00 E6 DA D0 00 E7 DC' + 'D2 00 E7 DE D5 00 E8 DD D2 00 E9 DF D6 00 EA E0' + 'C2 00 EC E2 C5 00 EC E1 C8 00 ED E4 CB 00 EE E5' + 'CF 00 EE E6 CF 00 EF E8 CE 00 EA E0 D7 00 E6 E0' + 'DB 00 E9 E1 DA 00 EC E2 D9 00 EC E4 DB 00 EA E3' + 'DC 00 EB E4 DD 00 ED E5 DE 00 F0 EA D0 00 F0 EC' + 'D3 00 F2 ED D5 00 F2 ED D8 00 F0 E8 DF 00 F0 EE' + 'DC 00 F2 F0 DB 00 F2 F1 DF 00 E0 E0 E0 00 ED E6' + 'E0 00 EF E8 E2 00 EB EA E9 00 ED EB E8 00 F1 E9' + 'E2 00 F1 EA E4 00 F1 EC E7 00 F2 EC E8 00 F5 EF' + 'EA 00 F4 F3 E4 00 F3 F0 EB 00 F3 F1 EE 00 F5 F1' + 'ED 00 F8 F4 EF 00 F2 F1 F0 00 F5 F2 F0 00 F6 F4' + 'F2 00 F8 F5 F1 00 FA F6 F4 00 FA F8 F5 00 FC F8' + 'F5 00 FA F9 F8 00 FC FA F8 00 FD FC FB 00 F0 FB' + 'FF 00 A4 A0 A0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 07 70 66 54 60 54 60 54 60' + '54 60 54 60 54 60 54 60 54 60 54 60 54 60 54 60' + '54 60 54 60 54 60 54 60 54 60 54 60 54 60 54 61' + '61 6B 00 00 00 00 00 71 A4 69 49 41 41 3C 3F 3C' + '3A 3A 32 3A 32 32 32 2F 2D 2C 2F 2C 2C 2A 29 29' + '2A 29 29 27 27 25 25 25 25 14 14 14 14 14 14 14' + '14 14 00 00 00 00 00 6D AA B5 A8 A4 71 71 6F 6F' + '6C 6D 6C 6C 6B 6B 6B 69 68 68 66 64 64 62 F7 54' + '54 4E 49 48 42 3D 3D 34 2D 32 2D 32 2D 2D 2D 2C' + '3C 0D 00 00 00 00 00 6B 69 71 6D B0 CE D2 D3 DE' + 'E8 ED EE EE EE EF EE EE EA ED ED ED EA E9 E9 E9' + 'E9 ED E9 E9 E9 EC EC E0 A5 3A 14 14 12 12 12 11' + '33 0D 4F 00 00 00 00 69 48 6E A8 ED CD C0 BC C1' + 'C1 C1 C0 BB C1 C3 C1 D0 D1 D4 D9 E3 E3 E7 E3 E4' + 'E2 D9 CD C4 C1 C0 BF C1 E8 AE 29 14 14 12 12 12' + '3B 11 3F 00 00 00 00 66 2C 6C B3 F4 F3 73 F4 F1' + 'ED D4 CF E3 E4 CF DF 8F CD E4 C2 C2 DE D3 D2 D3' + 'DF E5 EA E6 E4 E4 E3 E5 F1 F3 63 14 14 14 14 11' + '34 11 2A 00 00 00 00 67 13 6B AE EF F0 AB 1C 77' + '15 17 16 17 18 1F 83 D1 98 80 F4 E5 20 7B 82 77' + '21 1B 53 81 B8 E6 EB EA EA F2 A6 F8 14 14 12 12' + '2D 12 11 00 00 00 00 68 10 6C A8 F0 F1 E5 7C 99' + 'E6 C3 F0 E3 DF 82 BD 8F 76 E4 F5 E4 81 8F BE 99' + '92 8F C0 F3 EA F0 F1 F1 F4 F2 B2 29 F8 14 14 12' + '27 14 0F 00 00 00 00 68 0F 6C 07 E1 EB CD 76 1E' + 'BB E4 EB EF EF EB 98 74 1D 76 D1 EB 92 72 DE 98' + 'C4 18 F4 ED EB EA EA EB EA F0 AE 29 27 27 14 14' + '14 29 0E 00 00 00 00 68 0E 69 6E A9 EC F3 F0 AF' + 'BB E5 F2 F1 F0 F0 F5 F4 EA EF EB F1 F4 E3 BD D1' + 'EE F2 F4 F0 F0 F0 F2 F2 F2 ED 66 29 29 27 27 14' + '14 2C 0C 00 00 00 00 67 0F 65 71 6E A8 B7 DD DD' + 'B7 B7 B7 B6 B7 DD B7 B7 B7 B5 B7 B2 B5 B2 B3 B2' + 'B2 B2 B2 B0 B0 B0 B2 B2 B0 6B 32 2C 2A 29 29 F8' + '14 3C 0B 48 00 00 00 69 0F 4D A4 70 6E 70 6C 6C' + '69 6B 69 67 69 69 67 66 67 66 65 63 63 63 61 61' + '4F 4F 4F 4A 44 46 46 46 41 32 2F 2C 2C 29 27 28' + 'F8 2D 0C 2D 00 00 00 6B 11 3C AC 07 71 70 6E 6C' + '6C 6B 67 66 66 65 65 64 62 63 61 61 61 4F 4E 4C' + '48 46 46 41 3F 3C 3C 3C 3A 3A 33 30 2F 29 2A 27' + '27 2D 10 14 00 00 00 6B 14 2A B2 A4 A4 71 6E 6E' + '6C 6B 69 69 68 66 65 64 64 62 61 63 61 4E 48 4C' + '4C 48 46 3F 40 3F 3A 3A 3A 3A 33 32 2D 2C 2C 29' + '29 2C 2C 0F 00 00 00 6B 29 14 B5 A5 A4 07 6E 6E' + '6C 6C 6B 69 69 66 65 63 64 62 63 61 61 4E 4C 48' + '4C 47 46 46 41 3F 3C 3C 3C 34 33 32 31 2C 2C 2C' + '29 29 2D 0C 00 00 00 6B 3A 11 B7 A6 A7 A4 71 70' + '6D 6C 6C 69 69 67 65 64 64 64 61 4F 61 61 4E 4C' + '4C 4C 46 46 46 41 41 3F 3C 3A 3A 3A 34 2F 2C 2C' + '29 29 3C 0A 00 00 00 6B 3F 10 B5 A9 A7 A5 07 71' + '70 6C 6C 6B 6B 66 66 66 64 64 63 61 61 61 4E 4C' + '4C 4C 4C 49 46 46 46 41 3F 3F 3A 3C 3A 32 32 2D' + '2C 29 34 0C 3F 00 00 6C 47 10 B2 AA A8 A5 A4 A4' + '71 6E 6C 6C 69 69 67 65 66 63 64 61 61 61 54 4F' + '4E 4C 4F 48 48 48 46 46 41 41 3C 3C 3A 3A 33 32' + '2C 2A 3D 11 2C 00 00 6C 61 10 AC AC A9 A7 A5 A4' + '07 70 6E 6C 6B 69 68 66 64 64 63 61 61 61 4F 61' + '4C 4F 49 4C 48 4C 47 46 41 41 41 3F 3C 3A 33 33' + '2C 2C 33 27 11 00 00 6B 65 12 A6 B0 AA A8 A7 A5' + '07 71 70 6D 6C 6B 67 66 65 64 63 62 61 61 4F 4F' + '4E 4C 4C 4C 4C 4C 48 48 46 44 41 40 3C 3C 33 33' + '2C 2C 2C 34 0E 00 00 6C 64 29 69 B7 AC AA A8 A7' + 'A5 07 71 6E 6C 6B 6B 69 66 65 64 62 61 61 4E 4F' + '4F 4E 4A 4C 4C 4C 49 48 46 46 41 41 3C 3C 33 32' + '2D 2A 29 3A 0C 00 00 6C 65 34 2C 67 A5 A4 A4 71' + '71 6D 6C 6B 6B 66 65 64 63 54 4E 4E 48 4C 4E 61' + '4C 4E 4E 48 4C 48 48 47 46 41 41 3F 3C 32 33 2D' + '2A 29 27 49 29 00 00 6B 66 3F 3C 2C 2A 29 27 27' + '14 14 12 12 11 11 11 11 11 11 10 10 11 10 11 33' + '61 61 61 61 61 4E 61 4E 4C 48 47 46 3F 3A 3A 32' + '2D 3B 48 46 65 00 00 70 69 42 46 40 3F 3A 3A 2D' + '2C 29 29 27 27 F8 12 14 12 11 12 12 11 11 10 11' + '12 14 14 14 14 12 14 12 12 12 12 11 11 11 10 0F' + '11 0F 3A 64 00 00 00 AA 70 4F 60 4C 49 41 41 3C' + '3A 32 2F 2C 29 27 27 F8 25 14 14 12 12 14 12 12' + '12 12 12 11 11 12 11 11 10 10 10 0F 0F 0E 0E 0D' + '10 0D 00 00 00 00 00 00 A5 65 60 4B 4A 45 43 3E' + '38 2E 2B 2A 28 26 23 22 22 13 22 14 25 14 25 14' + '14 14 14 12 12 12 12 12 12 11 10 11 0F 10 0E 0E' + '12 0F 00 00 00 00 00 00 B4 AD 90 90 8C 87 7D 6A' + '5F 5F 5D 5B 56 56 51 36 35 1A 1A 19 22 27 14 25' + '25 14 25 14 14 14 12 12 12 11 11 10 10 0F 0F 0E' + '31 00 00 00 00 00 00 00 B6 CA 9C 97 95 94 8D 8B' + '88 86 7F 78 75 5E 5C 59 58 55 50 57 37 44 4F 4C' + '48 47 47 46 42 3D 3C 3C 33 2C 2C 29 27 14 14 3A' + '00 00 00 00 00 00 00 00 00 E7 CB C7 BA A3 A2 A0' + '9D 96 8E 8A 89 85 7E 79 75 5E 5C 7A 5A 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 DA DC DB D8 D7 D6' + 'D5 CC C9 C8 C6 B9 A1 9F 9E 9B 9A 93 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 80 00' + '00 00 00 0F 00 00 80 00 00 00 00 0F 00 00 80 00' + '00 00 00 0F 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 03 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 03 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 03 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 0F 00 00 C0 00 00 00 00 0F 00 00 C0 00' + '00 00 00 1F 00 00 C0 00 00 00 00 3F 00 00 E0 00' + '01 FF FF FF 00 00 F0 00 03 FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 20 00 00 00 00 00 80 25 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 1E 00 00' + '00 2B 00 00 00 2C 00 00 00 2C 00 00 00 2C 00 00' + '00 2C 00 00 00 2C 00 00 00 2C 00 00 00 2C 00 00' + '00 2C 00 00 00 2C 00 00 00 2C 00 00 00 2C 00 00' + '00 2C 00 00 00 2C 00 00 00 2C 00 00 00 2C 00 00' + '00 2C 00 00 00 2C 00 00 00 2C 00 00 00 2C 00 00' + '00 2C 00 00 00 2C 00 00 00 2C 00 00 00 2C 00 00' + '00 2C 00 00 00 2C 00 00 00 2C 00 00 00 2C 00 00' + '00 2C 00 00 00 2C 00 00 00 2C 00 00 00 2C 00 00' + '00 2C 00 00 00 2C 00 00 00 2C 00 00 00 2C 00 00' + '00 2C 00 00 00 2B 00 00 00 28 00 00 00 16 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C3 C3 C3 4D A4 A4 A4 9E 89 89' + '89 B2 85 85 85 B4 83 83 83 B4 80 80 80 B4 80 80' + '80 B4 80 7F 80 B4 7C 7B 7C B4 7B 7B 7B B4 7B 7B' + '7B B4 7B 7B 7B B4 78 78 79 B4 78 78 79 B4 78 79' + '79 B4 75 77 77 B4 75 75 75 B4 74 74 74 B4 74 74' + '74 B4 72 72 72 B4 72 72 72 B4 6F 6F 71 B4 6E 6E' + '6E B4 6E 6E 6E B4 6E 6E 6E B4 6B 6B 6D B4 6B 6B' + '6B B4 68 6A 6A B4 67 67 68 B4 64 66 66 B4 64 64' + '64 B4 64 64 64 B4 64 64 64 B4 61 61 61 B4 61 61' + '61 B4 61 61 61 B4 61 61 61 B4 60 60 60 B4 60 60' + '60 B4 60 60 60 B3 5A 5A 5A A6 05 05 05 5B 00 00' + '00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 2F 2F 2F 10 D5 D5 D5 9E D9 D9 D9 FA C8 C8' + 'C8 FC C1 C1 C1 FC BE BD BD FC BE BD BD FC BD BC' + 'BB FC BB BA B9 FC B9 B7 B7 FC B7 B7 B6 FC B7 B6' + 'B5 FC B5 B5 B4 FC B3 B2 B2 FC B2 B1 B1 FC B1 B0' + 'B0 FC AF AE AE FC AD AC AB FC AC AB AA FC AB A8' + 'A8 FC A7 A6 A6 FC A6 A5 A4 FC A4 A2 A2 FC A2 A1' + 'A0 FC A1 9F 9F FC A0 9E 9E FC 9E 9D 9D FC 9C 9B' + '9B FC 99 99 98 FC 98 97 97 FC 95 94 94 FC 94 93' + '92 FC 8F 8E 8E FC 8B 8B 8B FC 8B 8B 8B FC 8C 8C' + '8C FC 8B 8B 8B FC 8C 8C 8C FC 8A 8A 8A FC 8A 8A' + '8A FC 89 89 89 FC 90 90 90 EE 1C 1C 1B 8D 00 00' + '00 1D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 16 16 16 17 A4 A4 A4 AF BE BE BE FF B6 B6' + 'B6 FF D3 D2 D1 FF E6 E0 DB FF EA E3 DC FF EB E4' + 'DD FF ED E6 E0 FF F2 ED E9 FF F5 F3 F1 FF F6 F4' + 'F3 FF F6 F4 F3 FF F6 F4 F3 FF F6 F4 F2 FF F5 F3' + 'F1 FF F5 F3 F0 FF F5 F2 F0 FF F4 F2 F0 FF F4 F2' + 'EF FF F4 F1 EF FF F3 F1 EE FF F3 F1 EE FF F3 F1' + 'EE FF F3 F1 EE FF F4 F1 EE FF F3 F1 EF FF F3 F1' + 'EE FF F3 F1 EF FF F3 F2 EF FF F3 F1 F0 FF F3 F1' + 'F0 FF EB EA E9 FF C6 C5 C4 FF 90 90 90 FF 7D 7D' + '7D FF 7B 7B 7B FF 7B 7B 7B FF 79 79 79 FF 78 78' + '78 FF 76 76 76 FF 8C 8C 8D F9 3A 3A 3B AB 00 00' + '00 2D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 12 12 12 1C 8B 8B 8B B9 BA BA BA FF C9 C8' + 'C8 FF F5 F3 F0 FF E9 DF D6 FF E5 D9 CD FF E4 D7' + 'CA FF E7 DB D0 FF E7 DB D1 FF E5 DA D0 FF E3 D8' + 'CE FF E3 D6 CC FF E4 D9 CF FF E7 DD D2 FF E7 DC' + 'D1 FF EC E2 D9 FF ED E3 DA FF ED E5 DD FF F0 E8' + 'DF FF F1 EB E4 FF F2 EB E5 FF F2 EB E5 FF F2 EB' + 'E5 FF F2 EB E4 FF F1 E9 E2 FF EF E6 DE FF EC E3' + 'D9 FF E8 DD D2 FF E5 DA CF FF E4 D9 CF FF E3 D8' + 'CC FF E6 DB D1 FF F3 F0 EB FF D0 CF CE FF 87 87' + '86 FF 7D 7D 7D FF 7C 7C 7C FF 7B 7B 7B FF 7A 7A' + '7A FF 78 78 78 FF 90 90 90 FC 5D 5D 5D BF 00 00' + '00 3A 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 17 17 17 21 76 76 76 C1 B5 B5 B6 FF D7 D6' + 'D6 FF FD FB FA FF FB F8 F7 FF C1 A6 8D FF FC FB' + 'FA FF F8 F6 F4 FF F5 F2 EF FF EB E5 DE FF EA E1' + 'DA FF F1 EA E5 FF F1 EC E7 FF E9 E1 D9 FF EF E8' + 'E2 FF D4 C2 B0 FF EA E1 DA FF F1 EB E6 FF E7 DC' + 'D3 FF E5 DA D1 FF ED E6 E0 FF EC E5 DF FF EA E3' + 'DB FF EC E5 DE FF EF E8 E2 FF F3 ED E8 FF F5 EF' + 'EB FF F3 EE E9 FF F2 EC E8 FF F1 EC E7 FF F1 EB' + 'E5 FF F2 ED E8 FF FA F8 F6 FF F9 F8 F8 FF A3 A3' + 'A3 FF 7D 7D 7D FF 7C 7C 7C FF 7D 7D 7D FF 7B 7B' + '7B FF 78 78 78 FF 8E 8E 8E FE 63 63 63 CA 00 00' + '00 4D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 39 39 39 28 6B 6B 6B CE B1 B1 B1 FF D3 D1' + 'CF FF FA F6 F1 FF FA F6 F3 FF DD CF C3 FF AF 89' + '6A FF CC B5 A1 FF 98 69 4E FF A6 7B 5B FF 99 6C' + '4F FF A4 79 5A FF A6 7D 58 FF B3 91 70 FF D3 BF' + 'AD FF EC E3 DB FF D9 C8 B8 FF D0 B9 A5 FF FD FB' + 'F9 FF F3 EB E6 FF B4 92 74 FF CE B7 A3 FF D2 BE' + 'AB FF CC B5 9F FF B5 94 75 FF AB 84 61 FF BD 9F' + '85 FF CF B7 A3 FF E0 D1 C4 FF F6 EF EA FF F7 F2' + 'ED FF F6 F1 ED FF F6 F2 ED FF FC F8 F4 FF C9 C8' + 'C6 FF 80 80 80 FF 7E 7E 7E FF 7C 7C 7C FF 7B 7B' + '7B FF 7A 7A 7A FF 8B 8B 8B FE 6E 6E 6E E1 09 09' + '09 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 55 55 55 30 66 66 66 DF B4 B4 B4 FF CB CA' + 'C9 FF F8 F6 F4 FF FB F8 F6 FF F2 EC E9 FF CB B5' + 'A6 FF DB CA BB FF F4 F0 EC FF E7 DE D5 FF F9 F5' + 'F2 FF F0 EB E5 FF EF E8 E2 FF D1 BD AA FF E3 D7' + 'CC FF D6 C3 B3 FF C9 B1 9D FF F1 EC E7 FF FD FC' + 'FB FF F2 EB E5 FF CF B9 A6 FF D5 C3 B3 FF E3 D6' + 'CC FF DB CC BE FF D7 C5 B6 FF D5 C3 B3 FF E4 D8' + 'CD FF FC F9 F8 FF F5 F0 EC FF FA F7 F5 FF FA F8' + 'F6 FF FA F8 F6 FF FA F7 F6 FF FC F9 F8 FF D6 D4' + 'D3 FF 84 84 84 FF 80 80 80 FF 7C 7C 7C FF 7C 7C' + '7C FF 7B 7B 7B FF 80 80 80 FF 79 79 79 ED 1D 1D' + '1D 81 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 5D 5D 5D 34 65 65 65 E5 B5 B5 B5 FF C0 BF' + 'BE FF ED EB E8 FF F8 F4 F0 FF EA E0 D7 FF CA B2' + '9C FF B1 8D 71 FF E2 D6 C9 FF F3 EC E7 FF F8 F4' + 'F0 FF F7 F4 F1 FF F8 F5 F1 FF F8 F4 F0 FF DA C8' + 'B9 FF C7 AD 96 FF B2 8E 6F FF CB B3 9E FF EC E3' + 'DA FF F8 F4 EF FF D9 C7 B7 FF C2 A5 8B FF ED E6' + 'E0 FF DA CA BA FF E7 DC D2 FF A6 7D 59 FF FB FA' + 'F8 FF F7 F3 EE FF F7 F2 EF FF F6 F2 EE FF F6 F2' + 'EE FF F6 F2 EE FF F6 F1 ED FF FB F7 F3 FF D1 D0' + 'CE FF 85 85 85 FF 83 83 83 FF 80 80 80 FF 7E 7E' + '7E FF 7C 7C 7C FF 7D 7D 7D FF 83 83 83 F6 2C 2C' + '2C 96 00 00 00 16 00 00 00 00 00 00 00 00 00 00' + '00 00 64 64 64 38 63 63 63 EB AD AD AD FF B8 B8' + 'B8 FF CC CB CB FF F1 F1 F0 FF FC FA F8 FF F8 F6' + 'F3 FF DF D2 CA FF E3 D6 CB FF F3 ED E8 FF FB F8' + 'F5 FF FB F7 F4 FF FA F7 F4 FF FA F7 F3 FF FD FC' + 'FB FF FB FA F8 FF F6 F2 EE FF F8 F5 F2 FF F6 F2' + 'EE FF FB F8 F6 FF FC FB F9 FF F0 E9 E4 FF E4 D7' + 'CC FF EC E4 DB FF F7 F3 F0 FF FB F8 F5 FF FC F9' + 'F8 FF FA F7 F4 FF FA F7 F4 FF FB F7 F4 FF FB F7' + 'F4 FF FB F7 F4 FF FC F9 F6 FF F4 F3 F2 FF A8 A8' + 'A8 FF 86 86 86 FF 84 84 84 FF 82 82 82 FF 81 81' + '81 FF 7D 7D 7D FF 7D 7D 7D FF 87 87 87 FC 37 37' + '37 B0 00 00 00 24 00 00 00 00 00 00 00 00 00 00' + '00 00 66 66 66 3C 66 66 66 EF A7 A7 A7 FF BE BE' + 'BE FF BA BA BA FF C8 C8 C7 FF DE DD DD FF E0 E0' + 'E0 FF DF DF DF FF DE DE DE FF DD DD DD FF DD DD' + 'DD FF DC DC DC FF DD DD DD FF DE DE DE FF DD DD' + 'DD FF DD DD DD FF DD DD DD FF DC DC DC FF DB DB' + 'DB FF D8 D7 D7 FF D7 D7 D7 FF D7 D7 D7 FF D6 D6' + 'D6 FF D5 D5 D5 FF D6 D5 D5 FF D7 D7 D7 FF D5 D6' + 'D6 FF D3 D3 D3 FF D2 D2 D2 FF D3 D3 D3 FF D6 D5' + 'D5 FF D6 D6 D6 FF D1 D1 D1 FF B3 B3 B3 FF 8D 8D' + '8D FF 89 89 89 FF 87 87 87 FF 85 85 85 FF 83 83' + '83 FF 81 81 81 FF 7E 7E 7E FF 91 91 91 FE 3F 3F' + '3F C5 00 00 00 33 00 00 00 00 00 00 00 00 00 00' + '00 00 6F 6F 6F 3E 6B 6B 6B F2 9C 9C 9C FF C3 C3' + 'C3 FF BA BA BA FF BA BA BA FF BA BA BA FF B7 B7' + 'B7 FF B4 B4 B4 FF AF AF AF FF B0 B0 B0 FF AD AD' + 'AD FF AB AB AB FF AC AC AC FF AD AD AD FF AC AC' + 'AC FF AB AB AB FF AA AA AA FF A9 A9 A9 FF A7 A7' + 'A7 FF A4 A4 A4 FF A3 A3 A4 FF A3 A3 A3 FF A2 A2' + 'A2 FF A0 A0 A0 FF 9F 9F 9F FF 9E 9E 9E FF 9C 9C' + '9C FF 99 99 9A FF 98 98 98 FF 97 97 97 FF 98 98' + '98 FF 97 97 97 FF 95 95 95 FF 8E 8E 8E FF 8B 8B' + '8B FF 8A 8A 8A FF 87 87 87 FF 85 85 85 FF 83 83' + '83 FF 82 82 82 FF 7F 7F 7F FF 8C 8C 8B FE 50 50' + '50 DA 00 00 00 45 00 00 00 00 00 00 00 00 00 00' + '00 00 81 81 81 3F 74 74 74 F5 92 91 92 FF CE CE' + 'CE FF BF BF BF FF BE BE BE FF BB BB BB FF B9 B9' + 'B9 FF B6 B6 B6 FF B3 B3 B3 FF B0 B0 B0 FF AC AC' + 'AC FF AA AA AA FF A9 A9 A9 FF A7 A7 A7 FF A6 A6' + 'A6 FF A5 A5 A5 FF A3 A3 A3 FF A2 A2 A2 FF A2 A2' + 'A2 FF A1 A1 A1 FF A0 A0 A0 FF 9F 9F 9F FF 9D 9D' + '9D FF 9C 9C 9C FF 9A 9A 9A FF 98 98 98 FF 98 98' + '98 FF 96 96 96 FF 94 94 94 FF 93 93 93 FF 92 92' + '92 FF 91 91 91 FF 91 91 91 FF 90 90 90 FF 8E 8E' + '8E FF 8C 8C 8C FF 8A 8A 8A FF 87 87 87 FF 86 86' + '86 FF 82 82 82 FF 81 81 81 FF 8C 8C 8C FF 6C 6C' + '6C EC 02 02 02 5B 00 00 00 00 00 00 00 00 00 00' + '00 00 89 89 89 41 7A 7B 7A F7 87 86 87 FF D4 D4' + 'D4 FF C2 C2 C2 FF C0 C0 C0 FF BD BD BD FF BA BA' + 'BA FF B8 B8 B8 FF B4 B4 B4 FF B1 B1 B1 FF B0 B0' + 'B0 FF AD AD AD FF AB AB AB FF AA AA AA FF A6 A6' + 'A6 FF A6 A6 A6 FF A4 A4 A4 FF A3 A3 A3 FF A2 A2' + 'A2 FF A2 A2 A2 FF A1 A1 A1 FF 9E 9E 9E FF 9B 9B' + '9B FF 9C 9C 9C FF 9C 9C 9C FF 99 99 99 FF 98 98' + '98 FF 95 95 95 FF 95 95 95 FF 93 93 93 FF 91 91' + '91 FF 91 91 91 FF 90 90 90 FF 90 90 90 FF 8E 8E' + '8E FF 8D 8D 8D FF 8B 8B 8B FF 8A 8A 8A FF 88 88' + '88 FF 86 86 86 FF 84 84 84 FF 89 89 89 FF 86 86' + '86 F8 04 04 04 6E 00 00 00 12 00 00 00 00 00 00' + '00 00 90 90 90 45 84 84 84 F9 7D 7D 7D FF D8 D8' + 'D8 FF C5 C5 C5 FF C3 C3 C3 FF BF BF BF FF BA BA' + 'BA FF B8 B8 B8 FF B5 B5 B5 FF B3 B3 B3 FF B1 B1' + 'B1 FF AE AE AE FF AC AC AC FF A9 A9 A9 FF A7 A7' + 'A7 FF A4 A4 A4 FF A4 A4 A4 FF A3 A3 A3 FF A3 A3' + 'A3 FF A1 A1 A1 FF A1 A1 A1 FF 9E 9E 9E FF 9D 9D' + '9D FF 9B 9B 9B FF 9B 9B 9B FF 9A 9A 9A FF 98 98' + '98 FF 97 97 97 FF 96 96 96 FF 95 95 95 FF 92 92' + '92 FF 92 92 92 FF 91 91 91 FF 90 90 90 FF 8F 8F' + '8F FF 8D 8D 8D FF 8D 8D 8D FF 8A 8A 8A FF 89 89' + '89 FF 86 86 86 FF 85 85 85 FF 86 86 86 FF 8A 8A' + '8A FC 09 09 09 86 00 00 00 1C 00 00 00 00 00 00' + '00 00 92 92 92 4B 8E 8E 8E FB 75 75 75 FF DD DD' + 'DD FF C7 C7 C7 FF C6 C6 C6 FF C1 C1 C1 FF BE BE' + 'BE FF BB BB BB FF B8 B8 B8 FF B6 B6 B6 FF B3 B3' + 'B3 FF AF AF AF FF AD AD AD FF AA AA AA FF A8 A8' + 'A8 FF A5 A5 A5 FF A4 A4 A4 FF A4 A4 A4 FF A2 A2' + 'A2 FF 9F 9F 9F FF A0 A0 A0 FF A0 A0 A0 FF 9E 9E' + '9E FF 9C 9C 9C FF 9C 9C 9C FF 9C 9C 9C FF 99 99' + '99 FF 98 98 98 FF 98 98 98 FF 96 96 96 FF 96 96' + '96 FF 93 93 93 FF 92 92 92 FF 91 91 91 FF 90 90' + '90 FF 90 90 90 FF 8F 8F 8F FF 8C 8C 8C FF 89 89' + '89 FF 88 88 88 FF 86 86 86 FF 85 85 85 FF 91 91' + '91 FF 21 21 21 A6 00 00 00 2C 00 00 00 00 00 00' + '00 00 94 94 94 4F 93 93 93 FD 71 71 71 FF DA DA' + 'DA FF CA CA CA FF C7 C7 C7 FF C4 C4 C4 FF C1 C1' + 'C1 FF BE BE BE FF BA BA BA FF B7 B7 B7 FF B4 B4' + 'B4 FF B2 B2 B2 FF AF AF AF FF AB AB AB FF A9 A9' + 'A9 FF A7 A7 A7 FF A6 A6 A6 FF A5 A5 A5 FF A3 A3' + 'A3 FF A1 A1 A1 FF A1 A1 A1 FF A0 A0 A0 FF 9E 9E' + '9E FF 9D 9D 9D FF 9C 9C 9C FF 9C 9C 9C FF 9C 9C' + '9C FF 9B 9B 9B FF 98 98 98 FF 98 98 98 FF 97 97' + '97 FF 96 96 96 FF 95 95 95 FF 93 93 93 FF 91 91' + '91 FF 92 92 92 FF 90 90 90 FF 8E 8E 8E FF 8D 8D' + '8D FF 8C 8C 8C FF 88 88 88 FF 86 86 86 FF 8F 8F' + '8F FF 42 42 42 BF 00 00 00 3B 00 00 00 00 00 00' + '00 00 99 99 99 53 9A 9A 9A FD 71 71 71 FF D6 D6' + 'D6 FF CD CD CD FF C8 C8 C8 FF C6 C6 C6 FF C2 C2' + 'C2 FF C0 C0 C0 FF BC BC BC FF B9 B9 B9 FF B6 B6' + 'B6 FF B3 B3 B3 FF B0 B0 B0 FF AD AD AD FF AA AA' + 'AA FF A8 A8 A8 FF A7 A7 A7 FF A5 A5 A5 FF A4 A4' + 'A4 FF A2 A2 A2 FF A1 A1 A1 FF A1 A1 A1 FF 9F 9F' + '9F FF 9E 9E 9E FF 9D 9D 9D FF 9D 9D 9D FF 9D 9D' + '9D FF 9B 9B 9B FF 9A 9A 9A FF 9A 9A 9A FF 98 98' + '98 FF 97 97 97 FF 96 96 96 FF 95 95 95 FF 93 93' + '93 FF 93 93 93 FF 90 90 90 FF 90 90 90 FF 8E 8E' + '8E FF 8D 8D 8D FF 8A 8A 8A FF 86 86 86 FF 94 94' + '94 FF 67 67 67 D4 00 00 00 4C 00 00 00 00 00 00' + '00 00 9E 9E 9E 57 A1 A1 A1 FE 72 72 72 FF CF CF' + 'CF FF CF CF CF FF CB CB CB FF C8 C8 C8 FF C5 C5' + 'C5 FF C2 C2 C2 FF BF BF BF FF BC BC BC FF B8 B8' + 'B8 FF B5 B5 B5 FF B2 B2 B2 FF AF AF AF FF AB AB' + 'AB FF A9 A9 A9 FF A6 A6 A6 FF A4 A4 A4 FF A3 A3' + 'A3 FF A2 A2 A2 FF A1 A1 A1 FF A0 A0 A0 FF 9F 9F' + '9F FF 9F 9F 9F FF 9E 9E 9E FF 9E 9E 9E FF 9B 9B' + '9B FF 9B 9B 9B FF 9B 9B 9B FF 9B 9B 9B FF 9A 9A' + '9A FF 98 98 98 FF 97 97 97 FF 96 96 96 FF 95 95' + '95 FF 94 94 94 FF 92 92 92 FF 90 90 90 FF 8F 8F' + '8F FF 8E 8E 8E FF 89 89 89 FF 88 88 88 FF 8E 8E' + '8E FF 7A 7A 7A E3 02 02 02 65 00 00 00 00 00 00' + '00 00 9D 9D 9D 5C A6 A6 A6 FF 7A 7A 7A FF C6 C6' + 'C6 FF D3 D3 D3 FF CC CC CC FF C9 C9 C9 FF C7 C7' + 'C7 FF C4 C4 C4 FF C1 C1 C1 FF BE BE BE FF BB BB' + 'BB FF B7 B7 B7 FF B3 B3 B3 FF B0 B0 B0 FF AC AC' + 'AC FF AB AB AB FF A7 A7 A7 FF A5 A5 A5 FF A3 A3' + 'A3 FF A2 A2 A2 FF A1 A1 A1 FF A1 A1 A1 FF 9F 9F' + '9F FF 9E 9E 9E FF 9D 9D 9D FF 9D 9D 9D FF 9C 9C' + '9C FF 9B 9B 9B FF 9C 9C 9C FF 9D 9D 9D FF 9A 9A' + '9A FF 9A 9A 9A FF 98 98 98 FF 97 97 97 FF 96 96' + '96 FF 95 95 95 FF 93 93 93 FF 91 91 91 FF 8F 8F' + '8F FF 8E 8E 8E FF 8A 8A 8A FF 89 89 89 FF 88 88' + '88 FF 8B 8B 8B EC 0C 0C 0C 7C 00 00 00 15 00 00' + '00 00 9F 9F 9F 65 A6 A6 A6 FF 84 84 84 FF AE AE' + 'AE FF DB DB DB FF D0 D0 D0 FF CD CD CD FF CA CA' + 'CA FF C7 C7 C8 FF C3 C3 C3 FF C1 C1 C1 FF BE BE' + 'BE FF B9 B9 B9 FF B6 B6 B6 FF B2 B2 B2 FF B0 B0' + 'B0 FF AC AC AC FF AA AA AA FF A7 A7 A7 FF A4 A4' + 'A4 FF A3 A3 A3 FF A1 A1 A1 FF A0 A0 A0 FF 9F 9F' + '9F FF 9E 9E 9E FF 9E 9E 9E FF 9D 9D 9D FF 9C 9C' + '9C FF 9C 9C 9C FF 9C 9C 9C FF 9C 9C 9C FF 9B 9B' + '9B FF 9A 9A 9A FF 99 99 99 FF 97 97 97 FF 97 97' + '97 FF 95 95 95 FF 93 93 93 FF 91 91 91 FF 8F 8F' + '8F FF 8D 8D 8D FF 8B 8B 8B FF 88 88 88 FF 84 84' + '84 FF 8F 8F 8F F2 1A 1A 1A 91 00 00 00 1C 00 00' + '00 00 A8 A8 A8 76 A7 A7 A7 FF 8F 8F 8F FF 88 88' + '88 FF AD AD AD FF C5 C5 C5 FF C1 C1 C1 FF C0 C0' + 'C0 FF BF BE BE FF BB BB BC FF B9 B9 B8 FF B5 B5' + 'B5 FF B1 B0 B1 FF AF AF AE FF AB AB AB FF A7 A7' + 'A7 FF A5 A5 A5 FF A3 A3 A3 FF A0 A0 A0 FF 9D 9D' + '9D FF 9D 9D 9C FF 9B 9B 9B FF 9D 9C 9C FF 9E 9E' + '9D FF A0 A0 A0 FF 9D 9D 9D FF 9D 9D 9D FF 9D 9D' + '9D FF 9B 9B 9B FF 9B 9B 9B FF 9B 9B 9B FF 9A 9A' + '9A FF 99 99 99 FF 98 98 98 FF 97 97 97 FF 96 96' + '96 FF 93 93 93 FF 91 91 91 FF 8F 8F 8F FF 8E 8E' + '8E FF 8B 8B 8B FF 87 87 87 FF 84 84 84 FF 81 81' + '81 FF 99 99 99 F5 58 58 58 8D 00 00 00 16 00 00' + '00 00 AA AA AA 99 A9 A9 A9 FF 93 93 93 FF 92 92' + '92 FF 89 89 89 FF 87 87 87 FF 84 84 84 FF 83 83' + '83 FF 81 81 80 FF 7E 7E 7E FF 7C 7C 7C FF 7A 7A' + '7A FF 79 78 78 FF 77 77 76 FF 76 76 75 FF 74 74' + '74 FF 74 74 74 FF 74 74 74 FF 74 74 74 FF 73 73' + '73 FF 73 73 73 FF 72 72 72 FF 73 72 72 FF 75 75' + '75 FF 8E 8E 8E FF A2 A2 A2 FF A1 A1 A1 FF A1 A1' + 'A1 FF A0 A0 A0 FF A0 A0 A0 FF 9F 9F 9F FF 9F 9F' + '9F FF 9E 9E 9E FF 9C 9C 9C FF 9B 9B 9B FF 99 99' + '99 FF 98 98 98 FF 94 94 94 FF 91 91 91 FF 90 90' + '90 FF 8D 8D 8D FF 8B 8B 8B FF 92 92 92 FF 9B 9B' + '9B FF 8E 8E 8E CE 62 62 62 46 00 00 00 00 00 00' + '00 00 B7 B7 B7 B6 AD AD AD FF 98 98 98 FF 98 98' + '98 FF 95 95 95 FF 93 93 93 FF 91 91 91 FF 8F 8F' + '8F FF 8C 8C 8C FF 88 88 88 FF 86 86 86 FF 84 84' + '84 FF 81 81 81 FF 80 80 80 FF 7E 7E 7E FF 7B 7B' + '7B FF 7B 7B 7B FF 79 79 79 FF 78 78 78 FF 78 78' + '78 FF 77 77 77 FF 76 76 76 FF 74 74 74 FF 73 73' + '73 FF 74 74 74 FF 7B 7B 7B FF 7D 7D 7D FF 7D 7D' + '7D FF 7D 7D 7D FF 7C 7C 7C FF 7B 7B 7B FF 7C 7C' + '7C FF 7A 7A 7A FF 79 79 79 FF 78 78 78 FF 78 78' + '78 FF 77 77 77 FF 75 75 75 FF 73 73 73 FF 71 71' + '71 FF 6E 6E 6E FF 73 73 73 FB 62 62 62 DF 7E 7E' + '7E B9 55 55 55 42 00 00 00 00 00 00 00 00 00 00' + '00 00 D5 D5 D5 AD B9 B9 BA FE 9F 9F A0 FF 9E 9F' + '9F FF 9C 9C 9C FF 9B 9B 9B FF 97 97 97 FF 95 95' + '95 FF 92 92 92 FF 91 91 91 FF 8D 8D 8E FF 8A 8A' + '8A FF 89 89 89 FF 86 86 86 FF 83 83 83 FF 81 81' + '81 FF 7F 7F 7F FF 7F 7F 7F FF 7E 7D 7D FF 7D 7D' + '7D FF 7A 7A 7A FF 7A 7A 7A FF 7B 7B 7B FF 7A 7A' + '7A FF 78 78 78 FF 79 79 79 FF 79 79 79 FF 78 78' + '78 FF 77 77 77 FF 76 76 76 FF 77 77 77 FF 75 75' + '75 FF 74 74 74 FF 73 73 73 FF 71 71 71 FF 70 70' + '70 FF 6E 6E 6E FF 6C 6C 6C FF 6B 6B 6B FF 68 68' + '68 FF 65 65 65 FF 6C 6C 6C F4 22 22 22 92 00 00' + '00 1B 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 BB BB BB 44 C7 C5 C5 E4 A6 A7 A9 FF 9D 9E' + 'A1 FF 9B 9C 9F FF 99 9A 9D FF 97 98 9B FF 93 95' + '98 FF 91 92 95 FF 8D 8E 91 FF 89 8B 8E FF 87 89' + '8C FF 84 86 88 FF 82 83 86 FF 7F 81 84 FF 7C 7E' + '82 FF 7B 7D 80 FF 7A 7C 7F FF 7A 7C 7F FF 7B 7D' + '80 FF 7B 7C 7F FF 7E 7E 7E FF 7E 7E 7E FF 7E 7E' + '7E FF 7D 7D 7D FF 7D 7D 7D FF 7D 7D 7D FF 7C 7C' + '7C FF 7B 7B 7B FF 7A 7A 7A FF 7A 7A 7A FF 79 79' + '79 FF 78 78 78 FF 77 77 77 FF 75 75 75 FF 74 74' + '74 FF 72 72 72 FF 70 70 70 FF 6E 6E 6E FF 6B 6B' + '6B FF 68 68 68 FF 75 75 75 F4 2D 2D 2D 8B 00 00' + '00 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 AF AF 9F 10 E8 E7 DF A0 DE D5 C1 FF D2 C7' + 'B0 FF D2 C7 B0 FF D0 C4 AE FF CB C0 AA FF C4 BA' + 'A4 FF BF B4 A0 FF BB B1 9C FF BA B0 9C FF B6 AD' + '98 FF AE A5 93 FF AA A0 8F FF A8 9F 8E FF A4 9C' + '8B FF 9D 95 85 FF 9A 91 81 FF 95 8E 7F FF 96 8E' + '7E FF 89 85 7E FF 7B 7D 81 FF 80 80 80 FF 7E 7E' + '7E FF 7F 7F 7F FF 7F 7F 7F FF 7E 7E 7E FF 7E 7E' + '7E FF 7E 7E 7E FF 7D 7D 7D FF 7C 7C 7C FF 7A 7A' + '7A FF 7A 7A 7A FF 78 78 78 FF 76 76 76 FF 74 74' + '74 FF 72 72 72 FF 71 71 71 FF 6D 6D 6D FF 6B 6B' + '6B FF 6C 6C 6C FF 87 87 87 EA 3D 3D 3D 64 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 F5 F4 EE 8C EE E5 CF FD DE D0' + 'B6 FF DB CE B4 FF DA CD B3 FF D8 CB B0 FF D7 C9' + 'AE FF D3 C6 AC FF D0 C3 A8 FF CD C0 A5 FF C9 BC' + 'A2 FF C6 B9 9E FF C3 B5 99 FF BE B1 94 FF BC AE' + '93 FF B7 AA 8F FF B2 A4 89 FF AE A0 84 FF AA 9C' + '81 FF B2 A2 82 FF 9A 95 8C FC 95 96 98 F7 9D 9D' + '9D F6 9A 9A 9A F6 9A 9A 9A F6 98 98 98 F7 98 98' + '98 F7 97 97 97 F7 96 96 96 F7 93 93 93 F7 91 91' + '91 F7 90 90 90 F7 8C 8C 8C F8 89 89 89 F9 87 87' + '87 F9 84 84 84 FA 81 81 81 FA 7B 7B 7B FB 7B 7B' + '7B FB 8F 8F 8F F3 26 26 26 69 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 E1 DA CE 45 FB FA E9 DF EE E6' + 'CF FF EC E2 C5 FF EB DF C4 FF E9 DC BF FF E8 DA' + 'BE FF E4 D5 B9 FF E0 D2 B5 FF DD CF B3 FF D9 CB' + 'AF FF D5 C7 AB FF D2 C4 A8 FF CE C0 A3 FF CB BD' + 'A1 FF C8 B9 9D FF C3 B5 99 FF BF B1 95 FF BC AE' + '92 FF CE BE 9F FC AA 9D 84 C6 90 90 90 7A AB AB' + 'AB 6E AD AD AD 6E B0 B0 B0 6F B5 B5 B5 6F B5 B5' + 'B5 6F B5 B5 B5 6F B6 B6 B6 70 B8 B8 B8 70 B0 B0' + 'B0 71 AE AE AE 72 A4 A4 A4 76 9E 9E 9E 79 95 95' + '95 7B 8F 8F 8F 7C 8A 8A 8A 7F 81 81 81 82 82 82' + '82 83 85 85 85 6D 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 D4 D0 CC 42 FC FA' + 'E3 CC FD FB E5 D3 FC FA E0 D4 FC F6 DD D4 FC F6' + 'D9 D4 FA F5 D7 D4 FA F2 D3 D4 F8 F0 D1 D4 F6 EB' + 'CD D4 F5 E8 CA D4 F2 E6 C2 D4 F1 E0 C1 D4 EE DD' + 'BB D4 E9 D9 B9 D4 E8 D8 B8 D4 E5 D3 B2 D4 E4 D4' + 'B0 D4 E0 CD AE BA 64 5B 48 38 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 E5 C2 B5 A9 F4 C3' - 'AA FF FF CD A0 FF F4 A9 8A FF F4 B8 AA FF FF FC' - 'FF FF FF FC FF FF FF FA FF FF FF F7 FF FF FF F3' - 'F5 FF FA EB EA FF F0 DF DF FF EF BD B4 FF E9 9B' - '89 FF DF 77 5F FF CA 7F 6A FF BA 8F 7F FF B0 A7' - 'A0 FF B9 B1 B4 F3 C4 BC C4 D2 CF C8 CF 9C DA D7' - 'DA 66 DB D8 D5 32 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 E5 B8 A6 A9 F4 BA' - '9C FF FF D1 A0 FF F4 A7 83 FF F4 B5 A2 FF FF FC' - 'FF FF FF FC FF FF FF FA FF FF FF F7 FF FF FF F3' - 'F5 FF FD EE EE FF FA E9 EA FF F3 DE DC FF ED CF' - 'CA FF EA BC B4 FF E3 A9 9B FF DA 97 83 FF CF 87' - '6A FF C4 8B 78 FB BD 97 8F F0 BA AC AF DD C4 BC' - 'C0 CB CC C5 C9 A8 D0 C8 C9 73 D7 CF D0 50 D7 D0' - 'CD 29 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 E5 AF 9C A9 F4 B4' - '93 FF FF D9 A5 FF F4 AB 81 FF F4 B5 9D FF FF F8' - 'FA FF FF F9 FD FF FF F9 FF FF FF F7 FF FF FF F3' - 'F5 FF FD F0 F0 FF FA EF F0 FF F3 EF F0 FF F0 EA' - 'EC FF F0 E1 E5 FF EF CA C4 FF EB B2 A3 FF E4 9A' - '7F FF D3 8F 78 FF C7 8C 7C FF BF 91 89 FF C2 9A' - '94 FE C4 A6 A4 EC C4 B4 B9 C9 CB C3 C8 A5 CE C9' - 'CA 77 CC C6 BF 3F D2 CB C6 2E D8 D0 CB 1C DB D3' - 'CF 0C D5 CD C4 04 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 E5 AA 95 A9 F4 B2' - '90 FF FF E4 B0 FF F5 B6 85 FF F5 BA 9A FF FF F1' - 'F0 FF FF F5 FA FF FF F7 FF FF FF F7 FF FF FF F3' - 'F5 FF FA F0 F0 FF F0 EF F0 FF F0 EF F0 FF F0 EC' - 'EF FF F0 E7 EF FF F0 E3 E5 FF F0 E2 E0 FF F0 E2' - 'E0 FF E5 C0 B5 FF DF 9C 8A FF DF 77 5F FF D4 71' - '55 FF C4 79 64 FF B0 90 8F FF B0 A5 A4 FF B5 B5' - 'B5 EA C0 C0 C0 C0 D4 CF D4 8A E4 DC E4 56 EF E8' - 'EF 26 DB D4 CF 0C 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 D8 C8 BE 38 E6 C6 BD A9 EC B6 A2 E2 F4 BE' - '9A FF FF E0 A5 FF F5 BA 88 FF F5 B6 90 FF FF D3' - 'BA FF FF D2 AF FF FF D9 BB FF FF E7 DF FF FF ED' - 'EA FF FA F0 F0 FF F0 EF F0 FF F0 EF F0 FF F0 EC' - 'EF FF F0 E7 EF FF F0 E3 E5 FF F0 E2 E0 FF F0 E2' - 'E0 FF EC D5 D1 FF E9 C8 C3 FF E9 BC B4 FF DF A8' - '9C FF D6 94 85 FF CF 80 6F FF C8 7F 6F FF C2 88' - '7B F8 BF 9A 94 E9 BF B0 B1 D8 C5 C1 C5 BC CF CC' - 'CF 98 D0 CD CC 6C D7 D3 D1 45 E5 DD DE 23 D8 D0' - 'C9 0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 DC C8 BE 54 F0 C5 BD FF F3 C0 AD FF F8 C9' - 'A3 FF FF DE A0 FF F5 BD 8A FF F5 B4 8A FF FF C3' - '9F FF FF BD 83 FF FF C0 84 FF FF CB A4 FF FF D2' - 'B3 FF FA D9 C3 FF F0 E1 D5 FF F0 EA E7 FF F0 EC' - 'EF FF F0 E7 EF FF F0 E3 E5 FF EF E1 E0 FF EF E1' - 'E0 FF EF DF E0 FF EF DF E0 FF EF DF E0 FF E5 CB' - 'CA FF DF B2 AC FF DF 95 84 FF D8 89 79 FF D0 84' - '76 FF C9 86 79 FF BF 8F 84 FF BC 9B 93 F5 BF A9' - 'A5 E0 C3 BA B7 BD CD C9 CA 97 DF D7 DF 6F D5 CF' - 'CD 45 D4 CF C8 25 DB D6 CF 0E D5 CE C4 04 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 DC C9 BF 54 F0 C7 BF FF FA CA B5 FF FF D2' - 'AA FF FF DE A0 FF F5 BF 8A FF F5 B5 8A FF FF BF' - '9F FF FF B5 74 FF FF AD 59 FF FF A5 4F FF FF A1' - '4F FF FA AC 6A FF F0 C7 A0 FF F0 E1 D5 FF F0 EC' - 'EF FF F0 E7 EF FF F0 E3 E5 FF EF E1 E0 FF EF DF' - 'E0 FF EF DF E0 FF EF DF E0 FF EF DF E0 FF E5 D9' - 'DF FF E0 D4 DA FF E0 D0 D0 FF DF C4 C4 FF DF AB' - 'A4 FF E0 84 6F FF D5 6A 4F FF CA 68 4F FF BF 7F' - '70 FF B5 99 8F FF B5 AC AA F6 BF B8 BF E4 CA C7' - 'CA AE DA DA DA 70 F0 EF F0 2C DC D6 CF 0E 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 DB C3 B7 54 EF B5 A9 FF F9 D0 B4 FF FF DE' - 'B4 FF FF E2 AA FF FB C7 95 FF F8 BA 8E FF F5 B9' - '95 FF FB BD 7F FF FF B9 6B FF FF AD 59 FF FF A6' - '52 FF FA A4 54 FF F0 A7 60 FF F0 AC 71 FF F0 B5' - '88 FF F0 BF A5 FF F0 D0 C4 FF EF DA D8 FF EF DF' - 'E0 FF EF DF E0 FF EB DD DF FF E5 D9 DF FF E1 D7' - 'DF FF E0 D4 DA FF E0 D0 D0 FF DF CB CC FF DF C3' - 'C1 FF DF B5 AF FF DB A2 96 FF D4 92 81 FF CA 85' - '70 FF C7 7A 64 FF C3 7D 6A FC BF 8C 7F F6 C2 AA' - 'A6 E4 CF C6 C8 C0 E4 DF E4 8A D8 D1 CB 2E 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 DB BF B2 54 EF AB 9A FF F9 D2 B2 FF FF E5' - 'BA FF FF E4 B0 FF FF CE 9E FF FA BD 91 FF F0 B1' - '8A FF FA C1 83 FF FF C1 74 FF FF B3 5F FF FF AC' - '57 FF FB A4 4F FF F5 9B 44 FF F1 95 41 FF EF 96' - '4B FF EF 9D 65 FF EF AB 84 FF ED B8 9F FF EA C4' - 'B5 FF EA CD C6 FF E6 D3 D4 FF E0 D7 DF FF E0 D5' - 'DB FF E0 D3 D6 FF E0 CF D0 FF DF CF D0 FF DF CF' - 'D0 FF DF CF D0 FF DF C2 C1 FF DA B1 AA FF D0 9A' - '8A FF D0 85 74 FF CC 78 64 FF C4 74 5A FF C0 90' - '84 FF C7 B0 AF EB D9 D2 D9 C5 DB D5 D2 4C D8 D2' - 'C9 0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 DC BE AF 54 F0 A8 90 FF FA D2 AF FF FF E6' - 'BA FF FF E4 B0 FF FF D4 A4 FF FA BF 94 FF F0 A7' - '80 FF FA BF 80 FF FF C5 75 FF FF B8 60 FF FF B3' - '5F FF FF AD 59 FF FF A5 4F FF F5 9B 45 FF EF 8F' - '3A FF EF 80 30 FF EF 75 25 FF EA 7A 35 FF E0 90' - '60 FF E0 AA 94 FF E0 C2 BE FF E0 D7 DF FF E0 D2' - 'D5 FF E0 CF D0 FF E0 CF D0 FF DF CF D0 FF DF CF' - 'D0 FF DF CF D0 FF DF C9 CF FF DA C4 CA FF D0 C0' - 'C0 FF D0 BA BF FF D0 9F 9A FF D0 6F 50 FF C4 79' - '65 FF C4 97 8F F3 CF C8 CF DD E4 E2 E4 69 E6 E3' - 'DF 1F 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E1 C7' - 'BD 71 EB C1 B4 C6 F0 B8 A4 FF FA D7 B6 FF FF E5' - 'BA FF FF E4 B0 FF FF DE AC FF FA CA 9C FF F0 A7' - '80 FF FA C7 8E FF FF CF 86 FF FF BD 69 FF FF B8' - '63 FF FF B2 5D FF FF AD 59 FF FB A4 4F FF F6 9A' - '44 FF EF 8F 3A FF EF 81 30 FF ED 7A 2E FF EA 7A' - '35 FF E2 7F 46 FF DF 87 5B FF DF 92 74 FF DF A6' - '94 FF DF BA B3 FF E0 CF D0 FF DF CF D0 FF DF CF' - 'D0 FF DF CF D0 FF DF C9 CF FF DA C4 CA FF D0 C0' - 'C0 FF D0 BD BF FF D0 AD A8 FF D0 8E 7A FF C5 7D' - '65 FF C1 8E 7E F9 C5 C2 C5 EF E1 E0 E1 79 E6 E2' - 'DF 29 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E7 C8' - 'BD A9 F3 C7 BA FF F5 C7 B4 FF FB DC BB FF FF E6' - 'BB FF FF E4 B5 FF FF E4 B1 FF F9 CD 9E FF EF 9F' - '7A FF F9 CA 97 FF FF D6 95 FF FF C4 74 FF FF BD' - '6A FF FF B8 63 FF FF B3 5F FF FF AC 57 FF FB A4' - '4F FF F5 9B 44 FF F1 8E 3A FF EF 81 30 FF EF 75' - '25 FF E4 6D 21 FF DF 6B 26 FF DF 6D 34 FF DB 7F' - '57 FF DA 92 78 FF DA A7 95 FF DA B1 A6 FF DB BC' - 'B8 FF DF C9 CA FF DF C7 CD FF DA C4 CA FF D0 C0' - 'C0 FF D0 BF C0 FF D0 B6 B3 FF D0 A4 9A FF C8 7C' - '65 FF C3 84 71 FD BF BD BF FB DF DC DF 88 E5 E1' - 'DF 34 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E6 CC' - 'BF A9 F5 D0 BF FF FF D7 BF FF FF E2 BF FF FF E7' - 'BF FF FF E6 BF FF FF E4 B5 FF F9 C7 9A FF EF 8F' - '70 FF F9 C7 9A FF FF DC A0 FF FF CC 80 FF FF C4' - '74 FF FF BD 6A FF FF B8 60 FF FF B3 5F FF FF AD' - '59 FF FF A5 4F FF F5 9B 45 FF EF 8F 3A FF EF 80' - '30 FF E5 75 25 FF DF 6D 1F FF DF 67 1F FF D5 5D' - '1F FF D0 57 1F FF D0 57 1F FF D0 74 54 FF D5 97' - '89 FF DF BF BF FF DF C4 C9 FF DA C4 CA FF D0 C0' - 'C0 FF D0 BF C0 FF D0 BA BA FF D0 B0 B0 FF D0 78' - '64 FF CA 7A 69 FF BF B7 BF FF DE D7 DE 95 E5 DE' - 'DF 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 C0' - 'B1 A9 F4 CA B5 FF FF E6 CA FF FF E7 C3 FF FF E7' - 'BF FF FF E7 BF FF FF E6 BB FF F9 CD A4 FF EF 9E' - '7A FF F9 CE A5 FF FF DD A7 FF FF CC 80 FF FF C9' - '7C FF FF C4 74 FF FF BD 69 FF FF B8 63 FF FF B2' - '5A FF FF AC 4F FF FB A4 4B FF F6 9A 44 FF EF 8F' - '3A FF EB 81 2F FF E6 76 26 FF DF 6D 1F FF DB 65' - '1F FF D6 5E 1F FF D0 57 1F FF CF 5D 2A FF D1 65' - '38 FF D4 6D 4A FF D4 82 69 FF D2 97 89 FF D0 AF' - 'AA FF D0 B9 B8 FF D0 BC BE FF D0 B8 BA FF D0 7B' - '68 FF C7 79 66 FF B5 B2 B5 FF D4 D2 D4 A1 DE DA' - 'D8 4C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 BB' - 'AA A9 F4 C7 B1 FF FF EF D5 FF FF EC CA FF FF E9' - 'C3 FF FF E8 C0 FF FF E7 BF FF FA D1 AB FF F0 A7' - '85 FF FA CE AC FF FF DB AC FF FF CE 85 FF FF CC' - '81 FF FF C9 7C FF FF C4 74 FF FF BD 6A FF FF B8' - '5F FF FF B2 55 FF FF AC 54 FF FB A4 4F FF F5 9B' - '44 FF F4 8E 39 FF EF 81 2F FF E5 75 24 FF E1 6D' - '21 FF DB 65 1F FF D5 5C 1F FF D1 55 18 FF CF 4E' - '13 FF CF 48 10 FF CF 58 2B FF CD 6C 4A FF CA 82' - '6A FF C7 91 83 FF C7 9E 96 FF CA A9 A4 FF CE 7C' - '68 FF C7 7B 68 FF B5 A7 A5 FF CE C9 C8 AD D7 D4' - 'D1 58 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E6 BB' - 'AA A9 F5 C9 B5 FF FF F3 DF FF FF F0 D4 FF FF EC' - 'CA FF FF E8 C0 FF FF E8 C0 FF FA D2 AF FF F0 A7' - '8F FF FA C6 AE FF FF D6 AF FF FF D4 90 FF FF CE' - '85 FF FF CC 80 FF FF CC 80 FF FF C4 74 FF FF BD' - '6A FF FF B8 60 FF FF B3 5F FF FF AD 59 FF FF A5' - '4F FF FF 9B 44 FF F9 8F 39 FF EF 80 2F FF E5 75' - '25 FF DF 6D 1F FF DF 67 1F FF D5 5D 1F FF CF 55' - '1A FF CF 4F 10 FF CF 48 10 FF CA 40 0A FF C0 38' - '00 FF B5 47 1F FF B5 60 45 FF C0 84 70 FF CA 7B' - '64 FF CA 81 6E FF BF 97 8F FF CA BC BA BA D0 CE' - 'CA 65 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 D9 C9 BE 38 E8 C7 BD A9 F4 C3' - 'B7 E2 FB D1 BF FF FF F1 D5 FF FF EF D1 FF FF ED' - 'CD FF FF EC C9 FF FF E9 C3 FF FA D6 B3 FF F0 B2' - '99 FF F3 BB A4 FF F8 C7 A8 FF FF D6 A5 FF FF D7' - 'A1 FF FF D5 98 FF FF CE 89 FF FF C7 7F FF FF C2' - '74 FF FF BD 69 FF FF B8 63 FF FF B2 5D FF FF AC' - '59 FF FF A8 4F FF FD A2 44 FF F9 99 3A FF F6 8D' - '2F FF F0 82 2A FF E9 77 29 FF DF 69 22 FF D6 5D' - '1D FF CF 54 19 FF CF 4E 13 FF CD 47 0E FF C9 40' - '0A FF B8 38 0E FF B1 3F 1A FF B5 56 30 FF C0 56' - '32 FF C3 68 4B FF BF 8A 7A FF CA B5 B2 C6 D6 D2' - 'D4 73 E5 E3 DF 07 D8 D2 C9 02 00 00 00 00 00 00' - '00 00 00 00 00 00 DC C9 C0 54 F2 C9 C2 FF FA CD' - 'C3 FF FF DA C9 FF FF F1 D5 FF FF EF D1 FF FF EE' - 'CF FF FF EE CF FF FF EB C8 FF FB DE BC FF F5 C7' - 'A9 FF F5 BE A3 FF F6 BE A0 FF FA C7 A0 FF F9 C7' - '9F FF FB C8 9B FF FF C9 94 FF FF CA 8D FF FF C9' - '84 FF FF C6 79 FF FF BE 6B FF FF B8 63 FF FF B3' - '5F FF FF B0 58 FF FF AD 4F FF FF A9 44 FF FF 9F' - '3A FF FB 95 35 FF F4 8C 34 FF ED 7D 2A FF E4 6F' - '22 FF DA 62 1F FF D6 59 18 FF D2 50 13 FF CF 48' - '10 FF B9 32 05 FF B1 30 06 FF B5 44 14 FF BC 46' - '1B FF C0 58 38 FF C0 7D 6A FF C6 AD AA D2 D6 D3' - 'D6 83 F0 EF F0 12 DC D6 CF 06 00 00 00 00 00 00' - '00 00 00 00 00 00 DC CB C4 54 F0 CF CF FF FA D9' - 'CF FF FF E5 D4 FF FF F3 DF FF FF F1 D5 FF FF EF' - 'D0 FF FF EE D0 FF FF EE CF FF FF EC CA FF FF E7' - 'C0 FF FF D0 AA FF FA BB 95 FF F0 A7 80 FF EF 9E' - '80 FF F4 A6 8A FF FF BF A0 FF FF CC A0 FF FF D4' - '9A FF FF D4 90 FF FF C6 7A FF FF BD 6A FF FF B8' - '60 FF FF B3 5F FF FF B0 59 FF FF AF 4F FF FF A9' - '45 FF FF A6 40 FF FF A6 40 FF FF 9A 35 FF FA 8A' - '2A FF F0 78 20 FF E4 67 1F FF D9 59 1A FF CF 4F' - '10 FF B9 34 05 FF B4 34 0A FF C0 4F 1F FF C0 49' - '1F FF C0 54 34 FF C0 6F 5F FF C0 A4 9F DF CF CF' - 'CF 95 F0 EF F0 23 DC D6 CF 0B 00 00 00 00 00 00' - '00 00 00 00 00 00 DC C8 BD 54 F0 C4 BA FF FA E0' - 'CF FF FF EF DB FF FF F3 DF FF FF F2 DB FF FF F1' - 'D6 FF FF EF D0 FF FF EE CF FF FF ED CD FF FF EC' - 'C9 FF FF E1 BC FF FD D8 B1 FF F9 D2 AA FF F9 C8' - 'A3 FF FB C2 9C FF FF BF 95 FF F7 AE 86 FF F4 A8' - '81 FF F4 AD 85 FF FB BC 8C FF FF C4 8A FF FF C7' - '7F FF FF BB 6A FF FF B3 5A FF FF AF 4F FF FF AD' - '4B FF FF AA 46 FF FF A6 40 FF FF A1 3C FF FD 9B' - '35 FF F9 91 2A FF F6 85 29 FF EB 72 24 FF D9 59' - '19 FF BD 3F 0F FF B8 3D 11 FF C9 54 1F FF C3 4F' - '1F FF C3 54 2D FF C9 65 4A FF C2 9D 98 EB CF CA' - 'CF A7 EF EA EF 32 DB D4 CF 11 00 00 00 00 00 00' - '00 00 00 00 00 00 DC C5 B9 54 F0 BD AF FF FA E3' - 'D3 FF FF F6 E3 FF FF F4 DF FF FF F3 DF FF FF F2' - 'DB FF FF F1 D5 FF FF EF D1 FF FF EE CF FF FF EE' - 'CF FF FF EB C8 FF FF E9 C3 FF FF E8 C0 FF FF E0' - 'B8 FF FF D7 AE FF FF CC 9F FF F7 B6 8D FF F4 AB' - '86 FF F4 AA 8A FF F7 B0 8E FF FB BC 93 FF FF CE' - '99 FF FF C3 7A FF FF BA 63 FF FF B2 55 FF FF B0' - '51 FF FF AD 4B FF FF A9 44 FF FF A7 41 FF FF A4' - '3C FF FF A1 35 FF FF 98 31 FF F6 85 2B FF E4 67' - '24 FF CC 50 1D FF C4 4B 1B FF CF 59 1F FF C8 54' - '1F FF C8 55 28 FF CF 5C 3A FF C1 97 8F F6 C9 C4' - 'C9 B7 EA E5 EA 42 DA D3 CD 16 00 00 00 00 00 00' - '00 00 00 00 00 00 DC C4 B9 54 F0 BA AF FF FA E4' - 'DA FF FF F8 EA FF FF F6 E0 FF FF F4 DF FF FF F3' - 'DF FF FF F3 DF FF FF F1 D5 FF FF EF D0 FF FF EE' - 'D0 FF FF EE CF FF FF EC CA FF FF E8 C0 FF FF E8' - 'C0 FF FF E7 BF FF FF E6 BF FF FF E4 B5 FF FF DC' - 'AA FF FF CD A0 FF F4 A3 7F FF F4 A3 84 FF FF CC' - 'B0 FF FF CC 90 FF FF C5 75 FF FF B8 60 FF FF B2' - '55 FF FF AF 4F FF FF AF 4F FF FF A9 45 FF FF A6' - '40 FF FF A6 40 FF FF A1 35 FF FA 92 2F FF F0 78' - '2F FF E4 68 2F FF D9 60 2A FF CF 60 20 FF CF 5A' - '1F FF CF 56 24 FF D0 54 2F FF BA 91 84 FF BF BF' - 'BF C5 E0 DF E0 52 D6 D1 CA 1B 00 00 00 00 00 00' - '00 00 E1 C8 BD 71 EE C7 BC C6 F9 C8 BA FF FD E9' - 'DE FF FF F9 EE FF FF F8 EA FF FF F6 E3 FF FF F4' - 'DF FF FF F3 DF FF FF F2 DB FF FF F1 D6 FF FF EF' - 'D0 FF FF EE CF FF FF ED CD FF FF EC C9 FF FF E9' - 'C3 FF FF E7 BF FF FF E7 BF FF FF E6 BB FF FF E2' - 'B4 FF FF DC AA FF FB C4 98 FF F4 B1 8C FF EA A3' - '85 FF F8 C3 97 FF FF CF 91 FF FF C5 75 FF FF BA' - '63 FF FF B3 56 FF FF AF 4F FF FF AD 4B FF FF AA' - '46 FF FF A6 40 FF FF A4 3C FF FA 93 36 FF F0 72' - '2F FF F2 81 3D FF EF 88 3F FF E4 84 35 FF D6 6A' - '26 FF CF 5A 24 FF D0 54 2F FF C1 86 76 FF C3 B1' - 'AD CB D5 D5 D5 64 D3 CD C6 21 00 00 00 00 00 00' - '00 00 E7 CA C1 AA F6 CE C5 FF FF D6 CA FF FF EE' - 'E3 FF FF FA F0 FF FF FA F0 FF FF F8 E8 FF FF F6' - 'E3 FF FF F4 DF FF FF F3 DF FF FF F2 DB FF FF F1' - 'D5 FF FF EF D1 FF FF EE CF FF FF EF CF FF FF EB' - 'C8 FF FF E9 C3 FF FF E8 C0 FF FF E7 BF FF FF E6' - 'BB FF FF E4 B5 FF FF DA AA FF F8 C2 97 FF EA 9F' - '7A FF F1 B1 8C FF F6 BB 8E FF F9 BB 80 FF FD B8' - '75 FF FF B6 6C FF FF B5 64 FF FF B4 5D FF FF B0' - '53 FF FF A9 45 FF FF A7 41 FF F8 92 3A FF EA 6A' - '2F FF F8 92 48 FF FB A5 51 FF F5 A1 4A FF E2 7E' - '35 FF D6 65 2B FF D0 57 2F FF C4 7D 68 FF C4 A4' - '9D D1 CF CD CF 77 D7 D3 CF 2C DB D6 CF 07 00 00' - '00 00 E6 D0 C9 AA F5 D9 D4 FF FF E4 DF FF FF F2' - 'EA FF FF FA F0 FF FF FA F0 FF FF FA F0 FF FF F8' - 'EA FF FF F6 E0 FF FF F4 DF FF FF F3 DF FF FF F3' - 'DF FF FF F1 D5 FF FF EF CF FF FF EF CF FF FF EF' - 'CF FF FF EC CA FF FF E8 C0 FF FF E8 C0 FF FF E7' - 'BF FF FF E6 BF FF FF E4 B5 FF FF D8 A5 FF FF C0' - '90 FF EA 96 70 FF E5 89 6A FF EF 9A 80 FF F9 AD' - '89 FF FF BA 8F FF FF C0 8F FF FF C0 7A FF FF BA' - '65 FF FF B0 50 FF FF A9 45 FF F4 8E 3A FF E0 60' - '2F FF F4 9A 4F FF FF B8 60 FF FF B8 60 FF F4 97' - '4A FF E4 79 39 FF D0 5F 2F FF C5 77 5A FF C5 9A' - '8F D8 CF C7 CF 8A E4 E2 E4 3C F0 F0 F0 16 00 00' - '00 00 E6 CA C2 AA F5 D6 D1 FF FF EC EA FF FF F6' - 'F4 FF FF FA F6 FF FF FA F0 FF FF FA F0 FF FF F9' - 'EE FF FF F8 EA FF FF F6 E3 FF FF F4 DF FF FF F3' - 'DF FF FF F2 DB FF FF F1 D6 FF FF EF CF FF FF EF' - 'CF FF FF EE CD FF FF EC C9 FF FF E9 C3 FF FF E7' - 'BF FF FF E7 BF FF FF E6 BB FF FF E0 B3 FF FF D7' - 'A5 FF F8 CA 9A FF F6 C0 95 FF F9 BB 94 FF F6 B3' - '89 FF F1 A7 7D FF EA 96 6F FF EA 96 68 FF ED 9B' - '65 FF F5 A4 65 FF FB A9 68 FF F8 9C 61 FF E9 7A' - '4F FF F7 AC 68 FF FF C2 71 FF FF BD 69 FF FB AB' - '5B FF EF 90 48 FF D9 6A 2F FF CF 72 4C FF C8 8F' - '7E DE C5 C1 C5 9B D4 CF CB 38 DC D6 CF 07 00 00' - '00 00 E6 C6 BB AA F5 D4 CD FF FF F4 F5 FF FF F9' - 'FB FF FF FB FB FF FF FA F5 FF FF FA F1 FF FF FA' - 'F0 FF FF FA F0 FF FF F8 E8 FF FF F6 E3 FF FF F4' - 'DF FF FF F3 DF FF FF F2 DB FF FF F1 D5 FF FF EF' - 'D1 FF FF EF CF FF FF EF CF FF FF EB C8 FF FF E9' - 'C3 FF FF E8 C0 FF FF E7 BF FF FF E6 BB FF FF E4' - 'B5 FF FF E4 B1 FF FF DE AC FF FF D3 A4 FF F8 C5' - '96 FF F1 B4 86 FF EA A0 75 FF EA 9E 75 FF EC 9F' - '75 FF F0 A4 74 FF F6 A3 74 FF F4 97 6B FF E9 7F' - '5A FF F4 A3 6C FF F9 B6 75 FF FA B9 74 FF FD B7' - '6D FF F7 A6 5D FF E9 87 44 FF D8 73 48 FF CB 87' - '73 EB C5 C1 C5 C2 CD C7 C1 41 00 00 00 00 00 00' - '00 00 E6 C3 B5 AA F5 D4 CA FF FF FC FF FF FF FC' - 'FF FF FF FC FF FF FF FC FF FF FF FA F5 FF FF FA' - 'F0 FF FF FA F0 FF FF FA F0 FF FF F8 EA FF FF F6' - 'E0 FF FF F4 DF FF FF F3 DF FF FF F3 DF FF FF F1' - 'D5 FF FF EF CF FF FF EF CF FF FF EF CF FF FF EC' - 'CA FF FF E8 C0 FF FF E8 BF FF FF E7 BF FF FF E6' - 'BF FF FF E4 B5 FF FF E4 B0 FF FF E4 B0 FF FF E4' - 'B0 FF FF E2 AA FF FF DE A0 FF FF D7 A0 FF FA C8' - '95 FF F0 B0 7F FF EF 95 6A FF E9 7F 5A FF DF 6F' - '4F FF E9 7F 5A FF EF 93 6A FF F0 AC 80 FF F9 B9' - '80 FF FF BD 7A FF FF B7 6F FF DF 7D 4F FF CF 82' - '6F FF CF C7 CF FF D0 C8 C4 55 00 00 00 00 00 00' - '00 00 EA CB C3 AA F6 D0 CA FF F5 D8 D4 FF F5 D5' - 'CD FF F8 D8 D1 FF FF E1 DF FF FF E8 E2 FF FF F0' - 'E8 FF FF FA F0 FF FF FA F0 FF FF F9 EE FF FF F8' - 'EA FF FF F4 E2 FF FB E3 D0 FF F5 C5 B4 FF F4 BD' - 'A9 FF F4 BD A8 FF F5 C4 AF FF FB D1 B6 FF FF DB' - 'BC FF FF E2 BF FF FF E6 BF FF FF E7 BF FF FF E7' - 'BF FF FF E6 BB FF FF E4 B6 FF FF E4 B0 FF FF E4' - 'B0 FF FF E3 AE FF FF E2 AA FF FF DD A3 FF FD D6' - '9C FF F9 CE 94 FF F3 AF 7F FF E6 98 76 FF D4 89' - '79 FF DF A4 93 FF E8 B0 9C FF EF AC 94 FF EC 99' - '78 FF EA 8D 65 FF EA 87 59 FF DF 69 41 FF D7 7F' - '64 C6 D0 C8 C4 55 D1 C9 C0 1C 00 00 00 00 00 00' - '00 00 E3 CE C6 71 EA CD C6 AA E6 C8 BF AA EC C0' - 'B4 BB F3 C2 B7 D7 FA CE C7 FF FA D4 CE FF FA DB' - 'D3 FF FA E2 D5 FF FA E4 D8 FF FB E7 DC FF FF EC' - 'E0 FF FB E2 D1 FF F6 D1 BF FF F0 B8 A9 FF E8 B1' - 'A2 C6 E5 B0 A2 AA E6 B7 A9 AA F3 C5 B1 E2 F9 CD' - 'B5 FF F9 D0 B4 FF F9 CE AE FF F9 D0 AE FF FA D6' - 'B4 FF FD D9 B4 FF FF DE B4 FF FF E4 B5 FF FF E4' - 'B1 FF FF E4 B0 FF FF E4 B0 FF FF E1 A8 FF FF DF' - 'A3 FF FF DE A0 FF F5 B9 86 FF E3 A3 82 FF CA 9C' - '94 FF D5 B9 B1 C6 DE C2 B8 AA E5 B6 A9 AA DE 9F' - '8D AA DB 91 7B AA DB 8D 74 AA E1 83 66 E2 DE 97' - '7F AA 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E6 BF' - 'B4 33 F0 BC B2 88 F2 C2 B8 FF F0 C1 B8 FF F0 BC' - 'B0 FF F0 B4 A0 FF F0 B8 AA FF F5 C1 B4 FF FF D0' - 'C0 FF F5 BD AA FF F0 BC AA FF F0 CD C0 FF DC CB' - 'BF 55 00 00 00 00 00 00 00 00 E6 CC BF A9 EF C4' - 'B5 FF EF B4 A0 FF EF A2 8A FF EF A2 8A FF F0 B4' - 'A0 FF F9 BF A0 FF FF CF AA FF FF E6 BF FF FF E4' - 'B5 FF FF E4 B0 FF FF E4 B0 FF FF E4 B0 FF FF E2' - 'AA FF FF DE A0 FF F5 B4 80 FF E0 A1 7F FF C0 A7' - 'A0 FF CB BE B4 55 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 E5 CC BF A9 E6 CC' - 'BF AA 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 D8 C6' - 'BB 11 DC C5 BA 2D DC C7 BC 55 DC C7 BC 55 DC C5' - 'B9 55 DC C2 B4 55 E9 BC B0 A6 F1 BC B0 C1 F5 C1' - 'B4 A5 F1 BA AD C2 E8 BD B0 A7 DC CB BF 55 D5 CA' - 'BF 1C 00 00 00 00 00 00 00 00 D8 CA BF 38 DB C8' - 'BB 55 DB C2 B4 55 DB BC AD 55 DB BC AD 55 DC C2' - 'B4 55 EC BF AD C6 F4 BA A3 FF F4 B3 95 FF F4 B2' - '91 FF F4 B9 93 FF F5 C6 9A FF FB CF A1 FF FF D9' - 'A7 FF FF E2 AA FF ED A8 7C FF DC 9D 83 FF CA C1' - 'BF FF CF C7 BF 55 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 D8 CA BF 38 D8 CA' - 'BF 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 DF C2 B7 51 E6 BF B4 6C E6 BF' - 'B4 50 E5 BE B3 6D DE C2 B7 52 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 DF C2 B7 71 E5 B8 A9 AA E5 A9 94 AA E5 A9' - '94 AA EA B1 9A C6 F5 C0 A4 FF F8 C0 9E FF F8 C2' - '9A FF F4 C7 9A FF E6 9C 77 FF DA 9D 86 E2 D0 CD' - 'CA AA D1 CB C2 38 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 E0 CD C4 54 FF D4 CF FF F4 B6 A5 FF EA 9F' - '85 FF E0 8F 70 FF E0 8F 70 FF DB A2 8A AA 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 D7 CB C0 1C E1 CD C4 55 DD C3 B6 55 DA BB' - 'AB 55 D6 B6 A4 55 D6 B6 A4 55 D5 BC AD 38 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -3805,32 +7923,42 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' - 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF 00' - '3F FF FF FF 00 00 FF 00 3F FF FF FF 00 00 FF 00' - '07 FF FF FF 00 00 FF 00 00 FF FF FF 00 00 FF 00' - '00 FF FF FF 00 00 FE 00 00 1F FF FF 00 00 FE 00' - '00 03 FF FF 00 00 FE 00 00 03 FF FF 00 00 FE 00' - '00 00 7F FF 00 00 FE 00 00 00 03 FF 00 00 FE 00' - '00 00 03 FF 00 00 F8 00 00 00 00 7F 00 00 F8 00' - '00 00 00 0F 00 00 F8 00 00 00 00 0F 00 00 F8 00' - '00 00 00 0F 00 00 F8 00 00 00 00 07 00 00 F8 00' - '00 00 00 07 00 00 F0 00 00 00 00 07 00 00 F0 00' - '00 00 00 07 00 00 F0 00 00 00 00 07 00 00 F0 00' - '00 00 00 07 00 00 F0 00 00 00 00 07 00 00 F0 00' - '00 00 00 07 00 00 C0 00 00 00 00 01 00 00 C0 00' - '00 00 00 01 00 00 C0 00 00 00 00 01 00 00 C0 00' - '00 00 00 01 00 00 C0 00 00 00 00 01 00 00 C0 00' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 E0 00 00 00 00 0F 00 00 C0 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 03 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 03 00 00 80 00 00 00 00 03 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' '00 00 00 00 00 00 80 00 00 00 00 00 00 00 80 00' '00 00 00 00 00 00 80 00 00 00 00 01 00 00 80 00' - '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' - '00 00 00 07 00 00 F0 00 60 00 03 E7 00 00 F0 00' - '60 00 03 E7 00 00 FF C1 FF 80 03 FF 00 00 FF FF' - 'FF F8 0F FF 00 00 FF FF FF F8 0F FF 00 00 FF FF' + '00 00 00 03 00 00 80 00 00 00 00 07 00 00 80 00' + '00 00 00 07 00 00 80 00 00 00 00 0F 00 00 C0 00' + '00 00 00 1F 00 00 C0 00 00 00 00 3F 00 00 E0 00' + '01 FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 28 00' - '00 00 20 00 00 00 40 00 00 00 01 00 20 00 00 00' - '00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 40 00 00 00 80 00 00 00 01 00 04 00 00 00' + '00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -3848,412 +7976,501 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 C5' - 'BC FF D0 BC BF FF BF B8 BF FF CF C7 CF A5 DF D8' - 'DF 54 F0 EF F0 0F 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 88 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 00 00 00 00 78 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 00 00 00 00 78 88 88 88 88 88 88 88 88' + '88 88 88 87 78 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 70 00 00 00 78 88 88 FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF 88 88 88' + '88 88 88 70 00 00 00 78 88 8F 44 44 4F FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF F8 88 88' + '88 88 88 70 00 00 00 78 88 FF FF FF F4 44 44 44' + '44 44 44 44 FF FF FF F4 44 44 44 44 4F FF 88 88' + '88 88 88 70 00 00 00 87 88 FF F4 FF FF 4F 4F FF' + '4F F4 FF 4F 44 44 44 4F FF FF FF FF FF FF 88 88' + '88 88 88 70 00 00 00 87 88 FF FF 4F 44 FF 4F 44' + 'F4 4F 44 FF F4 F4 F4 F4 4F 44 FF FF FF FF F8 88' + '88 88 88 87 00 00 00 87 88 FF FF 44 FF 44 F4 F4' + '44 44 F4 FF F4 FF 44 44 F4 FF FF FF FF FF F8 88' + '88 88 88 87 00 00 00 87 88 8F FF 4F 44 FF FF FF' + 'F4 F4 F4 FF F4 44 F4 FF 4F FF FF FF FF FF F8 88' + '88 88 88 87 00 00 00 87 88 8F FF F4 F4 FF FF FF' + 'FF F4 44 4F FF 4F FF 4F FF FF FF FF FF FF F8 88' + '88 88 88 87 00 00 00 87 88 88 FF F4 44 FF FF FF' + 'FF FF FF FF FF F4 4F FF FF FF FF FF FF FF 88 88' + '88 88 88 87 00 00 00 87 88 88 8F FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF F8 88 88' + '88 88 88 87 00 00 00 87 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 87 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 87 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 87 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 87 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 87 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 87 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 00 87 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 00 87 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 00 87 77 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 00 87 77 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 00 87 77 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 00 87 77 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 00 87 77 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 00 87 77 78 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 70 00 87 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 88 88 88 88 88 88 88 88 88' + '88 88 88 88 87 00 00 87 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 78 88 88 88 88 88 88 88 88' + '88 88 88 88 70 00 00 87 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 00 00 00 87 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 80 00 00 00 87 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 80 00 00 00 88 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 80 00 00 00 08 66 66 66 66 66 66 66 66' + '66 66 66 66 66 67 77 77 77 77 77 77 77 77 77 77' + '77 77 88 00 00 00 00 08 66 66 66 66 66 66 66 66' + '66 66 66 66 66 68 88 88 88 88 88 88 88 88 88 88' + '88 88 00 00 00 00 00 08 FE 6E 6E 6E 6E 6E 6E 6E' + '6E 6E 6E 6E 6E 80 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 8F EF EF EF EF EF EF EF' + 'EF EF EF EF EF 80 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 08 88 88 88 88 88 88 88' + '88 88 88 88 88 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 1F C0 00 00 00 00 00 00 1F C0 00' + '00 00 00 00 00 1F C0 00 00 00 00 00 00 1F C0 00' + '00 00 00 00 00 1F C0 00 00 00 00 00 00 0F C0 00' + '00 00 00 00 00 0F C0 00 00 00 00 00 00 0F C0 00' + '00 00 00 00 00 0F C0 00 00 00 00 00 00 0F C0 00' + '00 00 00 00 00 0F C0 00 00 00 00 00 00 07 C0 00' + '00 00 00 00 00 07 C0 00 00 00 00 00 00 07 C0 00' + '00 00 00 00 00 07 C0 00 00 00 00 00 00 07 C0 00' + '00 00 00 00 00 07 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 01 C0 00 00 00 00 00 00 01 C0 00' + '00 00 00 00 00 01 C0 00 00 00 00 00 00 01 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 07 C0 00' + '00 00 00 00 00 0F C0 00 00 00 00 00 00 1F C0 00' + '00 00 00 00 00 1F C0 00 00 00 00 00 00 1F E0 00' + '00 00 00 00 00 3F E0 00 00 00 00 00 00 FF E0 00' + '00 01 FF FF FF FF F0 00 00 01 FF FF FF FF F8 00' + '00 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 40 00 00 00 80 00' + '00 00 01 00 08 00 00 00 00 00 00 10 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 C0 C0 C0 00 C0 DC' + 'C0 00 F0 CA A6 00 67 67 67 00 69 69 69 00 6D 6D' + '6D 00 71 71 71 00 74 74 73 00 75 75 75 00 79 79' + '79 00 7C 7B 7B 00 7C 7C 7B 00 7D 7D 7D 00 95 66' + '4D 00 97 69 4B 00 9F 73 4B 00 9E 70 51 00 A1 75' + '59 00 A6 7D 59 00 AB 83 5E 00 90 8A 7F 00 9D 93' + '7F 00 B1 8B 6A 00 B1 8D 72 00 B5 93 74 00 B7 95' + '77 00 B8 98 7B 00 BA 9A 7E 00 BB 9C 7F 00 7C 7E' + '80 00 FF 00 FF 00 7F 80 82 00 81 81 81 00 80 81' + '84 00 83 84 86 00 85 85 85 00 85 86 88 00 87 88' + '8A 00 89 89 89 00 89 8A 8D 00 8D 8D 8D 00 95 8F' + '84 00 90 8F 8E 00 9F 95 82 00 8F 90 92 00 91 91' + '91 00 95 94 93 00 95 95 95 00 98 97 96 00 98 97' + '98 00 97 98 99 00 99 99 99 00 9C 9B 9A 00 9A 9B' + '9C 00 9D 9D 9D 00 A3 98 84 00 A6 9C 87 00 AA 9C' + '81 00 AC 9E 83 00 AB 9E 84 00 AA 9F 8A 00 BE 9F' + '84 00 A0 9F 9E 00 AF A0 82 00 AE A0 86 00 BE A1' + '87 00 B0 A3 89 00 B2 A4 89 00 B3 A6 8C 00 B5 A8' + '8D 00 AB A4 95 00 A2 A0 9F 00 B1 A7 91 00 B3 A8' + '92 00 B7 AA 90 00 B7 AB 95 00 BA AD 92 00 BC AF' + '94 00 BF A5 99 00 BB AF 98 00 BE B1 96 00 9F 9F' + 'A1 00 A1 A1 A1 00 A4 A3 A1 00 A6 A5 A3 00 A3 A3' + 'A4 00 A5 A5 A5 00 A8 A7 A6 00 AA A8 A7 00 A9 A9' + 'A9 00 AC AB AA 00 AD AC AB 00 AB AB AC 00 AD AD' + 'AD 00 B0 AE AE 00 B1 B0 AF 00 B0 AF B0 00 B1 B1' + 'B1 00 B5 B4 B3 00 B3 B4 B4 00 B5 B5 B5 00 B8 B7' + 'B6 00 BA B8 B7 00 B7 B7 B8 00 B9 B9 B9 00 BC BA' + 'B9 00 BD BC BB 00 BD BD BD 00 C1 A4 89 00 C2 A5' + '8D 00 C4 AA 91 00 C2 B4 99 00 C5 B7 9A 00 C1 B5' + '9C 00 C4 B7 9C 00 C9 B1 9B 00 C6 B9 9E 00 CA BB' + '9F 00 CD B6 A2 00 C6 BA A0 00 C9 BC A2 00 CC BE' + 'A1 00 CC BF A4 00 D0 B7 A2 00 D2 BC AA 00 CE C1' + 'A7 00 CF C2 A8 00 D0 C0 A1 00 D0 C2 A5 00 D0 C3' + 'A9 00 D1 C4 AA 00 D4 C6 AA 00 D4 C0 AD 00 D3 C6' + 'AC 00 D6 C9 AE 00 D9 CB AF 00 C8 C0 B3 00 D4 C1' + 'B1 00 D6 C4 B3 00 D5 C2 B5 00 D6 C4 B4 00 D8 C6' + 'B5 00 D8 CB B1 00 DA CC B2 00 DC CE B1 00 DB CE' + 'B4 00 DC CF B4 00 D8 C7 B8 00 D2 CA B8 00 D4 CB' + 'B9 00 D5 CD BC 00 DA C9 B9 00 DD CC BB 00 DA CB' + 'BC 00 DC CD BE 00 DD D0 B5 00 DF D2 B8 00 D8 D0' + 'BE 00 E1 D3 B7 00 E0 D3 B8 00 E3 D5 B9 00 E4 D6' + 'BA 00 E6 D8 BD 00 E9 DB BF 00 C1 C1 C1 00 C5 C4' + 'C1 00 C5 C5 C5 00 C9 C9 C9 00 CD CD CD 00 DC CD' + 'C0 00 DA D2 C1 00 DF D0 C3 00 DB D4 C2 00 DF D1' + 'C6 00 DB D5 C6 00 DC D8 C7 00 D7 D5 C9 00 D2 D1' + 'CF 00 DA D6 CB 00 DC DA CA 00 DD DB CD 00 D1 D1' + 'D1 00 D5 D4 D2 00 D4 D4 D4 00 DD DC D1 00 D9 D9' + 'D9 00 DD DB DA 00 DF DC DA 00 DE DE DC 00 E0 D1' + 'C4 00 EA DE C1 00 E2 D4 C9 00 E4 D7 CB 00 E5 D8' + 'CD 00 E5 DA D0 00 E6 DC D2 00 E7 DD D5 00 E8 DE' + 'D4 00 ED E2 C6 00 EE E4 C7 00 F0 E7 CA 00 F4 E9' + 'CD 00 E9 E0 D6 00 EC E7 D7 00 EA E1 D9 00 EC E2' + 'DA 00 EC E3 DC 00 ED E5 DE 00 F5 EE D2 00 F6 EE' + 'D4 00 F6 F3 D6 00 E3 E2 E1 00 E7 E6 E5 00 EA E6' + 'E3 00 ED E8 E4 00 EE EC EA 00 F0 E8 E1 00 F1 EA' + 'E4 00 F2 EC E7 00 F2 ED E9 00 F4 EE E9 00 F0 EE' + 'EC 00 F6 F0 E9 00 F2 F0 EE 00 F6 F2 EE 00 F8 F2' + 'EE 00 F8 F4 EE 00 F6 F3 F0 00 F6 F5 F4 00 F9 F5' + 'F1 00 FA F6 F4 00 FA F8 F5 00 FC F9 F6 00 FE FC' + 'F7 00 FB F9 F8 00 FC FA F9 00 FD FC FA 00 FE FD' + 'FC 00 00 00 00 00 F0 FB FF 00 A4 A0 A0 00 80 80' + '80 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 98' - '8F FF E0 84 6F FF D0 84 6F FF BF 98 90 FF B0 B0' - 'B0 FF C0 C0 C0 B7 DF D7 DF 66 EF E8 EF 1E 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0 84' - '6F FF F0 BF B0 FF FF DF D0 FF EF 97 80 FF DF 78' - '5F FF BF 90 80 FF B0 AF B0 FF C0 BF C0 C9 D0 D0' - 'D0 78 EF E7 EF 2D 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 F0 C4 BC FF EF 97' - '7F FF EF 9F 8F FF FF FC FF FF FF FC FF FF FF E7' - 'E0 FF F0 A7 8F FF D0 77 5F FF BF 88 7F FF B0 AF' - 'B0 FF BF B8 BF DB D0 CF D0 8A E0 E0 E0 3C 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 EF BF B0 FF FF CD' - 'A0 FF EF 97 80 FF FF FC FF FF FF FC FF FF FF F7' - 'FF FF FF F1 F0 FF F0 DF DF FF EF AD 9F FF DF 77' - '5F FF C0 84 70 FF B0 A7 A0 FF BF B7 BF ED CF C8' - 'CF 9C E0 E0 E0 4B 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 EF A7 90 FF FF D4' - 'A0 FF EF 8F 6F FF FF FC FF FF FF FC FF FF FF F7' - 'FF FF FF F1 F0 FF FF EF F0 FF F0 EF F0 FF F0 DF' - 'E0 FF EF AD 9F FF DF 77 50 FF C0 78 60 FF B0 9F' - '9F FF BF B7 BF FE CF C7 CF AE DF D8 DF 5D 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 EF 9A 80 FF FF E4' - 'B0 FF F0 9F 70 FF FF F1 F0 FF FF F7 FF FF FF F7' - 'FF FF FF F1 F0 FF F0 EF F0 FF F0 EF F0 FF F0 E7' - 'EF FF F0 E2 E0 FF F0 E2 E0 FF E0 AF A0 FF DF 77' - '5F FF CF 6F 50 FF B0 90 8F FF B0 B0 B0 FF C0 C0' - 'C0 C0 DF D7 DF 6F EF E8 EF 26 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 F0 C4 BC FF F0 B8 A0 FF FF DE' - 'A0 FF F0 AC 80 FF FF C5 A0 FF FF C0 80 FF FF DF' - 'CF FF FF F1 F0 FF F0 EF F0 FF F0 EF F0 FF F0 E7' - 'EF FF F0 E2 E0 FF F0 E2 E0 FF EF DF E0 FF EF DF' - 'E0 FF E0 B7 B0 FF DF 78 5F FF CF 67 4F FF BF 87' - '7F FF B0 AF B0 FF C0 BF C0 D2 D0 D0 D0 81 EF E7' - 'EF 35 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 F0 C7 BF FF FF CC B0 FF FF DE' - 'A0 FF F0 B0 80 FF FF BF 9F FF FF B1 5F FF FF A5' - '4F FF FF 9F 50 FF F0 C7 A0 FF F0 EF F0 FF F0 E7' - 'EF FF F0 E2 E0 FF EF DF E0 FF EF DF E0 FF EF DF' - 'E0 FF E0 D7 DF FF E0 D0 D0 FF DF BF BF FF E0 84' - '6F FF D0 5D 3F FF BF 7F 70 FF B0 A7 A0 FF BF B8' - 'BF E4 D0 D0 D0 93 F0 EF F0 2C 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 EF AD 9F FF FF E6 BF FF FF E4' - 'B0 FF FF C0 90 FF F0 B7 90 FF FF C7 7F FF FF B1' - '5F FF FF A5 4F FF F0 97 40 FF F0 90 3F FF F0 AC' - '80 FF F0 D4 CF FF EF DF E0 FF EF DF E0 FF E0 D7' - 'DF FF E0 D7 DF FF E0 D0 D0 FF DF CF D0 FF DF CF' - 'D0 FF DF B7 B0 FF D0 88 70 FF D0 5D 3F FF BF 77' - '60 FF BF AF AF FF DF D8 DF BA 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 F0 A8 90 FF FF E8 C0 FF FF E4' - 'B0 FF FF CC 9F FF F0 A7 80 FF FF CC 80 FF FF B8' - '60 FF FF B1 5F FF FF A5 4F FF F0 97 40 FF EF 80' - '30 FF EF 70 20 FF E0 90 60 FF E0 B8 AF FF E0 D7' - 'DF FF E0 D0 D0 FF E0 CF D0 FF DF CF D0 FF DF CF' - 'D0 FF DF C7 CF FF D0 C0 C0 FF D0 B7 BF FF D0 6F' - '50 FF BF 7F 70 FF CF C8 CF DD F0 F0 F0 2F 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 F4 C5 BC FF F0 C0 AF FF FF E6 BF FF FF E4' - 'B0 FF FF E4 B0 FF F0 A7 80 FF FF DE A0 FF FF C0' - '6F FF FF B8 60 FF FF B1 5F FF FF A5 4F FF F0 97' - '40 FF EF 80 30 FF EF 70 20 FF DF 67 1F FF DF 70' - '3F FF DF A0 8F FF E0 CF D0 FF DF CF D0 FF DF CF' - 'D0 FF DF C7 CF FF D0 C0 C0 FF D0 BF C0 FF D0 9F' - '90 FF C0 6F 50 FF C0 C0 C0 F9 F0 EF F0 46 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 F0 CD C0 FF FF D7 BF FF FF E8 C0 FF FF E6' - 'BF FF FF E4 B0 FF EF 8F 70 FF FF E4 B0 FF FF CC' - '80 FF FF C0 6F FF FF B8 60 FF FF B1 5F FF FF A5' - '4F FF F0 97 40 FF EF 80 30 FF E0 70 20 FF DF 67' - '1F FF D0 58 1F FF D0 57 1F FF D0 84 6F FF DF BF' - 'BF FF DF C7 CF FF D0 C0 C0 FF D0 BF C0 FF D0 B0' - 'B0 FF D0 5D 3F FF BF B7 BF FF EF E8 EF 60 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 EF B4 A0 FF FF EE D0 FF FF E8 C0 FF FF E8' - 'C0 FF FF E6 BF FF F0 A7 80 FF FF E8 C0 FF FF CC' - '80 FF FF CC 80 FF FF C0 6F FF FF B8 60 FF FF B0' - '50 FF FF A5 4F FF F0 97 40 FF EF 80 2F FF E0 70' - '20 FF DF 67 1F FF D0 58 1F FF CF 4F 10 FF CF 45' - '10 FF CF 6F 4F FF D0 A7 9F FF D0 BF C0 FF D0 BC' - 'BF FF D0 5D 3F FF B0 B0 B0 FF E0 DF E0 7C 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 F0 B4 A0 FF FF F3 DF FF FF EF CF FF FF E8' - 'C0 FF FF E8 C0 FF F0 A7 8F FF FF D7 BF FF FF D4' - '90 FF FF CC 80 FF FF CC 80 FF FF C0 6F FF FF B8' - '60 FF FF B1 5F FF FF A5 4F FF FF 97 3F FF EF 80' - '2F FF E0 70 20 FF DF 67 1F FF D0 58 1F FF CF 4F' - '10 FF CF 45 10 FF C0 38 00 FF B0 4F 30 FF C0 84' - '70 FF D0 77 5F FF BF 97 8F FF D0 D0 D0 98 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F4 C6' - 'BC FF FF C8 BF FF FF F0 D0 FF FF EE D0 FF FF EF' - 'CF FF FF E8 C0 FF F0 B8 9F FF F0 B4 A0 FF FF D7' - 'B0 FF FF DF AF FF FF CF 8F FF FF C7 7F FF FF C0' - '6F FF FF B8 60 FF FF B1 5F FF FF AF 4F FF FF A6' - '40 FF FF 94 30 FF EF 80 2F FF DF 67 1F FF D0 58' - '1F FF CF 4F 10 FF CF 45 10 FF AF 27 00 FF B0 3F' - '10 FF C0 47 1F FF C0 84 70 FF CF C8 CF B3 F0 F0' - 'F0 0B 00 00 00 00 00 00 00 00 00 00 00 00 F0 CF' - 'CF FF FF DF CF FF FF F3 DF FF FF F0 D0 FF FF EE' - 'D0 FF FF EF CF FF FF E7 C0 FF FF C5 A0 FF F0 A7' - '80 FF EF 9A 80 FF FF BF A0 FF FF D4 A0 FF FF D4' - '90 FF FF C0 6F FF FF B8 60 FF FF B1 5F FF FF AF' - '4F FF FF A6 40 FF FF A6 40 FF FF 94 30 FF F0 78' - '20 FF DF 5F 1F FF CF 4F 10 FF AF 27 00 FF C0 4F' - '1F FF C0 47 1F FF C0 6F 5F FF C0 C0 C0 CF F0 EF' - 'F0 23 00 00 00 00 00 00 00 00 00 00 00 00 F0 BF' - 'B0 FF FF F6 E0 FF FF F3 DF FF FF F3 DF FF FF F0' - 'D0 FF FF EE D0 FF FF EF CF FF FF E8 C0 FF FF E8' - 'C0 FF FF D8 B0 FF FF C0 90 FF EF 8F 6F FF EF 9A' - '80 FF FF C5 A0 FF FF CF 8F FF FF B8 60 FF FF B0' - '50 FF FF AF 4F FF FF A6 40 FF FF A6 40 FF FF 9F' - '30 FF FF 8F 2F FF DF 5F 1F FF B0 37 10 FF CF 57' - '1F FF C0 4F 1F FF CF 60 40 FF BF B7 BF EB EF E8' - 'EF 3B 00 00 00 00 00 00 00 00 00 00 00 00 F0 BA' - 'AF FF FF FA F0 FF FF F6 E0 FF FF F3 DF FF FF F3' - 'DF FF FF F0 D0 FF FF EE D0 FF FF EF CF FF FF E8' - 'C0 FF FF E8 C0 FF FF E6 BF FF FF E4 B0 FF FF CD' - 'A0 FF EF 8F 6F FF FF CC B0 FF FF CC 80 FF FF B8' - '60 FF FF B0 50 FF FF AF 4F FF FF A6 40 FF FF A6' - '40 FF FF 9F 30 FF F0 78 2F FF DF 60 2F FF CF 60' - '20 FF CF 57 1F FF D0 54 2F FF B0 B0 B0 FF E0 DF' - 'E0 52 00 00 00 00 00 00 00 00 F4 C6 BC FF FF D0' - 'C0 FF FF FA F0 FF FF FA F0 FF FF F6 E0 FF FF F3' - 'DF FF FF F3 DF FF FF F0 D0 FF FF EE D0 FF FF EF' - 'CF FF FF E8 C0 FF FF E8 C0 FF FF E6 BF FF FF E4' - 'B0 FF FF CD A0 FF E0 8F 70 FF FF D8 B0 FF FF CC' - '80 FF FF B8 60 FF FF B0 50 FF FF AF 4F FF FF A6' - '40 FF FF A6 40 FF F0 6F 2F FF FF 9F 4F FF F0 97' - '40 FF CF 60 20 FF D0 54 2F FF BF 97 8F FF D0 D0' - 'D0 6E 00 00 00 00 00 00 00 00 F0 D4 CF FF FF E4' - 'DF FF FF FA F0 FF FF FA F0 FF FF FA F0 FF FF F6' - 'E0 FF FF F3 DF FF FF F3 DF FF FF F0 D0 FF FF EF' - 'CF FF FF EF CF FF FF E8 C0 FF FF E8 C0 FF FF E6' - 'BF FF FF E4 B0 FF FF C0 90 FF E0 82 60 FF EF 9A' - '80 FF FF B7 8F FF FF C0 8F FF FF C0 70 FF FF B0' - '50 FF FF A6 40 FF E0 60 2F FF FF B8 60 FF FF B8' - '60 FF EF 87 3F FF D0 5F 2F FF C0 84 70 FF CF C7' - 'CF 8A F0 F0 F0 16 00 00 00 00 F0 C7 BF FF FF F1' - 'F0 FF FF FC FF FF FF FA F0 FF FF FA F0 FF FF FA' - 'F0 FF FF F6 E0 FF FF F3 DF FF FF F3 DF FF FF F0' - 'D0 FF FF EF CF FF FF EF CF FF FF E8 C0 FF FF E8' - 'C0 FF FF E6 BF FF FF E4 B0 FF FF E4 B0 FF FF CC' - '9F FF F0 AC 7F FF E0 82 60 FF E0 82 60 FF F0 9F' - '70 FF FF B0 7F FF EF 88 60 FF FF CC 80 FF FF C0' - '6F FF FF B1 5F FF DF 70 30 FF CF 6F 50 FF C0 BF' - 'C0 A5 00 00 00 00 00 00 00 00 F0 C0 B0 FF FF FC' - 'FF FF FF FC FF FF FF FC FF FF FF FA F0 FF FF FA' - 'F0 FF FF FA F0 FF FF F6 E0 FF FF F3 DF FF FF F3' - 'DF FF FF F0 D0 FF FF EF CF FF FF EF CF FF FF E8' - 'C0 FF FF E8 BF FF FF E6 BF FF FF E4 B0 FF FF E4' - 'B0 FF FF E4 B0 FF FF DE A0 FF FF D4 A0 FF F0 B0' - '7F FF EF 88 60 FF DF 6F 4F FF EF 88 60 FF F0 AC' - '80 FF FF C0 80 FF FF B7 6F FF D0 60 40 FF CF C7' - 'CF FF 00 00 00 00 00 00 00 00 FA D3 D0 FF F0 C7' - 'BF FF F0 BF B0 FF FF D4 CF FF FF E4 DF FF FF FA' - 'F0 FF FF FA F0 FF FF FA F0 FF FF F3 DF FF F0 AF' - '9F FF EF 9F 8F FF F0 AF 9F FF FF CC B0 FF FF DF' - 'BF FF FF E8 C0 FF FF E8 BF FF FF E6 BF FF FF E4' - 'B0 FF FF E4 B0 FF FF E4 B0 FF FF DE A0 FF FF DE' - 'A0 FF F0 AC 7F FF CF 97 8F FF E0 C7 C0 FF EF AD' - '9F FF E0 78 60 FF E0 70 4F FF DF 58 30 FF 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 F0 BA AF 4D F2 C2 B8 FF F0 C1 B8 FF F0 B4' - 'A0 FF F0 BA AF FF FF D0 C0 FF F0 B4 A0 FF F0 CD' - 'C0 FF 00 00 00 00 00 00 00 00 F0 CD C0 FF EF B4' - 'A0 FF EF 9A 80 FF F0 B4 A0 FF FF C5 A0 FF FF E6' - 'BF FF FF E4 B0 FF FF E4 B0 FF FF E4 B0 FF FF DE' - 'A0 FF F0 9F 70 FF C0 A7 A0 FF 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 F0 CD C0 FF 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 F0 BA AF B7 F0 BA AF 79 EF B9 AE B9 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 F0 BA AF FF EF 9A' - '80 FF EF 9A 80 FF F0 B7 90 FF FF CD A0 FF FF E4' - 'B0 FF E0 82 60 FF D0 CF D0 FF 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 FF D4 CF FF EF A7 90 FF E0 8F' - '70 FF E0 8F 70 FF 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FC 0F' - 'FF FF FC 03 FF FF FC 00 FF FF F8 00 3F FF F8 00' - '0F FF F8 00 03 FF F8 00 00 7F F0 00 00 1F F0 00' - '00 07 F0 00 00 07 F0 00 00 03 E0 00 00 03 E0 00' - '00 03 E0 00 00 03 E0 00 00 03 C0 00 00 01 C0 00' - '00 01 C0 00 00 01 C0 00 00 01 80 00 00 01 80 00' - '00 00 80 00 00 01 80 00 00 01 80 00 00 03 E0 18' - '00 7B FE 3F 80 7F FF FF F0 FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF 28 00 00 00 10 00 00 00 20 00' - '00 00 01 00 20 00 00 00 00 00 00 04 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 D7 C8 BE 01 DD C6 BD 57 CA C1' - 'BF 65 D8 D1 D0 27 DD D7 D1 03 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 D7 B8 AC 03 DF A0 92 D1 E0 AA' - '9B FD CA A2 98 E7 CC C3 C4 98 D6 D1 CF 38 DC D4' - 'D0 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 E4 C3 B7 2A F2 AE 94 F8 FC E7' - 'E4 FF FA DF DB FF DD AC A0 FE C9 9F 97 ED C6 BB' - 'B8 AC D2 CC CA 49 D7 D1 CB 08 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 E6 B1 9D 31 F9 C4 99 FD FC E5' - 'E0 FF FF F6 FB FF F7 ED ED FF EE D6 D4 FF DF AA' - '98 FF C8 96 8C F4 C5 B0 B0 BF CD C7 C5 59 DD D5' - 'D3 15 DE D7 D5 01 00 00 00 00 00 00 00 00 00 00' - '00 00 DB C8 BE 02 EA C5 B8 BF FA CF 9C FF FC C1' - '99 FF FF C8 9B FF F3 DD CD FF F0 E6 EA FF EF E0' - 'DF FF EA D0 CF FF DA A3 98 FF CC 8F 83 F8 C4 A8' - 'A2 D0 D3 CB CC 78 DC D8 D2 19 00 00 00 00 00 00' - '00 00 DB C0 B3 03 ED BC A8 D1 FF DD AC FF F7 B8' - '8A FF FF B4 62 FF F7 A2 52 FF EF A1 6B FF EA BC' - 'A8 FF E3 D4 D8 FF E0 D0 D1 FF DF C4 C3 FF D1 9F' - '94 FF C6 85 72 FB D6 CA CB A6 DC D6 D0 05 00 00' - '00 00 E6 C8 BD 2A F6 CD B7 F8 FF E4 B6 FF F5 B6' - '8A FF FF C7 7A FF FF B2 5D FF F6 99 44 FF EB 7B' - '2F FF DE 7A 45 FF D9 9C 85 FF DE C1 C1 FF D3 C0' - 'C2 FF CE 9D 91 FF CA B6 B3 DA E5 E0 DF 10 00 00' - '00 00 E6 BE AD 31 FC E3 C8 FD FF E8 C0 FF F6 BB' - '99 FF FF D1 8F FF FF C3 73 FF FF B2 59 FF F7 99' - '44 FF E7 77 27 FF D7 5F 1F FF D0 59 28 FF C9 7B' - '60 FF C9 94 88 FF C2 A4 9D E7 D7 D3 D1 19 DB C9' - 'C0 03 ED CB C2 BF FF EB D2 FF FF ED CC FF F8 CE' - 'AE FF F9 C2 9B FF FE C7 92 FF FF C5 7A FF FF B3' - '5E FF FD A4 44 FF F3 8B 32 FF DE 65 1E FF C9 45' - '0E FF B8 47 1D FF C2 81 70 F1 E1 DE DC 30 DC C6' - 'BA 04 EE CB BE D1 FF F4 E0 FF FF F1 D6 FF FF EC' - 'CC FF FE E0 BA FF FD CD A3 FF F7 B5 8E FF FD C3' - '8C FF FF B5 59 FF FF AA 46 FF FD 9C 35 FF E2 69' - '24 FF CB 55 1F FF C9 6B 4E FB DC D6 D7 56 E6 CB' - 'C2 3A FA DA D1 F8 FF F9 ED FF FF F4 E0 FF FF F1' - 'D6 FF FF ED CC FF FF E8 C1 FF FF DF B2 FF F3 B3' - '8B FF F8 B5 7E FF FF B5 68 FF FF AA 49 FF F0 7F' - '39 FF F0 96 46 FF CF 68 3F FE D0 C4 C2 7B E7 C8' - 'BD 40 FC EC EB FD FF FA F6 FF FF F9 ED FF FF F4' - 'E0 FF FF F0 D5 FF FF ED CB FF FF E8 C1 FF FE E0' - 'B3 FF FC D0 A1 FF F1 B2 83 FF F2 A7 73 FF ED 8A' - '5E FF F8 B3 71 FF E8 8D 51 FF CB B4 B0 AB E6 CD' - 'C5 25 EB C9 C1 98 F7 D0 C8 EE F8 DC D0 FC FC E3' - 'D4 FD F0 C5 B4 E8 EC C2 B1 A7 F7 CD B1 FA F9 D1' - 'AF FC FF E1 B3 FF FF E2 AD FF FB D0 97 FF D4 A3' - '92 EB DE B3 A3 94 E2 9B 80 A5 DD AC 9A 3F 00 00' - '00 00 D9 C5 BA 02 DE C6 BB 17 E0 C1 B6 2A EA BF' - 'B3 5D DF C4 B8 25 D9 CA BF 05 DD C1 B2 1A E1 C0' - 'B3 30 E8 B9 A5 93 F4 C3 A7 E8 EF B7 8E FA D4 B5' - 'A8 8B D0 C9 C1 01 D9 CA BF 05 D9 CA BF 05 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 DF CA BF 15 D8 B5 A2 1A D6 BA' - 'A9 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C1 FF' - '00 00 C0 7F 00 00 C0 1F 00 00 C0 03 00 00 80 01' - '00 00 80 00 00 00 80 00 00 00 80 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 80 00 00 00 FF C7 00 00 FF FF 00 00 28 00' - '00 00 10 00 00 00 20 00 00 00 01 00 08 00 00 00' - '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 7F 7F 7F 00 00 99' - '99 00 66 CC CC 00 99 CC FF 00 99 FF FF 00 F8 F8' - 'F8 00 00 00 00 00 E2 69 24 00 E7 77 27 00 EB 7B' - '2F 00 F0 7F 39 00 C7 6B 4E 00 DE 7A 45 00 C9 7B' - '60 00 F3 8B 32 00 FD 9C 35 00 BE 80 70 00 BF 91' - '7F 00 F0 96 46 00 F6 99 44 00 F7 99 44 00 E8 8D' - '51 00 ED 8A 5E 00 FD A4 44 00 FF AA 46 00 FF AA' - '49 00 F7 A2 52 00 FF B2 59 00 FF B5 59 00 FF B2' - '5D 00 FF B3 5E 00 C4 84 72 00 EF A1 6B 00 FF B4' - '62 00 FF B5 68 00 F2 A7 73 00 F8 B3 71 00 F8 B5' - '7E 00 FF C3 73 00 FF C5 7A 00 FF C7 7A 00 96 8A' - '85 00 96 90 8E 00 99 91 8E 00 92 91 90 00 96 95' - '94 00 9F 97 94 00 9A 98 97 00 3C 17 95 00 00 00' - '38 00 A8 44 F9 77 21 00 00 00 B8 0C 38 00 00 00' - '38 00 30 7E 0D 01 14 17 95 00 B1 A2 9F 00 5C 19' - '95 00 00 40 0D 01 68 19 95 00 77 82 F5 77 98 7F' - 'F5 77 3A 8A F5 77 00 04 00 00 C4 A2 AF 00 10 00' - '00 00 10 00 00 00 C9 94 88 00 CD 9A 8E 00 D9 9C' - '85 00 C3 9C 95 00 C2 9E 95 00 CE 9D 91 00 D1 9F' - '94 00 CD A0 90 00 C6 AA 9F 00 DA A3 98 00 DF AA' - '98 00 DF A9 9A 00 DC AB 9F 00 EC B5 8D 00 F1 B2' - '83 00 F3 B3 8B 00 F5 B6 8A 00 F7 B5 8E 00 F7 B8' - '8A 00 EE AC 93 00 F6 BB 99 00 CF B3 A9 00 D9 B0' - 'A0 00 D1 B7 B1 00 D9 BD B2 00 E9 BC A3 00 E5 BE' - 'AF 00 EA BC A8 00 FD C3 8C 00 FF D1 8F 00 FE C7' - '92 00 F8 C3 98 00 F9 C2 9B 00 FC C1 99 00 FF C8' - '9B 00 FA CF 9C 00 FB D0 97 00 FD CD A3 00 F8 CE' - 'AE 00 FC D0 A1 00 F7 D0 AE 00 FF DD AC 00 F4 CB' - 'B0 00 F2 CA B5 00 FF DF B2 00 FF E2 AD 00 FE E0' - 'B3 00 FF E1 B3 00 FF E4 B6 00 FE E0 BA 00 D3 C0' - 'C2 00 A8 01 38 00 10 44 0D 01 40 00 00 00 00 00' - '00 00 A8 01 38 00 38 44 0D 01 38 44 0D 01 E0 D0' - 'D1 00 EE D6 D4 00 E3 D4 D8 00 FA DF DB 00 FB E2' - 'C7 00 FF E8 C0 00 FF E8 C1 00 FF ED CB 00 FF EC' - 'CC 00 08 44 0D 01 08 44 0D 01 FB E2 D3 00 FF EB' - 'D2 00 FF F0 D5 00 FF F1 D6 00 FC E5 E0 00 FC E7' - 'E4 00 F0 E6 EA 00 00 40 0D 01 A4 18 95 00 E5 3A' - 'F8 77 06 00 00 00 08 44 0D 01 00 00 38 00 00 40' - '0D 01 00 00 00 00 78 19 95 00 CA 8C F5 77 78 01' - '38 00 BE 8E F5 77 08 06 38 00 37 90 F5 77 08 40' - '0D 01 08 40 0D 01 40 00 00 00 40 00 00 00 D0 BB' - 'BB 00 E0 B2 A5 00 EE BD A2 00 E3 B4 AA 00 E4 BB' - 'AF 00 F1 B6 A7 00 F6 BE A2 00 F0 BA AB 00 E5 BE' - 'B3 00 EC BC B0 00 00 40 0D 01 00 40 0D 01 F4 C4' - '97 00 78 01 38 00 08 40 0D 01 E0 07 0D 01 E8 07' - '0D 01 78 01 38 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 F5 C8 AA 00 00 40 0D 01 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 E9 C2' - 'B7 00 08 04 00 00 87 00 00 00 08 04 00 00 08 01' - '00 00 00 00 00 00 00 00 00 00 00 00 01 01 00 00' - '38 00 BC 18 95 00 C0 18 95 00 B0 19 95 00 F0 88' - 'FA 77 88 1C F5 77 FF FF FF FF 37 90 F5 77 81 E9' - '49 00 00 00 38 00 00 00 00 00 08 40 0D 01 08 40' - '0D 01 A4 1A 95 00 40 00 00 00 40 00 00 00 B8 10' - 'E9 77 FF FF FF FF C9 F1 E7 77 16 EA 4B 00 F8 00' - '00 00 B4 1A 95 00 DC E3 49 00 E0 A3 4E 00 FF FF' - 'FF FF 10 00 00 00 10 DA 4B 00 08 40 0D 01 71 CC' - '43 00 08 40 0D 01 30 7E 0D 01 A4 1A 95 00 C4 A2' - 'AF 00 C4 F5 AF 00 02 00 00 00 30 7E 0D 01 0F 02' - '00 00 48 40 0D 01 10 00 00 00 00 00 00 00 20 00' - '00 00 08 40 0D 01 40 00 00 00 10 00 00 00 00 01' - '00 00 00 04 00 00 00 00 00 00 00 00 00 00 10 00' - '00 00 10 00 00 00 28 00 00 00 10 00 00 00 20 00' - '00 00 01 00 20 00 00 00 00 00 00 04 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 07 07 07 07 07 07 07' - '07 07 07 07 07 07 00 00 02 02 02 02 02 02 02 02' - '02 02 02 02 07 07 00 00 02 06 04 05 04 05 04 04' - '04 04 03 02 07 07 00 02 06 05 05 04 05 04 05 04' - '04 04 03 07 02 07 00 02 06 05 05 05 05 05 04 05' - '04 04 02 07 02 07 02 06 05 05 05 05 04 05 05 04' - '05 03 07 03 03 07 02 06 05 05 05 05 05 05 04 05' - '04 03 07 03 03 07 02 02 02 02 02 02 02 02 02 02' - '02 02 03 05 03 07 00 02 06 05 05 05 05 05 05 05' - '05 05 05 05 03 07 00 02 06 05 05 05 05 05 05 05' - '06 06 06 06 03 07 00 02 06 05 05 05 05 05 06 02' - '02 02 02 02 02 00 00 00 02 06 06 06 06 06 02 00' - '00 00 00 00 00 00 00 00 00 02 02 02 02 02 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 FF FF 00 00 FF FF 00 00 E0 00' - '00 00 C0 00 00 00 C0 00 00 00 80 00 00 00 80 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00' - '00 00 80 00 00 00 80 01 00 00 C0 7F 00 00 E0 FF' - '00 00 FF FF 00 00' -} */ + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '07 68 3A 31 2F 31 2F 2F 31 2F 2F 31 2F 2F 31 2F' + '2F 31 2F 2F 31 2F 2F 31 2F 2F 31 2F 2F 31 2F 2F' + '31 2F 2F 31 2F 2F 31 2F 2F 31 2F 2F 31 2F 2F 31' + '2F 2F 31 2F 2F 31 33 3A 00 00 00 00 00 00 00 00' + '07 BE AB 71 6F 6B 6E 6A 6A 6A 6A 67 67 64 64 67' + '63 63 63 63 63 5E 60 5D 5D 5E 5C 5C 5C 58 58 59' + '58 58 3D 3D 3C 3C 3C 38 36 36 36 36 33 34 33 34' + '34 34 34 33 31 33 36 13 00 00 00 00 00 00 00 00' + '6B BE AD 07 72 6F 72 72 71 70 70 6D 6C 6C 6C 6B' + '69 68 66 66 65 65 61 64 60 5F 5F 5D 5B 5B 5A 5A' + '45 4E 45 45 3B 3A 3A 37 35 35 31 2D 2D 2A 2B 2A' + '2A 2A 2A 2A 27 2A 34 10 3B 00 00 00 00 00 00 00' + '60 72 6E 6A 6F C1 E4 EA E6 E6 E6 E6 E6 E6 E6 E6' + 'E6 E6 E6 E6 E6 E4 E6 E4 E6 E6 E4 E4 E4 E4 E4 E4' + 'E4 E4 E4 DE E4 DE E4 E4 E2 DE DB AF 37 F8 F8 13' + '13 13 11 11 10 11 2C 10 36 00 00 00 00 00 00 00' + '3D 68 6F 6A C2 E8 8B 94 94 9F C4 D1 E5 EF EF F0' + 'EE ED EC EC EC EC EC EC E9 EC E9 E9 EC E9 E9 E9' + 'EC E8 EC EC EC EC EC EC EE EF F0 F0 DC 3A 27 13' + '13 13 11 11 10 10 2A 13 2F 00 00 00 00 00 00 00' + '37 59 6F AC EB F2 EC E7 E3 E3 E2 D1 B0 92 8B 90' + '91 9A 9E 9F B0 C4 C6 C7 CA CC D1 D4 D5 D5 D4 D5' + 'D3 CC C9 C7 C4 9E 9E 9E 91 91 9A D6 F0 DA 2F 13' + '13 F8 12 12 11 10 F8 2B 27 00 00 00 00 00 00 00' + '37 36 6F AF F1 EE F4 21 EB F4 E1 E0 D6 CB D1 F3' + 'F4 EA D3 EE D4 83 DD E3 D6 D1 CA CC D3 D3 D1 D3' + 'D3 D4 D6 E1 EC EE EE EF F4 F4 F4 F3 F2 F2 67 13' + '13 13 24 13 12 10 12 2F 12 00 00 00 00 00 00 00' + '38 2A 72 AD E9 E8 EC DF 19 82 7A 14 1D 18 15 17' + '22 16 73 7A D3 F3 A1 75 E8 EC E0 1F 7A 83 8B 82' + '75 1F 1A 75 74 8B C4 E1 E8 E8 E7 E8 E5 ED B8 27' + '27 13 F8 11 12 10 11 2D 10 59 00 00 00 00 00 00' + '34 0F AB AB EB F4 F3 F4 55 7D EC F2 92 93 EA C7' + 'B0 A0 48 E3 A0 83 90 B0 EF F3 ED 83 C7 90 C6 C9' + 'B4 75 A1 75 F2 ED E0 EC F3 F2 F3 F2 F3 F4 DA 2A' + '27 F8 12 13 12 11 10 2E 12 3B 00 00 00 00 00 00' + '2F 0D AB 70 DE EC EC ED E3 7A 7A E1 F3 F0 EF EF' + 'F2 F2 CA 90 E1 83 74 EC ED ED D6 94 23 B2 CA 75' + 'E3 A0 7A F0 ED ED EC ED E8 ED E8 ED E8 ED DC 2F' + '27 27 F8 13 13 12 13 2B 0F 35 00 00 00 00 00 00' + '2F 0C 72 6E BD EC EC E9 74 9E 1E 9E C7 EC E9 EA' + 'E8 EC EF E0 22 8B 20 75 8B EC ED E5 44 83 DF D3' + '83 E7 19 ED EC E9 E8 E9 EA EA EA E8 EA EE C1 2D' + '2A 2A 27 F8 F8 12 10 2A 27 2D 00 00 00 00 00 00' + '2D 0B 71 6B 6F DB F3 EF F0 D6 92 B2 C7 EF EC ED' + 'EC ED EC EF EF E3 DF EF D3 ED ED EF E1 C9 C6 C7' + 'E3 E5 E0 EF EC ED EC EC ED EC EC ED EF EE 6B 2D' + '2A 2A 27 27 F8 13 12 27 2A 27 00 00 00 00 00 00' + '2D 0A 6B 72 6F 72 C3 EB F2 F3 F4 F2 F2 F3 F3 F2' + 'F3 F3 F3 F4 F2 F4 F4 F4 F4 F4 F3 F3 F4 FF F4 F4' + 'F4 F4 F4 F3 F4 F3 F3 F3 F3 F4 F0 F2 E6 AB 31 2C' + '2D 2A 2A 2A 2A 13 F8 F8 2D 10 00 00 00 00 00 00' + '2D 0A 64 AB 71 6F 6F AB AD AD AD AD AD AB AC AB' + '07 07 72 07 72 72 71 71 6F 6F 6F 6E 6E 6D 6E 6A' + '6B 6B 69 68 68 67 67 65 65 64 67 64 3D 2F 2D 2E' + '2D 2D 2A 2A 27 27 F8 F8 31 10 5A 00 00 00 00 00' + '2D 0B 5E AB 72 71 6F 6F 6E 6A 6A 67 67 67 63 63' + '63 60 60 5E 5D 5E 5D 5C 5C 5C 45 58 58 45 45 3D' + '3C 3C 3A 37 37 36 36 33 35 33 34 34 31 2F 2F 2F' + '2D 2B 2A 2A 2A 27 27 F8 31 0F 3B 00 00 00 00 00' + '2F 0B 59 AE AB 07 72 72 6F 6E 6B 6A 68 67 64 63' + '60 60 60 5D 5E 5D 5C 5C 5C 5A 59 58 59 3D 45 3D' + '3C 3A 3A 3A 39 36 36 36 35 35 33 34 34 34 2F 2F' + '2F 2D 2D 2D 27 27 29 F8 31 10 34 00 00 00 00 00' + '31 0C 39 AF AD AB 07 07 71 6F 6F 6B 6B 68 67 64' + '63 63 60 5E 60 5D 5C 5D 5C 5A 59 5C 45 58 3C 3D' + '3D 3B 3A 3A 39 37 36 35 36 33 35 33 34 31 31 33' + '2D 2F 2D 2D 2D 2D 2A 27 31 2B 2D 00 00 00 00 00' + '35 0D 2F BC AD AD 07 07 72 6F 6F 6B 6A 6A 67 67' + '64 64 61 60 5D 5D 5D 5D 5D 5C 5C 59 59 45 3D 3D' + '3D 3C 3B 3C 37 36 39 36 34 34 35 33 34 34 31 31' + '2F 2F 2E 2D 2D 2A 2A 2A 2D 35 F8 00 00 00 00 00' + '36 0F 2C BD AE AD AD 07 72 71 6E 6E 6B 6A 68 68' + '65 64 60 60 5E 5D 5D 5C 5D 5C 59 59 59 58 3D 3D' + '3D 3D 3A 3A 3A 37 39 37 36 36 34 35 33 34 33 2F' + '31 2F 2F 2D 2D 2B 2A 2A 2D 34 12 00 00 00 00 00' + '37 10 27 BE AE AE AD AB AB 72 72 6E 6E 6B 6A 67' + '67 62 60 60 5E 5D 5D 5D 5D 5C 45 59 59 58 45 3D' + '3D 3D 3D 3A 39 3A 3A 37 39 36 36 34 35 33 34 34' + '31 33 2F 2D 2D 2D 2D 2A 2A 37 13 59 00 00 00 00' + '3A 24 11 BE AF AE AE AD AC 07 72 71 6F 6B 6A 6A' + '67 65 63 60 60 60 5D 5D 5D 5C 59 5C 59 45 58 3D' + '3D 3D 45 3C 3C 3B 3A 3A 39 37 39 35 36 34 35 33' + '34 34 2F 2F 2F 2D 2D 2D 2D 3A 10 3B 00 00 00 00' + '3B 27 11 AF AF AE AE AD AD AB 07 6F 71 6E 6B 6A' + '68 68 62 63 60 60 5E 5E 5D 5D 59 5C 59 59 58 45' + '3D 3D 3D 3D 3D 3C 3A 3A 3A 3A 37 39 35 36 35 34' + '34 34 34 34 2F 2F 2E 2D 2A 37 27 34 00 00 00 00' + '58 2A 10 AF AF AF AE AE AD AD 07 72 72 6F 6E 6B' + '6A 67 65 63 60 60 5E 5D 5D 5D 5C 59 59 5C 59 3D' + '45 3D 58 3D 3D 3B 3D 3C 3B 39 3A 37 39 36 35 36' + '36 33 34 31 2F 2F 2E 2D 2D 34 2D 2D 00 00 00 00' + '5C 2D 10 AD BC AF AE AE AE AC AB 07 72 71 6E 6E' + '6A 68 66 64 60 60 60 5C 5D 5D 5C 59 59 59 59 59' + '3D 58 45 3D 3C 3D 3D 3B 3C 3B 39 3A 37 39 36 36' + '35 33 34 34 33 31 2D 2D 2D 2F 2D F8 00 00 00 00' + '5E 2E 12 72 BE AF AF AF AE AD AD AB 07 72 6F 6F' + '6A 69 67 67 63 61 5E 5D 5D 5C 5C 59 59 59 59 58' + '3D 45 3D 3D 3C 3D 3D 3D 3D 3B 3A 39 37 39 39 37' + '35 34 35 33 31 2F 2F 2D 2D 2E 35 12 60 00 00 00' + '60 2E 27 6B C0 B8 AF AF AE AD AD AD 07 07 72 6E' + '6E 6A 66 67 64 63 60 5E 5D 5C 5C 59 59 59 59 58' + '45 3D 3D 3D 3D 3C 3D 3D 3B 3C 3B 3C 3A 37 39 36' + '36 36 34 34 31 2F 2F 2D 2C 2D 36 0F 59 00 00 00' + '60 31 2D 3A BE BE BC B8 AE AF AE AD AB AB 07 6F' + '6F 6E 68 6A 65 63 62 60 5D 5D 5D 5C 59 59 59 3D' + '59 3D 45 3D 3D 3D 3C 3D 3D 3C 3B 3C 3A 37 3A 36' + '36 36 34 34 31 2F 2F 2C 2D 2A 37 0F 3C 00 00 00' + '60 34 31 2C 5A AE BE AF AF AF AE AE AD AC 07 72' + '71 6E 6B 68 67 64 63 60 5D 5D 5C 5C 5A 5D 5C 59' + '3D 45 3D 58 3C 3D 3B 3C 3D 3A 3B 3A 3A 39 37 36' + '36 34 34 34 2F 2F 2D 2B 2A 2A 34 2D 59 00 00 00' + '63 35 34 34 2F 2C 2F 2D 2D 2A 2A 2A 2A 27 27 26' + '13 12 12 13 12 12 10 12 12 12 12 10 12 12 27 39' + '59 58 3D 3D 45 3C 3D 3B 3C 3B 3A 3A 39 39 37 36' + '34 34 31 2F 2F 2D 2D 2B 27 2B 36 3D 00 00 00 00' + '62 39 37 39 35 33 34 2F 2F 2F 2D 2D 2A 27 27 F8' + '13 13 11 10 10 0F 10 0F 10 0E 0E 0E 0E 0E 0D 0E' + '36 59 58 59 3D 45 58 3D 45 3D 3D 3C 3C 3A 37 39' + '36 35 34 31 2F 2E 2D 34 36 3B 36 00 00 00 00 00' + '67 3D 3B 3A 39 37 36 36 33 31 2F 2F 2C 2D 2A 2A' + 'F8 27 27 13 13 13 12 12 10 10 10 10 10 0E 0F 0F' + '0F 10 0F 10 0F 10 0F 0F 0F 0F 0E 0F 0E 0D 0D 0D' + '0D 0C 0D 0B 0C 0B 0D 0D 31 5D 00 00 00 00 00 00' + '6F 59 59 45 3D 3C 3B 39 36 36 34 34 31 2F 2D 2D' + '2B 2A 27 27 27 F8 F8 13 13 13 12 10 12 12 12 10' + '10 10 10 10 10 0F 10 0F 0F 0F 0F 0E 0E 0D 0D 0C' + '0C 0C 0C 0B 0B 0A 0D 0E 58 00 00 00 00 00 00 00' + '07 63 5C 59 59 45 3D 3C 3A 3A 36 36 33 31 31 2E' + '2D 2D 2D 2A 27 27 27 27 F8 27 13 13 13 13 13 13' + '12 13 12 12 10 12 10 12 10 10 10 0F 0F 0F 0E 0E' + '0D 0D 0C 0C 0B 0B 0F 0F 59 00 00 00 00 00 00 00' + '72 AC 5F 5D 5C 59 58 3D 3C 39 39 33 33 2F 2E 2C' + '2B 29 28 28 26 24 13 24 24 24 26 F8 F8 F8 F8 F8' + 'F8 F8 24 F8 13 13 13 12 12 12 10 10 10 0F 0F 0F' + '0E 0D 0D 0C 0B 0C 0F 24 60 00 00 00 00 00 00 00' + 'AB D7 A8 99 A2 97 96 95 89 84 7F 7E 7E 7E 78 56' + '52 50 50 4F 43 3F 3E 32 1C 1C 1B 26 2B 2C 2D 2A' + '2A 2D 2A 2A 2A 27 2A 27 F8 24 F8 12 12 10 10 0F' + '0F 0E 0D 0D 0D 0D 27 39 00 00 00 00 00 00 00 00' + 'AC D9 A9 A6 A3 98 98 96 8E 8D 8C 89 85 81 80 7E' + '79 76 54 54 53 4C 4A 47 41 40 46 30 2E 3D 3D 3D' + '3D 3D 3D 3D 3D 3D 3B 3C 37 39 36 35 33 2F 2F 2D' + '2D 2A 26 F8 F8 27 34 00 00 00 00 00 00 00 00 00' + '00 B9 D0 A8 A3 A2 98 96 95 8D 89 88 85 81 7F 7E' + '7B 78 57 54 53 51 4B 49 47 42 4A 53 3B 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 D2 D8 CF CE CD C5 C5 AA A9 A8 A5 A2 97 8E' + '8D 8A 87 87 80 7C 7B 77 77 76 86 4D 66 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 B7 BF BF BF BF BB BB BA BA BA B6 B6 B5' + 'B1 B3 B1 A4 9D 9D 9D 9C 9B 9B 8F 6C 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 3F C0 00 00 00 00 00 00 1F C0 00' + '00 00 00 00 00 1F C0 00 00 00 00 00 00 1F C0 00' + '00 00 00 00 00 1F C0 00 00 00 00 00 00 1F C0 00' + '00 00 00 00 00 0F C0 00 00 00 00 00 00 0F C0 00' + '00 00 00 00 00 0F C0 00 00 00 00 00 00 0F C0 00' + '00 00 00 00 00 0F C0 00 00 00 00 00 00 0F C0 00' + '00 00 00 00 00 07 C0 00 00 00 00 00 00 07 C0 00' + '00 00 00 00 00 07 C0 00 00 00 00 00 00 07 C0 00' + '00 00 00 00 00 07 C0 00 00 00 00 00 00 07 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 01 C0 00' + '00 00 00 00 00 01 C0 00 00 00 00 00 00 01 C0 00' + '00 00 00 00 00 01 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 07 C0 00 00 00 00 00 00 0F C0 00' + '00 00 00 00 00 1F C0 00 00 00 00 00 00 1F C0 00' + '00 00 00 00 00 1F C0 00 00 00 00 00 00 3F C0 00' + '00 00 00 00 00 7F E0 00 00 01 FF FF FF FF F0 00' + '00 01 FF FF FF FF F8 00 00 03 FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF' +}*/ + + /* BINRES floppy.ico */ 5 ICON floppy.ico @@ -5840,162 +10057,60 @@ END /* BINRES drive.ico */ 8 ICON drive.ico /* { - '00 00 01 00 08 00 20 20 00 00 01 00 08 00 A8 08' - '00 00 86 00 00 00 10 10 00 00 01 00 08 00 68 05' - '00 00 2E 09 00 00 20 20 00 00 01 00 04 00 E8 02' - '00 00 96 0E 00 00 10 10 00 00 01 00 04 00 28 01' - '00 00 7E 11 00 00 30 30 00 00 01 00 08 00 A8 0E' - '00 00 A6 12 00 00 30 30 00 00 01 00 20 00 A8 25' - '00 00 4E 21 00 00 20 20 00 00 01 00 20 00 A8 10' - '00 00 F6 46 00 00 10 10 00 00 01 00 20 00 68 04' - '00 00 9E 57 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 11 00 00 00 00 11 00 00 11 11 11 00 22 22' - '22 00 33 33 33 00 44 44 44 00 55 55 55 00 66 66' - '66 00 77 77 77 00 7F 7F 7F 00 00 CC 66 00 66 99' - '99 00 33 CC 99 00 66 CC 99 00 88 88 88 00 99 99' - '99 00 AA AA AA 00 BB BB BB 00 99 CC 99 00 CC CC' - 'CC 00 DD DD DD 00 EE EE EE 00 FF FF FF 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 C0 17 95 00 00 00 38 00 A8 44' - 'F9 77 13 00 00 00 18 0A 38 00 00 00 38 00 18 6C' - '38 00 98 17 95 00 00 00 00 00 E0 19 95 00 F0 88' - 'FA 77 70 38 F5 77 FF FF FF FF A8 44 F9 77 70 7D' - 'F5 77 3A 8A F5 77 86 00 00 00 86 00 00 00 08 00' - '00 00 B0 18 95 00 00 00 00 00 CB 44 F9 77 38 9F' - '07 00 CD 8B F5 77 78 13 05 00 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00' - '00 00 00 00 00 00 54 00 56 00 00 EC FD 7F 1A 02' - '00 00 4C 16 95 00 40 9F 07 00 FC 15 95 00 FF FF' - 'FF FF B4 1A 95 00 45 00 00 00 28 02 00 00 FF FF' - 'FF FF E2 D8 F5 77 7D 9B F5 77 94 B6 01 00 00 00' - '05 00 F4 17 95 00 80 00 10 C0 B4 1A 95 00 F0 88' - 'FA 77 88 1C F5 77 FF FF FF FF 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 9B B2 E7 77 B7 00 00 00 02 00' - '00 00 A4 1A 95 00 01 00 00 00 18 00 00 00 00 00' - '00 00 10 19 95 00 42 00 00 00 00 00 00 00 F4 18' - '95 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00' - '00 00 02 00 00 00 01 01 F5 77 00 EC FD 7F 54 00' - '00 00 00 00 00 00 03 00 00 00 5C 00 1A 02 40 9F' - '07 00 00 00 00 00 40 9F 07 00 05 00 00 00 BE B3' - 'E7 77 4C 19 95 00 A3 B4 E7 77 F8 00 00 00 00 00' - '00 C0 00 00 00 00 00 00 00 00 02 00 00 00 80 00' - '00 00 00 00 00 00 8C 1A 95 00 7F E9 4B 00 50 6C' - '0C 01 00 00 00 C0 00 00 00 00 80 1A 95 00 02 00' - '00 00 80 00 00 00 00 00 00 00 C0 27 95 00 C4 F5' - 'AF 00 02 00 00 00 44 3A 5C 6F 73 65 78 70 65 72' - '74 73 5C 72 65 61 63 74 6F 73 5C 6C 69 62 5C 73' - '68 65 6C 6C 33 32 5C 64 72 69 76 65 2E 69 63 6F' - '00 00 1A 93 4B 00 14 1A 95 00 1F 3B D4 77 13 00' - '00 00 98 00 00 00 4F 3B D4 77 E0 19 95 00 33 3B' - 'D4 77 64 C5 F5 77 A9 F1 E7 77 F8 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 1A 95 00 18 6C' - '38 00 86 00 00 00 00 00 00 00 C9 F1 E7 77 86 00' - '00 00 A4 1A 95 00 08 00 00 00 00 00 00 00 86 00' - '00 00 86 00 00 00 08 00 00 00 F4 19 95 00 33 3B' - 'D4 77 B4 1A 95 00 09 48 E9 77 B8 10 E9 77 FF FF' - 'FF FF C9 F1 E7 77 16 EA 4B 00 F8 00 00 00 18 6C' - '38 00 86 00 00 00 58 1A 95 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 18 18 18 18 18 18 18 18 18 18 18 18 18 18' - '18 18 18 18 18 02 18 18 18 18 00 00 00 00 00 00' - '00 18 18 03 03 04 03 04 03 03 04 03 03 04 03 03' - '04 03 03 04 03 03 03 04 01 18 18 00 00 00 00 00' - '18 18 06 12 14 12 12 12 12 11 12 12 12 12 12 12' - '12 12 12 12 12 11 0F 10 0C 04 18 18 00 00 00 00' - '18 18 12 16 16 11 07 08 0F 10 11 12 12 11 12 12' - '12 12 12 12 12 12 0D 0B 10 0F 18 18 00 00 00 00' - '18 03 14 16 16 11 06 0F 0F 10 11 11 11 11 10 10' - '11 11 10 11 11 12 13 0E 12 0F 18 18 00 00 00 00' - '18 03 14 16 16 12 09 09 0F 10 10 11 12 0F 0F 10' - '0F 10 0F 10 11 14 12 12 14 10 18 18 00 00 00 00' - '18 03 15 16 16 12 08 0F 0F 10 11 11 12 10 09 0F' - '10 0F 0F 0F 11 14 14 12 14 0F 18 18 00 00 00 00' - '18 03 14 17 15 16 15 15 15 15 15 14 15 14 15 14' - '14 14 14 14 14 14 14 12 14 0F 18 18 00 00 00 00' - '18 03 14 17 17 16 17 17 16 17 16 17 16 17 16 17' - '16 17 16 17 16 16 16 17 15 10 18 18 00 00 00 00' - '18 03 15 16 15 15 15 15 15 15 15 15 15 15 15 15' - '15 15 15 15 15 15 15 15 17 10 18 18 00 00 00 00' - '18 03 15 15 11 11 14 15 15 14 15 15 14 15 14 15' - '14 15 14 15 14 12 11 12 16 10 18 18 00 00 00 00' - '18 18 11 15 12 12 12 14 14 15 15 15 15 16 15 16' - '15 15 15 14 15 11 12 12 16 09 18 18 00 00 00 00' - '18 18 0F 16 14 14 14 15 15 15 16 15 15 15 15 15' - '15 16 15 15 14 15 14 14 16 07 18 18 00 00 00 00' - '00 18 08 16 14 14 14 15 15 15 14 15 15 14 15 14' - '15 14 15 15 16 14 14 14 15 05 18 00 00 00 00 00' - '00 18 05 15 14 14 15 15 14 14 14 14 14 14 14 14' - '14 14 14 14 14 15 14 14 14 03 18 00 00 00 00 00' - '00 18 04 14 14 14 15 14 14 14 15 14 14 14 14 14' - '12 14 14 14 14 14 14 14 11 18 18 00 00 00 00 00' - '00 18 18 12 15 14 15 14 15 14 14 14 15 14 14 12' - '14 12 12 14 14 14 12 15 0F 18 18 00 00 00 00 00' - '00 18 18 10 15 14 14 15 14 15 14 15 14 14 15 15' - '14 12 14 12 12 12 12 14 08 18 18 00 00 00 00 00' - '00 18 18 09 15 15 14 15 14 15 15 15 15 15 14 15' - '15 15 15 15 15 12 14 14 06 18 18 00 00 00 00 00' - '00 00 18 06 15 15 15 14 16 15 15 15 15 15 15 15' - '15 15 15 15 14 15 15 14 05 18 00 00 00 00 00 00' - '00 00 18 05 14 16 15 15 14 15 15 16 15 16 16 15' - '16 15 15 15 15 15 15 11 03 18 00 00 00 00 00 00' - '00 00 18 04 11 15 16 16 16 15 15 15 15 15 15 15' - '15 15 15 15 16 16 15 10 18 18 00 00 00 00 00 00' - '00 00 18 18 11 16 14 14 15 16 16 15 16 15 15 16' - '15 16 16 15 12 15 15 09 18 18 00 00 00 00 00 00' - '00 00 18 18 0F 15 14 12 16 17 17 17 17 17 17 17' - '17 17 17 15 14 14 15 07 18 18 00 00 00 00 00 00' - '00 00 00 18 04 10 15 15 15 15 15 15 15 15 15 15' - '15 15 15 15 15 14 09 03 18 00 00 00 00 00 00 00' - '00 00 00 18 18 03 04 04 04 04 04 04 04 04 04 04' - '04 04 04 04 04 04 18 18 18 00 00 00 00 00 00 00' - '00 00 00 00 18 18 18 18 18 18 18 18 18 18 18 18' - '18 18 18 18 18 18 18 18 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 01 00 0C 00 10 10 10 00 01 00 04 00 28 01' + '00 00 C6 00 00 00 10 10 00 00 01 00 08 00 68 05' + '00 00 EE 01 00 00 10 10 00 00 01 00 20 00 68 04' + '00 00 56 07 00 00 20 20 10 00 01 00 04 00 E8 02' + '00 00 BE 0B 00 00 20 20 00 00 01 00 08 00 A8 08' + '00 00 A6 0E 00 00 20 20 00 00 01 00 20 00 A8 10' + '00 00 4E 17 00 00 30 30 10 00 01 00 04 00 68 06' + '00 00 F6 27 00 00 30 30 00 00 01 00 08 00 A8 0E' + '00 00 5E 2E 00 00 30 30 00 00 01 00 20 00 A8 25' + '00 00 06 3D 00 00 40 40 10 00 01 00 04 00 68 0A' + '00 00 AE 62 00 00 40 40 00 00 01 00 08 00 28 16' + '00 00 16 6D 00 00 40 40 00 00 01 00 20 00 28 42' + '00 00 3E 83 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 04 00 00 00 00 00 80 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 77 88' + '88 88 88 88 88 77 88 77 77 77 77 77 77 88 77 88' + '88 88 88 88 AA 77 88 FF FF FF FF FF FF 88 8F 77' + '77 77 77 77 77 F8 87 88 88 88 88 88 88 78 08 78' + '88 FF FF 88 87 80 00 87 88 77 77 88 87 80 00 87' + '88 88 88 88 78 00 00 08 77 77 77 77 78 00 00 00' + '88 88 88 88 80 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' - 'FF FF FF FF FF FF FF FF FF FF F0 00 00 0F E0 00' - '00 07 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 E0 00' - '00 07 E0 00 00 07 E0 00 00 07 E0 00 00 07 E0 00' - '00 07 E0 00 00 07 F0 00 00 0F F0 00 00 0F F0 00' - '00 0F F0 00 00 0F F0 00 00 0F F8 00 00 1F F8 00' - '00 1F FC 00 00 3F FF FF FF FF FF FF FF FF 28 00' + '00 00 FF FF 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 01' + '00 00 C0 01 00 00 C0 03 00 00 E0 03 00 00 F0 07' + '00 00 FF FF 00 00 FF FF 00 00 FF FF 00 00 28 00' '00 00 10 00 00 00 20 00 00 00 01 00 08 00 00 00' - '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 11 00 11 11' - '11 00 22 00 00 00 22 22 22 00 44 44 44 00 55 55' - '55 00 66 66 66 00 77 77 77 00 7F 7F 7F 00 33 CC' - '99 00 88 88 88 00 99 99 99 00 AA AA AA 00 BB BB' - 'BB 00 99 CC 99 00 CC CC CC 00 DD DD DD 00 EE EE' - 'EE 00 00 00 00 00 CC CC CC 00 DD DD DD 00 EE EE' - 'EE 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 C0 DC C0 00 F0 CA A6 00 65 65' + '65 00 6E 6E 6E 00 71 71 71 00 78 78 78 00 7A 7A' + '7A 00 7B 7B 7B 00 7E 7E 7E 00 3F BE 3F 00 1E DD' + '1E 00 FF 00 FF 00 81 81 81 00 84 84 84 00 87 87' + '87 00 8A 8A 8A 00 8F 8F 8F 00 92 92 92 00 93 93' + '93 00 94 94 94 00 95 95 95 00 96 96 96 00 97 97' + '97 00 9B 9B 9B 00 9C 9C 9C 00 9E 9E 9E 00 A0 A0' + 'A0 00 A1 A1 A1 00 A2 A2 A2 00 A3 A3 A3 00 A4 A4' + 'A4 00 A5 A5 A5 00 A6 A6 A6 00 A7 A7 A7 00 A8 A8' + 'A8 00 A9 A9 A9 00 AA AA AA 00 AB AB AB 00 AC AC' + 'AC 00 AD AD AD 00 AE AE AE 00 AF AF AF 00 B0 B0' + 'B0 00 B1 B1 B1 00 B2 B2 B2 00 B3 B3 B3 00 B4 B4' + 'B4 00 B5 B5 B5 00 B6 B6 B6 00 B7 B7 B7 00 B8 B8' + 'B8 00 B9 B9 B9 00 BA BA BA 00 BB BB BB 00 BC BC' + 'BC 00 BD BD BD 00 BE BE BE 00 BF BF BF 00 C0 C0' + 'C0 00 C1 C1 C1 00 C2 C2 C2 00 C3 C3 C3 00 C4 C4' + 'C4 00 C5 C5 C5 00 C6 C6 C6 00 C7 C7 C7 00 C8 C8' + 'C8 00 C9 C9 C9 00 CA CA CA 00 CB CB CB 00 CF CF' + 'CF 00 D0 D0 D0 00 D6 D6 D6 00 D7 D7 D7 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -6009,145 +10124,207 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 17' - '95 00 00 00 38 00 A8 44 F9 77 13 00 00 00 18 0A' - '38 00 00 00 38 00 18 6C 38 00 98 17 95 00 00 00' - '00 00 E0 19 95 00 F0 88 FA 77 70 38 F5 77 FF FF' - 'FF FF A8 44 F9 77 70 7D F5 77 3A 8A F5 77 86 00' - '00 00 86 00 00 00 08 00 00 00 B0 18 95 00 00 00' - '00 00 CB 44 F9 77 38 9F 07 00 CD 8B F5 77 78 13' - '05 00 37 90 F5 77 00 00 00 00 3E 8A F5 77 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 10 00 00 00 00 00 00 00 54 00' - '56 00 00 EC FD 7F 1A 02 00 00 4C 16 95 00 40 9F' - '07 00 FC 15 95 00 FF FF FF FF B4 1A 95 00 45 00' - '00 00 28 02 00 00 FF FF FF FF E2 D8 F5 77 7D 9B' - 'F5 77 94 B6 01 00 00 00 05 00 F4 17 95 00 80 00' - '10 C0 B4 1A 95 00 F0 88 FA 77 88 1C F5 77 FF FF' - 'FF FF 37 90 F5 77 00 00 00 00 3E 8A F5 77 9B B2' - 'E7 77 B7 00 00 00 02 00 00 00 A4 1A 95 00 01 00' - '00 00 18 00 00 00 00 00 00 00 10 19 95 00 42 00' - '00 00 00 00 00 00 F4 18 95 00 00 00 00 00 00 00' - '00 00 00 00 00 00 0C 00 00 00 02 00 00 00 01 01' - 'F5 77 00 EC FD 7F 54 00 00 00 00 00 00 00 03 00' - '00 00 5C 00 1A 02 40 9F 07 00 00 00 00 00 40 9F' - '07 00 05 00 00 00 BE B3 E7 77 4C 19 95 00 A3 B4' - 'E7 77 F8 00 00 00 00 00 00 C0 00 00 00 00 00 00' - '00 00 02 00 00 00 80 00 00 00 00 00 00 00 8C 1A' - '95 00 7F E9 4B 00 50 6C 0C 01 00 00 00 C0 00 00' - '00 00 80 1A 95 00 02 00 00 00 80 00 00 00 00 00' - '00 00 C0 27 95 00 C4 F5 AF 00 02 00 00 00 44 3A' - '5C 6F 73 65 78 70 65 72 74 73 5C 72 65 61 63 74' - '6F 73 5C 6C 69 62 5C 73 68 65 6C 6C 33 32 5C 64' - '72 69 76 65 2E 69 63 6F 00 00 1A 93 4B 00 14 1A' - '95 00 1F 3B D4 77 13 00 00 00 98 00 00 00 4F 3B' - 'D4 77 E0 19 95 00 33 3B D4 77 64 C5 F5 77 A9 F1' - 'E7 77 F8 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 1A 95 00 18 6C 38 00 86 00 00 00 00 00' - '00 00 C9 F1 E7 77 86 00 00 00 A4 1A 95 00 08 00' - '00 00 00 00 00 00 86 00 00 00 86 00 00 00 08 00' - '00 00 F4 19 95 00 33 3B D4 77 B4 1A 95 00 09 48' - 'E9 77 B8 10 E9 77 FF FF FF FF C9 F1 E7 77 16 EA' - '4B 00 F8 00 00 00 18 6C 38 00 86 00 00 00 58 1A' - '95 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 13 13 02 02 02 02 02 02 02' - '02 02 03 01 13 00 00 13 0C 0E 0B 0C 0D 0E 0D 0E' - '0E 0D 0A 07 13 00 00 02 11 10 07 0B 0D 0D 0C 0C' - '0C 0E 0F 0C 13 00 00 02 11 11 0C 0D 0E 0E 0C 0D' - '0C 0E 10 0D 13 00 00 02 11 12 12 12 11 11 12 11' - '11 11 11 0E 13 00 00 13 10 0E 10 10 11 11 11 11' - '11 10 10 0E 13 00 00 13 0D 10 10 11 11 11 11 11' - '11 10 11 0B 13 00 00 13 0B 11 11 11 10 10 10 11' - '10 10 11 07 13 00 00 13 07 11 10 10 11 10 10 0E' - '10 10 10 06 13 00 00 13 05 10 11 11 10 11 11 10' - '11 10 10 04 13 00 00 13 04 11 11 11 11 11 11 11' - '11 11 0E 02 13 00 00 00 13 0E 11 12 12 12 12 12' - '11 12 0D 13 00 00 00 00 13 08 10 11 11 12 11 12' - '10 10 07 13 00 00 00 00 13 13 04 02 04 02 04 02' - '04 02 13 13 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 FF FF 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 C0 03 00 00 C0 03 00 00 C0 03' - '00 00 FF FF 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 04 00 00 00 00 00 00 02 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 80 00 00 00 00 80 00 00 80 80 00 00 00 00' - '80 00 80 00 80 00 00 80 80 00 C0 C0 C0 00 80 80' - '80 00 FF 00 00 00 00 FF 00 00 FF FF 00 00 00 00' - 'FF 00 FF 00 FF 00 00 FF FF 00 FF FF FF 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 80 00 80 00 00 00 00 80 82 00 00 00 00 00' - '87 77 77 77 77 77 77 77 77 87 88 88 00 00 00 00' - '7F F7 88 87 87 77 77 77 77 77 2E 88 00 00 00 00' - '7F F8 88 88 78 78 77 88 77 77 77 78 00 00 00 00' - 'F7 F7 88 78 77 77 88 87 88 77 77 78 00 00 00 08' - '7F 77 88 88 87 78 88 78 88 77 77 77 00 00 00 00' - '7F FF 77 77 F7 77 77 77 77 77 77 78 00 00 00 00' - '7F FF FF FF FF FF FF FF FF 7F FF F8 00 00 00 04' - '7F 77 F7 FF 7F 7F 77 F7 F7 FF 77 F8 00 00 00 00' - 'F7 77 7F 77 7F 77 F7 F7 7F 77 77 77 00 00 00 00' - '77 77 77 77 F7 FF 7F 77 F7 77 77 F8 00 00 00 00' - '8F 77 77 FF 7F 77 F7 FF 7F 77 77 F8 00 00 00 00' - '8F 77 7F F7 F7 7F 7F 77 F7 7F 7F 78 00 00 00 00' - '8F 77 F7 77 7F 77 77 F7 7F 77 77 70 00 00 00 00' - '87 7F 77 7F 77 F7 7F 77 77 7F 77 70 00 00 00 00' - '07 77 F7 77 F7 77 77 77 77 77 7F 80 00 00 00 00' - '08 7F 77 F7 77 7F 77 77 77 77 77 80 00 00 00 00' - '08 F7 7F 7F 7F 77 F7 F7 F7 77 77 80 00 00 00 00' - '08 7F 77 F7 F7 FF 7F 7F 7F 77 F7 80 00 00 00 00' - '04 7F 7F 7F 7F 7F F7 F7 F7 F7 77 00 00 00 00 00' - '08 8F FF 77 F7 F7 7F 7F 7F 7F F8 00 00 00 00 00' - '00 77 77 FF 7F 7F F7 F7 FF 77 F8 00 00 00 00 00' - '00 8F 77 FF FF FF FF FF F7 7F 78 00 00 00 00 00' - '00 48 7F 77 F7 F7 7F 7F 77 F7 80 00 00 00 00 00' - '00 00 80 48 08 04 80 80 48 04 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 FB' + 'FF 00 A4 A0 A0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 2F 22 39 35 3A 3C 3B 3A 34 31' + '2E 29 25 23 17 2F 48 20 17 1B 1A 1D 1B 18 17 15' + '10 0D 0C 0B 0D 25 2F 19 2A 2F 34 2F 2D 25 20 1D' + '18 16 11 12 0A 0F 48 0E 07 43 45 45 47 49 48 48' + '49 48 4C 4F 3F 33 3E 14 1F 24 28 2C 30 35 38 3A' + '3D 40 43 1C 21 4D 3E 21 22 25 28 2E 30 35 3A 3A' + '3E 41 41 4B 27 40 00 38 1E 26 2A 2A 07 50 50 4E' + '3A 46 41 24 07 00 00 00 37 1F 2B 1E 32 3F 44 21' + '27 48 4D 23 41 00 00 00 41 23 2E 32 2D 20 22 2C' + '44 4E 29 4A 00 00 00 00 00 45 21 35 39 3E 3F 44' + '4B 51 30 41 00 00 00 00 00 00 36 23 35 37 43 43' + '46 32 40 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF 00 00 FF FF 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 80 01 00 00 C0 01 00 00 C0 03' + '00 00 E0 03 00 00 F0 07 00 00 FF FF 00 00 FF FF' + '00 00 FF FF 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 20 00 00 00 00 00 40 04 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 CE CE' + 'CE 73 96 96 96 F9 BE BE BE F9 B8 B8 B8 F8 BF BF' + 'BF F9 C3 C3 C3 F9 C2 C2 C2 F9 BF BF BF F9 B7 B7' + 'B7 F8 B0 B0 B0 F8 AB AB AB F8 A1 A1 A1 F8 9A 9A' + '9A F8 97 97 97 FA 6C 6C 6C F9 AC AC AC 54 E9 E9' + 'E9 A8 9C 9C 9C FF 8A 8A 8A FF 94 94 94 FF 93 93' + '93 FF 96 96 96 FF 94 94 94 FF 8F 8F 8F FF 8A 8A' + '8A FF 84 84 84 FF 7E 7E 7E FF 78 78 78 FF 71 71' + '71 FF 6E 6E 6E FF 81 81 81 FF AA AA AA 7F 9B 9B' + '9B A3 92 92 92 FF A8 A8 A8 FF AD AD AD FF B2 B2' + 'B2 FF AD AD AD FF AB AB AB FF A3 A3 A3 FF 9C 9C' + '9C FF 96 96 96 FF 8F 8F 8F FF 87 87 87 FF 40 BF' + '40 FF 1E DD 1E FF 65 65 65 FF 58 58 58 A8 CF CF' + 'CF 92 7A 7A 7A FF C0 C0 C0 FF C1 C1 C1 FF C3 C3' + 'C3 FF C3 C3 C3 FF C5 C5 C5 FF C7 C7 C7 FF C6 C6' + 'C6 FF C6 C6 C6 FF C7 C7 C7 FF C6 C6 C6 FF CA CA' + 'CA FF D0 D0 D0 FF BD BD BD FF A5 A5 A5 91 B2 B2' + 'B2 4B 81 81 81 FF 9B 9B 9B FF A2 A2 A2 FF A6 A6' + 'A6 FF AA AA AA FF AE AE AE FF B3 B3 B3 FF B6 B6' + 'B6 FF B8 B8 B8 FF BB BB BB FF BE BE BE FF C1 C1' + 'C1 FF 95 95 95 FF 9E 9E 9E FF E7 E7 E7 48 00 00' + '00 00 92 92 92 BA A0 A0 A0 FF A3 A3 A3 FF A6 A6' + 'A6 FF AC AC AC FF AE AE AE FF B3 B3 B3 FF B8 B8' + 'B8 FF B8 B8 B8 FF BC BC BC FF BF BF BF FF BF BF' + 'BF FF C9 C9 C9 FF A1 A1 A1 DD 00 00 00 00 00 00' + '00 00 99 99 99 4E 97 97 97 FF A4 A4 A4 FF A8 A8' + 'A8 FF A8 A8 A8 FF C0 C0 C0 FF D6 D6 D6 FF D6 D6' + 'D6 FF CF CF CF FF B8 B8 B8 FF C4 C4 C4 FF BF BF' + 'BF FF A2 A2 A2 FF BF BF BF 59 00 00 00 00 00 00' + '00 00 00 00 00 00 B2 B2 B2 F8 9B 9B 9B FF A9 A9' + 'A9 FF 97 97 97 FF B0 B0 B0 FF BD BD BD FF C2 C2' + 'C2 FF 9E 9E 9E FF A5 A5 A5 FF C6 C6 C6 FF CB CB' + 'CB FF 9B 9B 9B D7 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 C1 C1 C1 37 A1 A1 A1 FF AC AC' + 'AC FF B0 B0 B0 FF AB AB AB FF 9C 9C 9C FF A0 A0' + 'A0 FF AA AA AA FF C2 C2 C2 FF CF CF CF FF A7 A7' + 'A7 FF D4 D4 D4 61 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 9C 9C 9C 18 C8 C8 C8 90 9E 9E' + '9E FF B3 B3 B3 FF B7 B7 B7 FF BC BC BC FF BD BD' + 'BD FF C2 C2 C2 FF C9 C9 C9 FF D7 D7 D7 FF AD AD' + 'AD F7 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 CB CB CB 3B B0 B0' + 'B0 EC A0 A0 A0 FB B3 B3 B3 FB B5 B5 B5 FC C1 C1' + 'C1 F8 C1 C1 C1 F9 C4 C4 C4 F9 AF AF AF F4 BC BC' + 'BC 84 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' - 'FF FF FF FF FF FF FF FF FF FF F0 00 00 0F E0 00' - '00 07 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 E0 00' - '00 07 E0 00 00 07 E0 00 00 07 E0 00 00 07 E0 00' - '00 07 E0 00 00 07 F0 00 00 0F F0 00 00 0F F0 00' - '00 0F F0 00 00 0F F0 00 00 0F F8 00 00 1F F8 00' - '00 1F FC 00 00 3F FF FF FF FF FF FF FF FF 28 00' - '00 00 10 00 00 00 20 00 00 00 01 00 04 00 00 00' - '00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 80' - '00 00 80 80 00 00 00 00 80 00 80 00 80 00 00 80' - '80 00 C0 C0 C0 00 80 80 80 00 FF 00 00 00 00 FF' - '00 00 FF FF 00 00 00 00 FF 00 FF 00 FF 00 00 FF' - 'FF 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' - '80 00 00 02 00 00 00 87 88 77 77 77 68 00 00 F7' - '88 87 87 87 78 00 08 77 87 77 87 87 77 00 00 FF' - 'F7 FF 7F 7F 77 00 00 77 77 F7 F7 F7 77 00 00 77' - '7F 77 F7 7F 78 00 00 87 F7 7F 77 F7 F8 00 00 8F' - '77 F7 77 77 78 00 00 87 7F 77 7F 77 78 00 00 47' - 'F7 F7 F7 F7 70 00 00 07 F7 FF 7F 7F 70 00 00 08' - '7F 7F F7 F7 80 00 00 00 04 08 00 40 00 00 00 00' - '00 00 00 00 00 00 FF FF 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 C0 03 00 00 C0 03 00 00 C0 03' - '00 00 FF FF 00 00 28 00 00 00 30 00 00 00 60 00' - '00 00 01 00 08 00 00 00 00 00 00 09 00 00 00 00' + '00 00 FF FF 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 80 01 00 00 80 01' + '00 00 C0 03 00 00 C0 03 00 00 C0 07 00 00 E0 07' + '00 00 FF FF 00 00 FF FF 00 00 FF FF 00 00 28 00' + '00 00 20 00 00 00 40 00 00 00 01 00 04 00 00 00' + '00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 11 00 11 11 11 00 22 22 22 00 33 33' - '33 00 44 44 44 00 55 55 55 00 66 66 66 00 77 77' - '77 00 7F 7F 7F 00 00 99 66 00 33 99 66 00 00 CC' - '66 00 33 CC 66 00 66 99 99 00 88 88 88 00 99 99' - '99 00 AA AA AA 00 BB BB BB 00 99 CC 99 00 99 FF' - '99 00 99 CC CC 00 CC CC CC 00 DD DD DD 00 EE EE' - 'EE 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 77 78 88 88 88 88 88 88 88' + '88 88 88 87 77 00 08 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 80 08 88 77 77 77 77 77 77 77 77' + '77 77 77 77 88 80 88 87 88 88 88 88 88 88 88 88' + '88 88 88 AA 78 88 77 78 88 88 88 88 88 88 88 88' + '88 88 88 88 87 77 88 87 77 77 77 77 77 77 77 77' + '77 77 77 77 78 88 87 78 8F FF FF FF FF FF FF FF' + 'FF FF FF F8 87 78 88 88 F7 77 77 77 77 77 77 77' + '77 77 77 7F 88 88 08 8F 78 88 88 88 88 88 88 88' + '88 88 88 87 F8 80 08 87 88 88 88 88 88 88 88 88' + '88 88 88 88 78 80 00 87 88 88 88 88 88 88 88 88' + '88 88 88 88 78 00 00 87 88 88 88 88 8F FF FF F8' + '88 88 88 88 78 00 00 08 78 88 88 88 F8 88 88 8F' + '88 88 88 87 80 00 00 08 78 88 88 88 88 88 88 88' + '88 88 88 87 80 00 00 00 87 88 88 88 78 88 88 87' + '88 88 88 78 00 00 00 00 87 88 88 88 87 77 77 78' + '88 88 88 78 00 00 00 00 08 78 88 88 88 88 88 88' + '88 88 87 80 00 00 00 00 08 78 88 88 88 88 88 88' + '88 88 87 80 00 00 00 00 00 87 88 88 88 88 88 88' + '88 88 78 00 00 00 00 00 00 87 77 77 77 77 77 77' + '77 77 78 00 00 00 00 00 00 08 88 88 88 88 88 88' + '88 88 80 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF C0 00 00 03 80 00' + '00 01 80 00 00 01 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 80 00 00 01 80 00' + '00 01 C0 00 00 03 C0 00 00 03 E0 00 00 07 E0 00' + '00 07 F0 00 00 0F F0 00 00 0F F8 00 00 1F F8 00' + '00 1F FC 00 00 3F FC 00 00 3F FE 00 00 7F FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 20 00 00 00 40 00' + '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 C0 C0 C0 00 C0 DC' + 'C0 00 F0 CA A6 00 4D 4D 4D 00 4E 4E 4E 00 56 56' + '56 00 5C 5C 5C 00 5D 5D 5D 00 5F 5F 5F 00 61 61' + '61 00 63 63 63 00 64 64 64 00 65 65 65 00 66 66' + '66 00 68 68 68 00 6A 6A 6A 00 6C 6C 6C 00 6E 6E' + '6E 00 6F 6F 6F 00 70 70 70 00 72 72 72 00 73 73' + '73 00 74 74 74 00 75 75 75 00 77 77 77 00 79 79' + '79 00 7A 7A 7A 00 7B 7B 7B 00 7D 7D 7D 00 7E 7E' + '7E 00 1A D9 1A 00 1B DA 1B 00 FF 00 FF 00 80 80' + '80 00 81 81 81 00 83 83 83 00 84 84 84 00 85 85' + '85 00 86 86 86 00 87 87 87 00 88 88 88 00 89 89' + '89 00 8A 8A 8A 00 8B 8B 8B 00 8C 8C 8C 00 8D 8D' + '8D 00 8E 8E 8E 00 8F 8F 8F 00 90 90 90 00 91 91' + '91 00 92 92 92 00 93 93 93 00 94 94 94 00 95 95' + '95 00 96 96 96 00 97 97 97 00 98 98 98 00 99 99' + '99 00 9A 9A 9A 00 9B 9B 9B 00 9C 9C 9C 00 9D 9D' + '9D 00 9E 9E 9E 00 9F 9F 9F 00 A0 A0 A0 00 A1 A1' + 'A1 00 A2 A2 A2 00 A3 A3 A3 00 A4 A4 A4 00 A5 A5' + 'A5 00 A6 A6 A6 00 A7 A7 A7 00 A8 A8 A8 00 A9 A9' + 'A9 00 AA AA AA 00 AB AB AB 00 AC AC AC 00 AD AD' + 'AD 00 AE AE AE 00 AF AF AF 00 B0 B0 B0 00 B1 B1' + 'B1 00 B2 B2 B2 00 B3 B3 B3 00 B4 B4 B4 00 B5 B5' + 'B5 00 B6 B6 B6 00 B7 B7 B7 00 B8 B8 B8 00 B9 B9' + 'B9 00 BA BA BA 00 BB BB BB 00 BC BC BC 00 BD BD' + 'BD 00 BE BE BE 00 BF BF BF 00 C0 C0 C0 00 C1 C1' + 'C1 00 C2 C2 C2 00 C3 C3 C3 00 C4 C4 C4 00 C5 C5' + 'C5 00 C6 C6 C6 00 C7 C7 C7 00 C8 C8 C8 00 C9 C9' + 'C9 00 CA CA CA 00 CB CB CB 00 CC CC CC 00 CD CD' + 'CD 00 CE CE CE 00 CF CF CF 00 D0 D0 D0 00 D1 D1' + 'D1 00 D3 D3 D3 00 D4 D4 D4 00 D8 D8 D8 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -6161,50 +10338,6 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 C0 17 95 00 00 00 38 00 A8 44' - 'F9 77 13 00 00 00 18 0A 38 00 00 00 38 00 18 6C' - '38 00 98 17 95 00 00 00 00 00 E0 19 95 00 F0 88' - 'FA 77 70 38 F5 77 FF FF FF FF A8 44 F9 77 70 7D' - 'F5 77 3A 8A F5 77 86 00 00 00 86 00 00 00 08 00' - '00 00 B0 18 95 00 00 00 00 00 CB 44 F9 77 38 9F' - '07 00 CD 8B F5 77 78 13 05 00 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00' - '00 00 00 00 00 00 54 00 56 00 00 EC FD 7F 1A 02' - '00 00 4C 16 95 00 40 9F 07 00 FC 15 95 00 FF FF' - 'FF FF B4 1A 95 00 45 00 00 00 28 02 00 00 FF FF' - 'FF FF E2 D8 F5 77 7D 9B F5 77 94 B6 01 00 00 00' - '05 00 F4 17 95 00 80 00 10 C0 B4 1A 95 00 F0 88' - 'FA 77 88 1C F5 77 FF FF FF FF 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 9B B2 E7 77 B7 00 00 00 02 00' - '00 00 A4 1A 95 00 01 00 00 00 18 00 00 00 00 00' - '00 00 10 19 95 00 42 00 00 00 00 00 00 00 F4 18' - '95 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00' - '00 00 02 00 00 00 01 01 F5 77 00 EC FD 7F 54 00' - '00 00 00 00 00 00 03 00 00 00 5C 00 1A 02 40 9F' - '07 00 00 00 00 00 40 9F 07 00 05 00 00 00 BE B3' - 'E7 77 4C 19 95 00 A3 B4 E7 77 F8 00 00 00 00 00' - '00 C0 00 00 00 00 00 00 00 00 02 00 00 00 80 00' - '00 00 00 00 00 00 8C 1A 95 00 7F E9 4B 00 50 6C' - '0C 01 00 00 00 C0 00 00 00 00 80 1A 95 00 02 00' - '00 00 80 00 00 00 00 00 00 00 C0 27 95 00 C4 F5' - 'AF 00 02 00 00 00 44 3A 5C 6F 73 65 78 70 65 72' - '74 73 5C 72 65 61 63 74 6F 73 5C 6C 69 62 5C 73' - '68 65 6C 6C 33 32 5C 64 72 69 76 65 2E 69 63 6F' - '00 00 1A 93 4B 00 14 1A 95 00 1F 3B D4 77 13 00' - '00 00 98 00 00 00 4F 3B D4 77 E0 19 95 00 33 3B' - 'D4 77 64 C5 F5 77 A9 F1 E7 77 F8 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 1A 95 00 18 6C' - '38 00 86 00 00 00 00 00 00 00 C9 F1 E7 77 86 00' - '00 00 A4 1A 95 00 08 00 00 00 00 00 00 00 86 00' - '00 00 86 00 00 00 08 00 00 00 F4 19 95 00 33 3B' - 'D4 77 B4 1A 95 00 09 48 E9 77 B8 10 E9 77 FF FF' - 'FF FF C9 F1 E7 77 16 EA 4B 00 F8 00 00 00 18 6C' - '38 00 86 00 00 00 58 1A 95 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -6217,126 +10350,419 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A' - '1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A' - '1A 1A 1A 1A 1A 1A 1A 00 00 00 00 00 00 00 00 00' - '00 00 00 00 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A' - '1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A' - '1A 1A 1A 1A 1A 1A 1A 1A 1A 00 00 00 00 00 00 00' - '00 00 00 1A 1A 1A 1A 02 03 03 04 03 03 04 03 03' - '04 03 03 04 03 03 03 04 03 03 03 03 04 03 03 04' - '03 03 03 04 01 1A 1A 1A 1A 00 00 00 00 00 00 00' - '00 00 00 1A 1A 03 10 16 16 16 16 16 16 16 16 16' - '12 16 12 12 16 12 12 12 16 12 12 12 12 12 12 11' - '12 12 11 11 11 07 1A 1A 1A 00 00 00 00 00 00 00' - '00 00 1A 1A 1A 0F 17 18 18 17 0F 0F 10 10 10 11' - '12 16 12 16 16 16 16 17 16 16 16 17 16 16 16 16' - '16 11 0B 0B 13 12 06 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 02 16 18 17 17 16 04 05 06 08 0F 0F' - '11 10 11 11 12 11 10 12 10 12 10 16 10 12 10 16' - '12 0D 0C 0A 0B 16 08 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 03 16 18 17 18 17 05 06 08 0F 10 10' - '10 11 11 12 12 10 10 12 0F 12 10 12 10 12 0F 12' - '16 10 15 14 0E 16 0F 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 04 17 18 18 17 16 07 08 08 0F 0F 10' - '10 11 11 11 12 11 08 12 08 11 0F 12 0F 12 08 16' - '16 12 11 10 16 12 10 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 03 16 18 17 18 17 07 08 08 0F 0F 10' - '11 11 11 11 12 10 08 11 07 10 08 11 0F 11 07 12' - '12 16 12 16 12 16 0F 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 03 17 18 18 17 17 07 07 08 0F 0F 0F' - '10 11 10 11 12 0F 07 11 06 10 07 12 07 11 06 16' - '16 12 16 12 12 16 0F 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 03 16 18 18 18 17 11 11 11 11 11 12' - '11 12 12 12 12 12 11 12 11 12 12 12 11 12 11 12' - '16 12 16 12 16 12 10 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 03 17 18 18 18 18 17 18 18 18 17 17' - '18 17 17 17 17 17 17 17 17 17 16 17 16 16 16 16' - '16 16 12 16 12 16 0F 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 03 16 18 19 19 18 19 19 18 18 18 19' - '18 19 18 19 18 18 18 18 18 18 18 18 18 18 18 18' - '18 18 18 18 17 17 0F 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 03 17 19 17 17 18 17 17 18 17 17 17' - '17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17' - '18 17 18 17 19 18 0F 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 03 17 19 17 16 12 12 17 16 17 17 16' - '17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17' - '16 12 12 16 17 19 10 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 03 17 18 16 11 11 11 16 17 17 16 17' - '17 16 17 16 17 17 16 17 16 17 16 17 16 17 17 16' - '11 12 11 12 16 19 10 1A 1A 1A 00 00 00 00 00 00' - '00 00 1A 1A 1A 12 19 12 11 16 11 12 17 16 17 17' - '16 17 16 17 16 16 17 16 17 16 17 16 17 17 16 16' - '11 12 12 11 17 19 08 1A 1A 1A 00 00 00 00 00 00' - '00 00 00 1A 1A 11 18 16 16 16 16 16 16 17 16 16' - '17 17 17 18 18 18 18 18 17 18 17 17 16 16 17 16' - '16 16 12 16 16 18 06 1A 1A 00 00 00 00 00 00 00' - '00 00 00 1A 1A 08 18 17 16 16 16 16 16 16 17 18' - '18 17 17 17 17 17 17 17 17 17 17 18 18 18 16 16' - '16 16 16 16 17 17 04 1A 1A 00 00 00 00 00 00 00' - '00 00 00 1A 1A 06 18 16 16 16 16 16 17 18 18 17' - '16 17 17 17 16 17 17 16 17 17 16 17 17 17 18 17' - '16 16 16 16 17 12 03 1A 1A 00 00 00 00 00 00 00' - '00 00 00 1A 1A 05 17 16 16 16 16 16 18 17 16 16' - '16 17 16 16 16 17 16 16 17 16 16 17 16 16 17 18' - '16 16 16 16 17 11 1A 1A 1A 00 00 00 00 00 00 00' - '00 00 00 1A 1A 03 12 17 16 16 16 18 16 16 17 16' - '17 16 17 16 16 16 16 16 16 16 16 16 16 16 16 16' - '18 16 16 16 17 0F 1A 1A 1A 00 00 00 00 00 00 00' - '00 00 00 1A 1A 1A 10 17 16 16 17 16 17 16 16 16' - '16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16' - '16 17 12 16 17 06 1A 1A 1A 00 00 00 00 00 00 00' - '00 00 00 00 1A 1A 0F 17 17 16 17 16 16 17 16 17' - '16 17 16 16 16 16 16 16 12 16 12 16 16 16 12 16' - '16 16 16 16 17 05 1A 1A 00 00 00 00 00 00 00 00' - '00 00 00 00 1A 1A 06 17 16 16 16 17 16 16 16 16' - '16 16 16 16 17 16 16 12 16 12 16 12 16 12 16 12' - '16 12 16 16 17 03 1A 1A 00 00 00 00 00 00 00 00' - '00 00 00 00 1A 1A 05 17 16 17 16 16 17 16 17 16' - '17 16 17 16 16 17 16 16 16 12 16 12 12 12 16 12' - '12 16 12 16 11 03 1A 1A 00 00 00 00 00 00 00 00' - '00 00 00 00 1A 1A 03 16 17 16 16 17 16 17 16 17' - '16 17 16 16 17 16 16 17 16 17 16 16 16 16 12 16' - '16 11 12 16 11 1A 1A 1A 00 00 00 00 00 00 00 00' - '00 00 00 00 1A 1A 02 12 17 17 16 16 17 17 17 17' - '17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 16' - '16 16 16 16 08 1A 1A 1A 00 00 00 00 00 00 00 00' - '00 00 00 00 1A 1A 1A 10 17 17 17 16 17 17 17 17' - '17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17' - '16 17 17 16 06 1A 1A 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 1A 1A 08 17 17 18 17 16 17 17 17' - '17 17 18 17 17 18 17 17 17 17 17 17 18 17 17 16' - '17 17 17 12 04 1A 1A 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 1A 1A 06 16 17 17 18 17 17 16 17' - '17 18 17 18 17 17 18 17 17 18 18 17 17 16 16 18' - '17 18 16 11 03 1A 1A 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 1A 1A 04 12 18 17 17 18 17 17 16' - '17 17 17 17 18 17 17 18 17 17 17 17 16 17 18 17' - '18 17 17 10 1A 1A 1A 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 1A 1A 02 11 18 18 18 18 18 18 18' - '17 17 17 17 17 17 17 17 17 17 17 17 18 18 17 18' - '17 18 17 0F 1A 1A 1A 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 1A 1A 1A 11 17 17 12 12 17 18 18' - '18 18 18 18 18 17 18 18 18 18 18 18 18 17 16 12' - '17 18 16 08 1A 1A 1A 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 1A 1A 0F 17 16 11 12 11 18 18' - '18 18 18 18 18 18 18 18 18 18 18 18 18 17 11 12' - '11 18 16 07 1A 1A 1A 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 1A 1A 07 16 17 17 17 17 19 19' - '19 19 19 19 19 19 19 19 19 19 19 19 19 18 17 17' - '17 17 11 04 1A 1A 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 1A 1A 02 0F 12 17 17 17 17 17' - '17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17' - '17 11 07 1A 1A 1A 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 1A 1A 1A 1A 05 05 05 05 05 05' - '05 05 05 05 05 05 05 05 05 05 05 05 05 05 05 05' - '05 04 1A 1A 1A 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 1A 1A 1A 1A 1A 1A 1A 1A 1A' - '1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A' - '1A 1A 1A 1A 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 1A 1A 1A 1A 1A 1A 1A' - '1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A' - '1A 1A 1A 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 F0 FB FF 00 A4 A0 A0 00 80 80' + '80 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 49' + '3B 40 43 4E 55 54 56 58 59 59 56 55 52 4F 4D 49' + '45 42 3E 38 35 32 30 2C 24 1C 18 17 2E 00 00 4E' + '4D 52 58 58 58 5B 5E 60 62 62 60 5E 5B 59 54 51' + '4E 4A 47 44 3F 3B 37 34 2F 2C 22 20 21 00 00 4F' + '4C 57 24 3B 3C 3D 3C 3D 3F 3C 37 32 2F 2C 29 24' + '22 1F 1C 19 17 15 12 10 0E 0A 22 1D 1D 00 5E 55' + '55 37 5B 53 55 54 57 5A 5B 53 51 4E 49 45 41 3B' + '38 34 31 2A 28 22 1F 19 26 25 14 1F 1D 46 4F 38' + '38 3E 53 50 53 54 58 59 58 51 4F 4A 46 42 3C 38' + '35 32 2E 28 23 20 1E 1A 17 14 0D 0C 0B 29 44 3A' + '2E 3C 44 4E 52 55 58 56 57 56 57 55 52 51 4D 4B' + '48 45 42 40 3C 36 32 31 2D 23 1C 21 1B 47 4C 4C' + '2D 33 42 48 4A 4C 4E 4F 50 51 54 55 57 55 55 56' + '57 57 57 55 55 56 58 57 56 65 60 51 37 64 5A 4E' + '16 11 30 2D 34 36 37 39 3B 3B 3D 3C 3E 3E 3D 3E' + '3E 3E 40 3E 3E 3F 40 42 36 4B 5E 58 52 63 00 4A' + '0F 2B 35 4B 4C 4D 4F 51 53 55 57 59 5B 5C 5E 60' + '62 63 65 66 07 68 69 6B 62 37 3A 48 5C 00 00 59' + '2F 3B 4D 48 4A 4C 4D 50 52 54 56 58 5A 5C 5D 5F' + '60 62 63 65 66 07 07 68 6B 76 35 41 5E 00 00 00' + '32 40 48 49 4C 4D 50 51 54 56 57 59 58 5A 5C 5F' + '62 64 65 66 68 69 6A 6A 6B 6D 31 4C 00 00 00 00' + '51 36 3C 4A 4C 4E 50 52 54 57 59 5B 6E 6C 6C 70' + '65 63 68 69 69 6A 6C 6C 72 46 42 5E 00 00 00 00' + '00 36 46 4D 4D 4F 51 53 56 53 4F 5D 5C 5A 5B 5F' + '69 62 62 6B 6B 6D 6E 6E 79 31 4F 00 00 00 00 00' + '00 51 41 4A 4E 50 52 54 5A 3E 61 5D 5F 61 63 65' + '69 64 46 70 6D 6F 70 74 5C 44 60 00 00 00 00 00' + '00 00 48 49 4F 50 53 55 59 4B 50 5D 62 64 07 68' + '64 37 59 6F 6F 70 74 78 40 56 00 00 00 00 00 00' + '00 00 50 46 52 53 53 56 58 5C 5E 4E 48 44 46 48' + '4A 6B 71 6E 70 75 6E 40 55 62 00 00 00 00 00 00' + '00 00 00 41 13 4F 56 56 58 5B 5C 60 66 6A 6C 6D' + '6C 6B 6D 70 72 66 3E 6F 5F 00 00 00 00 00 00 00' + '00 00 00 49 49 4F 55 57 59 5B 5D 5F 61 64 66 69' + '6B 6D 6F 72 73 79 61 5E 64 00 00 00 00 00 00 00' + '00 00 00 00 51 52 55 57 59 5D 58 69 6C 61 68 69' + '6B 6E 70 72 74 7A 59 5C 00 00 00 00 00 00 00 00' + '00 00 00 00 65 54 54 57 59 5D 56 59 5A 58 68 6A' + '6C 6E 70 73 75 7B 53 65 00 00 00 00 00 00 00 00' + '00 00 00 00 00 65 69 61 61 63 66 6A 6A 6A 6A 6D' + '6F 71 73 77 71 69 62 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 80 00 00 01 80 00 00 01 80 00 00 01 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 80 00 00 01 80 00 00 01 C0 00 00 03 C0 00' + '00 03 E0 00 00 07 E0 00 00 07 F0 00 00 0F F0 00' + '00 0F F8 00 00 1F F8 00 00 1F FC 00 00 3F FC 00' + '00 3F FE 00 00 7F FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 28 00' + '00 00 20 00 00 00 40 00 00 00 01 00 20 00 00 00' + '00 00 80 10 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 99 99 99 C7 92 92' + '92 F4 97 97 97 F4 9A 9A 9A F4 A6 A6 A6 F4 AD AD' + 'AD F4 AC AC AC F3 AE AE AE F4 B0 B0 B0 F4 B1 B1' + 'B1 F4 B1 B1 B1 F4 AE AE AE F4 AD AD AD F4 AA AA' + 'AA F5 A7 A7 A7 F5 A5 A5 A5 F4 A0 A0 A0 F3 9C 9C' + '9C F3 99 99 99 F3 95 95 95 F3 8F 8F 8F F3 8C 8C' + '8C F3 88 88 88 F3 86 86 86 F4 82 82 82 F3 7B 7B' + '7B F4 70 70 70 F7 6A 6A 6A F4 68 68 68 F4 71 71' + '71 B8 00 00 00 00 00 00 00 00 A7 A7 A7 FA A6 A6' + 'A6 FF AB AB AB FF B1 B1 B1 FF B1 B1 B1 FF B1 B1' + 'B1 FF B4 B4 B4 FF B7 B7 B7 FF B9 B9 B9 FF BB BB' + 'BB FF BB BB BB FF B9 B9 B9 FF B7 B7 B7 FF B4 B4' + 'B4 FF B2 B2 B2 FF AD AD AD FF AA AA AA FF A7 A7' + 'A7 FF A3 A3 A3 FF A0 A0 A0 FF 9D 9D 9D FF 98 98' + '98 FF 94 94 94 FF 90 90 90 FF 8D 8D 8D FF 88 88' + '88 FF 85 85 85 FF 7B 7B 7B FF 79 79 79 FF 78 78' + '78 F8 00 00 00 00 AC AC AC 3E A8 A8 A8 FE A5 A5' + 'A5 FF B0 B0 B0 FF 7E 7E 7E FF 94 94 94 FF 95 95' + '95 FF 96 96 96 FF 95 95 95 FF 96 96 96 FF 98 98' + '98 FF 95 95 95 FF 90 90 90 FF 8B 8B 8B FF 88 88' + '88 FF 85 85 85 FF 81 81 81 FF 7E 7E 7E FF 7B 7B' + '7B FF 77 77 77 FF 73 73 73 FF 6F 6F 6F FF 6C 6C' + '6C FF 68 68 68 FF 64 64 64 FF 61 61 61 FF 5D 5D' + '5D FF 4D 4D 4D FF 7B 7B 7B FF 74 74 74 FF 74 74' + '74 FE 87 87 87 3E AB AB AB 71 AE AE AE FF AE AE' + 'AE FF 90 90 90 FF B4 B4 B4 FF AC AC AC FF AE AE' + 'AE FF AD AD AD FF B0 B0 B0 FF B3 B3 B3 FF B4 B4' + 'B4 FF AC AC AC FF AA AA AA FF A7 A7 A7 FF A2 A2' + 'A2 FF 9E 9E 9E FF 9A 9A 9A FF 94 94 94 FF 91 91' + '91 FF 8D 8D 8D FF 8A 8A 8A FF 83 83 83 FF 80 80' + '80 FF 7B 7B 7B FF 77 77 77 FF 6F 6F 6F FF 1B DA' + '1B FF 1B DA 1B FF 66 66 66 FF 77 77 77 FF 74 74' + '74 FF 7A 7A 7A 79 9C 9C 9C A8 91 91 91 FF 91 91' + '91 FF 97 97 97 FF AC AC AC FF A9 A9 A9 FF AC AC' + 'AC FF AD AD AD FF B1 B1 B1 FF B2 B2 B2 FF B1 B1' + 'B1 FF AA AA AA FF A8 A8 A8 FF A3 A3 A3 FF 9F 9F' + '9F FF 9B 9B 9B FF 95 95 95 FF 91 91 91 FF 8E 8E' + '8E FF 8B 8B 8B FF 87 87 87 FF 80 80 80 FF 7D 7D' + '7D FF 79 79 79 FF 75 75 75 FF 70 70 70 FF 6C 6C' + '6C FF 66 66 66 FF 5C 5C 5C FF 56 56 56 FF 4E 4E' + '4E FF 67 67 67 B4 96 96 96 D4 93 93 93 FF 87 87' + '87 FF 95 95 95 FF 9D 9D 9D FF A7 A7 A7 FF AB AB' + 'AB FF AE AE AE FF B1 B1 B1 FF AF AF AF FF B0 B0' + 'B0 FF AF AF AF FF B0 B0 B0 FF AE AE AE FF AB AB' + 'AB FF AA AA AA FF A6 A6 A6 FF A4 A4 A4 FF A1 A1' + 'A1 FF 9E 9E 9E FF 9B 9B 9B FF 99 99 99 FF 95 95' + '95 FF 8F 8F 8F FF 8B 8B 8B FF 8A 8A 8A FF 86 86' + '86 FF 7D 7D 7D FF 73 73 73 FF 7A 7A 7A FF 72 72' + '72 FF 9C 9C 9C E4 93 93 93 9A A5 A5 A5 FF 86 86' + '86 FF 8C 8C 8C FF 9B 9B 9B FF A1 A1 A1 FF A3 A3' + 'A3 FF A5 A5 A5 FF A7 A7 A7 FF A8 A8 A8 FF A9 A9' + 'A9 FF AA AA AA FF AD AD AD FF AE AE AE FF B0 B0' + 'B0 FF AE AE AE FF AE AE AE FF AF AF AF FF B0 B0' + 'B0 FF B0 B0 B0 FF B0 B0 B0 FF AE AE AE FF AE AE' + 'AE FF AF AF AF FF B1 B1 B1 FF B0 B0 B0 FF AF AF' + 'AF FF BE BE BE FF B9 B9 B9 FF AA AA AA FF 90 90' + '90 FF BB BB BB 9F 90 90 90 45 A7 A7 A7 FE 6A 6A' + '6A FF 63 63 63 FF 89 89 89 FF 86 86 86 FF 8D 8D' + '8D FF 8F 8F 8F FF 90 90 90 FF 92 92 92 FF 94 94' + '94 FF 94 94 94 FF 96 96 96 FF 95 95 95 FF 97 97' + '97 FF 97 97 97 FF 96 96 96 FF 97 97 97 FF 97 97' + '97 FF 97 97 97 FF 99 99 99 FF 97 97 97 FF 97 97' + '97 FF 98 98 98 FF 99 99 99 FF 9B 9B 9B FF 8F 8F' + '8F FF A4 A4 A4 FF B7 B7 B7 FF B1 B1 B1 FF AB AB' + 'AB F9 A8 A8 A8 2F 00 00 00 00 9C 9C 9C CF 5F 5F' + '5F FF 84 84 84 FF 8E 8E 8E FF A4 A4 A4 FF A5 A5' + 'A5 FF A6 A6 A6 FF A8 A8 A8 FF AA AA AA FF AC AC' + 'AC FF AE AE AE FF B0 B0 B0 FF B2 B2 B2 FF B4 B4' + 'B4 FF B5 B5 B5 FF B7 B7 B7 FF B9 B9 B9 FF BB BB' + 'BB FF BC BC BC FF BE BE BE FF BF BF BF FF C0 C0' + 'C0 FF C1 C1 C1 FF C2 C2 C2 FF C4 C4 C4 FF BB BB' + 'BB FF 90 90 90 FF 93 93 93 FF A1 A1 A1 FF B0 B0' + 'B0 B0 00 00 00 00 00 00 00 00 9D 9D 9D 63 88 88' + '88 FF 94 94 94 FF A6 A6 A6 FF A1 A1 A1 FF A3 A3' + 'A3 FF A5 A5 A5 FF A6 A6 A6 FF A9 A9 A9 FF AB AB' + 'AB FF AD AD AD FF AF AF AF FF B1 B1 B1 FF B3 B3' + 'B3 FF B5 B5 B5 FF B6 B6 B6 FF B8 B8 B8 FF B9 B9' + 'B9 FF BB BB BB FF BC BC BC FF BE BE BE FF BF BF' + 'BF FF C0 C0 C0 FF C0 C0 C0 FF C1 C1 C1 FF C4 C4' + 'C4 FF CF CF CF FF 8E 8E 8E FF 99 99 99 F7 91 91' + '91 33 00 00 00 00 00 00 00 00 00 00 00 00 85 85' + '85 E3 99 99 99 FF A1 A1 A1 FF A2 A2 A2 FF A5 A5' + 'A5 FF A6 A6 A6 FF A9 A9 A9 FF AA AA AA FF AD AD' + 'AD FF AF AF AF FF B0 B0 B0 FF B2 B2 B2 FF B1 B1' + 'B1 FF B3 B3 B3 FF B5 B5 B5 FF B8 B8 B8 FF BB BB' + 'BB FF BD BD BD FF BE BE BE FF BF BF BF FF C1 C1' + 'C1 FF C2 C2 C2 FF C3 C3 C3 FF C3 C3 C3 FF C4 C4' + 'C4 FF C6 C6 C6 FF 8A 8A 8A FF 97 97 97 A5 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 7D 8F 8F 8F FF 95 95 95 FF A3 A3 A3 FF A5 A5' + 'A5 FF A7 A7 A7 FF A9 A9 A9 FF AB AB AB FF AD AD' + 'AD FF B0 B0 B0 FF B2 B2 B2 FF B4 B4 B4 FF C7 C7' + 'C7 FF C5 C5 C5 FF C5 C5 C5 FF C9 C9 C9 FF BE BE' + 'BE FF BC BC BC FF C1 C1 C1 FF C2 C2 C2 FF C2 C2' + 'C2 FF C3 C3 C3 FF C5 C5 C5 FF C5 C5 C5 FF CB CB' + 'CB FF 9F 9F 9F FF 9A 9A 9A F7 8D 8D 8D 2D 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 8A 8A' + '8A 18 8D 8D 8D F3 9F 9F 9F FF A6 A6 A6 FF A6 A6' + 'A6 FF A8 A8 A8 FF AA AA AA FF AC AC AC FF AF AF' + 'AF FF AC AC AC FF A8 A8 A8 FF B6 B6 B6 FF B5 B5' + 'B5 FF B3 B3 B3 FF B4 B4 B4 FF B8 B8 B8 FF C2 C2' + 'C2 FF BB BB BB FF BB BB BB FF C4 C4 C4 FF C4 C4' + 'C4 FF C6 C6 C6 FF C7 C7 C7 FF C7 C7 C7 FF D3 D3' + 'D3 FF 8A 8A 8A FF 9A 9A 9A A3 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 9C 9C 9C 9B 9A 9A 9A FF A3 A3 A3 FF A7 A7' + 'A7 FF A9 A9 A9 FF AB AB AB FF AD AD AD FF B3 B3' + 'B3 FF 97 97 97 FF BA BA BA FF B6 B6 B6 FF B8 B8' + 'B8 FF BA BA BA FF BC BC BC FF BE BE BE FF C2 C2' + 'C2 FF BD BD BD FF 9F 9F 9F FF C9 C9 C9 FF C6 C6' + 'C6 FF C8 C8 C8 FF C9 C9 C9 FF CD CD CD FF B5 B5' + 'B5 FF 9C 9C 9C F7 96 96 96 2C 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 9C 9C 9C 27 A0 A0 A0 F8 A2 A2 A2 FF A8 A8' + 'A8 FF A9 A9 A9 FF AC AC AC FF AE AE AE FF B2 B2' + 'B2 FF A4 A4 A4 FF A9 A9 A9 FF B6 B6 B6 FF BB BB' + 'BB FF BD BD BD FF C0 C0 C0 FF C1 C1 C1 FF BD BD' + 'BD FF 90 90 90 FF B2 B2 B2 FF C8 C8 C8 FF C8 C8' + 'C8 FF C9 C9 C9 FF CD CD CD FF D1 D1 D1 FF 99 99' + '99 FF A6 A6 A6 A2 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 9D 9D 9D AA 9F 9F 9F FF AB AB' + 'AB FF AC AC AC FF AC AC AC FF AF AF AF FF B1 B1' + 'B1 FF B5 B5 B5 FF B7 B7 B7 FF A7 A7 A7 FF A1 A1' + 'A1 FF 9D 9D 9D FF 9F 9F 9F FF A1 A1 A1 FF A3 A3' + 'A3 FF C4 C4 C4 FF CA CA CA FF C7 C7 C7 FF C9 C9' + 'C9 FF CE CE CE FF C7 C7 C7 FF 99 99 99 FF AD AD' + 'AD F2 A1 A1 A1 26 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 A6 A6 A6 37 9A 9A 9A FC 65 65' + '65 FF A8 A8 A8 FF AF AF AF FF AF AF AF FF B1 B1' + 'B1 FF B4 B4 B4 FF B5 B5 B5 FF B9 B9 B9 FF BF BF' + 'BF FF C3 C3 C3 FF C5 C5 C5 FF C6 C6 C6 FF C5 C5' + 'C5 FF C4 C4 C4 FF C6 C6 C6 FF C9 C9 C9 FF CB CB' + 'CB FF BF BF BF FF 97 97 97 FF C8 C8 C8 FF B2 B2' + 'B2 9A 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 98 98 98 BF A2 A2' + 'A2 FF A8 A8 A8 FF AE AE AE FF B0 B0 B0 FF B2 B2' + 'B2 FF B4 B4 B4 FF B6 B6 B6 FF B8 B8 B8 FF BA BA' + 'BA FF BD BD BD FF BF BF BF FF C2 C2 C2 FF C4 C4' + 'C4 FF C6 C6 C6 FF C8 C8 C8 FF CB CB CB FF CC CC' + 'CC FF D3 D3 D3 FF BA BA BA FF B7 B7 B7 F4 AA AA' + 'AA 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 A7 A7 A7 40 AA AA' + 'AA FD AB AB AB FF AE AE AE FF B0 B0 B0 FF B2 B2' + 'B2 FF B6 B6 B6 FF B1 B1 B1 FF C2 C2 C2 FF C5 C5' + 'C5 FF BA BA BA FF C1 C1 C1 FF C2 C2 C2 FF C4 C4' + 'C4 FF C7 C7 C7 FF C9 C9 C9 FF CB CB CB FF CD CD' + 'CD FF D4 D4 D4 FF B2 B2 B2 FE AC AC AC 8F 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 BD BD' + 'BD C6 AD AD AD FE AD AD AD FF B0 B0 B0 FF B2 B2' + 'B2 FF B6 B6 B6 FF AF AF AF FF B2 B2 B2 FF B3 B3' + 'B3 FF B1 B1 B1 FF C1 C1 C1 FF C3 C3 C3 FF C5 C5' + 'C5 FF C7 C7 C7 FF C9 C9 C9 FF CC CC CC FF CE CE' + 'CE FF D8 D8 D8 FF AB AB AB F0 AD AD AD 16 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 B6 B6' + 'B6 38 BD BD BD BE C3 C3 C3 CD BA BA BA EC BA BA' + 'BA F4 BC BC BC F4 BF BF BF F4 C3 C3 C3 F4 C3 C3' + 'C3 F2 C3 C3 C3 F2 C3 C3 C3 F2 C6 C6 C6 F4 C8 C8' + 'C8 F4 CA CA CA F4 CD CD CD F4 D1 D1 D1 F4 CC CC' + 'CC D3 C2 C2 C2 CD B3 B3 B3 65 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF 80 00 00 01 80 00' + '00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 80 00 00 01 80 00' + '00 01 C0 00 00 03 C0 00 00 03 C0 00 00 07 E0 00' + '00 07 E0 00 00 0F F0 00 00 0F F0 00 00 1F F8 00' + '00 1F F8 00 00 3F FC 00 00 3F FC 00 00 7F FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 04 00 00 00 00 00 80 04 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 00 00 77 77 78 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 77 77 77 00 08 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 80 08 88 88 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 88 88 80 08 88' + '87 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 8A AA 78 88 80 08 88 87 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 78 88 80 87 77' + '78 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 87 77 78 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 87' + '77 7F FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 77 77 78 88 88 78 88 F7 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 F8 88 87 88 07 88' + '8F 78 FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF F8 7F 88 88 70 08 88 F7 8F 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 7F 87 F8 88 80 08 8F' + '78 F7 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 87 F8 7F 88 00 00 87 8F 78 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 7F 87 80 00 00 87' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 87 80 00 00 08 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 78 00 00 00 08' + '78 88 88 88 88 88 88 88 88 FF FF 88 88 88 88 88' + '88 88 88 78 00 00 00 00 87 88 88 88 88 88 88 8F' + 'FF 88 88 FF F8 88 88 88 88 88 87 80 00 00 00 00' + '87 88 88 88 88 88 88 F8 88 88 88 88 8F 88 88 88' + '88 88 87 80 00 00 00 00 08 78 88 88 88 88 8F 88' + '88 88 88 88 88 F8 88 88 88 88 78 00 00 00 00 00' + '08 78 88 88 88 88 87 88 88 88 88 88 88 78 88 88' + '88 88 78 00 00 00 00 00 00 87 88 88 88 88 88 78' + '88 88 88 88 87 88 88 88 88 87 80 00 00 00 00 00' + '00 87 88 88 88 88 88 87 77 88 88 77 78 88 88 88' + '88 87 80 00 00 00 00 00 00 08 77 88 88 88 88 88' + '88 77 77 88 88 88 88 88 87 78 00 00 00 00 00 00' + '00 08 78 78 88 88 88 88 88 88 88 88 88 88 88 88' + '78 78 00 00 00 00 00 00 00 00 87 88 88 88 88 88' + '88 88 88 88 88 88 88 88 87 80 00 00 00 00 00 00' + '00 00 87 88 88 88 88 8F FF 88 88 88 88 88 88 88' + '87 80 00 00 00 00 00 00 00 00 08 78 88 88 88 77' + '77 78 88 88 88 88 88 88 78 00 00 00 00 00 00 00' + '00 00 08 78 88 88 88 88 88 88 88 88 88 88 88 88' + '78 00 00 00 00 00 00 00 00 00 00 87 77 77 77 77' + '77 77 77 77 77 77 77 77 80 00 00 00 00 00 00 00' + '00 00 00 08 88 88 88 88 88 88 88 88 88 88 88 88' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -6350,31 +10776,66 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' - 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF 00' - '00 00 00 7F 00 00 FC 00 00 00 00 1F 00 00 F8 00' - '00 00 00 1F 00 00 F8 00 00 00 00 1F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F8 00 00 00 00 1F 00 00 F8 00' - '00 00 00 1F 00 00 F8 00 00 00 00 1F 00 00 F8 00' - '00 00 00 1F 00 00 F8 00 00 00 00 1F 00 00 F8 00' - '00 00 00 1F 00 00 FC 00 00 00 00 3F 00 00 FC 00' - '00 00 00 3F 00 00 FC 00 00 00 00 3F 00 00 FC 00' - '00 00 00 3F 00 00 FC 00 00 00 00 3F 00 00 FC 00' - '00 00 00 7F 00 00 FE 00 00 00 00 7F 00 00 FE 00' - '00 00 00 7F 00 00 FE 00 00 00 00 7F 00 00 FE 00' - '00 00 00 7F 00 00 FE 00 00 00 00 7F 00 00 FF 00' - '00 00 00 7F 00 00 FF 00 00 00 00 FF 00 00 FF 00' - '00 00 00 FF 00 00 FF 00 00 00 01 FF 00 00 FF 80' - '00 00 03 FF 00 00 FF E0 00 00 07 FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 C0 00' + '00 00 00 03 00 00 C0 00 00 00 00 03 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 03 00 00 C0 00 00 00 00 07 00 00 C0 00' + '00 00 00 07 00 00 E0 00 00 00 00 0F 00 00 E0 00' + '00 00 00 0F 00 00 F0 00 00 00 00 1F 00 00 F0 00' + '00 00 00 1F 00 00 F8 00 00 00 00 3F 00 00 F8 00' + '00 00 00 3F 00 00 FC 00 00 00 00 7F 00 00 FC 00' + '00 00 00 7F 00 00 FE 00 00 00 00 FF 00 00 FE 00' + '00 00 00 FF 00 00 FF 00 00 00 01 FF 00 00 FF 00' + '00 00 01 FF 00 00 FF 80 00 00 03 FF 00 00 FF 80' + '00 00 03 FF 00 00 FF C0 00 00 07 FF 00 00 FF E0' + '00 00 0F FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 28 00' - '00 00 30 00 00 00 60 00 00 00 01 00 20 00 00 00' - '00 00 00 24 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 30 00 00 00 60 00 00 00 01 00 08 00 00 00' + '00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 C0 C0 C0 00 C0 DC C0 00 F0 CA A6 00 4F 4F' + '4F 00 52 52 52 00 56 56 56 00 57 57 57 00 58 58' + '58 00 59 59 59 00 5A 5A 5A 00 5B 5B 5B 00 5C 5C' + '5C 00 5F 5F 5F 00 60 60 60 00 61 61 61 00 62 62' + '62 00 64 64 64 00 66 66 66 00 67 67 67 00 68 68' + '68 00 6A 6A 6A 00 6B 6B 6B 00 6C 6C 6C 00 6D 6D' + '6D 00 6E 6E 6E 00 6F 6F 6F 00 70 70 70 00 71 71' + '71 00 72 72 72 00 73 73 73 00 74 74 74 00 75 75' + '75 00 76 76 76 00 77 77 77 00 78 78 78 00 79 79' + '79 00 7A 7A 7A 00 7B 7B 7B 00 7C 7C 7C 00 7D 7D' + '7D 00 7E 7E 7E 00 7F 7F 7F 00 33 B2 33 00 37 B6' + '37 00 1A D9 1A 00 FF 00 FF 00 80 80 80 00 81 81' + '81 00 82 82 82 00 83 83 83 00 84 84 84 00 85 85' + '85 00 86 86 86 00 87 87 87 00 88 88 88 00 89 89' + '89 00 8A 8A 8A 00 8B 8B 8B 00 8C 8C 8C 00 8D 8D' + '8D 00 8E 8E 8E 00 8F 8F 8F 00 90 90 90 00 91 91' + '91 00 92 92 92 00 93 93 93 00 94 94 94 00 95 95' + '95 00 96 96 96 00 97 97 97 00 98 98 98 00 99 99' + '99 00 9A 9A 9A 00 9B 9B 9B 00 9C 9C 9C 00 9D 9D' + '9D 00 9E 9E 9E 00 9F 9F 9F 00 A0 A0 A0 00 A1 A1' + 'A1 00 A2 A2 A2 00 A3 A3 A3 00 A4 A4 A4 00 A5 A5' + 'A5 00 A6 A6 A6 00 A7 A7 A7 00 A8 A8 A8 00 A9 A9' + 'A9 00 AA AA AA 00 AB AB AB 00 AC AC AC 00 AD AD' + 'AD 00 AE AE AE 00 AF AF AF 00 B0 B0 B0 00 B1 B1' + 'B1 00 B2 B2 B2 00 B3 B3 B3 00 B4 B4 B4 00 B5 B5' + 'B5 00 B6 B6 B6 00 B7 B7 B7 00 B8 B8 B8 00 B9 B9' + 'B9 00 BA BA BA 00 BB BB BB 00 BC BC BC 00 BD BD' + 'BD 00 BE BE BE 00 BF BF BF 00 C0 C0 C0 00 C1 C1' + 'C1 00 C2 C2 C2 00 C3 C3 C3 00 C4 C4 C4 00 C5 C5' + 'C5 00 C6 C6 C6 00 C7 C7 C7 00 C8 C8 C8 00 C9 C9' + 'C9 00 CA CA CA 00 CB CB CB 00 CC CC CC 00 CD CD' + 'CD 00 CE CE CE 00 CF CF CF 00 D0 D0 D0 00 D1 D1' + 'D1 00 D2 D2 D2 00 D3 D3 D3 00 D5 D5 D5 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -6401,6 +10862,10 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 FB' + 'FF 00 A4 A0 A0 00 80 80 80 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -6424,506 +10889,99 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 16 00 00 00 34 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 35 00 00 00 18 00 00 00 04 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 05 00 00 00 69 00 00 00 C5 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F1 00 00 00 B1 00 00 00 5C 00 00 00 0D 00 00' - '00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00' - '00 9B 00 00 00 FF 00 00 00 FF 21 21 21 FF 2C 2C' - '2C FF 2B 2B 2B FF 2C 2C 2C FF 2B 2B 2B FF 2B 2B' - '2B FF 2B 2B 2B FF 2B 2B 2B FF 2B 2B 2B FF 2B 2B' - '2B FF 2B 2B 2B FF 2B 2B 2B FF 2B 2B 2B FF 2B 2B' - '2B FF 2B 2B 2B FF 2A 2A 2A FF 2A 2A 2A FF 2A 2A' - '2A FF 2A 2A 2A FF 2A 2A 2A FF 2A 2A 2A FF 2A 2A' - '2A FF 2A 2A 2A FF 2A 2A 2A FF 2B 2B 2B FF 2B 2B' - '2B FF 2B 2B 2B FF 2E 2C 2D FF 2E 2C 2D FF 13 13' - '13 FF 00 00 00 FF 00 00 00 F6 00 00 00 69 00 00' - '00 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 54 00 00' - '00 FF 26 26 26 FF 90 90 90 FF CA CA CA FF CD CD' - 'CD FF CB CB CB FF CE CE CE FF CD CD CD FF CC CC' - 'CC FF CB CB CB FF C9 C9 C9 FF C7 C7 C7 FF C6 C6' - 'C6 FF C5 C5 C5 FF C4 C4 C4 FF C3 C3 C3 FF C2 C2' - 'C2 FF C2 C2 C2 FF C3 C3 C3 FF C0 C0 C0 FF C1 C1' - 'C1 FF BF BF BF FF BF BF BF FF BC BC BC FF BD BD' - 'BD FF BA BA BA FF BB BB BB FF B5 B5 B5 FF B3 B4' - 'B3 FF B8 B4 B6 FF AD AD AD FF AB AC AC FF AD A8' - 'AB FF 63 63 63 FF 09 09 09 FF 00 00 00 EF 00 00' - '00 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 03 00 00 00 AD 01 01' - '01 FF 8A 8A 8A FF EB EB EB FF E8 E8 E8 FF E7 E7' - 'E7 FF D5 D5 D5 FF 85 85 85 FF 88 88 88 FF 92 92' - '92 FF 98 98 98 FF A2 A2 A2 FF AD AD AD FF BA BA' - 'BA FF C0 C0 C0 FF C3 C3 C3 FF C6 C6 C6 FF CB CB' - 'CB FF CA CA CA FF CA CA CA FF D6 D6 D6 FF CA CA' - 'CA FF D3 D3 D3 FF CB CB CB FF D7 D7 D7 FF CC CC' - 'CC FF D4 D4 D4 FF C7 C7 C7 FF C8 C9 C9 FF C6 C2' - 'C4 FF 9D B3 A8 FF 20 A8 63 FF 1C A8 60 FF 90 AF' - 'A0 FF BF BB BD FF 4F 4F 4F FF 00 00 00 FF 00 00' - '00 81 00 00 00 04 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 15 00 00 00 F0 19 19' - '19 FF C7 C7 C7 FF EC EC EC FF E2 E2 E2 FF E6 E6' - 'E6 FF CB CB CB FF 3B 3B 3B FF 3F 3F 3F FF 56 56' - '56 FF 72 72 72 FF 87 87 87 FF 92 92 92 FF 9D 9D' - '9D FF A5 A5 A5 FF A8 A8 A8 FF AC AC AC FF B7 B7' - 'B7 FF A9 A9 A9 FF 9C 9C 9C FF BD BD BD FF 99 99' - '99 FF B7 B7 B7 FF A1 A1 A1 FF C3 C3 C3 FF A3 A3' - 'A3 FF BD BD BD FF 98 98 98 FF C5 C4 C4 FF C7 BF' - 'C3 FF 51 A7 7C FF 17 CE 71 FF 0A BF 61 FF 2F 99' - '64 FF C3 BD C0 FF 84 83 84 FF 01 01 01 FF 00 00' - '00 A3 00 00 00 0B 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 30 00 00 00 FA 29 29' - '29 FF D1 D1 D1 FF EA EA EA FF E4 E4 E4 FF E7 E7' - 'E7 FF CD CD CD FF 47 47 47 FF 5F 5F 5F FF 79 79' - '79 FF 87 87 87 FF 8F 8F 8F FF 97 97 97 FF A0 A0' - 'A0 FF A7 A7 A7 FF AA AA AA FF AE AE AE FF B9 B9' - 'B9 FF A4 A4 A4 FF 90 90 90 FF B8 B8 B8 FF 8A 8A' - '8A FF AF AF AF FF 94 94 94 FF BF BF BF FF 97 97' - '97 FF B7 B7 B7 FF 88 88 88 FF C4 C3 C3 FF C6 C3' - 'C5 FF 95 B2 A3 FF 91 D6 B4 FF 91 D8 B5 FF 83 AB' - '97 FF C4 C1 C3 FF 8E 8E 8E FF 01 01 01 FF 00 00' - '00 B7 00 00 00 13 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 34 00 00 00 F9 28 28' - '28 FF D1 D1 D1 FF EC EC EC FF E6 E6 E6 FF E7 E7' - 'E7 FF D1 D1 D1 FF 68 68 68 FF 74 74 74 FF 7E 7E' - '7E FF 85 85 85 FF 8E 8E 8E FF 97 97 97 FF A0 A0' - 'A0 FF A7 A7 A7 FF AA AA AA FF AE AE AE FF BA BA' - 'BA FF 9F 9F 9F FF 83 83 83 FF B3 B3 B3 FF 7C 7C' - '7C FF A8 A8 A8 FF 87 87 87 FF BB BB BB FF 8A 8A' - '8A FF B2 B2 B2 FF 79 79 79 FF C2 C2 C2 FF C4 C4' - 'C4 FF C6 C3 C4 FF 99 AC A2 FF 96 AB A0 FF C2 BE' - 'C0 FF C4 C4 C4 FF 8D 8D 8D FF 01 01 01 FF 00 00' - '00 BA 00 00 00 14 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 33 00 00 00 F9 28 28' - '28 FF D1 D1 D1 FF EE EE EE FF E7 E7 E7 FF E8 E8' - 'E8 FF D6 D6 D6 FF 70 70 70 FF 71 71 71 FF 7D 7D' - '7D FF 85 85 85 FF 8E 8E 8E FF 97 97 97 FF A0 A0' - 'A0 FF A7 A7 A7 FF AA AA AA FF AE AE AE FF BB BB' - 'BB FF 98 98 98 FF 73 73 73 FF AD AD AD FF 6A 6A' - '6A FF A0 A0 A0 FF 78 78 78 FF B7 B7 B7 FF 7C 7C' - '7C FF AB AB AB FF 67 67 67 FF C2 C2 C2 FF C6 C6' - 'C6 FF C4 C3 C4 FF C7 C4 C5 FF C6 C2 C4 FF BF BF' - 'BF FF C5 C5 C5 FF 8D 8D 8D FF 01 01 01 FF 00 00' - '00 B9 00 00 00 14 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 33 00 00 00 F9 27 27' - '27 FF D1 D1 D1 FF EF EF EF FF E9 E9 E9 FF EB EB' - 'EB FF D6 D6 D6 FF 6A 6A 6A FF 6C 6C 6C FF 78 78' - '78 FF 81 81 81 FF 8A 8A 8A FF 93 93 93 FF 9C 9C' - '9C FF A3 A3 A3 FF A6 A6 A6 FF AB AB AB FF B9 B9' - 'B9 FF 90 90 90 FF 66 66 66 FF A5 A5 A5 FF 5B 5B' - '5B FF 98 98 98 FF 6B 6B 6B FF B1 B1 B1 FF 70 70' - '70 FF A4 A4 A4 FF 58 58 58 FF C1 C1 C1 FF C8 C8' - 'C8 FF C5 C5 C5 FF C3 C3 C3 FF C2 C2 C2 FF C1 C1' - 'C1 FF C6 C6 C6 FF 8E 8E 8E FF 01 01 01 FF 00 00' - '00 B9 00 00 00 14 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 33 00 00 00 F9 27 27' - '27 FF D2 D2 D2 FF F2 F2 F2 FF EB EB EB FF EA EA' - 'EA FF DF DF DF FF A7 A7 A7 FF A7 A7 A7 FF AB AB' - 'AB FF AE AE AE FF B0 B0 B0 FF B3 B3 B3 FF B6 B6' - 'B6 FF B9 B9 B9 FF B9 B9 B9 FF BA BA BA FF BD BD' - 'BD FF B7 B7 B7 FF B1 B1 B1 FF BE BE BE FF AF AF' - 'AF FF BA BA BA FF B1 B1 B1 FF BE BE BE FF B0 B0' - 'B0 FF BA BA BA FF AB AB AB FF C5 C5 C5 FF C8 C8' - 'C8 FF C6 C6 C6 FF C4 C4 C4 FF C3 C3 C3 FF C2 C2' - 'C2 FF C9 C9 C9 FF 8E 8E 8E FF 00 00 00 FF 00 00' - '00 B9 00 00 00 14 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 33 00 00 00 F9 26 26' - '26 FF D3 D3 D3 FF F3 F3 F3 FF EB EB EB FF EB EB' - 'EB FF EA EA EA FF ED ED ED FF EB EB EB FF E9 E9' - 'E9 FF E8 E8 E8 FF E6 E6 E6 FF E4 E4 E4 FF E2 E2' - 'E2 FF E1 E1 E1 FF DF DF DF FF DE DE DE FF DB DB' - 'DB FF DB DB DB FF DC DC DC FF D8 D8 D8 FF D9 D9' - 'D9 FF D6 D6 D6 FF D6 D6 D6 FF D2 D2 D2 FF D2 D2' - 'D2 FF CF CF CF FF D0 D0 D0 FF CB CB CB FF CA CA' - 'CA FF C9 C9 C9 FF C7 C7 C7 FF C4 C4 C4 FF C0 C0' - 'C0 FF CA CA CA FF 8E 8E 8E FF 00 00 00 FF 00 00' - '00 B9 00 00 00 14 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 33 00 00 00 F9 26 26' - '26 FF D3 D3 D3 FF F7 F7 F7 FF F8 F8 F8 FF F6 F6' - 'F6 FF F6 F6 F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5' - 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3 F3 FF F3 F3' - 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F2 F2 F2 FF F1 F1' - 'F1 FF F1 F1 F1 FF F1 F1 F1 FF F0 F0 F0 FF F0 F0' - 'F0 FF F0 F0 F0 FF EF EF EF FF EF EF EF FF EE EE' - 'EE FF EE EE EE FF ED ED ED FF ED ED ED FF ED ED' - 'ED FF EC EC EC FF EC EC EC FF EB EB EB FF E0 E0' - 'E0 FF D0 D0 D0 FF 8D 8D 8D FF 00 00 00 FF 00 00' - '00 B9 00 00 00 14 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 33 00 00 00 F9 24 24' - '24 FF D6 D6 D6 FF FF FF FF FF E9 E9 E9 FF E1 E1' - 'E1 FF E2 E2 E2 FF E2 E2 E2 FF E0 E0 E0 FF DF DF' - 'DF FF DF DF DF FF DF DF DF FF DF DF DF FF DF DF' - 'DF FF E0 E0 E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0 E0 FF E2 E2' - 'E2 FF E3 E3 E3 FF E3 E3 E3 FF E3 E3 E3 FF F8 F8' - 'F8 FF EF EF EF FF 8C 8C 8C FF 00 00 00 FF 00 00' - '00 B9 00 00 00 14 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 34 00 00 00 FA 24 24' - '24 FF DE DE DE FF F8 F8 F8 FF D9 D9 D9 FF C4 C4' - 'C4 FF BB BB BB FF BF BF BF FF D8 D8 D8 FF DA DA' - 'DA FF D9 D9 D9 FF D9 D9 D9 FF D9 D9 D9 FF D9 D9' - 'D9 FF D9 D9 D9 FF D9 D9 D9 FF D9 D9 D9 FF D9 D9' - 'D9 FF D9 D9 D9 FF D9 D9 D9 FF D9 D9 D9 FF D9 D9' - 'D9 FF D9 D9 D9 FF D9 D9 D9 FF D9 D9 D9 FF D9 D9' - 'D9 FF D9 D9 D9 FF D9 D9 D9 FF DB DB DB FF CF CF' - 'CF FF BC BC BC FF BB BB BB FF D1 D1 D1 FF DE DE' - 'DE FF FF FF FF FF 9A 9A 9A FF 00 00 00 FF 00 00' - '00 BA 00 00 00 14 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 25 00 00 00 F7 1F 1F' - '1F FF DA DA DA FF F3 F3 F3 FF C6 C6 C6 FF A9 A9' - 'A9 FF B7 B7 B7 FF B0 B0 B0 FF C3 C3 C3 FF DA DA' - 'DA FF D8 D8 D8 FF D8 D8 D8 FF D8 D8 D8 FF D8 D8' - 'D8 FF D8 D8 D8 FF D7 D7 D7 FF D7 D7 D7 FF D7 D7' - 'D7 FF D7 D7 D7 FF D7 D7 D7 FF D7 D7 D7 FF D7 D7' - 'D7 FF D7 D7 D7 FF D8 D8 D8 FF D8 D8 D8 FF D8 D8' - 'D8 FF D8 D8 D8 FF D9 D9 D9 FF D2 D2 D2 FF A8 A8' - 'A8 FF B6 B6 B6 FF B1 B1 B1 FF B5 B5 B5 FF D8 D8' - 'D8 FF FC FC FC FF 9C 9C 9C FF 00 00 00 FF 00 00' - '00 AE 00 00 00 0C 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 07 00 00 00 D0 05 05' - '05 FF C2 C2 C2 FF F6 F6 F6 FF BC BC BC FF B2 B2' - 'B2 FF C3 C3 C3 FF AB AB AB FF BB BB BB FF D8 D8' - 'D8 FF D5 D5 D5 FF D5 D5 D5 FF D5 D5 D5 FF D4 D4' - 'D4 FF D3 D3 D3 FF D3 D3 D3 FF D5 D5 D5 FF D7 D7' - 'D7 FF D8 D8 D8 FF D8 D8 D8 FF D7 D7 D7 FF D5 D5' - 'D5 FF D3 D3 D3 FF D3 D3 D3 FF D3 D3 D3 FF D4 D4' - 'D4 FF D5 D5 D5 FF D7 D7 D7 FF C9 C9 C9 FF A6 A6' - 'A6 FF C3 C3 C3 FF B3 B3 B3 FF AD AD AD FF D6 D6' - 'D6 FF F5 F5 F5 FF 76 76 76 FF 00 00 00 FF 00 00' - '00 8C 00 00 00 01 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 9C 00 00' - '00 FF A4 A4 A4 FF F4 F4 F4 FF CF CF CF FF C5 C5' - 'C5 FF D2 D2 D2 FF BF BF BF FF C9 C9 C9 FF D3 D3' - 'D3 FF D2 D2 D2 FF D0 D0 D0 FF D3 D3 D3 FF DD DD' - 'DD FF E1 E1 E1 FF E3 E3 E3 FF E6 E6 E6 FF EA EA' - 'EA FF ED ED ED FF ED ED ED FF EB EB EB FF E7 E7' - 'E7 FF E4 E4 E4 FF E2 E2 E2 FF DE DE DE FF D4 D4' - 'D4 FF D0 D0 D0 FF D2 D2 D2 FF D3 D3 D3 FF C5 C5' - 'C5 FF D0 D0 D0 FF C2 C2 C2 FF BF BF BF FF D9 D9' - 'D9 FF E8 E8 E8 FF 56 56 56 FF 00 00 00 FF 00 00' - '00 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 73 00 00' - '00 FF 7E 7E 7E FF EF EF EF FF D1 D1 D1 FF CF CF' - 'CF FF CB CB CB FF CF CF CF FF D1 D1 D1 FF CE CE' - 'CE FF CF CF CF FF DF DF DF FF F0 F0 F0 FF EC EC' - 'EC FF E1 E1 E1 FF DE DE DE FF DD DD DD FF DC DC' - 'DC FF DB DB DB FF DB DB DB FF DC DC DC FF DD DD' - 'DD FF DE DE DE FF E1 E1 E1 FF EC EC EC FF F1 F1' - 'F1 FF E1 E1 E1 FF D1 D1 D1 FF CE CE CE FF D0 D0' - 'D0 FF CB CB CB FF CE CE CE FF D1 D1 D1 FF D9 D9' - 'D9 FF D4 D4 D4 FF 3F 3F 3F FF 00 00 00 FF 00 00' - '00 55 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 59 00 00' - '00 FF 59 59 59 FF E9 E9 E9 FF D0 D0 D0 FF CD CD' - 'CD FF CD CD CD FF CD CD CD FF CA CA CA FF D6 D6' - 'D6 FF ED ED ED FF E6 E6 E6 FF DC DC DC FF D4 D4' - 'D4 FF D5 D5 D5 FF D6 D6 D6 FF D6 D6 D6 FF D6 D6' - 'D6 FF D6 D6 D6 FF D6 D6 D6 FF D6 D6 D6 FF D6 D6' - 'D6 FF D6 D6 D6 FF D5 D5 D5 FF D5 D5 D5 FF DB DB' - 'DB FF E4 E4 E4 FF ED ED ED FF DA DA DA FF CA CA' - 'CA FF CD CD CD FF CD CD CD FF CB CB CB FF DF DF' - 'DF FF B8 B8 B8 FF 22 22 22 FF 00 00 00 FB 00 00' - '00 3A 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 3E 00 00' - '00 FE 40 40 40 FF D7 D7 D7 FF D2 D2 D2 FF CA CA' - 'CA FF CB CB CB FF CA CA CA FF D4 D4 D4 FF E9 E9' - 'E9 FF DC DC DC FF D0 D0 D0 FF D1 D1 D1 FF D2 D2' - 'D2 FF D2 D2 D2 FF D2 D2 D2 FF D2 D2 D2 FF D2 D2' - 'D2 FF D2 D2 D2 FF D2 D2 D2 FF D2 D2 D2 FF D2 D2' - 'D2 FF D2 D2 D2 FF D2 D2 D2 FF D2 D2 D2 FF D1 D1' - 'D1 FF D0 D0 D0 FF D8 D8 D8 FF EA EA EA FF D8 D8' - 'D8 FF C9 C9 C9 FF CA CA CA FF C9 C9 C9 FF E0 E0' - 'E0 FF A0 A0 A0 FF 08 08 08 FF 00 00 00 EB 00 00' - '00 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 25 00 00' - '00 F8 22 22 22 FF B7 B7 B7 FF DB DB DB FF CA CA' - 'CA FF CB CB CB FF CE CE CE FF E4 E4 E4 FF D4 D4' - 'D4 FF CF CF CF FF D1 D1 D1 FF D0 D0 D0 FF D0 D0' - 'D0 FF D0 D0 D0 FF D0 D0 D0 FF CF CF CF FF CD CD' - 'CD FF CE CE CE FF CE CE CE FF CE CE CE FF CE CE' - 'CE FF CE CE CE FF CE CE CE FF CE CE CE FF CE CE' - 'CE FF CE CE CE FF CC CC CC FF D0 D0 D0 FF E2 E2' - 'E2 FF CF CF CF FF C7 C7 C7 FF C9 C9 C9 FF E2 E2' - 'E2 FF 80 80 80 FF 00 00 00 FF 00 00 00 C2 00 00' - '00 0E 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00 00' - '00 E1 07 07 07 FF 9F 9F 9F FF DD DD DD FF CC CC' - 'CC FF CD CD CD FF DB DB DB FF D6 D6 D6 FF CE CE' - 'CE FF D0 D0 D0 FF D0 D0 D0 FF D0 D0 D0 FF D0 D0' - 'D0 FF D0 D0 D0 FF D0 D0 D0 FF CF CF CF FF CC CC' - 'CC FF C9 C9 C9 FF CA CA CA FF CA CA CA FF CA CA' - 'CA FF CA CA CA FF CA CA CA FF CA CA CA FF CA CA' - 'CA FF CA CA CA FF CA CA CA FF C8 C8 C8 FF CF CF' - 'CF FF DA DA DA FF C7 C7 C7 FF C9 C9 C9 FF E2 E2' - 'E2 FF 5A 5A 5A FF 00 00 00 FF 00 00 00 94 00 00' - '00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 B2 00 00 00 FF 83 83 83 FF DD DD DD FF CF CF' - 'CF FF D1 D1 D1 FF DB DB DB FF D1 D1 D1 FF D0 D0' - 'D0 FF D0 D0 D0 FF D0 D0 D0 FF D0 D0 D0 FF D0 D0' - 'D0 FF D0 D0 D0 FF D0 D0 D0 FF D0 D0 D0 FF CF CF' - 'CF FF C9 C9 C9 FF C5 C5 C5 FF C6 C6 C6 FF C6 C6' - 'C6 FF C6 C6 C6 FF C6 C6 C6 FF C6 C6 C6 FF C6 C6' - 'C6 FF C6 C6 C6 FF C6 C6 C6 FF C6 C6 C6 FF C7 C7' - 'C7 FF D3 D3 D3 FF C8 C8 C8 FF C9 C9 C9 FF DE DE' - 'DE FF 42 42 42 FF 00 00 00 FF 00 00 00 6C 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 80 00 00 00 FF 5F 5F 5F FF DC DC DC FF D1 D1' - 'D1 FF D2 D2 D2 FF D4 D4 D4 FF D1 D1 D1 FF D1 D1' - 'D1 FF D1 D1 D1 FF D1 D1 D1 FF D1 D1 D1 FF D1 D1' - 'D1 FF D1 D1 D1 FF D1 D1 D1 FF D1 D1 D1 FF D1 D1' - 'D1 FF D0 D0 D0 FF C8 C8 C8 FF C3 C3 C3 FF C3 C3' - 'C3 FF C3 C3 C3 FF C4 C4 C4 FF C4 C4 C4 FF C4 C4' - 'C4 FF C4 C4 C4 FF C4 C4 C4 FF C4 C4 C4 FF C4 C4' - 'C4 FF C7 C7 C7 FF C5 C5 C5 FF CB CB CB FF CC CC' - 'CC FF 2D 2D 2D FF 00 00 00 F8 00 00 00 40 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 4B 00 00 00 FE 41 41 41 FF D8 D8 D8 FF D4 D4' - 'D4 FF D2 D2 D2 FF D0 D0 D0 FF D3 D3 D3 FF D3 D3' - 'D3 FF D3 D3 D3 FF D3 D3 D3 FF D3 D3 D3 FF D3 D3' - 'D3 FF D3 D3 D3 FF D3 D3 D3 FF D3 D3 D3 FF D3 D3' - 'D3 FF D3 D3 D3 FF D3 D3 D3 FF CD CD CD FF C7 C7' - 'C7 FF C4 C4 C4 FF C2 C2 C2 FF C1 C1 C1 FF C0 C0' - 'C0 FF C1 C1 C1 FF C1 C1 C1 FF C1 C1 C1 FF C2 C2' - 'C2 FF BF BF BF FF C0 C0 C0 FF CE CE CE FF B2 B2' - 'B2 FF 1A 1A 1A FF 00 00 00 E5 00 00 00 28 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 1A 00 00 00 F2 2C 2C 2C FF CC CC CC FF D6 D6' - 'D6 FF D5 D5 D5 FF CC CC CC FF D5 D5 D5 FF D6 D6' - 'D6 FF D6 D6 D6 FF D6 D6 D6 FF D6 D6 D6 FF D6 D6' - 'D6 FF D6 D6 D6 FF D6 D6 D6 FF D6 D6 D6 FF D6 D6' - 'D6 FF D6 D6 D6 FF D6 D6 D6 FF D7 D7 D7 FF D6 D6' - 'D6 FF D5 D5 D5 FF D3 D3 D3 FF CF CF CF FF CC CC' - 'CC FF C9 C9 C9 FF C6 C6 C6 FF C4 C4 C4 FF C2 C2' - 'C2 FF B3 B3 B3 FF BD BD BD FF D0 D0 D0 FF 99 99' - '99 FF 09 09 09 FF 00 00 00 CF 00 00 00 19 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 09 00 00 00 D8 18 18 18 FF B0 B0 B0 FF DB DB' - 'DB FF DB DB DB FF D0 D0 D0 FF D2 D2 D2 FF DB DB' - 'DB FF D9 D9 D9 FF DA DA DA FF DA DA DA FF DA DA' - 'DA FF DA DA DA FF DA DA DA FF DA DA DA FF DA DA' - 'DA FF DA DA DA FF DA DA DA FF DA DA DA FF DA DA' - 'DA FF DA DA DA FF DA DA DA FF DB DB DB FF DB DB' - 'DB FF DA DA DA FF D9 D9 D9 FF D9 D9 D9 FF CF CF' - 'CF FF C5 C5 C5 FF CC CC CC FF CA CA CA FF 7A 7A' - '7A FF 02 02 02 FF 00 00 00 B8 00 00 00 0A 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 02 00 00 00 BF 08 08 08 FF 97 97 97 FF DC DC' - 'DC FF DF DF DF FF DC DC DC FF CE CE CE FF D6 D6' - 'D6 FF DE DE DE FF DD DD DD FF DD DD DD FF DD DD' - 'DD FF DD DD DD FF DD DD DD FF DD DD DD FF DD DD' - 'DD FF DD DD DD FF DD DD DD FF DD DD DD FF DD DD' - 'DD FF DD DD DD FF DD DD DD FF DD DD DD FF DD DD' - 'DD FF DD DD DD FF DE DE DE FF D8 D8 D8 FF CE CE' - 'CE FF DD DD DD FF DC DC DC FF C6 C6 C6 FF 53 53' - '53 FF 00 00 00 FF 00 00 00 A0 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 A4 00 00 00 FF 77 77 77 FF D3 D3' - 'D3 FF E2 E2 E2 FF E1 E1 E1 FF DE DE DE FF CE CE' - 'CE FF D7 D7 D7 FF E1 E1 E1 FF E2 E2 E2 FF E0 E0' - 'E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF E0 E0 E0 FF E0 E0 E0 FF E1 E1' - 'E1 FF E1 E1 E1 FF D9 D9 D9 FF CD CD CD FF DA DA' - 'DA FF E3 E3 E3 FF D9 D9 D9 FF BB BB BB FF 39 39' - '39 FF 00 00 00 FF 00 00 00 7E 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 89 00 00 00 FF 4B 4B 4B FF C9 C9' - 'C9 FF E5 E5 E5 FF E3 E3 E3 FF E4 E4 E4 FF E1 E1' - 'E1 FF D7 D7 D7 FF D2 D2 D2 FF DA DA DA FF E4 E4' - 'E4 FF E5 E5 E5 FF E4 E4 E4 FF E4 E4 E4 FF E4 E4' - 'E4 FF E4 E4 E4 FF E4 E4 E4 FF E4 E4 E4 FF E4 E4' - 'E4 FF E4 E4 E4 FF E5 E5 E5 FF E5 E5 E5 FF DC DC' - 'DC FF D2 D2 D2 FF D6 D6 D6 FF E0 E0 E0 FF E4 E4' - 'E4 FF E6 E6 E6 FF D6 D6 D6 FF A8 A8 A8 FF 20 20' - '20 FF 00 00 00 FD 00 00 00 4E 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 62 00 00 00 FF 35 35 35 FF BD BD' - 'BD FF E6 E6 E6 FF E6 E6 E6 FF E6 E6 E6 FF E7 E7' - 'E7 FF E7 E7 E7 FF DF DF DF FF D2 D2 D2 FF D3 D3' - 'D3 FF DE DE DE FF E3 E3 E3 FF E3 E3 E3 FF E4 E4' - 'E4 FF E4 E4 E4 FF E4 E4 E4 FF E4 E4 E4 FF E3 E3' - 'E3 FF E3 E3 E3 FF DE DE DE FF D4 D4 D4 FF D1 D1' - 'D1 FF DD DD DD FF E7 E7 E7 FF E7 E7 E7 FF E6 E6' - 'E6 FF E8 E8 E8 FF D4 D4 D4 FF 9A 9A 9A FF 0B 0B' - '0B FF 00 00 00 ED 00 00 00 2C 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 2D 00 00 00 F9 1E 1E 1E FF AB AB' - 'AB FF E4 E4 E4 FF ED ED ED FF E7 E7 E7 FF E6 E6' - 'E6 FF EC EC EC FF EB EB EB FF EA EA EA FF E4 E4' - 'E4 FF E0 E0 E0 FF DF DF DF FF DF DF DF FF DD DD' - 'DD FF DC DC DC FF DC DC DC FF DD DD DD FF DF DF' - 'DF FF DF DF DF FF DF DF DF FF E2 E2 E2 FF E9 E9' - 'E9 FF EC EC EC FF EA EA EA FF E5 E5 E5 FF EB EB' - 'EB FF EB EB EB FF D6 D6 D6 FF 8A 8A 8A FF 00 00' - '00 FF 00 00 00 C3 00 00 00 17 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 09 00 00 00 DD 08 08 08 FF 99 99' - '99 FF E4 E4 E4 FF DF DF DF FF BD BD BD FF C1 C1' - 'C1 FF D6 D6 D6 FF EC EC EC FF ED ED ED FF ED ED' - 'ED FF EE EE EE FF EE EE EE FF ED ED ED FF EC EC' - 'EC FF EB EB EB FF EB EB EB FF EC EC EC FF ED ED' - 'ED FF EE EE EE FF EE EE EE FF ED ED ED FF EE EE' - 'EE FF E4 E4 E4 FF C2 C2 C2 FF BE BE BE FF D1 D1' - 'D1 FF EC EC EC FF D4 D4 D4 FF 75 75 75 FF 00 00' - '00 FF 00 00 00 9C 00 00 00 06 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 AE 00 00 00 FF 8D 8D' - '8D FF E0 E0 E0 FF C4 C4 C4 FF B2 B2 B2 FF B8 B8' - 'B8 FF B2 B2 B2 FF EB EB EB FF F1 F1 F1 FF F0 F0' - 'F0 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0' - 'F0 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0' - 'F0 FF F0 F0 F0 FF F0 F0 F0 FF F0 F0 F0 FF F3 F3' - 'F3 FF D5 D5 D5 FF B0 B0 B0 FF B6 B6 B6 FF B7 B7' - 'B7 FF EB EB EB FF CD CD CD FF 61 61 61 FF 00 00' - '00 FF 00 00 00 85 00 00 00 01 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 80 00 00 00 FF 64 64' - '64 FF CE CE CE FF DD DD DD FF DC DC DC FF D5 D5' - 'D5 FF E4 E4 E4 FF F9 F9 F9 FF F9 F9 F9 FF F9 F9' - 'F9 FF F9 F9 F9 FF F9 F9 F9 FF F9 F9 F9 FF F9 F9' - 'F9 FF F9 F9 F9 FF F9 F9 F9 FF F9 F9 F9 FF F9 F9' - 'F9 FF F9 F9 F9 FF F9 F9 F9 FF F9 F9 F9 FF FA FA' - 'FA FF F2 F2 F2 FF DF DF DF FF D8 D8 D8 FF DC DC' - 'DC FF D7 D7 D7 FF B0 B0 B0 FF 34 34 34 FF 00 00' - '00 FF 00 00 00 67 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 44 00 00 00 FF 19 19' - '19 FF 80 80 80 FF BE BE BE FF D3 D3 D3 FF D7 D7' - 'D7 FF DB DB DB FF D8 D8 D8 FF D8 D8 D8 FF D8 D8' - 'D8 FF D8 D8 D8 FF D8 D8 D8 FF D8 D8 D8 FF D8 D8' - 'D8 FF D8 D8 D8 FF D8 D8 D8 FF D8 D8 D8 FF D8 D8' - 'D8 FF D8 D8 D8 FF D8 D8 D8 FF D8 D8 D8 FF D8 D8' - 'D8 FF D9 D9 D9 FF D9 D9 D9 FF D7 D7 D7 FF CD CD' - 'CD FF B0 B0 B0 FF 5D 5D 5D FF 06 06 06 FF 00 00' - '00 ED 00 00 00 2B 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 02 00 00 00 9C 00 00' - '00 FF 0C 0C 0C FF 3D 3D 3D FF 49 49 49 FF 48 48' - '48 FF 48 48 48 FF 48 48 48 FF 48 48 48 FF 48 48' - '48 FF 48 48 48 FF 48 48 48 FF 48 48 48 FF 48 48' - '48 FF 48 48 48 FF 48 48 48 FF 48 48 48 FF 48 48' - '48 FF 48 48 48 FF 48 48 48 FF 48 48 48 FF 48 48' - '48 FF 48 48 48 FF 48 48 48 FF 49 49 49 FF 49 49' - '49 FF 2F 2F 2F FF 02 02 02 FF 00 00 00 FF 00 00' - '00 5C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00' - '00 7F 00 00 00 F1 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 D6 00 00 00 5C 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 2C 00 00 00 7E 00 00 00 84 00 00' - '00 84 00 00 00 84 00 00 00 84 00 00 00 84 00 00' - '00 84 00 00 00 84 00 00 00 84 00 00 00 84 00 00' - '00 84 00 00 00 84 00 00 00 84 00 00 00 84 00 00' - '00 84 00 00 00 84 00 00 00 84 00 00 00 84 00 00' - '00 84 00 00 00 84 00 00 00 84 00 00 00 84 00 00' - '00 84 00 00 00 70 00 00 00 1A 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 65 59 5A 5D 5D 59 64 67' + '68 69 6B 6C 6E 6E 70 6F 6C 6B 69 67 64 63 61 5F' + '5B 59 57 53 51 4E 4B 49 46 42 41 3C 3A 38 2F 2C' + '2B 2B 2B 30 00 00 00 00 4F 4E 4F 52 54 5B 5E 60' + '61 63 64 66 66 68 69 67 65 64 62 60 5E 5D 5A 58' + '56 54 52 4F 4D 4A 47 44 42 3F 3D 3B 37 30 26 23' + '21 1E 1D 1D 00 00 00 69 59 59 5A 5C 5F 60 61 62' + '64 65 66 68 6A 6A 6B 6A 69 68 65 63 62 60 5C 5A' + '58 57 53 51 50 4E 4B 48 46 43 40 3E 3B 39 36 2D' + '28 28 26 26 51 00 00 68 5A 5B 5C 61 35 47 49 49' + '4A 4A 4A 4B 4B 4B 4B 49 46 43 40 3F 3C 3A 38 35' + '2F 2D 2B 29 27 25 22 20 1E 1B 1A 18 17 15 13 0B' + '2C 27 25 25 3F 00 00 60 5B 5C 5D 4E 57 60 61 62' + '63 62 63 66 66 68 67 62 5F 5D 5B 57 54 53 50 4C' + '4A 47 44 41 3F 3C 39 37 2F 2C 2A 26 22 32 33 31' + '19 26 24 22 28 00 00 64 61 61 63 42 63 61 62 63' + '62 64 65 67 68 69 65 62 60 5E 5C 59 55 52 50 4C' + '4A 46 44 42 3F 3B 39 35 2F 2C 29 25 21 1E 1B 1B' + '14 28 25 22 1E 00 64 41 40 41 41 50 5E 5D 5F 61' + '61 62 65 66 67 66 62 5F 5E 5C 59 56 54 50 4E 49' + '48 45 43 42 3E 3B 37 35 2E 2B 28 25 22 1E 1C 19' + '14 0F 0E 0C 0D 49 69 57 59 57 56 57 59 59 5A 5B' + '5C 5E 5F 60 61 60 5D 5A 58 56 53 50 4E 4B 48 45' + '43 41 3D 3C 39 35 2F 2C 2A 27 25 24 22 1F 1B 19' + '18 16 13 10 11 37 4C 27 45 2C 48 48 4D 5A 5E 63' + '66 66 69 68 68 6A 6B 6A 6C 6E 6D 6C 6B 6A 68 68' + '67 66 65 64 62 60 61 5F 5B 58 57 54 54 53 4F 49' + '44 44 48 40 4E 5B 64 43 4B 29 50 50 50 44 40 40' + '42 43 44 44 46 48 49 49 4B 4C 4D 4F 4D 4D 4D 4D' + '4D 4D 4E 4E 4D 4C 4C 4D 4E 4D 4D 4E 4D 50 55 6E' + '7D 53 5D 39 41 69 00 6C 5B 53 2E 0A 43 5A 5E 62' + '66 68 6B 6B 6C 6E 6F 71 72 71 72 73 73 74 74 73' + '73 71 70 6E 6D 6C 69 67 67 6A 6D 6C 6A 69 6B 88' + '52 78 61 6D 6B 00 00 57 49 12 12 49 39 2D 4F 52' + '53 54 54 55 56 57 57 58 59 5A 5A 5C 5D 5D 5C 5C' + '5D 5D 5E 5E 5F 5F 5E 60 5F 5F 60 5F 62 46 5A 41' + '72 63 61 53 5E 00 00 65 42 0F 4F 25 3F 54 57 58' + '59 5B 5C 5D 5F 5F 61 62 64 64 66 67 68 69 6A 6B' + '6C 6D 6E 6F 71 71 72 73 74 74 07 07 76 79 52 49' + '50 55 4F 57 00 00 00 00 4F 21 54 48 54 56 57 59' + '5A 5B 5D 5E 5F 61 62 63 64 65 67 68 69 6A 6B 6C' + '6D 6E 6F 70 71 73 73 74 07 76 76 77 77 78 79 6D' + '43 4A 46 5F 00 00 00 00 60 2A 54 42 56 57 58 59' + '5B 5C 5D 5F 60 61 62 64 65 66 68 69 6A 6C 6C 6D' + '6E 70 71 72 73 74 74 76 76 77 78 78 79 79 79 7C' + '4F 46 51 00 00 00 00 00 00 4B 2F 48 56 57 59 5A' + '5B 5D 5E 60 61 62 64 65 66 68 68 6A 6B 6C 6D 6E' + '70 71 72 73 74 07 76 77 78 78 79 7A 7A 7B 7B 6F' + '44 4A 62 00 00 00 00 00 00 5D 2C 56 47 58 59 5B' + '5C 5D 5F 60 62 63 64 66 67 68 69 69 67 68 69 6A' + '6F 72 73 74 07 76 77 78 79 7A 7A 7B 7C 7C 7E 53' + '46 54 00 00 00 00 00 00 00 00 51 3E 49 59 5A 5B' + '5D 5E 60 61 62 64 65 66 67 68 74 83 88 86 86 84' + '86 89 72 73 77 77 79 79 7A 7B 7C 7C 7D 7E 7A 41' + '4D 62 00 00 00 00 00 00 00 00 62 35 53 57 5A 5C' + '5D 5F 60 62 63 64 66 61 5A 68 67 6B 6E 70 71 72' + '6E 68 7D 6F 6E 79 79 7A 7C 7C 7D 7E 7E 80 62 46' + '55 00 00 00 00 00 00 00 00 00 00 46 4D 51 5B 5C' + '5E 5F 61 62 64 65 64 50 68 6B 6C 6D 6F 70 71 73' + '73 07 77 67 59 79 7A 7B 7C 7D 7F 7F 80 81 4B 51' + '66 00 00 00 00 00 00 00 00 00 00 5F 47 54 5B 5D' + '5E 60 61 63 64 66 5F 56 6C 6B 6D 6E 6F 71 72 73' + '74 76 78 6A 49 7D 7B 7D 7E 7F 80 81 81 6B 4E 58' + '00 00 00 00 00 00 00 00 00 00 00 00 4E 54 56 5D' + '5F 60 62 63 65 66 69 5A 58 67 6E 6F 70 71 73 07' + '77 07 65 3A 65 7C 7D 7E 7F 80 81 82 7E 4F 56 67' + '00 00 00 00 00 00 00 00 00 00 00 00 5D 50 59 5C' + '5F 61 62 64 65 67 68 69 6C 61 54 51 5B 5F 5E 56' + '4A 49 53 7B 7C 7C 7E 7F 80 81 83 78 59 54 58 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 45 50 56' + '60 60 63 64 66 67 68 6A 6B 6D 70 73 68 65 67 73' + '7C 7C 7A 7B 7C 7E 7F 80 81 82 68 59 56 58 6D 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 5F 42 24' + '38 52 63 64 66 67 69 6A 6C 6D 6F 70 72 73 07 76' + '78 79 7A 7C 7D 7E 7F 81 83 65 56 82 68 5A 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 50' + '43 60 63 65 66 68 69 6B 6C 6E 6F 71 72 74 07 77' + '78 79 7B 7C 7E 7F 81 82 83 83 71 62 63 6D 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 61 54' + '5F 62 63 65 66 68 69 6B 6D 6E 72 72 72 74 76 77' + '79 7A 7B 7D 7E 80 80 83 84 85 83 53 66 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 57' + '60 62 64 65 67 68 6A 6A 63 72 76 77 72 71 76 77' + '79 7A 7C 7D 7F 80 82 83 85 86 6D 60 70 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63' + '5F 62 64 65 67 68 6A 6B 63 67 69 69 60 6C 76 78' + '79 7B 7C 7E 7F 81 82 84 85 88 5D 6C 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '61 65 63 65 67 68 6A 6C 6D 70 71 73 74 07 76 78' + '79 7B 7C 7E 7F 81 82 84 87 77 65 73 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '67 6D 70 6C 6A 6C 6D 6E 6F 71 72 74 07 76 78 79' + '7C 7C 7D 7E 80 81 83 83 7A 6A 72 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -6953,30 +11011,550 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 FF FF FF FF FF FF 00 00 FF FF' 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' - 'FF FF FF FF 00 00 FF 00 00 00 00 7F 00 00 FC 00' - '00 00 00 1F 00 00 F8 00 00 00 00 1F 00 00 F8 00' - '00 00 00 1F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F8 00' - '00 00 00 1F 00 00 F8 00 00 00 00 1F 00 00 F8 00' - '00 00 00 1F 00 00 F8 00 00 00 00 1F 00 00 F8 00' - '00 00 00 1F 00 00 F8 00 00 00 00 1F 00 00 FC 00' - '00 00 00 3F 00 00 FC 00 00 00 00 3F 00 00 FC 00' - '00 00 00 3F 00 00 FC 00 00 00 00 3F 00 00 FC 00' - '00 00 00 3F 00 00 FC 00 00 00 00 7F 00 00 FE 00' - '00 00 00 7F 00 00 FE 00 00 00 00 7F 00 00 FE 00' - '00 00 00 7F 00 00 FE 00 00 00 00 7F 00 00 FE 00' - '00 00 00 7F 00 00 FF 00 00 00 00 7F 00 00 FF 00' - '00 00 00 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00' - '00 00 01 FF 00 00 FF 80 00 00 03 FF 00 00 FF E0' - '00 00 07 FF 00 00 FF FF FF FF FF FF 00 00 FF FF' 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' - 'FF FF FF FF 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 20 00 00 00 00 00 00 10 00 00 00 00' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 C0 00 00 00 00 03 00 00 C0 00' + '00 00 00 03 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 03 00 00 C0 00' + '00 00 00 03 00 00 C0 00 00 00 00 07 00 00 E0 00' + '00 00 00 07 00 00 E0 00 00 00 00 0F 00 00 F0 00' + '00 00 00 0F 00 00 F0 00 00 00 00 1F 00 00 F8 00' + '00 00 00 1F 00 00 F8 00 00 00 00 3F 00 00 FC 00' + '00 00 00 3F 00 00 FC 00 00 00 00 7F 00 00 FE 00' + '00 00 00 7F 00 00 FE 00 00 00 00 FF 00 00 FF 00' + '00 00 00 FF 00 00 FF 00 00 00 01 FF 00 00 FF 80' + '00 00 01 FF 00 00 FF 80 00 00 03 FF 00 00 FF C0' + '00 00 03 FF 00 00 FF C0 00 00 07 FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 20 00 00 00 00 00 80 25 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 A0 A0 A0 3E B0 B0 B0 FC A4 A4 A4 FC A5 A5' + 'A5 FC A8 A8 A8 FC A8 A8 A8 FC A4 A4 A4 FC AF AF' + 'AF FC B2 B2 B2 FC B3 B3 B3 FC B4 B4 B4 FC B6 B6' + 'B6 FC B7 B7 B7 FC B9 B9 B9 FC B9 B9 B9 FC BB BB' + 'BB FC BA BA BA FC B7 B7 B7 FC B6 B6 B6 FC B4 B4' + 'B4 FC B2 B2 B2 FC AF AF AF FC AE AE AE FC AC AC' + 'AC FC AA AA AA FC A6 A6 A6 FC A4 A4 A4 FC A2 A2' + 'A2 FC 9E 9E 9E FC 9C 9C 9C FC 99 99 99 FC 95 95' + '95 FC 93 93 93 FC 90 90 90 FC 8C 8C 8C FC 8B 8B' + '8B FC 86 86 86 FC 84 84 84 FC 82 82 82 FC 7D 7D' + '7D FC 7A 7A 7A FC 79 79 79 FC 79 79 79 FC 79 79' + '79 FC 74 74 74 DB 8D 8D 8D 1B 00 00 00 00 00 00' + '00 00 A4 A4 A4 65 9A 9A 9A FF 99 99 99 FF 9A 9A' + '9A FF 9D 9D 9D FF 9F 9F 9F FF A6 A6 A6 FF A9 A9' + 'A9 FF AB AB AB FF AC AC AC FF AE AE AE FF AF AF' + 'AF FF B1 B1 B1 FF B1 B1 B1 FF B3 B3 B3 FF B4 B4' + 'B4 FF B2 B2 B2 FF B0 B0 B0 FF AF AF AF FF AD AD' + 'AD FF AB AB AB FF A9 A9 A9 FF A8 A8 A8 FF A5 A5' + 'A5 FF A3 A3 A3 FF A1 A1 A1 FF 9F 9F 9F FF 9D 9D' + '9D FF 9A 9A 9A FF 98 98 98 FF 95 95 95 FF 92 92' + '92 FF 8F 8F 8F FF 8D 8D 8D FF 8A 8A 8A FF 88 88' + '88 FF 86 86 86 FF 82 82 82 FF 7F 7F 7F FF 75 75' + '75 FF 72 72 72 FF 70 70 70 FF 6D 6D 6D FF 6C 6C' + '6C FF 6C 6C 6C FF 7F 7F 7F 5A 00 00 00 00 00 00' + '00 00 AC AC AC 98 A4 A4 A4 FF A4 A4 A4 FF A5 A5' + 'A5 FF A7 A7 A7 FF AA AA AA FF AB AB AB FF AC AC' + 'AC FF AD AD AD FF AF AF AF FF B0 B0 B0 FF B1 B1' + 'B1 FF B3 B3 B3 FF B5 B5 B5 FF B5 B5 B5 FF B6 B6' + 'B6 FF B5 B5 B5 FF B4 B4 B4 FF B3 B3 B3 FF B0 B0' + 'B0 FF AE AE AE FF AD AD AD FF AB AB AB FF A7 A7' + 'A7 FF A5 A5 A5 FF A3 A3 A3 FF A2 A2 A2 FF 9E 9E' + '9E FF 9C 9C 9C FF 9B 9B 9B FF 99 99 99 FF 96 96' + '96 FF 93 93 93 FF 91 91 91 FF 8E 8E 8E FF 8B 8B' + '8B FF 89 89 89 FF 86 86 86 FF 84 84 84 FF 81 81' + '81 FF 7C 7C 7C FF 77 77 77 FF 77 77 77 FF 75 75' + '75 FF 75 75 75 FF 83 83 83 95 00 00 00 00 00 00' + '00 00 B0 B0 B0 CF A5 A5 A5 FF A6 A6 A6 FF A7 A7' + 'A7 FF AC AC AC FF 80 80 80 FF 92 92 92 FF 94 94' + '94 FF 94 94 94 FF 95 95 95 FF 95 95 95 FF 95 95' + '95 FF 96 96 96 FF 96 96 96 FF 96 96 96 FF 96 96' + '96 FF 94 94 94 FF 91 91 91 FF 8E 8E 8E FF 8B 8B' + '8B FF 8A 8A 8A FF 87 87 87 FF 85 85 85 FF 83 83' + '83 FF 80 80 80 FF 7E 7E 7E FF 7C 7C 7C FF 7A 7A' + '7A FF 78 78 78 FF 76 76 76 FF 74 74 74 FF 71 71' + '71 FF 6F 6F 6F FF 6D 6D 6D FF 6A 6A 6A FF 68 68' + '68 FF 66 66 66 FF 64 64 64 FF 61 61 61 FF 5F 5F' + '5F FF 52 52 52 FF 7B 7B 7B FF 76 76 76 FF 74 74' + '74 FF 74 74 74 FF 7E 7E 7E D0 00 00 00 00 00 00' + '00 00 AB AB AB FA A6 A6 A6 FF A7 A7 A7 FF A8 A8' + 'A8 FF 99 99 99 FF A2 A2 A2 FF AB AB AB FF AC AC' + 'AC FF AD AD AD FF AE AE AE FF AD AD AD FF AE AE' + 'AE FF B1 B1 B1 FF B1 B1 B1 FF B3 B3 B3 FF B2 B2' + 'B2 FF AD AD AD FF AA AA AA FF A8 A8 A8 FF A6 A6' + 'A6 FF A2 A2 A2 FF 9F 9F 9F FF 9E 9E 9E FF 9B 9B' + '9B FF 97 97 97 FF 95 95 95 FF 92 92 92 FF 8F 8F' + '8F FF 8C 8C 8C FF 8A 8A 8A FF 87 87 87 FF 84 84' + '84 FF 82 82 82 FF 7E 7E 7E FF 7B 7B 7B FF 79 79' + '79 FF 75 75 75 FF 71 71 71 FF 37 B6 37 FF 1B DA' + '1B FF 34 B3 34 FF 67 67 67 FF 75 75 75 FF 73 73' + '73 FF 71 71 71 FF 76 76 76 FC 00 00 00 00 B9 B9' + 'B9 3B AF AF AF FF AC AC AC FF AC AC AC FF AE AE' + 'AE FF 8D 8D 8D FF AE AE AE FF AC AC AC FF AD AD' + 'AD FF AE AE AE FF AD AD AD FF AF AF AF FF B0 B0' + 'B0 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4 B4 FF B0 B0' + 'B0 FF AD AD AD FF AB AB AB FF A9 A9 A9 FF A7 A7' + 'A7 FF A4 A4 A4 FF A0 A0 A0 FF 9D 9D 9D FF 9B 9B' + '9B FF 97 97 97 FF 95 95 95 FF 91 91 91 FF 8F 8F' + '8F FF 8D 8D 8D FF 8A 8A 8A FF 86 86 86 FF 84 84' + '84 FF 80 80 80 FF 7E 7E 7E FF 7B 7B 7B FF 78 78' + '78 FF 74 74 74 FF 70 70 70 FF 6D 6D 6D FF 6A 6A' + '6A FF 6A 6A 6A FF 60 60 60 FF 77 77 77 FF 74 74' + '74 FF 71 71 71 FF 6D 6D 6D FF 8A 8A 8A 48 9A 9A' + '9A 72 8C 8C 8C FF 8B 8B 8B FF 8C 8C 8C FF 8C 8C' + '8C FF 9B 9B 9B FF A9 A9 A9 FF A8 A8 A8 FF AA AA' + 'AA FF AC AC AC FF AC AC AC FF AD AD AD FF B0 B0' + 'B0 FF B1 B1 B1 FF B2 B2 B2 FF B1 B1 B1 FF AD AD' + 'AD FF AA AA AA FF A9 A9 A9 FF A7 A7 A7 FF A4 A4' + 'A4 FF A1 A1 A1 FF 9F 9F 9F FF 9B 9B 9B FF 99 99' + '99 FF 94 94 94 FF 93 93 93 FF 90 90 90 FF 8E 8E' + '8E FF 8D 8D 8D FF 89 89 89 FF 86 86 86 FF 82 82' + '82 FF 80 80 80 FF 7D 7D 7D FF 7A 7A 7A FF 77 77' + '77 FF 74 74 74 FF 71 71 71 FF 6D 6D 6D FF 6B 6B' + '6B FF 67 67 67 FF 60 60 60 FF 59 59 59 FF 58 58' + '58 FF 56 56 56 FF 57 57 57 FF 6B 6B 6B 83 AE AE' + 'AE A7 A2 A2 A2 FF A4 A4 A4 FF A2 A2 A2 FF A1 A1' + 'A1 FF A2 A2 A2 FF A4 A4 A4 FF A4 A4 A4 FF A5 A5' + 'A5 FF A6 A6 A6 FF A7 A7 A7 FF A9 A9 A9 FF AA AA' + 'AA FF AB AB AB FF AC AC AC FF AB AB AB FF A8 A8' + 'A8 FF A5 A5 A5 FF A3 A3 A3 FF A1 A1 A1 FF 9E 9E' + '9E FF 9B 9B 9B FF 99 99 99 FF 96 96 96 FF 93 93' + '93 FF 90 90 90 FF 8E 8E 8E FF 8C 8C 8C FF 88 88' + '88 FF 87 87 87 FF 84 84 84 FF 80 80 80 FF 7E 7E' + '7E FF 7B 7B 7B FF 79 79 79 FF 76 76 76 FF 74 74' + '74 FF 73 73 73 FF 71 71 71 FF 6E 6E 6E FF 6A 6A' + '6A FF 67 67 67 FF 66 66 66 FF 62 62 62 FF 5F 5F' + '5F FF 5A 5A 5A FF 5B 5B 5B FF 6D 6D 6D BF 8D 8D' + '8D CF 76 76 76 FF 90 90 90 FF 7B 7B 7B FF 93 93' + '93 FF 93 93 93 FF 98 98 98 FF A5 A5 A5 FF A9 A9' + 'A9 FF AE AE AE FF B1 B1 B1 FF B1 B1 B1 FF B4 B4' + 'B4 FF B3 B3 B3 FF B3 B3 B3 FF B5 B5 B5 FF B6 B6' + 'B6 FF B5 B5 B5 FF B7 B7 B7 FF B9 B9 B9 FF B8 B8' + 'B8 FF B7 B7 B7 FF B6 B6 B6 FF B5 B5 B5 FF B3 B3' + 'B3 FF B3 B3 B3 FF B2 B2 B2 FF B1 B1 B1 FF B0 B0' + 'B0 FF AF AF AF FF AD AD AD FF AB AB AB FF AC AC' + 'AC FF AA AA AA FF A6 A6 A6 FF A3 A3 A3 FF A2 A2' + 'A2 FF 9F 9F 9F FF 9F 9F 9F FF 9E 9E 9E FF 9A 9A' + '9A FF 94 94 94 FF 8F 8F 8F FF 8F 8F 8F FF 93 93' + '93 FF 8B 8B 8B FF 99 99 99 FF A1 A1 A1 D2 98 98' + '98 6B 8E 8E 8E FF 96 96 96 FF 78 78 78 FF 9B 9B' + '9B FF 9B 9B 9B FF 9B 9B 9B FF 8F 8F 8F FF 8B 8B' + '8B FF 8B 8B 8B FF 8D 8D 8D FF 8E 8E 8E FF 8F 8F' + '8F FF 8F 8F 8F FF 91 91 91 FF 93 93 93 FF 94 94' + '94 FF 94 94 94 FF 96 96 96 FF 97 97 97 FF 98 98' + '98 FF 9A 9A 9A FF 98 98 98 FF 98 98 98 FF 98 98' + '98 FF 98 98 98 FF 98 98 98 FF 98 98 98 FF 99 99' + '99 FF 99 99 99 FF 98 98 98 FF 97 97 97 FF 97 97' + '97 FF 98 98 98 FF 99 99 99 FF 98 98 98 FF 98 98' + '98 FF 99 99 99 FF 98 98 98 FF 9B 9B 9B FF A0 A0' + 'A0 FF B9 B9 B9 FF C8 C8 C8 FF 9E 9E 9E FF A8 A8' + 'A8 FF 84 84 84 FF 8C 8C 8C FF A3 A3 A3 6D 7F 7F' + '7F 3A B7 B7 B7 FF A6 A6 A6 FF 9E 9E 9E FF 7D 7D' + '7D FF 4F 4F 4F FF 8E 8E 8E FF A5 A5 A5 FF A9 A9' + 'A9 FF AD AD AD FF B1 B1 B1 FF B3 B3 B3 FF B6 B6' + 'B6 FF B6 B6 B6 FF B7 B7 B7 FF B9 B9 B9 FF BA BA' + 'BA FF BC BC BC FF BD BD BD FF BC BC BC FF BD BD' + 'BD FF BE BE BE FF BE BE BE FF BF BF BF FF BF BF' + 'BF FF BE BE BE FF BE BE BE FF BC BC BC FF BB BB' + 'BB FF B9 B9 B9 FF B8 B8 B8 FF B7 B7 B7 FF B4 B4' + 'B4 FF B2 B2 B2 FF B2 B2 B2 FF B5 B5 B5 FF B8 B8' + 'B8 FF B7 B7 B7 FF B5 B5 B5 FF B4 B4 B4 FF B6 B6' + 'B6 FF D3 D3 D3 FF 9D 9D 9D FF C3 C3 C3 FF AC AC' + 'AC FF B8 B8 B8 FF B5 B5 B5 F3 CE CE CE 15 00 00' + '00 00 98 98 98 C2 94 94 94 FF 5C 5C 5C FF 5C 5C' + '5C FF 94 94 94 FF 84 84 84 FF 7C 7C 7C FF 9A 9A' + '9A FF 9D 9D 9D FF 9E 9E 9E FF 9F 9F 9F FF 9F 9F' + '9F FF A0 A0 A0 FF A1 A1 A1 FF A2 A2 A2 FF A2 A2' + 'A2 FF A3 A3 A3 FF A4 A4 A4 FF A5 A5 A5 FF A5 A5' + 'A5 FF A7 A7 A7 FF A8 A8 A8 FF A8 A8 A8 FF A7 A7' + 'A7 FF A7 A7 A7 FF A8 A8 A8 FF A8 A8 A8 FF A9 A9' + 'A9 FF A9 A9 A9 FF AA AA AA FF AA AA AA FF A9 A9' + 'A9 FF AB AB AB FF AA AA AA FF AA AA AA FF AB AB' + 'AB FF AA AA AA FF AD AD AD FF 91 91 91 FF A5 A5' + 'A5 FF 8C 8C 8C FF BD BD BD FF AE AE AE FF AC AC' + 'AC FF 9E 9E 9E FF 95 95 95 87 00 00 00 00 00 00' + '00 00 86 86 86 48 8D 8D 8D FF 59 59 59 FF 9A 9A' + '9A FF 74 74 74 FF 8A 8A 8A FF 9F 9F 9F FF A2 A2' + 'A2 FF A3 A3 A3 FF A4 A4 A4 FF A6 A6 A6 FF A7 A7' + 'A7 FF A8 A8 A8 FF AA AA AA FF AA AA AA FF AC AC' + 'AC FF AD AD AD FF AF AF AF FF AF AF AF FF B1 B1' + 'B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5' + 'B5 FF B6 B6 B6 FF B7 B7 B7 FF B8 B8 B8 FF B9 B9' + 'B9 FF BA BA BA FF BC BC BC FF BC BC BC FF BD BD' + 'BD FF BE BE BE FF BF BF BF FF BF BF BF FF C0 C0' + 'C0 FF C0 C0 C0 FF C1 C1 C1 FF C4 C4 C4 FF 9D 9D' + '9D FF 94 94 94 FF 9B 9B 9B FF A0 A0 A0 FF 9A 9A' + '9A FF A0 A0 A0 F2 7F 7F 7F 12 00 00 00 00 00 00' + '00 00 00 00 00 00 92 92 92 D1 70 70 70 FF 9F 9F' + '9F FF 93 93 93 FF 9F 9F 9F FF A1 A1 A1 FF A2 A2' + 'A2 FF A4 A4 A4 FF A5 A5 A5 FF A6 A6 A6 FF A8 A8' + 'A8 FF A9 A9 A9 FF AA AA AA FF AC AC AC FF AD AD' + 'AD FF AE AE AE FF AF AF AF FF B0 B0 B0 FF B2 B2' + 'B2 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5 B5 FF B6 B6' + 'B6 FF B7 B7 B7 FF B8 B8 B8 FF B9 B9 B9 FF BA BA' + 'BA FF BB BB BB FF BC BC BC FF BE BE BE FF BE BE' + 'BE FF BF BF BF FF C0 C0 C0 FF C1 C1 C1 FF C1 C1' + 'C1 FF C2 C2 C2 FF C2 C2 C2 FF C3 C3 C3 FF C4 C4' + 'C4 FF B8 B8 B8 FF 8E 8E 8E FF 95 95 95 FF 91 91' + '91 FF 95 95 95 82 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 83 83 83 59 79 79 79 FF 9F 9F' + '9F FF 8D 8D 8D FF A1 A1 A1 FF A2 A2 A2 FF A3 A3' + 'A3 FF A4 A4 A4 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8' + 'A8 FF AA AA AA FF AB AB AB FF AC AC AC FF AD AD' + 'AD FF AF AF AF FF B0 B0 B0 FF B1 B1 B1 FF B3 B3' + 'B3 FF B4 B4 B4 FF B5 B5 B5 FF B7 B7 B7 FF B7 B7' + 'B7 FF B8 B8 B8 FF B9 B9 B9 FF BB BB BB FF BC BC' + 'BC FF BD BD BD FF BE BE BE FF BF BF BF FF BF BF' + 'BF FF C1 C1 C1 FF C1 C1 C1 FF C2 C2 C2 FF C3 C3' + 'C3 FF C3 C3 C3 FF C4 C4 C4 FF C4 C4 C4 FF C4 C4' + 'C4 FF C7 C7 C7 FF 9A 9A 9A FF 91 91 91 FF 9A 9A' + '9A EF 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 90 90 90 E0 7E 7E' + '7E FF 93 93 93 FF A1 A1 A1 FF A2 A2 A2 FF A4 A4' + 'A4 FF A5 A5 A5 FF A6 A6 A6 FF A8 A8 A8 FF A9 A9' + 'A9 FF AB AB AB FF AC AC AC FF AD AD AD FF AF AF' + 'AF FF B0 B0 B0 FF B1 B1 B1 FF B3 B3 B3 FF B3 B3' + 'B3 FF B5 B5 B5 FF B6 B6 B6 FF B7 B7 B7 FF B8 B8' + 'B8 FF B9 B9 B9 FF BB BB BB FF BC BC BC FF BD BD' + 'BD FF BE BE BE FF BF BF BF FF C0 C0 C0 FF C1 C1' + 'C1 FF C2 C2 C2 FF C3 C3 C3 FF C3 C3 C3 FF C4 C4' + 'C4 FF C5 C5 C5 FF C5 C5 C5 FF C6 C6 C6 FF C6 C6' + 'C6 FF BA BA BA FF 8F 8F 8F FF 95 95 95 FF 99 99' + '99 7D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 8A 8A 8A 70 7B 7B' + '7B FF A1 A1 A1 FF 92 92 92 FF A3 A3 A3 FF A4 A4' + 'A4 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8 A8 FF AA AA' + 'AA FF AB AB AB FF AD AD AD FF AE AE AE FF AF AF' + 'AF FF B1 B1 B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4' + 'B4 FF B4 B4 B4 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4' + 'B4 FF B5 B5 B5 FF BA BA BA FF BD BD BD FF BE BE' + 'BE FF BF BF BF FF C0 C0 C0 FF C1 C1 C1 FF C2 C2' + 'C2 FF C3 C3 C3 FF C4 C4 C4 FF C5 C5 C5 FF C5 C5' + 'C5 FF C6 C6 C6 FF C7 C7 C7 FF C7 C7 C7 FF C9 C9' + 'C9 FF 9E 9E 9E FF 91 91 91 FF 9C 9C 9C EB 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9B 9B' + '9B F7 89 89 89 FF 94 94 94 FF A4 A4 A4 FF A5 A5' + 'A5 FF A6 A6 A6 FF A8 A8 A8 FF A9 A9 A9 FF AB AB' + 'AB FF AC AC AC FF AD AD AD FF AF AF AF FF B0 B0' + 'B0 FF B1 B1 B1 FF B2 B2 B2 FF B3 B3 B3 FF BF BF' + 'BF FF CE CE CE FF D3 D3 D3 FF D1 D1 D1 FF D1 D1' + 'D1 FF CF CF CF FF D1 D1 D1 FF D5 D5 D5 FF BD BD' + 'BD FF BE BE BE FF C2 C2 C2 FF C2 C2 C2 FF C4 C4' + 'C4 FF C4 C4 C4 FF C5 C5 C5 FF C6 C6 C6 FF C7 C7' + 'C7 FF C7 C7 C7 FF C8 C8 C8 FF C9 C9 C9 FF C5 C5' + 'C5 FF 8C 8C 8C FF 98 98 98 FF 97 97 97 74 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 9B 9B' + '9B 85 80 80 80 FF 9E 9E 9E FF A2 A2 A2 FF A5 A5' + 'A5 FF A7 A7 A7 FF A8 A8 A8 FF AA AA AA FF AB AB' + 'AB FF AD AD AD FF AE AE AE FF AF AF AF FF B1 B1' + 'B1 FF AC AC AC FF A5 A5 A5 FF B3 B3 B3 FF B2 B2' + 'B2 FF B6 B6 B6 FF B9 B9 B9 FF BB BB BB FF BC BC' + 'BC FF BD BD BD FF B9 B9 B9 FF B3 B3 B3 FF C8 C8' + 'C8 FF BA BA BA FF B9 B9 B9 FF C4 C4 C4 FF C4 C4' + 'C4 FF C5 C5 C5 FF C7 C7 C7 FF C7 C7 C7 FF C8 C8' + 'C8 FF C9 C9 C9 FF C9 C9 C9 FF CB CB CB FF AD AD' + 'AD FF 91 91 91 FF 9D 9D 9D E7 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 94' + '94 18 8F 8F 8F F7 98 98 98 FF 9C 9C 9C FF A6 A6' + 'A6 FF A7 A7 A7 FF A9 A9 A9 FF AA AA AA FF AC AC' + 'AC FF AD AD AD FF AF AF AF FF B0 B0 B0 FF AF AF' + 'AF FF 9B 9B 9B FF B3 B3 B3 FF B6 B6 B6 FF B7 B7' + 'B7 FF B8 B8 B8 FF BA BA BA FF BB BB BB FF BC BC' + 'BC FF BE BE BE FF BE BE BE FF C0 C0 C0 FF C2 C2' + 'C2 FF B2 B2 B2 FF A4 A4 A4 FF C4 C4 C4 FF C5 C5' + 'C5 FF C6 C6 C6 FF C7 C7 C7 FF C8 C8 C8 FF CA CA' + 'CA FF CA CA CA FF CB CB CB FF CC CC CC FF 96 96' + '96 FF 9C 9C 9C FF 9E 9E 9E 6F 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 9B 9B 9B 9A 92 92 92 FF 9F 9F 9F FF A6 A6' + 'A6 FF A8 A8 A8 FF A9 A9 A9 FF AB AB AB FF AC AC' + 'AC FF AE AE AE FF AF AF AF FF B1 B1 B1 FF AA AA' + 'AA FF A1 A1 A1 FF B7 B7 B7 FF B6 B6 B6 FF B8 B8' + 'B8 FF B9 B9 B9 FF BA BA BA FF BC BC BC FF BD BD' + 'BD FF BE BE BE FF BF BF BF FF C1 C1 C1 FF C3 C3' + 'C3 FF B5 B5 B5 FF 94 94 94 FF C8 C8 C8 FF C6 C6' + 'C6 FF C8 C8 C8 FF C9 C9 C9 FF CA CA CA FF CB CB' + 'CB FF CC CC CC FF CC CC CC FF B6 B6 B6 FF 99 99' + '99 FF 9F 9F 9F E3 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 99 99 99 28 99 99 99 FD 9F 9F 9F FF A1 A1' + 'A1 FF A8 A8 A8 FF AA AA AA FF AB AB AB FF AD AD' + 'AD FF AE AE AE FF B0 B0 B0 FF B1 B1 B1 FF B4 B4' + 'B4 FF A5 A5 A5 FF A3 A3 A3 FF B2 B2 B2 FF B9 B9' + 'B9 FF BA BA BA FF BB BB BB FF BC BC BC FF BE BE' + 'BE FF C0 C0 C0 FF C2 C2 C2 FF C0 C0 C0 FF B0 B0' + 'B0 FF 85 85 85 FF B0 B0 B0 FF C7 C7 C7 FF C8 C8' + 'C8 FF C9 C9 C9 FF CA CA CA FF CB CB CB FF CC CC' + 'CC FF CD CD CD FF C9 C9 C9 FF 9A 9A 9A FF A1 A1' + 'A1 FF 9D 9D 9D 69 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 9D 9D 9D B0 9B 9B 9B FF A4 A4' + 'A4 FF A7 A7 A7 FF AA AA AA FF AC AC AC FF AD AD' + 'AD FF AF AF AF FF B0 B0 B0 FF B2 B2 B2 FF B3 B3' + 'B3 FF B4 B4 B4 FF B7 B7 B7 FF AC AC AC FF 9F 9F' + '9F FF 9C 9C 9C FF A6 A6 A6 FF AA AA AA FF A9 A9' + 'A9 FF A1 A1 A1 FF 95 95 95 FF 94 94 94 FF 9E 9E' + '9E FF C6 C6 C6 FF C7 C7 C7 FF C7 C7 C7 FF C9 C9' + 'C9 FF CA CA CA FF CB CB CB FF CC CC CC FF CE CE' + 'CE FF C3 C3 C3 FF A4 A4 A4 FF 9F 9F 9F FF 9E 9E' + '9E DB 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 A6 A6 A6 1A 90 90 90 FE 9B 9B' + '9B FF A1 A1 A1 FF AB AB AB FF AB AB AB FF AE AE' + 'AE FF AF AF AF FF B1 B1 B1 FF B2 B2 B2 FF B3 B3' + 'B3 FF B5 B5 B5 FF B6 B6 B6 FF B8 B8 B8 FF BB BB' + 'BB FF BE BE BE FF B3 B3 B3 FF B0 B0 B0 FF B2 B2' + 'B2 FF BE BE BE FF C7 C7 C7 FF C7 C7 C7 FF C5 C5' + 'C5 FF C6 C6 C6 FF C7 C7 C7 FF C9 C9 C9 FF CA CA' + 'CA FF CB CB CB FF CC CC CC FF CD CD CD FF B3 B3' + 'B3 FF A4 A4 A4 FF A1 A1 A1 FF A3 A3 A3 FF A4 A4' + 'A4 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 A3 A3 A3 C3 8D 8D' + '8D FF 73 73 73 FF 83 83 83 FF 9D 9D 9D FF AE AE' + 'AE FF AF AF AF FF B1 B1 B1 FF B2 B2 B2 FF B4 B4' + 'B4 FF B5 B5 B5 FF B7 B7 B7 FF B8 B8 B8 FF BA BA' + 'BA FF BB BB BB FF BD BD BD FF BE BE BE FF C0 C0' + 'C0 FF C1 C1 C1 FF C3 C3 C3 FF C4 C4 C4 FF C5 C5' + 'C5 FF C7 C7 C7 FF C8 C8 C8 FF C9 C9 C9 FF CA CA' + 'CA FF CC CC CC FF CE CE CE FF B0 B0 B0 FF A1 A1' + 'A1 FF CD CD CD FF B3 B3 B3 FF A0 A0 A0 D6 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 A5 A5 A5 44 8C 8C' + '8C FE 9B 9B 9B FF 8E 8E 8E FF AB AB AB FF AE AE' + 'AE FF B0 B0 B0 FF B1 B1 B1 FF B3 B3 B3 FF B4 B4' + 'B4 FF B6 B6 B6 FF B7 B7 B7 FF B9 B9 B9 FF BA BA' + 'BA FF BC BC BC FF BD BD BD FF BF BF BF FF C0 C0' + 'C0 FF C2 C2 C2 FF C3 C3 C3 FF C4 C4 C4 FF C6 C6' + 'C6 FF C7 C7 C7 FF C9 C9 C9 FF CA CA CA FF CC CC' + 'CC FF CD CD CD FF CE CE CE FF CE CE CE FF BC BC' + 'BC FF AD AD AD FF AE AE AE FE A5 A5 A5 50 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 A6 A6' + 'A6 C3 9F 9F 9F FF AA AA AA FF AD AD AD FF AE AE' + 'AE FF B0 B0 B0 FF B1 B1 B1 FF B3 B3 B3 FF B4 B4' + 'B4 FF B6 B6 B6 FF B8 B8 B8 FF B9 B9 B9 FF BD BD' + 'BD FF BD BD BD FF BD BD BD FF BF BF BF FF C1 C1' + 'C1 FF C2 C2 C2 FF C4 C4 C4 FF C5 C5 C5 FF C6 C6' + 'C6 FF C8 C8 C8 FF C9 C9 C9 FF CB CB CB FF CB CB' + 'CB FF CE CE CE FF CF CF CF FF D0 D0 D0 FF CE CE' + 'CE FF 9E 9E 9E FF AC AC AC C5 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 A6 A6' + 'A6 45 A2 A2 A2 FE AB AB AB FF AD AD AD FF AF AF' + 'AF FF B0 B0 B0 FF B2 B2 B2 FF B3 B3 B3 FF B5 B5' + 'B5 FF B5 B5 B5 FF AE AE AE FF BD BD BD FF C1 C1' + 'C1 FF C2 C2 C2 FF BD BD BD FF BC BC BC FF C1 C1' + 'C1 FF C2 C2 C2 FF C4 C4 C4 FF C5 C5 C5 FF C7 C7' + 'C7 FF C8 C8 C8 FF CA CA CA FF CB CB CB FF CD CD' + 'CD FF CE CE CE FF D0 D0 D0 FF D1 D1 D1 FF B8 B8' + 'B8 FF AB AB AB FE AB AB AB 3D 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 A9 A9 A9 C4 AA AA AA FF AD AD AD FF AF AF' + 'AF FF B0 B0 B0 FF B2 B2 B2 FF B3 B3 B3 FF B5 B5' + 'B5 FF B6 B6 B6 FF AE AE AE FF B2 B2 B2 FF B4 B4' + 'B4 FF B4 B4 B4 FF AB AB AB FF B7 B7 B7 FF C1 C1' + 'C1 FF C3 C3 C3 FF C4 C4 C4 FF C6 C6 C6 FF C7 C7' + 'C7 FF C9 C9 C9 FF CA CA CA FF CC CC CC FF CD CD' + 'CD FF CF CF CF FF D0 D0 D0 FF D3 D3 D3 FF A8 A8' + 'A8 FF B3 B3 B3 B2 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 AE AE AE 46 AC AC AC FE B0 B0 B0 FF AE AE' + 'AE FF B0 B0 B0 FF B2 B2 B2 FF B3 B3 B3 FF B5 B5' + 'B5 FF B7 B7 B7 FF B8 B8 B8 FF BB BB BB FF BC BC' + 'BC FF BE BE BE FF BF BF BF FF C0 C0 C0 FF C1 C1' + 'C1 FF C3 C3 C3 FF C4 C4 C4 FF C6 C6 C6 FF C7 C7' + 'C7 FF C9 C9 C9 FF CA CA CA FF CC CC CC FF CD CD' + 'CD FF CF CF CF FF D2 D2 D2 FF C2 C2 C2 FF B0 B0' + 'B0 FC B3 B3 B3 2C 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 A4 A4 A4 7C B4 B4 B4 A0 B9 B9' + 'B9 B3 B4 B4 B4 C0 B4 B4 B4 EC B6 B6 B6 ED B7 B7' + 'B7 ED B9 B9 B9 ED BA BA BA ED BC BC BC ED BD BD' + 'BD ED BF BF BF ED C0 C0 C0 ED C1 C1 C1 ED C3 C3' + 'C3 ED C4 C4 C4 ED C7 C7 C7 ED C8 C8 C8 ED C9 C9' + 'C9 ED CA CA CA ED CC CC CC ED CD CD CD ED CF CF' + 'CF EE D2 D2 D2 C1 C7 C7 C7 B8 B0 B0 B0 A9 B8 B8' + 'B8 62 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -7003,220 +11581,20 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 15 00 00 00 54 00 00 00 63 00 00 00 61 00 00' - '00 61 00 00 00 61 00 00 00 61 00 00 00 61 00 00' - '00 61 00 00 00 61 00 00 00 61 00 00 00 61 00 00' - '00 61 00 00 00 61 00 00 00 61 00 00 00 61 00 00' - '00 61 00 00 00 61 00 00 00 61 00 00 00 61 00 00' - '00 61 00 00 00 63 00 00 00 50 00 00 00 14 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 33 00 00' - '00 D7 0D 0D 0D FF 1B 1B 1B FF 1C 1C 1C FF 1E 1E' - '1E FF 1D 1D 1D FF 1D 1D 1D FF 1C 1C 1C FF 1B 1B' - '1B FF 1B 1B 1B FF 1B 1B 1B FF 1A 1A 1A FF 1A 1A' - '1A FF 1A 1A 1A FF 1A 1A 1A FF 1A 1A 1A FF 1A 1A' - '1A FF 1A 1A 1A FF 1A 1A 1A FF 1A 1A 1A FF 20 1C' - '1E FF 22 1B 1F FF 09 09 09 FF 00 00 00 C3 00 00' - '00 29 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 07 00 00 00 C9 40 40' - '40 FF BA BA BA FF CB CB CB FF BE BE BE FF B3 B3' - 'B3 FF B4 B4 B4 FF B4 B4 B4 FF B7 B7 B7 FF BB BB' - 'BB FF BB BB BB FF BB BB BB FF BC BC BC FF BE BE' - 'BE FF BD BD BD FF BC BC BC FF BB BB BB FF BA BA' - 'BA FF BA BA BA FF B4 B4 B4 FF B1 AE AF FF 8C A4' - '98 FF 80 A5 93 FF 90 91 91 FF 21 21 21 FF 00 00' - '00 AB 00 00 00 07 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 3D 08 08 08 FD B7 B7' - 'B7 FF F2 F2 F2 FF EE EE EE FF AF AF AF FF 51 51' - '51 FF 6E 6E 6E FF 89 89 89 FF 9A 9A 9A FF AB AB' - 'AB FF B3 B3 B3 FF BC BC BC FF B5 B5 B5 FF B5 B5' - 'B5 FF BC BC BC FF BD BD BD FF BE BE BE FF B7 B7' - 'B7 FF C0 C0 C0 FF C4 C1 C3 FF B7 BF BA FF 2A BF' - '75 FF 0E C0 67 FF 8C B8 A2 FF 80 79 7C FF 00 00' - '00 EC 00 00 00 25 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 58 19 19 19 FF CE CE' - 'CE FF EB EB EB FF EB EB EB FF AA AA AA FF 56 56' - '56 FF 7A 7A 7A FF 8C 8C 8C FF 96 96 96 FF A2 A2' - 'A2 FF A9 A9 A9 FF B4 B4 B4 FF A1 A1 A1 FF 97 97' - '97 FF A1 A1 A1 FF A5 A5 A5 FF A5 A5 A5 FF 9B 9B' - '9B FF A7 A7 A7 FF B3 B1 B2 FF BC C1 BE FF 87 C6' - 'A6 FF 85 CC A8 FF A6 BC B1 FF 94 90 92 FF 02 02' - '02 F9 00 00 00 36 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 5A 18 18 18 FF CC CC' - 'CC FF ED ED ED FF EC EC EC FF B9 B9 B9 FF 6D 6D' - '6D FF 7D 7D 7D FF 8B 8B 8B FF 96 96 96 FF A2 A2' - 'A2 FF A9 A9 A9 FF B6 B6 B6 FF 96 96 96 FF 82 82' - '82 FF 8E 8E 8E FF 93 93 93 FF 94 94 94 FF 86 86' - '86 FF 95 95 95 FF A6 A6 A6 FF C8 C8 C8 FF C2 C0' - 'C1 FF BF BD BE FF C9 C6 C7 FF 8E 8E 8E FF 01 01' - '01 FA 00 00 00 37 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 5A 17 17 17 FF CD CD' - 'CD FF F0 F0 F0 FF ED ED ED FF BF BF BF FF 77 77' - '77 FF 85 85 85 FF 91 91 91 FF 9A 9A 9A FF A4 A4' - 'A4 FF AA AA AA FF B5 B5 B5 FF 97 97 97 FF 83 83' - '83 FF 8D 8D 8D FF 92 92 92 FF 93 93 93 FF 86 86' - '86 FF 93 93 93 FF A7 A7 A7 FF CA CA CA FF C4 C3' - 'C4 FF C3 C2 C2 FF C8 C8 C8 FF 8E 8E 8E FF 00 00' - '00 FA 00 00 00 37 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 5A 17 17 17 FF CE CE' - 'CE FF F2 F2 F2 FF EC EC EC FF E6 E6 E6 FF DC DC' - 'DC FF DB DB DB FF DA DA DA FF DA DA DA FF D9 D9' - 'D9 FF D8 D8 D8 FF D7 D7 D7 FF D4 D4 D4 FF D4 D4' - 'D4 FF D2 D2 D2 FF D0 D0 D0 FF CF CF CF FF CC CC' - 'CC FF CB CB CB FF CB CB CB FF CC CC CC FF CA CA' - 'CA FF C7 C7 C7 FF C9 C9 C9 FF 8D 8D 8D FF 00 00' - '00 FA 00 00 00 37 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 5A 17 17 17 FF D0 D0' - 'D0 FF FF FF FF FF F8 F8 F8 FF F8 F8 F8 FF F9 F9' - 'F9 FF F8 F8 F8 FF F7 F7 F7 FF F7 F7 F7 FF F6 F6' - 'F6 FF F6 F6 F6 FF F5 F5 F5 FF F5 F5 F5 FF F5 F5' - 'F5 FF F4 F4 F4 FF F4 F4 F4 FF F4 F4 F4 FF F3 F3' - 'F3 FF F3 F3 F3 FF F2 F2 F2 FF F1 F1 F1 FF F2 F2' - 'F2 FF F3 F3 F3 FF E8 E8 E8 FF 8D 8D 8D FF 00 00' - '00 FA 00 00 00 37 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 5B 17 17 17 FF D7 D7' - 'D7 FF F4 F4 F4 FF DB DB DB FF D8 D8 D8 FF DD DD' - 'DD FF DF DF DF FF DE DE DE FF DE DE DE FF DE DE' - 'DE FF DE DE DE FF DE DE DE FF DE DE DE FF DE DE' - 'DE FF DE DE DE FF DE DE DE FF DE DE DE FF DF DF' - 'DF FF DF DF DF FF E0 E0 E0 FF DD DD DD FF D8 D8' - 'D8 FF E0 E0 E0 FF FB FB FB FF 9C 9C 9C FF 00 00' - '00 FA 00 00 00 38 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 51 11 11 11 FF D3 D3' - 'D3 FF D9 D9 D9 FF AD AD AD FF B0 B0 B0 FF BE BE' - 'BE FF D9 D9 D9 FF D8 D8 D8 FF D7 D7 D7 FF D7 D7' - 'D7 FF D7 D7 D7 FF D6 D6 D6 FF D6 D6 D6 FF D6 D6' - 'D6 FF D6 D6 D6 FF D6 D6 D6 FF D7 D7 D7 FF D7 D7' - 'D7 FF D9 D9 D9 FF D3 D3 D3 FF AF AF AF FF B1 B1' - 'B1 FF B8 B8 B8 FF ED ED ED FF A0 A0 A0 FF 00 00' - '00 F7 00 00 00 2E 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 2B 00 00 00 F8 B3 B3' - 'B3 FF DC DC DC FF B9 B9 B9 FF BA BA BA FF BC BC' - 'BC FF D5 D5 D5 FF D3 D3 D3 FF D4 D4 D4 FF D8 D8' - 'D8 FF DF DF DF FF E3 E3 E3 FF E4 E4 E4 FF E4 E4' - 'E4 FF E3 E3 E3 FF DF DF DF FF D9 D9 D9 FF D4 D4' - 'D4 FF D3 D3 D3 FF CF CF CF FF BA BA BA FF B8 B8' - 'B8 FF B8 B8 B8 FF E9 E9 E9 FF 7C 7C 7C FF 00 00' - '00 DE 00 00 00 17 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 0C 00 00 00 E2 89 89' - '89 FF E5 E5 E5 FF CE CE CE FF D0 D0 D0 FF D0 D0' - 'D0 FF D1 D1 D1 FF DE DE DE FF E5 E5 E5 FF E5 E5' - 'E5 FF E2 E2 E2 FF DE DE DE FF DE DE DE FF DE DE' - 'DE FF DE DE DE FF E2 E2 E2 FF E6 E6 E6 FF E5 E5' - 'E5 FF DF DF DF FF D2 D2 D2 FF CF CF CF FF CF CF' - 'CF FF D0 D0 D0 FF E6 E6 E6 FF 54 54 54 FF 00 00' - '00 BC 00 00 00 07 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 BB 64 64' - '64 FF E2 E2 E2 FF CB CB CB FF CB CB CB FF CE CE' - 'CE FF E2 E2 E2 FF E0 E0 E0 FF D7 D7 D7 FF D3 D3' - 'D3 FF D4 D4 D4 FF D4 D4 D4 FF D4 D4 D4 FF D4 D4' - 'D4 FF D4 D4 D4 FF D4 D4 D4 FF D3 D3 D3 FF D7 D7' - 'D7 FF DF DF DF FF E4 E4 E4 FF D1 D1 D1 FF CB CB' - 'CB FF CF CF CF FF D7 D7 D7 FF 34 34 34 FF 00 00' - '00 99 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 93 3E 3E' - '3E FF DB DB DB FF CC CC CC FF CC CC CC FF DC DC' - 'DC FF D7 D7 D7 FF CF CF CF FF D0 D0 D0 FF D0 D0' - 'D0 FF D0 D0 D0 FF CF CF CF FF CD CD CD FF CD CD' - 'CD FF CD CD CD FF CD CD CD FF CD CD CD FF CD CD' - 'CD FF CC CC CC FF D3 D3 D3 FF DC DC DC FF C9 C9' - 'C9 FF CF CF CF FF C2 C2 C2 FF 1A 1A 1A FF 00 00' - '00 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 6B 21 21' - '21 FF C9 C9 C9 FF D2 D2 D2 FF D3 D3 D3 FF D7 D7' - 'D7 FF CF CF CF FF D0 D0 D0 FF D0 D0 D0 FF D0 D0' - 'D0 FF D0 D0 D0 FF CF CF CF FF CA CA CA FF C7 C7' - 'C7 FF C8 C8 C8 FF C8 C8 C8 FF C8 C8 C8 FF C8 C8' - 'C8 FF C8 C8 C8 FF C7 C7 C7 FF CF CF CF FF CC CC' - 'CC FF D0 D0 D0 FF A9 A9 A9 FF 04 04 04 FF 00 00' - '00 4D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 44 0B 0B' - '0B FF B1 B1 B1 FF D8 D8 D8 FF D2 D2 D2 FF D2 D2' - 'D2 FF D1 D1 D1 FF D1 D1 D1 FF D1 D1 D1 FF D1 D1' - 'D1 FF D1 D1 D1 FF D2 D2 D2 FF D1 D1 D1 FF C9 C9' - 'C9 FF C4 C4 C4 FF C3 C3 C3 FF C3 C3 C3 FF C4 C4' - 'C4 FF C4 C4 C4 FF C4 C4 C4 FF C5 C5 C5 FF C5 C5' - 'C5 FF D2 D2 D2 FF 86 86 86 FF 00 00 00 F3 00 00' - '00 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00' - '00 F4 93 93 93 FF DF DF DF FF D2 D2 D2 FF D2 D2' - 'D2 FF D5 D5 D5 FF D4 D4 D4 FF D4 D4 D4 FF D4 D4' - 'D4 FF D4 D4 D4 FF D4 D4 D4 FF D5 D5 D5 FF D5 D5' - 'D5 FF D2 D2 D2 FF CD CD CD FF C7 C7 C7 FF C3 C3' - 'C3 FF C2 C2 C2 FF C2 C2 C2 FF BE BE BE FF BC BC' - 'BC FF D0 D0 D0 FF 67 67 67 FF 00 00 00 D9 00 00' - '00 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 0B 00 00' - '00 DE 75 75 75 FF DE DE DE FF D7 D7 D7 FF D1 D1' - 'D1 FF DA DA DA FF D9 D9 D9 FF D9 D9 D9 FF D9 D9' - 'D9 FF D9 D9 D9 FF D9 D9 D9 FF D9 D9 D9 FF D9 D9' - 'D9 FF DA DA DA FF DA DA DA FF DA DA DA FF D8 D8' - 'D8 FF D6 D6 D6 FF D5 D5 D5 FF C7 C7 C7 FF C5 C5' - 'C5 FF CD CD CD FF 4B 4B 4B FF 00 00 00 BD 00 00' - '00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 B7 53 53 53 FF D9 D9 D9 FF E1 E1 E1 FF D5 D5' - 'D5 FF D5 D5 D5 FF DF DF DF FF DF DF DF FF DE DE' - 'DE FF DE DE DE FF DE DE DE FF DE DE DE FF DE DE' - 'DE FF DE DE DE FF DE DE DE FF DE DE DE FF DF DF' - 'DF FF E0 E0 E0 FF D7 D7 D7 FF D6 D6 D6 FF DE DE' - 'DE FF C3 C3 C3 FF 2C 2C 2C FF 00 00 00 9A 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 90 31 31 31 FF CB CB CB FF E5 E5 E5 FF E4 E4' - 'E4 FF DA DA DA FF D7 D7 D7 FF DD DD DD FF E2 E2' - 'E2 FF E4 E4 E4 FF E4 E4 E4 FF E4 E4 E4 FF E4 E4' - 'E4 FF E4 E4 E4 FF E4 E4 E4 FF E2 E2 E2 FF DE DE' - 'DE FF D7 D7 D7 FF D9 D9 D9 FF E4 E4 E4 FF DF DF' - 'DF FF AB AB AB FF 17 17 17 FF 00 00 00 73 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 67 1A 1A 1A FF B3 B3 B3 FF E8 E8 E8 FF EA EA' - 'EA FF EA EA EA FF E5 E5 E5 FF DE DE DE FF DD DD' - 'DD FF DD DD DD FF DF DF DF FF E0 E0 E0 FF E0 E0' - 'E0 FF E0 E0 E0 FF DD DD DD FF DC DC DC FF DD DD' - 'DD FF E5 E5 E5 FF E9 E9 E9 FF EC EC EC FF DF DF' - 'DF FF 95 95 95 FF 04 04 04 FF 00 00 00 4D 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 41 07 07 07 FE 9F 9F 9F FF E8 E8 E8 FF CD CD' - 'CD FF CB CB CB FF E7 E7 E7 FF EF EF EF FF ED ED' - 'ED FF EA EA EA FF E9 E9 E9 FF E7 E7 E7 FF E7 E7' - 'E7 FF E8 E8 E8 FF EA EA EA FF EC EC EC FF F0 F0' - 'F0 FF DB DB DB FF C6 C6 C6 FF DC DC DC FF E1 E1' - 'E1 FF 78 78 78 FF 00 00 00 F4 00 00 00 29 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 1E 00 00 00 F4 83 83 83 FF E0 E0 E0 FF C9 C9' - 'C9 FF C6 C6 C6 FF EA EA EA FF FA FA FA FF F8 F8' - 'F8 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8' - 'F8 FF F8 F8 F8 FF F8 F8 F8 FF F8 F8 F8 FF FB FB' - 'FB FF D9 D9 D9 FF C4 C4 C4 FF D1 D1 D1 FF D2 D2' - 'D2 FF 50 50 50 FF 00 00 00 DA 00 00 00 15 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 BB 24 24 24 FF 98 98 98 FF CF CF' - 'CF FF D2 D2 D2 FF D7 D7 D7 FF D7 D7 D7 FF D7 D7' - 'D7 FF D7 D7 D7 FF D7 D7 D7 FF D7 D7 D7 FF D7 D7' - 'D7 FF D7 D7 D7 FF D7 D7 D7 FF D7 D7 D7 FF D7 D7' - 'D7 FF D7 D7 D7 FF D2 D2 D2 FF C8 C8 C8 FF 7C 7C' - '7C FF 0D 0D 0D FF 00 00 00 9A 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 31 00 00 00 DF 0F 0F 0F FF 29 29' - '29 FF 29 29 29 FF 28 28 28 FF 28 28 28 FF 28 28' - '28 FF 28 28 28 FF 28 28 28 FF 28 28 28 FF 28 28' - '28 FF 28 28 28 FF 28 28 28 FF 28 28 28 FF 28 28' - '28 FF 28 28 28 FF 2A 2A 2A FF 26 26 26 FF 08 08' - '08 FF 00 00 00 C8 00 00 00 1D 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 14 00 00 00 62 00 00' - '00 87 00 00 00 84 00 00 00 84 00 00 00 84 00 00' - '00 84 00 00 00 84 00 00 00 84 00 00 00 84 00 00' - '00 84 00 00 00 84 00 00 00 84 00 00 00 84 00 00' - '00 84 00 00 00 84 00 00 00 84 00 00 00 86 00 00' - '00 55 00 00 00 0A 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' @@ -7234,85 +11612,1610 @@ END '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' - 'FF FF FF FF FF FF FF FF FF FF F0 00 00 0F E0 00' - '00 07 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 E0 00' - '00 07 E0 00 00 07 E0 00 00 07 E0 00 00 07 E0 00' - '00 07 E0 00 00 07 F0 00 00 0F F0 00 00 0F F0 00' - '00 0F F0 00 00 0F F0 00 00 0F F8 00 00 1F F8 00' - '00 1F FC 00 00 3F FF FF FF FF FF FF FF FF 28 00' - '00 00 10 00 00 00 20 00 00 00 01 00 20 00 00 00' - '00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 80 00 00 00 00 01 00 00 80 00' + '00 00 00 01 00 00 C0 00 00 00 00 03 00 00 C0 00' + '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' + '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' + '00 00 00 1F 00 00 F0 00 00 00 00 1F 00 00 F8 00' + '00 00 00 3F 00 00 F8 00 00 00 00 3F 00 00 FC 00' + '00 00 00 7F 00 00 FC 00 00 00 00 7F 00 00 FE 00' + '00 00 00 FF 00 00 FE 00 00 00 00 FF 00 00 FF 00' + '00 00 01 FF 00 00 FF 00 00 00 01 FF 00 00 FF 80' + '00 00 03 FF 00 00 FF 80 00 00 03 FF 00 00 FF C0' + '00 00 07 FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 28 00' + '00 00 40 00 00 00 80 00 00 00 01 00 04 00 00 00' + '00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 11 04 04' - '04 8A 16 16 16 A8 1B 1B 1B A8 18 18 18 A8 16 16' - '16 A8 15 15 15 A8 16 16 16 A8 15 15 15 A8 15 15' - '15 A8 14 13 14 A8 19 12 16 A8 05 03 04 84 00 00' - '00 0F 00 00 00 00 00 00 00 00 05 05 05 83 9B 9B' - '9B FF C1 C1 C1 FF 83 83 83 FF 9A 9A 9A FF AD AD' - 'AD FF B2 B2 B2 FF B3 B3 B3 FF B5 B5 B5 FF B2 B1' - 'B2 FF AC B2 AF FF 59 B0 84 FF 5B 6C 64 FF 04 01' - '03 71 00 00 00 00 00 00 00 00 13 13 13 B3 DC DC' - 'DC FF CB CB CB FF 65 65 65 FF 8A 8A 8A FF A7 A7' - 'A7 FF AB AB AB FF 95 95 95 FF A2 A2 A2 FF 95 94' - '95 FF BA BF BC FF 8F CC AD FF 93 A1 9A FF 09 06' - '07 95 00 00 00 00 00 00 00 00 13 13 13 B2 D7 D7' - 'D7 FF DE DE DE FF A1 A1 A1 FF AC AC AC FF BA BA' - 'BA FF B7 B7 B7 FF A2 A2 A2 FF A9 A9 A9 FF 9D 9D' - '9D FF C2 C1 C1 FF CF C9 CC FF A2 A0 A1 FF 06 06' - '06 93 00 00 00 00 00 00 00 00 14 14 14 B3 D9 D9' - 'D9 FF ED ED ED FF EA EA EA FF E8 E8 E8 FF E5 E5' - 'E5 FF E5 E5 E5 FF E6 E6 E6 FF E4 E4 E4 FF E4 E4' - 'E4 FF DF DF DF FF E3 E3 E3 FF B2 B2 B2 FF 04 05' - '05 94 00 00 00 00 00 00 00 00 11 11 11 A5 C6 C6' - 'C6 FF BC BC BC FF CA CA CA FF D9 D9 D9 FF DA DA' - 'DA FF DA DA DA FF DB DB DB FF DA DA DA FF DA DA' - 'DA FF C6 C6 C6 FF C5 C5 C5 FF B4 B4 B4 FF 04 04' - '04 8B 00 00 00 00 00 00 00 00 04 04 04 7B A8 A8' - 'A8 FF CE CE CE FF CF CF CF FF DC DC DC FF DD DD' - 'DD FF DE DE DE FF DE DE DE FF DE DE DE FF DC DC' - 'DC FF CF CF CF FF D4 D4 D4 FF 91 91 91 FF 00 00' - '00 69 00 00 00 00 00 00 00 00 00 00 00 53 85 85' - '85 FF DB DB DB FF D7 D7 D7 FF D3 D3 D3 FF D0 D0' - 'D0 FF CE CE CE FF CD CD CD FF CE CE CE FF D0 D0' - 'D0 FF D6 D6 D6 FF D9 D9 D9 FF 6E 6E 6E FE 00 00' - '00 43 00 00 00 00 00 00 00 00 00 00 00 2E 64 64' - '64 FA DD DD DD FF D3 D3 D3 FF D0 D0 D0 FF D1 D1' - 'D1 FF CF CF CF FF C8 C8 C8 FF C4 C4 C4 FF C4 C4' - 'C4 FF C5 C5 C5 FF CF CF CF FF 4D 4D 4D F3 00 00' - '00 20 00 00 00 00 00 00 00 00 00 00 00 0E 42 42' - '42 E8 D7 D7 D7 FF D6 D6 D6 FF D9 D9 D9 FF D8 D8' - 'D8 FF D8 D8 D8 FF D7 D7 D7 FF D4 D4 D4 FF D3 D3' - 'D3 FF CD CD CD FF C5 C5 C5 FF 2C 2C 2C D8 00 00' - '00 0A 00 00 00 00 00 00 00 00 00 00 00 02 22 22' - '22 C5 D1 D1 D1 FF E3 E3 E3 FF DC DC DC FF DF DF' - 'DF FF E0 E0 E0 FF E0 E0 E0 FF E0 E0 E0 FF DE DE' - 'DE FF E5 E5 E5 FF C0 C0 C0 FF 11 11 11 B6 00 00' - '00 01 00 00 00 00 00 00 00 00 00 00 00 00 08 08' - '08 A1 BE BE BE FF E1 E1 E1 FF EA EA EA FF EB EB' - 'EB FF EB EB EB FF EB EB EB FF EC EC EC FF E5 E5' - 'E5 FF E5 E5 E5 FF A3 A3 A3 FF 01 01 01 93 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 74 78 78 78 FF C7 C7 C7 FF D9 D9 D9 FF DE DE' - 'DE FF DD DD DD FF DD DD DD FF DF DF DF FF D4 D4' - 'D4 FF C7 C7 C7 FF 60 60 60 FF 00 00 00 65 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 11 04 04 04 94 1D 1D 1D CC 1C 1C 1C CC 1C 1C' - '1C CC 1C 1C 1C CC 1C 1C 1C CC 1C 1C 1C CC 1D 1D' - '1D CC 1B 1B 1B CB 02 02 02 88 00 00 00 0C 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 FF FF 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 C0 03 00 00 C0 03 00 00 C0 03' - '00 00 FF FF 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 80 00 00 87 77 77 77 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 77 77 77 78 00 00 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 00 00 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 00 08 88 88 88 87 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 78 88 88 88 80 08 88 88 88 78 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 8A' + 'AA A7 88 88 88 80 08 88 88 87 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 8A' + 'AA A8 78 88 88 80 88 88 88 87 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 78 88 88 88 87 77 77 78 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 77 77 78 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 77 77 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 77 77 88 88 88 87 88 88 FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF 88 88 78 88 88 78 88 8F 77 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '77 77 F8 88 87 88 08 88 88 F7 88 FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'F8 88 7F 88 88 80 08 88 8F 78 8F 77 77 77 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77' + '7F 88 87 F8 88 00 00 88 F7 88 F7 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '87 F8 88 7F 88 00 00 88 78 8F 78 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 7F 88 87 80 00 00 08 78 F7 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 87 F8 78 80 00 00 08 78 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 88 78 00 00 00 00 87 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 80 00 00 00 00 87 88 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 87 80 00 00 00 00 08 78 88 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 88 78 00 00 00 00 00 08 78 88 88 88 88 88 88' + '88 88 88 88 8F FF FF FF 88 88 88 88 88 88 88 88' + '88 88 78 00 00 00 00 00 00 87 88 88 88 88 88 88' + '88 88 88 FF F8 88 88 88 FF F8 88 88 88 88 88 88' + '88 87 80 00 00 00 00 00 00 87 88 88 88 88 88 88' + '88 88 FF 88 88 88 88 88 88 8F F8 88 88 88 88 88' + '88 87 80 00 00 00 00 00 00 08 78 88 88 88 88 88' + '88 8F 88 88 88 88 88 88 88 88 8F 88 88 88 88 88' + '88 78 00 00 00 00 00 00 00 08 78 88 88 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88' + '88 78 00 00 00 00 00 00 00 00 87 88 88 88 88 88' + '88 87 88 88 88 88 88 88 88 88 87 88 88 88 88 88' + '87 80 00 00 00 00 00 00 00 00 87 88 88 88 88 88' + '88 88 77 88 88 88 88 88 88 87 78 88 88 88 88 88' + '87 80 00 00 00 00 00 00 00 00 08 77 78 88 88 88' + '88 88 88 77 78 88 88 88 77 78 88 88 88 88 88 77' + '78 00 00 00 00 00 00 00 00 00 08 78 87 88 88 88' + '88 88 88 88 87 77 77 77 88 88 88 88 88 88 87 88' + '78 00 00 00 00 00 00 00 00 00 00 87 88 78 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 78 87' + '80 00 00 00 00 00 00 00 00 00 00 87 87 87 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 87 88 F7' + '80 00 00 00 00 00 00 00 00 00 00 08 78 78 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 8F 78' + '00 00 00 00 00 00 00 00 00 00 00 08 78 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 78' + '00 00 00 00 00 00 00 00 00 00 00 00 87 88 88 88' + '88 88 8F FF FF 88 88 88 88 88 88 88 88 88 87 80' + '00 00 00 00 00 00 00 00 00 00 00 00 87 88 88 88' + '88 88 78 88 88 78 88 88 88 88 88 88 88 88 87 80' + '00 00 00 00 00 00 00 00 00 00 00 00 08 78 88 88' + '88 88 87 77 77 88 88 88 88 88 88 88 88 88 78 00' + '00 00 00 00 00 00 00 00 00 00 00 00 08 78 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 78 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 87 77 77' + '77 77 77 77 77 77 77 77 77 77 77 77 77 77 80 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 88 88 88' + '88 88 88 88 88 88 88 88 88 88 88 88 88 88 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF F0 00 00 00 00 00 00 07 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 80 00 00 00 00 00 00 01 80 00' + '00 00 00 00 00 01 80 00 00 00 00 00 00 01 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00' + '00 00 00 00 00 01 80 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 07 E0 00' + '00 00 00 00 00 07 E0 00 00 00 00 00 00 0F F0 00' + '00 00 00 00 00 1F F0 00 00 00 00 00 00 1F F8 00' + '00 00 00 00 00 3F F8 00 00 00 00 00 00 3F FC 00' + '00 00 00 00 00 7F FC 00 00 00 00 00 00 7F FE 00' + '00 00 00 00 00 FF FE 00 00 00 00 00 00 FF FF 00' + '00 00 00 00 01 FF FF 00 00 00 00 00 01 FF FF 80' + '00 00 00 00 03 FF FF 80 00 00 00 00 03 FF FF C0' + '00 00 00 00 07 FF FF C0 00 00 00 00 07 FF FF E0' + '00 00 00 00 0F FF FF E0 00 00 00 00 0F FF FF F0' + '00 00 00 00 1F FF FF F0 00 00 00 00 1F FF FF F8' + '00 00 00 00 3F FF FF F8 00 00 00 00 3F FF FF FC' + '00 00 00 00 7F FF FF FC 00 00 00 00 FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 40 00 00 00 80 00' + '00 00 01 00 08 00 00 00 00 00 00 10 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 C0 C0 C0 00 C0 DC' + 'C0 00 F0 CA A6 00 50 50 50 00 52 52 52 00 53 53' + '53 00 55 55 55 00 57 57 57 00 58 58 58 00 5A 5A' + '5A 00 5C 5C 5C 00 5D 5D 5D 00 5F 5F 5F 00 60 60' + '60 00 61 61 61 00 62 62 62 00 63 63 63 00 64 64' + '64 00 65 65 65 00 66 66 66 00 67 67 67 00 68 68' + '68 00 69 69 69 00 6A 6A 6A 00 6B 6B 6B 00 6C 6C' + '6C 00 6D 6D 6D 00 6E 6E 6E 00 6F 6F 6F 00 70 70' + '70 00 71 71 71 00 72 72 72 00 73 73 73 00 74 74' + '74 00 75 75 75 00 76 76 76 00 77 77 77 00 78 78' + '78 00 79 79 79 00 7A 7A 7A 00 7B 7B 7B 00 7C 7C' + '7C 00 7D 7D 7D 00 7E 7E 7E 00 7F 7F 7F 00 35 B4' + '35 00 38 B7 38 00 39 B8 39 00 1B DA 1B 00 1C DB' + '1C 00 FF 00 FF 00 80 80 80 00 81 81 81 00 82 82' + '82 00 83 83 83 00 84 84 84 00 85 85 85 00 86 86' + '86 00 87 87 87 00 88 88 88 00 89 89 89 00 8A 8A' + '8A 00 8B 8B 8B 00 8C 8C 8C 00 8D 8D 8D 00 8E 8E' + '8E 00 8F 8F 8F 00 90 90 90 00 91 91 91 00 92 92' + '92 00 93 93 93 00 94 94 94 00 95 95 95 00 96 96' + '96 00 97 97 97 00 98 98 98 00 99 99 99 00 9A 9A' + '9A 00 9B 9B 9B 00 9C 9C 9C 00 9D 9D 9D 00 9E 9E' + '9E 00 9F 9F 9F 00 A0 A0 A0 00 A1 A1 A1 00 A2 A2' + 'A2 00 A3 A3 A3 00 A4 A4 A4 00 A5 A5 A5 00 A6 A6' + 'A6 00 A7 A7 A7 00 A8 A8 A8 00 A9 A9 A9 00 AA AA' + 'AA 00 AB AB AB 00 AC AC AC 00 AD AD AD 00 AE AE' + 'AE 00 AF AF AF 00 B0 B0 B0 00 B1 B1 B1 00 B2 B2' + 'B2 00 B3 B3 B3 00 B4 B4 B4 00 B5 B5 B5 00 B6 B6' + 'B6 00 B7 B7 B7 00 B8 B8 B8 00 B9 B9 B9 00 BA BA' + 'BA 00 BB BB BB 00 BC BC BC 00 BD BD BD 00 BE BE' + 'BE 00 BF BF BF 00 C0 C0 C0 00 C1 C1 C1 00 C2 C2' + 'C2 00 C3 C3 C3 00 C4 C4 C4 00 C5 C5 C5 00 C6 C6' + 'C6 00 C7 C7 C7 00 C8 C8 C8 00 C9 C9 C9 00 CA CA' + 'CA 00 CB CB CB 00 CC CC CC 00 CD CD CD 00 CE CE' + 'CE 00 CF CF CF 00 D0 D0 D0 00 D1 D1 D1 00 D2 D2' + 'D2 00 D4 D4 D4 00 D7 D7 D7 00 D9 D9 D9 00 DA DA' + 'DA 00 DB DB DB 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 F0 FB FF 00 A4 A0 A0 00 80 80' + '80 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 59 59 5A 5C 5D 5E 60 61 61 63 64 65 66 67' + '68 69 69 6A 6A 69 68 67 65 64 63 61 60 5F 5D 5C' + '5A 59 57 56 54 52 50 4D 4C 4B 48 47 45 43 42 40' + '3E 3C 3B 32 30 2F 2D 2C 2C 2D 33 00 00 00 00 00' + '5B 4B 4D 4E 4F 50 52 53 56 63 64 65 66 67 68 69' + '6A 6B 6C 6D 6D 6B 6A 68 67 66 64 63 62 61 5F 5D' + '5C 5A 58 57 55 53 51 4F 4D 4B 49 47 45 43 41 3F' + '3D 3B 32 2A 23 21 20 1E 1D 1B 1B 45 00 00 00 00' + '63 5D 5E 5E 60 61 61 62 64 65 66 67 68 69 6A 6B' + '6C 6D 6D 6E 6E 6D 6C 6A 69 68 66 65 63 62 60 5F' + '5D 5C 5A 58 56 55 53 52 4F 4E 4B 4A 48 45 44 42' + '40 3D 3A 32 31 2F 2D 2C 2B 2A 2A 40 00 00 00 00' + '61 5F 5F 60 61 62 60 5B 5D 5E 60 61 62 63 64 66' + '67 68 68 69 69 69 68 67 65 63 62 61 60 5F 5C 5B' + '5A 59 58 55 53 53 51 50 4E 4D 4A 48 46 44 43 41' + '3F 3D 3B 33 30 2E 2C 2B 2B 2A 29 2E 00 00 00 00' + '60 60 60 61 62 63 3D 4E 4F 4F 50 50 51 50 50 51' + '51 51 51 51 50 4D 4B 49 47 45 43 41 40 3E 3C 3A' + '33 31 30 2D 2C 2A 29 27 25 23 22 21 1F 1D 1B 1A' + '18 16 14 13 12 0D 2C 2B 2B 29 28 28 00 00 00 70' + '60 60 61 62 63 5D 4B 65 66 67 68 69 69 68 68 6A' + '6C 6B 6D 6E 6C 68 65 64 62 60 5D 5C 5A 58 56 54' + '52 50 4E 4C 49 47 45 43 41 3F 3D 3A 32 30 2E 2C' + '2A 36 38 37 34 14 20 2A 2A 28 26 26 54 00 00 6C' + '61 62 62 63 63 45 64 65 66 67 68 69 68 68 6B 6C' + '6D 6E 6E 6D 6A 67 66 64 62 61 5E 5B 5A 58 56 53' + '51 4F 4D 4B 49 47 46 44 40 3E 3D 3A 32 30 2D 2B' + '28 35 37 37 34 1C 13 2A 29 26 24 23 40 00 00 68' + '63 63 63 64 66 43 64 66 67 68 68 68 69 6A 6C 6C' + '6D 6E 6E 6C 69 67 66 64 62 60 5E 5C 5A 57 55 52' + '50 4F 4C 4B 49 47 45 43 3F 3E 3C 3A 31 2F 2D 2A' + '27 25 22 21 1E 1C 10 29 28 25 22 20 27 00 00 43' + '42 42 43 43 44 5A 64 64 63 65 66 67 67 69 6B 6C' + '6B 6D 6D 69 67 66 64 63 60 5E 5D 5A 58 56 54 51' + '4F 4E 4B 49 48 47 44 42 3F 3D 3B 33 31 2E 2C 2A' + '28 25 23 21 1D 1C 18 0E 0F 0E 0D 0C 0C 00 00 60' + '5F 5F 60 60 60 61 61 62 63 64 64 65 66 66 68 69' + '69 6A 69 67 65 63 62 60 5E 5C 5B 58 56 54 52 4F' + '4E 4C 4B 49 47 45 43 41 3E 3C 3A 32 30 2E 2D 2B' + '29 27 24 22 1F 1D 1F 1C 1B 19 17 15 14 00 62 57' + '59 54 46 4A 4B 4B 4F 52 54 55 56 57 58 58 5A 5B' + '5A 5A 5A 59 59 56 56 57 55 54 52 50 4F 4D 4B 49' + '47 46 44 42 40 3E 3C 33 32 31 2E 2B 28 25 23 22' + '20 1F 1D 1A 17 15 14 13 13 16 2D 2E 2E 4E 53 33' + '1D 56 31 55 59 5A 5F 67 71 74 78 7B 7E 7E 80 81' + '7F 81 83 84 85 84 86 87 86 86 86 85 85 85 84 84' + '83 82 82 82 82 81 80 7F 80 7F 7E 7E 7C 7B 7B 79' + '79 78 78 76 75 6D 64 5F 5E 5E 3A 26 5D 63 6C 3E' + '52 4E 26 50 54 55 56 57 4E 4C 4C 4E 4F 51 51 52' + '53 54 56 56 57 58 59 5A 5A 5C 5C 5C 5B 5A 5A 5B' + '5B 5C 5C 5C 5C 5C 5C 5B 5B 5B 5B 5D 5D 5C 5D 5D' + '5E 5D 5F 61 6C 81 83 62 57 5B 3C 55 4A 70 00 70' + '6B 5C 55 52 3D 12 32 58 65 6D 70 73 76 76 78 77' + '76 77 79 07 7B 7D 7E 7D 7E 7E 7E 7F 7F 7F 7F 7E' + '7D 7C 7C 07 79 77 77 76 74 71 70 6F 70 73 75 75' + '74 73 71 71 77 75 54 81 6A 74 7B 63 7E 00 00 56' + '50 51 4D 1F 0C 2A 4F 47 2C 3B 3B 3D 3E 3F 3F 40' + '40 42 43 43 43 44 45 45 44 45 46 46 46 46 45 45' + '45 45 45 45 45 44 45 44 43 43 43 43 43 43 43 43' + '44 43 43 50 4E 6F 8E 51 73 6D 57 51 65 00 00 6D' + '4C 43 0A 11 41 4E 3A 2B 54 5B 5D 5F 60 61 61 63' + '63 64 65 66 67 68 69 6A 6A 6B 6C 6D 6D 6E 6E 6F' + '6F 70 70 71 72 72 73 73 74 74 75 75 75 75 76 76' + '77 77 60 4D 65 4E 57 8C 5F 5B 5D 4F 00 00 00 00' + '53 31 0B 4B 4C 28 40 56 5A 5C 5D 5E 5F 60 61 62' + '63 64 65 66 67 68 69 6A 6B 6C 6D 6D 6E 6F 70 71' + '72 72 73 74 75 75 76 77 77 78 78 79 79 07 07 7B' + '7B 7B 7B 6D 45 59 58 4D 70 4D 54 66 00 00 00 00' + '69 48 1C 59 3F 4D 56 5B 5C 5D 5E 5F 60 61 62 63' + '64 65 66 67 68 69 6A 6B 6C 6C 6D 6E 6F 70 71 72' + '72 73 74 75 75 76 77 78 78 79 79 07 07 7B 7B 7C' + '7C 7C 7D 7D 07 56 4A 53 4B 4C 50 00 00 00 00 00' + '00 4D 28 4D 53 50 5A 5B 5C 5D 5E 5F 60 61 62 63' + '64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 72' + '73 74 75 76 76 77 78 78 79 07 07 7B 7B 7C 7C 7D' + '7D 7D 7E 7E 7E 7E 5C 50 4A 4D 00 00 00 00 00 00' + '00 66 44 2C 5E 45 5B 5C 5D 5E 5F 60 61 62 63 64' + '65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 71 72 73' + '74 75 76 77 77 78 79 79 07 7B 7B 7C 7C 7D 7D 7E' + '7E 7F 7F 7F 7F 7F 50 50 4C 54 00 00 00 00 00 00' + '00 00 4C 29 56 4B 59 5C 5D 5E 5F 60 61 63 64 65' + '66 67 68 69 6A 6B 6C 6D 6E 6E 6F 70 71 72 73 74' + '75 76 77 77 78 79 07 07 7B 7C 7C 7D 7E 7E 7F 7F' + '7F 80 80 80 80 6D 4C 4A 4F 00 00 00 00 00 00 00' + '00 00 63 41 3C 55 4D 5D 5E 5F 60 61 62 63 64 65' + '66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75' + '76 76 77 78 79 07 7B 7B 7C 7D 7D 7E 7F 7F 80 80' + '80 81 81 81 82 55 4D 4E 55 00 00 00 00 00 00 00' + '00 00 00 4F 2D 58 4A 5D 5E 5F 60 61 62 64 65 66' + '67 68 69 6A 6B 6C 6D 6E 6F 70 7C 8B 8F 91 8F 8D' + '88 7D 74 78 07 7B 7B 7C 7D 7E 7E 7F 80 80 81 81' + '82 82 82 82 76 4A 4E 50 00 00 00 00 00 00 00 00' + '00 00 00 63 43 47 51 5B 5F 60 61 62 63 64 65 66' + '67 68 69 6A 6B 6D 6A 6B 73 7C 7F 79 73 70 70 73' + '79 85 91 8E 75 76 7C 7D 7E 7F 7F 80 81 81 82 82' + '83 83 83 84 60 4D 50 5C 00 00 00 00 00 00 00 00' + '00 00 00 00 4F 33 59 55 5F 60 61 62 63 64 65 67' + '68 69 6A 6B 6C 63 5F 6B 6E 70 72 73 74 75 76 77' + '78 78 72 72 88 6F 70 7E 7F 7F 80 81 81 82 83 83' + '84 84 84 80 4F 4F 55 00 00 00 00 00 00 00 00 00' + '00 00 00 00 63 43 52 55 5F 60 62 63 64 65 66 67' + '68 69 6A 6B 66 56 68 6F 70 71 73 74 74 76 77 77' + '78 79 07 7B 75 6E 62 76 7F 80 81 82 82 83 84 84' + '85 85 86 6C 50 52 60 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 52 48 5B 5A 61 62 63 64 65 66 67' + '68 6A 6B 6C 60 5B 6F 70 71 72 73 74 75 76 77 78' + '79 07 7B 7C 7D 6C 64 66 80 81 82 83 83 84 85 85' + '86 86 84 58 55 57 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 63 4F 5A 58 61 62 63 65 66 67 68' + '69 6A 6B 6C 5E 5D 6C 70 71 73 74 75 76 77 78 79' + '07 7B 7C 7D 07 65 4C 75 81 82 83 84 84 85 86 86' + '87 87 70 53 59 63 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 54 54 5E 5D 63 64 65 66 67 68' + '69 6A 6B 6D 6D 60 5D 67 70 73 74 75 76 77 78 79' + '07 7B 07 70 5A 41 67 81 82 83 84 84 85 86 87 87' + '88 7C 57 5A 5A 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 60 54 57 62 60 64 65 66 67 68' + '6A 6B 6C 6D 6E 6F 70 68 5A 60 67 6C 6E 6F 70 6E' + '6A 62 4F 47 56 7B 81 82 83 83 84 85 86 87 88 88' + '79 5F 5B 59 63 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 4D 4E 57 63 64 65 67 68 69' + '6A 6B 6C 6D 6E 6F 70 72 75 6A 61 5B 57 56 57 5B' + '5F 64 73 7F 7F 80 81 82 83 84 85 86 87 88 86 71' + '60 55 57 5D 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 56 55 52 4C 63 65 68 69' + '6A 6B 6C 6E 6F 70 71 72 73 74 75 76 77 79 07 7B' + '7C 7D 7E 7F 80 81 82 83 84 85 86 87 88 7C 60 61' + '62 63 5A 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 57 41 2E 18 5A 59 68 69' + '6A 6C 6D 6E 6F 70 71 72 73 74 76 77 78 79 07 7B' + '7C 7D 7E 7F 80 81 82 84 85 86 86 87 88 63 5D 70' + '90 6A 5B 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 33 50 40 48 65 68 6A' + '6B 6C 6D 6E 6F 70 71 73 74 75 76 77 78 79 07 7B' + '7D 7E 7F 80 81 82 83 84 85 86 87 88 89 82 6E 6B' + '80 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 5A 5A 61 66 67 69 6A' + '6B 6C 6D 6E 6F 71 72 73 74 75 76 77 78 07 7B 7C' + '7D 7E 7F 80 81 82 83 85 86 87 88 89 8A 8B 8C 6D' + '5A 67 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 66 59 62 67 68 69 6A' + '6B 6C 6D 6E 70 71 72 73 77 7C 07 7B 79 07 7B 7C' + '7D 7E 7F 81 82 83 84 85 86 87 88 89 8A 8B 88 5F' + '66 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 5E 62 66 68 69 6A' + '6B 6C 6D 6F 70 6F 66 74 07 07 7C 79 72 77 7B 7C' + '7D 7F 80 81 82 83 84 85 86 87 89 8A 8B 8C 78 63' + '6B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 69 61 67 68 69 6A' + '6B 6C 6E 6F 70 6F 65 6C 70 75 74 6A 5E 74 7B 7D' + '7E 7F 80 81 82 83 84 86 87 88 89 8A 8B 8B 68 6A' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 62 67 68 69 6A' + '6B 6D 6E 6F 70 71 72 74 71 6F 71 77 79 07 7B 7D' + '7E 7F 80 81 82 84 85 86 87 88 89 8A 8B 7F 67 70' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 6E 66 6D 6E 6B' + '6C 6D 6E 6F 70 71 72 74 75 76 77 78 79 07 7C 7D' + '7E 7F 80 81 82 84 85 86 87 88 89 8A 85 6A 6B 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 68 75 74 74' + '71 70 72 73 74 75 75 77 78 79 07 7B 7B 7C 7D 7E' + '7F 7F 81 82 83 84 85 86 86 84 7B 75 75 6B 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF F0 00' + '00 00 00 00 00 07 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 80 00 00 00 00 00 00 01 80 00' + '00 00 00 00 00 01 80 00 00 00 00 00 00 01 80 00' + '00 00 00 00 00 01 80 00 00 00 00 00 00 01 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 80 00 00 00 00 00 00 01 80 00' + '00 00 00 00 00 01 80 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 07 E0 00' + '00 00 00 00 00 0F E0 00 00 00 00 00 00 0F F0 00' + '00 00 00 00 00 1F F0 00 00 00 00 00 00 1F F8 00' + '00 00 00 00 00 3F F8 00 00 00 00 00 00 3F FC 00' + '00 00 00 00 00 7F FC 00 00 00 00 00 00 7F FE 00' + '00 00 00 00 00 FF FE 00 00 00 00 00 00 FF FF 00' + '00 00 00 00 01 FF FF 00 00 00 00 00 01 FF FF 80' + '00 00 00 00 03 FF FF C0 00 00 00 00 07 FF FF C0' + '00 00 00 00 07 FF FF E0 00 00 00 00 0F FF FF E0' + '00 00 00 00 0F FF FF E0 00 00 00 00 1F FF FF F0' + '00 00 00 00 1F FF FF F0 00 00 00 00 3F FF FF F8' + '00 00 00 00 3F FF FF F8 00 00 00 00 7F FF FF FC' + '00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 28 00' + '00 00 40 00 00 00 80 00 00 00 01 00 20 00 00 00' + '00 00 00 42 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 AA AA' + 'AA 78 9D 9D 9D E8 9C 9C 9C E8 9C 9C 9C E8 9D 9D' + '9D E8 9F 9F 9F E8 A0 A0 A0 E8 A1 A1 A1 E8 A3 A3' + 'A3 E8 A4 A4 A4 E8 A5 A5 A5 E8 A7 A7 A7 E8 A8 A8' + 'A8 E8 A9 A9 A9 E8 AA AA AA E8 AB AB AB E8 AC AC' + 'AC E8 AD AD AD E8 AD AD AD E8 AE AE AE E8 AE AE' + 'AE E8 AD AD AD E8 AC AC AC E8 AB AB AB E8 A9 A9' + 'A9 E8 A8 A8 A8 E8 A7 A7 A7 E8 A4 A4 A4 E8 A3 A3' + 'A3 E8 A2 A2 A2 E8 A0 A0 A0 E8 9F 9F 9F E8 9D 9D' + '9D E8 9C 9C 9C E8 99 99 99 E8 98 98 98 E8 96 96' + '96 E8 94 94 94 E8 92 92 92 E8 8F 8F 8F E8 8D 8D' + '8D E8 8C 8C 8C E8 89 89 89 E8 88 88 88 E8 86 86' + '86 E8 83 83 83 E8 82 82 82 E8 80 80 80 E8 7E 7E' + '7E E8 7C 7C 7C E8 7B 7B 7B E8 78 78 78 E8 75 75' + '75 E8 74 74 74 E8 72 72 72 E8 71 71 71 E8 71 71' + '71 E8 72 72 72 E8 75 75 75 DD 81 81 81 51 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 96' + '96 BE 91 91 91 FF 93 93 93 FF 94 94 94 FF 95 95' + '95 FF 96 96 96 FF 98 98 98 FF 99 99 99 FF 9C 9C' + '9C FF A9 A9 A9 FF AA AA AA FF AB AB AB FF AC AC' + 'AC FF AD AD AD FF AE AE AE FF AF AF AF FF B0 B0' + 'B0 FF B1 B1 B1 FF B2 B2 B2 FF B3 B3 B3 FF B3 B3' + 'B3 FF B1 B1 B1 FF B0 B0 B0 FF AE AE AE FF AD AD' + 'AD FF AC AC AC FF AA AA AA FF A9 A9 A9 FF A8 A8' + 'A8 FF A7 A7 A7 FF A5 A5 A5 FF A3 A3 A3 FF A2 A2' + 'A2 FF A0 A0 A0 FF 9E 9E 9E FF 9D 9D 9D FF 9B 9B' + '9B FF 99 99 99 FF 97 97 97 FF 95 95 95 FF 93 93' + '93 FF 91 91 91 FF 8F 8F 8F FF 8D 8D 8D FF 8B 8B' + '8B FF 89 89 89 FF 87 87 87 FF 85 85 85 FF 83 83' + '83 FF 81 81 81 FF 7E 7E 7E FF 76 76 76 FF 6F 6F' + '6F FF 6D 6D 6D FF 6C 6C 6C FF 6A 6A 6A FF 69 69' + '69 FF 67 67 67 FF 67 67 67 FF 73 73 73 B0 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 A8 A8' + 'A8 F1 A3 A3 A3 FF A4 A4 A4 FF A4 A4 A4 FF A6 A6' + 'A6 FF A7 A7 A7 FF A7 A7 A7 FF A8 A8 A8 FF AA AA' + 'AA FF AB AB AB FF AC AC AC FF AD AD AD FF AE AE' + 'AE FF AF AF AF FF B0 B0 B0 FF B1 B1 B1 FF B2 B2' + 'B2 FF B3 B3 B3 FF B3 B3 B3 FF B4 B4 B4 FF B4 B4' + 'B4 FF B3 B3 B3 FF B2 B2 B2 FF B0 B0 B0 FF AF AF' + 'AF FF AE AE AE FF AC AC AC FF AB AB AB FF A9 A9' + 'A9 FF A8 A8 A8 FF A6 A6 A6 FF A5 A5 A5 FF A3 A3' + 'A3 FF A2 A2 A2 FF A0 A0 A0 FF 9E 9E 9E FF 9C 9C' + '9C FF 9B 9B 9B FF 99 99 99 FF 98 98 98 FF 95 95' + '95 FF 94 94 94 FF 91 91 91 FF 90 90 90 FF 8E 8E' + '8E FF 8B 8B 8B FF 8A 8A 8A FF 88 88 88 FF 86 86' + '86 FF 83 83 83 FF 80 80 80 FF 7E 7E 7E FF 7D 7D' + '7D FF 7B 7B 7B FF 79 79 79 FF 78 78 78 FF 77 77' + '77 FF 76 76 76 FF 76 76 76 FF 81 81 81 EA 00 00' + '00 00 00 00 00 00 00 00 00 00 B7 B7 B7 2B A7 A7' + 'A7 FE A5 A5 A5 FF A5 A5 A5 FF A6 A6 A6 FF A7 A7' + 'A7 FF A8 A8 A8 FF A6 A6 A6 FF A1 A1 A1 FF A3 A3' + 'A3 FF A4 A4 A4 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8' + 'A8 FF A9 A9 A9 FF AA AA AA FF AC AC AC FF AD AD' + 'AD FF AE AE AE FF AE AE AE FF AF AF AF FF AF AF' + 'AF FF AF AF AF FF AE AE AE FF AD AD AD FF AB AB' + 'AB FF A9 A9 A9 FF A8 A8 A8 FF A7 A7 A7 FF A6 A6' + 'A6 FF A5 A5 A5 FF A2 A2 A2 FF A1 A1 A1 FF A0 A0' + 'A0 FF 9F 9F 9F FF 9E 9E 9E FF 9B 9B 9B FF 99 99' + '99 FF 99 99 99 FF 97 97 97 FF 96 96 96 FF 94 94' + '94 FF 93 93 93 FF 90 90 90 FF 8E 8E 8E FF 8C 8C' + '8C FF 8A 8A 8A FF 89 89 89 FF 87 87 87 FF 85 85' + '85 FF 83 83 83 FF 81 81 81 FF 7F 7F 7F FF 7C 7C' + '7C FF 7A 7A 7A FF 78 78 78 FF 77 77 77 FF 77 77' + '77 FF 76 76 76 FF 75 75 75 FF 7A 7A 7A FE 9B 9B' + '9B 29 00 00 00 00 00 00 00 00 B0 B0 B0 62 A6 A6' + 'A6 FF A6 A6 A6 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8' + 'A8 FF A9 A9 A9 FF 83 83 83 FF 94 94 94 FF 95 95' + '95 FF 95 95 95 FF 96 96 96 FF 96 96 96 FF 97 97' + '97 FF 96 96 96 FF 96 96 96 FF 97 97 97 FF 97 97' + '97 FF 97 97 97 FF 97 97 97 FF 97 97 97 FF 96 96' + '96 FF 93 93 93 FF 91 91 91 FF 8F 8F 8F FF 8D 8D' + '8D FF 8B 8B 8B FF 89 89 89 FF 87 87 87 FF 86 86' + '86 FF 84 84 84 FF 82 82 82 FF 80 80 80 FF 7F 7F' + '7F FF 7D 7D 7D FF 7C 7C 7C FF 79 79 79 FF 78 78' + '78 FF 76 76 76 FF 75 75 75 FF 73 73 73 FF 71 71' + '71 FF 6F 6F 6F FF 6E 6E 6E FF 6D 6D 6D FF 6B 6B' + '6B FF 69 69 69 FF 67 67 67 FF 66 66 66 FF 64 64' + '64 FF 62 62 62 FF 60 60 60 FF 5F 5F 5F FF 5D 5D' + '5D FF 55 55 55 FF 78 78 78 FF 77 77 77 FF 77 77' + '77 FF 75 75 75 FF 74 74 74 FF 74 74 74 FF 8D 8D' + '8D 65 00 00 00 00 00 00 00 00 B0 B0 B0 98 A6 A6' + 'A6 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8 A8 FF A9 A9' + 'A9 FF A3 A3 A3 FF 91 91 91 FF AB AB AB FF AC AC' + 'AC FF AD AD AD FF AE AE AE FF AF AF AF FF AF AF' + 'AF FF AE AE AE FF AE AE AE FF B0 B0 B0 FF B2 B2' + 'B2 FF B1 B1 B1 FF B3 B3 B3 FF B4 B4 B4 FF B2 B2' + 'B2 FF AE AE AE FF AB AB AB FF AA AA AA FF A8 A8' + 'A8 FF A6 A6 A6 FF A3 A3 A3 FF A2 A2 A2 FF A0 A0' + 'A0 FF 9E 9E 9E FF 9C 9C 9C FF 9A 9A 9A FF 98 98' + '98 FF 96 96 96 FF 94 94 94 FF 92 92 92 FF 8F 8F' + '8F FF 8D 8D 8D FF 8B 8B 8B FF 89 89 89 FF 87 87' + '87 FF 85 85 85 FF 83 83 83 FF 80 80 80 FF 7E 7E' + '7E FF 7C 7C 7C FF 7A 7A 7A FF 78 78 78 FF 76 76' + '76 FF 73 73 73 FF 38 B7 38 FF 1C DB 1C FF 1B DA' + '1B FF 30 AF 30 FF 6C 6C 6C FF 76 76 76 FF 76 76' + '76 FF 74 74 74 FF 72 72 72 FF 72 72 72 FF 84 84' + '84 A0 00 00 00 00 00 00 00 00 AF AF AF CE A7 A7' + 'A7 FF A8 A8 A8 FF A8 A8 A8 FF A9 A9 A9 FF A9 A9' + 'A9 FF 8B 8B 8B FF AA AA AA FF AB AB AB FF AC AC' + 'AC FF AD AD AD FF AE AE AE FF AF AF AF FF AE AE' + 'AE FF AE AE AE FF B1 B1 B1 FF B2 B2 B2 FF B3 B3' + 'B3 FF B4 B4 B4 FF B4 B4 B4 FF B3 B3 B3 FF B0 B0' + 'B0 FF AD AD AD FF AC AC AC FF AA AA AA FF A8 A8' + 'A8 FF A7 A7 A7 FF A4 A4 A4 FF A1 A1 A1 FF A0 A0' + 'A0 FF 9E 9E 9E FF 9C 9C 9C FF 99 99 99 FF 97 97' + '97 FF 95 95 95 FF 93 93 93 FF 91 91 91 FF 8F 8F' + '8F FF 8D 8D 8D FF 8C 8C 8C FF 8A 8A 8A FF 86 86' + '86 FF 84 84 84 FF 83 83 83 FF 80 80 80 FF 7E 7E' + '7E FF 7C 7C 7C FF 79 79 79 FF 77 77 77 FF 74 74' + '74 FF 71 71 71 FF 37 B6 37 FF 1B DA 1B FF 1B DA' + '1B FF 34 B3 34 FF 5F 5F 5F FF 76 76 76 FF 75 75' + '75 FF 72 72 72 FF 70 70 70 FF 6F 6F 6F FF 7D 7D' + '7D DC 00 00 00 00 00 00 00 00 AE AE AE FA A9 A9' + 'A9 FF A9 A9 A9 FF A9 A9 A9 FF AA AA AA FF AC AC' + 'AC FF 89 89 89 FF AA AA AA FF AC AC AC FF AD AD' + 'AD FF AE AE AE FF AE AE AE FF AE AE AE FF AF AF' + 'AF FF B0 B0 B0 FF B2 B2 B2 FF B2 B2 B2 FF B3 B3' + 'B3 FF B4 B4 B4 FF B4 B4 B4 FF B2 B2 B2 FF AF AF' + 'AF FF AD AD AD FF AC AC AC FF AA AA AA FF A8 A8' + 'A8 FF A6 A6 A6 FF A4 A4 A4 FF A2 A2 A2 FF A0 A0' + 'A0 FF 9D 9D 9D FF 9B 9B 9B FF 98 98 98 FF 96 96' + '96 FF 95 95 95 FF 92 92 92 FF 91 91 91 FF 8F 8F' + '8F FF 8D 8D 8D FF 8B 8B 8B FF 89 89 89 FF 85 85' + '85 FF 84 84 84 FF 82 82 82 FF 80 80 80 FF 7D 7D' + '7D FF 7B 7B 7B FF 79 79 79 FF 76 76 76 FF 73 73' + '73 FF 71 71 71 FF 6E 6E 6E FF 6D 6D 6D FF 6A 6A' + '6A FF 68 68 68 FF 5A 5A 5A FF 75 75 75 FF 74 74' + '74 FF 71 71 71 FF 6E 6E 6E FF 6C 6C 6C FF 73 73' + '73 FE A3 A3 A3 19 99 99 99 3C 89 89 89 FF 88 88' + '88 FF 88 88 88 FF 89 89 89 FF 89 89 89 FF 8A 8A' + '8A FF A0 A0 A0 FF AA AA AA FF AA AA AA FF A9 A9' + 'A9 FF AB AB AB FF AC AC AC FF AD AD AD FF AD AD' + 'AD FF AF AF AF FF B1 B1 B1 FF B2 B2 B2 FF B1 B1' + 'B1 FF B3 B3 B3 FF B3 B3 B3 FF AF AF AF FF AD AD' + 'AD FF AC AC AC FF AA AA AA FF A9 A9 A9 FF A6 A6' + 'A6 FF A4 A4 A4 FF A3 A3 A3 FF A0 A0 A0 FF 9E 9E' + '9E FF 9C 9C 9C FF 9A 9A 9A FF 97 97 97 FF 95 95' + '95 FF 94 94 94 FF 91 91 91 FF 8F 8F 8F FF 8E 8E' + '8E FF 8D 8D 8D FF 8A 8A 8A FF 88 88 88 FF 85 85' + '85 FF 83 83 83 FF 81 81 81 FF 7F 7F 7F FF 7D 7D' + '7D FF 7A 7A 7A FF 78 78 78 FF 76 76 76 FF 74 74' + '74 FF 71 71 71 FF 6F 6F 6F FF 6D 6D 6D FF 69 69' + '69 FF 68 68 68 FF 64 64 64 FF 57 57 57 FF 58 58' + '58 FF 57 57 57 FF 55 55 55 FF 53 53 53 FF 53 53' + '53 FF 76 76 76 54 B9 B9 B9 72 A6 A6 A6 FF A5 A5' + 'A5 FF A5 A5 A5 FF A6 A6 A6 FF A6 A6 A6 FF A6 A6' + 'A6 FF A7 A7 A7 FF A7 A7 A7 FF A8 A8 A8 FF A9 A9' + 'A9 FF AA AA AA FF AA AA AA FF AB AB AB FF AC AC' + 'AC FF AC AC AC FF AE AE AE FF AF AF AF FF AF AF' + 'AF FF B0 B0 B0 FF AF AF AF FF AD AD AD FF AB AB' + 'AB FF A9 A9 A9 FF A8 A8 A8 FF A6 A6 A6 FF A4 A4' + 'A4 FF A2 A2 A2 FF A1 A1 A1 FF 9E 9E 9E FF 9C 9C' + '9C FF 9A 9A 9A FF 98 98 98 FF 95 95 95 FF 94 94' + '94 FF 92 92 92 FF 91 91 91 FF 8F 8F 8F FF 8D 8D' + '8D FF 8B 8B 8B FF 89 89 89 FF 87 87 87 FF 84 84' + '84 FF 82 82 82 FF 80 80 80 FF 7E 7E 7E FF 7C 7C' + '7C FF 7A 7A 7A FF 79 79 79 FF 77 77 77 FF 75 75' + '75 FF 73 73 73 FF 70 70 70 FF 6E 6E 6E FF 6B 6B' + '6B FF 69 69 69 FF 6B 6B 6B FF 68 68 68 FF 67 67' + '67 FF 65 65 65 FF 63 63 63 FF 61 61 61 FF 60 60' + '60 FF 7F 7F 7F 90 9D 9D 9D B1 9D 9D 9D FF 9F 9F' + '9F FF 9A 9A 9A FF 8C 8C 8C FF 90 90 90 FF 91 91' + '91 FF 91 91 91 FF 95 95 95 FF 98 98 98 FF 9A 9A' + '9A FF 9B 9B 9B FF 9C 9C 9C FF 9D 9D 9D FF 9E 9E' + '9E FF 9E 9E 9E FF A0 A0 A0 FF A1 A1 A1 FF A0 A0' + 'A0 FF A0 A0 A0 FF A0 A0 A0 FF 9F 9F 9F FF 9F 9F' + '9F FF 9C 9C 9C FF 9C 9C 9C FF 9D 9D 9D FF 9B 9B' + '9B FF 9A 9A 9A FF 98 98 98 FF 96 96 96 FF 95 95' + '95 FF 93 93 93 FF 91 91 91 FF 8F 8F 8F FF 8D 8D' + '8D FF 8C 8C 8C FF 8A 8A 8A FF 88 88 88 FF 86 86' + '86 FF 84 84 84 FF 82 82 82 FF 7F 7F 7F FF 7E 7E' + '7E FF 7D 7D 7D FF 7A 7A 7A FF 77 77 77 FF 74 74' + '74 FF 71 71 71 FF 6F 6F 6F FF 6E 6E 6E FF 6C 6C' + '6C FF 6B 6B 6B FF 69 69 69 FF 66 66 66 FF 63 63' + '63 FF 61 61 61 FF 60 60 60 FF 5F 5F 5F FF 5F 5F' + '5F FF 62 62 62 FF 79 79 79 FF 7A 7A 7A FF 7A 7A' + '7A FF 89 89 89 CC 89 89 89 B4 7F 7F 7F FF 69 69' + '69 FF 9C 9C 9C FF 7D 7D 7D FF 9B 9B 9B FF 9F 9F' + '9F FF A0 A0 A0 FF A5 A5 A5 FF AD AD AD FF B7 B7' + 'B7 FF BA BA BA FF BE BE BE FF C1 C1 C1 FF C4 C4' + 'C4 FF C4 C4 C4 FF C6 C6 C6 FF C7 C7 C7 FF C5 C5' + 'C5 FF C7 C7 C7 FF C9 C9 C9 FF CA CA CA FF CB CB' + 'CB FF CA CA CA FF CC CC CC FF CD CD CD FF CC CC' + 'CC FF CC CC CC FF CC CC CC FF CB CB CB FF CB CB' + 'CB FF CB CB CB FF CA CA CA FF CA CA CA FF C9 C9' + 'C9 FF C8 C8 C8 FF C8 C8 C8 FF C8 C8 C8 FF C8 C8' + 'C8 FF C7 C7 C7 FF C6 C6 C6 FF C5 C5 C5 FF C6 C6' + 'C6 FF C5 C5 C5 FF C4 C4 C4 FF C4 C4 C4 FF C2 C2' + 'C2 FF C1 C1 C1 FF C1 C1 C1 FF BF BF BF FF BF BF' + 'BF FF BE BE BE FF BE BE BE FF BC BC BC FF BB BB' + 'BB FF B3 B3 B3 FF AA AA AA FF A5 A5 A5 FF A4 A4' + 'A4 FF A4 A4 A4 FF 80 80 80 FF 72 72 72 FF A3 A3' + 'A3 FF 9F 9F 9F B1 83 83 83 3C 84 84 84 FE 98 98' + '98 FF 94 94 94 FF 72 72 72 FF 96 96 96 FF 9A 9A' + '9A FF 9B 9B 9B FF 9C 9C 9C FF 9D 9D 9D FF 94 94' + '94 FF 92 92 92 FF 92 92 92 FF 94 94 94 FF 95 95' + '95 FF 97 97 97 FF 97 97 97 FF 98 98 98 FF 99 99' + '99 FF 9A 9A 9A FF 9C 9C 9C FF 9C 9C 9C FF 9D 9D' + '9D FF 9E 9E 9E FF 9F 9F 9F FF A0 A0 A0 FF A0 A0' + 'A0 FF A2 A2 A2 FF A2 A2 A2 FF A2 A2 A2 FF A1 A1' + 'A1 FF A0 A0 A0 FF A0 A0 A0 FF A1 A1 A1 FF A1 A1' + 'A1 FF A2 A2 A2 FF A2 A2 A2 FF A2 A2 A2 FF A2 A2' + 'A2 FF A2 A2 A2 FF A2 A2 A2 FF A1 A1 A1 FF A1 A1' + 'A1 FF A1 A1 A1 FF A1 A1 A1 FF A3 A3 A3 FF A3 A3' + 'A3 FF A2 A2 A2 FF A3 A3 A3 FF A3 A3 A3 FF A4 A4' + 'A4 FF A3 A3 A3 FF A5 A5 A5 FF A7 A7 A7 FF B2 B2' + 'B2 FF C7 C7 C7 FF C9 C9 C9 FF A8 A8 A8 FF 9D 9D' + '9D FF A1 A1 A1 FF 82 82 82 FF 9B 9B 9B FF 90 90' + '90 FE 97 97 97 40 00 00 00 00 B6 B6 B6 F7 B1 B1' + 'B1 FF A2 A2 A2 FF 9B 9B 9B FF 98 98 98 FF 83 83' + '83 FF 5D 5D 5D FF 7E 7E 7E FF 9E 9E 9E FF AB AB' + 'AB FF B3 B3 B3 FF B6 B6 B6 FF B9 B9 B9 FF BC BC' + 'BC FF BC BC BC FF BE BE BE FF BD BD BD FF BC BC' + 'BC FF BD BD BD FF BF BF BF FF C0 C0 C0 FF C1 C1' + 'C1 FF C3 C3 C3 FF C4 C4 C4 FF C3 C3 C3 FF C4 C4' + 'C4 FF C4 C4 C4 FF C4 C4 C4 FF C5 C5 C5 FF C5 C5' + 'C5 FF C5 C5 C5 FF C5 C5 C5 FF C4 C4 C4 FF C3 C3' + 'C3 FF C2 C2 C2 FF C2 C2 C2 FF C0 C0 C0 FF BF BF' + 'BF FF BD BD BD FF BD BD BD FF BC BC BC FF BA BA' + 'BA FF B7 B7 B7 FF B6 B6 B6 FF B5 B5 B5 FF B6 B6' + 'B6 FF B9 B9 B9 FF BB BB BB FF BB BB BB FF BA BA' + 'BA FF B9 B9 B9 FF B7 B7 B7 FF B7 B7 B7 FF BD BD' + 'BD FF BB BB BB FF 9A 9A 9A FF C7 C7 C7 FF B0 B0' + 'B0 FF BA BA BA FF C1 C1 C1 FF A9 A9 A9 FF C5 C5' + 'C5 E1 00 00 00 00 00 00 00 00 87 87 87 A0 96 96' + '96 FF 97 97 97 FF 93 93 93 FF 6B 6B 6B FF 53 53' + '53 FF 76 76 76 FF 95 95 95 FF 8D 8D 8D FF 78 78' + '78 FF 81 81 81 FF 81 81 81 FF 83 83 83 FF 84 84' + '84 FF 85 85 85 FF 85 85 85 FF 86 86 86 FF 86 86' + '86 FF 88 88 88 FF 89 89 89 FF 89 89 89 FF 89 89' + '89 FF 8A 8A 8A FF 8B 8B 8B FF 8B 8B 8B FF 8A 8A' + '8A FF 8B 8B 8B FF 8C 8C 8C FF 8C 8C 8C FF 8C 8C' + '8C FF 8C 8C 8C FF 8B 8B 8B FF 8B 8B 8B FF 8B 8B' + '8B FF 8B 8B 8B FF 8B 8B 8B FF 8B 8B 8B FF 8B 8B' + '8B FF 8A 8A 8A FF 8B 8B 8B FF 8A 8A 8A FF 89 89' + '89 FF 89 89 89 FF 89 89 89 FF 89 89 89 FF 89 89' + '89 FF 89 89 89 FF 89 89 89 FF 89 89 89 FF 8A 8A' + '8A FF 89 89 89 FF 89 89 89 FF 96 96 96 FF 94 94' + '94 FF B5 B5 B5 FF D7 D7 D7 FF 97 97 97 FF B9 B9' + 'B9 FF B3 B3 B3 FF 9D 9D 9D FF 97 97 97 FF 8C 8C' + '8C 66 00 00 00 00 00 00 00 00 73 73 73 2C 92 92' + '92 FD 89 89 89 FF 50 50 50 FF 5C 5C 5C FF 87 87' + '87 FF 94 94 94 FF 80 80 80 FF 77 77 77 FF 9A 9A' + '9A FF A1 A1 A1 FF A3 A3 A3 FF A5 A5 A5 FF A6 A6' + 'A6 FF A7 A7 A7 FF A7 A7 A7 FF A9 A9 A9 FF A9 A9' + 'A9 FF AA AA AA FF AB AB AB FF AC AC AC FF AD AD' + 'AD FF AE AE AE FF AF AF AF FF B0 B0 B0 FF B0 B0' + 'B0 FF B1 B1 B1 FF B2 B2 B2 FF B3 B3 B3 FF B3 B3' + 'B3 FF B4 B4 B4 FF B4 B4 B4 FF B5 B5 B5 FF B5 B5' + 'B5 FF B6 B6 B6 FF B6 B6 B6 FF B7 B7 B7 FF B8 B8' + 'B8 FF B8 B8 B8 FF B9 B9 B9 FF B9 B9 B9 FF BA BA' + 'BA FF BA BA BA FF BB BB BB FF BB BB BB FF BB BB' + 'BB FF BB BB BB FF BC BC BC FF BC BC BC FF BD BD' + 'BD FF BD BD BD FF A6 A6 A6 FF 93 93 93 FF AB AB' + 'AB FF 94 94 94 FF 9D 9D 9D FF D2 D2 D2 FF A5 A5' + 'A5 FF A1 A1 A1 FF A3 A3 A3 FF 8E 8E 8E DC 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 89 89' + '89 B4 7D 7D 7D FF 52 52 52 FF 91 91 91 FF 92 92' + '92 FF 74 74 74 FF 86 86 86 FF 9C 9C 9C FF A0 A0' + 'A0 FF A2 A2 A2 FF A3 A3 A3 FF A4 A4 A4 FF A5 A5' + 'A5 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8 A8 FF A9 A9' + 'A9 FF AA AA AA FF AB AB AB FF AC AC AC FF AD AD' + 'AD FF AE AE AE FF AF AF AF FF B0 B0 B0 FF B1 B1' + 'B1 FF B2 B2 B2 FF B3 B3 B3 FF B3 B3 B3 FF B4 B4' + 'B4 FF B5 B5 B5 FF B6 B6 B6 FF B7 B7 B7 FF B8 B8' + 'B8 FF B8 B8 B8 FF B9 B9 B9 FF BA BA BA FF BB BB' + 'BB FF BB BB BB FF BC BC BC FF BD BD BD FF BD BD' + 'BD FF BE BE BE FF BE BE BE FF BF BF BF FF BF BF' + 'BF FF C0 C0 C0 FF C0 C0 C0 FF C1 C1 C1 FF C1 C1' + 'C1 FF C1 C1 C1 FF C1 C1 C1 FF B3 B3 B3 FF 8B 8B' + '8B FF 9F 9F 9F FF 9E 9E 9E FF 93 93 93 FF B6 B6' + 'B6 FF 93 93 93 FF 9A 9A 9A FE 8A 8A 8A 5E 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 7B 7B' + '7B 3E 8E 8E 8E FE 68 68 68 FF 9F 9F 9F FF 85 85' + '85 FF 93 93 93 FF 9C 9C 9C FF A1 A1 A1 FF A2 A2' + 'A2 FF A3 A3 A3 FF A4 A4 A4 FF A5 A5 A5 FF A6 A6' + 'A6 FF A7 A7 A7 FF A8 A8 A8 FF A9 A9 A9 FF AA AA' + 'AA FF AB AB AB FF AC AC AC FF AD AD AD FF AE AE' + 'AE FF AF AF AF FF B0 B0 B0 FF B1 B1 B1 FF B2 B2' + 'B2 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5' + 'B5 FF B6 B6 B6 FF B7 B7 B7 FF B8 B8 B8 FF B8 B8' + 'B8 FF B9 B9 B9 FF BA BA BA FF BB BB BB FF BB BB' + 'BB FF BC BC BC FF BD BD BD FF BE BE BE FF BE BE' + 'BE FF BF BF BF FF BF BF BF FF C0 C0 C0 FF C0 C0' + 'C0 FF C1 C1 C1 FF C1 C1 C1 FF C2 C2 C2 FF C2 C2' + 'C2 FF C2 C2 C2 FF C3 C3 C3 FF C3 C3 C3 FF C0 C0' + 'C0 FF 9C 9C 9C FF 90 90 90 FF 99 99 99 FF 91 91' + '91 FF 92 92 92 FF 8E 8E 8E D6 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 86 86 86 C7 74 74 74 FF 93 93 93 FF 99 99' + '99 FF 96 96 96 FF A0 A0 A0 FF A1 A1 A1 FF A2 A2' + 'A2 FF A3 A3 A3 FF A4 A4 A4 FF A5 A5 A5 FF A6 A6' + 'A6 FF A7 A7 A7 FF A8 A8 A8 FF A9 A9 A9 FF AA AA' + 'AA FF AB AB AB FF AC AC AC FF AD AD AD FF AE AE' + 'AE FF AF AF AF FF B0 B0 B0 FF B1 B1 B1 FF B2 B2' + 'B2 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5 B5 FF B6 B6' + 'B6 FF B7 B7 B7 FF B8 B8 B8 FF B8 B8 B8 FF B9 B9' + 'B9 FF BA BA BA FF BB BB BB FF BC BC BC FF BC BC' + 'BC FF BD BD BD FF BE BE BE FF BE BE BE FF BF BF' + 'BF FF C0 C0 C0 FF C0 C0 C0 FF C1 C1 C1 FF C1 C1' + 'C1 FF C2 C2 C2 FF C2 C2 C2 FF C3 C3 C3 FF C3 C3' + 'C3 FF C3 C3 C3 FF C4 C4 C4 FF C4 C4 C4 FF C4 C4' + 'C4 FF C4 C4 C4 FF A2 A2 A2 FF 96 96 96 FF 90 90' + '90 FF 93 93 93 FE 8B 8B 8B 56 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 82 82 82 52 8A 8A 8A FE 78 78 78 FF A4 A4' + 'A4 FF 8B 8B 8B FF A1 A1 A1 FF A2 A2 A2 FF A3 A3' + 'A3 FF A4 A4 A4 FF A5 A5 A5 FF A6 A6 A6 FF A7 A7' + 'A7 FF A8 A8 A8 FF A9 A9 A9 FF AA AA AA FF AB AB' + 'AB FF AC AC AC FF AD AD AD FF AE AE AE FF AF AF' + 'AF FF B0 B0 B0 FF B1 B1 B1 FF B2 B2 B2 FF B3 B3' + 'B3 FF B4 B4 B4 FF B5 B5 B5 FF B6 B6 B6 FF B7 B7' + 'B7 FF B7 B7 B7 FF B8 B8 B8 FF B9 B9 B9 FF BA BA' + 'BA FF BB BB BB FF BC BC BC FF BD BD BD FF BD BD' + 'BD FF BE BE BE FF BF BF BF FF BF BF BF FF C0 C0' + 'C0 FF C1 C1 C1 FF C1 C1 C1 FF C2 C2 C2 FF C2 C2' + 'C2 FF C3 C3 C3 FF C3 C3 C3 FF C4 C4 C4 FF C4 C4' + 'C4 FF C5 C5 C5 FF C5 C5 C5 FF C5 C5 C5 FF C5 C5' + 'C5 FF C5 C5 C5 FF 96 96 96 FF 96 96 96 FF 92 92' + '92 FF 91 91 91 D0 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 8A 8A 8A D9 75 75 75 FF 9C 9C' + '9C FF 91 91 91 FF 9F 9F 9F FF A2 A2 A2 FF A3 A3' + 'A3 FF A4 A4 A4 FF A5 A5 A5 FF A6 A6 A6 FF A7 A7' + 'A7 FF A9 A9 A9 FF AA AA AA FF AB AB AB FF AC AC' + 'AC FF AD AD AD FF AE AE AE FF AF AF AF FF B0 B0' + 'B0 FF B1 B1 B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4' + 'B4 FF B4 B4 B4 FF B5 B5 B5 FF B6 B6 B6 FF B7 B7' + 'B7 FF B8 B8 B8 FF B9 B9 B9 FF BA BA BA FF BB BB' + 'BB FF BC BC BC FF BD BD BD FF BD BD BD FF BE BE' + 'BE FF BF BF BF FF C0 C0 C0 FF C0 C0 C0 FF C1 C1' + 'C1 FF C2 C2 C2 FF C2 C2 C2 FF C3 C3 C3 FF C4 C4' + 'C4 FF C4 C4 C4 FF C5 C5 C5 FF C5 C5 C5 FF C5 C5' + 'C5 FF C6 C6 C6 FF C6 C6 C6 FF C6 C6 C6 FF C6 C6' + 'C6 FF B3 B3 B3 FF 92 92 92 FF 90 90 90 FF 95 95' + '95 FE 8F 8F 8F 4E 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 87 87 87 66 87 87 87 FF 82 82' + '82 FF 9B 9B 9B FF 93 93 93 FF A3 A3 A3 FF A4 A4' + 'A4 FF A5 A5 A5 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8' + 'A8 FF A9 A9 A9 FF AA AA AA FF AB AB AB FF AC AC' + 'AC FF AD AD AD FF AE AE AE FF AF AF AF FF B0 B0' + 'B0 FF B1 B1 B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4' + 'B4 FF B5 B5 B5 FF B6 B6 B6 FF B7 B7 B7 FF B8 B8' + 'B8 FF B9 B9 B9 FF BA BA BA FF BB BB BB FF BC BC' + 'BC FF BC BC BC FF BD BD BD FF BE BE BE FF BF BF' + 'BF FF C0 C0 C0 FF C1 C1 C1 FF C1 C1 C1 FF C2 C2' + 'C2 FF C3 C3 C3 FF C3 C3 C3 FF C4 C4 C4 FF C5 C5' + 'C5 FF C5 C5 C5 FF C6 C6 C6 FF C6 C6 C6 FF C6 C6' + 'C6 FF C7 C7 C7 FF C7 C7 C7 FF C7 C7 C7 FF C8 C8' + 'C8 FF 9B 9B 9B FF 93 93 93 FF 94 94 94 FF 91 91' + '91 C9 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 91 91 91 E7 79 79' + '79 FF 9E 9E 9E FF 90 90 90 FF A3 A3 A3 FF A4 A4' + 'A4 FF A5 A5 A5 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8' + 'A8 FF AA AA AA FF AB AB AB FF AC AC AC FF AD AD' + 'AD FF AE AE AE FF AF AF AF FF B0 B0 B0 FF B1 B1' + 'B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5' + 'B5 FF B6 B6 B6 FF C2 C2 C2 FF D1 D1 D1 FF D9 D9' + 'D9 FF DB DB DB FF D9 D9 D9 FF D4 D4 D4 FF CE CE' + 'CE FF C3 C3 C3 FF BA BA BA FF BE BE BE FF C0 C0' + 'C0 FF C1 C1 C1 FF C1 C1 C1 FF C2 C2 C2 FF C3 C3' + 'C3 FF C4 C4 C4 FF C4 C4 C4 FF C5 C5 C5 FF C6 C6' + 'C6 FF C6 C6 C6 FF C7 C7 C7 FF C7 C7 C7 FF C8 C8' + 'C8 FF C8 C8 C8 FF C8 C8 C8 FF C8 C8 C8 FF BC BC' + 'BC FF 90 90 90 FF 94 94 94 FF 96 96 96 FE 8E 8E' + '8E 46 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 8F 8F 8F 79 89 89' + '89 FF 8D 8D 8D FF 97 97 97 FF A1 A1 A1 FF A5 A5' + 'A5 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8 A8 FF A9 A9' + 'A9 FF AA AA AA FF AB AB AB FF AC AC AC FF AD AD' + 'AD FF AE AE AE FF AF AF AF FF B0 B0 B0 FF B1 B1' + 'B1 FF B3 B3 B3 FF B0 B0 B0 FF B1 B1 B1 FF B9 B9' + 'B9 FF C2 C2 C2 FF C5 C5 C5 FF BF BF BF FF B9 B9' + 'B9 FF B6 B6 B6 FF B6 B6 B6 FF B9 B9 B9 FF BF BF' + 'BF FF CB CB CB FF DB DB DB FF D7 D7 D7 FF BB BB' + 'BB FF BC BC BC FF C2 C2 C2 FF C3 C3 C3 FF C4 C4' + 'C4 FF C5 C5 C5 FF C5 C5 C5 FF C6 C6 C6 FF C7 C7' + 'C7 FF C7 C7 C7 FF C8 C8 C8 FF C8 C8 C8 FF C9 C9' + 'C9 FF C9 C9 C9 FF C9 C9 C9 FF CA CA CA FF A6 A6' + 'A6 FF 93 93 93 FF 96 96 96 FF 99 99 99 C1 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 87 87 87 11 93 93' + '93 F2 7F 7F 7F FF 9F 9F 9F FF 9B 9B 9B FF A5 A5' + 'A5 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8 A8 FF A9 A9' + 'A9 FF AA AA AA FF AB AB AB FF AD AD AD FF AE AE' + 'AE FF AF AF AF FF B0 B0 B0 FF B1 B1 B1 FF B2 B2' + 'B2 FF A9 A9 A9 FF A5 A5 A5 FF B1 B1 B1 FF B4 B4' + 'B4 FF B6 B6 B6 FF B8 B8 B8 FF B9 B9 B9 FF BA BA' + 'BA FF BB BB BB FF BC BC BC FF BD BD BD FF BE BE' + 'BE FF BE BE BE FF B8 B8 B8 FF B8 B8 B8 FF CE CE' + 'CE FF B5 B5 B5 FF B6 B6 B6 FF C4 C4 C4 FF C5 C5' + 'C5 FF C5 C5 C5 FF C6 C6 C6 FF C7 C7 C7 FF C7 C7' + 'C7 FF C8 C8 C8 FF C9 C9 C9 FF C9 C9 C9 FF CA CA' + 'CA FF CA CA CA FF CA CA CA FF C6 C6 C6 FF 95 95' + '95 FF 95 95 95 FF 9B 9B 9B FE 99 99 99 3F 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 96 96' + '96 8D 89 89 89 FF 98 98 98 FF 9B 9B 9B FF A5 A5' + 'A5 FF A6 A6 A6 FF A8 A8 A8 FF A9 A9 A9 FF AA AA' + 'AA FF AB AB AB FF AC AC AC FF AD AD AD FF AE AE' + 'AE FF AF AF AF FF B0 B0 B0 FF B1 B1 B1 FF AC AC' + 'AC FF 9C 9C 9C FF AE AE AE FF B5 B5 B5 FF B6 B6' + 'B6 FF B7 B7 B7 FF B9 B9 B9 FF BA BA BA FF BA BA' + 'BA FF BC BC BC FF BD BD BD FF BD BD BD FF BE BE' + 'BE FF BF BF BF FF C0 C0 C0 FF C1 C1 C1 FF BB BB' + 'BB FF B4 B4 B4 FF A8 A8 A8 FF BC BC BC FF C5 C5' + 'C5 FF C6 C6 C6 FF C7 C7 C7 FF C8 C8 C8 FF C8 C8' + 'C8 FF C9 C9 C9 FF CA CA CA FF CA CA CA FF CB CB' + 'CB FF CB CB CB FF CC CC CC FF B2 B2 B2 FF 96 96' + '96 FF 98 98 98 FF 9D 9D 9D BA 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 95' + '95 1D 97 97 97 F9 8E 8E 8E FF A1 A1 A1 FF A0 A0' + 'A0 FF A7 A7 A7 FF A8 A8 A8 FF A9 A9 A9 FF AA AA' + 'AA FF AB AB AB FF AC AC AC FF AD AD AD FF AE AE' + 'AE FF B0 B0 B0 FF B1 B1 B1 FF B2 B2 B2 FF A6 A6' + 'A6 FF A1 A1 A1 FF B5 B5 B5 FF B6 B6 B6 FF B7 B7' + 'B7 FF B8 B8 B8 FF B9 B9 B9 FF BA BA BA FF BB BB' + 'BB FF BC BC BC FF BD BD BD FF BE BE BE FF BF BF' + 'BF FF C0 C0 C0 FF C1 C1 C1 FF C2 C2 C2 FF C3 C3' + 'C3 FF B2 B2 B2 FF AA AA AA FF AC AC AC FF C6 C6' + 'C6 FF C7 C7 C7 FF C8 C8 C8 FF C9 C9 C9 FF C9 C9' + 'C9 FF CA CA CA FF CB CB CB FF CB CB CB FF CC CC' + 'CC FF CC CC CC FF CA CA CA FF 9E 9E 9E FF 9B 9B' + '9B FF 9D 9D 9D FE 9D 9D 9D 37 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 9B 9B 9B A1 95 95 95 FF A0 A0 A0 FF 9E 9E' + '9E FF A7 A7 A7 FF A8 A8 A8 FF A9 A9 A9 FF AB AB' + 'AB FF AC AC AC FF AD AD AD FF AE AE AE FF AF AF' + 'AF FF B0 B0 B0 FF B1 B1 B1 FF B2 B2 B2 FF A4 A4' + 'A4 FF A3 A3 A3 FF B2 B2 B2 FF B6 B6 B6 FF B7 B7' + 'B7 FF B9 B9 B9 FF BA BA BA FF BB BB BB FF BC BC' + 'BC FF BD BD BD FF BE BE BE FF BF BF BF FF C0 C0' + 'C0 FF C1 C1 C1 FF C2 C2 C2 FF C3 C3 C3 FF C0 C0' + 'C0 FF AB AB AB FF 92 92 92 FF BB BB BB FF C7 C7' + 'C7 FF C8 C8 C8 FF C9 C9 C9 FF CA CA CA FF CA CA' + 'CA FF CB CB CB FF CC CC CC FF CC CC CC FF CD CD' + 'CD FF CD CD CD FF B6 B6 B6 FF 99 99 99 FF 9F 9F' + '9F FF 9F 9F 9F B2 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 9E 9E 9E 2D 9A 9A 9A FD 9A 9A 9A FF A4 A4' + 'A4 FF A3 A3 A3 FF A9 A9 A9 FF AA AA AA FF AB AB' + 'AB FF AC AC AC FF AD AD AD FF AE AE AE FF AF AF' + 'AF FF B0 B0 B0 FF B1 B1 B1 FF B3 B3 B3 FF B3 B3' + 'B3 FF A6 A6 A6 FF A3 A3 A3 FF AD AD AD FF B6 B6' + 'B6 FF B9 B9 B9 FF BA BA BA FF BB BB BB FF BC BC' + 'BC FF BD BD BD FF BE BE BE FF BF BF BF FF C0 C0' + 'C0 FF C1 C1 C1 FF C0 C0 C0 FF B6 B6 B6 FF A0 A0' + 'A0 FF 87 87 87 FF AD AD AD FF C7 C7 C7 FF C8 C8' + 'C8 FF C9 C9 C9 FF CA CA CA FF CA CA CA FF CB CB' + 'CB FF CC CC CC FF CD CD CD FF CD CD CD FF CE CE' + 'CE FF C2 C2 C2 FF 9D 9D 9D FF A0 A0 A0 FF A0 A0' + 'A0 FD 9F 9F 9F 30 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 9C 9C 9C B5 9A 9A 9A FF 9D 9D' + '9D FF A8 A8 A8 FF A6 A6 A6 FF AA AA AA FF AB AB' + 'AB FF AC AC AC FF AD AD AD FF AE AE AE FF B0 B0' + 'B0 FF B1 B1 B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4' + 'B4 FF B5 B5 B5 FF B6 B6 B6 FF AE AE AE FF A0 A0' + 'A0 FF A6 A6 A6 FF AD AD AD FF B2 B2 B2 FF B4 B4' + 'B4 FF B5 B5 B5 FF B6 B6 B6 FF B4 B4 B4 FF B0 B0' + 'B0 FF A8 A8 A8 FF 95 95 95 FF 8D 8D 8D FF 9C 9C' + '9C FF C1 C1 C1 FF C7 C7 C7 FF C8 C8 C8 FF C9 C9' + 'C9 FF C9 C9 C9 FF CA CA CA FF CB CB CB FF CC CC' + 'CC FF CD CD CD FF CE CE CE FF CE CE CE FF BF BF' + 'BF FF A5 A5 A5 FF A1 A1 A1 FF 9F 9F 9F FF 9D 9D' + '9D AA 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 A4 A4 A4 2D 90 90 90 F1 94 94' + '94 FF 9D 9D 9D FF A9 A9 A9 FF AA AA AA FF AB AB' + 'AB FF AD AD AD FF AE AE AE FF AF AF AF FF B0 B0' + 'B0 FF B1 B1 B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4' + 'B4 FF B5 B5 B5 FF B6 B6 B6 FF B8 B8 B8 FF BB BB' + 'BB FF B0 B0 B0 FF A7 A7 A7 FF A1 A1 A1 FF 9D 9D' + '9D FF 9C 9C 9C FF 9D 9D 9D FF A1 A1 A1 FF A5 A5' + 'A5 FF AA AA AA FF B9 B9 B9 FF C5 C5 C5 FF C5 C5' + 'C5 FF C6 C6 C6 FF C7 C7 C7 FF C8 C8 C8 FF C9 C9' + 'C9 FF CA CA CA FF CB CB CB FF CC CC CC FF CD CD' + 'CD FF CE CE CE FF CC CC CC FF B7 B7 B7 FF A6 A6' + 'A6 FF 9B 9B 9B FF 9D 9D 9D FF A0 A0 A0 EB 97 97' + '97 1B 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 A7 A7 A7 AC 9C 9C' + '9C FF 9B 9B 9B FF 98 98 98 FF 92 92 92 FF A9 A9' + 'A9 FF AB AB AB FF AE AE AE FF AF AF AF FF B0 B0' + 'B0 FF B1 B1 B1 FF B2 B2 B2 FF B4 B4 B4 FF B5 B5' + 'B5 FF B6 B6 B6 FF B7 B7 B7 FF B8 B8 B8 FF B9 B9' + 'B9 FF BA BA BA FF BB BB BB FF BC BC BC FF BD BD' + 'BD FF BF BF BF FF C0 C0 C0 FF C1 C1 C1 FF C2 C2' + 'C2 FF C3 C3 C3 FF C4 C4 C4 FF C5 C5 C5 FF C6 C6' + 'C6 FF C7 C7 C7 FF C8 C8 C8 FF C9 C9 C9 FF CA CA' + 'CA FF CB CB CB FF CC CC CC FF CD CD CD FF CE CE' + 'CE FF C2 C2 C2 FF A6 A6 A6 FF A7 A7 A7 FF A8 A8' + 'A8 FF A9 A9 A9 FF A0 A0 A0 FF AD AD AD 8D 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 A7 A7 A7 4F 9D 9D' + '9D FE 87 87 87 FF 7A 7A 7A FF 64 64 64 FF A0 A0' + 'A0 FF 9F 9F 9F FF AE AE AE FF AF AF AF FF B0 B0' + 'B0 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5' + 'B5 FF B6 B6 B6 FF B7 B7 B7 FF B8 B8 B8 FF B9 B9' + 'B9 FF BA BA BA FF BC BC BC FF BD BD BD FF BE BE' + 'BE FF BF BF BF FF C0 C0 C0 FF C1 C1 C1 FF C2 C2' + 'C2 FF C3 C3 C3 FF C4 C4 C4 FF C5 C5 C5 FF C6 C6' + 'C6 FF C7 C7 C7 FF C8 C8 C8 FF CA CA CA FF CB CB' + 'CB FF CC CC CC FF CC CC CC FF CD CD CD FF CE CE' + 'CE FF A9 A9 A9 FF A3 A3 A3 FF B6 B6 B6 FF DA DA' + 'DA FF B0 B0 B0 FF A0 A0 A0 F6 A1 A1 A1 1E 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 A5 A5' + 'A5 CC 7F 7F 7F FF 96 96 96 FF 86 86 86 FF 8E 8E' + '8E FF AB AB AB FF AE AE AE FF B0 B0 B0 FF B1 B1' + 'B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5' + 'B5 FF B6 B6 B6 FF B7 B7 B7 FF B9 B9 B9 FF BA BA' + 'BA FF BB BB BB FF BC BC BC FF BD BD BD FF BE BE' + 'BE FF BF BF BF FF C0 C0 C0 FF C1 C1 C1 FF C3 C3' + 'C3 FF C4 C4 C4 FF C5 C5 C5 FF C6 C6 C6 FF C7 C7' + 'C7 FF C8 C8 C8 FF C9 C9 C9 FF CA CA CA FF CB CB' + 'CB FF CC CC CC FF CD CD CD FF CE CE CE FF CF CF' + 'CF FF C8 C8 C8 FF B4 B4 B4 FF B1 B1 B1 FF C6 C6' + 'C6 FF A6 A6 A6 FF A6 A6 A6 8A 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 AC AC' + 'AC 4D A0 A0 A0 FE A0 A0 A0 FF A7 A7 A7 FF AC AC' + 'AC FF AD AD AD FF AF AF AF FF B0 B0 B0 FF B1 B1' + 'B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5' + 'B5 FF B7 B7 B7 FF B8 B8 B8 FF B9 B9 B9 FF BA BA' + 'BA FF BB BB BB FF BC BC BC FF BD BD BD FF BE BE' + 'BE FF C0 C0 C0 FF C1 C1 C1 FF C2 C2 C2 FF C3 C3' + 'C3 FF C4 C4 C4 FF C5 C5 C5 FF C6 C6 C6 FF C7 C7' + 'C7 FF C8 C8 C8 FF C9 C9 C9 FF CB CB CB FF CC CC' + 'CC FF CD CD CD FF CE CE CE FF CF CF CF FF D0 D0' + 'D0 FF D1 D1 D1 FF D2 D2 D2 FF B3 B3 B3 FF A0 A0' + 'A0 FF AC AC AC ED B4 B4 B4 11 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 A7 A7 A7 CB 9F 9F 9F FF A8 A8 A8 FF AD AD' + 'AD FF AE AE AE FF AF AF AF FF B0 B0 B0 FF B1 B1' + 'B1 FF B2 B2 B2 FF B3 B3 B3 FF B4 B4 B4 FF B6 B6' + 'B6 FF B7 B7 B7 FF B8 B8 B8 FF B9 B9 B9 FF BD BD' + 'BD FF C2 C2 C2 FF C0 C0 C0 FF C1 C1 C1 FF BF BF' + 'BF FF C0 C0 C0 FF C1 C1 C1 FF C2 C2 C2 FF C3 C3' + 'C3 FF C4 C4 C4 FF C5 C5 C5 FF C7 C7 C7 FF C8 C8' + 'C8 FF C9 C9 C9 FF CA CA CA FF CB CB CB FF CC CC' + 'CC FF CD CD CD FF CE CE CE FF CF CF CF FF D0 D0' + 'D0 FF D1 D1 D1 FF CE CE CE FF A5 A5 A5 FF AC AC' + 'AC FF AC AC AC 75 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 AB AB AB 4C A4 A4 A4 FE A8 A8 A8 FF AC AC' + 'AC FF AE AE AE FF AF AF AF FF B0 B0 B0 FF B1 B1' + 'B1 FF B2 B2 B2 FF B3 B3 B3 FF B5 B5 B5 FF B6 B6' + 'B6 FF B5 B5 B5 FF AC AC AC FF BA BA BA FF C0 C0' + 'C0 FF C0 C0 C0 FF C2 C2 C2 FF BF BF BF FF B8 B8' + 'B8 FF BD BD BD FF C1 C1 C1 FF C2 C2 C2 FF C3 C3' + 'C3 FF C5 C5 C5 FF C6 C6 C6 FF C7 C7 C7 FF C8 C8' + 'C8 FF C9 C9 C9 FF CA CA CA FF CB CB CB FF CC CC' + 'CC FF CD CD CD FF CF CF CF FF D0 D0 D0 FF D1 D1' + 'D1 FF D2 D2 D2 FF BE BE BE FF A9 A9 A9 FF AF AF' + 'AF E1 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 AA AA AA CA A7 A7 A7 FF AD AD' + 'AD FF AE AE AE FF AF AF AF FF B0 B0 B0 FF B1 B1' + 'B1 FF B2 B2 B2 FF B4 B4 B4 FF B5 B5 B5 FF B6 B6' + 'B6 FF B5 B5 B5 FF AB AB AB FF B2 B2 B2 FF B6 B6' + 'B6 FF BB BB BB FF BA BA BA FF B0 B0 B0 FF A4 A4' + 'A4 FF BA BA BA FF C1 C1 C1 FF C3 C3 C3 FF C4 C4' + 'C4 FF C5 C5 C5 FF C6 C6 C6 FF C7 C7 C7 FF C8 C8' + 'C8 FF C9 C9 C9 FF CA CA CA FF CC CC CC FF CD CD' + 'CD FF CE CE CE FF CF CF CF FF D0 D0 D0 FF D1 D1' + 'D1 FF D1 D1 D1 FF AE AE AE FF B0 B0 B0 FE B1 B1' + 'B1 5F 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 AD AD AD 4B A8 A8 A8 FE AD AD' + 'AD FF AE AE AE FF AF AF AF FF B0 B0 B0 FF B1 B1' + 'B1 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5 B5 FF B6 B6' + 'B6 FF B7 B7 B7 FF B8 B8 B8 FF BA BA BA FF B7 B7' + 'B7 FF B5 B5 B5 FF B7 B7 B7 FF BD BD BD FF BF BF' + 'BF FF C0 C0 C0 FF C1 C1 C1 FF C3 C3 C3 FF C4 C4' + 'C4 FF C5 C5 C5 FF C6 C6 C6 FF C7 C7 C7 FF C8 C8' + 'C8 FF CA CA CA FF CB CB CB FF CC CC CC FF CD CD' + 'CD FF CE CE CE FF CF CF CF FF D0 D0 D0 FF D1 D1' + 'D1 FF C5 C5 C5 FF AD AD AD FF B4 B4 B4 D1 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 B1 B1 B1 C9 AC AC' + 'AC FF B3 B3 B3 FF B4 B4 B4 FF B1 B1 B1 FF B2 B2' + 'B2 FF B3 B3 B3 FF B4 B4 B4 FF B5 B5 B5 FF B6 B6' + 'B6 FF B7 B7 B7 FF B8 B8 B8 FF BA BA BA FF BB BB' + 'BB FF BC BC BC FF BD BD BD FF BE BE BE FF BF BF' + 'BF FF C0 C0 C0 FF C2 C2 C2 FF C3 C3 C3 FF C4 C4' + 'C4 FF C5 C5 C5 FF C6 C6 C6 FF C7 C7 C7 FF C8 C8' + 'C8 FF CA CA CA FF CB CB CB FF CC CC CC FF CD CD' + 'CD FF CE CE CE FF CF CF CF FF D0 D0 D0 FF CB CB' + 'CB FF B0 B0 B0 FF B1 B1 B1 FE B5 B5 B5 49 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 AD AD AD 1C A0 A0' + 'A0 91 B4 B4 B4 74 B6 B6 B6 9A B6 B6 B6 9E B4 B4' + 'B4 C7 B5 B5 B5 E8 B7 B7 B7 E8 B8 B8 B8 E8 B9 B9' + 'B9 E8 BA BA BA E8 BB BB BB E8 BD BD BD E8 BE BE' + 'BE E8 BF BF BF E8 C0 C0 C0 E8 C1 C1 C1 E8 C1 C1' + 'C1 E8 C2 C2 C2 E8 C3 C3 C3 E8 C4 C4 C4 E8 C5 C5' + 'C5 E8 C6 C6 C6 E8 C8 C8 C8 E8 C9 C9 C9 E8 CA CA' + 'CA E8 CB CB CB E8 CC CC CC E8 CD CD CD E8 CD CD' + 'CD E8 CB CB CB E7 C2 C2 C2 AD B8 B8 B8 9F B7 B7' + 'B7 8C A6 A6 A6 96 B9 B9 B9 54 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 03 80 00' + '00 00 00 00 00 01 80 00 00 00 00 00 00 01 80 00' + '00 00 00 00 00 01 80 00 00 00 00 00 00 01 80 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00' + '00 00 00 00 00 01 80 00 00 00 00 00 00 01 80 00' + '00 00 00 00 00 03 C0 00 00 00 00 00 00 03 C0 00' + '00 00 00 00 00 07 E0 00 00 00 00 00 00 07 E0 00' + '00 00 00 00 00 0F F0 00 00 00 00 00 00 0F F0 00' + '00 00 00 00 00 1F F8 00 00 00 00 00 00 1F F8 00' + '00 00 00 00 00 3F F8 00 00 00 00 00 00 3F FC 00' + '00 00 00 00 00 7F FC 00 00 00 00 00 00 7F FE 00' + '00 00 00 00 00 FF FE 00 00 00 00 00 00 FF FF 00' + '00 00 00 00 01 FF FF 00 00 00 00 00 01 FF FF 80' + '00 00 00 00 03 FF FF 80 00 00 00 00 03 FF FF C0' + '00 00 00 00 07 FF FF C0 00 00 00 00 07 FF FF E0' + '00 00 00 00 0F FF FF E0 00 00 00 00 1F FF FF F0' + '00 00 00 00 1F FF FF F0 00 00 00 00 3F FF FF F8' + '00 00 00 00 3F FF FF F8 00 00 00 00 7F FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF' } */ /* BINRES netdrive.ico */ @@ -13432,1585 +19335,3167 @@ END /* BINRES mycomputer.ico */ 15 ICON mycomputer.ico -/* { - '00 00 01 00 09 00 20 20 00 00 01 00 08 00 A8 08' - '00 00 96 00 00 00 10 10 00 00 01 00 08 00 68 05' - '00 00 3E 09 00 00 20 20 00 00 01 00 04 00 E8 02' - '00 00 A6 0E 00 00 10 10 00 00 01 00 04 00 28 01' - '00 00 8E 11 00 00 30 30 00 00 01 00 08 00 A8 0E' - '00 00 B6 12 00 00 30 30 00 00 01 00 04 00 68 06' - '00 00 5E 21 00 00 30 30 00 00 01 00 20 00 A8 25' - '00 00 C6 27 00 00 20 20 00 00 01 00 20 00 A8 10' - '00 00 6E 4D 00 00 10 10 00 00 01 00 20 00 68 04' - '00 00 16 5E 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' +/*{ + '00 00 01 00 0C 00 10 10 10 00 00 00 00 00 28 01' + '00 00 C6 00 00 00 10 10 00 00 00 00 00 00 68 05' + '00 00 EE 01 00 00 10 10 00 00 00 00 00 00 68 04' + '00 00 56 07 00 00 20 20 10 00 00 00 00 00 E8 02' + '00 00 BE 0B 00 00 20 20 00 00 00 00 00 00 A8 08' + '00 00 A6 0E 00 00 20 20 00 00 00 00 00 00 A8 10' + '00 00 4E 17 00 00 30 30 10 00 00 00 00 00 68 06' + '00 00 F6 27 00 00 30 30 00 00 00 00 00 00 A8 0E' + '00 00 5E 2E 00 00 30 30 00 00 00 00 00 00 A8 25' + '00 00 06 3D 00 00 40 40 10 00 00 00 00 00 68 0A' + '00 00 AE 62 00 00 40 40 00 00 00 00 00 00 28 16' + '00 00 16 6D 00 00 40 40 00 00 00 00 00 00 28 42' + '00 00 3E 83 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 04 00 00 00 00 00 80 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 01 00 00 00 02 00 00 00 03 00 00 00 00 00' - '05 00 11 11 11 00 22 22 22 00 33 33 33 00 44 44' - '44 00 55 55 55 00 66 66 66 00 6F 6D 70 00 77 77' - '77 00 7F 7F 7F 00 99 33 00 00 CC 33 00 00 99 66' - '33 00 CC 66 00 00 CC 66 33 00 D0 60 38 00 CC 99' - '33 00 CC 99 66 00 FF 99 66 00 FF CC 66 00 88 88' - '88 00 99 99 99 00 AA AA AA 00 BB BB BB 00 CC 99' - '99 00 CC CC CC 00 DD DD DD 00 FF CC CC 00 EE EE' - 'EE 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 70 70 70 70 00 00 00 07 07' + '07 07 07 77 77 77 00 70 00 77 70 07 07 77 00 70' + '00 00 00 07 77 77 87 A7 87 77 77 07 A7 77 76 66' + '66 66 67 07 00 07 76 66 66 66 67 07 00 07 76 66' + '66 66 67 07 00 07 76 66 66 66 67 07 00 07 76 66' + '66 66 67 08 00 07 76 66 66 66 67 08 00 07 76 66' + '66 66 67 08 00 07 76 66 66 77 77 08 77 77 77 78' + '88 88 87 78 88 88 00 00 00 00 00 00 00 00 FF FF' + '00 00 C0 7F 00 00 80 00 00 00 C0 00 00 00 C0 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 C0 17 95 00 00 00 38 00 A8 44' - 'F9 77 15 00 00 00 78 0A 38 00 00 00 38 00 B0 6C' - '38 00 98 17 95 00 00 00 00 00 E0 19 95 00 F0 88' - 'FA 77 70 38 F5 77 FF FF FF FF A8 44 F9 77 70 7D' - 'F5 77 3A 8A F5 77 96 00 00 00 96 00 00 00 09 00' - '00 00 B0 18 95 00 00 00 00 00 CB 44 F9 77 38 9F' - '07 00 CD 8B F5 77 78 13 05 00 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00' - '00 00 00 00 00 00 5E 00 60 00 00 EC FD 7F 1A 02' - '00 00 4C 16 95 00 40 9F 07 00 FC 15 95 00 FF FF' - 'FF FF B4 1A 95 00 45 00 00 00 28 02 00 00 FF FF' - 'FF FF E2 D8 F5 77 7D 9B F5 77 94 B6 01 00 00 00' - '05 00 F4 17 95 00 80 00 10 C0 B4 1A 95 00 F0 88' - 'FA 77 88 1C F5 77 FF FF FF FF 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 9B B2 E7 77 B7 00 00 00 02 00' - '00 00 A4 1A 95 00 01 00 00 00 18 00 00 00 00 00' - '00 00 10 19 95 00 42 00 00 00 00 00 00 00 F4 18' - '95 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00' - '00 00 02 00 00 00 01 01 F5 77 00 EC FD 7F 5E 00' - '00 00 00 00 00 00 03 00 00 00 66 00 1A 02 40 9F' - '07 00 00 00 00 00 40 9F 07 00 05 00 00 00 BE B3' - 'E7 77 4C 19 95 00 A3 B4 E7 77 F8 00 00 00 00 00' - '00 C0 00 00 00 00 00 00 00 00 02 00 00 00 80 00' - '00 00 00 00 00 00 8C 1A 95 00 7F E9 4B 00 C0 CE' - '0B 01 00 00 00 C0 00 00 00 00 80 1A 95 00 02 00' - '00 00 80 00 00 00 00 00 00 00 C0 27 95 00 C4 F5' - 'AF 00 02 00 00 00 44 3A 5C 6F 73 65 78 70 65 72' - '74 73 5C 72 65 61 63 74 6F 73 5C 6C 69 62 5C 73' - '68 65 6C 6C 33 32 5C 6D 79 63 6F 6D 70 75 74 65' - '72 2E 69 63 6F 00 14 1A 95 00 1F 3B D4 77 15 00' - '00 00 A8 00 00 00 4F 3B D4 77 E0 19 95 00 33 3B' - 'D4 77 64 C5 F5 77 A9 F1 E7 77 F8 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 1A 95 00 B0 6C' - '38 00 96 00 00 00 00 00 00 00 C9 F1 E7 77 96 00' - '00 00 A4 1A 95 00 09 00 00 00 00 00 00 00 96 00' - '00 00 96 00 00 00 09 00 00 00 F4 19 95 00 33 3B' - 'D4 77 B4 1A 95 00 09 48 E9 77 B8 10 E9 77 FF FF' - 'FF FF C9 F1 E7 77 16 EA 4B 00 F8 00 00 00 B0 6C' - '38 00 96 00 00 00 58 1A 95 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 22 22 22 22 22 01 01 01 02 02' - '02 03 04 03 22 22 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 22 0B 1B 1A 1A 1A 1A 19 19 19' - '19 18 0D 18 07 22 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 22 05 1E 21 20 1E 1E 1D 1D 1D 1B' - '1B 1B 19 1A 0D 22 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 22 06 20 21 20 20 20 20 1E 1E 1D' - '1D 1D 1A 1A 18 04 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 05 1D 21 21 21 20 20 20 1E 1E' - '1D 1D 1A 1B 0D 22 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 22 19 21 21 21 21 20 1E 1E 1E' - '1E 1D 1B 1D 09 22 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 22 06 1E 21 21 21 20 1A 1B 20' - '1E 1E 1D 19 05 22 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 22 08 20 21 21 21 1E 1E 20' - '20 20 1A 06 22 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 22 07 1B 21 21 21 21 21' - '1E 18 06 22 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 22 03 07 0A 1A 18 09' - '06 22 22 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 22 22 22 22 22 22 22 22 22 22 04 0C 08 22' - '22 22 22 22 22 22 22 22 22 22 00 00 00 00 00 00' - '00 22 06 19 1A 1A 1A 1A 1A 1A 1A 1A 1A 1D 1B 1A' - '19 19 19 19 19 19 19 19 0C 05 22 00 00 00 00 00' - '22 04 1B 21 21 21 21 21 21 21 20 20 20 1E 1E 1E' - '1E 1E 1E 1E 1E 1E 1D 1D 1E 0D 22 00 00 00 00 00' - '22 06 20 21 20 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E' - '1E 1E 1D 1D 1D 1D 1D 1D 1D 1A 04 00 00 00 00 00' - '22 06 20 21 1C 12 12 14 14 15 15 15 15 15 15 15' - '15 15 14 13 12 12 12 1B 1D 1A 04 00 00 00 00 00' - '22 06 20 21 1C 11 12 12 13 14 14 14 14 14 14 14' - '14 13 12 12 12 12 12 1B 1E 1A 04 00 00 00 00 00' - '22 06 20 21 1C 11 12 12 12 13 13 14 14 14 14 13' - '13 12 12 12 12 11 11 1B 1E 1A 04 00 00 00 00 00' - '22 06 20 21 1C 0F 12 12 12 12 12 12 12 12 12 12' - '12 12 12 12 12 11 11 1B 1E 1A 04 00 00 00 00 00' - '22 06 20 21 1C 0E 11 12 12 12 12 12 12 12 12 12' - '12 12 12 11 11 0E 0E 1B 1E 1A 04 00 00 00 00 00' - '22 06 20 21 15 0E 0E 11 11 12 12 12 12 12 12 12' - '12 12 11 0F 0E 0E 0E 1B 1E 1A 03 00 00 00 00 00' - '22 06 20 21 15 0E 0E 11 12 12 12 12 12 12 12 12' - '12 12 12 12 0E 0E 0E 1B 1E 1A 03 00 00 00 00 00' - '22 06 1E 21 15 0E 12 12 12 13 13 14 14 14 14 14' - '13 13 12 12 12 11 0E 1D 20 1A 02 00 00 00 00 00' - '22 06 1E 21 15 11 12 12 13 14 14 14 15 15 15 14' - '14 14 13 12 12 12 11 1B 20 1A 01 00 00 00 00 00' - '22 06 1E 21 15 12 12 13 14 14 15 15 15 15 15 15' - '15 14 14 13 12 12 12 1D 20 1A 01 00 00 00 00 00' - '22 06 1E 21 15 12 13 14 14 15 15 16 16 16 16 16' - '15 15 14 14 13 12 12 1D 21 1A 22 00 00 00 00 00' - '22 06 1E 21 15 12 14 14 15 15 16 16 16 16 16 16' - '16 15 15 14 13 12 12 1F 21 1A 22 00 00 00 00 00' - '22 06 1E 21 15 12 15 15 15 15 15 16 17 17 17 16' - '15 15 15 15 15 12 10 1F 21 1A 22 00 00 00 00 00' - '22 05 1E 21 20 1E 20 20 20 20 20 20 20 20 20 20' - '20 20 20 20 20 1E 1E 21 21 1A 22 00 00 00 00 00' - '22 22 18 21 21 21 21 21 21 21 21 21 21 21 21 21' - '21 21 21 21 21 21 21 21 20 09 22 00 00 00 00 00' - '00 22 05 09 0C 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B' - '0B 0B 0B 0B 0B 0B 0B 0B 08 02 22 00 00 00 00 00' - '00 00 22 22 22 22 22 22 22 22 22 22 22 22 22 22' - '22 22 22 22 22 22 22 22 22 22 00 00 00 00 FF FF' - 'FF FF FF 00 00 FF FF 00 00 FF FE 00 00 FF FE 00' - '00 FF FF 00 00 FF FF 00 00 FF FF 00 00 FF FF 80' - '01 FF FF C0 03 FF FF E0 07 FF F0 00 00 0F E0 00' - '00 07 C0 00 00 07 C0 00 00 07 C0 00 00 07 C0 00' - '00 07 C0 00 00 07 C0 00 00 07 C0 00 00 07 C0 00' - '00 07 C0 00 00 07 C0 00 00 07 C0 00 00 07 C0 00' - '00 07 C0 00 00 07 C0 00 00 07 C0 00 00 07 C0 00' - '00 07 C0 00 00 07 E0 00 00 07 F0 00 00 0F 28 00' + '00 00 00 00 00 00 00 00 00 00 FF FF 00 00 28 00' '00 00 10 00 00 00 20 00 00 00 01 00 08 00 00 00' - '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 01 00 00 00 02 00' - '00 00 03 00 00 00 00 00 05 00 08 00 00 00 11 11' - '11 00 22 22 22 00 33 33 33 00 44 44 44 00 55 55' - '55 00 66 66 66 00 6F 6D 70 00 77 77 77 00 7F 7F' - '7F 00 99 33 00 00 CC 66 00 00 CC 66 33 00 D0 60' - '38 00 CC 66 66 00 CC 99 33 00 CC 99 66 00 FF 99' - '66 00 FF CC 66 00 88 88 88 00 99 99 99 00 AA AA' - 'AA 00 BB BB BB 00 CC 99 99 00 CC CC 99 00 FF CC' - '99 00 CC CC CC 00 DD DD DD 00 EE EE EE 00 FF FF' - 'FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 17' - '95 00 00 00 38 00 A8 44 F9 77 15 00 00 00 78 0A' - '38 00 00 00 38 00 B0 6C 38 00 98 17 95 00 00 00' - '00 00 E0 19 95 00 F0 88 FA 77 70 38 F5 77 FF FF' - 'FF FF A8 44 F9 77 70 7D F5 77 3A 8A F5 77 96 00' - '00 00 96 00 00 00 09 00 00 00 B0 18 95 00 00 00' - '00 00 CB 44 F9 77 38 9F 07 00 CD 8B F5 77 78 13' - '05 00 37 90 F5 77 00 00 00 00 3E 8A F5 77 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 10 00 00 00 00 00 00 00 5E 00' - '60 00 00 EC FD 7F 1A 02 00 00 4C 16 95 00 40 9F' - '07 00 FC 15 95 00 FF FF FF FF B4 1A 95 00 45 00' - '00 00 28 02 00 00 FF FF FF FF E2 D8 F5 77 7D 9B' - 'F5 77 94 B6 01 00 00 00 05 00 F4 17 95 00 80 00' - '10 C0 B4 1A 95 00 F0 88 FA 77 88 1C F5 77 FF FF' - 'FF FF 37 90 F5 77 00 00 00 00 3E 8A F5 77 9B B2' - 'E7 77 B7 00 00 00 02 00 00 00 A4 1A 95 00 01 00' - '00 00 18 00 00 00 00 00 00 00 10 19 95 00 42 00' - '00 00 00 00 00 00 F4 18 95 00 00 00 00 00 00 00' - '00 00 00 00 00 00 0C 00 00 00 02 00 00 00 01 01' - 'F5 77 00 EC FD 7F 5E 00 00 00 00 00 00 00 03 00' - '00 00 66 00 1A 02 40 9F 07 00 00 00 00 00 40 9F' - '07 00 05 00 00 00 BE B3 E7 77 4C 19 95 00 A3 B4' - 'E7 77 F8 00 00 00 00 00 00 C0 00 00 00 00 00 00' - '00 00 02 00 00 00 80 00 00 00 00 00 00 00 8C 1A' - '95 00 7F E9 4B 00 C0 CE 0B 01 00 00 00 C0 00 00' - '00 00 80 1A 95 00 02 00 00 00 80 00 00 00 00 00' - '00 00 C0 27 95 00 C4 F5 AF 00 02 00 00 00 44 3A' - '5C 6F 73 65 78 70 65 72 74 73 5C 72 65 61 63 74' - '6F 73 5C 6C 69 62 5C 73 68 65 6C 6C 33 32 5C 6D' - '79 63 6F 6D 70 75 74 65 72 2E 69 63 6F 00 14 1A' - '95 00 1F 3B D4 77 15 00 00 00 A8 00 00 00 4F 3B' - 'D4 77 E0 19 95 00 33 3B D4 77 64 C5 F5 77 A9 F1' - 'E7 77 F8 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 1A 95 00 B0 6C 38 00 96 00 00 00 00 00' - '00 00 C9 F1 E7 77 96 00 00 00 A4 1A 95 00 09 00' - '00 00 00 00 00 00 96 00 00 00 96 00 00 00 09 00' - '00 00 F4 19 95 00 33 3B D4 77 B4 1A 95 00 09 48' - 'E9 77 B8 10 E9 77 FF FF FF FF C9 F1 E7 77 16 EA' - '4B 00 F8 00 00 00 B0 6C 38 00 96 00 00 00 58 1A' - '95 00 00 00 00 00 00 00 00 00 01 05 05 05 05 05' - '05 01 00 00 00 00 00 00 00 00 0A 1F 1B 1A 1A 19' - '18 08 00 00 00 00 00 00 00 00 0D 22 21 21 20 1F' - '1B 09 00 00 00 00 00 00 00 00 08 21 22 20 20 21' - '1B 07 00 00 00 00 00 00 00 00 00 0A 20 21 21 1B' - '08 00 00 00 00 00 00 00 02 04 04 23 06 0D 0B 06' - '23 05 05 02 00 00 00 03 19 1F 1F 1F 1B 1F 1B 1B' - '1B 1B 1B 0C 23 00 00 06 21 1E 1E 1E 1E 1E 1E 1D' - '1C 15 1D 1B 05 00 00 07 21 15 10 11 12 14 14 11' - '11 10 15 1B 05 00 00 07 21 15 10 11 11 11 11 11' - '11 0F 15 1B 05 00 00 06 21 11 0F 11 11 11 11 11' - '11 0F 15 1B 05 00 00 06 21 13 11 12 14 14 14 14' - '11 10 15 1B 05 00 00 06 20 15 11 14 15 16 15 15' - '14 11 15 1F 05 00 00 06 20 15 14 15 16 17 17 16' - '15 11 1C 1F 04 00 00 05 1F 22 21 21 21 21 21 21' - '21 21 22 1A 23 00 00 00 07 09 09 09 09 09 09 09' - '09 09 09 06 00 00 F0 0F 00 00 F0 0F 00 00 F0 0F' - '00 00 F0 0F 00 00 F8 1F 00 00 C0 03 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 C0 03 00 00 28 00 00 00 20 00 00 00 40 00' - '00 00 01 00 04 00 00 00 00 00 00 02 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 80 00 00 00 00 80 00 00 80 80 00 00 00 00' - '80 00 80 00 80 00 00 80 80 00 C0 C0 C0 00 80 80' - '80 00 FF 00 00 00 00 FF 00 00 FF FF 00 00 00 00' - 'FF 00 FF 00 FF 00 00 FF FF 00 FF FF FF 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 08 77 77 78 88 88 88 80 00 00 00 00 00 00' - '00 00 07 FF 77 77 77 77 87 80 00 00 00 00 00 00' - '00 00 0F FF FF FF 77 77 77 80 00 00 00 00 00 00' - '00 00 07 FF FF FF 77 77 77 80 00 00 00 00 00 00' - '00 00 08 FF FF FF 77 77 77 80 00 00 00 00 00 00' - '00 00 00 FF FF F7 7F F7 78 00 00 00 00 00 00 00' - '00 00 00 8F FF F7 7F FF 70 00 00 00 00 00 00 00' - '00 00 00 00 7F FF FF 78 00 00 00 00 00 00 00 00' - '00 00 00 00 00 87 88 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 08 80 00 00 00 00 00 00 00 00 00' - '08 77 77 77 77 77 77 88 88 88 88 80 00 00 00 00' - '7F FF FF FF FF FF 7F 77 77 77 77 78 00 00 00 00' - 'FF FF 77 77 77 77 77 77 77 77 77 77 00 00 00 00' - 'FF 73 33 88 87 77 77 88 83 33 37 77 00 00 00 00' - 'FF 73 33 33 88 88 88 83 33 33 37 77 00 00 00 00' - 'FF 73 33 33 33 88 33 33 33 33 37 77 00 00 00 00' - 'FF 73 33 33 33 33 33 33 33 33 37 77 00 00 00 00' - 'FF 83 33 33 33 33 33 33 33 33 37 77 00 00 00 00' - 'FF 83 33 33 33 33 33 33 33 33 37 77 00 00 00 00' - 'FF 83 33 33 33 33 33 33 33 33 37 F7 00 00 00 00' - 'FF 83 33 33 33 38 33 33 33 33 37 F7 00 00 00 00' - 'FF 83 33 33 88 88 88 83 33 33 37 F7 00 00 00 00' - '7F 83 33 88 88 87 88 88 83 33 37 F7 00 00 00 00' - '7F 83 38 88 77 77 77 78 88 33 37 F7 00 00 00 00' - '7F 83 38 88 77 77 77 78 88 33 37 F7 00 00 00 00' - '7F 83 88 87 77 77 77 77 88 83 37 F7 00 00 00 00' - '7F FF FF FF FF FF FF FF FF FF 7F F7 00 00 00 00' - '8F FF FF FF FF FF FF FF FF FF FF F8 00 00 00 00' - '08 88 88 88 88 88 88 88 88 88 88 80 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 00' - '00 FF FE 00 00 7F FE 00 00 7F FE 00 00 7F FE 00' - '00 7F FE 00 00 7F FE 00 00 7F FF 00 00 FF FF 00' - '00 FF FF 80 01 FF F0 00 00 0F E0 00 00 07 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 E0 00 00 07 28 00' - '00 00 10 00 00 00 20 00 00 00 01 00 04 00 00 00' - '00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 80' - '00 00 80 80 00 00 00 00 80 00 80 00 80 00 00 80' - '80 00 C0 C0 C0 00 80 80 80 00 FF 00 00 00 00 FF' - '00 00 FF FF 00 00 00 00 FF 00 FF 00 FF 00 00 FF' - 'FF 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' - '87 77 78 80 00 00 00 00 8F FF 77 78 00 00 00 00' - '0F FF 7F 70 00 00 00 00 08 FF F7 80 00 00 00 00' - '00 08 80 00 00 00 00 87 77 77 77 77 78 00 00 F7' - '77 77 77 78 77 00 00 F8 33 88 83 33 87 00 00 F8' - '33 33 33 33 87 00 00 F3 33 33 33 33 87 00 00 F3' - '38 88 88 33 87 00 00 F8 38 88 88 83 87 00 00 F8' - '88 77 78 83 77 00 00 7F FF FF FF FF F7 00 00 08' - '88 88 88 88 80 00 E0 0F 00 00 E0 07 00 00 E0 07' - '00 00 E0 07 00 00 F0 0F 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 28 00 00 00 30 00 00 00 60 00' - '00 00 01 00 08 00 00 00 00 00 00 09 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 01 00 00 00 02 00 00 00 03 00 00 00 00 00' - '05 00 08 00 00 00 11 11 11 00 22 22 22 00 33 33' - '33 00 44 44 44 00 55 55 55 00 6D 65 5C 00 69 6E' - '65 00 6F 6D 70 00 77 77 77 00 7F 7F 7F 00 99 33' - '00 00 CC 33 00 00 99 66 33 00 98 69 38 00 CC 66' - '00 00 CC 66 33 00 D0 60 38 00 CC 99 33 00 CC 99' - '66 00 FF 99 66 00 FF CC 66 00 88 88 88 00 99 99' - '99 00 AA AA AA 00 BB BB BB 00 CC 99 99 00 FF CC' - '99 00 CC CC CC 00 DD DD DD 00 EE EE EE 00 FF FF' - 'FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 C0 17 95 00 00 00 38 00 A8 44' - 'F9 77 15 00 00 00 78 0A 38 00 00 00 38 00 B0 6C' - '38 00 98 17 95 00 00 00 00 00 E0 19 95 00 F0 88' - 'FA 77 70 38 F5 77 FF FF FF FF A8 44 F9 77 70 7D' - 'F5 77 3A 8A F5 77 96 00 00 00 96 00 00 00 09 00' - '00 00 B0 18 95 00 00 00 00 00 CB 44 F9 77 38 9F' - '07 00 CD 8B F5 77 78 13 05 00 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00' - '00 00 00 00 00 00 5E 00 60 00 00 EC FD 7F 1A 02' - '00 00 4C 16 95 00 40 9F 07 00 FC 15 95 00 FF FF' - 'FF FF B4 1A 95 00 45 00 00 00 28 02 00 00 FF FF' - 'FF FF E2 D8 F5 77 7D 9B F5 77 94 B6 01 00 00 00' - '05 00 F4 17 95 00 80 00 10 C0 B4 1A 95 00 F0 88' - 'FA 77 88 1C F5 77 FF FF FF FF 37 90 F5 77 00 00' - '00 00 3E 8A F5 77 9B B2 E7 77 B7 00 00 00 02 00' - '00 00 A4 1A 95 00 01 00 00 00 18 00 00 00 00 00' - '00 00 10 19 95 00 42 00 00 00 00 00 00 00 F4 18' - '95 00 00 00 00 00 00 00 00 00 00 00 00 00 0C 00' - '00 00 02 00 00 00 01 01 F5 77 00 EC FD 7F 5E 00' - '00 00 00 00 00 00 03 00 00 00 66 00 1A 02 40 9F' - '07 00 00 00 00 00 40 9F 07 00 05 00 00 00 BE B3' - 'E7 77 4C 19 95 00 A3 B4 E7 77 F8 00 00 00 00 00' - '00 C0 00 00 00 00 00 00 00 00 02 00 00 00 80 00' - '00 00 00 00 00 00 8C 1A 95 00 7F E9 4B 00 C0 CE' - '0B 01 00 00 00 C0 00 00 00 00 80 1A 95 00 02 00' - '00 00 80 00 00 00 00 00 00 00 C0 27 95 00 C4 F5' - 'AF 00 02 00 00 00 44 3A 5C 6F 73 65 78 70 65 72' - '74 73 5C 72 65 61 63 74 6F 73 5C 6C 69 62 5C 73' - '68 65 6C 6C 33 32 5C 6D 79 63 6F 6D 70 75 74 65' - '72 2E 69 63 6F 00 14 1A 95 00 1F 3B D4 77 15 00' - '00 00 A8 00 00 00 4F 3B D4 77 E0 19 95 00 33 3B' - 'D4 77 64 C5 F5 77 A9 F1 E7 77 F8 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 1A 95 00 B0 6C' - '38 00 96 00 00 00 00 00 00 00 C9 F1 E7 77 96 00' - '00 00 A4 1A 95 00 09 00 00 00 00 00 00 00 96 00' - '00 00 96 00 00 00 09 00 00 00 F4 19 95 00 33 3B' - 'D4 77 B4 1A 95 00 09 48 E9 77 B8 10 E9 77 FF FF' - 'FF FF C9 F1 E7 77 16 EA 4B 00 F8 00 00 00 B0 6C' - '38 00 96 00 00 00 58 1A 95 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 25 25 25 25 25' - '25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25' - '25 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 25 25 25 25 25 25' - '25 25 25 25 01 01 01 01 01 02 02 03 04 04 03 25' - '25 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 25 25 07 1C 1E 1E 1D' - '1D 1D 1D 1D 1D 1D 1D 1C 1C 1C 1C 1B 1B 1B 1B 0A' - '03 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 25 25 0A 24 24 23 23' - '22 22 22 22 21 21 21 21 1E 1E 1E 1D 1C 1C 1C 1D' - '07 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 25 25 0F 24 24 23 23' - '22 22 22 22 21 21 21 21 1E 1E 1E 1E 1D 1C 1C 1D' - '09 25 25 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 25 25 1C 24 24 24 23' - '23 23 23 23 23 22 22 22 21 21 21 1E 1D 1D 1D 1E' - '09 25 25 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 25 25 1B 24 24 24 24' - '24 24 24 24 24 23 23 22 22 22 21 21 1E 1D 1D 1E' - '09 25 25 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 25 25 0B 24 24 24 23' - '23 23 22 22 22 22 22 21 21 21 1E 1E 1D 1D 1E 1E' - '07 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 25 08 23 24 24 24' - '24 24 24 23 23 23 23 22 22 22 22 21 1E 1E 1E 1C' - '06 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 25 04 1C 24 24 24' - '24 24 24 24 22 1E 21 22 23 22 22 21 21 1E 1E 09' - '25 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 25 25 06 21 24 24' - '24 24 24 24 1D 1C 1C 21 23 22 22 21 21 22 1B 25' - '25 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 25 25 09 21 24' - '24 24 24 24 23 22 22 23 23 23 22 22 22 0E 06 25' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 25 25 08 1E' - '24 24 24 24 24 24 24 24 23 23 23 21 0E 05 25 25' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 25 25 05' - '0A 21 24 24 24 24 24 24 24 22 1C 08 25 25 25 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 25 25' - '25 06 09 0C 1B 1D 1D 0F 0A 08 04 25 25 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 25 25 25 25 25 25 25 25 25 25 25' - '25 25 25 25 04 0F 09 25 25 25 25 25 25 25 25 25' - '25 25 25 25 25 25 25 00 00 00 00 00 00 00 00 00' - '00 00 00 25 25 25 25 25 25 25 25 25 25 25 25 25' - '25 25 25 25 0A 1E 1B 07 25 25 25 25 25 25 25 25' - '25 25 25 25 25 25 25 25 25 00 00 00 00 00 00 00' - '00 00 25 25 04 0A 1D 1D 1D 1D 1D 1D 1D 1D 1D 1D' - '1D 1D 1D 1D 21 21 21 1E 1D 1D 1D 1D 1C 1C 1C 1C' - '1C 1C 1C 1C 1C 1C 08 25 25 00 00 00 00 00 00 00' - '00 00 25 25 0E 24 24 24 24 24 24 24 24 24 23 23' - '23 23 23 23 23 22 22 22 22 22 22 22 22 22 22 22' - '22 21 21 21 21 22 21 08 25 25 00 00 00 00 00 00' - '00 25 25 06 22 24 24 24 23 23 23 23 23 23 23 23' - '23 23 22 22 22 22 22 22 22 22 22 21 21 21 21 21' - '21 21 21 21 1E 1E 22 1D 25 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 23 23 23 23 23 23 23 23 23' - '23 23 23 23 23 23 22 22 22 22 22 22 22 22 22 22' - '22 22 21 22 21 1E 21 1D 04 25 00 00 00 00 00 00' - '00 25 25 09 23 24 24 21 18 18 18 1F 1F 1F 20 20' - '20 20 20 20 20 20 20 20 20 20 20 20 1F 1F 1F 18' - '18 18 18 1F 22 21 21 1D 04 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 14 15 15 15 15 16 17 17' - '17 17 17 18 18 18 17 17 17 17 17 16 15 15 15 15' - '15 14 14 15 22 21 21 1D 04 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 14 15 15 15 15 16 17 17' - '17 17 17 18 18 18 18 17 17 17 17 16 15 15 15 15' - '15 15 14 15 22 21 21 1D 04 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 14 15 15 15 15 15 16 16' - '17 17 17 17 17 17 17 17 17 16 16 15 15 15 15 15' - '15 15 14 15 22 21 21 1D 04 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 14 15 15 15 15 15 15 15' - '16 16 16 17 17 17 16 16 16 15 15 15 15 15 15 15' - '15 14 10 15 22 21 21 1D 04 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 11 14 15 15 15 15 15 15' - '15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15' - '14 14 10 15 22 21 22 1D 04 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 10 14 14 15 15 15 15 15' - '15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 14' - '14 10 10 15 23 21 22 1D 04 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 10 11 14 14 15 15 15 15' - '15 15 15 15 15 15 15 15 15 15 15 15 15 14 14 14' - '10 10 10 15 23 21 22 1D 03 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 10 10 10 14 14 14 14 15' - '15 15 15 15 15 15 15 15 15 15 15 15 14 14 14 10' - '10 10 10 15 23 21 22 1D 03 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 10 10 10 10 14 14 15 15' - '15 15 15 15 15 15 15 15 15 15 15 15 15 14 14 10' - '10 10 10 15 23 22 22 1D 03 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 10 10 10 10 15 15 15 15' - '15 15 15 16 16 16 16 15 15 15 15 15 15 15 15 14' - '10 10 10 15 23 22 22 1D 02 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 1F 10 10 14 15 15 15 15 15' - '16 16 16 17 17 17 17 17 16 16 16 15 15 15 15 15' - '15 10 10 15 23 22 22 1D 02 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 18 10 14 15 15 15 15 16 16' - '17 17 17 17 17 17 17 17 17 17 17 16 15 15 15 15' - '15 15 10 15 23 22 22 1D 02 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 18 10 15 15 15 16 16 17 17' - '17 17 18 18 18 18 18 18 17 17 17 17 16 16 15 15' - '15 15 15 15 23 22 22 1D 01 25 00 00 00 00 00 00' - '00 25 25 08 23 24 24 18 14 15 15 15 16 17 17 17' - '18 18 18 18 18 18 18 18 18 18 17 17 17 17 16 15' - '15 15 15 15 23 22 22 1D 01 25 00 00 00 00 00 00' - '00 25 25 08 22 24 24 18 15 15 15 16 17 17 17 18' - '18 18 18 18 19 19 18 18 18 18 18 18 17 17 17 16' - '15 15 15 15 23 22 23 1D 25 25 00 00 00 00 00 00' - '00 25 25 08 22 24 24 18 15 15 16 17 17 17 18 18' - '18 19 19 19 19 19 19 19 19 18 18 18 18 17 17 16' - '16 15 15 15 23 23 23 1D 25 25 00 00 00 00 00 00' - '00 25 25 08 22 24 24 18 14 16 16 17 17 18 18 18' - '19 19 19 19 19 19 19 19 19 19 18 18 18 17 17 17' - '16 15 15 15 23 24 24 1D 25 25 00 00 00 00 00 00' - '00 25 25 07 22 24 24 18 10 15 17 17 17 18 18 19' - '19 19 19 1A 1A 1A 1A 19 19 19 19 18 18 17 17 17' - '16 15 15 15 23 24 24 1D 25 25 00 00 00 00 00 00' - '00 25 25 07 22 24 24 1F 12 15 18 18 18 18 18 18' - '18 18 18 1A 1A 1A 1A 18 18 18 18 18 18 18 18 18' - '18 15 12 13 24 24 24 1D 25 25 00 00 00 00 00 00' - '00 25 25 07 22 24 24 23 23 23 23 23 23 23 23 23' - '23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 22' - '22 22 22 23 24 24 24 1D 25 25 00 00 00 00 00 00' - '00 25 25 06 21 24 24 24 24 24 24 24 24 24 24 24' - '24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24' - '24 24 24 24 24 24 24 0F 25 25 00 00 00 00 00 00' - '00 00 25 25 0A 22 24 24 24 24 24 24 24 24 24 24' - '24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24' - '24 24 24 24 24 24 1D 07 25 25 00 00 00 00 00 00' - '00 00 25 25 01 08 0D 0F 0F 0F 0F 0F 0F 0F 0F 0F' - '0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0E 0E 0E' - '0E 0E 0E 0E 0E 0A 06 25 25 00 00 00 00 00 00 00' - '00 00 00 25 25 25 25 25 25 25 25 25 25 25 25 25' - '25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25' - '25 25 25 25 25 25 25 25 25 00 00 00 00 00 00 00' - '00 00 00 00 00 25 25 25 25 25 25 25 25 25 25 25' - '25 25 25 25 25 25 25 25 25 25 25 25 25 25 25 25' - '25 25 25 25 25 25 25 00 00 00 00 00 00 00 FF FF' - 'FF FF FF FF 00 00 FF F8 00 00 1F FF 00 00 FF F0' - '00 00 0F FF 00 00 FF E0 00 00 0F FF 00 00 FF E0' - '00 00 0F FF 00 00 FF E0 00 00 07 FF 00 00 FF E0' - '00 00 07 FF 00 00 FF E0 00 00 07 FF 00 00 FF E0' - '00 00 0F FF 00 00 FF F0 00 00 0F FF 00 00 FF F0' - '00 00 0F FF 00 00 FF F0 00 00 1F FF 00 00 FF F8' - '00 00 3F FF 00 00 FF FC 00 00 3F FF 00 00 FF FE' - '00 00 7F FF 00 00 FF FF 00 01 FF FF 00 00 FE 00' - '00 00 00 7F 00 00 F8 00 00 00 00 1F 00 00 F0 00' - '00 00 00 1F 00 00 F0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 1F 00 00 F8 00' - '00 00 00 1F 00 00 FE 00 00 00 00 7F 00 00 28 00' - '00 00 30 00 00 00 60 00 00 00 01 00 04 00 00 00' - '00 00 80 04 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 80' - '00 00 80 80 00 00 00 00 80 00 80 00 80 00 00 80' - '80 00 C0 C0 C0 00 80 80 80 00 FF 00 00 00 00 FF' - '00 00 FF FF 00 00 00 00 FF 00 FF 00 FF 00 00 FF' - 'FF 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 87 77 77 77 77 78 88 88 88 88 00' - '00 00 00 00 00 00 00 00 00 00 00 00 08 FF FF FF' - '77 77 77 77 77 88 87 00 00 00 00 00 00 00 00 00' - '00 00 00 00 08 FF FF F7 77 77 77 77 77 78 87 00' - '00 00 00 00 00 00 00 00 00 00 00 00 08 FF FF FF' - 'FF FF 77 77 77 77 77 80 00 00 00 00 00 00 00 00' - '00 00 00 00 08 FF FF FF FF FF FF 77 77 77 77 00' - '00 00 00 00 00 00 00 00 00 00 00 00 08 FF FF FF' - 'F7 77 77 77 77 77 77 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 FF FF FF FF FF FF F7 77 77 78 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 8F FF FF' - 'FF 77 7F FF 77 77 78 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 07 FF FF FF 78 87 FF 77 77 80 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 7F FF' - 'FF F7 FF FF 77 78 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 07 FF FF FF FF FF F7 80 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 87' - 'FF FF FF FF 80 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 08 87 78 80 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 08 80 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 87 80 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 08 77 77 77 77 77 77' - '77 77 77 77 77 77 77 78 88 78 00 00 00 00 00 00' - '00 8F FF FF FF FF FF FF FF FF FF FF F7 77 77 77' - '77 77 70 00 00 00 00 00 00 7F FF FF FF FF FF FF' - 'FF F7 77 77 77 77 77 77 77 77 77 00 00 00 00 00' - '00 FF FF FF FF FF FF FF FF FF FF FF F7 77 77 77' - '77 77 77 00 00 00 00 00 00 FF F7 87 77 77 77 77' - '77 77 77 77 77 77 77 78 87 F7 77 00 00 00 00 00' - '00 FF F7 33 33 33 38 88 88 88 88 88 33 33 33 33' - '33 F7 77 00 00 00 00 00 00 FF F7 33 33 33 38 88' - '88 88 88 88 83 33 33 33 33 F7 77 00 00 00 00 00' - '00 FF F7 33 33 33 33 38 88 88 88 83 33 33 33 33' - '33 F7 77 00 00 00 00 00 00 FF F7 33 33 33 33 33' - '33 33 33 33 33 33 33 33 33 F7 77 00 00 00 00 00' - '00 FF F7 33 33 33 33 33 33 33 33 33 33 33 33 33' - '33 F7 77 00 00 00 00 00 00 FF F7 33 33 33 33 33' - '33 33 33 33 33 33 33 33 13 F7 77 00 00 00 00 00' - '00 FF F7 13 33 33 33 33 33 33 33 33 33 33 33 33' - '13 F7 77 00 00 00 00 00 00 FF F7 13 33 33 33 33' - '33 33 33 33 33 33 33 31 13 F7 77 00 00 00 00 00' - '00 FF F8 11 33 33 33 33 33 33 33 33 33 33 33 11' - '13 F7 77 00 00 00 00 00 00 FF F8 11 13 33 33 33' - '33 33 33 33 33 33 33 11 13 F7 77 00 00 00 00 00' - '00 FF F8 11 33 33 33 33 38 88 83 33 33 33 33 33' - '13 F7 F7 00 00 00 00 00 00 FF F8 13 33 33 33 88' - '88 88 88 88 83 33 33 33 33 F7 F7 00 00 00 00 00' - '00 FF F8 33 33 33 88 88 88 88 88 88 88 33 33 33' - '33 F7 F7 00 00 00 00 00 00 FF F8 33 33 38 88 88' - '88 88 88 88 88 83 33 33 33 F7 F7 00 00 00 00 00' - '00 FF F8 33 33 88 88 88 88 77 78 88 88 88 33 33' - '38 FF F7 00 00 00 00 00 00 FF F8 33 38 88 88 87' - '77 77 77 78 88 88 83 33 38 FF F7 00 00 00 00 00' - '00 FF F8 33 38 88 88 77 77 77 77 77 88 88 88 33' - '33 FF F7 00 00 00 00 00 00 FF F8 33 38 88 87 77' - '77 77 77 77 78 88 83 33 33 FF F7 00 00 00 00 00' - '00 FF F8 38 88 88 87 77 77 77 77 77 78 88 88 88' - '38 FF F7 00 00 00 00 00 00 FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF F7 00 00 00 00 00' - '00 7F FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF F8 00 00 00 00 00 00 8F FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF 70 00 00 00 00 00' - '00 00 88 88 88 88 88 88 88 88 88 88 88 88 88 88' - '88 88 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 FF FF FF FF FF FF 00 00 FF F8' - '00 00 1F FF 00 00 FF F0 00 00 0F FF 00 00 FF E0' - '00 00 0F FF 00 00 FF E0 00 00 0F FF 00 00 FF E0' - '00 00 07 FF 00 00 FF E0 00 00 07 FF 00 00 FF E0' - '00 00 07 FF 00 00 FF E0 00 00 0F FF 00 00 FF F0' - '00 00 0F FF 00 00 FF F0 00 00 0F FF 00 00 FF F0' - '00 00 1F FF 00 00 FF F8 00 00 3F FF 00 00 FF FC' - '00 00 3F FF 00 00 FF FE 00 00 7F FF 00 00 FF FF' - '00 01 FF FF 00 00 FE 00 00 00 00 7F 00 00 F8 00' - '00 00 00 1F 00 00 F0 00 00 00 00 1F 00 00 F0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 E0 00 00 00 00 0F 00 00 E0 00' - '00 00 00 0F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 1F 00 00 F8 00 00 00 00 1F 00 00 FE 00' - '00 00 00 7F 00 00 28 00 00 00 30 00 00 00 60 00' - '00 00 01 00 20 00 00 00 00 00 00 24 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 0C 00 00 00 2F 00 00 00 38 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 37 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 38 00 00 00 2A 00 00 00 0B 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 43 00 00 00 B0 00 00 00 F4 00 00 00 F8 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F7 00 00 00 F7 00 00 00 F7 00 00 00 F7 00 00' - '00 F8 00 00 00 DA 00 00 00 85 00 00 00 28 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 11 00 00' - '00 E2 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 01 01 01 FF 01 01 01 FF 01 01' - '01 FF 01 01 01 FF 01 01 01 FF 02 02 02 FF 02 02' - '02 FF 03 03 03 FF 04 04 04 FF 04 04 04 FF 03 03' - '03 FF 00 00 00 FF 00 00 00 FF 00 00 00 AF 00 00' - '00 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 65 00 00' - '00 FF 20 20 20 FF 9B 9B 9B FF B9 B9 B9 FF B5 B5' - 'B5 FF B2 B2 B2 FF B0 B0 B0 FF AE AE AE FF AB AB' - 'AB FF A9 A9 A9 FF A7 A7 A7 FF A4 A4 A4 FF A2 A2' - 'A2 FF 9F 9F 9F FF 9C 9C 9C FF 9B 9B 9B FF 96 96' - '96 FF 8C 8C 8C FF 86 86 86 FF 88 88 88 FF 8E 8E' - '8E FF 5A 5A 5A FF 03 03 03 FF 00 00 00 E9 00 00' - '00 36 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 A6 00 00' - '00 FF 59 59 59 FF FF FF FF FF FA FA FA FF F1 F1' - 'F1 FF EB EB EB FF E4 E4 E4 FF E0 E0 E0 FF DC DC' - 'DC FF D8 D8 D8 FF D4 D4 D4 FF D0 D0 D0 FF CB CB' - 'CB FF C7 C7 C7 FF C3 C3 C3 FF BF BF BF FF BB BB' - 'BB FF B0 B0 B0 FF 9E 9E 9E FF 97 97 97 FF 9E 9E' - '9E FF B0 B0 B0 FF 25 25 25 FF 00 00 00 FD 00 00' - '00 5A 00 00 00 01 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 05 00 00 00 C6 00 00' - '00 FF 82 82 82 FF FF FF FF FF F7 F7 F7 FF F1 F1' - 'F1 FF E9 E9 E9 FF E4 E4 E4 FF DF DF DF FF DA DA' - 'DA FF D6 D6 D6 FF D2 D2 D2 FF CE CE CE FF CA CA' - 'CA FF C7 C7 C7 FF C3 C3 C3 FF BF BF BF FF BB BB' - 'BB FF B8 B8 B8 FF A9 A9 A9 FF 9C 9C 9C FF 98 98' - '98 FF AF AF AF FF 3D 3D 3D FF 00 00 00 FF 00 00' - '00 7C 00 00 00 01 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 09 00 00 00 D4 00 00' - '00 FF 92 92 92 FF FF FF FF FF FF FF FF FF F9 F9' - 'F9 FF F6 F6 F6 FF F6 F6 F6 FF F3 F3 F3 FF EF EF' - 'EF FF EC EC EC FF E6 E6 E6 FF E1 E1 E1 FF DC DC' - 'DC FF D6 D6 D6 FF D1 D1 D1 FF CC CC CC FF C6 C6' - 'C6 FF C3 C3 C3 FF AF AF AF FF A5 A5 A5 FF A8 A8' - 'A8 FF B6 B6 B6 FF 44 44 44 FF 00 00 00 FF 00 00' - '00 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 B5 00 00' - '00 FF 84 84 84 FF FF FF FF FF FF FF FF FF F7 F7' - 'F7 FF FB FB FB FF FD FD FD FF FC FC FC FF FC FC' - 'FC FF FA FA FA FF F8 F8 F8 FF F1 F1 F1 FF EB EB' - 'EB FF E5 E5 E5 FF DF DF DF FF D9 D9 D9 FF D3 D3' - 'D3 FF CF CF CF FF B5 B5 B5 FF A3 A3 A3 FF AD AD' - 'AD FF BB BB BB FF 3D 3D 3D FF 00 00 00 FF 00 00' - '00 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 88 00 00' - '00 FF 5F 5F 5F FF FF FF FF FF FF FF FF FF FB FB' - 'FB FF EF EF EF FF EE EE EE FF E9 E9 E9 FF E3 E3' - 'E3 FF DF DF DF FF DD DD DD FF DA DA DA FF D5 D5' - 'D5 FF D0 D0 D0 FF CC CC CC FF C7 C7 C7 FF C3 C3' - 'C3 FF BE BE BE FF B0 B0 B0 FF B2 B2 B2 FF B4 B4' - 'B4 FF BA BA BA FF 28 28 28 FF 00 00 00 F7 00 00' - '00 3C 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 34 00 00' - '00 FA 2E 2E 2E FF ED ED ED FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FA FA' - 'FA FF F5 F5 F5 FF F4 F4 F4 FF EF EF EF FF EB EB' - 'EB FF E5 E5 E5 FF E0 E0 E0 FF DC DC DC FF D8 D8' - 'D8 FF D2 D2 D2 FF C2 C2 C2 FF BA BA BA FF C1 C1' - 'C1 FF 97 97 97 FF 0A 0A 0A FF 00 00 00 D5 00 00' - '00 19 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00' - '00 C8 04 04 04 FF 97 97 97 FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FD FD FD FF DF DF DF FF BE BE BE FF C4 C4' - 'C4 FF E4 E4 E4 FF E6 E6 E6 FF E1 E1 E1 FF DD DD' - 'DD FF D1 D1 D1 FF C5 C5 C5 FF C2 C2 C2 FF BF BF' - 'BF FF 43 43 43 FF 00 00 00 FF 00 00 00 9A 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 6C 00 00 00 FF 18 18 18 FF D1 D1 D1 FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FE FE FE FF B2 B2 B2 FF 9D 9D 9D FF 98 98' - '98 FF D1 D1 D1 FF ED ED ED FF E5 E5 E5 FF DF DF' - 'DF FF D1 D1 D1 FF CC CC CC FF D5 D5 D5 FF 84 84' - '84 FF 00 00 00 FF 00 00 00 E2 00 00 00 29 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 96 00 00 00 FF 3E 3E 3E FF D1 D1' - 'D1 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF ED ED ED FF DE DE DE FF E1 E1' - 'E1 FF EC EC EC FF EE EE EE FF E7 E7 E7 FF DC DC' - 'DC FF DC DC DC FF D7 D7 D7 FF 7B 7B 7B FF 14 14' - '14 FF 00 00 00 FF 00 00 00 5F 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 1A 00 00 00 C6 00 00 00 FF 35 35' - '35 FF C3 C3 C3 FF F9 F9 F9 FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FC FC FC FF FC FC' - 'FC FF F7 F7 F7 FF F0 F0 F0 FF EC EC EC FF EB EB' - 'EB FF CF CF CF FF 77 77 77 FF 07 07 07 FF 00 00' - '00 FD 00 00 00 8F 00 00 00 05 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 05 00 00 00 B6 00 00' - '00 FF 08 08 08 FF 58 58 58 FF CB CB CB FF FD FD' - 'FD FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF F9 F9 F9 FF E3 E3 E3 FF 9E 9E' - '9E FF 31 31 31 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00' - '00 0C 00 00 00 14 00 00 00 14 00 00 00 14 00 00' - '00 14 00 00 00 14 00 00 00 0E 00 00 00 1F 00 00' - '00 7B 00 00 00 E9 00 00 00 FF 10 10 10 FF 3E 3E' - '3E FF 69 69 69 FF 89 89 89 FF B1 B1 B1 FF A2 A2' - 'A2 FF 7E 7E 7E FF 57 57 57 FF 2C 2C 2C FF 04 04' - '04 FF 00 00 00 FF 00 00 00 BF 00 00 00 4F 00 00' - '00 14 00 00 00 11 00 00 00 14 00 00 00 14 00 00' - '00 14 00 00 00 14 00 00 00 14 00 00 00 0C 00 00' - '00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 04 00 00 00 4C 00 00 00 8A 00 00' - '00 AD 00 00 00 BB 00 00 00 B9 00 00 00 B9 00 00' - '00 B9 00 00 00 B9 00 00 00 B9 00 00 00 B5 00 00' - '00 AB 00 00 00 D1 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 04 04 04 FF 7E 7E 7E FF 4B 4B' - '4B FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 F1 00 00 00 BA 00 00 00 AF 00 00' - '00 B7 00 00 00 B9 00 00 00 B9 00 00 00 B9 00 00' - '00 B9 00 00 00 B9 00 00 00 B8 00 00 00 A5 00 00' - '00 85 00 00 00 43 00 00 00 0A 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 06 00 00 00 95 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 54 54 54 FF BD BD BD FF 8A 8A' - '8A FF 1B 1B 1B FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 F0 00 00 00 69 00 00 00 0D 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 7A 00 00 00 FF 04 04 04 FF 5A 5A 5A FF A7 A7' - 'A7 FF AD AD AD FF AB AB AB FF AA AA AA FF AA AA' - 'AA FF AA AA AA FF A9 A9 A9 FF A9 A9 A9 FF A8 A8' - 'A8 FF A8 A8 A8 FF A8 A8 A8 FF A7 A7 A7 FF A6 A6' - 'A6 FF A9 A9 A9 FF C5 C5 C5 FF D3 D3 D3 FF C6 C6' - 'C6 FF B4 B4 B4 FF A3 A3 A3 FF A3 A3 A3 FF A3 A3' - 'A3 FF A2 A2 A2 FF A1 A1 A1 FF A1 A1 A1 FF A1 A1' - 'A1 FF A0 A0 A0 FF A0 A0 A0 FF 9F 9F 9F FF 9F 9F' - '9F FF 9E 9E 9E FF A1 A1 A1 FF 94 94 94 FF 31 31' - '31 FF 00 00 00 FF 00 00 00 F6 00 00 00 5C 00 00' - '00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 28 00 00' - '00 EA 00 00 00 FF 78 78 78 FF FA FA FA FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FE FE FE FF FB FB' - 'FB FF FA FA FA FF F8 F8 F8 FF F7 F7 F7 FF F4 F4' - 'F4 FF F3 F3 F3 FF F2 F2 F2 FF F0 F0 F0 FF EF EF' - 'EF FF EC EC EC FF E6 E6 E6 FF E2 E2 E2 FF E2 E2' - 'E2 FF E3 E3 E3 FF E4 E4 E4 FF E2 E2 E2 FF E0 E0' - 'E0 FF DE DE DE FF DC DC DC FF DB DB DB FF D9 D9' - 'D9 FF D8 D8 D8 FF D6 D6 D6 FF D4 D4 D4 FF D3 D3' - 'D3 FF D0 D0 D0 FF CF CF CF FF DD DD DD FF CB CB' - 'CB FF 39 39 39 FF 00 00 00 FF 00 00 00 AF 00 00' - '00 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 78 00 00' - '00 FF 17 17 17 FF DF DF DF FF FF FF FF FF F7 F7' - 'F7 FF F6 F8 F8 FF F5 F6 F8 FF F3 F4 F5 FF F1 F3' - 'F4 FF EF F0 F2 FF ED EF F0 FF EC ED EE FF E9 EA' - 'EB FF E8 E9 EA FF E6 E7 E9 FF E4 E6 E7 FF E2 E3' - 'E4 FF E0 E1 E3 FF DF E0 E1 FF DD DE DF FF DB DC' - 'DD FF D9 DB DC FF D8 D8 D9 FF D6 D7 D8 FF D4 D5' - 'D7 FF D2 D4 D4 FF D0 D1 D3 FF CE D0 D1 FF CD CE' - 'CF FF CB CD CE FF CA CB CD FF C7 C9 CA FF C6 C8' - 'C9 FF C4 C6 C7 FF C2 C3 C3 FF C0 C0 C0 FF D9 D9' - 'D9 FF A4 A4 A4 FF 00 00 00 FF 00 00 00 EE 00 00' - '00 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 39 39 39 FF F0 F0 F0 FF FF FF FF FF F9 F9' - 'F9 FF F6 F0 EB FF F5 EF EA FF F5 EE EA FF F3 EE' - 'E9 FF F3 ED E9 FF F2 EC E8 FF F2 EC E8 FF F0 EC' - 'E6 FF F0 EB E6 FF F0 EA E6 FF EF EA E4 FF ED E8' - 'E4 FF ED E8 E3 FF EC E7 E2 FF EB E6 E1 FF E9 E5' - 'E0 FF E8 E4 DF FF E7 E2 DD FF E6 E1 DC FF E5 DF' - 'DB FF E3 DE D9 FF E2 DD D8 FF E1 DB D6 FF DF DA' - 'D4 FF DE D8 D3 FF DD D7 D2 FF DB D5 D0 FF D9 D3' - 'CE FF DB D5 D2 FF D3 D2 D2 FF C3 C3 C3 FF CB CB' - 'CB FF AE AE AE FF 05 05 05 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 3C 3C 3C FF F1 F1 F1 FF FF FF FF FF FB FF' - 'FF FF E2 CA B9 FF D8 9B 70 FF DC A2 79 FF DE A7' - '7D FF E1 AA 81 FF E3 AF 85 FF E5 B2 88 FF E8 B6' - '8C FF E9 B9 8E FF EB BB 91 FF ED BE 94 FF EE C0' - '95 FF EF C1 96 FF EF C2 97 FF EF C2 97 FF EE C1' - '97 FF EE C0 95 FF EC BE 93 FF EB BB 90 FF E9 B8' - '8E FF E7 B5 8B FF E5 B1 87 FF E3 AE 84 FF E1 AA' - '81 FF DF A6 7D FF DC A2 79 FF D9 9E 75 FF D6 97' - '6E FF E0 AB 8B FF E5 E3 E2 FF C4 C5 C6 FF CC CC' - 'CC FF AB AB AB FF 06 06 06 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 3A 3A 3A FF F1 F1 F1 FF FF FF FF FF FD FF' - 'FF FF D0 A9 90 FF BC 56 11 FF C3 63 1F FF C6 69' - '26 FF CA 70 2C FF CD 76 32 FF D1 7B 37 FF D4 81' - '3D FF D7 86 41 FF D9 89 44 FF DC 8D 48 FF DD 90' - '4A FF DF 92 4D FF DF 93 4D FF DF 93 4D FF DE 91' - '4C FF DD 90 4A FF DB 8C 46 FF D8 88 43 FF D6 83' - '3F FF D3 7E 3A FF D0 78 34 FF CC 73 2F FF C8 6D' - '2A FF C5 67 24 FF C1 61 1E FF BD 5A 17 FF B6 4E' - '0A FF C4 6F 38 FF E5 E2 E0 FF C7 C8 C9 FF CF CF' - 'CF FF AC AC AC FF 05 05 05 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 39 39 39 FF F0 F0 F0 FF FF FF FF FF FF FF' - 'FF FF D1 AC 94 FF BC 5A 19 FF C2 66 26 FF C6 6C' - '2C FF CA 72 32 FF CD 77 37 FF D0 7C 3C FF D3 81' - '40 FF D5 85 44 FF D7 88 47 FF D9 8C 4A FF DA 8E' - '4C FF DC 8F 4D FF DC 90 4E FF DC 90 4E FF DB 8F' - '4D FF DA 8D 4B FF D9 8A 49 FF D6 87 46 FF D4 83' - '42 FF D1 7F 3E FF CE 7A 39 FF CB 75 34 FF C8 70' - '30 FF C5 6A 2B FF C1 64 25 FF BD 5E 1F FF B7 53' - '12 FF C6 73 3F FF E6 E3 E1 FF C8 C9 CA FF D0 D0' - 'D0 FF AC AC AC FF 05 05 05 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 38 38 38 FF F0 F0 F0 FF FF FF FF FF FF FF' - 'FF FF D1 AB 92 FF B9 55 14 FF BF 61 22 FF C3 67' - '28 FF C6 6D 2D FF CA 71 31 FF CC 76 36 FF CF 7B' - '3A FF D1 7E 3D FF D3 81 40 FF D5 85 44 FF D6 87' - '45 FF D7 88 47 FF D7 89 47 FF D7 89 47 FF D7 87' - '47 FF D6 86 45 FF D4 83 42 FF D2 80 3F FF D0 7D' - '3C FF CE 79 38 FF CB 74 34 FF C8 6F 2F FF C5 6A' - '2B FF C2 65 26 FF BE 60 21 FF BA 5A 1B FF B4 4E' - '0F FF C4 70 3C FF E6 E3 E2 FF CA CB CC FF D2 D2' - 'D2 FF AC AC AC FF 05 05 05 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 38 38 38 FF F0 F0 F0 FF FF FF FF FF FF FF' - 'FF FF D0 A9 91 FF B7 51 10 FF BD 5C 1D FF C0 62' - '23 FF C3 67 28 FF C6 6C 2C FF C9 71 30 FF CB 75' - '34 FF CE 78 37 FF CF 7B 3A FF D0 7E 3D FF D1 7F' - '3E FF D2 81 40 FF D3 81 40 FF D2 81 40 FF D2 80' - '3F FF D1 7F 3E FF D0 7C 3C FF CE 79 39 FF CD 76' - '36 FF CA 73 32 FF C7 6E 2E FF C5 6A 2A FF C2 66' - '26 FF BF 60 21 FF BC 5B 1C FF B8 55 17 FF B2 4A' - '0A FF C2 6C 39 FF E7 E4 E3 FF CC CD CE FF D4 D4' - 'D4 FF AB AB AB FF 04 04 04 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 36 36 36 FF EF EF EF FF FF FF FF FF FF FF' - 'FF FF CE A7 8F FF B3 4C 0B FF B9 58 18 FF BC 5D' - '1E FF C0 62 23 FF C3 66 27 FF C5 6A 2B FF C7 6E' - '2E FF C9 71 31 FF CB 74 33 FF CC 76 36 FF CD 78' - '37 FF CE 79 39 FF CE 79 39 FF CE 79 39 FF CE 79' - '38 FF CD 77 37 FF CC 75 35 FF CA 72 32 FF C8 70' - '30 FF C6 6C 2D FF C3 68 29 FF C1 64 25 FF BE 60' - '21 FF BC 5B 1C FF B8 56 18 FF B5 50 12 FF AF 45' - '05 FF BF 68 35 FF E8 E5 E3 FF CD CF D0 FF D6 D6' - 'D6 FF AB AB AB FF 04 04 04 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 36 36 36 FF EE EE EE FF FF FF FF FF FF FF' - 'FF FF CC A4 8C FF B0 46 06 FF B6 51 13 FF B9 57' - '18 FF BC 5B 1D FF BF 60 21 FF C1 64 24 FF C3 67' - '28 FF C5 6A 2B FF C6 6D 2D FF C8 6F 2F FF C9 70' - '30 FF C9 72 32 FF C9 72 32 FF C9 72 32 FF C9 72' - '31 FF C8 70 30 FF C7 6E 2E FF C6 6B 2C FF C4 69' - '2A FF C2 66 26 FF BF 62 22 FF BD 5E 1F FF BA 5A' - '1B FF B8 55 17 FF B5 50 12 FF B2 4B 0D FF AC 40' - '00 FF BC 64 31 FF E9 E5 E4 FF D0 D1 D2 FF D8 D8' - 'D8 FF AA AA AA FF 04 04 04 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 35 35 35 FF ED ED ED FF FF FF FF FF FF FF' - 'FF FF CA A0 88 FF AD 40 00 FF B3 4C 0E FF B5 51' - '13 FF B8 56 17 FF BB 5A 1B FF BD 5D 1E FF BF 61' - '21 FF C0 63 24 FF C1 65 25 FF C2 67 28 FF C3 69' - '29 FF C5 69 2A FF C5 6A 2A FF C5 6A 2A FF C4 69' - '29 FF C4 68 28 FF C2 66 27 FF C0 64 24 FF BF 61' - '22 FF BE 5E 20 FF BC 5B 1D FF BA 58 19 FF B7 54' - '16 FF B5 50 12 FF B2 4B 0D FF AF 46 08 FF A8 3B' - '00 FF BB 62 30 FF EA E7 E5 FF D1 D3 D4 FF D9 D9' - 'D9 FF AA AA AA FF 03 03 03 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 34 34 34 FF EC EC EC FF FF FF FF FF FF FF' - 'FF FF C6 9D 85 FF A9 3A 00 FF AF 46 09 FF B2 4B' - '0E FF B4 4F 12 FF B6 53 15 FF B8 55 17 FF B9 58' - '19 FF BB 5C 1D FF BF 60 22 FF C2 66 27 FF C4 68' - '29 FF C5 6A 2A FF C5 6B 2B FF C5 6B 2B FF C5 6B' - '2B FF C5 69 2A FF C3 68 29 FF C1 65 26 FF BF 60' - '22 FF BB 59 1B FF B7 54 15 FF B4 4F 11 FF B3 4D' - '0F FF B1 4A 0C FF AE 45 08 FF AB 40 03 FF A6 38' - '00 FF BA 61 2F FF EA E7 E6 FF D3 D4 D5 FF DB DB' - 'DB FF A9 A9 A9 FF 03 03 03 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 33 33 33 FF EC EC EC FF FF FF FF FF FF FF' - 'FF FF C4 9A 83 FF A6 37 00 FF AC 40 03 FF AE 45' - '07 FF AF 47 0A FF B3 4D 0F FF B9 57 18 FF BE 5F' - '20 FF C4 68 29 FF C6 6D 2D FF C8 70 30 FF CA 72' - '31 FF CA 73 33 FF CB 74 33 FF CB 74 33 FF CB 73' - '33 FF CA 73 32 FF C9 71 31 FF C8 70 30 FF C7 6D' - '2D FF C4 69 2A FF BF 60 22 FF B8 56 18 FF B3 4D' - '0F FF AC 42 04 FF AA 3F 02 FF A9 3E 01 FF A6 38' - '00 FF BA 61 2F FF EB E8 E6 FF D5 D6 D7 FF DC DC' - 'DC FF A9 A9 A9 FF 03 03 03 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 32 32 32 FF EB EB EB FF FF FF FF FF FF FF' - 'FF FF C3 99 81 FF A6 37 00 FF A9 3D 00 FF A9 3D' - '00 FF B1 49 0C FF C0 63 24 FF C6 6D 2D FF C9 71' - '31 FF CA 74 33 FF CB 75 35 FF CD 78 37 FF CE 79' - '39 FF CF 7B 3A FF CF 7B 3B FF CF 7B 3B FF CF 7B' - '3A FF CE 7A 3A FF CD 79 38 FF CC 76 36 FF CB 74' - '34 FF C9 71 32 FF C7 6E 2F FF C6 6B 2B FF C2 66' - '27 FF B7 54 16 FF AA 40 03 FF A8 3C 00 FF A6 38' - '00 FF BA 61 2F FF EC E9 E7 FF D7 D9 D9 FF DF DF' - 'DF FF A8 A8 A8 FF 02 02 02 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 31 31 31 FF EA EA EA FF FF FF FF FF FF FF' - 'FF FF C2 98 80 FF A5 36 00 FF A8 3C 00 FF B7 55' - '17 FF C4 6A 2A FF C8 70 31 FF CA 73 33 FF CD 76' - '36 FF CE 79 39 FF D0 7C 3B FF D1 7F 3E FF D2 80' - '3F FF D3 82 41 FF D3 83 42 FF D3 83 42 FF D3 82' - '41 FF D3 81 40 FF D2 80 3F FF D0 7D 3C FF CF 7B' - '3A FF CD 78 37 FF CB 74 34 FF C9 71 30 FF C7 6E' - '2E FF C5 6A 2B FF BE 5F 20 FF B0 4A 0C FF A5 36' - '00 FF BA 61 2F FF EC E9 E8 FF D8 DA DA FF E0 E0' - 'E0 FF A7 A7 A7 FF 02 02 02 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 30 30 30 FF E9 E9 E9 FF FF FF FF FF FF FF' - 'FF FF C0 96 7E FF A6 37 00 FF B8 55 17 FF C7 6D' - '2E FF C9 71 31 FF CC 75 35 FF CD 79 39 FF D0 7D' - '3C FF D2 80 3F FF D3 83 42 FF D5 86 44 FF D7 87' - '46 FF D8 89 48 FF D8 8A 48 FF D8 8A 48 FF D8 89' - '48 FF D7 88 47 FF D6 87 45 FF D5 84 43 FF D3 82' - '41 FF D1 7E 3D FF CE 7A 3A FF CD 77 36 FF CA 73' - '33 FF C7 6E 2E FF C5 6B 2B FF C0 63 23 FF AD 41' - '02 FF BA 60 2E FF ED EA E9 FF DA DC DD FF E2 E2' - 'E2 FF A7 A7 A7 FF 02 02 02 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 2F 2F 2F FF E8 E8 E8 FF FF FF FF FF FE FF' - 'FF FF BD 92 7B FF AF 45 05 FF C7 6D 2D FF C9 71' - '31 FF CC 76 35 FF CF 7A 3A FF D1 7E 3E FF D4 83' - '42 FF D6 86 46 FF D8 8A 48 FF DA 8D 4B FF DB 8F' - '4D FF DC 90 4E FF DC 91 50 FF DC 91 50 FF DC 91' - '4F FF DC 90 4E FF DA 8E 4C FF D9 8B 49 FF D7 88' - '47 FF D5 84 43 FF D2 80 3F FF D0 7C 3B FF CD 78' - '37 FF CB 73 32 FF C7 6E 2E FF C5 6A 2B FF BC 5B' - '1A FF BD 65 33 FF EE EA E9 FF DC DE DF FF E4 E4' - 'E4 FF A7 A7 A7 FF 01 01 01 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 2E 2E 2E FF E7 E7 E7 FF FF FF FF FF FE FF' - 'FF FF BD 93 7B FF BA 57 17 FF C9 71 31 FF CB 74' - '34 FF CE 7A 3A FF D2 7F 3E FF D4 84 43 FF D7 88' - '47 FF DA 8C 4A FF DC 8F 4D FF DE 93 51 FF DF 96' - '54 FF E0 98 56 FF E1 99 56 FF E1 99 56 FF E1 99' - '56 FF E0 97 55 FF DE 94 53 FF DD 91 50 FF DB 8E' - '4C FF D8 8A 48 FF D5 85 44 FF D3 81 40 FF D0 7C' - '3C FF CD 77 37 FF C9 72 32 FF C6 6D 2D FF C2 62' - '21 FF C4 70 3D FF ED EA E8 FF DE DF E0 FF E5 E5' - 'E5 FF A6 A6 A6 FF 01 01 01 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 2C 2C 2C FF E5 E5 E5 FF FF FF FF FF FD FF' - 'FF FF BE 96 7E FF C2 64 22 FF CB 74 33 FF CE 79' - '38 FF D1 7F 3E FF D5 84 43 FF D7 89 48 FF DA 8E' - '4C FF DE 92 50 FF E0 96 54 FF E2 9A 58 FF E3 9C' - '5A FF E5 9F 5C FF E6 A0 5D FF E6 A0 5D FF E5 A0' - '5D FF E4 9E 5B FF E3 9B 59 FF E1 98 56 FF DF 94' - '52 FF DC 90 4E FF D8 8B 49 FF D6 86 45 FF D3 81' - '40 FF D0 7C 3B FF CC 77 36 FF C9 70 30 FF C4 68' - '26 FF C8 76 43 FF EE EA E9 FF E1 E2 E3 FF E8 E8' - 'E8 FF A5 A5 A5 FF 00 00 00 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 2C 2C 2C FF E5 E5 E5 FF FF FF FF FF FD FF' - 'FF FF BC 94 7B FF C4 67 24 FF CD 77 37 FF D0 7C' - '3C FF D4 83 42 FF D7 88 47 FF DB 8D 4C FF DE 93' - '51 FF E1 97 56 FF E3 9C 59 FF E6 A0 5D FF E7 A3' - '60 FF E9 A6 63 FF EA A8 64 FF EA A8 64 FF E9 A7' - '64 FF E8 A5 62 FF E7 A2 5F FF E4 9E 5B FF E2 99' - '57 FF DF 95 53 FF DC 8F 4D FF D8 8A 49 FF D6 86' - '44 FF D2 80 3F FF CF 7A 3A FF CB 74 33 FF C6 6B' - '29 FF C7 76 43 FF F6 F2 F0 FF F4 F5 F6 FF F6 F6' - 'F6 FF A5 A5 A5 FF 00 00 00 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 2B 2B 2B FF E4 E4 E4 FF FF FF FF FF FD FF' - 'FF FF B7 8D 75 FF BC 5A 19 FF CF 7C 3B FF D2 80' - '3E FF D5 85 44 FF D9 8C 4A FF DD 91 4F FF E0 97' - '55 FF E3 9C 5A FF E6 A1 5E FF E9 A5 63 FF EB A9' - '66 FF ED AD 69 FF EF AF 6B FF EF AF 6C FF EE AF' - '6A FF ED AB 68 FF EA A7 65 FF E7 A3 61 FF E4 9E' - '5C FF E1 99 57 FF DE 93 52 FF DA 8E 4C FF D7 88' - '47 FF D4 82 41 FF D0 7C 3C FF CC 77 36 FF C7 6C' - '2A FF C3 6F 3D FF F7 F3 F2 FF F7 F9 FA FF FC FC' - 'FC FF A5 A5 A5 FF 00 00 00 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 2A 2A 2A FF E3 E3 E3 FF FF FF FF FF FD FF' - 'FF FF B4 88 70 FF AF 41 01 FF D0 78 34 FF D4 81' - '3E FF D8 87 43 FF DC 8D 49 FF E0 93 4F FF E3 9A' - '55 FF E7 A0 5B FF EB A5 60 FF EE AA 65 FF F1 AF' - '6A FF F3 B3 6E FF F5 B6 70 FF F5 B6 70 FF F4 B5' - '6F FF F2 B2 6C FF F0 AD 67 FF EC A7 62 FF E9 A2' - '5D FF E5 9C 58 FF E1 96 52 FF DE 90 4C FF DA 8A' - '46 FF D6 83 40 FF D2 7D 3A FF CF 78 35 FF C1 5F' - '1B FF BB 5F 2C FF F9 F5 F3 FF F9 FB FB FF FE FE' - 'FE FF A5 A5 A5 FF 00 00 00 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 2A 2A 2A FF E3 E3 E3 FF FF FF FF FF FD FF' - 'FF FF B4 95 84 FF A0 4F 23 FF B6 71 43 FF CB 89' - '53 FF CE 8D 57 FF D1 92 5B FF D4 97 61 FF D7 9D' - '66 FF DA A2 6A FF DD A7 6E FF E0 AB 73 FF E4 B0' - '78 FF E6 B4 7B FF E7 B7 7F FF E8 B7 7F FF E6 B6' - '7D FF E5 B2 7A FF E2 AE 76 FF DE A9 71 FF DC A4' - '6C FF D9 9F 68 FF D5 99 63 FF D2 94 5D FF CF 8F' - '59 FF CC 8A 54 FF CA 85 4F FF C3 7E 4A FF A7 5A' - '2C FF B3 73 4F FF FA F8 F7 FF FC FD FD FF FF FF' - 'FF FF A5 A5 A5 FF 00 00 00 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 84 00 00' - '00 FF 26 26 26 FF E1 E1 E1 FF FF FF FF FF FF FF' - 'FF FF EC EC ED FF E6 E6 E7 FF E6 E5 E6 FF E9 E7' - 'E5 FF E9 E7 E5 FF E9 E7 E5 FF E9 E7 E5 FF E9 E7' - 'E5 FF E9 E8 E6 FF E9 E8 E6 FF EA E8 E6 FF EA E8' - 'E6 FF EA E8 E6 FF EA E8 E6 FF EA E8 E6 FF EA E8' - 'E6 FF EA E8 E6 FF EA E8 E6 FF E9 E8 E6 FF E8 E7' - 'E5 FF E8 E6 E4 FF E8 E6 E4 FF E8 E6 E4 FF E8 E6' - 'E4 FF E7 E5 E3 FF E7 E5 E3 FF E6 E4 E3 FF E3 E3' - 'E3 FF E9 E9 E9 FF FC FC FC FF FD FD FD FF FF FF' - 'FF FF A4 A4 A4 FF 00 00 00 FF 00 00 00 F7 00 00' - '00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 7D 00 00' - '00 FF 0D 0D 0D FF C4 C4 C4 FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FE FE FE FF FF FF FF FF FB FB' - 'FB FF 7F 7F 7F FF 00 00 00 FF 00 00 00 F2 00 00' - '00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00' - '00 FB 00 00 00 FF 56 56 56 FF E3 E3 E3 FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FD FD FD FF AF AF' - 'AF FF 21 21 21 FF 00 00 00 FF 00 00 00 B9 00 00' - '00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 8D 00 00 00 FF 01 01 01 FF 2F 2F 2F FF 6D 6D' - '6D FF 7D 7D 7D FF 7C 7C 7C FF 7C 7C 7C FF 7C 7C' - '7C FF 7C 7C 7C FF 7C 7C 7C FF 7C 7C 7C FF 7C 7C' - '7C FF 7C 7C 7C FF 7C 7C 7C FF 7C 7C 7C FF 7C 7C' - '7C FF 7C 7C 7C FF 7C 7C 7C FF 7C 7C 7C FF 7C 7C' - '7C FF 7C 7C 7C FF 7C 7C 7C FF 7C 7C 7C FF 7C 7C' - '7C FF 7C 7C 7C FF 7C 7C 7C FF 7B 7B 7B FF 7B 7B' - '7B FF 7B 7B 7B FF 7A 7A 7A FF 7A 7A 7A FF 7A 7A' - '7A FF 7A 7A 7A FF 77 77 77 FF 57 57 57 FF 18 18' - '18 FF 00 00 00 FF 00 00 00 FF 00 00 00 57 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 19 00 00 00 B9 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' - '00 FF 00 00 00 F5 00 00 00 86 00 00 00 04 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 63 00 00 00 D0 00 00' - '00 F3 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F1 00 00 00 EF 00 00' - '00 B2 00 00 00 3D 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF F8' - '00 00 1F FF 00 00 FF F0 00 00 0F FF 00 00 FF E0' - '00 00 07 FF 00 00 FF E0 00 00 07 FF 00 00 FF E0' - '00 00 03 FF 00 00 FF C0 00 00 03 FF 00 00 FF C0' - '00 00 07 FF 00 00 FF E0 00 00 07 FF 00 00 FF E0' - '00 00 07 FF 00 00 FF E0 00 00 07 FF 00 00 FF E0' - '00 00 0F FF 00 00 FF F0 00 00 0F FF 00 00 FF F8' - '00 00 1F FF 00 00 FF F8 00 00 1F FF 00 00 FF FC' - '00 00 7F FF 00 00 FE 00 00 00 00 7F 00 00 F8 00' - '00 00 00 1F 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 E0 00 00 00 00 07 00 00 E0 00' - '00 00 00 07 00 00 F0 00 00 00 00 0F 00 00 F0 00' - '00 00 00 0F 00 00 FC 00 00 00 00 3F 00 00 28 00' + '00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 0C 0C 0C 00 14 14 14 00 16 16' + '16 00 1D 1D 1D 00 28 28 28 00 29 29 29 00 2A 2A' + '2A 00 2B 2B 2B 00 2D 2D 2D 00 39 39 39 00 3A 3A' + '3A 00 3B 3B 3B 00 3C 3C 3C 00 3E 3E 3E 00 3F 3F' + '3F 00 74 46 00 00 75 46 00 00 75 47 00 00 76 47' + '00 00 7B 4E 03 00 79 4D 04 00 7F 50 03 00 7F 51' + '04 00 40 40 40 00 41 41 41 00 42 42 42 00 44 44' + '44 00 45 45 45 00 46 46 46 00 48 48 48 00 49 49' + '49 00 4A 4A 4A 00 4B 4B 4B 00 4D 4D 4D 00 4F 4F' + '4F 00 50 50 50 00 51 51 51 00 53 53 53 00 55 55' + '55 00 56 56 56 00 57 57 57 00 58 58 58 00 59 59' + '59 00 5A 5A 5A 00 5C 5C 5C 00 5D 5D 5D 00 5E 5E' + '5E 00 6A 64 5A 00 6D 68 5F 00 7E 73 5E 00 61 61' + '61 00 62 62 62 00 61 65 61 00 64 64 64 00 65 65' + '65 00 66 66 66 00 67 67 67 00 6A 6A 6A 00 71 6C' + '65 00 75 71 69 00 71 71 71 00 72 72 72 00 74 74' + '74 00 75 75 75 00 76 76 76 00 77 77 77 00 7D 79' + '73 00 7C 7B 73 00 7B 79 75 00 79 79 79 00 7B 7B' + '7B 00 7F 7F 7F 00 80 52 04 00 80 52 05 00 81 54' + '06 00 81 55 07 00 83 57 07 00 82 57 08 00 82 59' + '0B 00 84 58 08 00 84 59 09 00 85 59 09 00 86 5A' + '09 00 86 5B 0A 00 87 5B 0A 00 84 5B 0C 00 88 5D' + '0B 00 89 5F 0C 00 8A 60 0D 00 8B 62 0E 00 8C 63' + '0F 00 8E 65 10 00 8F 66 11 00 8F 67 11 00 89 68' + '1B 00 90 69 12 00 91 6A 13 00 90 6B 16 00 95 6F' + '16 00 99 74 1A 00 9A 76 1A 00 9B 76 1B 00 9E 7B' + '1E 00 8A 70 3F 00 82 77 62 00 80 79 6C 00 8B 7F' + '6A 00 7B BA 71 00 9A 82 3B 00 A2 80 21 00 A7 87' + '26 00 A8 89 27 00 A9 8A 27 00 AD 8F 2B 00 AD 90' + '2C 00 B6 9B 34 00 B7 9D 35 00 B8 9E 36 00 BD A6' + '3C 00 9B 85 55 00 9C 96 6F 00 91 86 74 00 97 92' + '72 00 9E 91 7C 00 A2 90 60 00 A1 92 64 00 A1 96' + '6B 00 B2 A1 62 00 C4 AF 41 00 C7 B4 45 00 CB B9' + '4A 00 D0 C0 4F 00 FF 00 FF 00 81 81 81 00 86 86' + '86 00 8A 8A 8A 00 8B 8B 8B 00 8C 8C 8C 00 8D 8D' + '8D 00 92 8E 89 00 91 91 91 00 92 92 92 00 99 97' + '91 00 99 97 92 00 99 99 99 00 9A 9A 9A 00 9F 9F' + '9F 00 A7 9B 80 00 A5 9F 96 00 B2 A6 88 00 B0 A4' + '8C 00 B5 B0 87 00 A4 A4 A4 00 A8 A8 A8 00 AD AE' + 'AA 00 AD AD AD 00 AE AE AE 00 B8 B6 B4 00 BA BA' + 'BA 00 C3 B9 83 00 C1 C1 C1 00 C6 C6 C6 00 C9 C9' + 'C9 00 CF CF CF 00 D0 D0 D0 00 D2 D2 D2 00 D9 D9' + 'D9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 A7 A7 A7 A7 A7 A7 A7 A7 A7 A7' + 'A7 A7 A7 A7 A7 A7 A7 A7 29 29 29 29 40 8A 8A A7' + 'A7 A7 A7 A7 A7 A7 A7 40 1C 1F 27 29 33 33 39 39' + '27 40 29 40 46 91 A7 A7 46 02 03 07 27 1C 1F 07' + '09 40 09 1C 1C 33 A7 A7 91 02 07 A6 07 07 07 07' + '1C 85 33 28 29 46 9A 6B 95 93 96 7B 6A 31 31 69' + '1F 8A 6B 40 46 91 8F 61 61 59 50 4B 4B 50 50 67' + '1F 8A 09 09 09 46 8F 50 50 50 50 50 59 5B 61 77' + '1C 8C 09 1C 1C 46 8B 14 4B 4B 56 61 61 65 66 7C' + '09 91 1C 1C 1C 85 42 11 15 50 5C 65 6D 70 71 7D' + '1C 91 17 1F 1F 8A 3B 11 4B 56 61 6D 71 74 76 7E' + '1C 98 1C 27 27 85 3A 11 4B 59 65 6E 74 80 82 78' + '1D 99 1F 27 29 8C 30 11 15 59 65 70 74 81 83 7A' + '1D 9A 27 29 33 8C 2F 11 14 50 5E 6C 7F 9F 97 42' + '1F 9A 27 29 33 8A 42 79 94 9D A3 A5 A3 A0 A0 8A' + '40 A1 9A A1 A5 A6 A7 A7 A7 A7 A7 A7 A7 A7 A7 A7' + 'A7 A7 A7 A7 A7 A7 FF FF 00 00 C0 7F 00 00 80 00' + '00 00 C0 00 00 00 C4 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 FF FF 00 00 28 00 00 00 10 00 00 00 20 00' + '00 00 01 00 20 00 00 00 00 00 40 04 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 59 59 59 25 4F 4F 4F EB 45 45 45 CE 50 50' + '50 AC 61 61 61 8A 62 62 62 68 60 60 60 45 6D 6D' + '6D 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 57 57 57 B1 46 46 46 FE 4A 4A 4A FF 55 55' + '55 FF 57 57 57 FF 62 62 62 FF 66 66 66 FF 6A 6A' + '6A FF 63 63 63 F5 3A 3A 3A CC 75 75 75 FC 52 52' + '52 EC 5E 5E 5E CA 58 58 58 A8 71 71 71 7C 00 00' + '00 00 00 00 00 00 5F 5F 5F AB 14 14 14 FF 1D 1D' + '1D FF 28 28 28 FA 44 44 44 DC 46 46 46 FF 48 48' + '48 FF 29 29 29 FF 3B 3B 3B FF 74 74 74 FF 39 39' + '39 FF 45 45 45 FF 44 44 44 FF 62 62 62 F8 00 00' + '00 00 00 00 00 00 76 76 76 88 08 08 08 EC 15 15' + '15 E2 0B 0B 0B FD 29 29 29 FF 2A 2A 2A FF 2A 2A' + '2A FF 2D 2D 2D FF 40 40 40 FF 86 86 86 FF 61 65' + '61 FF 59 59 59 FF 5C 5C 5C FF 79 79 79 F8 A8 AA' + 'A4 CB 7B BA 71 FF B1 A4 84 ED A5 98 7B ED AE A0' + '84 DD 9D 90 7A F8 8B 7F 6A FF 82 77 62 FF 7E 73' + '5E FF 80 79 6C FF 4D 4D 4D FF 8C 8C 8C FF 7B BA' + '71 FF 71 71 71 FF 79 79 79 FF 98 98 98 F8 95 93' + '8D E8 90 6B 16 FF 91 6A 13 FF 8B 62 0E FF 84 59' + '09 FF 81 55 07 FF 81 54 06 FF 82 57 08 FF 84 59' + '09 FF 8A 70 3F FF 4B 4B 4B FF 8C 8C 8C FF 3A 3A' + '3A FF 3E 3E 3E FF 3F 3F 3F FF 73 73 73 F8 95 93' + '8D EA 82 59 0B FF 87 5B 0A FF 85 59 09 FF 84 58' + '08 FF 86 5B 0A FF 8A 60 0D FF 8E 65 10 FF 90 69' + '12 FF 9B 85 55 FF 42 42 42 FF 92 92 92 FF 3C 3C' + '3C FF 42 42 42 FF 42 42 42 FF 79 79 79 F8 8E 8A' + '85 ED 79 4D 04 FF 80 52 05 FF 83 57 07 FF 88 5D' + '0B FF 8F 67 11 FF 95 6F 16 FF 9B 76 1B FF 9E 7B' + '1E FF A2 90 60 FF 3E 3E 3E FF 99 99 99 FF 40 40' + '40 FF 45 45 45 FF 48 48 48 FF 7F 7F 7F F8 78 74' + '6E EF 75 47 00 FF 7F 50 03 FF 86 5A 09 FF 8F 66' + '11 FF 99 74 1A FF A2 80 21 FF A9 8A 27 FF AD 8F' + '2B FF A1 92 64 FF 42 42 42 FF 9F 9F 9F FF 41 41' + '41 FF 49 49 49 FF 4F 4F 4F FF 84 84 84 F8 70 6C' + '64 F0 74 46 00 FF 7F 51 04 FF 89 5F 0C FF 95 6F' + '16 FF A2 80 21 FF AD 90 2C FF B7 9D 35 FF BD A6' + '3C FF A1 96 6B FF 45 45 45 FF A4 A4 A4 FF 45 45' + '45 FF 50 50 50 FF 57 57 57 FF 88 88 88 F8 6D 68' + '60 F3 75 46 00 FF 80 52 04 FF 8B 62 0E FF 99 74' + '1A FF A7 87 26 FF B6 9B 34 FF C4 AF 41 FF CB B9' + '4A FF 9C 96 6F FF 48 48 48 FF A8 A8 A8 FF 4B 4B' + '4B FF 56 56 56 FF 5E 5E 5E FF 8C 8C 8C F8 69 64' + '5B F4 76 47 00 FF 80 52 05 FF 8C 63 0F FF 9A 76' + '1A FF A8 89 27 FF B8 9E 36 FF C7 B4 45 FF D0 C0' + '4F FF 97 92 72 FF 49 49 49 FF AD AD AD FF 51 51' + '51 FF 5C 5C 5C FF 64 64 64 FF 90 90 90 F8 66 60' + '55 F4 75 47 00 FF 7B 4E 03 FF 84 5B 0C FF 89 68' + '1B FF 9A 82 3B FF B2 A1 62 FF C3 B9 82 FA B5 AF' + '85 F6 7C 7B 73 FF 4B 4B 4B FF AE AE AE FF 53 53' + '53 FF 5E 5E 5E FF 61 61 61 FF 8A 8A 8A F8 72 70' + '6B E1 88 7B 66 D8 99 91 84 B3 B2 AF AB 90 E2 E2' + 'E2 6D FF FF FF 4A FF FF FF 23 00 00 00 00 00 00' + '00 00 0D 0D 0D 49 47 47 47 9C C7 C7 C7 E0 AA AA' + 'AA D0 C1 C1 C1 BE D8 D8 D8 AB EB EB EB 93 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + '00 00 80 7F 00 00 80 00 00 00 C0 00 00 00 C0 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 01 80 00 00 FF FF 00 00 28 00' + '00 00 20 00 00 00 40 00 00 00 01 00 04 00 00 00' + '00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 70 70 80 00 00 00 00 00' + '00 00 00 00 00 00 00 00 07 07 07 77 77 77 00 00' + '00 00 00 00 00 00 00 00 70 70 70 70 70 70 70 77' + '77 77 77 77 78 88 00 07 77 07 77 07 07 07 07 70' + '07 77 00 00 00 07 00 00 00 00 00 07 07 77 77 00' + '07 77 07 77 70 07 00 00 00 00 00 00 00 00 00 00' + '07 77 07 77 70 07 00 00 07 00 00 00 00 00 00 00' + '07 77 00 00 00 07 00 00 07 07 77 70 00 00 00 00' + '07 77 77 77 77 78 78 A8 88 88 88 88 88 77 77 77' + '07 77 7A 77 77 78 78 66 66 66 66 66 66 66 66 67' + '07 77 00 00 00 08 78 66 66 66 66 66 66 66 66 67' + '07 77 00 00 00 08 78 66 66 66 66 66 66 66 66 67' + '07 77 07 77 70 08 78 66 66 66 66 66 66 66 66 67' + '07 77 00 00 00 78 78 66 66 66 66 66 66 66 66 67' + '07 87 00 00 00 78 78 66 66 66 66 66 66 66 66 67' + '07 87 00 00 00 78 78 66 66 66 66 66 66 66 66 67' + '07 87 00 00 00 78 78 66 66 66 66 66 66 66 66 67' + '07 87 00 00 00 78 78 66 66 66 66 66 66 66 66 67' + '07 87 00 00 00 78 78 66 66 66 66 66 66 66 66 67' + '07 87 00 00 00 78 78 66 66 66 66 66 66 66 66 77' + '07 87 00 00 00 78 78 66 66 66 66 66 66 66 66 77' + '07 87 77 77 77 78 78 66 66 66 66 66 66 66 66 78' + '07 87 70 00 07 78 78 66 66 66 66 66 66 66 66 77' + '07 87 77 77 77 78 78 66 66 66 66 66 66 67 77 77' + '07 87 00 00 00 78 78 66 66 66 66 66 77 78 88 87' + '07 87 77 77 77 78 78 66 77 77 88 88 88 80 08 77' + '07 88 77 78 88 88 77 78 88 80 00 00 00 00 00 00' + '08 88 88 88 88 88 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF F0 7F FF FF F0 00 FF FF E0 00 00 00 E0 00' + '00 00 F8 08 00 00 F8 00 00 00 F8 00 00 00 F8 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 01 80 00 01 FF F8 00 FF FF' + 'FF FF FF FF FF FF 28 00 00 00 20 00 00 00 40 00' + '00 00 01 00 08 00 00 00 00 00 00 04 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00' + '00 00 0A 0A 0A 00 14 14 14 00 1C 1C 1C 00 23 23' + '23 00 25 25 25 00 29 29 29 00 2D 2D 2D 00 30 30' + '30 00 32 32 32 00 34 34 34 00 36 36 36 00 38 38' + '38 00 3A 3A 3A 00 3C 3C 3C 00 3E 3E 3E 00 78 48' + '00 00 7A 4A 00 00 7B 4C 01 00 7B 4D 02 00 7C 4D' + '02 00 7C 4E 02 00 7E 4E 02 00 7F 50 03 00 7E 50' + '04 00 7F 52 04 00 73 4F 1E 00 77 59 32 00 40 40' + '40 00 42 42 42 00 44 44 44 00 46 46 46 00 48 48' + '48 00 4A 4A 4A 00 4C 4C 4C 00 4E 4E 4E 00 50 50' + '50 00 52 52 52 00 54 54 54 00 56 56 56 00 58 58' + '58 00 5A 5A 5A 00 5C 5C 5C 00 5E 5E 5E 00 7C 65' + '47 00 7B 6B 54 00 7B 6B 59 00 60 60 60 00 62 62' + '62 00 64 64 64 00 66 66 66 00 68 68 68 00 6A 6A' + '6A 00 6C 6C 6C 00 6E 6E 6E 00 70 70 70 00 72 72' + '72 00 74 74 74 00 76 76 76 00 79 79 79 00 7C 7C' + '7C 00 7E 7E 7E 00 80 51 04 00 80 53 05 00 80 53' + '06 00 81 54 05 00 80 54 06 00 82 55 07 00 82 56' + '07 00 84 58 07 00 83 57 08 00 83 58 08 00 84 58' + '08 00 85 59 0A 00 85 5A 0A 00 86 5A 0A 00 87 5C' + '0B 00 87 5D 0C 00 89 5E 0B 00 88 5D 0C 00 88 5E' + '0C 00 8A 60 0C 00 8A 60 0E 00 8B 62 0E 00 8C 63' + '0F 00 8D 64 0E 00 8C 64 10 00 8E 66 11 00 8C 65' + '12 00 8F 67 12 00 8F 68 12 00 90 69 13 00 91 6A' + '12 00 91 6A 14 00 93 6C 15 00 93 6D 16 00 94 6E' + '16 00 95 70 17 00 96 70 16 00 96 70 18 00 97 72' + '19 00 99 74 19 00 98 74 1A 00 99 76 1B 00 9A 76' + '1B 00 9A 77 1C 00 9C 78 1B 00 9B 78 1C 00 9C 78' + '1D 00 9E 7A 1C 00 9D 7A 1E 00 9E 7B 1E 00 9F 7C' + '1E 00 8A 6B 22 00 9F 7D 20 00 8E 75 35 00 A0 7F' + '21 00 80 6E 56 00 81 71 5B 00 83 73 5C 00 82 74' + '5F 00 83 73 61 00 86 76 65 00 89 79 6A 00 8B 7C' + '6D 00 8D 7F 6F 00 7B BA 71 00 7C BA 72 00 A1 80' + '22 00 A2 81 22 00 A3 82 23 00 A6 84 22 00 A4 84' + '24 00 A7 87 26 00 A7 88 27 00 AB 8A 27 00 AA 8B' + '29 00 AA 8C 29 00 AB 8E 2B 00 AC 8E 2A 00 AE 91' + '2C 00 AF 93 2D 00 AF 93 2E 00 B1 95 2F 00 B1 96' + '2F 00 AC 93 35 00 A3 92 3E 00 A7 95 3D 00 AD 98' + '3B 00 B2 97 30 00 B3 98 30 00 B4 9A 31 00 B5 9B' + '32 00 B6 9D 33 00 B9 A0 35 00 B9 A1 36 00 BA A2' + '37 00 BD A5 39 00 BD A6 39 00 BF A8 3A 00 C0 AA' + '3C 00 C1 AC 3D 00 C2 AD 3E 00 C3 AE 3E 00 C9 B4' + '3F 00 9B 88 52 00 A4 95 47 00 B2 A4 4A 00 8B 80' + '6F 00 9B 91 60 00 8F 82 72 00 8B 81 74 00 93 87' + '79 00 95 8C 7F 00 A8 9B 70 00 C1 AF 44 00 C5 B1' + '40 00 C6 B2 41 00 C9 B5 43 00 C9 B6 43 00 CB B8' + '44 00 CD BC 47 00 CF BE 48 00 FF 00 FF 00 81 81' + '81 00 82 82 82 00 85 85 85 00 86 86 86 00 88 88' + '88 00 8A 8A 8A 00 8C 8C 8C 00 8C 8D 8E 00 8E 8E' + '8E 00 98 8E 81 00 91 8E 91 00 8D 90 93 00 8E 90' + '94 00 90 90 90 00 93 93 93 00 96 93 90 00 91 93' + '97 00 94 94 94 00 96 96 96 00 95 97 9B 00 98 98' + '98 00 99 99 9A 00 9D 9A 99 00 98 9A 9E 00 9C 9C' + '9C 00 A0 95 89 00 A0 99 90 00 A8 9E 92 00 B4 AB' + '8C 00 B0 A7 9A 00 9E 9F A0 00 9D 9E A2 00 A0 A0' + 'A0 00 A0 A1 A2 00 A2 A2 A2 00 A4 A4 A4 00 A6 A6' + 'A6 00 A8 A9 A8 00 A8 A9 AB 00 AA AA AA 00 AC AC' + 'AC 00 AE AE AE 00 B7 AF A1 00 B0 B0 AE 00 BD B6' + 'A8 00 BF BB AA 00 B0 B0 B0 00 B2 B2 B2 00 B7 B6' + 'B3 00 B4 B4 B4 00 B6 B6 B6 00 BD BB B5 00 BF BC' + 'B4 00 B8 B8 B8 00 BA BA BA 00 BC BC BC 00 BE BE' + 'BE 00 C1 BB AF 00 C1 BE B6 00 C5 C1 BA 00 C2 C0' + 'BC 00 C1 C1 C1 00 C6 C5 C5 00 C6 C6 C6 00 C7 C8' + 'CA 00 C8 C8 C8 00 CC CC CC 00 D0 D0 D0 00 D2 D2' + 'D2 00 D6 D6 D6 00 D8 DF D7 00 DC DC DC 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 29 29 30 37 BB 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 0C 1F 1F 1F 24 1F 25 29 29 36 3B BB 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 1F 1F 0C 1F 1F 24 29 25 25 29 29 30 30 30 29' + '36 36 37 29 36 29 24 29 37 BB C6 DC EE F5 00 00' + '00 25 24 24 24 24 1F 24 25 25 29 30 30 30 30 36' + '25 07 05 1F 37 29 07 36 25 25 25 0C 07 C6 00 00' + '00 00 00 05 01 02 03 05 07 1F 00 29 25 25 29 25' + '07 07 05 24 3B 29 09 1F 24 24 24 1F 09 C6 00 00' + '00 00 00 09 01 01 01 01 01 01 07 05 07 05 07 05' + '05 07 07 24 3B 30 09 09 09 07 09 09 09 C6 00 00' + '00 00 00 24 01 01 01 01 01 01 07 05 05 07 07 07' + '07 07 07 25 BB 37 25 24 1F 0C 0C 09 09 D0 00 00' + '00 00 00 36 02 BB 3B 3B 25 01 05 05 07 05 05 07' + '07 07 09 25 BB C2 7E BC BB C6 C6 D0 C6 E1 3B FE' + '7E F3 EC EB EB EB EB F3 E9 E1 DD D6 CF CB C6 C4' + 'C4 BC 24 25 BC BB 37 3B 3B BB BC C5 C6 E9 37 EC' + '6D 6D 64 62 5C 55 4E 47 41 17 16 16 16 17 17 17' + '41 2D 20 25 C6 36 0C 0C 0C 0C 1F 0C 0C E1 37 E4' + '62 5C 5C 59 55 4E 47 47 41 41 41 41 41 41 47 47' + '47 79 1F 29 C6 37 0C 0C 0C 0C 0C 1F 0C E1 37 E2' + '59 59 55 4E 47 47 47 46 41 46 46 47 47 4E 4E 4E' + '59 A8 0C 29 D0 37 0C 1F 0C 1F 09 0C 1F DD 37 D5' + '4E 47 47 47 4E 46 47 46 47 47 4E 4E 59 59 59 59' + '59 AD 0C 29 D0 3B 0C 0C 1F 1F 1F 1F 1F E1 37 D3' + '41 46 41 46 46 46 46 47 4E 59 59 59 59 5C 5F 5F' + '66 D2 07 29 DC 3B 1F 0C 1F 0C 1F 1F 1F E9 37 D1' + '16 16 17 17 46 41 47 4E 59 59 59 5C 66 66 6E 6E' + '6D D0 07 29 DC 3B 0C 1F 1F 1F 1F 1F 1F E9 37 C1' + '16 16 16 16 17 47 4E 59 59 5F 66 6E 6E 84 84 84' + '84 D0 08 30 DC 3B 1F 1F 0C 1F 1F 24 24 E9 37 AC' + '10 10 10 17 47 47 59 59 5F 6D 6E 84 86 86 86 8C' + '87 D0 09 30 E1 BB 0C 1F 24 1F 24 24 24 E9 36 AA' + '10 10 16 17 47 47 59 5C 6D 6E 86 86 8E 8E 99 99' + '86 D6 09 30 E1 BB 1F 1F 1F 24 24 24 25 EE 36 A8' + '10 10 16 17 47 59 5D 66 6E 84 86 8E 99 9C 9C 9C' + '94 D6 09 30 E1 BB 1F 1F 24 24 24 25 29 EE 36 A8' + '10 10 10 44 47 59 5C 66 84 86 8E 99 9C A1 B1 B1' + '94 D0 09 30 E9 BB 1F 24 24 25 29 29 30 EE 30 7B' + '10 10 16 17 47 5D 5C 6D 74 86 99 99 A1 B1 B4 B4' + '93 D8 0C 30 E9 BC 24 24 24 25 29 29 30 F5 30 79' + '10 10 16 46 47 5D 5F 6D 84 86 8E 9C A1 B4 B4 B6' + '93 DC 09 30 E9 C6 24 24 25 29 30 30 36 EE 30 79' + '16 10 16 41 4E 59 5F 66 84 86 99 9B A1 B4 B6 B6' + 'A6 CB 0C 30 E9 C6 24 25 29 29 30 30 30 EE 29 77' + '10 10 10 41 47 59 5C 6E 80 86 8C 99 A1 A4 AF A7' + 'A9 C5 0C 30 E9 C6 24 25 29 30 30 30 36 EE 29 75' + '10 10 10 17 47 4E 59 71 73 A5 AE D4 E5 F5 F8 EE' + 'DC 29 0C 30 E9 C6 25 29 29 30 36 30 36 EE 29 2E' + '1A 1B 2C 77 AA C6 DE EE F8 FC FC F8 F8 00 00 DC' + 'BB 29 1F 30 E9 DC 37 3B D0 DC EE F8 FC FF 3B BC' + 'D0 DC E3 ED EE 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 DC EE F8 FF FC FF FC FC FC F8 F8 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF FF FF FF FF FF FF F0 7F FF FF F0 00' + 'FF FF E0 00 00 00 E0 00 00 00 F8 08 00 00 F8 00' + '00 00 F8 00 00 00 F8 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01' + '80 00 01 FF F8 00 FF FF FF FF FF FF FF FF 28 00' '00 00 20 00 00 00 40 00 00 00 01 00 20 00 00 00' - '00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 0B 00 00 00 4B 00 00' - '00 63 00 00 00 61 00 00 00 61 00 00 00 61 00 00' - '00 61 00 00 00 61 00 00 00 61 00 00 00 61 00 00' - '00 61 00 00 00 61 00 00 00 61 00 00 00 63 00 00' - '00 3D 00 00 00 05 00 00 00 00 00 00 00 00 00 00' + '00 00 80 10 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 04 00 00 00 AF 00 00 00 FF 00 00' - '00 FF 00 00 00 FF 00 00 00 FF 01 01 01 FF 01 01' - '01 FF 01 01 01 FF 02 02 02 FF 02 02 02 FF 02 02' - '02 FF 03 03 03 FF 05 05 05 FF 03 03 03 FF 00 00' - '00 FA 00 00 00 80 00 00 00 03 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 3E 00 00 00 FD 6E 6E 6E FF B7 B7' - 'B7 FF AD AD AD FF A9 A9 A9 FF A5 A5 A5 FF A2 A2' - 'A2 FF 9F 9F 9F FF 9B 9B 9B FF 97 97 97 FF 94 94' - '94 FF 8C 8C 8C FF 83 83 83 FF 85 85 85 FF 38 38' - '38 FF 00 00 00 EA 00 00 00 27 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 67 0F 0F 0F FF D8 D8 D8 FF FF FF' - 'FF FF ED ED ED FF E2 E2 E2 FF DB DB DB FF D3 D3' - 'D3 FF CD CD CD FF C8 C8 C8 FF C2 C2 C2 FF BC BC' - 'BC FF B4 B4 B4 FF 9F 9F 9F FF A2 A2 A2 FF 7F 7F' - '7F FF 00 00 00 FD 00 00 00 47 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 72 1B 1B 1B FF E6 E6 E6 FF FF FF' - 'FF FF F6 F6 F6 FF F5 F5 F5 FF F1 F1 F1 FF EC EC' - 'EC FF E4 E4 E4 FF DC DC DC FF D4 D4 D4 FF CC CC' - 'CC FF C5 C5 C5 FF AB AB AB FF AD AD AD FF 90 90' - '90 FF 04 04 04 FF 00 00 00 50 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 56 0D 0D 0D FF D4 D4 D4 FF FF FF' - 'FF FF F7 F7 F7 FF F8 F8 F8 FF F3 F3 F3 FF F0 F0' - 'F0 FF EC EC EC FF E3 E3 E3 FF DA DA DA FF D2 D2' - 'D2 FF CA CA CA FF B0 B0 B0 FF B8 B8 B8 FF 82 82' - '82 FF 00 00 00 F8 00 00 00 32 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 1F 00 00 00 ED 94 94 94 FF FF FF' - 'FF FF FE FE FE FF FD FD FD FF FA FA FA FF F3 F3' - 'F3 FF E5 E5 E5 FF E1 E1 E1 FF E0 E0 E0 FF DA DA' - 'DA FF D1 D1 D1 FF BF BF BF FF C5 C5 C5 FF 52 52' - '52 FF 00 00 00 CD 00 00 00 0D 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 97 29 29 29 FF E4 E4' - 'E4 FF FF FF FF FF FF FF FF FF FF FF FF FF F0 F0' - 'F0 FF AB AB AB FF B4 B4 B4 FF E8 E8 E8 FF E4 E4' - 'E4 FF D5 D5 D5 FF D3 D3 D3 FF 9D 9D 9D FF 0C 0C' - '0C FF 00 00 00 6C 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 1C 00 00 00 D8 4C 4C' - '4C FF EC EC EC FF FF FF FF FF FF FF FF FF FA FA' - 'FA FF E2 E2 E2 FF E3 E3 E3 FF F0 F0 F0 FF EA EA' - 'EA FF E6 E6 E6 FF B1 B1 B1 FF 23 23 23 FF 00 00' - '00 B7 00 00 00 08 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 5A 5A 5A 11 4B 4B 4B DD 48 48 48 D4 38 38' + '38 B3 34 34 34 92 3D 3D 3D 70 47 47 47 4F 48 48' + '48 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 2B 00 00' - '00 D5 33 33 33 FF B4 B4 B4 FF F9 F9 F9 FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FB FB FB FF DC DC' - 'DC FF 89 89 89 FF 1A 1A 1A FF 00 00 00 B6 00 00' - '00 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 09 00 00 00 28 00 00' - '00 38 00 00 00 37 00 00 00 37 00 00 00 2C 00 00' - '00 50 00 00 00 CC 03 03 03 FF 31 31 31 FF 67 67' - '67 FF A2 A2 A2 FF 90 90 90 FF 59 59 59 FF 22 22' - '22 FF 00 00 00 FF 00 00 00 B4 00 00 00 41 00 00' - '00 2F 00 00 00 37 00 00 00 37 00 00 00 37 00 00' - '00 27 00 00 00 09 00 00 00 00 00 00 00 00 00 00' + '00 00 47 47 47 8F 39 39 39 FF 45 45 45 FF 46 46' + '46 FF 45 45 45 FF 50 50 50 FF 44 44 44 FF 52 52' + '52 FF 58 58 58 FD 59 59 59 EA 57 57 57 CA 54 54' + '54 A9 54 54 54 88 59 59 59 67 62 62 62 46 60 60' + '60 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 32 00 00 00 BE 00 00 00 F7 00 00' - '00 FB 00 00 00 FA 00 00 00 FA 00 00 00 FA 00 00' - '00 F3 00 00 00 F2 00 00 00 FF 00 00 00 FF 05 05' - '05 FF 79 79 79 FF 4C 4C 4C FF 00 00 00 FF 00 00' - '00 FF 00 00 00 FD 00 00 00 F0 00 00 00 F6 00 00' - '00 FA 00 00 00 FA 00 00 00 FA 00 00 00 FB 00 00' - '00 F1 00 00 00 AE 00 00 00 29 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 1B 00 00 00 DB 26 26 26 FF 94 94 94 FF AD AD' - 'AD FF AA AA AA FF A9 A9 A9 FF A7 A7 A7 FF A7 A7' - 'A7 FF A6 A6 A6 FF A4 A4 A4 FF A2 A2 A2 FF AB AB' - 'AB FF CD CD CD FF BF BF BF FF A2 A2 A2 FF 9E 9E' - '9E FF 9E 9E 9E FF 9C 9C 9C FF 9C 9C 9C FF 9B 9B' - '9B FF 9A 9A 9A FF 99 99 99 FF 9B 9B 9B FF 77 77' - '77 FF 11 11 11 FF 00 00 00 C1 00 00 00 14 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 70 70' + '70 46 43 43 43 F9 44 44 44 FF 3E 3E 3E FF 3E 3E' + '3E FF 46 46 46 FF 51 51 51 FF 5A 5A 5A FF 56 56' + '56 FF 55 55 55 FF 58 58 58 FF 5C 5C 5C FF 63 63' + '63 FF 64 64 64 FF 64 64 64 FF 5F 5F 5F FF 66 66' + '66 FE 65 65 65 E8 2E 2E 2E 89 46 46 46 D5 6E 6E' + '6E FE 5D 5D 5D FD 46 46 46 F7 4E 4E 4E DC 54 54' + '54 BB 5A 5A 5A 9B 66 66 66 7A 75 75 75 59 A3 A3' + 'A3 38 D4 D4 D4 12 00 00 00 00 00 00 00 00 54 54' + '54 85 45 45 45 DD 4A 4A 4A F4 4A 4A 4A FF 4E 4E' + '4E FF 4D 4D 4D FF 4D 4D 4D FF 4E 4E 4E FF 53 53' + '53 FF 56 56 56 FF 5B 5B 5B FF 60 60 60 FF 63 63' + '63 FF 61 61 61 FF 63 63 63 FF 69 69 69 FF 5C 5C' + '5C FF 2C 2C 2C FF 26 26 26 FF 46 46 46 FF 74 74' + '74 FF 5A 5A 5A FF 2E 2E 2E FF 6B 6B 6B FF 53 53' + '53 FF 58 58 58 FF 4F 4F 4F FF 3F 3F 3F FF 2E 2E' + '2E FF 8C 8C 8C DC 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 87 87 87 5C 2B 2B 2B FF 0B 0B' + '0B FF 14 14 14 FF 1C 1C 1C FF 23 23 23 FF 2A 2A' + '2A FF 3B 3B 3B EF 6B 6B 6B 95 52 52 52 E9 50 50' + '50 FF 55 55 55 FF 5A 5A 5A FF 50 50 50 FF 29 29' + '29 FF 29 29 29 FF 29 29 29 FF 4A 4A 4A FF 79 79' + '79 FF 5E 5E 5E FF 30 30 30 FF 48 48 48 FF 4F 4F' + '4F FF 4D 4D 4D FF 4C 4C 4C FF 43 43 43 FF 34 34' + '34 FF 91 91 91 F2 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 97 97 97 3B 38 38 38 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 09 09 09 FC 2B 2B 2B F9 28 28 28 FF 29 29' + '29 FF 2B 2B 2B FF 2A 2A 2A FF 2A 2A 2A FF 2B 2B' + '2B FF 2C 2C 2C FF 2D 2D 2D FF 4E 4E 4E FF 7E 7E' + '7E FF 62 62 62 FF 31 31 31 FF 32 32 32 FF 33 33' + '33 FF 33 33 33 FF 34 34 34 FF 35 35 35 FF 35 35' + '35 FF 94 94 94 F2 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 B3 B3 B3 1B 4F 4F 4F FE 01 01' + '01 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 02 02 02 FF 2E 2E 2E FF 28 28 28 FF 2B 2B' + '2B FF 2D 2D 2D FF 2B 2B 2B FF 2C 2C 2C FF 2E 2E' + '2E FF 2E 2E 2E FF 2F 2F 2F FF 51 51 51 FF 82 82' + '82 FF 75 75 75 FF 52 52 52 FF 4D 4D 4D FF 46 46' + '46 FF 3A 3A 3A FF 38 38 38 FF 35 35 35 FF 35 35' + '35 FF 96 96 96 F2 AA AA AA 12 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 6A 6A 6A F7 16 16' + '16 F4 52 52 52 7F 4C 4C 4C 85 53 53 53 8F 46 46' + '46 DE 0A 0A 0A FF 28 28 28 FF 25 25 25 FF 28 28' + '28 FF 2A 2A 2A FF 2A 2A 2A FF 2B 2B 2B FF 2C 2C' + '2C FF 2D 2D 2D FF 31 31 31 FF 53 53 53 FF 87 87' + '87 FF 91 8E 91 FF 7B BA 71 FF 8A 8A 8A FF 87 87' + '87 FF 90 90 90 FF 98 98 98 FF 98 98 98 FF 8F 8F' + '8F FF AE AE AE F2 68 68 68 CA D8 DF D7 FD 7B BA' + '71 FC C5 C1 BA FC C1 BE B6 FC BF BC B4 FE BF BC' + 'B5 FE BE BB B5 FD BD BB B5 FC C2 C0 BC FC B7 B6' + 'B3 FD B0 B0 AE FF A8 A9 A8 FF A0 A1 A2 FF 98 9B' + '9E FF 95 97 9B FF 91 93 97 FF 8E 90 94 FF 8D 90' + '93 FF 8C 8D 8E FF 4A 4A 4A FF 55 55 55 FF 8B 8B' + '8B FF 85 85 85 FF 74 74 74 FF 76 76 76 FF 7A 7A' + '7A FF 86 86 86 FF 8D 8D 8D FF 8E 8E 8E FF 99 99' + '99 FF B4 B4 B4 F2 5F 5F 5F CC C1 BB AF FF 9E 7A' + '1C FF 9C 78 1B FF 99 74 19 FF 96 70 16 FF 91 6A' + '12 FF 8D 64 0E FF 89 5E 0B FF 84 58 07 FF 81 54' + '05 FF 7F 50 03 FF 7E 4F 03 FF 7E 4E 02 FF 7E 4E' + '02 FF 7F 50 03 FF 80 51 04 FF 80 51 05 FF 81 53' + '06 FF 7B 6B 54 FF 49 49 49 FF 57 57 57 FF 90 90' + '90 FF 6F 6F 6F FF 38 38 38 FF 39 39 39 FF 3A 3A' + '3A FF 3B 3B 3B FF 3C 3C 3C FF 3D 3D 3D FF 3C 3C' + '3C FF A4 A4 A4 F2 61 61 61 D1 BD B6 A8 FF 95 70' + '17 FF 94 6E 16 FF 92 6B 14 FF 8F 67 12 FF 8C 64' + '10 FF 89 5F 0D FF 85 5A 0A FF 83 57 08 FF 81 54' + '07 FF 80 53 05 FF 80 53 05 FF 80 53 05 FF 81 55' + '06 FF 82 56 07 FF 83 57 08 FF 84 58 09 FF 85 5A' + '0A FF 82 74 5F FF 44 44 44 FF 58 58 58 FF 94 94' + '94 FF 71 71 71 FF 3A 3A 3A FF 3B 3B 3B FF 3C 3C' + '3C FF 3D 3D 3D FF 3E 3E 3E FF 3E 3E 3E FF 3F 3F' + '3F FF A8 A8 A8 F2 64 64 64 D6 B7 AF A1 FF 8E 66' + '11 FF 8D 64 10 FF 8B 63 0F FF 8A 60 0E FF 88 5D' + '0C FF 85 5A 0A FF 83 57 08 FF 82 56 07 FF 82 56' + '07 FF 82 56 07 FF 83 57 08 FF 84 59 09 FF 86 5A' + '0A FF 87 5C 0B FF 88 5E 0C FF 89 5F 0D FF 8A 61' + '0E FF 8B 80 6F FF 3E 3E 3E FF 59 59 59 FF 98 98' + '98 FF 74 74 74 FF 3B 3B 3B FF 3D 3D 3D FF 3D 3D' + '3D FF 3C 3C 3C FF 39 39 39 FF 3D 3D 3D FF 41 41' + '41 FF AA AA AA F2 62 62 62 D6 B0 A7 9A FF 87 5C' + '0B FF 86 5B 0B FF 85 5A 0A FF 85 59 0A FF 84 59' + '09 FF 83 57 08 FF 83 57 08 FF 83 57 08 FF 84 59' + '09 FF 85 5A 0A FF 87 5D 0C FF 89 5F 0D FF 8B 62' + '0F FF 8D 64 10 FF 8E 66 11 FF 8F 67 12 FF 90 69' + '13 FF 95 8C 7F FF 36 36 36 FF 5B 5B 5B FF 9C 9C' + '9C FF 77 77 77 FF 3D 3D 3D FF 3D 3D 3D FF 41 41' + '41 FF 45 45 45 FF 46 46 46 FF 44 44 44 FF 43 43' + '43 FF AD AD AD F2 63 63 63 D7 A8 9E 92 FF 82 55' + '07 FF 81 55 06 FF 81 55 06 FF 81 55 07 FF 82 56' + '07 FF 82 56 07 FF 83 57 08 FF 85 5A 0A FF 87 5D' + '0C FF 8A 60 0E FF 8C 64 10 FF 8F 67 12 FF 91 6A' + '14 FF 93 6D 16 FF 95 6F 17 FF 96 70 18 FF 97 73' + '19 FF A0 99 90 FF 2D 2D 2D FF 5D 5D 5D FF A0 A0' + 'A0 FF 7A 7A 7A FF 3E 3E 3E FF 3F 3F 3F FF 41 41' + '41 FF 42 42 42 FF 43 43 43 FF 44 44 44 FF 45 45' + '45 FF B3 B3 B3 F2 65 65 65 DB A0 95 89 FF 7D 4F' + '03 FF 7D 4F 03 FF 7E 50 04 FF 7E 51 04 FF 80 54' + '06 FF 82 56 07 FF 85 59 0A FF 88 5E 0C FF 8C 63' + '0F FF 8F 68 12 FF 92 6B 15 FF 95 70 17 FF 98 74' + '1A FF 9A 77 1C FF 9C 78 1D FF 9D 7A 1E FF 9F 7C' + '1E FF 9F 9B 98 FF 2D 2D 2D FF 5F 5F 5F FF A4 A4' + 'A4 FF 7C 7C 7C FF 40 40 40 FF 41 41 41 FF 43 43' + '43 FF 44 44 44 FF 45 45 45 FF 48 48 48 FF 4A 4A' + '4A FF B5 B5 B5 F2 66 66 66 DE 98 8E 81 FF 7A 4A' + '00 FF 7A 4B 01 FF 7C 4E 02 FF 7E 51 04 FF 81 55' + '06 FF 84 58 09 FF 88 5E 0C FF 8C 64 10 FF 91 6A' + '14 FF 95 6F 17 FF 99 74 1A FF 9C 79 1D FF 9F 7D' + '20 FF A1 80 22 FF A3 83 23 FF A4 84 24 FF A6 84' + '22 FF 9C 9A 9B FF 30 30 30 FF 60 60 60 FF A7 A7' + 'A7 FF 7F 7F 7F FF 41 41 41 FF 43 43 43 FF 44 44' + '44 FF 45 45 45 FF 48 48 48 FF 4B 4B 4B FF 4E 4E' + '4E FF B7 B7 B7 F2 64 64 64 DF 93 87 79 FF 78 48' + '00 FF 79 49 00 FF 7C 4D 02 FF 7E 51 04 FF 82 56' + '07 FF 86 5B 0B FF 8B 62 0F FF 90 69 13 FF 95 70' + '17 FF 9A 77 1C FF 9F 7D 20 FF A3 82 23 FF A7 87' + '26 FF AA 8B 29 FF AC 8E 2A FF AC 8F 2B FF AB 8A' + '27 FF 99 9A 9E FF 32 32 32 FF 61 61 61 FF AA AA' + 'AA FF 83 83 83 FF 42 42 42 FF 44 44 44 FF 46 46' + '46 FF 4A 4A 4A FF 4D 4D 4D FF 4F 4F 4F FF 52 52' + '52 FF B9 B9 B9 F2 62 62 62 DF 8F 82 72 FF 78 48' + '00 FF 78 48 00 FF 7B 4C 01 FF 7F 52 04 FF 83 58' + '08 FF 89 5F 0D FF 8E 66 11 FF 94 6E 16 FF 9A 76' + '1B FF 9F 7D 20 FF A4 84 24 FF AA 8C 29 FF AE 91' + '2C FF B1 95 2F FF B3 99 31 FF B4 9A 31 FF AB 8E' + '2B FF 9D 9E A2 FF 34 34 34 FF 63 63 63 FF AC AC' + 'AC FF 85 85 85 FF 44 44 44 FF 46 46 46 FF 49 49' + '49 FF 4D 4D 4D FF 50 50 50 FF 54 54 54 FF 57 57' + '57 FF BA BA BA F2 61 61 61 E2 8D 7F 6F FF 79 49' + '00 FF 78 48 00 FF 7B 4C 01 FF 80 53 05 FF 85 5A' + '0A FF 8B 62 0E FF 90 69 13 FF 97 72 19 FF 9E 7B' + '1E FF A3 83 23 FF AA 8C 29 FF AF 93 2E FF B5 9B' + '32 FF B9 A1 36 FF BD A5 39 FF BD A6 39 FF AC 93' + '35 FF 9E 9F A1 FF 35 35 35 FF 63 63 63 FF AE AE' + 'AE FF 86 86 86 FF 45 45 45 FF 49 49 49 FF 4D 4D' + '4D FF 50 50 50 FF 54 54 54 FF 57 57 57 FF 5A 5A' + '5A FF BC BC BC F2 60 60 60 E4 8B 7C 6D FF 79 49' + '00 FF 78 48 00 FF 7B 4C 01 FF 80 54 06 FF 86 5B' + '0A FF 8C 64 10 FF 93 6D 15 FF 99 76 1B FF A0 7F' + '21 FF A7 88 27 FF AE 91 2C FF B5 9B 32 FF BB A3' + '37 FF C1 AC 3D FF C5 B1 40 FF C6 B2 41 FF AD 98' + '3B FF 9C 9C 9D FF 36 36 36 FF 64 64 64 FF B0 B0' + 'B0 FF 88 88 88 FF 48 48 48 FF 4C 4C 4C FF 50 50' + '50 FF 55 55 55 FF 58 58 58 FF 5C 5C 5C FF 5F 5F' + '5F FF BC BC BC F2 5E 5E 5E E9 89 79 6A FF 79 49' + '00 FF 78 48 00 FF 7C 4D 02 FF 81 55 06 FF 87 5C' + '0B FF 8D 65 11 FF 94 6E 16 FF 9B 78 1C FF A2 81' + '22 FF AA 8C 29 FF B1 96 2F FF B9 A0 35 FF C0 AA' + '3C FF C6 B3 41 FF CB B8 44 FF CB B9 45 FF A7 95' + '3D FF 9F 9F A0 FF 36 36 36 FF 64 64 64 FF B2 B2' + 'B2 FF 8C 8C 8C FF 4B 4B 4B FF 4F 4F 4F FF 53 53' + '53 FF 57 57 57 FF 5B 5B 5B FF 5F 5F 5F FF 62 62' + '62 FF BE BE BE F2 5B 5B 5B E9 86 76 65 FF 79 49' + '00 FF 78 48 00 FF 7C 4D 02 FF 81 55 07 FF 87 5D' + '0C FF 8E 66 11 FF 95 6F 17 FF 9C 79 1D FF A3 82' + '23 FF AA 8C 29 FF B3 98 30 FF BA A2 37 FF C3 AE' + '3E FF C9 B6 43 FF CD BC 47 FF CF BE 48 FF A3 92' + '3E FF A2 A2 A2 FF 37 37 37 FF 64 64 64 FF B3 B3' + 'B3 FF 90 90 90 FF 4E 4E 4E FF 53 53 53 FF 57 57' + '57 FF 5C 5C 5C FF 60 60 60 FF 63 63 63 FF 67 67' + '67 FF BE BE BE F2 58 58 58 E9 83 73 61 FF 79 49' + '00 FF 78 48 00 FF 7B 4D 02 FF 81 55 06 FF 87 5D' + '0B FF 8E 66 11 FF 94 6F 17 FF 9C 79 1D FF A3 82' + '23 FF AA 8C 29 FF B2 97 30 FF B9 A1 36 FF C2 AD' + '3E FF C9 B5 43 FF CD BC 47 FF CF BE 48 FF A4 95' + '47 FF 99 99 9A FF 37 37 37 FF 63 63 63 FF B4 B4' + 'B4 FF 93 93 93 FF 51 51 51 FF 55 55 55 FF 5A 5A' + '5A FF 5E 5E 5E FF 61 61 61 FF 65 65 65 FF 67 67' + '67 FF BF BF BF F2 56 56 56 E9 81 71 5B FF 79 49' + '00 FF 78 48 00 FF 7A 4B 01 FF 80 53 06 FF 86 5B' + '0A FF 8C 64 10 FF 93 6C 15 FF 9A 76 1B FF A0 7F' + '21 FF A7 88 27 FF AF 93 2D FF B6 9D 33 FF BF A8' + '3A FF C9 B4 3F FF C1 AF 44 FF B2 A4 4A FF 9B 91' + '60 FF 91 91 91 FF 38 38 38 FF 64 64 64 FF B5 B5' + 'B5 FF 93 93 93 FF 51 51 51 FF 54 54 54 FF 58 58' + '58 FF 5E 5E 5E FF 62 62 62 FF 65 65 65 FF 68 68' + '68 FF BE BE BE F2 53 53 53 E9 80 6E 56 FF 79 49' + '00 FF 78 48 00 FF 79 49 00 FF 7E 51 04 FF 84 58' + '08 FF 8A 60 0C FF 8C 65 12 FF 8A 6B 22 FF 8E 75' + '35 FF 9B 88 52 FF A8 9B 70 FF B4 AB 8C FF BF BB' + 'AA FE C6 C5 C5 F8 C8 C9 CB EF BA BA BA FE A6 A6' + 'A6 FF 5E 5E 5E FF 38 38 38 FF 64 64 64 FF B5 B5' + 'B5 FF 91 91 91 FF 59 59 59 FF 59 59 59 FF 5F 5F' + '5F FF 64 64 64 FF 68 68 68 FF 68 68 68 FF 6B 6B' + '6B FF BD BD BD F2 50 50 50 E9 7B 6B 59 FF 73 4F' + '1E FF 77 59 32 FF 7C 65 47 FF 83 73 5C FF 8B 81' + '74 FF 96 93 90 FE A7 A8 AA F2 BC BC BC D4 D1 D1' + 'D1 B3 DD DD DD 90 E6 E6 E6 69 EF EF EF 43 EF EF' + 'EF 21 00 00 00 00 00 00 00 00 42 42 42 32 3E 3E' + '3E 7B 3C 3C 3C C3 3B 3B 3B FA 64 64 64 FF B4 B4' + 'B4 FF A2 A2 A2 FF 76 76 76 FF 7C 7C 7C FF 96 96' + '96 FF A7 A7 A7 FF B8 B8 B8 FF C8 C8 C8 FF D6 D6' + 'D6 FF DE DE DE EE 60 60 60 B4 87 87 87 EA 8E 8E' + '8E C6 97 97 97 A2 9F 9F 9F 7E AA AA AA 5B BB BB' + 'BB 39 BF BF BF 18 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 6F 06 06 06 FF C0 C0 C0 FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FD FF FF FF FA FD FE FF F7 FA' - 'FC FF F4 F7 F9 FF F1 F4 F6 FF EE F1 F3 FF EA EC' - 'EE FF E2 E4 E6 FF E1 E3 E5 FF E2 E5 E7 FF E1 E2' - 'E5 FF DE E0 E2 FF DA DD DF FF D8 DA DD FF D5 D8' - 'DA FF D3 D6 D8 FF D0 D3 D5 FF CE CF CF FF DD DD' - 'DD FF 81 81 81 FF 00 00 00 FF 00 00 00 4E 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 22 22 22 FF EB EB EB FF FF FF FF FF F3 EA' - 'E4 FF F0 E0 D4 FF EF E1 D6 FF EF E1 D6 FF EF E1' - 'D6 FF EE E1 D6 FF ED E1 D6 FF EC E1 D5 FF EC E0' - 'D5 FF EA DE D3 FF E9 DD D2 FF E7 DA CF FF E5 D8' - 'CD FF E2 D5 CA FF DF D2 C7 FF DD CF C4 FF DB CC' - 'C1 FF D8 C9 BE FF D8 C8 BD FF D2 CE CC FF CB CC' - 'CC FF AB AB AB FF 06 06 06 FF 00 00 00 61 00 00' + '00 00 00 00 00 00 46 46 46 12 7F 7F 7F 72 BC BC' + 'BC C6 CA CA CA C1 E0 E0 E0 AE E4 E4 E4 9C EA EA' + 'EA 8B EE EE EE 79 ED ED ED 67 F0 F0 F0 55 F3 F3' + 'F3 43 F8 F8 F8 29 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 24 24 24 FF EB EB EB FF FF FF FF FF D4 AB' - '8F FF C5 67 26 FF CD 77 39 FF D2 81 42 FF D7 89' - '4A FF DC 91 50 FF E0 97 57 FF E3 9C 5C FF E5 9F' - '5E FF E5 9F 5F FF E4 9E 5D FF E2 9A 5A FF DF 94' - '55 FF DA 8D 4E FF D6 85 47 FF D1 7D 3F FF CC 75' - '37 FF C6 6A 2C FF C5 68 2B FF D8 C3 B5 FF CE D1' - 'D3 FF A9 A9 A9 FF 06 06 06 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 22 22 22 FF EA EA EA FF FF FF FF FF D0 A5' - '89 FF BD 5A 16 FF C6 6A 2A FF CB 73 32 FF D0 7A' - '3A FF D4 82 40 FF D7 87 45 FF DA 8C 49 FF DB 8E' - '4B FF DB 8E 4C FF DA 8D 4A FF D8 89 47 FF D5 84' - '42 FF D2 7E 3C FF CD 76 35 FF C8 6E 2E FF C3 66' - '26 FF BD 5B 1A FF BB 59 1A FF D7 C0 B2 FF D1 D5' - 'D8 FF A9 A9 A9 FF 05 05 05 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 22 22 22 FF EA EA EA FF FF FF FF FF D0 A4' - '88 FF BA 54 12 FF C2 65 25 FF C7 6D 2D FF CB 73' - '34 FF CF 7A 39 FF D2 7F 3E FF D3 82 42 FF D4 84' - '43 FF D5 84 43 FF D4 83 42 FF D2 80 3F FF D0 7C' - '3B FF CD 76 36 FF C8 6F 30 FF C4 68 29 FF BF 60' - '21 FF B9 56 17 FF B8 54 17 FF D8 C1 B3 FF D4 D8' - 'DA FF A9 A9 A9 FF 05 05 05 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 21 21 21 FF E9 E9 E9 FF FF FF FF FF CD A0' - '84 FF B5 4C 0B FF BD 5D 1E FF C1 64 25 FF C5 6B' - '2A FF C9 70 30 FF CB 74 34 FF CD 77 37 FF CD 78' - '38 FF CD 78 38 FF CD 78 37 FF CC 75 35 FF CA 72' - '32 FF C7 6D 2D FF C3 67 27 FF BF 60 21 FF BB 58' - '1A FF B5 4F 10 FF B4 4D 11 FF D8 C0 B2 FF D8 DB' - 'DE FF A9 A9 A9 FF 04 04 04 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 20 20 20 FF E8 E8 E8 FF FF FF FF FF CA 9B' - '80 FF B0 44 02 FF B8 54 15 FF BC 5A 1C FF BF 60' - '21 FF C1 65 25 FF C4 68 28 FF C5 6B 2B FF C6 6C' - '2D FF C6 6D 2D FF C6 6C 2D FF C4 69 2A FF C2 66' - '26 FF C0 61 22 FF BD 5D 1E FF B9 57 19 FF B5 50' - '12 FF B0 47 08 FF B0 46 0A FF D8 C0 B2 FF DA DE' - 'E0 FF A8 A8 A8 FF 04 04 04 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 20 20 20 FF E7 E7 E7 FF FF FF FF FF C6 96' - '7B FF AA 3B 00 FF B2 4A 0D FF B4 4F 11 FF B8 55' - '17 FF BD 5E 1F FF C2 65 25 FF C4 69 2A FF C5 6B' - '2C FF C6 6C 2C FF C6 6C 2C FF C4 69 2A FF C2 65' - '26 FF BE 5E 20 FF B8 55 17 FF B3 4C 0E FF AF 46' - '08 FF AB 3F 01 FF AD 43 06 FF DA C2 B4 FF DE E2' - 'E4 FF A7 A7 A7 FF 03 03 03 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 1F 1F 1F FF E6 E6 E6 FF FF FF FF FF C2 93' - '78 FF A5 34 00 FF AB 40 03 FF B6 52 14 FF C1 64' - '26 FF C7 6E 2F FF CA 73 33 FF CC 77 36 FF CD 78' - '38 FF CD 79 38 FF CD 78 38 FF CC 77 37 FF CB 74' - '34 FF C8 6F 30 FF C4 69 2A FF BC 5C 1D FF AF 47' - '0A FF A7 3A 00 FF AC 43 06 FF DB C3 B5 FF E0 E4' - 'E7 FF A7 A7 A7 FF 03 03 03 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 1E 1E 1E FF E5 E5 E5 FF FF FF FF FF C1 90' - '76 FF A6 36 00 FF BA 59 1A FF C8 6F 2F FF CC 75' - '35 FF CF 7A 3A FF D1 7F 3E FF D3 82 41 FF D4 83' - '42 FF D4 84 43 FF D4 83 42 FF D3 82 41 FF D1 7E' - '3E FF CF 7A 39 FF CC 75 35 FF C9 70 30 FF C3 67' - '27 FF B3 4D 0E FF AC 42 06 FF DC C4 B6 FF E3 E7' - 'EA FF A7 A7 A7 FF 02 02 02 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 1D 1D 1D FF E4 E4 E4 FF FF FF FF FF BE 8E' - '73 FF B7 50 0E FF CA 72 32 FF CD 77 37 FF D1 7D' - '3D FF D4 83 43 FF D8 88 47 FF D9 8C 4B FF DB 8F' - '4D FF DB 8F 4D FF DB 8F 4D FF D9 8C 4B FF D8 88' - '47 FF D4 83 43 FF D1 7D 3D FF CD 76 36 FF C9 70' - '30 FF C4 67 27 FF B5 51 14 FF DC C3 B6 FF E6 EA' - 'EC FF A6 A6 A6 FF 01 01 01 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 1C 1C 1C FF E2 E2 E2 FF FF FF FF FF C3 96' - '7A FF C4 65 22 FF CD 76 36 FF D2 7E 3E FF D6 85' - '44 FF DA 8C 4B FF DD 92 50 FF E0 97 54 FF E1 99' - '57 FF E2 99 58 FF E1 99 57 FF E0 97 54 FF DD 92' - '50 FF DA 8C 4B FF D6 85 44 FF D2 7E 3E FF CC 76' - '36 FF C7 6D 2D FF C2 65 27 FF DE C6 B9 FF E8 EB' - 'EE FF A6 A6 A6 FF 01 01 01 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 1B 1B 1B FF E1 E1 E1 FF FF FF FF FF C5 9A' - '7E FF C8 6C 28 FF D1 7D 3C FF D6 85 44 FF DA 8D' - '4B FF DF 94 52 FF E3 9B 59 FF E6 A1 5E FF E8 A4' - '62 FF E9 A5 62 FF E8 A4 62 FF E6 A1 5E FF E3 9B' - '59 FF DF 94 52 FF DA 8D 4B FF D6 85 44 FF D0 7D' - '3C FF CA 72 31 FF C7 6D 2E FF E5 CE C1 FF F4 F8' - 'FA FF A7 A7 A7 FF 00 00 00 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 1A 1A 1A FF E0 E0 E0 FF FF FF FF FF BC 8D' - '70 FF C7 68 23 FF D4 81 3E FF D9 89 46 FF DE 92' - '4E FF E4 9A 56 FF E9 A2 5E FF ED AA 65 FF F0 AE' - '6A FF F1 B0 6B FF F0 AE 6A FF ED AA 65 FF E9 A2' - '5E FF E4 9A 56 FF DE 92 4E FF D9 89 46 FF D4 80' - '3D FF CE 76 33 FF C2 63 23 FF E9 D0 C2 FF FF FF' - 'FF FF AA AA AA FF 00 00 00 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 1A 1A 1A FF E0 E0 E0 FF FF FF FF FF B5 8D' - '76 FF B4 60 29 FF CF 88 4F FF D4 90 56 FF D9 98' - '5D FF DE A0 65 FF E3 A8 6D FF E7 AF 74 FF EB B5' - '79 FF EC B7 7B FF EB B5 79 FF E7 AF 74 FF E3 A8' - '6D FF DE A0 65 FF D8 98 5D FF D3 90 56 FF CF 87' - '4E FF C7 7C 44 FF B0 5D 2B FF E8 D3 C8 FF FF FF' - 'FF FF AB AB AB FF 00 00 00 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 84 15 15 15 FF DB DB DB FF FF FF FF FF EE EC' - 'EC FF E7 E4 E2 FF EB E7 E4 FF EB E8 E5 FF EC E8' - 'E5 FF EC E9 E6 FF EC E9 E6 FF EC EA E7 FF ED EA' - 'E7 FF ED EA E7 FF ED EA E7 FF EC EA E7 FF EC E9' - 'E6 FF EB E8 E5 FF EA E8 E5 FF EA E7 E4 FF EA E6' - 'E3 FF E8 E4 E2 FF E6 E3 E2 FF F8 F8 F7 FF FF FF' - 'FF FF A7 A7 A7 FF 00 00 00 FF 00 00 00 61 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 6C 00 00 00 FF 8E 8E 8E FF FF FF FF FF FF FF' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF E0 1F FF FF E0 00 1F FF C0 00 00 00 C0 00' + '00 00 F0 00 00 00 F0 00 00 00 F0 00 00 00 78 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 01 80 00 00 FF F0 00 FF FF' + 'FF FF FF FF FF FF 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 04 00 00 00 00 00 80 04 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00' + '00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0' + 'C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' + '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 77 77 70 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 70 70 70 70 70 77 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '07 07 77 07 77 07 77 07 77 07 77 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 70 70 70 70 70 70 70' + '70 70 70 70 77 07 77 77 77 77 77 70 00 00 00 00' + '07 07 07 07 07 77 77 77 77 77 77 77 77 00 77 77' + '00 70 00 07 77 78 00 00 70 70 70 70 70 70 70 70' + '70 70 70 77 00 00 77 77 07 77 77 77 70 78 00 00' + '00 00 00 00 07 07 77 77 77 77 77 70 00 00 77 77' + '07 77 77 77 70 78 00 00 00 00 00 00 00 00 00 07' + '00 00 07 00 00 00 77 77 00 00 00 00 00 78 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 77 77' + '00 00 00 00 00 78 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 77 77 00 00 00 00 00 78 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 77 77' + '77 77 77 77 77 78 00 00 00 00 70 78 88 77 00 00' + '00 00 00 00 00 00 77 77 77 77 77 77 77 88 78 8A' + 'A8 88 88 88 88 88 87 77 77 77 77 77 77 70 77 77' + '7A 77 78 77 78 88 78 77 77 77 77 77 77 77 77 77' + '77 77 77 77 77 70 77 77 77 77 77 77 77 78 78 76' + '66 66 66 66 66 66 66 66 66 66 66 66 67 70 77 77' + '00 00 00 00 00 78 78 76 66 66 66 66 66 66 66 66' + '66 66 66 66 67 70 77 77 00 00 00 00 00 78 78 76' + '66 66 66 66 66 66 66 66 66 66 66 66 67 70 77 87' + '00 00 00 00 00 78 78 76 66 66 66 66 66 66 66 66' + '66 66 66 66 67 70 77 87 00 00 00 00 00 78 78 76' + '66 66 66 66 66 66 66 66 66 66 66 66 68 70 77 87' + '00 77 77 77 00 78 78 76 66 66 66 66 66 66 66 66' + '66 66 66 66 68 00 77 87 00 00 00 00 00 78 78 76' + '66 66 66 66 66 66 66 66 66 66 66 66 68 00 77 87' + '00 00 00 00 00 7F 78 76 66 66 66 66 66 66 66 66' + '66 66 66 66 68 00 77 87 00 00 00 00 00 7F 78 76' + '66 66 66 66 66 66 66 66 66 66 66 66 68 00 78 87' + '00 00 00 00 00 7F 78 76 66 66 66 66 66 66 66 66' + '66 66 66 66 68 00 78 87 00 00 00 00 00 7F 78 76' + '66 66 66 66 66 66 66 66 66 66 66 66 68 00 78 87' + '00 00 00 00 00 7F 78 76 66 66 66 66 66 66 66 66' + '66 66 66 66 78 00 78 87 00 00 00 00 00 7F 78 76' + '66 66 66 66 66 66 66 66 66 66 66 66 78 00 78 87' + '00 00 00 00 00 7F 78 76 66 66 66 66 66 66 66 66' + '66 66 66 66 78 00 78 87 00 00 00 00 00 7F 78 76' + '66 66 66 66 66 66 66 66 66 66 66 66 78 00 78 87' + '00 00 00 00 00 7F 78 76 66 66 66 66 66 66 66 66' + '66 66 66 66 78 00 78 87 00 00 00 00 00 7F 78 76' + '66 66 66 66 66 66 66 66 66 66 66 67 78 00 78 87' + '00 00 00 00 00 7F 78 76 66 66 66 66 66 66 66 66' + '66 66 66 67 78 00 78 87 00 00 00 00 00 7F 78 76' + '66 66 66 66 66 66 66 66 66 66 66 67 87 00 78 87' + '00 00 00 00 00 7F 78 76 66 66 66 66 66 66 66 66' + '66 66 66 67 87 00 78 87 07 77 77 77 70 7F 78 76' + '66 66 66 66 66 66 66 66 66 66 66 67 87 00 78 87' + '07 00 00 00 70 7F 78 66 66 66 66 66 66 66 66 66' + '66 66 67 77 87 00 78 87 07 77 77 77 70 7F 78 66' + '66 66 66 66 66 66 66 67 77 77 78 88 F7 00 78 87' + '00 00 00 00 00 7F 78 66 66 66 66 66 66 67 77 78' + '88 FF 88 77 70 00 78 87 00 00 07 77 77 7F 78 66' + '66 67 77 77 78 88 88 88 80 00 08 77 70 00 78 87' + '77 77 77 88 88 8F 78 77 77 78 88 88 88 00 00 00' + '00 00 00 00 00 07 78 88 88 8F FF FF FF F8 78 88' + '88 80 00 00 00 00 00 00 00 00 00 00 00 00 88 88' + '88 88 88 80 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FC 1F' + 'FF FF FF FF 00 00 FC 00 3F FF FF FF 00 00 F8 00' + '00 3F FF FF 00 00 F8 00 00 02 00 1F 00 00 F0 00' + '00 00 00 00 00 00 F0 00 00 00 00 00 00 00 FF 00' + '00 00 00 00 00 00 FF 00 20 00 00 00 00 00 FF 00' + '00 00 00 00 00 00 FF 00 00 00 00 00 00 00 FF 00' + '00 00 00 00 00 00 FF 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '07 80 00 00 00 00 00 03 FF FE 00 00 00 00 01 FF' + 'FF FF 00 1F 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 28 00' + '00 00 30 00 00 00 60 00 00 00 01 00 08 00 00 00' + '00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 01' + '00 00 00 01 00 00 00 00 00 00 05 05 05 00 0A 0A' + '0A 00 0D 0D 0D 00 13 13 13 00 1B 1B 1B 00 21 21' + '21 00 26 26 26 00 29 29 29 00 2D 2D 2D 00 31 31' + '31 00 35 35 35 00 38 38 38 00 3A 3A 3A 00 3D 3D' + '3D 00 76 48 02 00 78 48 00 00 79 4A 00 00 7A 4A' + '00 00 7B 4C 01 00 7B 4C 02 00 7C 4E 02 00 73 4A' + '0C 00 7E 50 04 00 7F 52 05 00 7F 54 07 00 7E 55' + '0A 00 7F 57 0D 00 7D 58 11 00 7C 5B 17 00 73 50' + '1C 00 7F 60 1E 00 76 5A 2E 00 7B 5F 34 00 7F 66' + '2B 00 7D 61 37 00 7D 63 3A 00 7D 64 3E 00 41 41' + '41 00 44 44 44 00 46 46 46 00 49 49 49 00 4C 4C' + '4C 00 4E 4E 4E 00 51 51 51 00 54 54 54 00 56 56' + '56 00 58 58 58 00 5A 5A 5A 00 5D 5D 5D 00 7B 66' + '43 00 7D 65 41 00 7F 67 44 00 61 61 61 00 65 65' + '65 00 69 69 69 00 6C 6C 6C 00 6E 6E 6E 00 78 72' + '6A 00 7A 73 6A 00 71 71 71 00 75 75 75 00 78 78' + '78 00 7A 7A 7A 00 7D 7A 78 00 7C 7C 7C 00 7E 7E' + '7E 00 80 53 05 00 81 55 06 00 82 56 08 00 83 58' + '08 00 84 58 08 00 85 59 0A 00 85 5A 0A 00 86 5A' + '0A 00 86 5C 0B 00 87 5C 0C 00 88 5E 0C 00 8A 60' + '0E 00 8C 63 0F 00 8C 63 10 00 8D 65 11 00 88 63' + '15 00 8F 68 13 00 90 68 13 00 91 6A 14 00 92 6C' + '15 00 94 6E 16 00 95 70 17 00 96 71 18 00 98 73' + '19 00 99 74 1A 00 9A 77 1C 00 9B 78 1D 00 9C 79' + '1D 00 9E 7C 1F 00 86 67 23 00 81 64 24 00 9F 7D' + '20 00 85 6C 32 00 85 70 3A 00 A0 7E 20 00 81 69' + '46 00 82 6B 49 00 84 6D 4B 00 85 6F 4E 00 87 75' + '44 00 89 75 46 00 86 71 51 00 87 73 55 00 8F 7E' + '50 00 81 72 59 00 88 76 59 00 8A 78 5E 00 86 78' + '61 00 8A 7B 63 00 8B 7C 63 00 8C 7D 62 00 87 7E' + '6F 00 80 7E 7C 00 51 CB 51 00 5A CA 5A 00 A2 81' + '22 00 A4 83 24 00 A5 85 24 00 A7 88 26 00 A9 8A' + '28 00 AA 8D 29 00 AC 8E 2A 00 AD 90 2B 00 AE 92' + '2C 00 B1 95 2F 00 AE 93 30 00 A9 92 38 00 B2 96' + '30 00 B3 98 31 00 B4 99 31 00 B6 9C 33 00 B7 9D' + '34 00 B8 9F 35 00 B9 A1 36 00 BB A3 38 00 BD A5' + '39 00 BF A8 3B 00 C0 AA 3C 00 C2 AD 3D 00 C4 AF' + '3F 00 95 86 5B 00 91 83 5E 00 A4 92 44 00 A3 95' + '53 00 BC A8 40 00 91 81 63 00 94 83 63 00 95 84' + '64 00 98 86 64 00 9A 88 66 00 9D 8B 67 00 98 8C' + '66 00 91 81 68 00 95 86 6D 00 9A 8B 72 00 9D 92' + '71 00 9B 92 79 00 9E 90 78 00 A1 8F 6A 00 A3 91' + '6A 00 A5 93 6B 00 A6 94 6C 00 A8 97 6E 00 A8 9E' + '6A 00 AA 9A 6F 00 AE 9E 71 00 A3 96 7E 00 A2 98' + '79 00 C5 B1 40 00 C8 B4 42 00 CA B7 44 00 CB B8' + '45 00 CC BA 45 00 CD BC 47 00 CF BD 48 00 D0 BF' + '49 00 FF 00 FF 00 81 81 81 00 85 85 85 00 88 87' + '86 00 8F 8C 86 00 88 88 88 00 8A 8A 8A 00 8D 8D' + '8D 00 90 90 90 00 92 92 92 00 94 94 94 00 96 96' + '96 00 98 98 98 00 9A 9A 9A 00 9E 9E 9E 00 A4 9B' + '83 00 A8 9C 85 00 A6 9F 8C 00 8D BE 8D 00 AF A8' + '82 00 AC A2 8B 00 A6 A2 95 00 AA A3 94 00 AC A7' + '9D 00 B0 A6 91 00 B2 AA 96 00 B4 B0 98 00 A0 A0' + 'A0 00 A2 A2 A2 00 A4 A4 A4 00 A6 A6 A6 00 AF AC' + 'A7 00 A8 A8 A8 00 AA AA AA 00 AC AC AC 00 AE AE' + 'AE 00 B3 B2 AF 00 B9 B7 AF 00 B0 B0 B0 00 B2 B2' + 'B2 00 B6 B3 B0 00 B4 B4 B4 00 B6 B6 B6 00 B9 B9' + 'B9 00 BC BC BC 00 BE BE BE 00 C4 C1 B8 00 C0 C0' + 'C0 00 C2 C2 C2 00 C4 C4 C3 00 C4 C4 C4 00 C6 C6' + 'C6 00 C8 C8 C8 00 CB CB CB 00 CC CC CC 00 CE CE' + 'CE 00 C5 DB C5 00 D0 D0 D0 00 D2 D2 D2 00 D5 D5' + 'D5 00 D7 D7 D7 00 D9 D9 D9 00 DA DA DA 00 DC DC' + 'DC 00 E1 E1 E1 00 E3 E3 E3 00 E4 E4 E4 00 E6 E6' + 'E6 00 EB EB EB 00 ED ED ED 00 EE EE EE 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 35 2C 35 39' + 'B8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 26 26 2B 26' + '26 0D 26 26 26 31 39 B8 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 2B 26 26 2B 26' + '26 26 2C 26 31 31 31 31 2C 31 2C 2C 31 39 42 BE' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 0D 26 2B 0C 2B' + '26 2B 2C 2B 2C 2B 2C 31 31 31 35 35 35 31 35 31' + '35 39 39 42 00 42 35 35 3C 2C 2C 35 35 42 00 00' + '00 00 00 00 00 00 00 00 00 00 0D 0D 26 0C 26 0D' + '2B 2B 2B 2B 2C 2C 2C 31 2C 31 35 35 35 35 35 35' + '35 35 31 0D 07 07 2C 3C 42 2B 09 26 26 0D 0C 09' + '0D 26 31 35 B8 DA 00 00 00 00 31 31 2C 26 2B 26' + '2B 26 2B 2C 2C 2C 31 2C 31 31 31 35 35 39 35 39' + '39 31 0C 07 07 07 2C 3C 42 26 0C 2C 35 31 31 39' + '39 26 26 0C 2B D3 00 00 00 00 00 00 00 00 03 04' + '05 07 07 0C 0C 26 26 2C 3C 3C 35 35 35 39 35 35' + '31 09 07 09 07 09 31 3C B8 2B 09 26 31 35 31 31' + '31 2C 2B 0C 2B D3 00 00 00 00 00 00 00 00 01 01' + '01 01 01 01 01 01 01 07 00 2C 0C 09 0C 0C 0D 26' + '09 09 07 09 07 09 31 3C BE 2B 0C 0C 09 0C 09 0C' + '09 0C 0C 0C 26 DE 00 00 00 00 00 00 00 00 04 01' + '01 01 01 01 01 01 01 03 09 09 07 07 09 09 09 07' + '09 07 09 09 09 09 31 42 B8 2C 09 09 09 0C 09 0C' + '0C 09 0C 0C 2B DA 00 00 00 00 00 00 00 00 07 01' + '01 01 01 01 01 01 01 01 09 07 07 09 09 09 09 07' + '09 09 09 09 09 0C 2C B8 BE 2C 0C 09 0C 0C 0C 09' + '0C 0C 0C 0C 2B DE 00 00 00 00 00 00 00 00 0D 01' + '01 01 01 01 01 01 01 01 09 07 09 07 09 09 09 09' + '09 09 09 0C 09 09 35 B8 BE BE B8 B8 3C 39 35 31' + '31 2C 2C 2B 31 DE 00 00 00 00 00 00 00 00 2C 03' + '39 DA D3 C5 C1 39 07 01 09 07 07 09 09 09 09 09' + '09 0C 09 09 0C 09 35 B8 C1 BE 78 BE C1 BE B8 BE' + 'C1 C5 C5 C1 D3 E2 42 DE EF 79 C9 EE EE EB E2 E2' + 'E2 EB E2 EE EB E2 DA C5 C1 C1 BE B8 B8 B8 B8 42' + '3C 42 42 3C 2B 0C 35 BE C1 C1 C5 C1 C1 C1 BE D3' + 'C5 C5 C5 C5 D3 E2 39 DE E5 AC AC AC AB AC AC A9' + 'A9 A9 A9 9C 9C 9C 9B 9B 9B 98 9B 74 74 74 74 74' + '74 74 72 BC 2C 0C 35 BE C1 31 0D 26 2B 2C 2C 35' + '39 35 39 3C B8 EB 39 DA D0 5E 5E 5C 5C 58 57 57' + '50 4E 4B 4B 4B 43 18 18 15 15 15 18 18 43 43 18' + '18 43 18 42 2C 0C 35 BE C5 31 0D 0D 0D 0D 0D 0D' + '0D 0D 26 26 2C EE 35 DE CF 5C 59 57 57 57 53 50' + '50 4E 4B 47 43 43 43 18 18 19 18 43 18 43 47 47' + '4B 47 19 B8 2C 0C 35 C1 C5 31 0D 0D 0D 0D 0D 26' + '0D 26 0D 0D 31 EE 35 DE CB 57 57 57 50 53 50 50' + '4E 4D 4B 47 4B 43 43 43 43 43 47 4B 47 4B 47 4B' + '4B 4B 1B BE 2B 09 39 BE D3 35 0D 0D 0D 26 0D 26' + '0D 26 26 26 2C F0 35 DE C7 50 4E 50 50 4E 4E 4B' + '4B 4B 47 47 43 4B 46 4B 4B 4B 47 4B 4B 4B 4B 4B' + '4D 4E 1C C1 26 0C 39 C1 D3 35 0D 0D 0D 0D 0D 0D' + '0D 0D 26 26 31 F0 35 DA AD 4D 4D 4D 4E 4B 4B 4B' + '4B 47 47 47 4B 43 46 47 4B 4B 4B 4B 4D 50 50 50' + '50 50 1D D3 26 09 39 C1 D3 39 0D 0D 26 26 2B 26' + '26 26 26 26 31 F5 35 DA A4 4B 4B 47 43 4B 43 4B' + '43 47 47 47 43 4B 4B 4B 4B 4D 50 50 53 57 50 57' + '57 57 1F DA 0D 0C 39 C5 D3 35 26 26 0D 0D 26 26' + '26 26 26 26 31 F5 35 D3 A4 43 43 43 43 43 4B 43' + '47 47 47 47 4B 4B 4D 4E 50 50 57 53 57 57 57 59' + '58 58 61 DE 0C 09 39 C5 DA 39 0D 26 26 26 26 26' + '26 26 2B 26 31 F7 35 D3 A0 18 18 43 43 43 18 43' + '43 47 47 4B 4B 4E 50 50 57 53 57 59 5C 5C 5C 5C' + '5C 5C 22 E2 09 0C 3C C5 DA 39 0D 26 26 26 26 2B' + '26 26 26 2B 35 F7 35 C5 9B 15 15 15 15 18 18 43' + '43 4B 47 4B 4E 50 57 53 57 59 5C 5C 5C 5C 5E 5F' + '5F 5F 63 E2 0C 0C 3C C5 DA 39 26 26 26 26 26 26' + '2B 2B 2B 2B 31 F7 35 C5 74 15 10 15 15 15 18 43' + '46 4B 4B 4E 50 53 57 58 5C 5C 5C 5E 65 7A 7A 7A' + '7A 7A 64 E2 0C 09 3C D3 DE 39 26 26 26 2B 26 2B' + '26 2B 2B 2B 35 F7 35 C3 71 10 10 15 15 15 18 43' + '4B 4B 4D 4E 57 57 58 5C 5C 5E 65 7A 7A 7E 7F 7F' + '7F 7F 6A DE 0C 0C 3C D3 DE 3C 26 26 26 26 2B 26' + '2B 2B 2B 2B 35 F7 31 C1 71 15 10 10 15 15 43 43' + '4B 4B 4E 57 50 59 5C 5E 65 7A 7D 7F 7F 81 81 81' + '81 81 6E DE 0C 09 42 D3 E2 3C 26 2B 26 26 2B 2B' + '2B 2B 2B 2C 39 F7 31 C1 6C 10 10 15 15 15 43 4B' + '47 4E 50 57 58 5C 5C 65 7A 7F 7F 81 81 86 88 88' + '88 88 93 D3 0C 0C 3C DA E2 42 26 26 2B 2B 2B 2B' + '2B 2C 2C 31 39 F7 31 BF 6C 15 10 10 15 15 43 43' + '4D 4E 50 57 59 5C 65 7A 7F 7F 81 86 88 88 8D 8D' + '8D 8D 9C D3 0C 0C 42 D3 E2 3C 26 26 2B 26 2B 2B' + '2B 2C 31 2C 39 F7 31 BE 6C 10 10 10 15 15 43 4B' + '4D 50 57 57 5C 5E 7A 7E 7F 81 86 88 8D 8D 90 8F' + '8F 8F A2 C5 0C 0A 3E DA E2 42 26 2B 2B 2B 2B 2C' + '2C 31 31 31 39 F7 31 BE 69 10 15 10 15 15 43 4B' + '4D 50 57 57 5C 65 7A 7F 7F 86 88 8D 90 90 AF AF' + 'AF AF AE BF 0C 0C 3C DA E2 42 2B 26 2B 2C 2C 2C' + '31 2C 31 31 3C F7 31 BE 69 10 10 10 15 15 47 4B' + '4E 53 57 59 5E 65 7A 7F 86 88 8D 8D 90 B0 B0 B1' + 'B1 B1 C7 B8 0C 0C 42 DA E2 42 26 2C 2B 2C 2C 31' + '31 31 31 35 3C F7 31 B8 69 15 10 10 15 15 47 4B' + '4E 50 57 5C 5E 7A 7E 7F 86 88 8D 90 AF B0 B1 B1' + 'B1 B1 C8 B8 0C 0C 3C DA E2 B8 2B 2B 2C 2C 31 2C' + '31 31 35 35 3C F7 31 B8 66 10 15 10 15 15 4B 4B' + '4E 57 57 59 5E 7A 7D 7F 86 88 8D 92 92 B1 B5 B6' + 'B6 B5 CD 42 0C 0C 42 DA EB 42 2B 2C 2C 31 2C 35' + '31 35 35 39 3C FA 2C BA 34 15 10 10 15 15 47 4B' + '4D 50 57 5C 5E 7A 7D 7F 86 8B 8D 90 B0 B1 B1 B6' + 'B6 B1 CE 3E 0C 0C 3C DA E2 B8 2C 2C 2C 31 31 31' + '35 35 35 39 42 F7 2C B8 34 10 10 10 15 15 4B 4B' + '4D 53 57 5C 5E 7A 7E 7F 86 88 8D 90 AF B1 B1 B6' + 'B5 B0 D6 39 0C 0C 42 DA EB BE 2C 2C 31 2C 31 31' + '35 39 35 35 3C F7 2C B8 25 15 10 10 10 15 47 47' + '50 50 57 57 5E 65 7A 7F 81 88 8D 8F 90 B1 B1 B1' + 'B1 97 DE 35 0D 0C 3E DA EB B8 2C 2C 2C 31 31 31' + '31 35 35 35 42 F7 2C B8 25 10 10 15 10 15 18 47' + '4B 50 50 59 5C 5E 7A 7E 7F 81 85 95 96 AB CA D1' + 'DC EA FA 35 0C 0C 3E DA E2 BC 2C 2C 31 31 31 31' + '39 39 39 39 3C FA 2B 42 23 15 10 10 10 15 18 4B' + '4B 4D 50 60 63 6B 94 A3 CC DE EB F0 F7 FA F6 E1' + 'C5 B8 37 0D 0C 0D 3E DA EB B8 31 2C 31 31 35 35' + '35 35 39 39 3C FA 2B 42 21 10 10 16 1E 20 34 6F' + '76 BB C5 D7 DE EB EB EE EE EE EE 00 00 00 00 E1' + 'C1 39 26 0D 0C 0D 42 DA E2 BE 2C 31 39 42 BE C1' + 'DA DE EB F0 F5 F7 2B 3C 3B 3A 42 BC BF C1 C3 D3' + 'DA DD DE E2 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 31 3C DA EB EE F0 F5 F5 F7 FA FD' + 'FA FD FA FA FA F5 C1 42 BE C1 D3 DA DE 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 DE DE E2 EE F0 F0 EE EE EE EB' + 'EE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FC 1F FF FF FF FF 00 00 FC 00' + '3F FF FF FF 00 00 F8 00 00 3F FF FF 00 00 F8 00' + '00 02 00 FF 00 00 F0 00 00 00 00 00 00 00 F0 00' + '00 00 00 00 00 00 FF 00 00 00 00 00 00 00 FF 00' + '20 00 00 00 00 00 FF 00 00 00 00 00 00 00 FF 00' + '00 00 00 00 00 00 FF 00 00 00 00 00 00 00 FF 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 07 80 00 00 00 00 00 03' + 'FF FE 00 00 00 00 01 FF FF FF 00 1F 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 28 00 00 00 30 00 00 00 60 00' + '00 00 01 00 20 00 00 00 00 00 80 25 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 43 43 43 C2 3C 3C 3C D7 3A 3A' + '3A B6 35 35 35 94 35 35 35 73 32 32 32 51 2F 2F' + '2F 30 2D 2D 2D 11 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 48 48 48 66 3F 3F 3F FE 45 45 45 FF 4C 4C' + '4C FF 45 45 45 FF 43 43 43 FF 3A 3A 3A FF 42 42' + '42 FE 3E 3E 3E FB 3D 3D 3D EB 40 40 40 CA 43 43' + '43 A9 47 47 47 87 49 49 49 65 47 47 47 44 48 48' + '48 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 3E 3E 3E E7 41 41 41 FF 46 46 46 FF 47 47' + '47 FF 40 40 40 FF 41 41 41 FF 46 46 46 FF 52 52' + '52 FF 45 45 45 FF 56 56 56 FF 5B 5B 5B FF 5C 5C' + '5C FF 5B 5B 5B FF 59 59 59 FF 57 57 57 FF 55 55' + '55 FE 52 52 52 F6 50 50 50 DE 50 50 50 BD 53 53' + '53 9B 55 55 55 7A 56 56 56 58 53 53 53 37 4D 4D' + '4D 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 78 78 78 13 4B 4B' + '4B 99 3D 3D 3D FF 42 42 42 FF 49 49 49 FF 3A 3A' + '3A FF 4A 4A 4A FF 42 42 42 FF 4A 4A 4A FF 51 51' + '51 FF 4A 4A 4A FF 58 58 58 FF 4F 4F 4F FF 55 55' + '55 FF 5B 5B 5B FF 58 58 58 FF 61 61 61 FF 61 61' + '61 FF 63 63 63 FF 63 63 63 FF 5C 5C 5C FF 67 67' + '67 FF 5F 5F 5F FF 63 63 63 FF 6B 6B 6B FF 6B 6B' + '6B FD 6B 6B 6B CF 42 42 42 36 2A 2A 2A 73 4E 4E' + '4E D0 66 66 66 FC 6E 6E 6E FD 54 54 54 F8 4D 4D' + '4D F4 58 58 58 DA 61 61 61 B9 6D 6D 6D 98 7B 7B' + '7B 76 90 90 90 55 9C 9C 9C 34 A5 A5 A5 14 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 67 67 67 3B 3D 3D' + '3D FF 3E 3E 3E FF 45 45 45 FF 34 34 34 FF 48 48' + '48 FF 3E 3E 3E FF 48 48 48 FF 4C 4C 4C FF 4E 4E' + '4E FF 51 51 51 FF 57 57 57 FF 51 51 51 FF 57 57' + '57 FF 5C 5C 5C FF 57 57 57 FF 5F 5F 5F FF 63 63' + '63 FF 5F 5F 5F FF 62 62 62 FF 66 66 66 FF 60 60' + '60 FF 60 60 60 FF 66 66 66 FF 62 62 62 FF 63 63' + '63 FF 36 36 36 F4 25 25 25 FD 25 25 25 FF 54 54' + '54 FF 6E 6E 6E FF 7C 7C 7C FF 47 47 47 FF 2D 2D' + '2D FF 3C 3C 3C FF 44 44 44 FF 3B 3B 3B FF 33 33' + '33 FF 30 30 30 FF 3B 3B 3B FF 45 45 45 FD 4E 4E' + '4E ED 52 52 52 CF 65 65 65 AD 8C 8C 8C 57 00 00' + '00 00 00 00 00 00 00 00 00 00 51 51 51 71 4A 4A' + '4A D3 48 48 48 E2 4C 4C 4C F4 45 45 45 FF 4D 4D' + '4D FF 43 43 43 FF 4E 4E 4E FF 49 49 49 FF 4A 4A' + '4A FF 56 56 56 FF 4F 4F 4F FF 55 55 55 FF 5B 5B' + '5B FF 53 53 53 FF 57 57 57 FF 5D 5D 5D FF 5D 5D' + '5D FF 60 60 60 FF 64 64 64 FF 68 68 68 FF 67 67' + '67 FF 65 65 65 FF 69 69 69 FF 60 60 60 FF 32 32' + '32 FF 26 26 26 FF 26 26 26 FF 27 27 27 FF 56 56' + '56 FF 72 72 72 FF 7F 7F 7F FF 49 49 49 FF 2F 2F' + '2F FF 5A 5A 5A FF 60 60 60 FF 5B 5B 5B FF 5D 5D' + '5D FF 6D 6D 6D FF 68 68 68 FF 47 47 47 FF 45 45' + '45 FF 32 32 32 FF 4B 4B 4B FF A2 A2 A2 DD 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 89 89 89 1A 6C 6C 6C FD 0D 0D' + '0D FF 13 13 13 FF 1B 1B 1B FF 22 22 22 FF 29 29' + '29 FF 32 32 32 FF 35 35 35 FF 40 40 40 FF 48 48' + '48 FF 50 50 50 F3 67 67 67 D3 68 68 68 E4 61 61' + '61 F7 62 62 62 FF 65 65 65 FF 68 68 68 FF 66 66' + '66 FF 66 66 66 FF 5C 5C 5C FF 2D 2D 2D FF 29 29' + '29 FF 29 29 29 FF 29 29 29 FF 29 29 29 FF 58 58' + '58 FF 75 75 75 FF 82 82 82 FF 4C 4C 4C FF 30 30' + '30 FF 45 45 45 FF 5A 5A 5A FF 61 61 61 FF 5E 5E' + '5E FF 5D 5D 5D FF 62 62 62 FF 4F 4F 4F FF 4E 4E' + '4E FF 34 34 34 FF 4D 4D 4D FF A8 A8 A8 EB 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 78 78 78 EF 04 04' + '04 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 0D 0D 0D E2 2D 2D 2D 6A 2A 2A 2A B9 2A 2A' + '2A F8 2D 2D 2D FF 33 33 33 FF 39 39 39 FF 3E 3E' + '3E FF 40 40 40 FF 2C 2C 2C FF 2A 2A 2A FF 2B 2B' + '2B FF 2B 2B 2B FF 2B 2B 2B FF 2C 2C 2C FF 5B 5B' + '5B FF 78 78 78 FF 85 85 85 FF 4E 4E 4E FF 30 30' + '30 FF 31 31 31 FF 32 32 32 FF 32 32 32 FF 33 33' + '33 FF 33 33 33 FF 33 33 33 FF 33 33 33 FF 37 37' + '37 FF 35 35 35 FF 4E 4E 4E FF AA AA AA EB 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 7E 7E 7E D3 13 13' + '13 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 0C 0C 0C FF 2B 2B 2B FF 28 28 28 FF 28 28' + '28 FF 2A 2A 2A FF 2B 2B 2B FF 2B 2B 2B FF 2B 2B' + '2B FF 2B 2B 2B FF 2B 2B 2B FF 2C 2C 2C FF 2D 2D' + '2D FF 2D 2D 2D FF 2D 2D 2D FF 2E 2E 2E FF 5E 5E' + '5E FF 7B 7B 7B FF 89 89 89 FF 50 50 50 FF 31 31' + '31 FF 32 32 32 FF 33 33 33 FF 33 33 33 FF 34 34' + '34 FF 34 34 34 FF 35 35 35 FF 36 36 36 FF 36 36' + '36 FF 36 36 36 FF 50 50 50 FF AE AE AE EB 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 84 84 84 B3 26 26' + '26 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 06 06 06 FF 2E 2E 2E FF 29 29 29 FF 29 29' + '29 FF 2A 2A 2A FF 2D 2D 2D FF 2D 2D 2D FF 2C 2C' + '2C FF 2B 2B 2B FF 2C 2C 2C FF 2D 2D 2D FF 2E 2E' + '2E FF 2E 2E 2E FF 2F 2F 2F FF 30 30 30 FF 60 60' + '60 FF 7E 7E 7E FF 8C 8C 8C FF 53 53 53 FF 31 31' + '31 FF 32 32 32 FF 34 34 34 FF 34 34 34 FF 35 35' + '35 FF 35 35 35 FF 36 36 36 FF 36 36 36 FF 37 37' + '37 FF 37 37 37 FF 51 51 51 FF B4 B4 B4 EB 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 8C 8C 8C 93 3B 3B' + '3B FF 06 06 06 FF 03 03 03 FF 01 01 01 FF 01 01' + '01 FF 00 00 00 FF 01 01 01 FF 00 00 00 FF 00 00' + '00 FF 06 06 06 FF 2D 2D 2D FF 29 29 29 FF 29 29' + '29 FF 2B 2B 2B FF 2D 2D 2D FF 2D 2D 2D FF 2D 2D' + '2D FF 2C 2C 2C FF 2D 2D 2D FF 2F 2F 2F FF 2F 2F' + '2F FF 30 30 30 FF 30 30 30 FF 31 31 31 FF 62 62' + '62 FF 80 80 80 FF 8F 8F 8F FF 8A 8A 8A FF 88 88' + '88 FF 82 82 82 FF 78 78 78 FF 71 71 71 FF 65 65' + '65 FF 5A 5A 5A FF 5F 5F 5F FF 5A 5A 5A FF 54 54' + '54 FF 4B 4B 4B FF 5D 5D 5D FF B5 B5 B5 EB 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 94 94 94 75 51 51' + '51 FF 0E 0E 0E FF 2E 2E 2E 90 8B 8B 8B 4D 7E 7E' + '7E 57 71 71 71 63 5F 5F 5F 6D 58 58 58 C3 28 28' + '28 FF 0A 0A 0A FF 2A 2A 2A FF 29 29 29 FF 29 29' + '29 FF 2C 2C 2C FF 2E 2E 2E FF 2E 2E 2E FF 2E 2E' + '2E FF 2D 2D 2D FF 2E 2E 2E FF 30 30 30 FF 30 30' + '30 FF 31 31 31 FF 31 31 31 FF 32 32 32 FF 64 64' + '64 FF 84 84 84 FF 92 92 92 FF 8F 8F 8F FF 51 CB' + '51 FF 8A 8A 8A FF 8F 8F 8F FF 8D 8D 8D FF 88 88' + '88 FF 8F 8F 8F FF 97 97 97 FF 9B 9B 9B FF 9B 9B' + '9B FF 99 99 99 FF A2 A2 A2 FF B8 B8 B8 EB 5C 5C' + '5C A5 B4 B4 B4 F2 C5 DD C5 EC 51 CB 51 EA 87 BE' + '87 E5 D1 D1 D1 E5 CC CC CC E5 C8 C8 C8 F3 BC BC' + 'BC FF B8 B8 B8 FF C0 C0 C0 F3 C5 C5 C5 EC BE BE' + 'BE E9 C7 C7 C7 E5 C6 C6 C6 E5 BC BC BC ED AF AF' + 'AF FF 9A 9A 9A FF 9C 9C 9C FF 95 95 95 FF 8D 8D' + '8D FF 88 88 88 FF 86 86 86 FF 82 82 82 FF 80 80' + '80 FF 7B 7B 7B FF 7B 7B 7B FF 7B 7B 7B FF 7A 7A' + '7A FF 78 78 78 FF 4E 4E 4E FF 32 32 32 FF 66 66' + '66 FF 87 87 87 FF 95 95 95 FF 96 96 96 FF 99 99' + '99 FF 98 98 98 FF 96 96 96 FF 93 93 93 FF 8E 8E' + '8E FF A0 A0 A0 FF 9B 9B 9B FF 9F 9F 9F FF 9F 9F' + '9F FF A0 A0 A0 FF A7 A7 A7 FF BC BC BC EB 46 46' + '46 B2 B4 B4 B4 FF C4 C1 B8 FF AE 9E 71 FF AF 9F' + '72 FF AE 9E 71 FF AB 9B 6F FF AB 9A 6F FF AA 99' + '6F FF A8 97 6E FF A5 93 6B FF A6 94 6C FF A3 91' + '6A FF A1 8F 6A FF 9D 8B 67 FF 9A 88 66 FF 98 86' + '64 FF 95 84 64 FF 94 83 63 FF 91 81 63 FF 8F 7F' + '62 FF 8D 7E 63 FF 8C 7D 63 FF 8C 7D 63 FF 8C 7D' + '63 FF 8B 7C 63 FF 8A 7B 63 FF 8A 7B 63 FF 86 78' + '61 FF 88 87 86 FF 59 59 59 FF 32 32 32 FF 67 67' + '67 FF 89 89 89 FF 98 98 98 FF 5F 5F 5F FF 40 40' + '40 FF 43 43 43 FF 4C 4C 4C FF 53 53 53 FF 56 56' + '56 FF 65 65 65 FF 68 68 68 FF 69 69 69 FF 6F 6F' + '6F FF 74 74 74 FF 83 83 83 FF C2 C2 C2 EB 43 43' + '43 B2 B3 B3 B3 FF B2 AA 96 FF 9C 79 1D FF 9C 78' + '1D FF 9B 77 1C FF 99 75 1B FF 97 72 19 FF 94 6F' + '17 FF 92 6B 15 FF 8F 67 12 FF 8C 63 10 FF 89 60' + '0D FF 86 5B 0B FF 84 58 09 FF 82 56 07 FF 80 53' + '06 FF 7F 52 05 FF 7E 51 04 FF 7E 50 04 FF 7E 50' + '04 FF 7E 50 04 FF 7E 51 04 FF 7F 52 05 FF 80 53' + '05 FF 80 54 06 FF 81 54 06 FF 82 56 07 FF 7F 54' + '07 FF 7D 7A 78 FF 54 54 54 FF 33 33 33 FF 69 69' + '69 FF 8C 8C 8C FF 9B 9B 9B FF 5E 5E 5E FF 38 38' + '38 FF 39 39 39 FF 3A 3A 3A FF 3A 3A 3A FF 3B 3B' + '3B FF 3C 3C 3C FF 3C 3C 3C FF 3D 3D 3D FF 3D 3D' + '3D FF 3D 3D 3D FF 56 56 56 FF C9 C9 C9 EB 47 47' + '47 B9 B2 B2 B2 FF B0 A6 91 FF 98 73 19 FF 97 72' + '19 FF 96 71 18 FF 94 6F 17 FF 93 6C 15 FF 91 69' + '14 FF 8F 67 12 FF 8C 63 10 FF 8A 60 0E FF 87 5C' + '0B FF 85 59 09 FF 83 57 08 FF 82 55 07 FF 81 54' + '06 FF 80 53 05 FF 7F 52 05 FF 7F 52 05 FF 7F 52' + '05 FF 80 53 06 FF 80 54 06 FF 81 55 07 FF 82 56' + '07 FF 82 56 08 FF 83 57 08 FF 84 58 09 FF 7E 55' + '0A FF 81 81 81 FF 50 50 50 FF 33 33 33 FF 6A 6A' + '6A FF 8F 8F 8F FF 9E 9E 9E FF 5F 5F 5F FF 39 39' + '39 FF 3A 3A 3A FF 3B 3B 3B FF 3C 3C 3C FF 3C 3C' + '3C FF 3D 3D 3D FF 3D 3D 3D FF 3E 3E 3E FF 3E 3E' + '3E FF 3F 3F 3F FF 57 57 57 FF CE CE CE EB 48 48' + '48 BD B1 B1 B1 FF AC A2 8B FF 92 6C 15 FF 92 6B' + '15 FF 91 6A 14 FF 90 69 13 FF 8F 67 12 FF 8D 65' + '11 FF 8C 63 0F FF 8A 60 0E FF 88 5D 0C FF 85 5A' + '0A FF 84 58 09 FF 83 57 08 FF 82 56 07 FF 81 55' + '07 FF 81 55 07 FF 81 55 07 FF 81 55 07 FF 82 56' + '07 FF 83 57 08 FF 84 58 09 FF 84 58 09 FF 85 59' + '0A FF 85 5A 0A FF 86 5B 0B FF 87 5C 0C FF 7F 57' + '0D FF 8A 8A 8A FF 4D 4D 4D FF 32 32 32 FF 6B 6B' + '6B FF 91 91 91 FF A0 A0 A0 FF 61 61 61 FF 3B 3B' + '3B FF 3C 3C 3C FF 3D 3D 3D FF 3E 3E 3E FF 3E 3E' + '3E FF 3F 3F 3F FF 3F 3F 3F FF 40 40 40 FF 40 40' + '40 FF 40 40 40 FF 58 58 58 FF D2 D2 D2 EB 49 49' + '49 C1 AE AE AE FF A8 9C 85 FF 8E 66 11 FF 8E 65' + '11 FF 8D 65 10 FF 8C 63 10 FF 8B 62 0F FF 8A 60' + '0E FF 88 5E 0D FF 87 5C 0B FF 85 5A 0A FF 84 58' + '09 FF 83 57 08 FF 82 57 08 FF 83 57 08 FF 83 57' + '08 FF 83 57 08 FF 84 58 08 FF 84 58 09 FF 85 59' + '0A FF 86 5B 0B FF 87 5C 0B FF 88 5D 0C FF 88 5E' + '0D FF 89 5F 0D FF 8A 60 0E FF 8B 61 0F FF 7D 58' + '11 FF 96 96 96 FF 47 47 47 FF 31 31 31 FF 6D 6D' + '6D FF 94 94 94 FF A3 A3 A3 FF 63 63 63 FF 3C 3C' + '3C FF 3B 3B 3B FF 37 37 37 FF 38 38 38 FF 39 39' + '39 FF 3A 3A 3A FF 3B 3B 3B FF 3E 3E 3E FF 40 40' + '40 FF 41 41 41 FF 59 59 59 FF D7 D7 D7 EB 48 48' + '48 C1 AC AC AC FF A3 96 7E FF 89 5F 0D FF 89 5F' + '0D FF 88 5E 0D FF 88 5E 0C FF 87 5D 0C FF 87 5C' + '0B FF 86 5B 0B FF 85 59 0A FF 84 58 09 FF 83 58' + '08 FF 83 57 08 FF 83 57 08 FF 84 58 09 FF 84 59' + '09 FF 85 59 0A FF 86 5B 0B FF 87 5C 0B FF 88 5E' + '0C FF 89 5F 0D FF 8A 61 0E FF 8C 63 0F FF 8C 64' + '10 FF 8D 65 10 FF 8E 66 11 FF 8E 66 12 FF 7C 5B' + '17 FF A1 A1 A1 FF 41 41 41 FF 31 31 31 FF 6F 6F' + '6F FF 97 97 97 FF A6 A6 A6 FF 65 65 65 FF 3D 3D' + '3D FF 3C 3C 3C FF 42 42 42 FF 44 44 44 FF 46 46' + '46 FF 47 47 47 FF 45 45 45 FF 40 40 40 FF 40 40' + '40 FF 42 42 42 FF 5A 5A 5A FF DC DC DC EB 47 47' + '47 C1 A9 A9 A9 FF 9E 90 78 FF 85 5A 0A FF 85 59' + '0A FF 84 59 09 FF 84 58 09 FF 84 58 09 FF 84 58' + '09 FF 84 58 09 FF 83 57 08 FF 83 57 08 FF 83 57' + '08 FF 83 58 08 FF 84 58 09 FF 85 5A 0A FF 86 5C' + '0B FF 87 5D 0C FF 89 5F 0D FF 8A 61 0E FF 8C 63' + '10 FF 8D 64 10 FF 8F 67 12 FF 90 68 13 FF 90 69' + '13 FF 91 6A 14 FF 92 6B 15 FF 92 6C 15 FF 7F 60' + '1E FF AB AB AB FF 3B 3B 3B FF 32 32 32 FF 71 71' + '71 FF 9A 9A 9A FF A8 A8 A8 FF 67 67 67 FF 3D 3D' + '3D FF 3E 3E 3E FF 3E 3E 3E FF 3F 3F 3F FF 40 40' + '40 FF 41 41 41 FF 41 41 41 FF 42 42 42 FF 42 42' + '42 FF 43 43 43 FF 5B 5B 5B FF E0 E0 E0 EB 47 47' + '47 C3 A6 A6 A6 FF 9A 8B 72 FF 82 56 07 FF 82 56' + '07 FF 81 55 07 FF 81 55 07 FF 81 55 07 FF 82 56' + '07 FF 82 56 07 FF 82 56 07 FF 82 57 08 FF 83 57' + '08 FF 84 58 09 FF 86 5B 0B FF 87 5D 0C FF 89 5F' + '0D FF 8B 61 0F FF 8C 64 10 FF 8E 66 11 FF 90 68' + '13 FF 91 6A 14 FF 93 6D 16 FF 94 6E 16 FF 95 6F' + '17 FF 96 70 18 FF 96 71 18 FF 97 72 19 FF 81 64' + '24 FF B5 B5 B5 FF 35 35 35 FF 32 32 32 FF 72 72' + '72 FF 9C 9C 9C FF AB AB AB FF 69 69 69 FF 3F 3F' + '3F FF 40 40 40 FF 40 40 40 FF 41 41 41 FF 42 42' + '42 FF 43 43 43 FF 43 43 43 FF 45 45 45 FF 45 45' + '45 FF 46 46 46 FF 5F 5F 5F FF E4 E4 E4 EB 49 49' + '49 C8 A2 A2 A2 FF 95 86 6D FF 7F 52 05 FF 7E 51' + '04 FF 7E 51 04 FF 7F 52 05 FF 7F 52 05 FF 80 53' + '06 FF 81 54 06 FF 81 55 07 FF 83 57 08 FF 84 58' + '09 FF 86 5B 0B FF 88 5E 0C FF 8A 61 0E FF 8C 64' + '10 FF 8E 66 12 FF 90 68 13 FF 92 6B 14 FF 94 6E' + '16 FF 96 70 18 FF 98 73 19 FF 99 75 1A FF 9A 75' + '1B FF 9A 77 1C FF 9B 77 1C FF 9B 77 1C FF 7F 66' + '2B FF B9 B9 B9 FF 32 32 32 FF 33 33 33 FF 74 74' + '74 FF 9F 9F 9F FF AE AE AE FF 6B 6B 6B FF 40 40' + '40 FF 41 41 41 FF 41 41 41 FF 42 42 42 FF 44 44' + '44 FF 44 44 44 FF 45 45 45 FF 45 45 45 FF 47 47' + '47 FF 49 49 49 FF 62 62 62 FF E4 E4 E4 EB 48 48' + '48 C8 9F 9F 9F FF 91 81 68 FF 7D 4E 03 FF 7C 4D' + '02 FF 7C 4E 03 FF 7D 4F 03 FF 7E 50 04 FF 7F 52' + '05 FF 81 54 06 FF 82 56 07 FF 84 58 08 FF 86 5A' + '0A FF 88 5E 0C FF 8B 61 0F FF 8D 65 11 FF 90 68' + '13 FF 92 6B 15 FF 94 6E 17 FF 96 71 18 FF 98 74' + '1A FF 9A 77 1C FF 9C 78 1D FF 9D 7A 1E FF 9E 7C' + '1F FF 9F 7D 20 FF A0 7E 20 FF 9F 7E 20 FF 84 6C' + '33 FF B9 B9 B9 FF 33 33 33 FF 33 33 33 FF 75 75' + '75 FF A1 A1 A1 FF B0 B0 B0 FF 6D 6D 6D FF 41 41' + '41 FF 42 42 42 FF 43 43 43 FF 43 43 43 FF 44 44' + '44 FF 45 45 45 FF 46 46 46 FF 48 48 48 FF 4A 4A' + '4A FF 4B 4B 4B FF 63 63 63 FF E6 E6 E6 EB 4B 4B' + '4B CE 9D 9D 9D FF 8D 7C 63 FF 7A 4B 01 FF 7A 4B' + '00 FF 7B 4C 01 FF 7C 4E 03 FF 7E 4F 03 FF 7F 52' + '05 FF 81 55 07 FF 83 57 08 FF 85 5A 0A FF 88 5D' + '0C FF 8B 61 0E FF 8D 65 11 FF 90 69 13 FF 93 6D' + '16 FF 96 71 18 FF 98 74 1A FF 9A 77 1C FF 9D 7A' + '1E FF 9F 7D 1F FF A0 7F 21 FF A2 81 22 FF A3 83' + '23 FF A4 84 24 FF A4 84 24 FF A4 84 24 FF 85 70' + '3A FF BA BA BA FF 33 33 33 FF 33 33 33 FF 77 77' + '77 FF A3 A3 A3 FF B3 B3 B3 FF 6E 6E 6E FF 41 41' + '41 FF 43 43 43 FF 44 44 44 FF 45 45 45 FF 46 46' + '46 FF 47 47 47 FF 49 49 49 FF 4B 4B 4B FF 4D 4D' + '4D FF 4E 4E 4E FF 66 66 66 FF E4 E4 E4 EB 4A 4A' + '4A D0 99 99 99 FF 8A 78 5E FF 79 49 00 FF 79 49' + '00 FF 7A 4A 00 FF 7C 4D 02 FF 7E 4F 03 FF 7F 52' + '05 FF 82 55 07 FF 84 58 09 FF 86 5C 0B FF 89 60' + '0D FF 8D 65 10 FF 90 68 13 FF 93 6D 16 FF 96 72' + '18 FF 9A 76 1B FF 9C 79 1E FF 9F 7D 1F FF A1 80' + '21 FF A3 83 23 FF A6 86 25 FF A7 88 27 FF A9 8A' + '28 FF AA 8B 29 FF AA 8C 29 FF AA 8B 29 FF 87 75' + '44 FF B6 B6 B6 FF 33 33 33 FF 34 34 34 FF 78 78' + '78 FF A4 A4 A4 FF B5 B5 B5 FF 71 71 71 FF 41 41' + '41 FF 42 42 42 FF 44 44 44 FF 45 45 45 FF 46 46' + '46 FF 48 48 48 FF 4A 4A 4A FF 4C 4C 4C FF 4E 4E' + '4E FF 50 50 50 FF 67 67 67 FF E4 E4 E4 EB 49 49' + '49 D0 96 96 96 FF 88 76 59 FF 79 49 00 FF 79 49' + '00 FF 79 4A 00 FF 7C 4D 02 FF 7D 4F 03 FF 80 53' + '05 FF 82 56 07 FF 85 59 0A FF 88 5E 0C FF 8C 63' + '0F FF 8F 67 12 FF 93 6C 15 FF 96 71 18 FF 9A 76' + '1B FF 9D 7A 1E FF A0 7F 21 FF A3 83 23 FF A6 86' + '26 FF A9 8A 28 FF AB 8D 2A FF AD 90 2B FF AE 92' + '2C FF AF 93 2D FF AF 93 2D FF AF 92 2D FF 8F 7E' + '50 FF B0 B0 B0 FF 34 34 34 FF 35 35 35 FF 79 79' + '79 FF A6 A6 A6 FF B6 B6 B6 FF 74 74 74 FF 43 43' + '43 FF 44 44 44 FF 45 45 45 FF 47 47 47 FF 49 49' + '49 FF 4C 4C 4C FF 4E 4E 4E FF 50 50 50 FF 52 52' + '52 FF 53 53 53 FF 6A 6A 6A FF E4 E4 E4 EB 48 48' + '48 D0 93 93 93 FF 87 73 55 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7D 4F 03 FF 80 53' + '06 FF 83 57 08 FF 86 5B 0B FF 8A 60 0E FF 8D 65' + '11 FF 92 6A 14 FF 95 70 18 FF 99 75 1B FF 9D 7A' + '1E FF A0 7F 21 FF A4 83 24 FF A7 88 27 FF AB 8D' + '29 FF AD 90 2C FF AF 93 2D FF B1 96 2F FF B3 98' + '31 FF B4 9A 32 FF B4 9A 32 FF B4 99 31 FF 95 86' + '5B FF A9 A9 A9 FF 34 34 34 FF 35 35 35 FF 7A 7A' + '7A FF A7 A7 A7 FF B8 B8 B8 FF 75 75 75 FF 44 44' + '44 FF 45 45 45 FF 47 47 47 FF 49 49 49 FF 4C 4C' + '4C FF 4E 4E 4E FF 50 50 50 FF 52 52 52 FF 54 54' + '54 FF 57 57 57 FF 6C 6C 6C FF E6 E6 E6 EB 47 47' + '47 D0 90 90 90 FF 86 72 52 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7E 4F 03 FF 81 54' + '06 FF 84 58 09 FF 87 5D 0C FF 8B 62 0F FF 8F 67' + '12 FF 93 6D 16 FF 97 73 19 FF 9C 78 1D FF A0 7E' + '20 FF A3 83 23 FF A7 88 27 FF AB 8E 2A FF AF 92' + '2D FF B2 96 30 FF B4 9A 32 FF B7 9E 34 FF B9 A0' + '36 FF BA A2 37 FF BA A2 37 FF B9 A1 36 FF 98 8C' + '66 FF A4 A4 A4 FF 35 35 35 FF 36 36 36 FF 7A 7A' + '7A FF A9 A9 A9 FF BA BA BA FF 75 75 75 FF 44 44' + '44 FF 45 45 45 FF 48 48 48 FF 4B 4B 4B FF 4D 4D' + '4D FF 4F 4F 4F FF 52 52 52 FF 55 55 55 FF 56 56' + '56 FF 58 58 58 FF 6D 6D 6D FF E6 E6 E6 EB 4A 4A' + '4A D7 8E 8E 8E FF 86 70 50 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7E 50 04 FF 81 55' + '07 FF 85 59 0A FF 89 5E 0D FF 8D 64 10 FF 91 69' + '14 FF 95 70 17 FF 99 75 1B FF 9E 7C 1F FF A2 81' + '22 FF A6 87 26 FF AB 8D 2A FF AE 92 2D FF B3 97' + '30 FF B6 9D 33 FF BA A1 36 FF BD A5 39 FF BF A9' + '3B FF C1 AB 3C FF C0 AA 3C FF BF A8 3B FF 9D 92' + '71 FF 9F 9F 9F FF 35 35 35 FF 36 36 36 FF 7A 7A' + '7A FF AA AA AA FF BC BC BC FF 78 78 78 FF 47 47' + '47 FF 49 49 49 FF 4C 4C 4C FF 4E 4E 4E FF 51 51' + '51 FF 53 53 53 FF 56 56 56 FF 58 58 58 FF 5A 5A' + '5A FF 5C 5C 5C FF 6F 6F 6F FF E6 E6 E6 EB 4A 4A' + '4A D7 8C 8C 8C FF 85 6F 4E FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4B 01 FF 7E 50 04 FF 82 55' + '07 FF 85 5A 0A FF 89 60 0D FF 8E 66 11 FF 92 6B' + '15 FF 96 72 18 FF 9B 77 1C FF 9F 7D 20 FF A4 84' + '24 FF A9 8A 28 FF AD 90 2C FF B1 96 2F FF B6 9C' + '33 FF BA A2 37 FF BE A7 3A FF C2 AD 3E FF C5 B0' + '40 FF C6 B2 41 FF C6 B2 41 FF C5 B1 40 FF A2 98' + '79 FF 91 91 91 FF 36 36 36 FF 37 37 37 FF 7A 7A' + '7A FF AB AB AB FF BD BD BD FF 79 79 79 FF 48 48' + '48 FF 4B 4B 4B FF 4D 4D 4D FF 50 50 50 FF 53 53' + '53 FF 55 55 55 FF 58 58 58 FF 5A 5A 5A FF 5D 5D' + '5D FF 5F 5F 5F FF 71 71 71 FF E6 E6 E6 EB 4C 4C' + '4C DA 8A 8A 8A FF 84 6D 4B FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7E 51 04 FF 82 56' + '07 FF 86 5B 0B FF 8A 61 0E FF 8F 67 12 FF 93 6C' + '15 FF 98 73 19 FF 9C 79 1D FF A1 7F 21 FF A6 86' + '25 FF AB 8D 2A FF AF 93 2D FF B4 9A 32 FF B9 A0' + '36 FF BD A6 3A FF C3 AE 3E FF C6 B2 41 FF C9 B5' + '43 FF CA B7 44 FF CA B7 44 FF C9 B6 43 FF A4 9B' + '83 FF 89 89 89 FF 36 36 36 FF 37 37 37 FF 7B 7B' + '7B FF AD AD AD FF BE BE BE FF 7C 7C 7C FF 4A 4A' + '4A FF 4D 4D 4D FF 4F 4F 4F FF 52 52 52 FF 55 55' + '55 FF 57 57 57 FF 5A 5A 5A FF 5D 5D 5D FF 5F 5F' + '5F FF 62 62 62 FF 73 73 73 FF E7 E7 E7 EB 4B 4B' + '4B DF 88 88 88 FF 82 6B 49 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 02 FF 7F 51 04 FF 83 57' + '08 FF 86 5C 0B FF 8A 61 0E FF 8F 67 12 FF 94 6D' + '16 FF 98 74 1A FF 9D 7A 1E FF A1 80 22 FF A7 88' + '26 FF AC 8E 2A FF B1 95 2F FF B6 9D 33 FF BB A3' + '38 FF C0 AA 3C FF C5 B1 40 FF C8 B5 43 FF CB B9' + '45 FF CD BB 46 FF CC BB 46 FF CC BA 46 FF A6 9F' + '8C FF 83 83 83 FF 37 37 37 FF 37 37 37 FF 7A 7A' + '7A FF AD AD AD FF BF BF BF FF 80 80 80 FF 4C 4C' + '4C FF 4E 4E 4E FF 51 51 51 FF 54 54 54 FF 57 57' + '57 FF 59 59 59 FF 5C 5C 5C FF 5E 5E 5E FF 61 61' + '61 FF 62 62 62 FF 73 73 73 FF E7 E7 E7 EB 49 49' + '49 DF 87 87 87 FF 81 69 46 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7F 51 04 FF 83 57' + '08 FF 87 5C 0B FF 8B 62 0F FF 8F 68 13 FF 94 6E' + '16 FF 99 75 1B FF 9E 7B 1F FF A2 81 22 FF A7 88' + '27 FF AC 8F 2B FF B1 96 2F FF B7 9E 34 FF BC A4' + '38 FF C2 AC 3D FF C6 B2 41 FF CA B7 44 FF CD BC' + '47 FF CF BD 48 FF CF BE 48 FF CD BB 46 FF AA A3' + '94 FF 7C 7C 7C FF 37 37 37 FF 38 38 38 FF 7A 7A' + '7A FF AE AE AE FF C0 C0 C0 FF 83 83 83 FF 4E 4E' + '4E FF 51 51 51 FF 54 54 54 FF 57 57 57 FF 5A 5A' + '5A FF 5C 5C 5C FF 5F 5F 5F FF 62 62 62 FF 64 64' + '64 FF 67 67 67 FF 76 76 76 FF E7 E7 E7 EB 46 46' + '46 DF 85 85 85 FF 7F 67 44 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7F 51 04 FF 83 57' + '08 FF 87 5C 0B FF 8B 62 0F FF 8F 68 13 FF 94 6E' + '16 FF 99 75 1B FF 9E 7B 1F FF A2 81 22 FF A7 88' + '26 FF AC 8F 2B FF B2 96 2F FF B7 9D 34 FF BC A4' + '38 FF C2 AD 3E FF C7 B3 42 FF CB B8 45 FF CE BD' + '47 FF CF BE 48 FF D0 BF 49 FF CC BA 45 FF AC A7' + '9D FF 77 77 77 FF 37 37 37 FF 38 38 38 FF 79 79' + '79 FF AF AF AF FF C1 C1 C1 FF 85 85 85 FF 50 50' + '50 FF 52 52 52 FF 55 55 55 FF 58 58 58 FF 5B 5B' + '5B FF 5E 5E 5E FF 61 61 61 FF 64 64 64 FF 66 66' + '66 FF 69 69 69 FF 77 77 77 FF E7 E7 E7 EB 44 44' + '44 DF 83 83 83 FF 7D 65 41 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7E 51 04 FF 83 57' + '08 FF 86 5B 0B FF 8B 61 0E FF 8F 67 12 FF 93 6D' + '16 FF 98 74 1A FF 9D 7A 1E FF A2 81 22 FF A6 86' + '26 FF AB 8E 2A FF B1 95 2F FF B6 9C 33 FF BB A2' + '37 FF C1 AB 3C FF C6 B2 41 FF CA B7 44 FF CC BB' + '46 FF CF BD 48 FF CF BE 48 FF C8 B4 42 FF AF AC' + 'A7 FF 6E 6E 6E FF 37 37 37 FF 39 39 39 FF 79 79' + '79 FF AF AF AF FF C2 C2 C2 FF 87 87 87 FF 51 51' + '51 FF 54 54 54 FF 57 57 57 FF 5A 5A 5A FF 5D 5D' + '5D FF 5F 5F 5F FF 61 61 61 FF 64 64 64 FF 65 65' + '65 FF 67 67 67 FF 77 77 77 FF E7 E7 E7 EB 43 43' + '43 DF 81 81 81 FF 7D 64 3E FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7A 4B 01 FF 7E 50 04 FF 82 56' + '07 FF 86 5A 0A FF 89 60 0E FF 8E 66 11 FF 92 6C' + '15 FF 97 72 19 FF 9B 78 1D FF A0 7E 20 FF A5 85' + '24 FF A9 8B 28 FF AF 92 2D FF B4 99 31 FF B8 9F' + '35 FF BE A7 3A FF C4 AF 3F FF C8 B4 42 FF CA B7' + '44 FF CC BA 46 FF CD BC 47 FF BC A8 40 FF B6 B3' + 'B0 FF 67 67 67 FF 38 38 38 FF 39 39 39 FF 7A 7A' + '7A FF AF AF AF FF C2 C2 C2 FF 88 88 88 FF 54 54' + '54 FF 54 54 54 FF 54 54 54 FF 56 56 56 FF 5A 5A' + '5A FF 5E 5E 5E FF 61 61 61 FF 64 64 64 FF 64 64' + '64 FF 63 63 63 FF 75 75 75 FF E7 E7 E7 EB 42 42' + '42 DF 7F 7F 7F FF 7D 63 3A FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 79 4A 00 FF 7D 4E 03 FF 81 54' + '06 FF 84 59 09 FF 88 5E 0C FF 8D 64 10 FF 90 69' + '14 FF 95 70 17 FF 9A 76 1B FF 9E 7B 1F FF A3 82' + '23 FF A7 88 26 FF AC 8F 2B FF AE 93 30 FF A9 92' + '38 FF A4 92 44 FF A3 95 53 FF A8 9E 6A FF AF A8' + '82 FF B4 B0 98 FF B9 B7 AF FF C4 C4 C3 FF E6 E6' + 'E6 FF 5F 5F 5F FF 38 38 38 FF 39 39 39 FF 79 79' + '79 FF AF AF AF FF C2 C2 C2 FF 84 84 84 FF 50 50' + '50 FF 53 53 53 FF 59 59 59 FF 5C 5C 5C FF 5F 5F' + '5F FF 62 62 62 FF 66 66 66 FF 68 68 68 FF 6B 6B' + '6B FF 6E 6E 6E FF 77 77 77 FF E9 E9 E9 EB 3E 3E' + '3E DF 7E 7E 7E FF 7D 61 37 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 79 49 00 FF 7B 4D 02 FF 7F 52' + '05 FF 83 57 08 FF 86 5B 0B FF 89 60 0E FF 88 63' + '15 FF 86 67 23 FF 86 6D 32 FF 89 75 46 FF 91 83' + '5E FF 9B 92 79 FF A6 A2 95 FF B3 B2 AF FF C5 C5' + 'C5 FF D7 D7 D7 FF E4 E4 E4 FC EA EA EA EA E3 E3' + 'E3 D5 B5 B5 B5 F7 9A 9A 9A FF 81 81 81 FF 68 68' + '68 FF 3B 3B 3B FF 38 38 38 FF 3A 3A 3A FF 79 79' + '79 FF AF AF AF FF C2 C2 C2 FF 86 86 86 FF 57 57' + '57 FF 57 57 57 FF 5B 5B 5B FF 5F 5F 5F FF 63 63' + '63 FF 64 64 64 FF 66 66 66 FF 67 67 67 FF 6A 6A' + '6A FF 6C 6C 6C FF 76 76 76 FF E9 E9 E9 EB 3D 3D' + '3D DF 7C 7C 7C FF 7B 5F 34 FF 78 48 00 FF 76 48' + '02 FF 73 4A 0C FF 73 50 1C FF 76 5A 2E FF 7B 66' + '43 FF 81 72 59 FF 87 7E 6F FF 8F 8C 86 FF 9B 9A' + '9A FF A9 A9 A9 FF B6 B6 B6 FF C2 C2 C2 FB CA CA' + 'CA EA D2 D2 D2 C8 D7 D7 D7 A2 DC DC DC 76 E0 E0' + 'E0 54 E0 E0 E0 32 E2 E2 E2 12 00 00 00 00 00 00' + '00 00 43 43 43 13 3C 3C 3C 55 39 39 39 9F 39 39' + '39 E4 39 39 39 FE 39 39 39 FF 3B 3B 3B FF 79 79' + '79 FF AE AE AE FF C1 C1 C1 FF 88 88 88 FF 54 54' + '54 FF 5B 5B 5B FF 6D 6D 6D FF 79 79 79 FF 8B 8B' + '8B FF 96 96 96 FF A9 A9 A9 FF B7 B7 B7 FF C4 C4' + 'C4 FF D2 D2 D2 FF DA DA DA FF E6 E6 E6 EB 3E 3E' + '3E DE 79 79 79 FF 7A 73 6A FF 78 72 6A FF 80 7E' + '7C FF 89 89 89 FF 90 90 90 FF 95 95 95 FE 99 99' + '99 F6 9F 9F 9F DA A3 A3 A3 B8 A8 A8 A8 96 AD AD' + 'AD 74 B4 B4 B4 52 BB BB BB 31 BB BB BB 13 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 3C 3C 3C 33 3B 3B 3B 7C 3C 3C 3C C6 7A 7A' + '7A FA AD AD AD FF C1 C1 C1 FF CE CE CE FF D7 D7' + 'D7 FF DC DC DC FF D9 D9 D9 FF E4 E4 E4 FF EB EB' + 'EB FF EE EE EE FE EE EE EE FE EE EE EE FB F0 F0' + 'F0 F4 F0 F0 F0 E5 EF EF EF D3 E5 E5 E5 9E 4F 4F' + '4F 6D 78 78 78 F1 81 81 81 D6 88 88 88 B2 8C 8C' + '8C 8E 91 91 91 69 97 97 97 45 A2 A2 A2 21 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 93 93' + '93 55 B2 B2 B2 A9 BF BF BF A9 D4 D4 D4 9D DF DF' + 'DF 89 E3 E3 E3 77 E0 E0 E0 65 E6 E6 E6 53 E3 E3' + 'E3 40 E3 E3 E3 2F ED ED ED 1D 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 FC 03' + 'FF FF FF FF 00 00 F8 00 07 FF FF FF 00 00 F8 00' + '00 07 FF FF 00 00 E0 00 00 00 00 0F 00 00 E0 00' + '00 00 00 00 00 00 E0 00 00 00 00 00 00 00 FC 00' + '00 00 00 00 00 00 FE 00 00 00 00 00 00 00 FE 00' + '00 00 00 00 00 00 FE 00 00 00 00 00 00 00 FE 00' + '00 00 00 00 00 00 FE 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '01 80 00 00 00 00 00 00 FF F8 00 00 00 00 00 FF' + 'FF FF 00 1F 00 00 FF FF FF FF FF FF 00 00 FF FF' + 'FF FF FF FF 00 00 FF FF FF FF FF FF 00 00 28 00' + '00 00 40 00 00 00 80 00 00 00 01 00 04 00 00 00' + '00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' + '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' + '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' + '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' + '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 07 77 70 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 70 70 70 70 70 77' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 07 07 07 07 07 77 07' + '07 07 77 77 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 70 70 70 70 70 70' + '70 70 70 70 70 70 77 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 77 07 07 07 77 07 77' + '07 77 07 77 77 77 77 77 77 70 00 07 77 77 77 77' + '77 00 00 00 00 00 00 00 00 70 70 70 70 70 70 70' + '70 70 70 70 70 70 70 70 70 77 70 07 77 70 00 00' + '00 77 77 77 88 88 00 00 07 00 07 07 07 07 77 07' + '77 07 77 07 77 77 77 77 77 70 00 07 77 70 00 77' + '77 70 00 00 00 78 00 00 00 70 70 70 70 70 70 70' + '70 70 70 70 70 70 77 70 70 00 00 07 77 70 00 77' + '77 77 77 77 00 78 00 00 00 00 00 70 00 07 07 77' + '77 77 77 77 77 77 77 77 00 00 00 07 77 70 00 77' + '77 77 77 77 00 78 00 00 00 00 00 70 00 00 00 00' + '00 07 00 07 77 77 77 70 00 00 00 07 77 70 00 00' + '00 00 77 77 00 78 00 00 00 00 00 70 00 00 00 00' + '00 00 70 00 00 00 00 00 00 00 00 07 77 70 00 00' + '00 00 00 00 00 78 00 00 00 00 00 70 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 07 77 70 00 00' + '00 00 00 00 00 78 00 00 00 00 00 70 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 07 77 70 00 00' + '00 00 00 00 00 78 00 00 00 00 00 70 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 07 77 77 77 77' + '77 00 00 00 00 78 00 00 00 00 00 70 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 07 77 77 77 77' + '77 77 77 77 77 78 00 00 00 00 00 70 00 88 88 88' + '77 00 00 00 00 00 00 00 00 00 00 07 77 77 A7 77' + '77 77 77 77 77 88 77 88 AA 88 88 88 88 88 88 88' + '88 77 77 77 77 77 77 77 77 77 70 07 77 77 77 77' + '77 78 77 77 87 88 77 F8 88 88 88 88 88 88 88 88' + '88 88 88 88 87 77 77 77 77 77 70 07 77 77 77 77' + '77 77 88 88 88 88 77 F7 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 67 70 07 77 77 00 00' + '00 00 00 07 77 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 67 70 07 77 87 00 00' + '00 00 00 00 00 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 67 70 07 77 87 00 00' + '00 00 00 00 00 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 67 70 07 77 87 00 00' + '00 00 00 00 00 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 67 70 07 78 87 00 07' + '77 77 77 00 00 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 68 70 07 78 87 00 00' + '00 00 00 00 00 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 68 70 07 78 87 00 00' + '00 00 00 00 00 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 68 70 07 78 87 00 00' + '00 00 00 00 00 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 78 00 07 78 87 00 00' + '00 00 00 00 00 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 78 00 07 78 87 00 00' + '00 00 00 00 00 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 78 00 07 78 87 00 00' + '00 00 00 00 07 78 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 78 00 07 78 87 00 00' + '00 00 00 00 07 88 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 78 00 07 78 87 00 00' + '00 00 00 00 07 88 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 78 00 07 78 87 00 00' + '00 00 00 00 07 88 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 78 00 07 78 87 00 00' + '00 00 00 00 07 88 77 87 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 78 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 87 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 87 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 87 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 87 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 66 87 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 67 87 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 67 87 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 67 87 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 67 F7 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 67 F7 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 67 F7 00 07 88 87 00 00' + '00 00 00 00 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 67 F7 00 07 88 87 00 00' + '00 77 77 77 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 67 F7 00 07 88 87 07 77' + '77 00 00 07 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 66 66 66 67 F0 00 07 88 87 07 00' + '00 00 00 07 07 88 77 86 66 66 66 66 66 66 66 66' + '66 66 66 66 66 77 77 77 78 F0 00 07 88 87 07 00' + '00 07 77 77 07 8F 77 86 66 66 66 66 66 66 66 66' + '66 67 77 77 77 88 88 FF FF 80 00 07 88 87 07 77' + '77 70 00 00 07 8F 77 86 66 66 66 66 66 66 66 67' + '77 77 88 88 FF FF 88 77 77 00 00 07 88 87 00 00' + '00 00 07 77 77 8F 77 86 66 66 66 66 77 77 78 88' + '88 88 88 00 00 00 00 87 70 00 00 07 88 87 77 77' + '77 77 77 88 88 8F 77 86 67 77 77 77 78 88 88 80' + '00 00 00 00 00 00 00 00 00 77 70 07 88 88 88 88' + 'FF FF FF FF FF F8 77 88 88 88 88 88 88 00 00 00' + '00 00 00 00 00 00 00 00 00 00 08 77 78 FF FF FF' + 'FF FF FF F8 88 88 87 77 78 80 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 08 88 88 88' + '80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF 87 FF FF FF FF FF FF FF 00' + '0F FF FF FF FF FF FE 00 00 0F FF FF FF FF FE 00' + '00 00 3F FF FF FF FC 00 00 00 01 E0 03 FF F8 00' + '00 00 00 00 00 00 F8 00 00 00 00 00 00 00 F8 00' + '00 00 00 00 00 00 FF C0 00 00 00 00 00 00 FF C0' + '00 E0 00 00 00 00 FF C0 00 00 00 00 00 00 FF C0' + '00 00 00 00 00 00 FF C0 00 00 00 00 00 00 FF C0' + '00 00 00 00 00 00 FF C0 00 00 00 00 00 00 FF C0' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 3F C0 00 00 00 00 00 1F FF FC 00 00 00 00 03' + 'FF FF FF 80 00 00 01 FF FF FF FF F8 07 FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF 28 00 00 00 40 00 00 00 80 00' + '00 00 01 00 08 00 00 00 00 00 00 10 00 00 00 00' + '00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00' + '00 00 05 05 05 00 09 09 09 00 0E 0E 0E 00 11 11' + '11 00 16 16 16 00 19 19 19 00 1E 1E 1E 00 25 25' + '25 00 29 29 29 00 2D 2D 2D 00 31 31 31 00 35 35' + '35 00 39 39 39 00 3D 3D 3D 00 75 47 02 00 79 49' + '00 00 7B 4C 01 00 7C 4E 02 00 73 4C 0F 00 7E 50' + '03 00 7E 51 04 00 6D 4E 1A 00 6A 4F 1F 00 77 51' + '18 00 74 53 1F 00 77 54 1E 00 78 53 1B 00 6B 51' + '24 00 6C 53 29 00 6B 54 2D 00 77 57 26 00 78 56' + '21 00 7F 5E 22 00 7E 5E 26 00 75 5A 2F 00 78 58' + '29 00 7E 5F 29 00 7A 5B 2C 00 7B 5D 2F 00 67 54' + '32 00 6E 56 33 00 6B 5A 3B 00 70 58 35 00 7D 5E' + '32 00 70 5A 38 00 7E 60 2C 00 7E 61 30 00 7E 61' + '35 00 7E 62 39 00 7E 64 3D 00 41 41 41 00 45 45' + '45 00 49 49 49 00 4D 4D 4D 00 51 51 51 00 55 55' + '55 00 59 59 59 00 5D 5D 5D 00 77 64 43 00 72 61' + '45 00 7E 66 41 00 7E 6C 40 00 7E 68 45 00 77 69' + '50 00 7B 6D 56 00 75 6A 5A 00 7A 70 5C 00 61 61' + '61 00 65 65 65 00 69 69 69 00 6D 6D 6D 00 79 74' + '6D 00 71 71 71 00 75 75 75 00 78 78 78 00 7A 7A' + '7A 00 7D 7D 7D 00 80 53 05 00 81 55 06 00 82 56' + '08 00 84 57 08 00 83 58 08 00 84 59 09 00 86 5C' + '0B 00 87 5C 0C 00 88 5E 0C 00 83 5F 15 00 8A 61' + '0E 00 8C 63 0F 00 8C 63 10 00 8D 65 10 00 8F 68' + '13 00 90 68 13 00 91 6A 14 00 92 6C 15 00 94 6E' + '16 00 95 70 17 00 96 71 18 00 98 73 19 00 99 75' + '1A 00 9A 77 1C 00 9B 78 1C 00 9C 79 1D 00 9E 7C' + '1F 00 80 5F 21 00 82 61 21 00 84 62 20 00 86 65' + '21 00 88 67 20 00 89 69 21 00 8D 6C 22 00 8F 6F' + '24 00 80 62 2E 00 90 71 25 00 93 74 26 00 94 76' + '27 00 9F 7D 20 00 95 77 28 00 97 79 29 00 98 7A' + '2A 00 80 62 30 00 80 67 31 00 88 78 3D 00 8C 7B' + '3D 00 8F 7F 3C 00 A0 7E 20 00 81 6B 49 00 83 6E' + '4C 00 86 76 41 00 84 77 49 00 86 71 4F 00 82 74' + '53 00 89 75 54 00 85 78 51 00 86 79 56 00 8C 79' + '58 00 8F 7C 5B 00 91 7F 5F 00 80 77 68 00 81 79' + '6C 00 78 B0 78 00 78 B6 78 00 9C 82 2F 00 9F 85' + '2D 00 9C 85 32 00 9C 86 35 00 96 83 39 00 96 82' + '3C 00 9B 86 38 00 A2 81 22 00 A0 82 27 00 A4 83' + '24 00 A3 84 26 00 A5 85 24 00 A7 88 26 00 A8 88' + '27 00 A1 84 28 00 A8 89 28 00 AA 8C 29 00 AC 8E' + '2A 00 AD 90 2B 00 AE 91 2C 00 B0 93 2E 00 B0 94' + '2E 00 B2 96 30 00 B3 98 30 00 B4 99 31 00 B6 9C' + '33 00 B7 9D 34 00 B8 9F 35 00 B9 A1 35 00 BC A5' + '38 00 BF A9 3B 00 C0 A9 3B 00 C0 AA 3C 00 C2 AC' + '3D 00 C4 AF 3F 00 C4 B0 3F 00 92 82 48 00 93 87' + '59 00 9F 95 5C 00 A7 9A 50 00 BB A9 44 00 B2 A2' + '4B 00 89 80 6A 00 96 85 64 00 99 89 6A 00 9C 8D' + '6F 00 9E 97 6D 00 88 80 73 00 8E 87 7E 00 97 8F' + '70 00 9F 91 74 00 A0 94 79 00 A3 98 7D 00 C1 AC' + '40 00 C5 B1 40 00 C8 B5 42 00 CA B7 44 00 CB B8' + '45 00 CC BA 45 00 CD BC 47 00 CE BD 48 00 D0 BF' + '49 00 D0 C0 49 00 FF 00 FF 00 81 81 81 00 84 84' + '84 00 86 86 86 00 89 86 82 00 89 89 89 00 8D 8D' + '8D 00 91 8D 84 00 93 90 8A 00 90 90 90 00 92 92' + '92 00 95 95 95 00 99 99 99 00 9C 9C 9C 00 9E 9E' + '9E 00 A1 9C 80 00 A3 9C 87 00 A0 9B 8A 00 A2 A0' + '9D 00 A1 A1 A1 00 A4 A4 A3 00 A5 A5 A5 00 A8 A6' + 'A2 00 A9 A8 A0 00 A8 A8 A8 00 AA AA AA 00 AC AC' + 'AC 00 AE AE AE 00 B1 B1 B1 00 B5 B5 B5 00 B8 B8' + 'B7 00 B9 B9 B9 00 BD BD BD 00 8B DC 8B 00 88 E0' + '88 00 C1 C1 C1 00 C5 C5 C5 00 C9 C9 C9 00 CD CD' + 'CD 00 C2 D2 C2 00 D2 D2 D2 00 D5 D5 D5 00 D9 D9' + 'D9 00 DE DE DE 00 C9 E0 C9 00 E1 E1 E1 00 E5 E5' + 'E5 00 E9 E9 E9 00 EE EE EE 00 F1 F1 F1 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 35 38 46 4D 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 38 35 39 38 33 0C 0C 0C 33 38' + '46 4D 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 39 0C 0C 0C 0C 33 38 33 35 38 38' + '0C 33 33 33 35 35 38 46 4D D3 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 33 33 33 38 38 33 0C 33 33 35 35' + '33 39 44 44 46 44 44 39 39 38 38 35 38 39 44 4D' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 35 33 33 35 33 33 33 33 35 33 38 35' + '38 39 38 39 35 44 38 39 39 44 44 46 44 44 46 44' + '39 39 46 46 46 00 00 00 00 44 44 46 46 38 39 39' + '44 44 4B D3 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 46 33 33 33 33 0C 33 33 33 35 35 35 39' + '35 39 35 44 38 39 38 44 39 46 39 46 44 44 39 44' + '44 44 44 44 46 39 35 09 08 44 46 4D 4D 0C 0C 09' + '0C 09 0C 33 33 38 44 4B D3 DA E7 EC ED ED 00 00' + '00 00 00 33 33 0C 33 09 33 33 33 38 33 39 35 38' + '38 38 38 38 38 39 38 44 39 44 44 44 39 46 46 44' + '44 4B 44 46 33 09 08 08 08 44 46 4D 4D 0C 09 0C' + '44 39 38 35 33 33 33 09 0C 0C 09 33 4B E6 00 00' + '00 00 00 44 39 38 38 39 33 39 33 35 35 35 35 35' + '38 35 38 38 39 38 39 39 44 44 39 44 44 44 46 44' + '46 46 46 33 09 08 08 08 08 46 46 CF CF 0C 09 0C' + '4D 35 39 38 44 4B 4B 38 35 33 0C 09 4B E7 00 00' + '00 00 00 00 00 00 00 00 33 05 07 08 09 0C 33 33' + '33 35 39 39 44 44 46 39 44 39 44 44 44 44 44 44' + '46 44 0C 08 09 09 09 09 09 46 46 CF D3 0C 0C 0C' + '44 4B 46 4B 44 44 38 38 33 33 0C 0C 4B E7 00 00' + '00 00 00 00 00 00 00 00 35 FF FF FF FF FF FF FF' + 'FF FF FF FF 02 33 00 00 00 35 35 38 38 39 39 44' + '39 09 09 09 09 09 09 09 09 46 4D CF D3 0C 0C 09' + '0C 09 0C 09 33 0C 33 35 35 33 0C 0C 4D E7 00 00' + '00 00 00 00 00 00 00 00 39 FF FF FF FF FF FF FF' + 'FF FF FF FF FF 07 35 0B 09 09 09 08 09 09 09 09' + '09 09 09 09 09 09 09 09 0C 46 4B CF D3 0C 0C 09' + '0C 0C 0C 09 0C 09 33 0C 0C 0C 0C 0C 4D E7 00 00' + '00 00 00 00 00 00 00 00 46 FF FF FF FF FF FF FF' + 'FF FF FF FF FF 05 09 09 09 09 09 09 09 09 09 0C' + '09 09 09 0C 09 0C 09 0C 09 46 4B CF D8 33 09 0C' + '0C 09 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 4D EA 00 00' + '00 00 00 00 00 00 00 00 4D FF FF FF FF FF FF FF' + 'FF FF FF FF FF 02 0C 08 09 09 09 09 0C 09 09 09' + '09 09 0C 09 09 09 0C 09 0C 46 4D D3 D3 33 09 0C' + '09 33 0C 09 33 0C 0C 0C 0C 0C 0C 0C CF EA 00 00' + '00 00 00 00 00 00 00 00 CF 07 FF FF FF FF FF FF' + 'FF FF FF FF FF FF 09 09 09 09 09 09 09 0C 09 09' + '0C 09 09 0C 09 0C 09 0C 0C 4B 4B D3 D8 44 44 39' + '38 35 33 33 0C 09 0C 0C 0C 33 0C 33 4D EA 00 00' + '00 00 00 00 00 00 00 00 D3 09 02 02 05 05 05 FF' + '02 FF FF FF FF 02 0C 09 09 09 09 0C 09 09 0C 09' + '09 0C 09 0C 09 0C 09 0C 09 46 4D D8 D8 D3 D3 D8' + 'D3 D3 D3 CF 4D 4D CF 4D 4D 46 44 44 D8 ED 00 00' + '00 00 00 00 00 00 00 00 D8 33 02 0C EA F1 F1 F1' + 'ED ED D3 38 02 02 09 09 09 09 09 09 0C 09 09 0C' + '09 09 0C 0C 09 0C 0C 0C 0C 4B CF D8 D8 D3 EF D8' + 'D3 D3 D8 CF DA D3 DA DA DA E6 DA DA E7 ED D8 D3' + 'F7 F5 8E 8E F3 F4 F4 F4 ED E7 E6 E7 F1 F4 F1 ED' + 'F4 F1 ED EA D3 4D D3 CF 4D 4B 4B 46 4B 46 4B 44' + '46 44 46 44 46 44 33 0C 09 4B CF D8 DA D3 D8 D8' + 'D8 D8 D8 D3 E6 DA DA E6 DA DA E6 DA EA ED 4B 4B' + 'F9 FA EF EF F4 F4 F1 F4 F4 F4 F1 F4 F1 F4 F1 F1' + 'F4 F1 ED F1 ED EA EA EA E7 E6 E6 DA DA DA DA DA' + 'DA DA D8 DA D8 DA 46 0C 0C 4B CF D8 DA 4B 4B 4B' + '4B CF CF CF D3 E6 E1 E6 E6 E6 E6 DA EA F1 46 4B' + 'FB DE 77 77 76 77 77 73 73 73 73 6F 6F 6D 6F 6D' + '6D 6D 6D 21 69 6D 21 21 21 21 25 71 71 79 71 71' + '79 79 79 79 2D D3 44 0C 09 4D CF DA E6 33 0C 0C' + '0C 33 0C 33 0C 0C 0C 0C 33 33 33 38 D8 F1 4B 4B' + 'F9 C3 67 67 67 65 65 65 60 60 60 5C 5C 58 56 54' + '50 50 50 15 15 15 15 15 15 15 15 15 15 4E 4E 4E' + '4E 50 50 50 16 D3 39 0C 0C 4B D3 DA DA 33 33 0C' + '0C 0C 33 0C 33 0C 33 33 0D 33 33 33 D3 F4 46 4B' + 'F9 C2 65 62 65 62 60 60 60 5C 5C 58 58 58 54 54' + '50 50 50 50 15 15 50 15 15 15 15 4E 4E 4E 4E 4E' + '50 50 54 4E 17 D8 39 0C 0C 4D D3 DA E7 33 0C 33' + '0C 33 0C 33 0C 33 33 0D 33 0D 33 0D D3 F4 46 4B' + 'F7 C1 60 60 60 60 60 5D 5C 5C 5C 58 58 56 54 54' + '50 50 50 4E 50 4E 15 4E 15 4E 4E 4E 50 50 54 4E' + '54 54 4E 56 17 DA 38 0C 09 4D D3 DA E7 33 33 0C' + '0C 33 0C 33 33 0D 33 33 0C 33 33 0E D8 F4 46 4D' + 'F9 BB 60 5C 60 5C 5C 5C 58 58 58 58 54 54 54 50' + '50 50 4E 4E 4E 4E 50 4E 50 50 54 54 54 4E 56 54' + '54 56 56 56 1E DA 38 0C 0C 4D D8 E6 E6 33 0C 33' + '33 0C 33 0C 33 0D 33 33 33 33 33 33 D7 F4 46 4D' + 'F4 BB 58 5C 58 5C 56 56 58 58 56 54 54 54 4E 50' + '50 50 50 54 50 50 50 54 54 4E 54 54 54 56 56 56' + '56 56 58 58 1E E7 35 0C 09 CF D3 E6 E6 33 33 0C' + '33 0C 0C 0C 0C 0C 0C 0C 33 0D 33 33 D8 F7 46 4B' + 'F4 BA 58 58 58 56 56 56 56 56 54 54 50 54 50 50' + '50 50 50 54 4E 54 50 54 54 54 56 54 58 58 58 58' + '5C 5C 58 5C 28 EA 35 0C 0C CF D8 E6 E7 33 33 33' + '0C 33 33 35 35 35 35 33 33 33 33 33 D8 F7 46 4B' + 'F4 8A 54 54 54 54 54 54 54 50 54 50 4E 50 54 4E' + '50 54 4E 54 54 54 56 56 56 58 58 5C 58 5C 58 60' + '58 5D 60 5C 2A ED 33 09 0C CF D8 E6 E7 35 33 33' + '33 33 33 33 33 0E 33 33 33 33 33 0E DA F9 46 4D' + 'F1 8A 54 50 50 50 4E 54 4E 50 4E 54 50 4E 50 54' + '4E 54 56 54 54 56 56 58 5C 58 58 5C 60 60 60 60' + '60 60 60 60 3C ED 33 0C 0C CF DA E7 EA 35 33 0C' + '33 33 33 33 33 0E 0E 0E 33 0E 33 35 DA F8 44 4B' + 'F1 88 4E 4E 4E 4E 4E 4E 4E 54 4E 4E 4E 56 4E 54' + '54 54 54 56 56 58 5C 58 5C 60 5D 60 60 60 60 60' + '60 60 62 60 40 F1 0C 09 0C CF DA E7 EA 35 33 33' + '0C 33 33 33 33 0E 35 33 35 33 35 33 E1 F9 44 4D' + 'ED 88 15 4E 4E 4E 4E 4E 4E 4E 4E 51 4E 50 50 50' + '56 54 56 56 5C 5C 5C 5C 5D 60 60 60 62 62 62 65' + '65 65 65 65 43 ED 0C 0C 09 D3 DA E7 ED 35 33 33' + '33 33 33 33 33 33 35 33 35 33 35 35 DA FB 44 4D' + 'EC 83 15 15 15 15 15 15 15 4E 4E 4E 54 4E 56 54' + '54 56 58 5C 5C 5C 5D 60 60 60 62 65 65 65 67 65' + '67 67 67 67 8B ED 09 0C 0D D3 DA EA ED 35 33 33' + '33 33 33 33 33 35 33 35 33 35 35 35 E6 F9 44 4D' + 'EA 80 15 15 15 15 15 15 15 15 4E 4E 4E 54 54 54' + '56 5C 58 5D 5D 60 60 61 65 65 65 65 67 67 67 7E' + '7E 96 7E 7E BE EA 0C 0C 0C D3 DA EA ED 35 33 33' + '33 33 33 33 33 35 33 35 35 35 37 35 E1 F9 44 4D' + 'EA 80 10 0F 10 10 15 15 15 15 4E 54 50 50 56 56' + '5C 56 60 60 60 61 65 65 65 65 67 7E 96 96 96 96' + '96 96 96 96 BF EA 0C 09 0C D3 E6 E7 ED 38 33 33' + '33 33 33 35 33 35 35 35 37 35 37 37 E1 F9 44 4D' + 'E7 3F 10 10 10 15 0F 15 15 15 4E 4E 56 54 56 56' + '5C 5D 5D 60 62 65 65 65 67 7E 96 96 9B 96 9B 9F' + '9F 9F 9F 96 D7 E7 0C 0C 0C D3 E1 EA ED 38 33 33' + '33 33 33 33 35 33 35 35 35 37 35 38 E1 F9 44 4B' + 'E7 3F 10 10 10 10 10 15 15 14 4E 56 4E 56 56 5C' + '5C 5D 60 65 65 65 65 7E 96 96 9B 9B 9F 9F A1 9F' + '9F 9F 9F 96 DA E1 0C 0C 0C D8 E6 E9 F1 38 33 33' + '33 35 33 35 35 35 35 37 37 37 37 37 E6 F9 39 4D' + 'E6 31 10 10 10 10 15 10 15 4E 4E 50 56 56 5C 56' + '5D 60 62 65 65 7E 7E 96 9B 9B 9F 9F 9F A3 A1 A3' + 'A3 A3 A3 96 E6 DA 0C 0C 0C D3 E1 EA ED 39 33 33' + '33 35 33 35 35 35 38 37 37 37 38 38 E1 F9 39 4D' + 'DA 31 15 0F 10 10 10 15 15 4E 54 50 56 56 58 60' + '60 60 65 65 67 7E 96 96 9F 9F 9F A3 A3 A6 A7 A7' + 'A7 A7 A7 9D E7 DA 0B 0C 0C D8 E6 EA F1 38 33 35' + '35 33 35 35 35 38 35 38 37 38 38 39 E6 F9 39 4D' + 'DA 31 0F 10 10 10 15 10 15 4E 4E 56 56 58 5D 5D' + '60 65 65 65 7E 96 9F 9C 9F A3 A3 A3 A7 A7 AA AA' + 'AB AB AB 9D EA D8 0C 0C 0C D3 E6 ED F1 39 35 33' + '35 35 38 35 38 35 38 38 38 38 39 39 E1 F9 39 4D' + 'DA 31 10 10 10 10 10 15 15 4E 54 56 56 5B 5D 60' + '60 65 65 7E 96 96 9F 9F A3 A3 A7 AA AB AB AC AC' + 'AC AC AC 92 ED D3 0C 0C 0C D8 E6 EC F1 38 33 35' + '33 35 35 35 35 38 38 38 38 38 39 39 E6 F9 39 4B' + 'DA 30 10 10 10 10 15 10 14 50 50 54 58 5C 5C 60' + '65 65 67 7E 96 9F 9F A1 A3 A7 AA AA AC AC AF AF' + 'AF AF AF 92 F3 CF 0C 0C 0C D3 E6 ED F1 39 35 35' + '35 35 38 38 38 38 38 38 39 39 39 39 E6 F9 39 4B' + 'DA 2C 10 10 10 10 10 15 15 50 54 56 56 58 60 60' + '60 65 7E 96 96 9F 9F A3 A7 A7 AB AC AF AF C5 C5' + 'C6 C5 C5 95 F4 4B 0C 0C 33 D3 E7 ED F3 39 35 35' + '35 37 35 38 38 38 38 39 39 39 44 44 E6 F9 39 4B' + 'D8 2C 10 10 10 10 15 10 4E 50 50 56 58 60 5D 61' + '65 67 7E 96 9F 9F A3 A3 AA AB AC AD AF C5 C6 C6' + 'C6 C6 C6 93 F7 44 0C 0C 0C D8 E7 ED F1 44 35 35' + '35 38 37 35 38 38 39 39 39 44 44 44 E6 F9 39 4B' + 'D8 2C 15 0F 10 10 10 15 14 50 54 56 58 5D 60 62' + '65 67 7E 96 9F 9F A3 A7 A7 AB AD AF C5 C6 C8 C8' + 'C8 C8 C8 7D F9 44 33 0C 33 D3 E7 ED F4 44 35 35' + '38 35 39 38 38 39 39 39 44 39 44 44 E7 F9 38 4B' + 'D3 2C 0F 10 10 10 15 15 4E 50 54 56 5C 5D 60 62' + '65 67 96 96 9F 9F A3 A7 AB AC AF C5 C5 C6 C8 C8' + 'CB C8 C8 7D FB 39 0C 0C 0C D8 E7 ED F4 44 35 38' + '35 38 38 38 38 39 39 44 44 44 44 44 E7 F9 38 46' + 'D8 25 10 10 10 10 10 15 15 50 54 56 58 5D 60 62' + '67 75 96 96 9F A1 A3 AA AA AC AF C5 C6 C8 C8 CD' + 'CB CD CB 7B FF 38 0C 33 0C D3 E7 ED F4 44 36 38' + '38 38 38 39 39 39 44 44 44 44 46 46 E7 F9 38 4B' + 'D7 24 10 10 10 10 15 10 4E 50 54 56 5C 5D 60 62' + '65 67 96 9B 9F 9F A3 A7 AB AC AF C5 C6 C8 C8 CB' + 'CB CD CB 81 FE 38 0C 0C 0C D8 E7 ED F4 46 35 36' + '38 38 38 39 39 44 44 44 44 46 46 46 E7 F9 38 46' + 'D8 24 10 10 10 10 10 15 15 54 54 56 58 60 60 62' + '67 75 96 96 9F A1 A3 A7 AA AC AF C5 C6 C8 CB CB' + 'CD CC CD 83 FE 35 0C 0D 33 D3 E7 F1 F4 46 38 38' + '38 38 39 39 39 44 44 44 44 46 46 46 E6 F9 38 4B' + 'D3 1F 10 10 10 10 10 15 14 4E 54 56 58 5C 60 61' + '65 67 7E 96 9C 9F A3 A7 AA AC AF AF C5 C8 C8 C8' + 'CB CB C8 86 FE 0E 33 0B 0C D3 E7 F1 F1 49 35 38' + '38 38 39 39 39 44 39 46 44 44 44 46 E7 F9 35 46' + 'D3 20 10 10 10 10 10 15 4E 50 54 54 58 5C 60 60' + '65 67 7E 96 96 9F A3 A3 A7 AB AC AF C5 C6 C8 C8' + 'C8 CD CD 87 FE 33 0C 0D 33 D3 E7 F1 F4 46 38 38' + '38 39 39 39 44 44 44 44 46 44 44 44 E7 F9 38 46' + 'D3 21 0F 10 10 10 10 15 15 4E 54 54 56 58 60 60' + '65 65 67 96 9B 9F 9F A3 A7 AA AC AF C4 B7 B8 B6' + 'B5 BD DE E5 FD 0D 0D 0D 0D D7 E6 F1 F4 46 38 35' + '38 35 39 39 39 44 44 44 46 46 46 4B E6 FB 35 4B' + 'CF 20 10 10 10 10 10 10 15 4E 4E 54 56 58 5C 5D' + '60 65 67 7E 96 9D 8F 95 B3 B4 C0 DF E7 EC F3 F9' + 'FB FF FF FE F3 0D 0C 0D 0D D3 E7 F1 F4 44 38 38' + '39 39 39 44 44 44 44 46 46 4B 46 46 E6 FB 35 46' + 'D3 1B 10 10 10 10 10 10 15 4E 4E 54 56 56 57 6D' + '30 3E 84 B9 D5 E0 EA F1 F7 F9 FB FB FC FB F9 EA' + 'D3 4B 39 33 33 0C 0D 0D 0D D7 E7 F1 F4 46 39 38' + '39 39 44 44 44 44 46 46 44 44 46 46 E6 FB 35 46' + 'CF 1B 10 10 10 10 0F 13 19 23 3B 40 8B D2 D8 E1' + 'E7 ED F1 F4 F4 F7 F7 F7 00 00 00 00 00 00 00 00' + 'E7 CF 39 0E 0C 0D 0D 0D 0D D3 E7 F1 F1 46 38 38' + '35 39 38 44 4B 4D D8 DA EA ED F4 F7 F9 FB 35 46' + 'CF 2B 28 3C 42 48 CF D3 D7 D8 DA E6 E6 E6 E7 EA' + 'ED 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 D8 46 33 0E 33 D3 E7 F1 F1 EA E7 EA' + 'ED F4 FB FE FF FF FF FF FF FF FF FF FE F7 38 44' + 'CF CF CF D3 D3 D4 D8 DA E6 E6 EA EA 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 E6 4D D3 DA F1 F1 F7 F9 F9' + 'FB FE FB FB FB FB FB FB FB F9 F9 F7 F7 F1 E7 4D' + 'CF D8 E0 E6 EA 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 F1 F1 F4 F4 F4' + 'F1 F1 F4 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 87' + 'FF FF FF FF FF FF FF 00 0F FF FF FF FF FF FE 00' + '00 0F FF FF FF FF FE 00 00 00 3F FF FF FF FC 00' + '00 00 01 E0 03 FF F8 00 00 00 00 00 00 00 F8 00' + '00 00 00 00 00 00 F8 00 00 00 00 00 00 00 FF C0' + '00 00 00 00 00 00 FF DF FC E0 00 00 00 00 FF DF' + 'FE 00 00 00 00 00 FF DF FE 00 00 00 00 00 FF DF' + 'FE 00 00 00 00 00 FF CF FF 00 00 00 00 00 FF C0' + '5E 00 00 00 00 00 FF C0 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 3F C0 00 00 00 00 00' + '1F FF FC 00 00 00 00 03 FF FF FF 80 00 00 01 FF' + 'FF FF FF F8 07 FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF 28 00' + '00 00 40 00 00 00 80 00 00 00 01 00 20 00 00 00' + '00 00 00 42 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 40 40 40 9E 34 34 34 DA 31 31' + '31 B9 2D 2D 2D 97 2D 2D 2D 76 2D 2D 2D 54 2D 2D' + '2D 32 1E 1E 1E 11 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 53 53 53 40 4F 4F 4F FC 51 51 51 FF 54 54' + '54 FF 54 54 54 FF 42 42 42 FF 35 35 35 FF 35 35' + '35 FF 34 34 34 FE 37 37 37 EB 3A 3A 3A CC 3F 3F' + '3F AA 3F 3F 3F 88 40 40 40 67 3E 3E 3E 45 41 41' + '41 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 43 43 43 C9 31 31 31 FF 31 31 31 FF 37 37' + '37 FF 3A 3A 3A FF 43 43 43 FF 56 56 56 FF 3E 3E' + '3E FF 4D 4D 4D FF 53 53 53 FF 50 50 50 FF 39 39' + '39 FF 47 47 47 FF 45 45 45 FF 45 45 45 FF 45 45' + '45 FE 45 45 45 F8 48 48 48 DD 4B 4B 4B BD 4D 4D' + '4D 9B 4D 4D 4D 7A 4B 4B 4B 58 4B 4B 4B 36 45 45' + '45 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 50 50' + '50 5F 3A 3A 3A FE 44 44 44 FF 4C 4C 4C FF 51 51' + '51 FF 51 51 51 FF 49 49 49 FF 34 34 34 FF 46 46' + '46 FF 40 40 40 FF 4B 4B 4B FF 4C 4C 4C FF 42 42' + '42 FF 59 59 59 FF 5F 5F 5F FF 64 64 64 FF 65 65' + '65 FF 63 63 63 FF 60 60 60 FF 5B 5B 5B FF 58 58' + '58 FF 55 55 55 FF 53 53 53 FF 52 52 52 FF 51 51' + '51 FD 51 51 51 EC 53 53 53 D0 56 56 56 AE 56 56' + '56 8D 55 55 55 6B 53 53 53 49 52 52 52 28 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 5D 5D 5D 13 3F 3F' + '3F E1 47 47 47 FF 44 44 44 FF 49 49 49 FF 44 44' + '44 FF 39 39 39 FF 46 46 46 FF 3B 3B 3B FF 4C 4C' + '4C FF 40 40 40 FF 4F 4F 4F FF 4E 4E 4E FF 51 51' + '51 FF 5F 5F 5F FF 4C 4C 4C FF 5F 5F 5F FF 4E 4E' + '4E FF 5C 5C 5C FF 56 56 56 FF 5E 5E 5E FF 5D 5D' + '5D FF 63 63 63 FF 65 65 65 FF 69 69 69 FF 64 64' + '64 FF 61 61 61 FF 6A 6A 6A FF 60 60 60 FF 5D 5D' + '5D FF 5F 5F 5F FF 69 69 69 FF 6A 6A 6A FE 67 67' + '67 F7 68 68 68 BA 66 66 66 1E 38 38 38 1B 2B 2B' + '2B 63 52 52 52 D1 62 62 62 FA 6B 6B 6B FE 6C 6C' + '6C FA 50 50 50 F5 58 58 58 F1 66 66 66 D8 73 73' + '73 B7 85 85 85 97 8E 8E 8E 74 96 96 96 53 9E 9E' + '9E 32 99 99 99 14 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 85 85 85 17 5E 5E 5E DE 3F 3F' + '3F FF 3D 3D 3D FF 41 41 41 FF 3A 3A 3A FF 3B 3B' + '3B FF 40 40 40 FF 46 46 46 FF 3A 3A 3A FF 4C 4C' + '4C FF 4B 4B 4B FF 4F 4F 4F FF 5B 5B 5B FF 4B 4B' + '4B FF 5D 5D 5D FF 4C 4C 4C FF 5D 5D 5D FF 51 51' + '51 FF 5F 5F 5F FF 53 53 53 FF 61 61 61 FF 58 58' + '58 FF 65 65 65 FF 5B 5B 5B FF 67 67 67 FF 5C 5C' + '5C FF 64 64 64 FF 5E 5E 5E FF 60 60 60 FF 63 63' + '63 FF 5E 5E 5E FF 60 60 60 FF 62 62 62 FF 68 68' + '68 FE 4E 4E 4E D9 28 28 28 C4 25 25 25 F8 25 25' + '25 FF 62 62 62 FF 69 69 69 FF 7A 7A 7A FF 7E 7E' + '7E FF 31 31 31 FF 2D 2D 2D FF 2D 2D 2D FF 2D 2D' + '2D FF 2D 2D 2D FF 33 33 33 FF 3B 3B 3B FF 49 49' + '49 FE 51 51 51 FA 5D 5D 5D E7 62 62 62 CC 6C 6C' + '6C AA 7C 7C 7C 89 8A 8A 8A 67 9E 9E 9E 45 9E 9E' + '9E 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 5A 5A 5A 1F 3E 3E 3E F2 3B 3B' + '3B FF 38 38 38 FF 3E 3E 3E FF 33 33 33 FF 3E 3E' + '3E FF 47 47 47 FF 3B 3B 3B FF 56 56 56 FF 3F 3F' + '3F FF 57 57 57 FF 49 49 49 FF 50 50 50 FF 52 52' + '52 FF 50 50 50 FF 56 56 56 FF 55 55 55 FF 56 56' + '56 FF 5C 5C 5C FF 56 56 56 FF 63 63 63 FF 58 58' + '58 FF 68 68 68 FF 5D 5D 5D FF 66 66 66 FF 5F 5F' + '5F FF 66 66 66 FF 64 64 64 FF 64 64 64 FF 67 67' + '67 FF 6C 6C 6C FF 67 67 67 FF 69 69 69 FF 47 47' + '47 FF 25 25 25 FF 25 25 25 FF 25 25 25 FF 26 26' + '26 FF 64 64 64 FF 6B 6B 6B FF 7C 7C 7C FF 80 80' + '80 FF 33 33 33 FF 2E 2E 2E FF 33 33 33 FF 66 66' + '66 FF 5F 5F 5F FF 52 52 52 FF 4C 4C 4C FF 45 45' + '45 FF 3E 3E 3E FF 38 38 38 FF 34 34 34 FF 33 33' + '33 FF 32 32 32 FF 31 31 31 FF 39 39 39 FF 75 75' + '75 FE 90 90 90 86 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 5C 5C 5C 5B 4A 4A 4A C9 4F 4F' + '4F D7 48 48 48 E5 4F 4F 4F F2 59 59 59 FE 43 43' + '43 FF 57 57 57 FF 42 42 42 FF 51 51 51 FF 48 48' + '48 FF 4A 4A 4A FF 4D 4D 4D FF 49 49 49 FF 51 51' + '51 FF 4D 4D 4D FF 54 54 54 FF 51 51 51 FF 59 59' + '59 FF 56 56 56 FF 5E 5E 5E FF 5D 5D 5D FF 5F 5F' + '5F FF 5F 5F 5F FF 60 60 60 FF 62 62 62 FF 60 60' + '60 FF 65 65 65 FF 65 65 65 FF 64 64 64 FF 67 67' + '67 FF 6C 6C 6C FF 6B 6B 6B FF 3B 3B 3B FF 27 27' + '27 FF 26 26 26 FF 27 27 27 FF 27 27 27 FF 27 27' + '27 FF 65 65 65 FF 6E 6E 6E FF 7E 7E 7E FF 83 83' + '83 FF 34 34 34 FF 2F 2F 2F FF 36 36 36 FF 7C 7C' + '7C FF 4D 4D 4D FF 5A 5A 5A FF 50 50 50 FF 63 63' + '63 FF 75 75 75 FF 74 74 74 FF 57 57 57 FF 50 50' + '50 FF 43 43 43 FF 32 32 32 FF 33 33 33 FF 76 76' + '76 FF A8 A8 A8 DB 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 78 78 78 D0 47 47' + '47 FF 16 16 16 FF 1D 1D 1D FF 25 25 25 FF 2B 2B' + '2B FF 33 33 33 FF 3B 3B 3B FF 40 40 40 FF 4C 4C' + '4C FF 4D 4D 4D FF 5D 5D 5D FF 58 58 58 FF 67 67' + '67 FF 5D 5D 5D FE 69 69 69 FE 5B 5B 5B FF 61 61' + '61 FF 5C 5C 5C FF 5D 5D 5D FF 63 63 63 FF 5D 5D' + '5D FF 66 66 66 FF 60 60 60 FF 64 64 64 FF 67 67' + '67 FF 62 62 62 FF 32 32 32 FF 28 28 28 FF 28 28' + '28 FF 29 29 29 FF 28 28 28 FF 28 28 28 FF 29 29' + '29 FF 68 68 68 FF 70 70 70 FF 81 81 81 FF 86 86' + '86 FF 35 35 35 FF 30 30 30 FF 34 34 34 FF 66 66' + '66 FF 6C 6C 6C FF 73 73 73 FF 70 70 70 FF 6B 6B' + '6B FF 64 64 64 FF 5A 5A 5A FF 5A 5A 5A FF 43 43' + '43 FF 43 43 43 FF 33 33 33 FF 33 33 33 FF 77 77' + '77 FF AC AC AC E5 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 7C 7C 7C AE 4B 4B' + '4B FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 02 02 02 FF 05 05' + '05 FF 1C 1C 1C C6 72 72 72 26 65 65 65 53 4D 4D' + '4D AF 45 45 45 F6 49 49 49 FF 50 50 50 FF 53 53' + '53 FF 5A 5A 5A FF 5D 5D 5D FF 63 63 63 FF 5B 5B' + '5B FF 2F 2F 2F FF 29 29 29 FF 2A 2A 2A FF 2A 2A' + '2A FF 2A 2A 2A FF 2A 2A 2A FF 2B 2B 2B FF 2B 2B' + '2B FF 6A 6A 6A FF 73 73 73 FF 83 83 83 FF 89 89' + '89 FF 36 36 36 FF 30 30 30 FF 30 30 30 FF 31 31' + '31 FF 31 31 31 FF 32 32 32 FF 31 31 31 FF 36 36' + '36 FF 39 39 39 FF 44 44 44 FF 49 49 49 FF 4B 4B' + '4B FF 47 47 47 FF 35 35 35 FF 35 35 35 FF 7A 7A' + '7A FF AB AB AB E5 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 80 80 80 8D 5B 5B' + '5B FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 14 14 14 EE 2D 2D 2D C5 29 29 29 F8 28 28' + '28 FF 28 28 28 FF 28 28 28 FF 2A 2A 2A FF 2A 2A' + '2A FF 2A 2A 2A FF 2A 2A 2A FF 2A 2A 2A FF 2A 2A' + '2A FF 2A 2A 2A FF 2B 2B 2B FF 2B 2B 2B FF 2C 2C' + '2C FF 2C 2C 2C FF 2C 2C 2C FF 2C 2C 2C FF 2D 2D' + '2D FF 6C 6C 6C FF 75 75 75 FF 86 86 86 FF 8C 8C' + '8C FF 37 37 37 FF 31 31 31 FF 31 31 31 FF 32 32' + '32 FF 32 32 32 FF 32 32 32 FF 33 33 33 FF 33 33' + '33 FF 33 33 33 FF 34 34 34 FF 34 34 34 FF 35 35' + '35 FF 36 36 36 FF 35 35 35 FF 35 35 35 FF 7C 7C' + '7C FF AE AE AE E5 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 86 86 86 6C 6B 6B' + '6B FF 01 01 01 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 10 10 10 FF 2C 2C 2C FF 29 29 29 FF 28 28' + '28 FF 29 29 29 FF 2A 2A 2A FF 2B 2B 2B FF 2C 2C' + '2C FF 2C 2C 2C FF 2B 2B 2B FF 2B 2B 2B FF 2B 2B' + '2B FF 2B 2B 2B FF 2C 2C 2C FF 2D 2D 2D FF 2D 2D' + '2D FF 2D 2D 2D FF 2D 2D 2D FF 2E 2E 2E FF 2E 2E' + '2E FF 6E 6E 6E FF 77 77 77 FF 88 88 88 FF 8E 8E' + '8E FF 38 38 38 FF 31 31 31 FF 32 32 32 FF 32 32' + '32 FF 33 33 33 FF 33 33 33 FF 34 34 34 FF 34 34' + '34 FF 34 34 34 FF 35 35 35 FF 35 35 35 FF 36 36' + '36 FF 36 36 36 FF 36 36 36 FF 36 36 36 FF 7E 7E' + '7E FF B1 B1 B1 E5 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 8E 8E 8E 4D 7B 7B' + '7B FF 06 06 06 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 08 08 08 FF 30 30 30 FF 29 29 29 FF 29 29' + '29 FF 29 29 29 FF 2A 2A 2A FF 2C 2C 2C FF 2D 2D' + '2D FF 2C 2C 2C FF 2C 2C 2C FF 2B 2B 2B FF 2B 2B' + '2B FF 2C 2C 2C FF 2D 2D 2D FF 2E 2E 2E FF 2E 2E' + '2E FF 2E 2E 2E FF 2F 2F 2F FF 2F 2F 2F FF 30 30' + '30 FF 70 70 70 FF 79 79 79 FF 8A 8A 8A FF 91 91' + '91 FF 3A 3A 3A FF 32 32 32 FF 32 32 32 FF 33 33' + '33 FF 34 34 34 FF 34 34 34 FF 34 34 34 FF 35 35' + '35 FF 35 35 35 FF 36 36 36 FF 36 36 36 FF 36 36' + '36 FF 36 36 36 FF 37 37 37 FF 37 37 37 FF 81 81' + '81 FF B5 B5 B5 E5 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 93 93 93 2D 83 83' + '83 FE 17 17 17 FF 03 03 03 FF 01 01 01 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 00 00 00 FF 00 00 00 FF 00 00 00 FF 00 00' + '00 FF 07 07 07 FF 2F 2F 2F FF 29 29 29 FF 29 29' + '29 FF 29 29 29 FF 2A 2A 2A FF 2C 2C 2C FF 2E 2E' + '2E FF 2D 2D 2D FF 2C 2C 2C FF 2C 2C 2C FF 2C 2C' + '2C FF 2D 2D 2D FF 2E 2E 2E FF 2F 2F 2F FF 2F 2F' + '2F FF 2F 2F 2F FF 30 30 30 FF 30 30 30 FF 31 31' + '31 FF 71 71 71 FF 7B 7B 7B FF 8D 8D 8D FF 93 93' + '93 FF 64 64 64 FF 61 61 61 FF 59 59 59 FF 54 54' + '54 FF 4E 4E 4E FF 46 46 46 FF 40 40 40 FF 36 36' + '36 FF 34 34 34 FF 35 35 35 FF 36 36 36 FF 37 37' + '37 FF 37 37 37 FF 38 38 38 FF 38 38 38 FF 82 82' + '82 FF B5 B5 B5 E5 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 BB BB BB 13 89 89' + '89 F9 2B 2B 2B FF 09 09 09 FF 07 07 07 FE 12 12' + '12 FE 18 18 18 FE 0F 0F 0F FE 08 08 08 FE 05 05' + '05 FF 04 04 04 FF 02 02 02 FF 01 01 01 FF 01 01' + '01 FF 08 08 08 FF 2D 2D 2D FF 29 29 29 FF 29 29' + '29 FF 29 29 29 FF 2B 2B 2B FF 2D 2D 2D FF 2E 2E' + '2E FF 2E 2E 2E FF 2D 2D 2D FF 2D 2D 2D FF 2D 2D' + '2D FF 2E 2E 2E FF 2F 2F 2F FF 2F 2F 2F FF 2F 2F' + '2F FF 30 30 30 FF 31 31 31 FF 31 31 31 FF 31 31' + '31 FF 73 73 73 FF 7D 7D 7D FF 8F 8F 8F FF 94 94' + '94 FF 87 87 87 FF 8D 8D 8D FF 8F 8F 8F FF 8A 8A' + '8A FF 89 89 89 FF 8C 8C 8C FF 85 85 85 FF 7A 7A' + '7A FF 79 79 79 FF 87 87 87 FF 7F 7F 7F FF 7A 7A' + '7A FF 73 73 73 FF 6A 6A 6A FF 62 62 62 FF 92 92' + '92 FF B8 B8 B8 E5 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 91 91' + '91 EB 3F 3F 3F FF 0E 0E 0E FF 1C 1C 1C E0 9C 9C' + '9C 27 CA CA CA 2C D4 D4 D4 36 C9 C9 C9 3E B7 B7' + 'B7 4B 9D 9D 9D 56 75 75 75 AB 53 53 53 FE 0B 0B' + '0B FF 0B 0B 0B FF 2B 2B 2B FF 29 29 29 FF 29 29' + '29 FF 29 29 29 FF 2B 2B 2B FF 2E 2E 2E FF 2E 2E' + '2E FF 2E 2E 2E FF 2E 2E 2E FF 2D 2D 2D FF 2E 2E' + '2E FF 2F 2F 2F FF 30 30 30 FF 30 30 30 FF 30 30' + '30 FF 31 31 31 FF 31 31 31 FF 32 32 32 FF 32 32' + '32 FF 75 75 75 FF 80 80 80 FF 91 91 91 FF 97 97' + '97 FF 8A 8A 8A FF 8B DC 8B FF 8F 8F 8F FF 8A 8A' + '8A FF 8E 8E 8E FF 8C 8C 8C FF 82 82 82 FF 98 98' + '98 FF 8F 8F 8F FF 96 96 96 FF 9C 9C 9C FF 9D 9D' + '9D FF 9D 9D 9D FF 9A 9A 9A FF 9B 9B 9B FF AC AC' + 'AC FF BC BC BC E5 63 63 63 79 83 83 83 E8 DE DE' + 'DE CF C2 D6 C2 CE 66 B4 66 CD 62 AB 62 C4 CE CE' + 'CE C4 D1 D1 D1 C4 D2 D2 D2 C4 CC CC CC C9 BD BD' + 'BD F9 AC AC AC FF A4 A4 A4 FF AA AA AA F3 C6 C6' + 'C6 CF C6 C6 C6 CE C0 C0 C0 CB C0 C0 C0 C4 C7 C7' + 'C7 C4 C3 C3 C3 C4 B9 B9 B9 CA B0 B0 B0 FD 8D 8D' + '8D FF 7F 7F 7F FF 88 88 88 FF 83 83 83 FF 7C 7C' + '7C FF 76 76 76 FF 73 73 73 FF 73 73 73 FF 73 73' + '73 FF 6D 6D 6D FF 6D 6D 6D FF 68 68 68 FF 66 66' + '66 FF 67 67 67 FF 68 68 68 FF 67 67 67 FF 68 68' + '68 FF 63 63 63 FF 4B 4B 4B FF 32 32 32 FF 32 32' + '32 FF 76 76 76 FF 82 82 82 FF 93 93 93 FF 99 99' + '99 FF 8C 8C 8C FF 93 93 93 FF 94 94 94 FF 94 94' + '94 FF 93 93 93 FF 94 94 94 FF 8B 8B 8B FF 9F 9F' + '9F FF A1 A1 A1 FF 99 99 99 FF 9F 9F 9F FF 9F 9F' + '9F FF 9F 9F 9F FF A0 A0 A0 FF A0 A0 A0 FF AE AE' + 'AE FF BE BE BE E5 42 42 42 99 76 76 76 FF E0 E0' + 'E0 FF C9 E0 C9 FF 88 E0 88 FF 8B DC 8B FF CD CD' + 'CD FF CB CB CB FF CA CA CA FF CB CB CB FF CE CE' + 'CE FF CA CA CA FF CB CB CB FF CA CA CA FF C4 C4' + 'C4 FF C9 C9 C9 FF C3 C3 C3 FF C6 C6 C6 FF C9 C9' + 'C9 FF C4 C4 C4 FF C0 C0 C0 FF BC BC BC FF B9 B9' + 'B9 FF B5 B5 B5 FF B2 B2 B2 FF AE AE AE FF AB AB' + 'AB FF A6 A6 A6 FF A3 A3 A3 FF 9F 9F 9F FF 9D 9D' + '9D FF 9D 9D 9D FF 9B 9B 9B FF 9A 9A 9A FF 98 98' + '98 FF 98 98 98 FF 98 98 98 FF 96 96 96 FF 95 95' + '95 FF 94 94 94 FF 69 69 69 FF 33 33 33 FF 32 32' + '32 FF 78 78 78 FF 84 84 84 FF 95 95 95 FF 9D 9D' + '9D FF 6D 6D 6D FF 73 73 73 FF 73 73 73 FF 76 76' + '76 FF 80 80 80 FF 83 83 83 FF 85 85 85 FF 89 89' + '89 FF 9E 9E 9E FF A3 A3 A3 FF A4 A4 A4 FF A5 A5' + 'A5 FF A7 A7 A7 FF A4 A4 A4 FF A2 A2 A2 FF AE AE' + 'AE FF C1 C1 C1 E5 3D 3D 3D 99 76 76 76 FF E0 E0' + 'E0 FF A3 9C 87 FF 98 7A 2A FF 97 7A 29 FF 97 79' + '29 FF 97 79 29 FF 95 77 28 FF 94 76 27 FF 93 74' + '26 FF 90 71 25 FF 8F 6F 24 FF 8D 6C 22 FF 8A 6A' + '22 FF 89 68 21 FF 88 67 20 FF 86 65 21 FF 85 63' + '20 FF 84 62 20 FF 82 60 20 FF 81 5F 20 FF 80 5F' + '21 FF 7F 5E 22 FF 80 5F 23 FF 7F 5F 25 FF 7E 5E' + '27 FF 7F 5F 28 FF 7E 5F 2A FF 7E 60 2C FF 7F 61' + '2D FF 80 62 2E FF 80 62 2F FF 80 63 2F FF 80 62' + '30 FF 80 62 30 FF 80 63 30 FF 7E 61 30 FF 70 5A' + '38 FF 89 89 89 FF 64 64 64 FF 32 32 32 FF 32 32' + '32 FF 79 79 79 FF 86 86 86 FF 98 98 98 FF A0 A0' + 'A0 FF 41 41 41 FF 38 38 38 FF 39 39 39 FF 39 39' + '39 FF 3A 3A 3A FF 3A 3A 3A FF 3A 3A 3A FF 3A 3A' + '3A FF 3A 3A 3A FF 3B 3B 3B FF 3B 3B 3B FF 3C 3C' + '3C FF 43 43 43 FF 4B 4B 4B FF 4F 4F 4F FF 93 93' + '93 FF C3 C3 C3 E5 3C 3C 3C 99 75 75 75 FF DE DE' + 'DE FF A3 98 7D FF 9C 79 1D FF 9C 79 1D FF 9B 78' + '1C FF 9A 77 1C FF 99 75 1B FF 98 73 1A FF 96 71' + '18 FF 94 6E 16 FF 92 6B 15 FF 90 68 13 FF 8E 66' + '11 FF 8C 63 0F FF 89 60 0E FF 87 5D 0C FF 85 59' + '0A FF 84 58 08 FF 82 56 07 FF 81 54 06 FF 7F 52' + '05 FF 7F 52 05 FF 7E 51 04 FF 7E 50 04 FF 7E 50' + '03 FF 7E 50 04 FF 7E 50 04 FF 7E 50 04 FF 7E 51' + '04 FF 7F 52 05 FF 80 53 05 FF 80 53 06 FF 81 54' + '06 FF 81 55 07 FF 82 56 07 FF 83 57 08 FF 6D 4E' + '1A FF 8B 8B 8B FF 5F 5F 5F FF 33 33 33 FF 33 33' + '33 FF 7B 7B 7B FF 88 88 88 FF 9A 9A 9A FF A2 A2' + 'A2 FF 42 42 42 FF 38 38 38 FF 39 39 39 FF 3A 3A' + '3A FF 3A 3A 3A FF 3A 3A 3A FF 3B 3B 3B FF 3C 3C' + '3C FF 3C 3C 3C FF 3D 3D 3D FF 3C 3C 3C FF 3D 3D' + '3D FF 3D 3D 3D FF 3D 3D 3D FF 3E 3E 3E FF 8E 8E' + '8E FF C8 C8 C8 E5 40 40 40 A1 76 76 76 FF DD DD' + 'DD FF A0 94 79 FF 99 74 1A FF 98 74 1A FF 98 73' + '19 FF 97 72 19 FF 96 70 18 FF 94 6F 17 FF 93 6C' + '15 FF 91 6A 14 FF 8F 68 13 FF 8D 65 11 FF 8B 63' + '0F FF 8A 60 0E FF 87 5D 0C FF 85 5A 0A FF 84 58' + '09 FF 83 57 08 FF 82 55 07 FF 81 54 06 FF 80 53' + '05 FF 7F 52 05 FF 7F 52 05 FF 7F 51 04 FF 7F 51' + '04 FF 7F 51 04 FF 7F 52 05 FF 80 53 05 FF 80 53' + '06 FF 81 54 06 FF 81 55 07 FF 82 55 07 FF 82 56' + '08 FF 83 57 08 FF 83 58 08 FF 84 58 09 FF 6A 4F' + '1F FF 90 90 90 FF 5C 5C 5C FF 33 33 33 FF 33 33' + '33 FF 7C 7C 7C FF 8A 8A 8A FF 9C 9C 9C FF A5 A5' + 'A5 FF 43 43 43 FF 39 39 39 FF 39 39 39 FF 3A 3A' + '3A FF 3B 3B 3B FF 3B 3B 3B FF 3C 3C 3C FF 3C 3C' + '3C FF 3D 3D 3D FF 3D 3D 3D FF 3D 3D 3D FF 3E 3E' + '3E FF 3E 3E 3E FF 3F 3F 3F FF 3F 3F 3F FF 90 90' + '90 FF CB CB CB E5 40 40 40 A2 77 77 77 FF DB DB' + 'DB FF 9F 91 74 FF 95 6F 17 FF 95 6F 17 FF 94 6E' + '17 FF 93 6D 16 FF 92 6B 15 FF 91 6A 14 FF 90 68' + '13 FF 8F 67 12 FF 8D 65 11 FF 8C 63 0F FF 8A 61' + '0E FF 88 5E 0D FF 87 5C 0B FF 85 5A 0A FF 84 58' + '09 FF 83 57 08 FF 82 56 07 FF 81 55 07 FF 81 54' + '06 FF 81 54 06 FF 80 54 06 FF 80 53 06 FF 80 54' + '06 FF 81 54 06 FF 82 55 07 FF 82 56 07 FF 82 56' + '08 FF 83 57 08 FF 84 58 08 FF 84 58 08 FF 84 58' + '09 FF 85 59 0A FF 85 5A 0A FF 86 5B 0B FF 6B 51' + '24 FF 97 97 97 FF 57 57 57 FF 33 33 33 FF 32 32' + '32 FF 7E 7E 7E FF 8C 8C 8C FF 9E 9E 9E FF A7 A7' + 'A7 FF 44 44 44 FF 3A 3A 3A FF 3B 3B 3B FF 3B 3B' + '3B FF 3C 3C 3C FF 3C 3C 3C FF 3D 3D 3D FF 3D 3D' + '3D FF 3E 3E 3E FF 3F 3F 3F FF 3E 3E 3E FF 3F 3F' + '3F FF 3F 3F 3F FF 3F 3F 3F FF 40 40 40 FF 91 91' + '91 FF CF CF CF E5 46 46 46 AB 78 78 78 FF D8 D8' + 'D8 FF 9C 8D 6F FF 91 6A 14 FF 91 6A 14 FF 91 69' + '14 FF 90 68 13 FF 8F 67 12 FF 8E 66 12 FF 8D 65' + '11 FF 8C 64 10 FF 8B 62 0F FF 8A 60 0E FF 88 5E' + '0C FF 87 5C 0B FF 85 5A 0A FF 84 58 09 FF 83 58' + '08 FF 82 57 08 FF 82 56 07 FF 82 55 07 FF 82 55' + '07 FF 82 55 07 FF 82 56 07 FF 82 56 07 FF 82 56' + '07 FF 83 57 08 FF 84 58 09 FF 84 59 09 FF 85 59' + '09 FF 85 5A 0A FF 86 5A 0A FF 86 5B 0A FF 86 5B' + '0B FF 87 5D 0C FF 88 5E 0C FF 88 5E 0D FF 6C 53' + '29 FF 9E 9E 9E FF 54 54 54 FF 32 32 32 FF 31 31' + '31 FF 7F 7F 7F FF 8E 8E 8E FF A0 A0 A0 FF AA AA' + 'AA FF 45 45 45 FF 3B 3B 3B FF 3B 3B 3B FF 3C 3C' + '3C FF 3D 3D 3D FF 3D 3D 3D FF 3E 3E 3E FF 3E 3E' + '3E FF 3E 3E 3E FF 3F 3F 3F FF 40 40 40 FF 40 40' + '40 FF 41 41 41 FF 40 40 40 FF 40 40 40 FF 93 93' + '93 FF D1 D1 D1 E5 44 44 44 AC 78 78 78 FF D4 D4' + 'D4 FF 99 89 6A FF 8E 66 11 FF 8E 65 11 FF 8D 65' + '11 FF 8D 64 10 FF 8C 63 10 FF 8B 62 0F FF 8A 61' + '0E FF 89 60 0D FF 88 5E 0D FF 87 5D 0C FF 86 5B' + '0B FF 85 5A 0A FF 84 58 09 FF 83 57 08 FF 82 57' + '08 FF 82 57 08 FF 83 57 08 FF 83 57 08 FF 83 57' + '08 FF 83 57 08 FF 84 58 08 FF 84 58 09 FF 84 59' + '09 FF 85 5A 0A FF 86 5B 0B FF 87 5C 0B FF 87 5C' + '0C FF 88 5D 0C FF 88 5F 0D FF 89 5F 0D FF 89 5F' + '0D FF 8A 61 0E FF 8B 61 0F FF 8B 62 0F FF 6B 54' + '2D FF A8 A8 A8 FF 4F 4F 4F FF 31 31 31 FF 31 31' + '31 FF 81 81 81 FF 90 90 90 FF A2 A2 A2 FF AB AB' + 'AB FF 46 46 46 FF 3C 3C 3C FF 3B 3B 3B FF 3C 3C' + '3C FF 37 37 37 FF 39 39 39 FF 37 37 37 FF 37 37' + '37 FF 37 37 37 FF 37 37 37 FF 3E 3E 3E FF 3F 3F' + '3F FF 3F 3F 3F FF 40 40 40 FF 41 41 41 FF 95 95' + '95 FF D4 D4 D4 E5 42 42 42 AC 79 79 79 FF CF CF' + 'CF FF 96 85 64 FF 8A 61 0E FF 8A 61 0E FF 8A 60' + '0E FF 89 60 0D FF 89 5F 0D FF 88 5E 0D FF 88 5E' + '0C FF 87 5D 0C FF 87 5C 0B FF 86 5A 0B FF 85 59' + '0A FF 84 58 09 FF 84 58 08 FF 83 57 08 FF 83 57' + '08 FF 83 57 08 FF 84 58 08 FF 84 58 09 FF 84 58' + '09 FF 85 59 09 FF 85 5A 0A FF 86 5B 0A FF 86 5C' + '0B FF 87 5D 0C FF 88 5E 0C FF 89 5F 0D FF 8A 60' + '0E FF 8B 62 0F FF 8B 62 0F FF 8C 63 0F FF 8C 64' + '10 FF 8D 65 10 FF 8D 65 11 FF 8E 66 11 FF 67 54' + '32 FF B2 B2 B2 FF 48 48 48 FF 31 31 31 FF 32 32' + '32 FF 83 83 83 FF 93 93 93 FF A4 A4 A4 FF AE AE' + 'AE FF 47 47 47 FF 3D 3D 3D FF 3C 3C 3C FF 3E 3E' + '3E FF 42 42 42 FF 41 41 41 FF 48 48 48 FF 4A 4A' + '4A FF 4B 4B 4B FF 4A 4A 4A FF 40 40 40 FF 3F 3F' + '3F FF 40 40 40 FF 42 42 42 FF 42 42 42 FF 97 97' + '97 FF D8 D8 D8 E5 41 41 41 AC 79 79 79 FF CB CB' + 'CB FF 91 7F 5F FF 87 5C 0B FF 87 5C 0B FF 86 5C' + '0B FF 86 5B 0B FF 86 5B 0B FF 86 5B 0B FF 85 5A' + '0A FF 85 5A 0A FF 85 59 0A FF 84 58 09 FF 84 58' + '09 FF 84 58 08 FF 83 58 08 FF 83 57 08 FF 83 57' + '08 FF 84 58 09 FF 85 58 09 FF 85 59 0A FF 85 5A' + '0A FF 86 5B 0B FF 87 5D 0C FF 88 5E 0C FF 89 5F' + '0D FF 8A 61 0E FF 8B 61 0F FF 8C 63 10 FF 8D 64' + '10 FF 8E 66 11 FF 8E 66 11 FF 8F 67 12 FF 8F 68' + '13 FF 90 68 13 FF 90 69 13 FF 91 69 14 FF 6B 5A' + '3B FF BA BA BA FF 42 42 42 FF 32 32 32 FF 32 32' + '32 FF 84 84 84 FF 95 95 95 FF A6 A6 A6 FF AF AF' + 'AF FF 48 48 48 FF 3D 3D 3D FF 3D 3D 3D FF 3D 3D' + '3D FF 3D 3D 3D FF 3F 3F 3F FF 40 40 40 FF 40 40' + '40 FF 40 40 40 FF 40 40 40 FF 40 40 40 FF 41 41' + '41 FF 40 40 40 FF 42 42 42 FF 43 43 43 FF 99 99' + '99 FF DB DB DB E5 3F 3F 3F AC 79 79 79 FF C7 C7' + 'C7 FF 8F 7C 5B FF 84 59 09 FF 84 58 09 FF 84 58' + '09 FF 84 58 08 FF 83 58 08 FF 83 57 08 FF 83 57' + '08 FF 83 57 08 FF 84 57 08 FF 83 57 08 FF 83 57' + '08 FF 83 57 08 FF 83 57 08 FF 83 58 08 FF 84 58' + '09 FF 85 59 0A FF 86 5B 0B FF 87 5C 0B FF 87 5D' + '0C FF 88 5E 0D FF 8A 60 0E FF 8B 61 0F FF 8C 63' + '10 FF 8D 65 10 FF 8E 66 11 FF 8F 68 13 FF 90 68' + '13 FF 91 6A 14 FF 91 6A 14 FF 92 6B 15 FF 92 6C' + '15 FF 93 6D 16 FF 93 6D 16 FF 93 6D 16 FF 72 62' + '45 FF BE BE BE FF 3B 3B 3B FF 31 31 31 FF 32 32' + '32 FF 86 86 86 FF 97 97 97 FF A8 A8 A8 FF B2 B2' + 'B2 FF 49 49 49 FF 3E 3E 3E FF 3F 3F 3F FF 40 40' + '40 FF 40 40 40 FF 40 40 40 FF 41 41 41 FF 41 41' + '41 FF 42 42 42 FF 41 41 41 FF 41 41 41 FF 42 42' + '42 FF 43 43 43 FF 44 44 44 FF 44 44 44 FF 9B 9B' + '9B FF DD DD DD E5 3E 3E 3E AF 79 79 79 FF C2 C2' + 'C2 FF 8C 79 58 FF 82 56 07 FF 82 56 07 FF 82 55' + '07 FF 81 55 07 FF 81 55 07 FF 82 55 07 FF 81 55' + '07 FF 82 56 07 FF 82 56 07 FF 82 56 07 FF 82 56' + '07 FF 83 57 08 FF 83 57 08 FF 84 58 09 FF 85 59' + '0A FF 86 5B 0B FF 87 5D 0C FF 88 5F 0D FF 89 60' + '0E FF 8B 62 0F FF 8C 64 10 FF 8D 65 11 FF 8F 67' + '12 FF 90 68 13 FF 91 6A 14 FF 92 6C 15 FF 93 6D' + '16 FF 94 6E 17 FF 95 6F 17 FF 95 70 18 FF 96 71' + '18 FF 96 71 18 FF 97 72 19 FF 97 72 19 FF 77 69' + '50 FF C1 C1 C1 FF 36 36 36 FF 32 32 32 FF 32 32' + '32 FF 87 87 87 FF 98 98 98 FF AA AA AA FF B4 B4' + 'B4 FF 4A 4A 4A FF 3D 3D 3D FF 3F 3F 3F FF 3F 3F' + '3F FF 40 40 40 FF 40 40 40 FF 41 41 41 FF 42 42' + '42 FF 43 43 43 FF 44 44 44 FF 45 45 45 FF 45 45' + '45 FF 46 46 46 FF 45 45 45 FF 47 47 47 FF 9E 9E' + '9E FF E3 E3 E3 E5 43 43 43 B6 79 79 79 FF BD BD' + 'BD FF 89 75 54 FF 80 53 05 FF 7F 52 05 FF 7F 52' + '05 FF 7F 52 05 FF 80 53 05 FF 7F 53 05 FF 80 53' + '06 FF 81 54 06 FF 81 55 07 FF 81 55 07 FF 82 56' + '07 FF 83 57 08 FF 84 57 08 FF 85 59 0A FF 86 5B' + '0B FF 87 5D 0C FF 89 5F 0D FF 8B 61 0E FF 8C 64' + '10 FF 8E 66 11 FF 8F 67 12 FF 90 68 13 FF 91 6A' + '14 FF 93 6C 15 FF 94 6E 17 FF 96 71 18 FF 97 72' + '19 FF 98 73 19 FF 98 74 1A FF 99 75 1B FF 9A 76' + '1B FF 9A 76 1B FF 9A 76 1B FF 9A 76 1B FF 7A 70' + '5C FF BF BF BF FF 32 32 32 FF 32 32 32 FF 33 33' + '33 FF 89 89 89 FF 9A 9A 9A FF AC AC AC FF B6 B6' + 'B6 FF 4C 4C 4C FF 40 40 40 FF 41 41 41 FF 41 41' + '41 FF 41 41 41 FF 42 42 42 FF 42 42 42 FF 43 43' + '43 FF 44 44 44 FF 45 45 45 FF 45 45 45 FF 45 45' + '45 FF 46 46 46 FF 48 48 48 FF 49 49 49 FF 9F 9F' + '9F FF E2 E2 E2 E5 41 41 41 B6 7A 7A 7A FF B8 B8' + 'B8 FF 86 71 4F FF 7E 50 04 FF 7D 4F 03 FF 7D 4F' + '03 FF 7E 4F 03 FF 7E 50 04 FF 7E 51 04 FF 7F 51' + '04 FF 7F 52 05 FF 80 54 06 FF 81 55 07 FF 82 56' + '08 FF 83 57 08 FF 85 59 09 FF 86 5B 0B FF 88 5D' + '0C FF 8A 60 0E FF 8B 62 0F FF 8D 65 10 FF 8F 67' + '12 FF 90 69 13 FF 92 6B 15 FF 93 6C 15 FF 95 6F' + '17 FF 96 71 18 FF 98 73 1A FF 99 75 1B FF 9A 76' + '1B FF 9B 78 1C FF 9C 78 1D FF 9C 79 1D FF 9D 7A' + '1E FF 9D 7A 1E FF 9D 7A 1E FF 9D 7A 1E FF 80 77' + '68 FF BA BA BA FF 33 33 33 FF 33 33 33 FF 33 33' + '33 FF 8A 8A 8A FF 9B 9B 9B FF AD AD AD FF B8 B8' + 'B8 FF 4D 4D 4D FF 40 40 40 FF 41 41 41 FF 41 41' + '41 FF 42 42 42 FF 43 43 43 FF 44 44 44 FF 44 44' + '44 FF 45 45 45 FF 46 46 46 FF 46 46 46 FF 47 47' + '47 FF 49 49 49 FF 4A 4A 4A FF 4A 4A 4A FF A0 A0' + 'A0 FF E3 E3 E3 E5 3F 3F 3F B6 7A 7A 7A FF B4 B4' + 'B4 FF 83 6E 4C FF 7C 4D 02 FF 7C 4D 02 FF 7C 4D' + '02 FF 7C 4D 02 FF 7D 4E 03 FF 7E 4F 03 FF 7E 50' + '04 FF 7F 52 05 FF 80 54 06 FF 81 55 07 FF 83 57' + '08 FF 84 58 09 FF 86 5B 0B FF 88 5D 0C FF 8A 60' + '0E FF 8C 63 0F FF 8E 66 11 FF 90 68 13 FF 91 6A' + '14 FF 93 6D 16 FF 95 6F 17 FF 96 71 18 FF 98 74' + '1A FF 9A 76 1B FF 9B 77 1C FF 9C 79 1E FF 9E 7B' + '1F FF 9E 7C 1F FF 9F 7D 20 FF A0 7E 20 FF A0 7F' + '21 FF A1 80 21 FF A1 7F 21 FF A0 7E 20 FF 88 80' + '73 FF B5 B5 B5 FF 33 33 33 FF 33 33 33 FF 33 33' + '33 FF 8B 8B 8B FF 9D 9D 9D FF AF AF AF FF B9 B9' + 'B9 FF 4F 4F 4F FF 41 41 41 FF 42 42 42 FF 42 42' + '42 FF 43 43 43 FF 43 43 43 FF 44 44 44 FF 45 45' + '45 FF 45 45 45 FF 46 46 46 FF 47 47 47 FF 49 49' + '49 FF 4A 4A 4A FF 4C 4C 4C FF 4D 4D 4D FF A1 A1' + 'A1 FF E2 E2 E2 E5 44 44 44 BF 7A 7A 7A FF B1 B1' + 'B1 FF 81 6B 49 FF 7A 4B 01 FF 7A 4B 00 FF 7B 4B' + '01 FF 7B 4C 02 FF 7C 4E 02 FF 7D 4F 03 FF 7E 50' + '04 FF 7F 52 05 FF 81 54 06 FF 82 56 07 FF 84 58' + '09 FF 85 5A 0A FF 87 5D 0C FF 89 60 0D FF 8C 63' + '0F FF 8E 66 11 FF 90 68 13 FF 92 6B 15 FF 94 6E' + '17 FF 96 71 18 FF 98 73 1A FF 9A 76 1B FF 9B 78' + '1C FF 9D 7A 1E FF 9E 7C 1F FF A0 7E 20 FF A1 80' + '21 FF A2 81 22 FF A3 82 23 FF A4 83 24 FF A4 84' + '24 FF A4 84 24 FF A4 84 24 FF A2 81 22 FF 8E 87' + '7E FF B1 B1 B1 FF 33 33 33 FF 33 33 33 FF 34 34' + '34 FF 8D 8D 8D FF 9F 9F 9F FF B1 B1 B1 FF BB BB' + 'BB FF 50 50 50 FF 42 42 42 FF 43 43 43 FF 44 44' + '44 FF 44 44 44 FF 45 45 45 FF 46 46 46 FF 46 46' + '46 FF 47 47 47 FF 49 49 49 FF 4B 4B 4B FF 4C 4C' + '4C FF 4D 4D 4D FF 4E 4E 4E FF 50 50 50 FF A2 A2' + 'A2 FF E2 E2 E2 E5 42 42 42 C0 7A 7A 7A FF AD AD' + 'AD FF 7E 68 45 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 7A 4B 01 FF 7C 4D 02 FF 7D 4F 03 FF 7E 50' + '04 FF 7F 52 05 FF 81 55 07 FF 83 57 08 FF 85 59' + '09 FF 86 5B 0B FF 88 5E 0D FF 8B 62 0F FF 8D 65' + '11 FF 90 68 13 FF 92 6B 15 FF 94 6F 17 FF 97 72' + '19 FF 99 75 1B FF 9B 77 1C FF 9D 7A 1E FF 9E 7C' + '1F FF A0 7E 21 FF A2 81 22 FF A3 83 23 FF A5 85' + '25 FF A6 86 26 FF A7 88 26 FF A8 89 27 FF A8 89' + '28 FF A8 8A 28 FF A8 89 27 FF A3 82 23 FF 93 90' + '8A FF AA AA AA FF 33 33 33 FF 34 34 34 FF 34 34' + '34 FF 8D 8D 8D FF A0 A0 A0 FF B2 B2 B2 FF BD BD' + 'BD FF 51 51 51 FF 40 40 40 FF 41 41 41 FF 42 42' + '42 FF 43 43 43 FF 43 43 43 FF 44 44 44 FF 46 46' + '46 FF 47 47 47 FF 49 49 49 FF 4A 4A 4A FF 4B 4B' + '4B FF 4D 4D 4D FF 4E 4E 4E FF 50 50 50 FF A2 A2' + 'A2 FF E0 E0 E0 E5 41 41 41 C0 7A 7A 7A FF A8 A8' + 'A8 FF 7E 66 41 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 7A 4A 00 FF 7B 4C 02 FF 7D 4F 03 FF 7E 50' + '04 FF 80 53 05 FF 82 55 07 FF 84 58 08 FF 85 5A' + '0A FF 87 5D 0C FF 8A 60 0E FF 8D 64 10 FF 8F 67' + '12 FF 92 6B 15 FF 94 6E 17 FF 97 72 19 FF 99 75' + '1B FF 9C 79 1D FF 9E 7C 1F FF A0 7E 20 FF A2 81' + '22 FF A4 83 24 FF A6 86 25 FF A7 88 27 FF A9 8A' + '28 FF AA 8C 29 FF AB 8E 2A FF AC 8F 2B FF AD 8F' + '2B FF AC 8F 2B FF AC 8F 2B FF A4 84 24 FF 99 99' + '98 FF A2 A2 A2 FF 33 33 33 FF 34 34 34 FF 35 35' + '35 FF 8F 8F 8F FF A1 A1 A1 FF B4 B4 B4 FF BE BE' + 'BE FF 55 55 55 FF 42 42 42 FF 44 44 44 FF 45 45' + '45 FF 45 45 45 FF 46 46 46 FF 47 47 47 FF 48 48' + '48 FF 4A 4A 4A FF 4C 4C 4C FF 4E 4E 4E FF 50 50' + '50 FF 51 51 51 FF 52 52 52 FF 53 53 53 FF A3 A3' + 'A3 FF E2 E2 E2 E5 3F 3F 3F C0 79 79 79 FF A5 A5' + 'A5 FF 7E 65 3F FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 4A 00 FF 7B 4C 02 FF 7D 4E 03 FF 7E 51' + '04 FF 80 53 06 FF 82 56 07 FF 84 58 09 FF 86 5B' + '0B FF 89 5F 0D FF 8B 63 0F FF 8E 66 12 FF 91 6A' + '14 FF 94 6E 16 FF 97 72 18 FF 99 75 1B FF 9C 79' + '1D FF 9E 7C 1F FF A1 7F 21 FF A3 82 23 FF A5 85' + '25 FF A7 88 27 FF AA 8B 29 FF AB 8D 2A FF AD 8F' + '2B FF AE 91 2C FF AF 93 2D FF B0 94 2E FF B0 94' + '2E FF B0 94 2E FF B0 94 2E FF A3 84 26 FF A4 A4' + 'A3 FF 9E 9E 9E FF 34 34 34 FF 34 34 34 FF 35 35' + '35 FF 8F 8F 8F FF A2 A2 A2 FF B5 B5 B5 FF BF BF' + 'BF FF 56 56 56 FF 43 43 43 FF 44 44 44 FF 44 44' + '44 FF 46 46 46 FF 47 47 47 FF 49 49 49 FF 4B 4B' + '4B FF 4C 4C 4C FF 4E 4E 4E FF 50 50 50 FF 51 51' + '51 FF 53 53 53 FF 54 54 54 FF 55 55 55 FF A4 A4' + 'A4 FF E0 E0 E0 E5 3D 3D 3D C0 79 79 79 FF A1 A1' + 'A1 FF 7E 64 3C FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4B 01 FF 7C 4E 03 FF 7E 51' + '04 FF 80 53 06 FF 83 57 08 FF 85 59 0A FF 88 5D' + '0C FF 8A 61 0E FF 8D 65 10 FF 90 68 13 FF 93 6C' + '15 FF 96 71 18 FF 99 75 1B FF 9C 78 1D FF 9E 7C' + '1F FF A0 7F 21 FF A3 83 23 FF A6 86 25 FF A9 8A' + '28 FF AB 8D 2A FF AD 90 2B FF AE 92 2D FF B0 94' + '2E FF B1 96 2F FF B3 98 30 FF B4 99 31 FF B4 9A' + '31 FF B4 99 31 FF B3 98 31 FF A1 84 28 FF AD AD' + 'AD FF 94 94 94 FF 34 34 34 FF 35 35 35 FF 35 35' + '35 FF 90 90 90 FF A3 A3 A3 FF B6 B6 B6 FF C1 C1' + 'C1 FF 57 57 57 FF 44 44 44 FF 45 45 45 FF 46 46' + '46 FF 47 47 47 FF 48 48 48 FF 4B 4B 4B FF 4D 4D' + '4D FF 4F 4F 4F FF 50 50 50 FF 52 52 52 FF 53 53' + '53 FF 54 54 54 FF 56 56 56 FF 58 58 58 FF A5 A5' + 'A5 FF E2 E2 E2 E5 3D 3D 3D C0 78 78 78 FF 9E 9E' + '9E FF 7F 63 3A FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4B 01 FF 7D 4E 03 FF 7E 51' + '04 FF 81 54 06 FF 83 58 08 FF 86 5B 0B FF 89 5F' + '0D FF 8B 63 0F FF 8E 66 11 FF 91 6A 14 FF 94 6E' + '17 FF 97 73 19 FF 9A 77 1C FF 9D 7B 1E FF A0 7E' + '21 FF A3 82 23 FF A6 86 26 FF A9 8A 28 FF AC 8E' + '2A FF AE 91 2C FF B0 94 2E FF B2 96 30 FF B4 99' + '31 FF B6 9C 33 FF B7 9E 34 FF B8 9F 35 FF B8 A0' + '35 FF B8 9F 35 FF B8 9F 35 FF 9F 85 2D FF B6 B6' + 'B5 FF 90 90 90 FF 35 35 35 FF 35 35 35 FF 36 36' + '36 FF 91 91 91 FF A4 A4 A4 FF B8 B8 B8 FF C2 C2' + 'C2 FF 57 57 57 FF 45 45 45 FF 46 46 46 FF 47 47' + '47 FF 49 49 49 FF 4C 4C 4C FF 4D 4D 4D FF 4F 4F' + '4F FF 50 50 50 FF 52 52 52 FF 54 54 54 FF 55 55' + '55 FF 56 56 56 FF 58 58 58 FF 59 59 59 FF A6 A6' + 'A6 FF E0 E0 E0 E5 3D 3D 3D C4 77 77 77 FF 9C 9C' + '9C FF 7E 62 38 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4B 01 FF 7C 4E 03 FF 7F 51' + '04 FF 81 55 07 FF 84 58 09 FF 86 5C 0B FF 89 60' + '0D FF 8D 64 10 FF 8F 67 12 FF 92 6C 15 FF 96 70' + '18 FF 99 75 1A FF 9C 79 1D FF 9F 7D 20 FF A2 81' + '22 FF A5 85 25 FF A9 8A 28 FF AC 8E 2A FF AE 91' + '2C FF B1 95 2F FF B4 99 31 FF B6 9C 33 FF B8 9F' + '35 FF BA A2 36 FF BC A4 38 FF BD A5 39 FF BD A5' + '39 FF BD A5 39 FF BC A4 38 FF 9C 85 32 FF BE BE' + 'BE FF 89 89 89 FF 35 35 35 FF 36 36 36 FF 36 36' + '36 FF 91 91 91 FF A5 A5 A5 FF B9 B9 B9 FF C3 C3' + 'C3 FF 57 57 57 FF 44 44 44 FF 45 45 45 FF 47 47' + '47 FF 48 48 48 FF 4B 4B 4B FF 4D 4D 4D FF 4F 4F' + '4F FF 50 50 50 FF 53 53 53 FF 55 55 55 FF 56 56' + '56 FF 57 57 57 FF 59 59 59 FF 5B 5B 5B FF A6 A6' + 'A6 FF E0 E0 E0 E5 40 40 40 CA 77 77 77 FF 9A 9A' + '9A FF 7F 62 36 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7A 4B 01 FF 7D 4E 03 FF 7F 52' + '05 FF 82 56 07 FF 85 59 09 FF 87 5D 0C FF 8A 61' + '0E FF 8E 65 11 FF 90 69 13 FF 94 6E 16 FF 97 72' + '19 FF 9A 77 1C FF 9E 7B 1F FF A1 80 21 FF A4 84' + '24 FF A8 88 27 FF AB 8D 2A FF AE 91 2C FF B1 95' + '2F FF B4 99 31 FF B7 9D 34 FF B9 A1 36 FF BC A4' + '38 FF BE A7 3A FF C0 AA 3C FF C2 AC 3D FF C2 AD' + '3D FF C2 AC 3D FF C1 AB 3C FF 9C 86 35 FF C5 C5' + 'C5 FF 84 84 84 FF 35 35 35 FF 36 36 36 FF 37 37' + '37 FF 91 91 91 FF A6 A6 A6 FF BA BA BA FF C5 C5' + 'C5 FF 5A 5A 5A FF 47 47 47 FF 48 48 48 FF 4A 4A' + '4A FF 4D 4D 4D FF 4F 4F 4F FF 50 50 50 FF 52 52' + '52 FF 54 54 54 FF 56 56 56 FF 58 58 58 FF 59 59' + '59 FF 5B 5B 5B FF 5C 5C 5C FF 5E 5E 5E FF A6 A6' + 'A6 FF E3 E3 E3 E5 40 40 40 CA 76 76 76 FF 98 98' + '98 FF 7E 60 34 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7A 4B 01 FF 7D 4E 03 FF 7F 52' + '05 FF 82 56 07 FF 85 59 0A FF 88 5D 0C FF 8B 62' + '0F FF 8E 66 12 FF 91 6A 14 FF 95 6F 17 FF 98 74' + '1A FF 9B 78 1D FF 9F 7C 1F FF A2 81 22 FF A6 86' + '25 FF A9 8B 28 FF AD 8F 2B FF B0 93 2E FF B3 98' + '30 FF B6 9D 33 FF B9 A1 36 FF BC A5 39 FF C0 A9' + '3B FF C3 AD 3E FF C4 B0 40 FF C6 B2 40 FF C6 B2' + '41 FF C6 B2 41 FF C5 B1 40 FF 9B 86 38 FF CE CE' + 'CE FF 72 72 72 FF 36 36 36 FF 36 36 36 FF 37 37' + '37 FF 91 91 91 FF A7 A7 A7 FF BB BB BB FF C5 C5' + 'C5 FF 5B 5B 5B FF 47 47 47 FF 4A 4A 4A FF 4C 4C' + '4C FF 4D 4D 4D FF 50 50 50 FF 52 52 52 FF 54 54' + '54 FF 56 56 56 FF 58 58 58 FF 59 59 59 FF 5B 5B' + '5B FF 5D 5D 5D FF 5E 5E 5E FF 60 60 60 FF A7 A7' + 'A7 FF E2 E2 E2 E5 40 40 40 CA 75 75 75 FF 97 97' + '97 FF 7E 5F 33 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7A 4B 01 FF 7D 4F 03 FF 7F 52' + '05 FF 83 56 08 FF 85 5A 0A FF 88 5E 0C FF 8C 63' + '0F FF 8F 67 12 FF 92 6B 15 FF 96 70 18 FF 99 75' + '1B FF 9D 79 1E FF A0 7E 20 FF A3 83 23 FF A7 88' + '27 FF AB 8D 2A FF AE 91 2C FF B2 96 2F FF B6 9B' + '33 FF B8 A0 35 FF BC A4 38 FF BF A9 3B FF C3 AE' + '3E FF C6 B1 40 FF C7 B3 42 FF C8 B5 43 FF C9 B6' + '43 FF C9 B6 43 FF C8 B5 43 FF 96 83 39 FF D6 D6' + 'D6 FF 6A 6A 6A FF 36 36 36 FF 37 37 37 FF 37 37' + '37 FF 91 91 91 FF A8 A8 A8 FF BC BC BC FF C6 C6' + 'C6 FF 5D 5D 5D FF 49 49 49 FF 4B 4B 4B FF 4D 4D' + '4D FF 4F 4F 4F FF 51 51 51 FF 53 53 53 FF 55 55' + '55 FF 56 56 56 FF 59 59 59 FF 5B 5B 5B FF 5D 5D' + '5D FF 5F 5F 5F FF 60 60 60 FF 62 62 62 FF A7 A7' + 'A7 FF E3 E3 E3 E5 44 44 44 D4 74 74 74 FF 96 96' + '96 FF 7D 5E 31 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7D 4F 03 FF 80 53' + '06 FF 83 57 08 FF 86 5B 0B FF 89 5F 0D FF 8C 64' + '10 FF 8F 68 13 FF 93 6C 15 FF 96 71 18 FF 9A 76' + '1B FF 9D 7A 1E FF A0 7F 21 FF A4 84 24 FF A8 89' + '28 FF AC 8E 2A FF AF 93 2D FF B3 98 31 FF B7 9E' + '34 FF BB A2 37 FF BE A7 3A FF C3 AD 3E FF C6 B1' + '40 FF C8 B4 42 FF CA B7 44 FF CB B9 45 FF CC BA' + '45 FF CB B9 45 FF CB B8 45 FF 8F 7F 3C FF DE DE' + 'DE FF 65 65 65 FF 36 36 36 FF 37 37 37 FF 38 38' + '38 FF 90 90 90 FF A9 A9 A9 FF BD BD BD FF C7 C7' + 'C7 FF 61 61 61 FF 4A 4A 4A FF 4D 4D 4D FF 4F 4F' + '4F FF 51 51 51 FF 54 54 54 FF 56 56 56 FF 57 57' + '57 FF 59 59 59 FF 5A 5A 5A FF 5C 5C 5C FF 5D 5D' + '5D FF 5F 5F 5F FF 61 61 61 FF 62 62 62 FF A8 A8' + 'A8 FF E2 E2 E2 E5 42 42 42 D4 73 73 73 FF 93 93' + '93 FF 7B 5D 2F FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7E 50 03 FF 80 53' + '06 FF 83 57 08 FF 86 5B 0B FF 89 5F 0D FF 8C 64' + '10 FF 90 68 13 FF 93 6D 16 FF 97 72 19 FF 9A 77' + '1C FF 9E 7B 1F FF A1 80 21 FF A5 85 25 FF A9 8A' + '28 FF AD 8F 2B FF B0 94 2E FF B4 9A 32 FF B9 A0' + '35 FF BC A4 38 FF C0 AA 3C FF C4 AF 3F FF C7 B3' + '41 FF C9 B6 43 FF CC B9 45 FF CD BC 47 FF CD BB' + '47 FF CD BB 46 FF CC BB 46 FF 8C 7B 3D FF E6 E6' + 'E6 FF 5C 5C 5C FF 37 37 37 FF 37 37 37 FF 38 38' + '38 FF 90 90 90 FF A9 A9 A9 FF BD BD BD FF C8 C8' + 'C8 FF 63 63 63 FF 4C 4C 4C FF 4D 4D 4D FF 50 50' + '50 FF 51 51 51 FF 53 53 53 FF 55 55 55 FF 57 57' + '57 FF 5A 5A 5A FF 5C 5C 5C FF 5F 5F 5F FF 61 61' + '61 FF 63 63 63 FF 64 64 64 FF 66 66 66 FF A8 A8' + 'A8 FF E3 E3 E3 E5 3F 3F 3F D4 73 73 73 FF 92 92' + '92 FF 7A 5B 2C FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4B 01 FF 7D 4F 03 FF 80 53' + '06 FF 83 57 08 FF 86 5B 0B FF 89 5F 0D FF 8C 64' + '10 FF 90 68 13 FF 93 6D 16 FF 97 72 19 FF 9B 77' + '1C FF 9E 7C 1F FF A1 80 22 FF A5 86 25 FF A9 8A' + '28 FF AD 90 2B FF B1 95 2F FF B5 9B 32 FF B9 A0' + '36 FF BD A5 39 FF C1 AB 3D FF C5 B1 40 FF C8 B4' + '42 FF CB B8 45 FF CD BB 46 FF CE BD 48 FF CF BE' + '48 FF CF BE 48 FF CF BD 48 FF 88 78 3D FF ED ED' + 'ED FF 57 57 57 FF 37 37 37 FF 37 37 37 FF 38 38' + '38 FF 8F 8F 8F FF A9 A9 A9 FF BE BE BE FF C9 C9' + 'C9 FF 66 66 66 FF 4D 4D 4D FF 50 50 50 FF 52 52' + '52 FF 54 54 54 FF 57 57 57 FF 58 58 58 FF 5B 5B' + '5B FF 5C 5C 5C FF 5F 5F 5F FF 60 60 60 FF 63 63' + '63 FF 64 64 64 FF 66 66 66 FF 68 68 68 FF A8 A8' + 'A8 FF E2 E2 E2 E5 3E 3E 3E D4 72 72 72 FF 91 91' + '91 FF 79 59 2A FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4B 01 FF 7D 4F 03 FF 80 53' + '06 FF 83 58 08 FF 86 5C 0B FF 89 5F 0D FF 8D 65' + '10 FF 90 68 13 FF 93 6D 16 FF 97 72 19 FF 9B 77' + '1C FF 9E 7C 1F FF A1 80 22 FF A6 86 25 FF A9 8A' + '28 FF AD 90 2B FF B1 95 2F FF B5 9B 33 FF B9 A0' + '36 FF BD A6 39 FF C2 AC 3D FF C6 B2 40 FF C9 B5' + '43 FF CB B9 45 FF CE BC 47 FF CF BE 48 FF CF BE' + '48 FF D0 BF 49 FF D0 BF 49 FF 86 76 41 FF EF EF' + 'EF FF 52 52 52 FF 37 37 37 FF 38 38 38 FF 39 39' + '39 FF 8F 8F 8F FF A9 A9 A9 FF BF BF BF FF C9 C9' + 'C9 FF 69 69 69 FF 4F 4F 4F FF 51 51 51 FF 53 53' + '53 FF 55 55 55 FF 58 58 58 FF 5A 5A 5A FF 5C 5C' + '5C FF 5E 5E 5E FF 60 60 60 FF 62 62 62 FF 64 64' + '64 FF 66 66 66 FF 68 68 68 FF 6A 6A 6A FF A8 A8' + 'A8 FF E3 E3 E3 E5 3C 3C 3C D4 71 71 71 FF 8F 8F' + '8F FF 78 58 29 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7B 4C 01 FF 7D 4F 03 FF 80 53' + '06 FF 84 58 09 FF 86 5B 0B FF 89 5F 0D FF 8D 64' + '10 FF 90 68 13 FF 93 6D 16 FF 97 72 19 FF 9B 77' + '1C FF 9E 7C 1F FF A1 80 22 FF A5 85 25 FF A8 8A' + '28 FF AD 90 2B FF B1 95 2F FF B5 9A 32 FF B8 A0' + '35 FF BC A5 39 FF C1 AB 3D FF C5 B1 40 FF C8 B5' + '43 FF CB B8 45 FF CD BC 47 FF CF BE 48 FF D0 BF' + '49 FF D0 BF 49 FF D0 C0 49 FF 84 77 49 FF EF EF' + 'EF FF 4A 4A 4A FF 37 37 37 FF 38 38 38 FF 39 39' + '39 FF 8F 8F 8F FF AA AA AA FF C0 C0 C0 FF C9 C9' + 'C9 FF 6B 6B 6B FF 50 50 50 FF 52 52 52 FF 54 54' + '54 FF 56 56 56 FF 59 59 59 FF 5B 5B 5B FF 5D 5D' + '5D FF 5F 5F 5F FF 61 61 61 FF 63 63 63 FF 65 65' + '65 FF 67 67 67 FF 69 69 69 FF 6B 6B 6B FF A9 A9' + 'A9 FF E2 E2 E2 E5 3A 3A 3A D4 70 70 70 FF 8D 8D' + '8D FF 77 57 26 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7A 4B 01 FF 7D 4F 03 FF 80 53' + '05 FF 83 57 08 FF 86 5A 0A FF 89 5F 0D FF 8C 64' + '10 FF 8F 68 13 FF 93 6C 15 FF 96 71 18 FF 9A 76' + '1B FF 9D 7B 1E FF A1 7F 21 FF A4 84 24 FF A8 88' + '27 FF AC 8E 2A FF B0 94 2E FF B4 99 31 FF B7 9E' + '34 FF BB A3 37 FF C0 A9 3B FF C4 B0 3F FF C7 B3' + '42 FF CA B7 44 FF CC BA 45 FF CE BC 47 FF CF BE' + '48 FF CF BE 48 FF CF BE 48 FF 85 78 51 FF EE EE' + 'EE FF 43 43 43 FF 37 37 37 FF 38 38 38 FF 39 39' + '39 FF 8E 8E 8E FF AA AA AA FF C0 C0 C0 FF C9 C9' + 'C9 FF 6D 6D 6D FF 51 51 51 FF 54 54 54 FF 56 56' + '56 FF 58 58 58 FF 59 59 59 FF 5B 5B 5B FF 5E 5E' + '5E FF 5F 5F 5F FF 60 60 60 FF 63 63 63 FF 64 64' + '64 FF 65 65 65 FF 67 67 67 FF 69 69 69 FF AB AB' + 'AB FF E3 E3 E3 E5 39 39 39 D4 6F 6F 6F FF 8C 8C' + '8C FF 78 56 23 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 7A 4A 00 FF 7D 4E 03 FF 7F 52' + '05 FF 83 56 08 FF 85 5A 0A FF 88 5E 0C FF 8B 62' + '0F FF 8F 67 12 FF 92 6B 15 FF 95 70 17 FF 99 75' + '1B FF 9C 79 1D FF A0 7E 20 FF A3 83 23 FF A6 87' + '26 FF AA 8C 29 FF AE 92 2C FF B2 97 30 FF B5 9B' + '33 FF B9 A1 36 FF BD A6 39 FF C2 AD 3E FF C6 B1' + '40 FF C8 B5 43 FF CA B7 44 FF CC BA 45 FF CD BC' + '47 FF CD BC 47 FF CE BD 47 FF 86 79 56 FF ED ED' + 'ED FF 3D 3D 3D FF 38 38 38 FF 38 38 38 FF 3A 3A' + '3A FF 8F 8F 8F FF AA AA AA FF C1 C1 C1 FF C9 C9' + 'C9 FF 6D 6D 6D FF 50 50 50 FF 54 54 54 FF 57 57' + '57 FF 5A 5A 5A FF 5B 5B 5B FF 5E 5E 5E FF 60 60' + '60 FF 61 61 61 FF 61 61 61 FF 62 62 62 FF 64 64' + '64 FF 61 61 61 FF 5E 5E 5E FF 64 64 64 FF A8 A8' + 'A8 FF E2 E2 E2 E5 38 38 38 D4 6E 6E 6E FF 8A 8A' + '8A FF 78 56 20 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 79 49 00 FF 7C 4D 02 FF 7E 51' + '04 FF 82 55 07 FF 84 58 09 FF 87 5C 0C FF 8A 61' + '0E FF 8E 66 11 FF 91 6A 14 FF 94 6E 17 FF 97 73' + '19 FF 9B 77 1C FF 9E 7C 1F FF A1 80 22 FF A5 85' + '25 FF A8 89 28 FF AD 8F 2B FF B0 94 2E FF B4 99' + '31 FF B7 9E 34 FF BB A3 37 FF BF A9 3B FF C1 AC' + '40 FF BB A9 44 FF B2 A2 4B FF A7 9A 50 FF 9F 95' + '5C FF 9E 97 6D FF A1 9C 80 FF A8 A6 A2 FF EB EB' + 'EB FF 39 39 39 FF 38 38 38 FF 39 39 39 FF 3A 3A' + '3A FF 8F 8F 8F FF AA AA AA FF C1 C1 C1 FF C9 C9' + 'C9 FF 6A 6A 6A FF 50 50 50 FF 4E 4E 4E FF 50 50' + '50 FF 53 53 53 FF 55 55 55 FF 57 57 57 FF 5B 5B' + '5B FF 5F 5F 5F FF 62 62 62 FF 66 66 66 FF 69 69' + '69 FF 6B 6B 6B FF 6D 6D 6D FF 6C 6C 6C FF A8 A8' + 'A8 FF E5 E5 E5 E5 37 37 37 D4 6C 6C 6C FF 89 89' + '89 FF 77 54 1E FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 79 49 00 FF 7A 4B 01 FF 7E 50' + '04 FF 81 54 06 FF 83 57 08 FF 86 5B 0B FF 89 5F' + '0D FF 8C 64 10 FF 8F 67 12 FF 92 6C 15 FF 96 71' + '18 FF 99 75 1B FF 9C 79 1D FF A0 7E 20 FF A3 83' + '23 FF A0 82 27 FF 9C 82 2F FF 96 82 3C FF 92 82' + '48 FF 93 87 59 FF 97 8F 70 FF A0 9B 8A FF A9 A8' + 'A0 FF B8 B8 B7 FF CA CA CA FF DC DC DC FF E8 E8' + 'E8 FF EF EF EF FF F1 F1 F1 FF ED ED ED FF CC CC' + 'CC FF 38 38 38 FF 38 38 38 FF 39 39 39 FF 3A 3A' + '3A FF 8E 8E 8E FF AA AA AA FF C1 C1 C1 FF C9 C9' + 'C9 FF 6A 6A 6A FF 57 57 57 FF 56 56 56 FF 58 58' + '58 FF 5A 5A 5A FF 5D 5D 5D FF 5F 5F 5F FF 61 61' + '61 FF 63 63 63 FF 66 66 66 FF 68 68 68 FF 6A 6A' + '6A FF 6C 6C 6C FF 6F 6F 6F FF 6D 6D 6D FF A8 A8' + 'A8 FF E4 E4 E4 E5 34 34 34 D4 6C 6C 6C FF 88 88' + '88 FF 78 53 1B FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 79 49 00 FF 79 49 00 FF 7A 4A 00 FF 7D 4F' + '03 FF 80 53 05 FF 82 56 07 FF 85 59 09 FF 87 5D' + '0C FF 88 5F 0E FF 83 5F 15 FF 82 63 22 FF 80 67' + '31 FF 7E 6C 40 FF 82 74 53 FF 89 80 6A FF 91 8D' + '84 FF A2 A0 9D FF B3 B3 B3 FF C5 C5 C5 FF D5 D5' + 'D5 FF DF DF DF FF E5 E5 E5 FF E6 E6 E6 FE E8 E8' + 'E8 F3 E9 E9 E9 D9 E6 E6 E6 BE B1 B1 B1 DF 8B 8B' + '8B FE 75 75 75 FF 5C 5C 5C FF 46 46 46 FF 3C 3C' + '3C FF 38 38 38 FF 38 38 38 FF 39 39 39 FF 3A 3A' + '3A FF 8E 8E 8E FF AA AA AA FF C1 C1 C1 FF C9 C9' + 'C9 FF 6B 6B 6B FF 58 58 58 FF 56 56 56 FF 58 58' + '58 FF 5C 5C 5C FF 60 60 60 FF 64 64 64 FF 63 63' + '63 FF 64 64 64 FF 65 65 65 FF 65 65 65 FF 67 67' + '67 FF 69 69 69 FF 6B 6B 6B FF 6C 6C 6C FF A6 A6' + 'A6 FF E4 E4 E4 E5 33 33 33 D4 6A 6A 6A FF 86 86' + '86 FF 77 51 18 FF 79 49 00 FF 79 49 00 FF 79 49' + '00 FF 78 48 00 FF 75 47 02 FF 73 4C 0F FF 74 53' + '1F FF 75 5A 2F FF 77 64 43 FF 7B 6D 56 FF 81 79' + '6C FF 89 86 82 FF 95 95 94 FF A1 A1 A1 FF AD AD' + 'AD FF B9 B9 B9 FF C1 C1 C1 FF C9 C9 C9 FF CE CE' + 'CE FE D2 D2 D2 F6 D5 D5 D5 DB DB DB DB AC DE DE' + 'DE 87 DE DE DE 65 E3 E3 E3 42 E7 E7 E7 20 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 41' + '41 2F 3B 3B 3B 79 3A 3A 3A C1 38 38 38 F8 38 38' + '38 FF 39 39 39 FF 39 39 39 FF 3A 3A 3A FF 3B 3B' + '3B FF 8E 8E 8E FF A9 A9 A9 FF C0 C0 C0 FF C8 C8' + 'C8 FF 6E 6E 6E FF 50 50 50 FF 51 51 51 FF 53 53' + '53 FF 54 54 54 FF 54 54 54 FF 63 63 63 FF 70 70' + '70 FF 7D 7D 7D FF 92 92 92 FF 9D 9D 9D FF AE AE' + 'AE FF B9 B9 B9 FF C8 C8 C8 FF D3 D3 D3 FF DE DE' + 'DE FF E4 E4 E4 E5 33 33 33 D4 68 68 68 FF 83 83' + '83 FF 70 58 35 FF 6E 56 33 FF 72 60 45 FF 75 6A' + '5A FF 79 74 6D FF 83 82 80 FF 8A 8A 8A FF 91 91' + '91 FF 97 97 97 FF 9D 9D 9D FF A1 A1 A1 FF A5 A5' + 'A5 FF A7 A7 A7 FE AC AC AC ED B0 B0 B0 D0 B5 B5' + 'B5 AE BA BA BA 8D BF BF BF 6C C3 C3 C3 49 C7 C7' + 'C7 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 3F 3F 3F 14 3D 3D' + '3D 57 3B 3B 3B A2 3A 3A 3A E6 3B 3B 3B FE 3C 3C' + '3C FF 8E 8E 8E FF A8 A8 A8 FF C0 C0 C0 FF C7 C7' + 'C7 FF AE AE AE FF AC AC AC FF B6 B6 B6 FF B8 B8' + 'B8 FF CB CB CB FF E1 E1 E1 FF EE EE EE FF F2 F2' + 'F2 FF F2 F2 F2 FF F3 F3 F3 FF F3 F3 F3 FF F2 F2' + 'F2 FF F1 F1 F1 FF F1 F1 F1 FF F2 F2 F2 FF EB EB' + 'EB FF DB DB DB D5 39 39 39 CD 66 66 66 FF 7F 7F' + '7F FF 81 81 81 FF 85 85 85 FF 89 89 89 FF 8C 8C' + '8C FF 8E 8E 8E FF 91 91 91 FC 96 96 96 E4 98 98' + '98 C2 9B 9B 9B 9F A0 A0 A0 7C A4 A4 A4 5A AB AB' + 'AB 37 B9 B9 B9 16 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 42 42 42 36 3D 3D' + '3D 80 88 88 88 E8 9C 9C 9C FF BC BC BC FF C5 C5' + 'C5 FF D2 D2 D2 FF DD DD DD FF E0 E0 E0 FF E2 E2' + 'E2 FF E8 E8 E8 FF E7 E7 E7 FE E6 E6 E6 FB E8 E8' + 'E8 F3 E7 E7 E7 E2 EA EA EA D1 EA EA EA BF F1 F1' + 'F1 AD F0 F0 F0 9A F0 F0 F0 89 EF EF EF 76 F2 F2' + 'F2 64 F5 F5 F5 35 58 58 58 2B 6C 6C 6C D4 80 80' + '80 E8 83 83 83 C2 87 87 87 9E 8D 8D 8D 79 91 91' + '91 54 9A 9A 9A 30 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 A3 A3 A3 54 C4 C4 C4 8C C8 C8 C8 8C BB BB' + 'BB 8B E1 E1 E1 77 E5 E5 E5 63 E3 E3 E3 53 E6 E6' + 'E6 3F D7 D7 D7 2D E3 E3 E3 1C 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' + 'FF FF FF FF FF FF FF 00 FF FF FF FF FF FF FE 00' + '01 FF FF FF FF FF FE 00 00 01 FF FF FF FF FC 00' + '00 00 03 FF FF FF F8 00 00 00 00 00 00 FF F0 00' + '00 00 00 00 00 01 F0 00 00 00 00 00 00 00 F0 00' + '00 00 00 00 00 00 FF 80 00 00 00 00 00 00 FF 80' + '00 00 00 00 00 00 FF 80 00 00 00 00 00 00 FF 80' + '00 00 00 00 00 00 FF 80 00 00 00 00 00 00 FF 80' + '00 00 00 00 00 00 FF 80 00 00 00 00 00 00 FF C0' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' + '00 03 C0 00 00 00 00 00 01 FF F8 00 00 00 00 00' + 'FF FF FF 80 00 00 00 FF FF FF FF E0 07 FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF' - 'FF FF FF FF FF FF FF FF FF FF FF FF FF FF F4 F4' - 'F4 FF 58 58 58 FF 00 00 00 FD 00 00 00 45 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 16 00 00 00 D5 0A 0A 0A FF 53 53 53 FF 74 74' - '74 FF 72 72 72 FF 72 72 72 FF 72 72 72 FF 72 72' - '72 FF 72 72 72 FF 72 72 72 FF 71 71 71 FF 71 71' - '71 FF 71 71 71 FF 71 71 71 FF 71 71 71 FF 71 71' - '71 FF 71 71 71 FF 71 71 71 FF 71 71 71 FF 70 70' - '70 FF 70 70 70 FF 70 70 70 FF 6F 6F 6F FF 3F 3F' - '3F FF 02 02 02 FF 00 00 00 B2 00 00 00 05 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 28 00 00 00 B1 00 00 00 EF 00 00' - '00 F1 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00' - '00 F0 00 00 00 F0 00 00 00 F0 00 00 00 F2 00 00' - '00 E8 00 00 00 97 00 00 00 15 00 00 00 00 00 00' - '00 00 00 00 00 00 FF 00 00 FF FE 00 00 7F FE 00' - '00 7F FE 00 00 7F FE 00 00 7F FE 00 00 7F FE 00' - '00 7F FF 00 00 FF FF 00 00 FF FF 80 01 FF F0 00' - '00 0F E0 00 00 07 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 C0 00 00 03 C0 00 00 03 C0 00 00 03 C0 00' - '00 03 E0 00 00 07 28 00 00 00 10 00 00 00 20 00' - '00 00 01 00 20 00 00 00 00 00 00 04 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 03 01 01' - '01 7C 08 08 08 AD 08 08 08 A8 08 08 08 A8 08 08' - '08 A8 08 08 08 A8 08 08 08 AD 01 01 01 6D 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 31 57 57' - '57 FA CE CE CE FF BB BB BB FF B1 B1 B1 FF A8 A8' - 'A8 FF 9D 9D 9D FF 8D 8D 8D FF 30 30 30 F2 00 00' - '00 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 3A 7B 7B' - '7B FC FF FF FF FF F5 F5 F5 FF EC EC EC FF DD DD' - 'DD FF CA CA CA FF BA BA BA FF 4C 4C 4C F7 00 00' - '00 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 0D 37 37' - '37 D7 F5 F5 F5 FF FF FF FF FF E2 E2 E2 FF D9 D9' - 'D9 FF E6 E6 E6 FF B7 B7 B7 FF 1C 1C 1C C5 00 00' - '00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 45 59 59 59 EB E5 E5 E5 FF F1 F1 F1 FF EA EA' - 'EA FF C2 C2 C2 FF 3B 3B 3B E1 00 00 00 35 00 00' - '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' - '00 00 00 00 00 12 02 02 02 78 06 06 06 96 05 06' - '06 8A 00 00 00 C6 16 16 16 FF 77 77 77 FF 66 66' - '66 FF 11 11 11 FF 00 00 00 BC 07 07 07 89 07 07' - '07 96 02 02 02 73 00 00 00 0E 00 00 00 00 00 00' - '00 00 03 03 03 9C 94 94 93 FF D1 D4 D7 FF C7 CD' - 'D0 FF C3 C7 CA FF B9 BD C0 FF C2 C6 C9 FF BB BF' - 'C2 FF B0 B4 B8 FF B2 B7 BA FF B0 B5 B9 FF B1 B4' - 'B6 FF 6E 6D 6D FF 00 00 00 84 00 00 00 00 00 00' - '00 00 19 19 1A CC EC ED ED FF ED C9 B1 FF E6 B4' - '8F FF EB BF 9B FF ED C4 A0 FF EB C3 9E FF E8 C0' - '9B FF E5 BA 95 FF DD AF 8B FF D4 9E 79 FF D9 B9' - 'A5 FF B9 BB BD FF 09 0A 0A A8 00 00 00 00 00 00' - '00 00 1A 1B 1B CC EB EC ED FF CC 86 58 FF C2 5E' - '17 FF CE 75 30 FF D5 80 3B FF D8 85 40 FF D6 82' - '3D FF D1 78 34 FF C8 6A 26 FF BA 4F 08 FF CC 8E' - '68 FF B8 BC C0 FF 09 09 09 A8 00 00 00 00 00 00' - '00 00 19 1A 1A CC EA EB EC FF C7 81 57 FF B9 53' - '12 FF C2 66 26 FF C7 6E 2E FF CA 71 32 FF C8 6F' - '30 FF C4 68 29 FF BD 5C 1E FF B1 45 04 FF C9 8C' - '68 FF BA BE C1 FF 08 09 09 A8 00 00 00 00 00 00' - '00 00 18 19 1A CC E9 EA EB FF BE 73 4A FF AF 44' - '03 FF C0 62 23 FF C6 6D 2D FF C9 71 31 FF C8 70' - '30 FF C4 69 2A FF BB 5A 1B FF A9 39 00 FF C7 88' - '65 FF BC C1 C4 FF 08 08 08 A8 00 00 00 00 00 00' - '00 00 18 18 19 CC E6 E7 E8 FF C1 7A 4F FF C2 62' - '1F FF D0 7C 3C FF D5 85 44 FF D7 88 47 FF D7 87' - '46 FF D3 81 40 FF CC 76 36 FF BB 57 13 FF CC 8F' - '6C FF BD C1 C4 FF 07 08 08 A8 00 00 00 00 00 00' - '00 00 16 17 18 CC E3 E4 E4 FF CD 8D 60 FF D0 78' - '33 FF DA 8C 49 FF E2 98 54 FF E6 9F 5B FF E4 9C' - '59 FF DE 92 4F FF D6 84 42 FF C9 6B 26 FF DA A2' - '7C FF C1 C5 C8 FF 07 07 07 A8 00 00 00 00 00 00' - '00 00 17 17 18 CC E4 E4 E5 FF CA 90 6A FF D5 89' - '4B FF E1 9F 63 FF EB AE 71 FF F0 B9 7A FF EE B5' - '77 FF E6 A7 6A FF DC 97 5B FF CC 7B 3D FF DF AD' - '8D FF D0 D4 D7 FF 06 07 07 A8 00 00 00 00 00 00' - '00 00 09 09 09 C1 CB CC CC FF FA F6 F4 FF F2 EE' - 'EA FF F4 F0 EC FF F4 F1 EE FF F4 F2 EE FF F4 F2' - 'EE FF F4 F0 ED FF F2 EF EA FF F0 EB E8 FF FD FA' - 'F8 FF AF AF AF FF 00 00 00 9F 00 00 00 00 00 00' - '00 00 00 00 00 4A 1E 1E 1D E2 45 46 47 F3 43 45' - '46 F0 42 43 45 F0 42 43 44 F0 42 43 44 F0 42 43' - '44 F0 42 43 44 F0 42 43 44 F0 42 44 45 F0 41 42' - '43 F4 16 16 16 D9 00 00 00 39 00 00 00 00 E0 0F' - '00 00 E0 07 00 00 E0 07 00 00 E0 07 00 00 F0 0F' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00 80 01' - '00 00 80 01 00 00 80 01 00 00 80 01 00 00' -} */ - + 'FF FF FF FF FF FF' +}*/ diff --git a/reactos/lib/shell32/shv_bg_cmenu.c b/reactos/lib/shell32/shv_bg_cmenu.c index b879d180fa2..a22384de83b 100644 --- a/reactos/lib/shell32/shv_bg_cmenu.c +++ b/reactos/lib/shell32/shv_bg_cmenu.c @@ -46,6 +46,7 @@ typedef struct IContextMenu2Vtbl *lpVtbl; IShellFolder* pSFParent; DWORD ref; + BOOL bDesktop; } BgCmImpl; @@ -54,7 +55,7 @@ static struct IContextMenu2Vtbl cmvt; /************************************************************************** * ISVBgCm_Constructor() */ -IContextMenu2 *ISvBgCm_Constructor(IShellFolder* pSFParent) +IContextMenu2 *ISvBgCm_Constructor(IShellFolder* pSFParent, BOOL bDesktop) { BgCmImpl* cm; @@ -62,6 +63,7 @@ IContextMenu2 *ISvBgCm_Constructor(IShellFolder* pSFParent) cm->lpVtbl = &cmvt; cm->ref = 1; cm->pSFParent = pSFParent; + cm->bDesktop = bDesktop; if(pSFParent) IShellFolder_AddRef(pSFParent); TRACE("(%p)->()\n",cm); @@ -362,12 +364,22 @@ static HRESULT WINAPI ISVBgCm_fnInvokeCommand( case FCIDM_SHVIEW_NEWFOLDER: DoNewFolder(iface, lpSV); break; + case FCIDM_SHVIEW_INSERT: DoPaste(iface); break; + + case FCIDM_SHVIEW_PROPERTIES: + if (This->bDesktop) { + ShellExecuteA(lpcmi->hwnd, "open", "rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL, NULL, SW_SHOWNORMAL); + } else { + FIXME("launch item properties dialog\n"); + } + break; + default: /* if it's a id just pass it to the parent shv */ - SendMessageA(hWndSV, WM_COMMAND, MAKEWPARAM(LOWORD(lpcmi->lpVerb), 0),0 ); + if (hWndSV) SendMessageA(hWndSV, WM_COMMAND, MAKEWPARAM(LOWORD(lpcmi->lpVerb), 0),0 ); break; } } diff --git a/reactos/lib/shellext/slayer/De.rc b/reactos/lib/shellext/slayer/De.rc new file mode 100644 index 00000000000..245907ed827 --- /dev/null +++ b/reactos/lib/shellext/slayer/De.rc @@ -0,0 +1,38 @@ +LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT + +IDD_SLAYERSHEET DIALOGEX 0, 0, 224, 226 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Kompatibilität" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Falls dieses Programm Probleme verursacht, wählen Sie den zutreffenden Kompatibilitätsmodus, der mit der Betriebssystem-Version übereinstimmt, unter der das Programm korrekt funktionierte.", -1, 7,7,210,31 + GROUPBOX "Kompatibilitätsmodus", IDC_COMPATGROUP, 7,41,210,49 + CHECKBOX "Programm im Kompatibilitätsmodus ausführen für:", IDC_CHKRUNCOMPATIBILITY, 18,57,188,10, BS_AUTOCHECKBOX | WS_TABSTOP + COMBOBOX IDC_COMPATIBILITYMODE, 18,70,188,85, CBS_HASSTRINGS | CBS_DROPDOWNLIST | WS_TABSTOP | WS_DISABLED + GROUPBOX "Anzeigeeinstellungen", -1, 7,99,210,61, BS_GROUPBOX | WS_DISABLED + CHECKBOX "Mit 256 Farben ausführen", IDC_CHKRUNIN256COLORS, 18,114,188,10, BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED + CHECKBOX "In Bildschirmauflösung 640 x 480 ausführen", IDC_CHKRUNIN640480RES, 18,129,188,10, BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED + CHECKBOX "Visuelle Designs deaktivieren", IDC_CHKDISABLEVISUALTHEMES, 18,144,188,10, BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED + PUSHBUTTON "Kompatibilitätsmodus ändern", IDC_EDITCOMPATIBILITYMODES, 117,167,100,15 + /* CONTROL "Weitere Informationen über Programmkompatibilität.", IDC_INFOLINK, "SysLink", WS_CHILD | WS_TABSTOP | WS_VISIBLE, 7,207,210,10, WS_EX_NOPARENTNOTIFY */ +END + +IDD_EDITCOMPATIBILITYMODES DIALOGEX 0, 0, 230, 139 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Kompatibilitätsmodus ändern" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LISTBOX IDC_COMPATIBILITYMODE, 9,6,148,108, LBS_NOTIFY | LBS_WANTKEYBOARDINPUT | WS_BORDER + PUSHBUTTON "&Hinzufügen...", IDC_ADD, 162,6,60,14, WS_DISABLED + PUSHBUTTON "&Ändern...", IDC_EDIT, 162,24,60,14, WS_DISABLED + PUSHBUTTON "&Löschen", IDC_DELETE, 162,42,60,14, WS_DISABLED + PUSHBUTTON "&OK", IDOK, 95,116,60,14 + PUSHBUTTON "&Abbrechen", IDCANCEL, 162,116,60,14 +END + +STRINGTABLE +BEGIN + IDS_SLAYER "Slayer" + IDS_DESCRIPTION "Application Compatibility Layer Shell Extension" +END + diff --git a/reactos/lib/shellext/slayer/en.rc b/reactos/lib/shellext/slayer/en.rc deleted file mode 100644 index b78d892695b..00000000000 --- a/reactos/lib/shellext/slayer/en.rc +++ /dev/null @@ -1,38 +0,0 @@ -LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT - -IDD_SLAYERSHEET DIALOGEX 0, 0, 224, 226 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION -CAPTION "Compatibility" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - LTEXT "If you have problems running this program in ReactOS, select an operating system ReactOS should report to the application.", -1, 7,7,210,31 - GROUPBOX "Compatibility mode", IDC_COMPATGROUP, 7,41,210,49 - CHECKBOX "Run this program in compatibility mode for:", IDC_CHKRUNCOMPATIBILITY, 18,57,188,10, BS_AUTOCHECKBOX | WS_TABSTOP - COMBOBOX IDC_COMPATIBILITYMODE, 18,70,188,85, CBS_HASSTRINGS | CBS_DROPDOWNLIST | WS_TABSTOP | WS_DISABLED - GROUPBOX "Display settings", -1, 7,99,210,61, BS_GROUPBOX | WS_DISABLED - CHECKBOX "Run in 256 colors", IDC_CHKRUNIN256COLORS, 18,114,188,10, BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED - CHECKBOX "Run in 640x480 screenresolution", IDC_CHKRUNIN640480RES, 18,129,188,10, BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED - CHECKBOX "Disable visual themes", IDC_CHKDISABLEVISUALTHEMES, 18,144,188,10, BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED - PUSHBUTTON "E&dit compatibility modes...", IDC_EDITCOMPATIBILITYMODES, 117,167,100,15 - /* CONTROL "Learn more about program compatibility.", IDC_INFOLINK, "SysLink", WS_CHILD | WS_TABSTOP | WS_VISIBLE, 7,207,210,10, WS_EX_NOPARENTNOTIFY */ -END - -IDD_EDITCOMPATIBILITYMODES DIALOGEX 0, 0, 230, 139 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Edit Compatibility Modes" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - LISTBOX IDC_COMPATIBILITYMODE, 9,6,148,108, LBS_NOTIFY | LBS_WANTKEYBOARDINPUT | WS_BORDER - PUSHBUTTON "&Add...", IDC_ADD, 162,6,60,14, WS_DISABLED - PUSHBUTTON "&Edit...", IDC_EDIT, 162,24,60,14, WS_DISABLED - PUSHBUTTON "&Delete", IDC_DELETE, 162,42,60,14, WS_DISABLED - PUSHBUTTON "&OK", IDOK, 95,116,60,14 - PUSHBUTTON "&Cancel", IDCANCEL, 162,116,60,14 -END - -STRINGTABLE -BEGIN - IDS_SLAYER "Slayer" - IDS_DESCRIPTION "Application Compatibility Layer Shell Extension" -END - diff --git a/reactos/lib/shellext/slayer/slayer.rc b/reactos/lib/shellext/slayer/slayer.rc index 978976223e6..0e69992f335 100644 --- a/reactos/lib/shellext/slayer/slayer.rc +++ b/reactos/lib/shellext/slayer/slayer.rc @@ -9,5 +9,12 @@ #define REACTOS_STR_ORIGINAL_FILENAME "slayer.dll\0" #include -#include "en.rc" +/* + * Everything specific to any language goes in one of the specific + * files. Note that you can and may override resources which also have + * a neutral version. This is to get localized bitmaps for example. + */ + +#include "En.rc" +#include "De.rc" diff --git a/reactos/lib/shlwapi/assoc.c b/reactos/lib/shlwapi/assoc.c index 340ac2071b5..81109b96ecf 100644 --- a/reactos/lib/shlwapi/assoc.c +++ b/reactos/lib/shlwapi/assoc.c @@ -43,7 +43,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); * when you right-click on a file). * * HELPERS - * You can use this object tranparently by calling the helper functions + * You can use this object transparently by calling the helper functions * AssocQueryKeyA(), AssocQueryStringA() and AssocQueryStringByKeyA(). These * create an IQueryAssociations object, perform the requested actions * and then dispose of the object. Alternatively, you can create an instance diff --git a/reactos/lib/shlwapi/msgbox.c b/reactos/lib/shlwapi/msgbox.c index 7b2785d20b1..ec9a4a3c02c 100644 --- a/reactos/lib/shlwapi/msgbox.c +++ b/reactos/lib/shlwapi/msgbox.c @@ -62,7 +62,7 @@ typedef struct tagDLGDATAEX /* Dialogue procedure for general message boxes */ static INT_PTR CALLBACK SHDlgProcEx(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { - DLGDATAEX *d = (DLGDATAEX *)GetWindowLongW(hDlg, DWL_USER); + DLGDATAEX *d = (DLGDATAEX *)GetWindowLongPtrW(hDlg, DWLP_USER); TRACE("(%p,%u,%d,%ld) data %p\n", hDlg, uMsg, wParam, lParam, d); @@ -71,7 +71,7 @@ static INT_PTR CALLBACK SHDlgProcEx(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM case WM_INITDIALOG: { /* FIXME: Not sure where native stores its lParam */ - SetWindowLongW(hDlg, DWL_USER, lParam); + SetWindowLongPtrW(hDlg, DWLP_USER, lParam); d = (DLGDATAEX *)lParam; TRACE("WM_INITDIALOG: %p, %s,%p,%p\n", hDlg, debugstr_w(d->lpszId), d->dlgProc, (void*)d->lParam); diff --git a/reactos/lib/shlwapi/ordinal.c b/reactos/lib/shlwapi/ordinal.c index 616d1ba9d3f..112c124fbcb 100644 --- a/reactos/lib/shlwapi/ordinal.c +++ b/reactos/lib/shlwapi/ordinal.c @@ -2397,7 +2397,7 @@ HWND WINAPI SHCreateWorkerWindowA(LONG wndProc, HWND hWndParent, DWORD dwExStyle hWndParent, hMenu, shlwapi_hInstance, 0); if (hWnd) { - SetWindowLongA(hWnd, DWL_MSGRESULT, z); + SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, z); if (wndProc) SetWindowLongPtrA(hWnd, GWLP_WNDPROC, wndProc); @@ -2684,7 +2684,7 @@ HWND WINAPI SHCreateWorkerWindowW(LONG wndProc, HWND hWndParent, DWORD dwExStyle hWndParent, hMenu, shlwapi_hInstance, 0); if (hWnd) { - SetWindowLongW(hWnd, DWL_MSGRESULT, z); + SetWindowLongPtrW(hWnd, DWLP_MSGRESULT, z); if (wndProc) SetWindowLongPtrW(hWnd, GWLP_WNDPROC, wndProc); diff --git a/reactos/lib/shlwapi/shlwapi.spec b/reactos/lib/shlwapi/shlwapi.spec index 63e501b3a65..f234b7eb6ad 100644 --- a/reactos/lib/shlwapi/shlwapi.spec +++ b/reactos/lib/shlwapi/shlwapi.spec @@ -368,9 +368,9 @@ 368 stdcall @(wstr wstr ptr long wstr) kernel32.GetPrivateProfileStructW 369 stdcall @(wstr wstr ptr ptr long long ptr wstr ptr ptr) kernel32.CreateProcessW 370 stdcall -noname ExtractIconWrapW(long wstr long) -371 stdcall DdeInitializeWrapW(ptr ptr long long) user32.DdeInitializeW -372 stdcall DdeCreateStringHandleWrapW(long ptr long) user32.DdeCreateStringHandleW -373 stdcall DdeQueryStringWrapW(long ptr wstr long long) user32.DdeQueryStringW +371 stdcall -noname DdeInitializeWrapW(ptr ptr long long) user32.DdeInitializeW +372 stdcall -noname DdeCreateStringHandleWrapW(long ptr long) user32.DdeCreateStringHandleW +373 stdcall -noname DdeQueryStringWrapW(long ptr wstr long long) user32.DdeQueryStringW 374 stub -noname SHCheckDiskForMediaA 375 stub -noname SHCheckDiskForMediaW 376 stdcall -noname MLGetUILanguage() # kernel32.GetUserDefaultUILanguage @@ -389,7 +389,7 @@ 389 stdcall -noname GetSaveFileNameWrapW(ptr) 390 stdcall -noname WNetRestoreConnectionWrapW(long wstr) 391 stdcall -noname WNetGetLastErrorWrapW(ptr ptr long ptr long) -392 stdcall EndDialogWrap(ptr ptr) user32.EndDialog +392 stdcall -noname EndDialogWrap(ptr ptr) user32.EndDialog 393 stdcall @(long ptr long ptr long) user32.CreateDialogIndirectParamW 394 stdcall @(long ptr long ptr long) user32.CreateDialogIndirectParamA 395 stub -noname MLWinHelpA @@ -456,8 +456,8 @@ 456 stdcall -noname PathIsValidCharW(long long) 457 stub -noname GetLongPathNameWrapW 458 stub -noname GetLongPathNameWrapA -459 stdcall SHExpandEnvironmentStringsA(str ptr long) kernel32.ExpandEnvironmentStringsA -460 stdcall SHExpandEnvironmentStringsW(wstr ptr long) kernel32.ExpandEnvironmentStringsW +459 stdcall -noname SHExpandEnvironmentStringsA(str ptr long) kernel32.ExpandEnvironmentStringsA +460 stdcall -noname SHExpandEnvironmentStringsW(wstr ptr long) kernel32.ExpandEnvironmentStringsW 461 stdcall -noname SHGetAppCompatFlags(long) 462 stub -noname UrlFixupW 463 stub -noname SHExpandEnvironmentStringsForUserA diff --git a/reactos/lib/shlwapi/shlwapi_Es.rc b/reactos/lib/shlwapi/shlwapi_Es.rc index 8d3fced06a9..e9706e26826 100644 --- a/reactos/lib/shlwapi/shlwapi_Es.rc +++ b/reactos/lib/shlwapi/shlwapi_Es.rc @@ -18,7 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -LANGUAGE LANG_SPANISH, SUBLANG_DEFAULT +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL IDD_ERR_DIALOG DIALOG MOVEABLE DISCARDABLE 0, 0, 220, 60 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU diff --git a/reactos/lib/shlwapi/string.c b/reactos/lib/shlwapi/string.c index fc6b6f7ccd3..78f5c1d6141 100644 --- a/reactos/lib/shlwapi/string.c +++ b/reactos/lib/shlwapi/string.c @@ -534,7 +534,7 @@ LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch) /************************************************************************* * StrRStrIA [SHLWAPI.@] * - * Find the last occurence of a substring within a string. + * Find the last occurrence of a substring within a string. * * PARAMS * lpszStr [I] String to search in @@ -542,7 +542,7 @@ LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch) * lpszSearch [I] String to look for * * RETURNS - * The last occurence lpszSearch within lpszStr, or NULL if not found. + * The last occurrence lpszSearch within lpszStr, or NULL if not found. */ LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch) { @@ -711,7 +711,7 @@ int WINAPI StrToIntW(LPCWSTR lpszStr) * NOTES * Leading whitespace, '-' and '+' are allowed before the number. If * dwFlags includes STIF_SUPPORT_HEX, hexadecimal numbers are allowed, if - * preceeded by '0x'. If this flag is not set, or there is no '0x' prefix, + * preceded by '0x'. If this flag is not set, or there is no '0x' prefix, * the string is treated as a decimal string. A leading '-' is ignored for * hexadecimal numbers. */ @@ -1171,7 +1171,7 @@ static LPWSTR WINAPI SHLWAPI_StrRChrHelperW(LPCWSTR lpszStr, /************************************************************************** * StrRChrA [SHLWAPI.@] * - * Find the last occurence of a character in string. + * Find the last occurrence of a character in string. * * PARAMS * lpszStr [I] String to search in @@ -1205,7 +1205,7 @@ LPWSTR WINAPI StrRChrW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch) /************************************************************************** * StrRChrIA [SHLWAPI.@] * - * Find the last occurence of a character in string, ignoring case. + * Find the last occurrence of a character in string, ignoring case. * * PARAMS * lpszStr [I] String to search in diff --git a/reactos/lib/smdll/makefile b/reactos/lib/smdll/makefile index 3275d1b26ee..44c3b188fc1 100644 --- a/reactos/lib/smdll/makefile +++ b/reactos/lib/smdll/makefile @@ -6,7 +6,7 @@ TARGET_TYPE = dynlink TARGET_NAME = smdll -TARGET_SDKLIBS = ntdll.a +TARGET_SDKLIBS = smlib.a ntdll.a TARGET_CFLAGS = -I./include -Wall -Werror @@ -19,9 +19,7 @@ TARGET_LFLAGS = -nostartfiles -nostdlib TARGET_OBJECTS = \ dllmain.o \ - connect.o \ - execpgm.o \ - compses.o + query.o DEP_OBJECTS = $(TARGET_OBJECTS) diff --git a/reactos/lib/smdll/query.c b/reactos/lib/smdll/query.c new file mode 100644 index 00000000000..0e8fe38c7fd --- /dev/null +++ b/reactos/lib/smdll/query.c @@ -0,0 +1,39 @@ +/* $Id: compses.c 13731 2005-02-23 23:37:06Z ea $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: lib/smdll/query.c + * PURPOSE: Call SM API SM_API_QUERY (not in NT) + */ +#define NTOS_MODE_USER +#include +#include +#include + +#define NDEBUG +#include + +/********************************************************************** + * NAME EXPORTED + * SmQuery/4 + * + * DESCRIPTION + * + * ARGUMENTS + * + * RETURN VALUE + */ +NTSTATUS STDCALL +SmQuery (IN HANDLE SmApiPort, + IN SM_INFORMATION_CLASS SmInformationClass, + IN OUT PVOID Data, + IN OUT PULONG DataLength) +{ + /* TODO */ + if(NULL != DataLength) + { + *DataLength = 0; + } + return STATUS_SUCCESS; +} +/* EOF */ diff --git a/reactos/lib/smdll/smdll.def b/reactos/lib/smdll/smdll.def index 0c2e91eb482..8be3c1fb4ae 100644 --- a/reactos/lib/smdll/smdll.def +++ b/reactos/lib/smdll/smdll.def @@ -3,3 +3,4 @@ EXPORTS SmCompleteSession@12 SmConnectApiPort@16 SmExecuteProgram@8 +SmQuery@16 diff --git a/reactos/lib/smdll/smdll.xml b/reactos/lib/smdll/smdll.xml index 4441cece082..4344f470030 100644 --- a/reactos/lib/smdll/smdll.xml +++ b/reactos/lib/smdll/smdll.xml @@ -2,10 +2,9 @@ . + smlib ntdll dllmain.c - connect.c - execpgm.c - compses.c + query.c smdll.rc diff --git a/reactos/lib/smdll/compses.c b/reactos/lib/smlib/compses.c similarity index 88% rename from reactos/lib/smdll/compses.c rename to reactos/lib/smlib/compses.c index eaaccbc6e06..f4b43166e2b 100644 --- a/reactos/lib/smdll/compses.c +++ b/reactos/lib/smlib/compses.c @@ -1,64 +1,64 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS system libraries - * FILE: lib/smlib/compses.c - * PURPOSE: Call SM API SM_API_COMPLETE_SESSION - */ -#define NTOS_MODE_USER -#include -#include -#include - -#define NDEBUG -#include - -/********************************************************************** - * NAME EXPORTED - * SmCompleteSession/3 - * - * DESCRIPTION - * This function is called by an environment subsystem server to - * tell the SM it finished initialization phase and is ready to - * manage processes it registered for (SmConnectApiPort). - * - * ARGUMENTS - * hSmApiPort: port handle returned by SmConnectApiPort; - * hSbApiPort: call back API port of the subsystem (handle); - * hApiPort : API port of the subsystem (handle). - * - * RETURN VALUE - * Success status as handed by the SM reply; otherwise a failure - * status code. - */ -NTSTATUS STDCALL -SmCompleteSession (IN HANDLE hSmApiPort, - IN HANDLE hSbApiPort, - IN HANDLE hApiPort) -{ - NTSTATUS Status; - SM_PORT_MESSAGE SmReqMsg; - - DPRINT("SMDLL: %s called\n", __FUNCTION__); - - /* Marshal Ses in the LPC message */ - SmReqMsg.CompSes.hApiPort = hApiPort; - SmReqMsg.CompSes.hSbApiPort = hSbApiPort; - - /* SM API to invoke */ - SmReqMsg.ApiIndex = SM_API_COMPLETE_SESSION; - - /* Port message */ - SmReqMsg.Header.MessageType = LPC_NEW_MESSAGE; - SmReqMsg.Header.DataSize = SM_PORT_DATA_SIZE(SmReqMsg.CompSes); - SmReqMsg.Header.MessageSize = SM_PORT_MESSAGE_SIZE; - Status = NtRequestWaitReplyPort (hSmApiPort, (PLPC_MESSAGE) & SmReqMsg, (PLPC_MESSAGE) & SmReqMsg); - if (NT_SUCCESS(Status)) - { - return SmReqMsg.Status; - } - DPRINT("SMDLL: %s failed (Status=0x%08lx)\n", __FUNCTION__, Status); - return Status; -} - -/* EOF */ +/* $Id: compses.c 13731 2005-02-23 23:37:06Z ea $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: lib/smlib/compses.c + * PURPOSE: Call SM API SM_API_COMPLETE_SESSION + */ +#define NTOS_MODE_USER +#include +#include +#include + +#define NDEBUG +#include + +/********************************************************************** + * NAME EXPORTED + * SmCompleteSession/3 + * + * DESCRIPTION + * This function is called by an environment subsystem server to + * tell the SM it finished initialization phase and is ready to + * manage processes it registered for (SmConnectApiPort). + * + * ARGUMENTS + * hSmApiPort: port handle returned by SmConnectApiPort; + * hSbApiPort: call back API port of the subsystem (handle); + * hApiPort : API port of the subsystem (handle). + * + * RETURN VALUE + * Success status as handed by the SM reply; otherwise a failure + * status code. + */ +NTSTATUS STDCALL +SmCompleteSession (IN HANDLE hSmApiPort, + IN HANDLE hSbApiPort, + IN HANDLE hApiPort) +{ + NTSTATUS Status; + SM_PORT_MESSAGE SmReqMsg; + + DPRINT("SMLIB: %s called\n", __FUNCTION__); + + /* Marshal Ses in the LPC message */ + SmReqMsg.CompSes.hApiPort = hApiPort; + SmReqMsg.CompSes.hSbApiPort = hSbApiPort; + + /* SM API to invoke */ + SmReqMsg.ApiIndex = SM_API_COMPLETE_SESSION; + + /* Port message */ + SmReqMsg.Header.MessageType = LPC_NEW_MESSAGE; + SmReqMsg.Header.DataSize = SM_PORT_DATA_SIZE(SmReqMsg.CompSes); + SmReqMsg.Header.MessageSize = SM_PORT_MESSAGE_SIZE; + Status = NtRequestWaitReplyPort (hSmApiPort, (PLPC_MESSAGE) & SmReqMsg, (PLPC_MESSAGE) & SmReqMsg); + if (NT_SUCCESS(Status)) + { + return SmReqMsg.Status; + } + DPRINT("SMLIB: %s failed (Status=0x%08lx)\n", __FUNCTION__, Status); + return Status; +} + +/* EOF */ diff --git a/reactos/lib/smdll/connect.c b/reactos/lib/smlib/connect.c similarity index 81% rename from reactos/lib/smdll/connect.c rename to reactos/lib/smlib/connect.c index 17ba96c562e..ce6f1353b68 100644 --- a/reactos/lib/smdll/connect.c +++ b/reactos/lib/smlib/connect.c @@ -1,95 +1,98 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS system libraries - * FILE: reactos/lib/smdll/connect.c - * PURPOSE: Connect to the API LPC port exposed by the SM - */ -#define NTOS_MODE_USER -#include -#include -#include -#include - -#define NDEBUG -#include - -/********************************************************************** - * NAME EXPORTED - * SmConnectApiPort/4 - * - * DESCRIPTION - * Connect to SM API port and register a session "begin" port (Sb) - * or to issue API requests to SmApiPort. - * - * ARGUMENTS - * pSbApiPortName: name of the Sb port the calling subsystem - * server already created in the system name space; - * hSbApiPort: LPC port handle (checked, but not used); - * dwSubsystem: a valid IMAGE_SUBSYSTEM_xxx value; - * phSmApiPort: a pointer to a HANDLE, which will be - * filled with a valid client-side LPC comm port. - * - * RETURN VALUE - * If all three optional values are omitted, an LPC status. - * STATUS_INVALID_PARAMETER_MIX if PortName is defined and - * both hSbApiPort and dwSubsystem are 0. - */ -NTSTATUS STDCALL -SmConnectApiPort (IN PUNICODE_STRING pSbApiPortName OPTIONAL, - IN HANDLE hSbApiPort OPTIONAL, - IN DWORD dwSubsystem OPTIONAL, - IN OUT PHANDLE phSmApiPort) -{ - UNICODE_STRING SmApiPortName; - SECURITY_QUALITY_OF_SERVICE SecurityQos; - NTSTATUS Status = STATUS_SUCCESS; - SM_CONNECT_DATA ConnectData = {0,{0}}; - ULONG ConnectDataLength = 0; - - DPRINT("SMDLL: %s called\n", __FUNCTION__); - - if (pSbApiPortName->Length > (sizeof pSbApiPortName->Buffer[0] * SM_SB_NAME_MAX_LENGTH)) - { - return STATUS_INVALID_PARAMETER_1; - } - if (pSbApiPortName) - { - if (NULL == hSbApiPort || IMAGE_SUBSYSTEM_UNKNOWN == dwSubsystem) - { - return STATUS_INVALID_PARAMETER_MIX; - } - RtlZeroMemory (& ConnectData, sizeof ConnectData); - ConnectData.Subsystem = dwSubsystem; - RtlCopyMemory (& ConnectData.SbName, - pSbApiPortName->Buffer, - pSbApiPortName->Length); - } - ConnectDataLength = sizeof ConnectData; - - SecurityQos.Length = sizeof (SecurityQos); - SecurityQos.ImpersonationLevel = SecurityIdentification; - SecurityQos.ContextTrackingMode = TRUE; - SecurityQos.EffectiveOnly = TRUE; - - RtlInitUnicodeString (& SmApiPortName, SM_API_PORT_NAME); - - Status = NtConnectPort ( - phSmApiPort, - & SmApiPortName, - & SecurityQos, - NULL, - NULL, - NULL, - & ConnectData, - & ConnectDataLength - ); - if (NT_SUCCESS(Status)) - { - return STATUS_SUCCESS; - } - DPRINT("SMDLL: %s failed (Status=0x%08lx)\n", __FUNCTION__, Status); - return Status; -} - -/* EOF */ +/* $Id: connect.c 14015 2005-03-13 17:00:19Z ea $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: reactos/lib/smlib/connect.c + * PURPOSE: Connect to the API LPC port exposed by the SM + */ +#define NTOS_MODE_USER +#include +#include +#include +#include + +#define NDEBUG +#include + +/********************************************************************** + * NAME EXPORTED + * SmConnectApiPort/4 + * + * DESCRIPTION + * Connect to SM API port and register a session "begin" port (Sb) + * or to issue API requests to SmApiPort. + * + * ARGUMENTS + * pSbApiPortName: name of the Sb port the calling subsystem + * server already created in the system name space; + * hSbApiPort: LPC port handle (checked, but not used); + * dwSubsystem: a valid IMAGE_SUBSYSTEM_xxx value; + * phSmApiPort: a pointer to a HANDLE, which will be + * filled with a valid client-side LPC comm port. + * + * RETURN VALUE + * If all three optional values are omitted, an LPC status. + * STATUS_INVALID_PARAMETER_MIX if PortName is defined and + * both hSbApiPort and dwSubsystem are 0. + */ +NTSTATUS STDCALL +SmConnectApiPort (IN PUNICODE_STRING pSbApiPortName OPTIONAL, + IN HANDLE hSbApiPort OPTIONAL, + IN DWORD dwSubsystem OPTIONAL, + IN OUT PHANDLE phSmApiPort) +{ + UNICODE_STRING SmApiPortName; + SECURITY_QUALITY_OF_SERVICE SecurityQos; + NTSTATUS Status = STATUS_SUCCESS; + SM_CONNECT_DATA ConnectData = {0,{0}}; + ULONG ConnectDataLength = 0; + + DPRINT("SMLIB: %s called\n", __FUNCTION__); + + if (pSbApiPortName) + { + if (pSbApiPortName->Length > (sizeof pSbApiPortName->Buffer[0] * SM_SB_NAME_MAX_LENGTH)) + { + return STATUS_INVALID_PARAMETER_1; + } + if (NULL == hSbApiPort || IMAGE_SUBSYSTEM_UNKNOWN == dwSubsystem) + { + return STATUS_INVALID_PARAMETER_MIX; + } + RtlZeroMemory (& ConnectData, sizeof ConnectData); + ConnectData.Subsystem = dwSubsystem; + if (pSbApiPortName->Length > 0) + { + RtlCopyMemory (& ConnectData.SbName, + pSbApiPortName->Buffer, + pSbApiPortName->Length); + } + } + ConnectDataLength = sizeof ConnectData; + + SecurityQos.Length = sizeof (SecurityQos); + SecurityQos.ImpersonationLevel = SecurityIdentification; + SecurityQos.ContextTrackingMode = TRUE; + SecurityQos.EffectiveOnly = TRUE; + + RtlInitUnicodeString (& SmApiPortName, SM_API_PORT_NAME); + + Status = NtConnectPort ( + phSmApiPort, + & SmApiPortName, + & SecurityQos, + NULL, + NULL, + NULL, + & ConnectData, + & ConnectDataLength + ); + if (NT_SUCCESS(Status)) + { + return STATUS_SUCCESS; + } + DPRINT("SMLIB: %s failed (Status=0x%08lx)\n", __FUNCTION__, Status); + return Status; +} + +/* EOF */ diff --git a/reactos/lib/smdll/execpgm.c b/reactos/lib/smlib/execpgm.c similarity index 87% rename from reactos/lib/smdll/execpgm.c rename to reactos/lib/smlib/execpgm.c index 477096820e0..75b7aaf58ff 100644 --- a/reactos/lib/smdll/execpgm.c +++ b/reactos/lib/smlib/execpgm.c @@ -1,76 +1,76 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS system libraries - * FILE: lib/smdll/execpgm.c - * PURPOSE: Call SM API SM_API_EXECPGM - */ -#define NTOS_MODE_USER -#include -#include -#include -#include - -#define NDEBUG -#include - -/********************************************************************** - * NAME EXPORTED - * SmExecuteProgram/2 - * - * DESCRIPTION - * This function is used to make the SM start an environment - * subsystem server process. - * - * ARGUMENTS - * hSmApiPort: port handle returned by SmConnectApiPort; - * Pgm : name of the subsystem (to be used by the SM to - * lookup the image name from the registry). - * Valid names are: DEBUG, WINDOWS, POSIX, OS2, - * and VMS. - * - * RETURN VALUE - * Success status as handed by the SM reply; otherwise a failure - * status code. - */ -NTSTATUS STDCALL -SmExecuteProgram (IN HANDLE hSmApiPort, - IN PUNICODE_STRING Pgm) -{ - NTSTATUS Status; - SM_PORT_MESSAGE SmReqMsg; - - - DPRINT("SMDLL: %s called\n", __FUNCTION__); - - /* Check Pgm's length */ - if (Pgm->Length > (sizeof (Pgm->Buffer[0]) * SM_EXEXPGM_MAX_LENGTH)) - { - return STATUS_INVALID_PARAMETER; - } - /* Marshal Pgm in the LPC message */ - RtlZeroMemory (& SmReqMsg, sizeof SmReqMsg); - SmReqMsg.ExecPgm.NameLength = Pgm->Length; - RtlCopyMemory (SmReqMsg.ExecPgm.Name, - Pgm->Buffer, - Pgm->Length); - - /* SM API to invoke */ - SmReqMsg.ApiIndex = SM_API_EXECUTE_PROGRAMME; - - /* LPC message */ - SmReqMsg.Header.MessageType = LPC_NEW_MESSAGE; - SmReqMsg.Header.DataSize = SM_PORT_DATA_SIZE(SmReqMsg.ExecPgm); - SmReqMsg.Header.MessageSize = SM_PORT_MESSAGE_SIZE; - - /* Call SM and wait for a reply */ - Status = NtRequestWaitReplyPort (hSmApiPort, (PLPC_MESSAGE) & SmReqMsg, (PLPC_MESSAGE) & SmReqMsg); - if (NT_SUCCESS(Status)) - { - return SmReqMsg.Status; - } - DPRINT("SMDLL: %s failed (Status=0x%08lx)\n", __FUNCTION__, Status); - return Status; -} - -/* EOF */ +/* $Id: execpgm.c 13731 2005-02-23 23:37:06Z ea $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: lib/smlib/execpgm.c + * PURPOSE: Call SM API SM_API_EXECPGM + */ +#define NTOS_MODE_USER +#include +#include +#include +#include + +#define NDEBUG +#include + +/********************************************************************** + * NAME EXPORTED + * SmExecuteProgram/2 + * + * DESCRIPTION + * This function is used to make the SM start an environment + * subsystem server process. + * + * ARGUMENTS + * hSmApiPort: port handle returned by SmConnectApiPort; + * Pgm : name of the subsystem (to be used by the SM to + * lookup the image name from the registry). + * Valid names are: DEBUG, WINDOWS, POSIX, OS2, + * and VMS. + * + * RETURN VALUE + * Success status as handed by the SM reply; otherwise a failure + * status code. + */ +NTSTATUS STDCALL +SmExecuteProgram (IN HANDLE hSmApiPort, + IN PUNICODE_STRING Pgm) +{ + NTSTATUS Status; + SM_PORT_MESSAGE SmReqMsg; + + + DPRINT("SMLIB: %s called\n", __FUNCTION__); + + /* Check Pgm's length */ + if (Pgm->Length > (sizeof (Pgm->Buffer[0]) * SM_EXEXPGM_MAX_LENGTH)) + { + return STATUS_INVALID_PARAMETER; + } + /* Marshal Pgm in the LPC message */ + RtlZeroMemory (& SmReqMsg, sizeof SmReqMsg); + SmReqMsg.ExecPgm.NameLength = Pgm->Length; + RtlCopyMemory (SmReqMsg.ExecPgm.Name, + Pgm->Buffer, + Pgm->Length); + + /* SM API to invoke */ + SmReqMsg.ApiIndex = SM_API_EXECUTE_PROGRAMME; + + /* LPC message */ + SmReqMsg.Header.MessageType = LPC_NEW_MESSAGE; + SmReqMsg.Header.DataSize = SM_PORT_DATA_SIZE(SmReqMsg.ExecPgm); + SmReqMsg.Header.MessageSize = SM_PORT_MESSAGE_SIZE; + + /* Call SM and wait for a reply */ + Status = NtRequestWaitReplyPort (hSmApiPort, (PLPC_MESSAGE) & SmReqMsg, (PLPC_MESSAGE) & SmReqMsg); + if (NT_SUCCESS(Status)) + { + return SmReqMsg.Status; + } + DPRINT("SMLIB: %s failed (Status=0x%08lx)\n", __FUNCTION__, Status); + return Status; +} + +/* EOF */ diff --git a/reactos/lib/smlib/makefile b/reactos/lib/smlib/makefile new file mode 100644 index 00000000000..9ec7cc1e38d --- /dev/null +++ b/reactos/lib/smlib/makefile @@ -0,0 +1,35 @@ + +PATH_TO_TOP = ../.. + +TARGET_TYPE = library + +TARGET_NAME = smlib + +include $(PATH_TO_TOP)/config + +TARGET_CFLAGS = -Wall -Werror -ffreestanding + +# require os code to explicitly request A/W version of structs/functions +TARGET_CFLAGS += -D_DISABLE_TIDENTS + +ifneq ($(DBG), 0) +TARGET_CFLAGS += -DDBG +endif + +TARGET_OBJECTS = \ + connect.o \ + execpgm.o \ + compses.o + + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + +DEP_OBJECTS := $(TARGET_OBJECTS) + +TARGET_CLEAN = $(DEP_FILES) + +include $(PATH_TO_TOP)/tools/depend.mk + +# EOF diff --git a/reactos/lib/smlib/smlib.xml b/reactos/lib/smlib/smlib.xml new file mode 100644 index 00000000000..6b06eb10210 --- /dev/null +++ b/reactos/lib/smlib/smlib.xml @@ -0,0 +1,7 @@ + + . + + connect.c + execpgm.c + compses.c + diff --git a/reactos/lib/string/Makefile b/reactos/lib/string/Makefile index 8694d1c244b..d3a2904bdc6 100644 --- a/reactos/lib/string/Makefile +++ b/reactos/lib/string/Makefile @@ -13,7 +13,7 @@ include Makefile.$(ARCH) TARGET_CFLAGS = -Wall -Werror # require os code to explicitly request A/W version of structs/functions -TARGET_CFLAGS += -D_DISABLE_TIDENTS +TARGET_CFLAGS += -D_DISABLE_TIDENTS -D__USE_W32API ifeq ($(DBG), 1) TARGET_CFLAGS += -g diff --git a/reactos/lib/syssetup/wizard.c b/reactos/lib/syssetup/wizard.c index 4ddca5662b1..db615e34162 100644 --- a/reactos/lib/syssetup/wizard.c +++ b/reactos/lib/syssetup/wizard.c @@ -1125,6 +1125,7 @@ static BOOL SetSystemLocalTime(HWND hwnd, PSETUPDATA SetupData) { HANDLE hToken; + DWORD PrevSize; TOKEN_PRIVILEGES priv, previouspriv; BOOL Ret = FALSE; @@ -1133,7 +1134,7 @@ SetSystemLocalTime(HWND hwnd, PSETUPDATA SetupData) */ if(OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES, + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { priv.PrivilegeCount = 1; @@ -1148,7 +1149,7 @@ SetSystemLocalTime(HWND hwnd, PSETUPDATA SetupData) &priv, sizeof(previouspriv), &previouspriv, - 0) && + &PrevSize) && GetLastError() == ERROR_SUCCESS) { /* diff --git a/reactos/lib/tgetopt/Makefile b/reactos/lib/tgetopt/Makefile index e9b37fb8992..2b54813bf7c 100644 --- a/reactos/lib/tgetopt/Makefile +++ b/reactos/lib/tgetopt/Makefile @@ -7,7 +7,7 @@ TARGET_TYPE = library TARGET_NAME = tgetopt # require os code to explicitly request A/W version of structs/functions -TARGET_CFLAGS += -D_DISABLE_TIDENTS +TARGET_CFLAGS += -D__USE_W32API -D_DISABLE_TIDENTS TARGET_OBJECTS = getopt.o _wgetopt.o diff --git a/reactos/lib/urlmon/urlmon.spec b/reactos/lib/urlmon/urlmon.spec index 056a2e45e10..f6ca4356e3c 100644 --- a/reactos/lib/urlmon/urlmon.spec +++ b/reactos/lib/urlmon/urlmon.spec @@ -1,5 +1,10 @@ +# Up until Windows 2000 these APIs have hardcoded ordinals. +# Keep it that way for compatibility. 1 stub CDLGetLongPathNameA 2 stub CDLGetLongPathNameW +# IsJITInProgress has a hardcoded ordinal on WinME and Windows 2000 +#3 stub IsJITInProgress + @ stub AsyncGetClassBits @ stub AsyncInstallDistributionUnit @ stub BindAsyncMoniker diff --git a/reactos/lib/user32/Da.rc b/reactos/lib/user32/Da.rc new file mode 100644 index 00000000000..e31b2104f95 --- /dev/null +++ b/reactos/lib/user32/Da.rc @@ -0,0 +1,74 @@ +LANGUAGE LANG_DANISH, SUBLANG_DEFAULT + +///////////////////////////////////////////////////////////////////////////// +// +// Dialogs +// + +SELWINDOW DIALOG DISCARDABLE 20, 20, 220, 140 +STYLE DS_SYSMODAL | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +FONT 8, "Bitstream Vera Sans" +CAPTION "Vælg Vindue" +BEGIN + LISTBOX 100, 5, 5, 210, 110, LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_HSCROLL + PUSHBUTTON "&OK", 1, 60, 120, 40, 15, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "&Fortryd", 2, 120, 120, 40, 15, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + +///////////////////////////////////////////////////////////////////////////// +// +// Menus +// + +EDITMENU MENU LOADONCALL MOVEABLE DISCARDABLE +{ + POPUP "DUMMY" + BEGIN + MENUITEM "&Fortryd", EM_UNDO + MENUITEM SEPARATOR + MENUITEM "Kl&ip", WM_CUT + MENUITEM "&Kopier", WM_COPY + MENUITEM "&Indsæt", WM_PASTE + MENUITEM "&Slet", WM_CLEAR + MENUITEM SEPARATOR + MENUITEM "Vælg &Alle", EM_SETSEL + END +} + +SYSMENU MENU LOADONCALL MOVEABLE DISCARDABLE +{ + MENUITEM "&Gendan", 61728 + MENUITEM "&Flyt", 61456 + MENUITEM "&Størrelse", 61440 + MENUITEM "M&inimere", 61472 + MENUITEM "Ma&ksimere", 61488 + MENUITEM SEPARATOR + MENUITEM "&Luk\tAlt-F4", 61536 +} + +///////////////////////////////////////////////////////////////////////////// +// +// Strings +// + +STRINGTABLE +{ + IDS_ERROR, "Fejl" + IDS_OK, "OK" + IDS_CANCEL, "Fortryd" + IDS_ABORT, "&Afbryd" + IDS_RETRY, "&Forsøg Igen" + IDS_IGNORE, "&Ignorer" + IDS_YES, "&Ja" + IDS_NO, "&Nej" + IDS_HELP, "Hjælp" + IDS_TRYAGAIN, "&Forsøg Igen" + IDS_CONTINUE, "&Forsæt" +} + +STRINGTABLE +{ + IDS_MDI_MOREWINDOWS, "&More Windows..." +} + diff --git a/reactos/lib/user32/Sv.rc b/reactos/lib/user32/Sv.rc new file mode 100644 index 00000000000..4ac5fc8f0d3 --- /dev/null +++ b/reactos/lib/user32/Sv.rc @@ -0,0 +1,95 @@ +///////////////////////////////////////////////////////////////////////////// +// Swedish (SE) resources +/* + * Copyright 2005 David Nordenberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT + +///////////////////////////////////////////////////////////////////////////// +// +// Dialogs +// + +SELWINDOW DIALOG DISCARDABLE 20, 20, 220, 140 +STYLE DS_SYSMODAL | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +FONT 8, "Bitstream Vera Sans" +CAPTION "Välj fönster" +BEGIN + LISTBOX 100, 5, 5, 210, 110, LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_HSCROLL + PUSHBUTTON "&OK", 1, 60, 120, 40, 15, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "&Avbryt", 2, 120, 120, 40, 15, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + +///////////////////////////////////////////////////////////////////////////// +// +// Menus +// + +EDITMENU MENU LOADONCALL MOVEABLE DISCARDABLE +{ + POPUP "DUMMY" + BEGIN + MENUITEM "&Ångra", EM_UNDO + MENUITEM SEPARATOR + MENUITEM "&Klipp ut", WM_CUT + MENUITEM "K&opiera", WM_COPY + MENUITEM "K&listra in", WM_PASTE + MENUITEM "&Ta bort", WM_CLEAR + MENUITEM SEPARATOR + MENUITEM "&Markera allt", EM_SETSEL + END +} + + +SYSMENU MENU LOADONCALL MOVEABLE DISCARDABLE +{ + MENUITEM "&Återställ", 61728 + MENUITEM "&Flytta", 61456 + MENUITEM "S&torlek", 61440 + MENUITEM "&Minimera", 61472 + MENUITEM "Ma&ximera", 61488 + MENUITEM SEPARATOR + MENUITEM "&Stäng\tAlt-F4", 61536 +} + +///////////////////////////////////////////////////////////////////////////// +// +// Strings +// + +STRINGTABLE +{ + IDS_ERROR, "Fel" + IDS_OK, "OK" + IDS_CANCEL, "Avbryt" + IDS_ABORT, "&Avbryt" + IDS_RETRY, "&Försök igen" + IDS_IGNORE, "&Ignorera" + IDS_YES, "&Ja" + IDS_NO, "&Nej" + IDS_HELP, "Hjälp" + IDS_TRYAGAIN, "&Försök igen" + IDS_CONTINUE, "F&ortsätt" +} + +STRINGTABLE +{ + IDS_MDI_MOREWINDOWS, "&Fler fönster..." +} + diff --git a/reactos/lib/user32/user32.rc b/reactos/lib/user32/user32.rc index 3505f6e9b4d..699765a947f 100644 --- a/reactos/lib/user32/user32.rc +++ b/reactos/lib/user32/user32.rc @@ -1,7 +1,8 @@ /* $Id$ */ #include -#include +#include + #include #define REACTOS_VERSION_DLL @@ -59,4 +60,6 @@ OBM_COMBO BITMAP "resources/obm_combo.bmp" OBM_MNARROW BITMAP "resources/obm_mnarrow.bmp" #include "En.rc" +#include "Da.rc" #include "De.rc" +#include "Sv.rc" diff --git a/reactos/lib/user32/windows/accel.c b/reactos/lib/user32/windows/accel.c index 5840e2a5e9b..f560695e63a 100644 --- a/reactos/lib/user32/windows/accel.c +++ b/reactos/lib/user32/windows/accel.c @@ -291,32 +291,31 @@ HACCEL WINAPI CreateAcceleratorTableW(LPACCEL lpaccl, int cEntries) int WINAPI CopyAcceleratorTableA ( HACCEL hAccelSrc, - LPACCEL lpAccelDst, + LPACCEL lpAccelDst, /* can be NULL */ int cAccelEntries ) { - int i; - - cAccelEntries = CopyAcceleratorTableW(hAccelSrc, lpAccelDst, cAccelEntries); + int i; - if(cAccelEntries == 0) return 0; + cAccelEntries = CopyAcceleratorTableW(hAccelSrc, lpAccelDst, cAccelEntries); + + if (lpAccelDst == NULL) return cAccelEntries; - for(i = 0; i < cAccelEntries; ++ i) - if(!(lpAccelDst[i].fVirt & FVIRTKEY)) - { - NTSTATUS nErrCode = RtlUnicodeToMultiByteN - ( - (PCHAR)&lpAccelDst[i].key, - sizeof(lpAccelDst[i].key), - NULL, - (PWCHAR)&lpAccelDst[i].key, - sizeof(lpAccelDst[i].key) - ); - - if(!NT_SUCCESS(nErrCode)) lpAccelDst[i].key = 0; - } - - return cAccelEntries; + for(i = 0; i < cAccelEntries; ++ i) + if(!(lpAccelDst[i].fVirt & FVIRTKEY)) + { + NTSTATUS nErrCode = RtlUnicodeToMultiByteN( + (PCHAR)&lpAccelDst[i].key, + sizeof(lpAccelDst[i].key), + NULL, + (PWCHAR)&lpAccelDst[i].key, + sizeof(lpAccelDst[i].key) + ); + + if(!NT_SUCCESS(nErrCode)) lpAccelDst[i].key = 0; + } + + return cAccelEntries; } diff --git a/reactos/lib/user32/windows/bitmap.c b/reactos/lib/user32/windows/bitmap.c index 26fae34e3ea..b7cff98c64a 100644 --- a/reactos/lib/user32/windows/bitmap.c +++ b/reactos/lib/user32/windows/bitmap.c @@ -552,7 +552,7 @@ LoadBitmapImage(HINSTANCE hInstance, LPCWSTR lpszName, UINT fuLoad) /* FIXME: Handle color conversion and transparency. */ - hScreenDc = CreateDCW(L"DISPLAY", NULL, NULL, NULL); + hScreenDc = CreateCompatibleDC(NULL); if (hScreenDc == NULL) { RtlFreeHeap(GetProcessHeap(), 0, PrivateInfo); diff --git a/reactos/lib/user32/windows/defwnd.c b/reactos/lib/user32/windows/defwnd.c index 6d7ed4b39dd..e0f9d0d9edc 100644 --- a/reactos/lib/user32/windows/defwnd.c +++ b/reactos/lib/user32/windows/defwnd.c @@ -31,7 +31,7 @@ #define WM_QUERYDROPOBJECT 0x022B #endif -LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn); +LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active); LRESULT DefWndNCCalcSize(HWND hWnd, BOOL CalcSizeStruct, RECT *Rect); LRESULT DefWndNCActivate(HWND hWnd, WPARAM wParam); LRESULT DefWndNCHitTest(HWND hWnd, POINT Point); @@ -317,7 +317,8 @@ DefWndStartSizeMove(HWND hWnd, WPARAM wParam, POINT *capturePoint) { while(!hittest) { - GetMessageW(&msg, NULL, 0, 0); + if (GetMessageW(&msg, NULL, 0, 0) <= 0) + break; switch(msg.message) { case WM_MOUSEMOVE: @@ -572,7 +573,8 @@ DefWndDoSizeMove(HWND hwnd, WORD wParam) { int dx = 0, dy = 0; - GetMessageW(&msg, 0, 0, 0); + if (GetMessageW(&msg, 0, 0, 0) <= 0) + break; /* Exit on button-up, Return, or Esc */ if ((msg.message == WM_LBUTTONUP) || @@ -947,7 +949,7 @@ User32DefWindowProc(HWND hWnd, { case WM_NCPAINT: { - return DefWndNCPaint(hWnd, (HRGN)wParam); + return DefWndNCPaint(hWnd, (HRGN)wParam, -1); } case WM_NCCALCSIZE: @@ -1470,7 +1472,7 @@ DefWindowProcA(HWND hWnd, if ((GetWindowLongW(hWnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION) { - DefWndNCPaint(hWnd, (HRGN)1); + DefWndNCPaint(hWnd, (HRGN)1, -1); } return TRUE; } @@ -1530,7 +1532,7 @@ DefWindowProcW(HWND hWnd, if ((GetWindowLongW(hWnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION) { - DefWndNCPaint(hWnd, (HRGN)1); + DefWndNCPaint(hWnd, (HRGN)1, -1); } return (1); } diff --git a/reactos/lib/user32/windows/dialog.c b/reactos/lib/user32/windows/dialog.c index 7417386edbd..53775c1a6da 100644 --- a/reactos/lib/user32/windows/dialog.c +++ b/reactos/lib/user32/windows/dialog.c @@ -708,8 +708,12 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate, rect.left = rect.top = 0; rect.right = MulDiv(template.cx, dlgInfo->xBaseUnit, 4); rect.bottom = MulDiv(template.cy, dlgInfo->yBaseUnit, 8); + if (template.style & WS_CHILD) + template.style &= ~(WS_CAPTION|WS_SYSMENU); if (template.style & DS_MODALFRAME) template.exStyle |= WS_EX_DLGMODALFRAME; + if (template.style & DS_CONTROL) + template.exStyle |= WS_EX_CONTROLPARENT; AdjustWindowRectEx( &rect, template.style, (dlgInfo->hMenu != 0), template.exStyle ); rect.right -= rect.left; rect.bottom -= rect.top; diff --git a/reactos/lib/user32/windows/hook.c b/reactos/lib/user32/windows/hook.c index b49ef2b0bac..7e26167f5fe 100644 --- a/reactos/lib/user32/windows/hook.c +++ b/reactos/lib/user32/windows/hook.c @@ -156,8 +156,7 @@ BOOL STDCALL DeregisterShellHookWindow(HWND hWnd) { - UNIMPLEMENTED; - return FALSE; + return NtUserCallHwnd(HWND_ROUTINE_DEREGISTERSHELLHOOKWINDOW, (DWORD)hWnd); } /* @@ -167,8 +166,7 @@ BOOL STDCALL RegisterShellHookWindow(HWND hWnd) { - UNIMPLEMENTED; - return FALSE; + return NtUserCallHwnd(HWND_ROUTINE_REGISTERSHELLHOOKWINDOW, (DWORD)hWnd); } /* diff --git a/reactos/lib/user32/windows/icon.c b/reactos/lib/user32/windows/icon.c index e9c28116d91..a6019e94a05 100644 --- a/reactos/lib/user32/windows/icon.c +++ b/reactos/lib/user32/windows/icon.c @@ -566,7 +566,7 @@ LookupIconIdFromDirectoryEx( HDC hdc; int ColorBits; - hdc = GetDC(0); + hdc = CreateICW(NULL, NULL, NULL, NULL); if (Flags & LR_MONOCHROME) { ColorBits = 1; @@ -577,7 +577,7 @@ LookupIconIdFromDirectoryEx( if (ColorBits > 8) ColorBits = 8; } - ReleaseDC(0, hdc); + DeleteDC(hdc); entry = CURSORICON_FindBestIcon( dir, cxDesired, cyDesired, ColorBits ); diff --git a/reactos/lib/user32/windows/messagebox.c b/reactos/lib/user32/windows/messagebox.c index b0c4673b704..c7ce634b822 100644 --- a/reactos/lib/user32/windows/messagebox.c +++ b/reactos/lib/user32/windows/messagebox.c @@ -32,7 +32,6 @@ /* INCLUDES ******************************************************************/ #include "user32.h" -#include #include #include #include diff --git a/reactos/lib/user32/windows/nonclient.c b/reactos/lib/user32/windows/nonclient.c index 50e50f56504..444d84bf96b 100644 --- a/reactos/lib/user32/windows/nonclient.c +++ b/reactos/lib/user32/windows/nonclient.c @@ -288,10 +288,9 @@ UserDrawCaptionButtonWnd(HWND hWnd, HDC hDC, BOOL bDown, ULONG Type) * - Correct drawing of size-box */ LRESULT -DefWndNCPaint(HWND hWnd, HRGN hRgn) +DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active) { HDC hDC; - BOOL Active; DWORD Style, ExStyle; HWND Parent; RECT ClientRect, WindowRect, CurrentRect, TempRect; @@ -309,15 +308,18 @@ DefWndNCPaint(HWND hWnd, HRGN hRgn) Parent = GetParent(hWnd); ExStyle = GetWindowLongW(hWnd, GWL_EXSTYLE); - if (ExStyle & WS_EX_MDICHILD) + if (Active == -1) { - Active = IsChild(GetForegroundWindow(), hWnd); - if (Active) - Active = (hWnd == (HWND)SendMessageW(Parent, WM_MDIGETACTIVE, 0, 0)); - } - else - { - Active = (GetForegroundWindow() == hWnd); + if (ExStyle & WS_EX_MDICHILD) + { + Active = IsChild(GetForegroundWindow(), hWnd); + if (Active) + Active = (hWnd == (HWND)SendMessageW(Parent, WM_MDIGETACTIVE, 0, 0)); + } + else + { + Active = (GetForegroundWindow() == hWnd); + } } GetWindowRect(hWnd, &WindowRect); GetClientRect(hWnd, &ClientRect); @@ -376,7 +378,7 @@ DefWndNCPaint(HWND hWnd, HRGN hRgn) } /* Now the other bit of the frame */ - if (Style & WS_CAPTION || ExStyle & WS_EX_DLGMODALFRAME) + if (Style & (WS_DLGFRAME | WS_BORDER) || ExStyle & WS_EX_DLGMODALFRAME) { DWORD Width = GetSystemMetrics(SM_CXBORDER); DWORD Height = GetSystemMetrics(SM_CYBORDER); @@ -660,7 +662,7 @@ DefWndNCCalcSize(HWND hWnd, BOOL CalcSizeStruct, RECT *Rect) LRESULT DefWndNCActivate(HWND hWnd, WPARAM wParam) { - DefWndNCPaint(hWnd, (HRGN)1); + DefWndNCPaint(hWnd, (HRGN)1, wParam); return TRUE; } @@ -882,79 +884,75 @@ DefWndNCHitTest(HWND hWnd, POINT Point) VOID DefWndDoButton(HWND hWnd, WPARAM wParam) { - MSG Msg; - BOOL InBtn, HasBtn = FALSE; - ULONG Btn, Style; - WPARAM SCMsg, CurBtn = wParam, OrigBtn = wParam; - HDC WindowDC = NULL; + MSG Msg; + HDC WindowDC; + BOOL Pressed = TRUE, OldState; + WPARAM SCMsg; + ULONG ButtonType, Style; - Style = GetWindowLongW(hWnd, GWL_STYLE); - switch(wParam) - { - case HTCLOSE: - Btn = DFCS_CAPTIONCLOSE; - SCMsg = SC_CLOSE; - HasBtn = (Style & WS_SYSMENU); - break; - case HTMINBUTTON: - Btn = DFCS_CAPTIONMIN; - SCMsg = ((Style & WS_MINIMIZE) ? SC_RESTORE : SC_MINIMIZE); - HasBtn = (Style & WS_MINIMIZEBOX); - break; - case HTMAXBUTTON: - Btn = DFCS_CAPTIONMAX; - SCMsg = ((Style & WS_MAXIMIZE) ? SC_RESTORE : SC_MAXIMIZE); - HasBtn = (Style & WS_MAXIMIZEBOX); - break; - default: - return; - } + Style = GetWindowLongW(hWnd, GWL_STYLE); + switch (wParam) + { + case HTCLOSE: + if (!(Style & WS_SYSMENU)) + return; + ButtonType = DFCS_CAPTIONCLOSE; + SCMsg = SC_CLOSE; + break; + case HTMINBUTTON: + if (!(Style & WS_MINIMIZEBOX)) + return; + ButtonType = DFCS_CAPTIONMIN; + SCMsg = ((Style & WS_MINIMIZE) ? SC_RESTORE : SC_MINIMIZE); + break; + case HTMAXBUTTON: + if (!(Style & WS_MAXIMIZEBOX)) + return; + ButtonType = DFCS_CAPTIONMAX; + SCMsg = ((Style & WS_MAXIMIZE) ? SC_RESTORE : SC_MAXIMIZE); + break; + + default: + ASSERT(FALSE); + return; + } - InBtn = HasBtn; + /* + * FIXME: Not sure where to do this, but we must flush the pending + * window updates when someone clicks on the close button and at + * the same time the window is overlapped with another one. This + * looks like a good place for now... + */ + UpdateWindow(hWnd); + + WindowDC = GetWindowDC(hWnd); + UserDrawCaptionButtonWnd(hWnd, WindowDC, TRUE, ButtonType); + + SetCapture(hWnd); - SetCapture(hWnd); + for (;;) + { + if (GetMessageW(&Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST) <= 0) + break; + + if (Msg.message == WM_LBUTTONUP) + break; + + if (Msg.message != WM_MOUSEMOVE) + continue; + + OldState = Pressed; + Pressed = (DefWndNCHitTest(hWnd, Msg.pt) == wParam); + if (Pressed != OldState) + UserDrawCaptionButtonWnd(hWnd, WindowDC, Pressed, ButtonType); + } - if(HasBtn) - { - WindowDC = GetWindowDC(hWnd); - UserDrawCaptionButtonWnd(hWnd, WindowDC, HasBtn , Btn); - } - - for(;;) - { - GetMessageW(&Msg, 0, 0, 0); - switch(Msg.message) - { - case WM_LBUTTONUP: - if(InBtn) - goto done; - else - { - ReleaseCapture(); - if (HasBtn) - ReleaseDC(hWnd, WindowDC); - return; - } - case WM_MOUSEMOVE: - if(HasBtn) - { - CurBtn = DefWndNCHitTest(hWnd, Msg.pt); - if(InBtn != (CurBtn == OrigBtn)) - { - UserDrawCaptionButtonWnd( hWnd, WindowDC, (CurBtn == OrigBtn) , Btn); - } - InBtn = CurBtn == OrigBtn; - } - break; - } - } - -done: - UserDrawCaptionButtonWnd( hWnd, WindowDC, FALSE , Btn); - ReleaseDC(hWnd, WindowDC); - ReleaseCapture(); - SendMessageW(hWnd, WM_SYSCOMMAND, SCMsg, 0); - return; + if (Pressed) + UserDrawCaptionButtonWnd(hWnd, WindowDC, FALSE, ButtonType); + ReleaseCapture(); + ReleaseDC(hWnd, WindowDC); + if (Pressed) + SendMessageW(hWnd, WM_SYSCOMMAND, SCMsg, 0); } diff --git a/reactos/lib/user32/windows/window.c b/reactos/lib/user32/windows/window.c index 20efad69ea9..bab497bf025 100644 --- a/reactos/lib/user32/windows/window.c +++ b/reactos/lib/user32/windows/window.c @@ -22,7 +22,7 @@ BOOL ControlsInitialized = FALSE; -LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn); +LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active); /* FUNCTIONS *****************************************************************/ @@ -1236,7 +1236,7 @@ SetWindowTextA(HWND hWnd, if ((GetWindowLongW(hWnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION) { - DefWndNCPaint(hWnd, (HRGN)1); + DefWndNCPaint(hWnd, (HRGN)1, -1); } return TRUE; } @@ -1270,7 +1270,7 @@ SetWindowTextW(HWND hWnd, if ((GetWindowLongW(hWnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION) { - DefWndNCPaint(hWnd, (HRGN)1); + DefWndNCPaint(hWnd, (HRGN)1, -1); } return TRUE; } diff --git a/reactos/lib/userenv/De.rc b/reactos/lib/userenv/De.rc index c6d12ff1909..218b055b5a8 100644 --- a/reactos/lib/userenv/De.rc +++ b/reactos/lib/userenv/De.rc @@ -43,4 +43,5 @@ BEGIN IDS_CACHE "Lokale Einstellungen\\Temporary Internet Files" IDS_HISTORY "Lokale Einstellungen\\Verlauf" IDS_COOKIES "Cookies" + IDS_PROGRAMFILES "%SystemDrive%\\Programme" END diff --git a/reactos/lib/uuid/uuid.c b/reactos/lib/uuid/uuid.c index 854e3ae82ff..264094a7eba 100644 --- a/reactos/lib/uuid/uuid.c +++ b/reactos/lib/uuid/uuid.c @@ -81,6 +81,9 @@ DEFINE_GUID(FMTID_SummaryInformation,0xF29F85E0,0x4FF9,0x1068,0xAB,0x91,0x08,0x0 DEFINE_GUID(FMTID_DocSummaryInformation,0xD5CDD502,0x2E9C,0x101B,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE); DEFINE_GUID(FMTID_UserDefinedProperties,0xD5CDD505,0x2E9C,0x101B,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE); +DEFINE_GUID(IID_IRichEditOle,0x00020D00,0,0,0xC0,0,0,0,0,0,0,0x46); +DEFINE_GUID(IID_IRichEditOleCallback,0x00020D03,0,0,0xC0,0,0,0,0,0,0,0x46); + DEFINE_OLEGUID(IID_StdOle,0x00020430,0,0); DEFINE_GUID(CLSID_StdFont,0x0be35203,0x8f91,0x11ce,0x9d,0xe3,0x00,0xaa,0x00,0x4b,0xb8,0x51); diff --git a/reactos/lib/wininet/stubs.c b/reactos/lib/wininet/stubs.c index 0348c5ee974..44516e84bd6 100644 --- a/reactos/lib/wininet/stubs.c +++ b/reactos/lib/wininet/stubs.c @@ -1,4 +1,5 @@ #include +#include #include #define NDEBUG diff --git a/reactos/lib/winmm/driver.c b/reactos/lib/winmm/driver.c index f03902712eb..9bda8da6dbf 100644 --- a/reactos/lib/winmm/driver.c +++ b/reactos/lib/winmm/driver.c @@ -226,7 +226,7 @@ BOOL DRIVER_GetLibName(LPCWSTR keyName, LPCWSTR sectName, LPWSTR buf, int sz) if (lRet == ERROR_SUCCESS) return TRUE; /* default to system.ini if we can't find it in the registry, * to support native installations where system.ini is still used */ - return GetPrivateProfileStringW(sectName, keyName, &wsznull, buf, sz, wszSystemIni); + return GetPrivateProfileStringW(sectName, keyName, &wsznull, buf, sz / sizeof(WCHAR), wszSystemIni); } /************************************************************************** diff --git a/reactos/lib/winmm/mci.c b/reactos/lib/winmm/mci.c index 0ebeaa6b9e8..6cf7a639f52 100644 --- a/reactos/lib/winmm/mci.c +++ b/reactos/lib/winmm/mci.c @@ -278,6 +278,11 @@ static int MCI_MapMsgAtoW(UINT msg, DWORD_PTR dwParam1, DWORD_PTR *dwParam2) case MCI_UPDATE: case MCI_RESUME: case MCI_DELETE: + case MCI_MONITOR: + case MCI_SETAUDIO: + case MCI_SIGNAL: + case MCI_SETVIDEO: + case MCI_LIST: return 0; case MCI_OPEN: @@ -975,8 +980,8 @@ static LPCWSTR MCI_FindCommand(UINT uTbl, LPCWSTR verb) */ static DWORD MCI_GetReturnType(LPCWSTR lpCmd) { - lpCmd += strlenW(lpCmd) + 1 + sizeof(DWORD) + sizeof(WORD); - if (*lpCmd == '\0' && *(const WORD*)(lpCmd + 1 + sizeof(DWORD)) == MCI_RETURN) { + lpCmd = (LPCWSTR)((BYTE*)(lpCmd + strlenW(lpCmd) + 1) + sizeof(DWORD) + sizeof(WORD)); + if (*lpCmd == '\0' && *(const WORD*)((BYTE*)(lpCmd + 1) + sizeof(DWORD)) == MCI_RETURN) { return *(const DWORD*)(lpCmd + 1); } return 0L; @@ -1743,7 +1748,7 @@ errCleanUp: /************************************************************************** * MCI_Close [internal] */ -static DWORD MCI_Close(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms) +static DWORD MCI_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms) { DWORD dwRet; LPWINE_MCIDRIVER wmd; @@ -1835,7 +1840,7 @@ static DWORD MCI_SysInfo(UINT uDevID, DWORD dwFlags, LPMCI_SYSINFO_PARMSW lpParm RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0); RegCloseKey( hKey ); } - if (GetPrivateProfileStringW(wszMci, 0, wszNull, buf, sizeof(buf), wszSystemIni)) + if (GetPrivateProfileStringW(wszMci, 0, wszNull, buf, sizeof(buf) / sizeof(buf[0]), wszSystemIni)) for (s = buf; *s; s += strlenW(s) + 1) cnt++; } } else { @@ -1887,7 +1892,7 @@ static DWORD MCI_SysInfo(UINT uDevID, DWORD dwFlags, LPMCI_SYSINFO_PARMSW lpParm RegCloseKey( hKey ); } if (!s) { - if (GetPrivateProfileStringW(wszMci, 0, wszNull, buf, sizeof(buf), wszSystemIni)) { + if (GetPrivateProfileStringW(wszMci, 0, wszNull, buf, sizeof(buf) / sizeof(buf[0]), wszSystemIni)) { for (p = buf; *p; p += strlenW(p) + 1, cnt++) { TRACE("%ld: %s\n", cnt, debugstr_w(p)); if (cnt == lpParms->dwNumber - 1) { diff --git a/reactos/lib/winmm/time.c b/reactos/lib/winmm/time.c index ea8daef3697..dbd912e3904 100644 --- a/reactos/lib/winmm/time.c +++ b/reactos/lib/winmm/time.c @@ -408,9 +408,19 @@ MMRESULT WINAPI timeGetDevCaps(LPTIMECAPS lpCaps, UINT wSize) { TRACE("(%p, %u)\n", lpCaps, wSize); + if (lpCaps == 0) { + WARN("invalid lpCaps\n"); + return TIMERR_NOCANDO; + } + + if (wSize < sizeof(TIMECAPS)) { + WARN("invalid wSize\n"); + return TIMERR_NOCANDO; + } + lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL; lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL; - return 0; + return TIMERR_NOERROR; } /************************************************************************** diff --git a/reactos/lib/winmm/wavemap/wavemap.c b/reactos/lib/winmm/wavemap/wavemap.c index 9fb05d2db70..2ff43cdbe1e 100644 --- a/reactos/lib/winmm/wavemap/wavemap.c +++ b/reactos/lib/winmm/wavemap/wavemap.c @@ -62,6 +62,9 @@ typedef struct tagWAVEMAPDATA { /* ratio to compute position from a PCM playback to any format */ DWORD avgSpeedOuter; DWORD avgSpeedInner; + /* channel size of inner and outer */ + DWORD nSamplesPerSecOuter; + DWORD nSamplesPerSecInner; } WAVEMAPDATA; static BOOL WAVEMAP_IsData(WAVEMAPDATA* wm) @@ -177,6 +180,7 @@ static DWORD wodOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags) wom->dwClientInstance = lpDesc->dwInstance; wom->u.out.hOuterWave = (HWAVEOUT)lpDesc->hWave; wom->avgSpeedOuter = wom->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec; + wom->nSamplesPerSecOuter = wom->nSamplesPerSecInner = lpDesc->lpFormat->nSamplesPerSec; for (i = ndlo; i < ndhi; i++) { /* if no ACM stuff is involved, no need to handle callbacks at this @@ -198,7 +202,7 @@ static DWORD wodOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags) #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \ switch (res=wodOpenHelper(wom, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \ - case MMSYSERR_NOERROR: wom->avgSpeedInner = wfx.nAvgBytesPerSec; goto found; \ + case MMSYSERR_NOERROR: wom->avgSpeedInner = wfx.nAvgBytesPerSec; wom->nSamplesPerSecInner = wfx.nSamplesPerSec; goto found; \ case WAVERR_BADFORMAT: break; \ default: goto error; \ } @@ -414,12 +418,59 @@ static DWORD wodUnprepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwPara static DWORD wodGetPosition(WAVEMAPDATA* wom, LPMMTIME lpTime, DWORD dwParam2) { DWORD val; + MMTIME timepos; TRACE("(%p %p %08lx)\n", wom, lpTime, dwParam2); - val = waveOutGetPosition(wom->u.out.hInnerWave, lpTime, dwParam2); - if (lpTime->wType == TIME_BYTES) - lpTime->u.cb = MulDiv(lpTime->u.cb, wom->avgSpeedOuter, wom->avgSpeedInner); - /* other time types don't require conversion */ + memcpy(&timepos, lpTime, sizeof(timepos)); + + /* For TIME_MS, we're going to recalculate using TIME_BYTES */ + if (lpTime->wType == TIME_MS) + timepos.wType = TIME_BYTES; + + val = waveOutGetPosition(wom->u.out.hInnerWave, &timepos, dwParam2); + + if (lpTime->wType == TIME_BYTES || lpTime->wType == TIME_MS) + { + DWORD dwInnerSamplesPerOuter = wom->nSamplesPerSecInner / wom->nSamplesPerSecOuter; + if (dwInnerSamplesPerOuter > 0) + { + DWORD dwInnerBytesPerSample = wom->avgSpeedInner / wom->nSamplesPerSecInner; + DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter; + DWORD remainder = 0; + + /* If we are up sampling (going from lower sample rate to higher), + ** we need to make a special accomodation for times when we've + ** written a partial output sample. This happens frequently + ** to us because we use msacm to do our up sampling, and it + ** will up sample on an unaligned basis. + ** For example, if you convert a 2 byte wide 8,000 'outer' + ** buffer to a 2 byte wide 48,000 inner device, you would + ** expect 2 bytes of input to produce 12 bytes of output. + ** Instead, msacm will produce 8 bytes of output. + ** But reporting our position as 1 byte of output is + ** nonsensical; the output buffer position needs to be + ** aligned on outer sample size, and aggressively rounded up. + */ + remainder = timepos.u.cb % dwInnerBytesPerOuterSample; + if (remainder > 0) + { + timepos.u.cb -= remainder; + timepos.u.cb += dwInnerBytesPerOuterSample; + } + } + + lpTime->u.cb = MulDiv(timepos.u.cb, wom->avgSpeedOuter, wom->avgSpeedInner); + + /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */ + if (lpTime->wType == TIME_MS) + lpTime->u.cb = MulDiv(lpTime->u.cb, 1000, wom->avgSpeedOuter); + } + else if (lpTime->wType == TIME_SAMPLES) + lpTime->u.cb = MulDiv(timepos.u.cb, wom->nSamplesPerSecOuter, wom->nSamplesPerSecInner); + else + /* other time types don't require conversion */ + lpTime->u = timepos.u; + return val; } @@ -691,6 +742,7 @@ static DWORD widOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags) } wim->avgSpeedOuter = wim->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec; + wim->nSamplesPerSecOuter = wim->nSamplesPerSecInner = lpDesc->lpFormat->nSamplesPerSec; for (i = ndlo; i < ndhi; i++) { if (waveInOpen(&wim->u.in.hInnerWave, i, lpDesc->lpFormat, (DWORD)widCallback, @@ -710,7 +762,7 @@ static DWORD widOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags) #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \ switch (res=widOpenHelper(wim, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \ - case MMSYSERR_NOERROR: wim->avgSpeedInner = wfx.nAvgBytesPerSec; goto found; \ + case MMSYSERR_NOERROR: wim->avgSpeedInner = wfx.nAvgBytesPerSec; wim->nSamplesPerSecInner = wfx.nSamplesPerSec; goto found; \ case WAVERR_BADFORMAT: break; \ default: goto error; \ } @@ -915,6 +967,8 @@ static DWORD widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2) val = waveInGetPosition(wim->u.in.hInnerWave, lpTime, dwParam2); if (lpTime->wType == TIME_BYTES) lpTime->u.cb = MulDiv(lpTime->u.cb, wim->avgSpeedOuter, wim->avgSpeedInner); + if (lpTime->wType == TIME_SAMPLES) + lpTime->u.cb = MulDiv(lpTime->u.cb, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner); /* other time types don't require conversion */ return val; } diff --git a/reactos/lib/winmm/winmm_Es.rc b/reactos/lib/winmm/winmm_Es.rc index 29be5a6ad9a..7c4712cada7 100644 --- a/reactos/lib/winmm/winmm_Es.rc +++ b/reactos/lib/winmm/winmm_Es.rc @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -STRINGTABLE LANGUAGE LANG_SPANISH, SUBLANG_DEFAULT +STRINGTABLE LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL BEGIN /* MMSYS errors */ diff --git a/reactos/lib/winspool/stubs.c b/reactos/lib/winspool/stubs.c index a257b934f3b..4caaa106d6c 100644 --- a/reactos/lib/winspool/stubs.c +++ b/reactos/lib/winspool/stubs.c @@ -10,6 +10,7 @@ #undef __USE_W32API #include +#include /* * @unimplemented diff --git a/reactos/lib/coredll/coredll_main.c b/reactos/lib/winsta/main.c similarity index 53% rename from reactos/lib/coredll/coredll_main.c rename to reactos/lib/winsta/main.c index 36170490ae9..b9d235df8a5 100644 --- a/reactos/lib/coredll/coredll_main.c +++ b/reactos/lib/winsta/main.c @@ -1,39 +1,50 @@ -/* - * ReactOS-CE Core OS Dll - * - * Copyright 2004 Steven Edwards - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * TODO: - * ATM this is just a dummy forwarding dll for the WinCE API to Win32 API - * When you exit a simple WinCE app on Windows or on ReactOS it causes - * a program error or unhandled exception probl'y because we dont handle - * threading and process attaching/detaching like we should here. - * - * There will be some parts of the WinCE API that cant just be forwarded to - * the Win32 API as paramater names or types may differ. In that case you will - * need to copy the Win32 implementation and make the needed changes. - */ - -#include "windows.h" - -BOOL STDCALL -DllMain(HANDLE hDll, - DWORD dwReason, - LPVOID lpReserved) -{ - return TRUE; -} +/* + * ReactOS WinStation + * Copyright (C) 2005 ReactOS Team + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* $Id$ + * + * PROJECT: ReactOS winsta.dll + * FILE: lib/winsta/main.c + * PURPOSE: WinStation + * PROGRAMMER: Emanuele Aliberti + */ +#include + +HINSTANCE hDllInstance; + + +BOOL STDCALL +DllMain(HINSTANCE hinstDLL, + DWORD dwReason, + LPVOID lpvReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + hDllInstance = hinstDLL; + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +/* EOF */ diff --git a/reactos/lib/winsta/makefile b/reactos/lib/winsta/makefile new file mode 100644 index 00000000000..e4dad8bd3f0 --- /dev/null +++ b/reactos/lib/winsta/makefile @@ -0,0 +1,23 @@ +# $Id$ + +PATH_TO_TOP = ../.. + +TARGET_TYPE = dynlink + +TARGET_NAME = winsta + +TARGET_CFLAGS = -g -Werror -Wall + +TARGET_OBJECTS = \ + main.o \ + misc.o \ + server.o \ + ws.o + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + +include $(TOOLS_PATH)/depend.mk + +# EOF diff --git a/reactos/lib/winsta/misc.c b/reactos/lib/winsta/misc.c new file mode 100644 index 00000000000..9c030ab7cd5 --- /dev/null +++ b/reactos/lib/winsta/misc.c @@ -0,0 +1,12 @@ +/* $Id$ */ +#include +#include "winsta.h" + +WINSTASTUB(LogonIdFromWinStationNameA) +WINSTASTUB(LogonIdFromWinStationNameW) +WINSTASTUB(RemoteAssistancePrepareSystemRestore) +WINSTASTUB(_NWLogonQueryAdmin) +WINSTASTUB(_NWLogonSetAdmin) +/* EOF */ + + diff --git a/reactos/lib/winsta/server.c b/reactos/lib/winsta/server.c new file mode 100644 index 00000000000..d54107537b3 --- /dev/null +++ b/reactos/lib/winsta/server.c @@ -0,0 +1,21 @@ +/* $Id$ */ +#include +#include "winsta.h" + +WINSTASTUB(ServerGetInternetConnectorStatus) +WINSTASTUB(ServerLicensingClose) +WINSTASTUB(ServerLicensingDeactivateCurrentPolicy) +WINSTASTUB(ServerLicensingFreePolicyInformation) +WINSTASTUB(ServerLicensingGetAvailablePolicyIds) +WINSTASTUB(ServerLicensingGetPolicy) +WINSTASTUB(ServerLicensingGetPolicyInformationA) +WINSTASTUB(ServerLicensingGetPolicyInformationW) +WINSTASTUB(ServerLicensingLoadPolicy) +WINSTASTUB(ServerLicensingOpenA) +WINSTASTUB(ServerLicensingOpenW) +WINSTASTUB(ServerLicensingSetPolicy) +WINSTASTUB(ServerLicensingUnloadPolicy) +WINSTASTUB(ServerQueryInetConnectorInformationA) +WINSTASTUB(ServerQueryInetConnectorInformationW) +WINSTASTUB(ServerSetInternetConnectorStatus) +/* EOF */ diff --git a/reactos/lib/winsta/winsta.def b/reactos/lib/winsta/winsta.def new file mode 100644 index 00000000000..34dd802e025 --- /dev/null +++ b/reactos/lib/winsta/winsta.def @@ -0,0 +1,100 @@ +; $Id$ +LIBRARY winsta.dll +EXPORTS +LogonIdFromWinStationNameA@0 +LogonIdFromWinStationNameW@0 +RemoteAssistancePrepareSystemRestore@0 +ServerGetInternetConnectorStatus@0 +ServerLicensingClose@0 +ServerLicensingDeactivateCurrentPolicy@0 +ServerLicensingFreePolicyInformation@0 +ServerLicensingGetAvailablePolicyIds@0 +ServerLicensingGetPolicy@0 +ServerLicensingGetPolicyInformationA@0 +ServerLicensingGetPolicyInformationW@0 +ServerLicensingLoadPolicy@0 +ServerLicensingOpenA@0 +ServerLicensingOpenW@0 +ServerLicensingSetPolicy@0 +ServerLicensingUnloadPolicy@0 +ServerQueryInetConnectorInformationA@0 +ServerQueryInetConnectorInformationW@0 +ServerSetInternetConnectorStatus@0 +WinStationActivateLicense@0 +WinStationAutoReconnect@0 +WinStationBroadcastSystemMessage@0 +WinStationCheckLoopBack@0 +WinStationCloseServer@0 +WinStationConnectA@0 +WinStationConnectCallback@0 +WinStationConnectW@0 +WinStationDisconnect@0 +WinStationEnumerateA@0 +WinStationEnumerateLicenses@0 +WinStationEnumerateProcesses@0 +WinStationEnumerateW@0 +WinStationEnumerate_IndexedA@0 +WinStationEnumerate_IndexedW@0 +WinStationFreeGAPMemory@0 +WinStationFreeMemory@0 +WinStationGenerateLicense@0 +WinStationGetAllProcesses@0 +WinStationGetLanAdapterNameA@0 +WinStationGetLanAdapterNameW@0 +WinStationGetMachinePolicy@0 +WinStationGetProcessSid@0 +WinStationGetTermSrvCountersValue@0 +WinStationInstallLicense@0 +WinStationIsHelpAssistantSession@0 +WinStationNameFromLogonIdA@0 +WinStationNameFromLogonIdW@0 +WinStationNtsdDebug@0 +WinStationOpenServerA@0 +WinStationOpenServerW@0 +WinStationQueryInformationA@0 +WinStationQueryInformationW@0 +WinStationQueryLicense@0 +WinStationQueryLogonCredentialsW@0 +WinStationQueryUpdateRequired@0 +WinStationRegisterConsoleNotification@0 +WinStationRemoveLicense@0 +WinStationRenameA@0 +WinStationRenameW@0 +WinStationRequestSessionsList@0 +WinStationReset@0 +WinStationSendMessageA@0 +WinStationSendMessageW@0 +WinStationSendWindowMessage@0 +WinStationServerPing@0 +WinStationSetInformationA@0 +WinStationSetInformationW@0 +WinStationSetPoolCount@0 +WinStationShadow@0 +WinStationShadowStop@0 +WinStationShutdownSystem@0 +WinStationTerminateProcess@0 +WinStationUnRegisterConsoleNotification@0 +WinStationVirtualOpen@0 +WinStationWaitSystemEvent@0 +_NWLogonQueryAdmin@0 +_NWLogonSetAdmin@0 +_WinStationAnnoyancePopup@0 +_WinStationBeepOpen@0 +_WinStationBreakPoint@0 +_WinStationCallback@0 +_WinStationCheckForApplicationName@0 +_WinStationFUSCanRemoteUserDisconnect@0 +_WinStationGetApplicationInfo@0 +_WinStationNotifyDisconnectPipe@0 +_WinStationNotifyLogoff@0 +_WinStationNotifyLogon@0 +_WinStationNotifyNewSession@0 +_WinStationReInitializeSecurity@0 +_WinStationReadRegistry@0 +_WinStationShadowTarget@0 +_WinStationShadowTargetSetup@0 +_WinStationUpdateClientCachedCredentials@0 +_WinStationUpdateSettings@0 +_WinStationUpdateUserConfig@0 +_WinStationWaitForConnect@0 +; EOF diff --git a/reactos/lib/winsta/winsta.h b/reactos/lib/winsta/winsta.h new file mode 100644 index 00000000000..86e74ea2ab0 --- /dev/null +++ b/reactos/lib/winsta/winsta.h @@ -0,0 +1,7 @@ +#if !defined(_WINSTA_H) +#define _WINSTA_H + +#define WINSTASTUB(n) VOID STDCALL n (VOID) { } + +#endif /* !def _WINSTA_H */ + diff --git a/reactos/lib/winsta/winsta.rc b/reactos/lib/winsta/winsta.rc new file mode 100644 index 00000000000..d9c3a50873c --- /dev/null +++ b/reactos/lib/winsta/winsta.rc @@ -0,0 +1,4 @@ +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS WinStation\0" +#define REACTOS_STR_INTERNAL_NAME "winsta\0" +#define REACTOS_STR_ORIGINAL_FILENAME "winsta.dll\0" +#include diff --git a/reactos/lib/winsta/ws.c b/reactos/lib/winsta/ws.c new file mode 100644 index 00000000000..fd574959dc4 --- /dev/null +++ b/reactos/lib/winsta/ws.c @@ -0,0 +1,80 @@ +/* $Id$ */ +#include +#include "winsta.h" + +WINSTASTUB(WinStationActivateLicense) +WINSTASTUB(WinStationAutoReconnect) +WINSTASTUB(WinStationBroadcastSystemMessage) +WINSTASTUB(WinStationCheckLoopBack) +WINSTASTUB(WinStationCloseServer) +WINSTASTUB(WinStationConnectA) +WINSTASTUB(WinStationConnectCallback) +WINSTASTUB(WinStationConnectW) +WINSTASTUB(WinStationDisconnect) +WINSTASTUB(WinStationEnumerateA) +WINSTASTUB(WinStationEnumerateLicenses) +WINSTASTUB(WinStationEnumerateProcesses) +WINSTASTUB(WinStationEnumerateW) +WINSTASTUB(WinStationEnumerate_IndexedA) +WINSTASTUB(WinStationEnumerate_IndexedW) +WINSTASTUB(WinStationFreeGAPMemory) +WINSTASTUB(WinStationFreeMemory) +WINSTASTUB(WinStationGenerateLicense) +WINSTASTUB(WinStationGetAllProcesses) +WINSTASTUB(WinStationGetLanAdapterNameA) +WINSTASTUB(WinStationGetLanAdapterNameW) +WINSTASTUB(WinStationGetMachinePolicy) +WINSTASTUB(WinStationGetProcessSid) +WINSTASTUB(WinStationGetTermSrvCountersValue) +WINSTASTUB(WinStationInstallLicense) +WINSTASTUB(WinStationIsHelpAssistantSession) +WINSTASTUB(WinStationNameFromLogonIdA) +WINSTASTUB(WinStationNameFromLogonIdW) +WINSTASTUB(WinStationNtsdDebug) +WINSTASTUB(WinStationOpenServerA) +WINSTASTUB(WinStationOpenServerW) +WINSTASTUB(WinStationQueryInformationA) +WINSTASTUB(WinStationQueryInformationW) +WINSTASTUB(WinStationQueryLicense) +WINSTASTUB(WinStationQueryLogonCredentialsW) +WINSTASTUB(WinStationQueryUpdateRequired) +WINSTASTUB(WinStationRegisterConsoleNotification) +WINSTASTUB(WinStationRemoveLicense) +WINSTASTUB(WinStationRenameA) +WINSTASTUB(WinStationRenameW) +WINSTASTUB(WinStationRequestSessionsList) +WINSTASTUB(WinStationReset) +WINSTASTUB(WinStationSendMessageA) +WINSTASTUB(WinStationSendMessageW) +WINSTASTUB(WinStationSendWindowMessage) +WINSTASTUB(WinStationServerPing) +WINSTASTUB(WinStationSetInformationA) +WINSTASTUB(WinStationSetInformationW) +WINSTASTUB(WinStationSetPoolCount) +WINSTASTUB(WinStationShadow) +WINSTASTUB(WinStationShadowStop) +WINSTASTUB(WinStationShutdownSystem) +WINSTASTUB(WinStationTerminateProcess) +WINSTASTUB(WinStationUnRegisterConsoleNotification) +WINSTASTUB(WinStationVirtualOpen) +WINSTASTUB(WinStationWaitSystemEvent) +WINSTASTUB(_WinStationAnnoyancePopup) +WINSTASTUB(_WinStationBeepOpen) +WINSTASTUB(_WinStationBreakPoint) +WINSTASTUB(_WinStationCallback) +WINSTASTUB(_WinStationCheckForApplicationName) +WINSTASTUB(_WinStationFUSCanRemoteUserDisconnect) +WINSTASTUB(_WinStationGetApplicationInfo) +WINSTASTUB(_WinStationNotifyDisconnectPipe) +WINSTASTUB(_WinStationNotifyLogoff) +WINSTASTUB(_WinStationNotifyLogon) +WINSTASTUB(_WinStationNotifyNewSession) +WINSTASTUB(_WinStationReInitializeSecurity) +WINSTASTUB(_WinStationReadRegistry) +WINSTASTUB(_WinStationShadowTarget) +WINSTASTUB(_WinStationShadowTargetSetup) +WINSTASTUB(_WinStationUpdateClientCachedCredentials) +WINSTASTUB(_WinStationUpdateSettings) +WINSTASTUB(_WinStationUpdateUserConfig) +WINSTASTUB(_WinStationWaitForConnect) +/* EOF */ diff --git a/reactos/lib/wintrust/wintrust.def b/reactos/lib/wintrust/wintrust.def index e1c6977ffcf..0ffac317848 100644 --- a/reactos/lib/wintrust/wintrust.def +++ b/reactos/lib/wintrust/wintrust.def @@ -1,3 +1,131 @@ - LIBRARY wintrust.dll +EXPORTS +;CryptCATVerifyMember +;CryptSIPGetInfo +;CryptSIPGetRegWorkingFlags +;GenericChainCertificateTrust +;GenericChainFinalProv +;HTTPSCertificateTrust +;SoftpubDefCertInit +;SoftpubFreeDefUsageCallData +;SoftpubLoadDefUsageCallData +;WTHelperCertFindIssuerCertificate +;AddPersonalTrustDBPages +;CatalogCompactHashDatabase +;CryptCATAdminAcquireContext +;CryptCATAdminAddCatalog +;CryptCATAdminCalcHashFromFileHandle +;CryptCATAdminEnumCatalogFromHash +;CryptCATAdminPauseServiceForBackup +;CryptCATAdminReleaseCatalogContext +;CryptCATAdminReleaseContext +;CryptCATAdminRemoveCatalog +;CryptCATAdminResolveCatalogPath +;CryptCATCDFClose +;CryptCATCDFEnumAttributes +;CryptCATCDFEnumAttributesWithCDFTag +;CryptCATCDFEnumCatAttributes +;CryptCATCDFEnumMembers +;CryptCATCDFEnumMembersByCDFTag +;CryptCATCDFEnumMembersByCDFTagEx +;CryptCATCDFOpen +;CryptCATCatalogInfoFromContext +;CryptCATClose +;CryptCATEnumerateAttr +;CryptCATEnumerateCatAttr +;CryptCATEnumerateMember +;CryptCATGetAttrInfo +;CryptCATGetCatAttrInfo +;CryptCATGetMemberInfo +;CryptCATHandleFromStore +;CryptCATOpen +;CryptCATPersistStore +;CryptCATPutAttrInfo +;CryptCATPutCatAttrInfo +;CryptCATPutMemberInfo +;CryptCATStoreFromHandle +;CryptSIPCreateIndirectData +;CryptSIPGetSignedDataMsg +;CryptSIPPutSignedDataMsg +;CryptSIPRemoveSignedDataMsg +;CryptSIPVerifyIndirectData +;DllRegisterServer +;DllUnregisterServer +;DriverCleanupPolicy +;DriverFinalPolicy +;DriverInitializePolicy +;FindCertsByIssuer +;HTTPSFinalProv +;I_CryptCatAdminMigrateToNewCatDB +;IsCatalogFile +;MsCatConstructHashTag +;MsCatFreeHashTag +;OfficeCleanupPolicy +;OfficeInitializePolicy +;OpenPersonalTrustDBDialog +;OpenPersonalTrustDBDialogEx +;SoftpubAuthenticode +;SoftpubCheckCert +;SoftpubCleanup +;SoftpubDllRegisterServer +;SoftpubDllUnregisterServer +;SoftpubDumpStructure +;SoftpubInitialize +;SoftpubLoadMessage +;SoftpubLoadSignature +;TrustDecode +;TrustFindIssuerCertificate +;TrustFreeDecode +;TrustIsCertificateSelfSigned +;TrustOpenStores +;WTHelperCertCheckValidSignature +;WTHelperCertIsSelfSigned +;WTHelperCheckCertUsage +;WTHelperGetAgencyInfo +;WTHelperGetFileHandle +;WTHelperGetFileHash +;WTHelperGetFileName +;WTHelperGetKnownUsages +;WTHelperGetProvCertFromChain +;WTHelperGetProvPrivateDataFromChain +;WTHelperGetProvSignerFromChain +;WTHelperIsInRootStore +;WTHelperOpenKnownStores +;WTHelperProvDataFromStateData +;WVTAsn1CatMemberInfoDecode +;WVTAsn1CatMemberInfoEncode +;WVTAsn1CatNameValueDecode +;WVTAsn1CatNameValueEncode +;WVTAsn1SpcFinancialCriteriaInfoDecode +;WVTAsn1SpcFinancialCriteriaInfoEncode +;WVTAsn1SpcIndirectDataContentDecode +;WVTAsn1SpcIndirectDataContentEncode +;WVTAsn1SpcLinkDecode +;WVTAsn1SpcLinkEncode +;WVTAsn1SpcMinimalCriteriaInfoDecode +;WVTAsn1SpcMinimalCriteriaInfoEncode +;WVTAsn1SpcPeImageDataDecode +;WVTAsn1SpcPeImageDataEncode +;WVTAsn1SpcSigInfoDecode +;WVTAsn1SpcSigInfoEncode +;WVTAsn1SpcSpAgencyInfoDecode +;WVTAsn1SpcSpAgencyInfoEncode +;WVTAsn1SpcSpOpusInfoDecode +;WVTAsn1SpcSpOpusInfoEncode +;WVTAsn1SpcStatementTypeDecode +;WVTAsn1SpcStatementTypeEncode +WinVerifyTrust@12 +;WinVerifyTrustEx +WintrustAddActionID +;WintrustAddDefaultForUsage +;WintrustCertificateTrust +;WintrustGetDefaultForUsage +;WintrustGetRegPolicyFlags +;WintrustLoadFunctionPointers +;WintrustRemoveActionID +;WintrustSetRegPolicyFlags +;mscat32DllRegisterServer +;mscat32DllUnregisterServer +;mssip32DllRegisterServer +;mssip32DllUnregisterServer ; EOF diff --git a/reactos/lib/ws2_32/include/ws2_32.h b/reactos/lib/ws2_32/include/ws2_32.h index 7cb725b250e..001970b348e 100644 --- a/reactos/lib/ws2_32/include/ws2_32.h +++ b/reactos/lib/ws2_32/include/ws2_32.h @@ -18,7 +18,7 @@ #include #undef assert #include -#include // DNS_A_DATA +#include // DNS_A_DATA /* Exported by ntdll.dll, but where is the prototype? */ unsigned long strtoul(const char *nptr, char **endptr, int base); diff --git a/reactos/media/drivers/etc/KDBinit b/reactos/media/drivers/etc/KDBinit new file mode 100644 index 00000000000..53a68e1e23a --- /dev/null +++ b/reactos/media/drivers/etc/KDBinit @@ -0,0 +1,15 @@ +# Example KDB.init file +# +# The disassembly flavor is set to "intel" (default is "at&t") and the +# + +# Set the disassembly flavor to "intel" (default is "at&t") +set syntax intel + +# Change the condition to enter KDB on INT3 to "always" (default is "kmode") +set condition INT3 first always + +# This is a special command available only in the KDB.init file - it breaks into +# KDB when it is interpreting the init file at startup. +#break + diff --git a/reactos/media/themes/.gitignore b/reactos/media/themes/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/reactos/ntoskrnl/cm/cm.h b/reactos/ntoskrnl/cm/cm.h index f3579cd777b..952b6c8a5c5 100644 --- a/reactos/ntoskrnl/cm/cm.h +++ b/reactos/ntoskrnl/cm/cm.h @@ -353,6 +353,12 @@ typedef struct _KEY_OBJECT /* List of subkeys loaded */ struct _KEY_OBJECT **SubKeys; + + /* List entry into the global key object list */ + LIST_ENTRY ListEntry; + + /* Time stamp for the last access by the parse routine */ + ULONG TimeStamp; } KEY_OBJECT, *PKEY_OBJECT; /* Bits 31-22 (top 10 bits) of the cell index is the directory index */ @@ -529,10 +535,11 @@ CmiAddKeyToList(IN PKEY_OBJECT ParentKey, NTSTATUS CmiRemoveKeyFromList(IN PKEY_OBJECT NewKey); -PKEY_OBJECT +NTSTATUS CmiScanKeyList(IN PKEY_OBJECT Parent, IN PUNICODE_STRING KeyName, - IN ULONG Attributes); + IN ULONG Attributes, + PKEY_OBJECT* ReturnedObject); NTSTATUS CmiCreateVolatileHive(PREGISTRY_HIVE *RegistryHive); diff --git a/reactos/ntoskrnl/cm/ntfunc.c b/reactos/ntoskrnl/cm/ntfunc.c index b44a7f0f9cc..9b1f635992b 100644 --- a/reactos/ntoskrnl/cm/ntfunc.c +++ b/reactos/ntoskrnl/cm/ntfunc.c @@ -21,6 +21,7 @@ extern POBJECT_TYPE CmiKeyType; extern PREGISTRY_HIVE CmiVolatileHive; +extern LIST_ENTRY CmiKeyObjectListHead; static BOOLEAN CmiRegistryInitialized = FALSE; @@ -300,6 +301,8 @@ NtCreateKey(OUT PHANDLE KeyHandle, KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE); + InsertTailList(&CmiKeyObjectListHead, &KeyObject->ListEntry); + /* add key to subkeys of parent if needed */ Status = CmiAddSubKey(KeyObject->RegistryHive, KeyObject->ParentKey, @@ -355,7 +358,7 @@ NtCreateKey(OUT PHANDLE KeyHandle, ExReleaseResourceLite(&CmiRegistryLock); KeLeaveCriticalRegion(); - ObDereferenceObject(KeyObject); + ObDereferenceObject(Object); if (Disposition) @@ -419,6 +422,9 @@ NtDeleteKey(IN HANDLE KeyHandle) /* Dereference the object */ ObDereferenceObject(KeyObject); + /* Remove the keep-alive reference */ + ObDereferenceObject(KeyObject); + if (KeyObject->RegistryHive != KeyObject->ParentKey->RegistryHive) ObDereferenceObject(KeyObject); @@ -454,9 +460,12 @@ NtEnumerateKey(IN HANDLE KeyHandle, PKEY_FULL_INFORMATION FullInformation; PDATA_CELL ClassCell; ULONG NameSize, ClassSize; + KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); DPRINT("KH %x I %d KIC %x KI %x L %d RL %x\n", KeyHandle, @@ -470,7 +479,7 @@ NtEnumerateKey(IN HANDLE KeyHandle, Status = ObReferenceObjectByHandle(KeyHandle, KEY_ENUMERATE_SUB_KEYS, CmiKeyType, - UserMode, + PreviousMode, (PVOID *) &KeyObject, NULL); if (!NT_SUCCESS(Status)) @@ -513,8 +522,7 @@ NtEnumerateKey(IN HANDLE KeyHandle, for (i = 0; i < KeyObject->NumberOfSubKeys; i++) { CurKey = KeyObject->SubKeys[i]; - if (CurKey->RegistryHive == CmiVolatileHive || - CurKey->RegistryHive != RegistryHive) + if (CurKey->RegistryHive != RegistryHive) { if (j == Index) break; @@ -1051,7 +1059,7 @@ NtFlushKey(IN HANDLE KeyHandle) /* Verify that the handle is valid and is a registry key */ Status = ObReferenceObjectByHandle(KeyHandle, - KEY_QUERY_VALUE, + 0, CmiKeyType, PreviousMode, (PVOID *)&KeyObject, @@ -1213,7 +1221,7 @@ NtQueryKey(IN HANDLE KeyHandle, /* Verify that the handle is valid and is a registry key */ Status = ObReferenceObjectByHandle(KeyHandle, - KEY_READ, + (KeyInformationClass != KeyNameInformation ? KEY_QUERY_VALUE : 0), CmiKeyType, UserMode, (PVOID *) &KeyObject, @@ -1372,6 +1380,13 @@ NtQueryKey(IN HANDLE KeyHandle, } break; + case KeyNameInformation: + case KeyCachedInformation: + case KeyFlagsInformation: + DPRINT1("Key information class 0x%x not yet implemented!\n", KeyInformationClass); + Status = STATUS_NOT_IMPLEMENTED; + break; + default: DPRINT1("Not handling 0x%x\n", KeyInformationClass); Status = STATUS_INVALID_INFO_CLASS; @@ -1653,14 +1668,12 @@ NtSetValueKey(IN HANDLE KeyHandle, KeyHandle, ValueName, Type); DesiredAccess = KEY_SET_VALUE; - if (Type == REG_LINK) - DesiredAccess |= KEY_CREATE_LINK; /* Verify that the handle is valid and is a registry key */ Status = ObReferenceObjectByHandle(KeyHandle, DesiredAccess, CmiKeyType, - UserMode, + ExGetPreviousMode(), (PVOID *)&KeyObject, NULL); if (!NT_SUCCESS(Status)) @@ -1998,6 +2011,34 @@ NtNotifyChangeKey (IN HANDLE KeyHandle, return(STATUS_NOT_IMPLEMENTED); } +#if 0 +NTSTATUS STDCALL +NtNotifyChangeKey (IN HANDLE KeyHandle, + IN HANDLE Event, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG CompletionFilter, + IN BOOLEAN WatchSubtree, + OUT PVOID Buffer, + IN ULONG Length, + IN BOOLEAN Asynchronous) +{ + return NtNotifyChangeMultipleKeys(KeyHandle, + 0, + NULL, + Event, + ApcRoutine, + ApcContext, + IoStatusBlock, + CompletionFilter, + WatchTree, + Buffer, + Length, + Asynchronous); +} + +#endif NTSTATUS STDCALL NtQueryMultipleValueKey (IN HANDLE KeyHandle, diff --git a/reactos/ntoskrnl/cm/registry.c b/reactos/ntoskrnl/cm/registry.c index 304192c8e7d..671afd06bae 100644 --- a/reactos/ntoskrnl/cm/registry.c +++ b/reactos/ntoskrnl/cm/registry.c @@ -20,12 +20,15 @@ POBJECT_TYPE CmiKeyType = NULL; PREGISTRY_HIVE CmiVolatileHive = NULL; -KSPIN_LOCK CmiKeyListLock; LIST_ENTRY CmiHiveListHead; ERESOURCE CmiRegistryLock; +KTIMER CmiWorkerTimer; +LIST_ENTRY CmiKeyObjectListHead; +ULONG CmiTimer = 0; + volatile BOOLEAN CmiHiveSyncEnabled = FALSE; volatile BOOLEAN CmiHiveSyncPending = FALSE; KDPC CmiHiveSyncDpc; @@ -241,6 +244,92 @@ CmiCheckRegistry(BOOLEAN Verbose) CmiCheckByName(Verbose, L"User"); } +VOID STDCALL +CmiWorkerThread(PVOID Param) +{ + NTSTATUS Status; + PLIST_ENTRY CurrentEntry; + PKEY_OBJECT CurrentKey; + ULONG Count; + + + while (1) + { + Status = KeWaitForSingleObject(&CmiWorkerTimer, + Executive, + KernelMode, + FALSE, + NULL); + if (Status == STATUS_SUCCESS) + { + DPRINT("CmiWorkerThread\n"); + + /* Acquire hive lock */ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE); + + CmiTimer++; + + Count = 0; + CurrentEntry = CmiKeyObjectListHead.Blink; + while (CurrentEntry != &CmiKeyObjectListHead) + { + CurrentKey = CONTAINING_RECORD(CurrentEntry, KEY_OBJECT, ListEntry); + if (CurrentKey->TimeStamp + 120 > CmiTimer) + { + /* The object was accessed in the last 10min */ + break; + } + if (1 == ObGetObjectPointerCount(CurrentKey) && + !(CurrentKey->Flags & KO_MARKED_FOR_DELETE)) + { + ObDereferenceObject(CurrentKey); + CurrentEntry = CmiKeyObjectListHead.Blink; + Count++; + } + else + { + CurrentEntry = CurrentEntry->Blink; + } + } + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + + DPRINT("Removed %d key objects\n", Count); + + } + else + { + KEBUGCHECK(0); + } + } +} + +VOID +INIT_FUNCTION +STDCALL +CmInitHives(BOOLEAN SetupBoot) +{ + PCHAR BaseAddress; + + /* Load Registry Hives. This one can be missing. */ + if (CachedModules[SystemRegistry]) { + BaseAddress = (PCHAR)CachedModules[SystemRegistry]->ModStart; + CmImportSystemHive(BaseAddress, + CachedModules[SystemRegistry]->ModEnd - (ULONG_PTR)BaseAddress); + } + + BaseAddress = (PCHAR)CachedModules[HardwareRegistry]->ModStart; + CmImportHardwareHive(BaseAddress, + CachedModules[HardwareRegistry]->ModEnd - (ULONG_PTR)BaseAddress); + + + /* Create dummy keys if no hardware hive was found */ + CmImportHardwareHive (NULL, 0); + + /* Initialize volatile registry settings */ + if (SetupBoot == FALSE) CmInit2((PCHAR)KeLoaderBlock.CommandLine); +} VOID INIT_FUNCTION CmInitializeRegistry(VOID) @@ -254,6 +343,9 @@ CmInitializeRegistry(VOID) HANDLE RootKeyHandle; HANDLE KeyHandle; NTSTATUS Status; + LARGE_INTEGER DueTime; + HANDLE ThreadHandle; + CLIENT_ID ThreadId; /* Initialize the Key object type */ CmiKeyType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); @@ -286,6 +378,29 @@ CmInitializeRegistry(VOID) /* Initialize registry lock */ ExInitializeResourceLite(&CmiRegistryLock); + /* Initialize the key object list */ + InitializeListHead(&CmiKeyObjectListHead); + + /* Initialize the worker timer */ + KeInitializeTimerEx(&CmiWorkerTimer, SynchronizationTimer); + + /* Initialize the worker thread */ + Status = PsCreateSystemThread(&ThreadHandle, + THREAD_ALL_ACCESS, + NULL, + NULL, + &ThreadId, + CmiWorkerThread, + NULL); + if (!NT_SUCCESS(Status)) + { + KEBUGCHECK(0); + } + + /* Start the timer */ + DueTime.QuadPart = -1; + KeSetTimerEx(&CmiWorkerTimer, DueTime, 5000, NULL); /* 5sec */ + /* Build volatile registry store */ Status = CmiCreateVolatileHive (&CmiVolatileHive); ASSERT(NT_SUCCESS(Status)); @@ -308,7 +423,7 @@ CmInitializeRegistry(VOID) ASSERT(NT_SUCCESS(Status)); Status = ObInsertObject(RootKey, NULL, - STANDARD_RIGHTS_REQUIRED, + KEY_ALL_ACCESS, 0, NULL, &RootKeyHandle); @@ -321,6 +436,7 @@ CmInitializeRegistry(VOID) RootKey->NumberOfSubKeys = 0; RootKey->SubKeys = NULL; RootKey->SizeOfSubKeys = 0; + InsertTailList(&CmiKeyObjectListHead, &RootKey->ListEntry); Status = RtlpCreateUnicodeString(&RootKey->Name, L"Registry", NonPagedPool); ASSERT(NT_SUCCESS(Status)); @@ -336,7 +452,6 @@ CmInitializeRegistry(VOID) CmiVolatileHive->RootSecurityCell = RootSecurityCell; #endif - KeInitializeSpinLock(&CmiKeyListLock); /* Create '\Registry\Machine' key. */ RtlInitUnicodeString(&KeyName, @@ -347,7 +462,7 @@ CmInitializeRegistry(VOID) RootKeyHandle, NULL); Status = ZwCreateKey(&KeyHandle, - STANDARD_RIGHTS_REQUIRED, + KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, @@ -364,7 +479,7 @@ CmInitializeRegistry(VOID) RootKeyHandle, NULL); Status = ZwCreateKey(&KeyHandle, - STANDARD_RIGHTS_REQUIRED, + KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, @@ -648,6 +763,7 @@ CmiConnectHive(IN POBJECT_ATTRIBUTES KeyObjectAttributes, NewKey->KeyCell = CmiGetCell (RegistryHive, NewKey->KeyCellOffset, NULL); NewKey->Flags = 0; NewKey->NumberOfSubKeys = 0; + InsertTailList(&CmiKeyObjectListHead, &NewKey->ListEntry); if (NewKey->KeyCell->NumberOfSubKeys != 0) { NewKey->SubKeys = ExAllocatePool(NonPagedPool, @@ -702,6 +818,8 @@ CmiDisconnectHive (IN POBJECT_ATTRIBUTES KeyObjectAttributes, PREGISTRY_HIVE Hive; HANDLE KeyHandle; NTSTATUS Status; + PLIST_ENTRY CurrentEntry; + PKEY_OBJECT CurrentKey; DPRINT("CmiDisconnectHive() called\n"); @@ -741,11 +859,36 @@ CmiDisconnectHive (IN POBJECT_ATTRIBUTES KeyObjectAttributes, return STATUS_INVALID_PARAMETER; } + /* Acquire registry lock exclusively */ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE); + + CurrentEntry = CmiKeyObjectListHead.Flink; + while (CurrentEntry != &CmiKeyObjectListHead) + { + CurrentKey = CONTAINING_RECORD(CurrentEntry, KEY_OBJECT, ListEntry); + if (1 == ObGetObjectPointerCount(CurrentKey) && + !(CurrentKey->Flags & KO_MARKED_FOR_DELETE)) + { + ObDereferenceObject(CurrentKey); + CurrentEntry = CmiKeyObjectListHead.Flink; + } + else + { + CurrentEntry = CurrentEntry->Flink; + } + } + if (ObGetObjectHandleCount (KeyObject) != 0 || ObGetObjectPointerCount (KeyObject) != 2) { - DPRINT1 ("Hive is still in use\n"); + DPRINT1 ("Hive is still in use (hc %d, rc %d)\n", ObGetObjectHandleCount (KeyObject), ObGetObjectPointerCount (KeyObject)); ObDereferenceObject (KeyObject); + + /* Release registry lock */ + ExReleaseResourceLite (&CmiRegistryLock); + KeLeaveCriticalRegion(); + return STATUS_UNSUCCESSFUL; } @@ -757,6 +900,10 @@ CmiDisconnectHive (IN POBJECT_ATTRIBUTES KeyObjectAttributes, *RegistryHive = Hive; + /* Release registry lock */ + ExReleaseResourceLite (&CmiRegistryLock); + KeLeaveCriticalRegion(); + DPRINT ("CmiDisconnectHive() done\n"); return STATUS_SUCCESS; diff --git a/reactos/ntoskrnl/cm/regobj.c b/reactos/ntoskrnl/cm/regobj.c index 0bb1984e724..4a5ffa8bc04 100644 --- a/reactos/ntoskrnl/cm/regobj.c +++ b/reactos/ntoskrnl/cm/regobj.c @@ -14,6 +14,8 @@ #include "cm.h" +extern LIST_ENTRY CmiKeyObjectListHead; +extern ULONG CmiTimer; static NTSTATUS CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive, @@ -76,10 +78,22 @@ CmiObjectParse(PVOID ParsedObject, KeyName.Length); KeyName.Buffer[KeyName.Length / sizeof(WCHAR)] = 0; + /* Acquire hive lock */ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE); - FoundObject = CmiScanKeyList(ParsedKey, - &KeyName, - Attributes); + + Status = CmiScanKeyList(ParsedKey, + &KeyName, + Attributes, + &FoundObject); + if (!NT_SUCCESS(Status)) + { + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + RtlFreeUnicodeString(&KeyName); + return Status; + } if (FoundObject == NULL) { Status = CmiScanForSubKey(ParsedKey->RegistryHive, @@ -91,6 +105,8 @@ CmiObjectParse(PVOID ParsedObject, Attributes); if (!NT_SUCCESS(Status) || (SubKeyCell == NULL)) { + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); RtlFreeUnicodeString(&KeyName); return(STATUS_UNSUCCESSFUL); } @@ -104,6 +120,9 @@ CmiObjectParse(PVOID ParsedObject, &LinkPath); if (NT_SUCCESS(Status)) { + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + DPRINT("LinkPath '%wZ'\n", &LinkPath); /* build new FullPath for reparsing */ @@ -140,7 +159,7 @@ CmiObjectParse(PVOID ParsedObject, } /* Create new key object and put into linked list */ - DPRINT("CmiObjectParse: %s\n", Path); + DPRINT("CmiObjectParse: %S\n", *Path); Status = ObCreateObject(KernelMode, CmiKeyType, NULL, @@ -152,14 +171,19 @@ CmiObjectParse(PVOID ParsedObject, (PVOID*)&FoundObject); if (!NT_SUCCESS(Status)) { + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); RtlFreeUnicodeString(&KeyName); return(Status); } + /* Add the keep-alive reference */ + ObReferenceObject(FoundObject); FoundObject->Flags = 0; FoundObject->KeyCell = SubKeyCell; FoundObject->KeyCellOffset = BlockOffset; FoundObject->RegistryHive = ParsedKey->RegistryHive; + InsertTailList(&CmiKeyObjectListHead, &FoundObject->ListEntry); RtlpCreateUnicodeString(&FoundObject->Name, KeyName.Buffer, NonPagedPool); CmiAddKeyToList(ParsedKey, FoundObject); @@ -179,6 +203,11 @@ CmiObjectParse(PVOID ParsedObject, if (NT_SUCCESS(Status)) { DPRINT("LinkPath '%wZ'\n", &LinkPath); + + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + + ObDereferenceObject(FoundObject); /* build new FullPath for reparsing */ TargetPath.MaximumLength = LinkPath.MaximumLength; @@ -212,13 +241,15 @@ CmiObjectParse(PVOID ParsedObject, return(STATUS_REPARSE); } } - - ObReferenceObjectByPointer(FoundObject, - STANDARD_RIGHTS_REQUIRED, - NULL, - UserMode); } + RemoveEntryList(&FoundObject->ListEntry); + InsertHeadList(&CmiKeyObjectListHead, &FoundObject->ListEntry); + FoundObject->TimeStamp = CmiTimer; + + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + DPRINT("CmiObjectParse: %s\n", FoundObject->Name); *Path = EndPtr; @@ -274,11 +305,16 @@ CmiObjectDelete(PVOID DeletedObject) ObReferenceObject (ParentKeyObject); + /* Acquire hive lock */ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&CmiRegistryLock, TRUE); + if (!NT_SUCCESS(CmiRemoveKeyFromList(KeyObject))) { DPRINT1("Key not found in parent list ???\n"); } + RemoveEntryList(&KeyObject->ListEntry); RtlFreeUnicodeString(&KeyObject->Name); if (KeyObject->Flags & KO_MARKED_FOR_DELETE) @@ -302,6 +338,9 @@ CmiObjectDelete(PVOID DeletedObject) ObDereferenceObject (ParentKeyObject); + ExReleaseResourceLite(&CmiRegistryLock); + KeLeaveCriticalRegion(); + if (KeyObject->NumberOfSubKeys) { KEBUGCHECK(REGISTRY_ERROR); @@ -532,11 +571,9 @@ VOID CmiAddKeyToList(PKEY_OBJECT ParentKey, PKEY_OBJECT NewKey) { - KIRQL OldIrql; DPRINT("ParentKey %.08x\n", ParentKey); - KeAcquireSpinLock(&CmiKeyListLock, &OldIrql); if (ParentKey->SizeOfSubKeys <= ParentKey->NumberOfSubKeys) { @@ -568,7 +605,6 @@ CmiAddKeyToList(PKEY_OBJECT ParentKey, NULL, UserMode); NewKey->ParentKey = ParentKey; - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); } @@ -576,11 +612,9 @@ NTSTATUS CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove) { PKEY_OBJECT ParentKey; - KIRQL OldIrql; DWORD Index; ParentKey = KeyToRemove->ParentKey; - KeAcquireSpinLock(&CmiKeyListLock, &OldIrql); /* FIXME: If list maintained in alphabetic order, use dichotomic search */ for (Index = 0; Index < ParentKey->NumberOfSubKeys; Index++) { @@ -591,7 +625,6 @@ CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove) &ParentKey->SubKeys[Index + 1], (ParentKey->NumberOfSubKeys - Index - 1) * sizeof(PKEY_OBJECT)); ParentKey->NumberOfSubKeys--; - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); DPRINT("Dereference parent key: 0x%x\n", ParentKey); @@ -599,25 +632,23 @@ CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove) return STATUS_SUCCESS; } } - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); return STATUS_UNSUCCESSFUL; } -PKEY_OBJECT +NTSTATUS CmiScanKeyList(PKEY_OBJECT Parent, PUNICODE_STRING KeyName, - ULONG Attributes) + ULONG Attributes, + PKEY_OBJECT* ReturnedObject) { PKEY_OBJECT CurKey; - KIRQL OldIrql; ULONG Index; - + DPRINT("Scanning key list for: %wZ (Parent: %wZ)\n", KeyName, &Parent->Name); - KeAcquireSpinLock(&CmiKeyListLock, &OldIrql); /* FIXME: if list maintained in alphabetic order, use dichotomic search */ for (Index=0; Index < Parent->NumberOfSubKeys; Index++) { @@ -627,8 +658,7 @@ CmiScanKeyList(PKEY_OBJECT Parent, if ((KeyName->Length == CurKey->Name.Length) && (_wcsicmp(KeyName->Buffer, CurKey->Name.Buffer) == 0)) { - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); - return CurKey; + break; } } else @@ -636,14 +666,26 @@ CmiScanKeyList(PKEY_OBJECT Parent, if ((KeyName->Length == CurKey->Name.Length) && (wcscmp(KeyName->Buffer, CurKey->Name.Buffer) == 0)) { - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); - return CurKey; + break; } } } - KeReleaseSpinLock(&CmiKeyListLock, OldIrql); - - return NULL; + + if (Index < Parent->NumberOfSubKeys) + { + if (CurKey->Flags & KO_MARKED_FOR_DELETE) + { + *ReturnedObject = NULL; + return STATUS_UNSUCCESSFUL; + } + ObReferenceObject(CurKey); + *ReturnedObject = CurKey; + } + else + { + *ReturnedObject = NULL; + } + return STATUS_SUCCESS; } diff --git a/reactos/ntoskrnl/dbg/i386/i386-dis.c b/reactos/ntoskrnl/dbg/i386/i386-dis.c index 486406610c3..95c5f4c619a 100644 --- a/reactos/ntoskrnl/dbg/i386/i386-dis.c +++ b/reactos/ntoskrnl/dbg/i386/i386-dis.c @@ -52,10 +52,10 @@ extern long MmSafeCopyFromUser(void *Dest, void *Src, unsigned long NumberOfByte int -print_insn_i386_att (bfd_vma pc, struct disassemble_info *info); +print_insn_i386 (bfd_vma pc, struct disassemble_info *info); int -KdbPrintDisasm(void* Ignored, const char* fmt, ...) +KdbpPrintDisasm(void* Ignored, const char* fmt, ...) { va_list ap; static char buffer[256]; @@ -69,46 +69,46 @@ KdbPrintDisasm(void* Ignored, const char* fmt, ...) } int -KdbNopPrintDisasm(void* Ignored, const char* fmt, ...) +KdbpNopPrintDisasm(void* Ignored, const char* fmt, ...) { return(0); } int static -KdbReadMemory(unsigned int Addr, unsigned char* Data, unsigned int Length, - struct disassemble_info * Ignored) +KdbpReadMemory(unsigned int Addr, unsigned char* Data, unsigned int Length, + struct disassemble_info * Ignored) { return KdbpSafeReadMemory(Data, (void *)Addr, Length); /* 0 means no error */ } void static -KdbMemoryError(int Status, unsigned int Addr, - struct disassemble_info * Ignored) +KdbpMemoryError(int Status, unsigned int Addr, + struct disassemble_info * Ignored) { } void static -KdbPrintAddressInCode(unsigned int Addr, struct disassemble_info * Ignored) +KdbpPrintAddressInCode(unsigned int Addr, struct disassemble_info * Ignored) { if (!KdbSymPrintAddress((void*)Addr)) { - DbgPrint("<0x%X>", Addr); + DbgPrint("<%08x>", Addr); } } void static -KdbNopPrintAddress(unsigned int Addr, struct disassemble_info * Ignored) +KdbpNopPrintAddress(unsigned int Addr, struct disassemble_info * Ignored) { } #include "dis-asm.h" long -KdbGetInstLength(unsigned int Address) +KdbpGetInstLength(unsigned int Address) { disassemble_info info; - info.fprintf_func = KdbNopPrintDisasm; + info.fprintf_func = KdbpNopPrintDisasm; info.stream = NULL; info.application_data = NULL; info.flavour = bfd_target_unknown_flavour; @@ -116,9 +116,9 @@ KdbGetInstLength(unsigned int Address) info.mach = bfd_mach_i386_i386; info.insn_sets = 0; info.flags = 0; - info.read_memory_func = KdbReadMemory; - info.memory_error_func = KdbMemoryError; - info.print_address_func = KdbNopPrintAddress; + info.read_memory_func = KdbpReadMemory; + info.memory_error_func = KdbpMemoryError; + info.print_address_func = KdbpNopPrintAddress; info.symbol_at_address_func = NULL; info.buffer = NULL; info.buffer_vma = info.buffer_length = 0; @@ -126,25 +126,25 @@ KdbGetInstLength(unsigned int Address) info.display_endian = BIG_ENDIAN_LITTLE; info.disassembler_options = NULL; - return(print_insn_i386_att(Address, &info)); + return(print_insn_i386(Address, &info)); } long -KdbDisassemble(unsigned int Address) +KdbpDisassemble(unsigned int Address, unsigned long IntelSyntax) { disassemble_info info; - info.fprintf_func = KdbPrintDisasm; + info.fprintf_func = KdbpPrintDisasm; info.stream = NULL; info.application_data = NULL; info.flavour = bfd_target_unknown_flavour; info.arch = bfd_arch_i386; - info.mach = bfd_mach_i386_i386; + info.mach = IntelSyntax ? bfd_mach_i386_i386_intel_syntax : bfd_mach_i386_i386; info.insn_sets = 0; info.flags = 0; - info.read_memory_func = KdbReadMemory; - info.memory_error_func = KdbMemoryError; - info.print_address_func = KdbPrintAddressInCode; + info.read_memory_func = KdbpReadMemory; + info.memory_error_func = KdbpMemoryError; + info.print_address_func = KdbpPrintAddressInCode; info.symbol_at_address_func = NULL; info.buffer = NULL; info.buffer_vma = info.buffer_length = 0; @@ -152,7 +152,7 @@ KdbDisassemble(unsigned int Address) info.display_endian = BIG_ENDIAN_LITTLE; info.disassembler_options = NULL; - return(print_insn_i386_att(Address, &info)); + return(print_insn_i386(Address, &info)); } /* Print i386 instructions for GDB, the GNU debugger. @@ -2113,7 +2113,7 @@ print_insn (pc, info) #else mode_64bit = 0; priv.orig_sizeflag = AFLAG | DFLAG; - intel_syntax = 0; + /*intel_syntax = 0;*/ #endif if (intel_syntax) diff --git a/reactos/ntoskrnl/dbg/i386/kdb_help.S b/reactos/ntoskrnl/dbg/i386/kdb_help.S index 2da6570fb36..cda895a42d9 100644 --- a/reactos/ntoskrnl/dbg/i386/kdb_help.S +++ b/reactos/ntoskrnl/dbg/i386/kdb_help.S @@ -1,29 +1,19 @@ #include #include - .data -_KdbEipTemp: - .int 0 +.text - .text -.globl _KdbEnter +.globl _KdbEnter _KdbEnter: - /* - * Record when we are inside the debugger. - */ - incl _KdbEntryCount - - /* - * Save the callers eip. - */ - popl _KdbEipTemp - /* * Set up a trap frame */ + /* Ss - space already reserved by return EIP */ + pushl %esp /* Esp */ pushfl /* Eflags */ pushl %cs /* Cs */ - pushl _KdbEipTemp /* Eip */ + pushl 12(%esp) /* Eip */ + movl %ss, 16(%esp) /* Save Ss */ pushl $0 /* ErrorCode */ pushl %ebp /* Ebp */ pushl %ebx /* Ebx */ @@ -56,34 +46,35 @@ _KdbEnter: pushl $0 /* TempEip */ pushl $0 /* TempCs */ pushl $0 /* DebugPointer */ - pushl $0 /* DebugArgMark */ - pushl _KdbEipTemp /* DebugEip */ + pushl $3 /* DebugArgMark (Exception number) */ + pushl 0x60(%esp) /* DebugEip */ pushl %ebp /* DebugEbp */ - /* - * Push a pointer to the trap frame - */ - pushl %esp - /* * Call KDB */ - call _KdbInternalEnter + movl %esp, %eax + pushl $1 /* FirstChance */ + pushl %eax /* Push a pointer to the trap frame */ + pushl $0 /* Context */ + pushl $0 /* PreviousMode (KernelMode) */ + pushl $0 /* ExceptionRecord */ + call _KdbEnterDebuggerException /* - * Pop the argument + * Pop the arguments and unused portions of the trap frame: + * DebugEbp + * DebugEip + * DebugArgMark + * DebugPointer + * TempCs + * TempEip */ - popl %eax + addl $(11*4), %esp /* - * Ignore unused portions of the trap frame. + * Restore/update debugging registers. */ - popl %eax /* DebugEbp */ - popl %eax /* DebugEip */ - popl %eax /* DebugArgMark */ - popl %eax /* DebugPointer */ - popl %eax /* TempCs */ - popl %eax /* TempEip */ popl %eax /* Dr0 */ movl %eax, %dr0 popl %eax /* Dr1 */ @@ -113,13 +104,17 @@ _KdbEnter: popl %edi /* Edi */ popl %esi /* Esi */ popl %ebx /* Ebx */ - popl %ebp /* Ebp */ - addl $4, %esp /* ErrorCode */ - /* - * Record when we are in the debugger. - */ - decl _KdbEntryCount + /* Remove SS:ESP from the stack */ + movl 16(%esp), %ebp + movl %ebp, 24(%esp) + movl 12(%esp), %ebp + movl %ebp, 20(%esp) + movl 8(%esp), %ebp + movl %ebp, 16(%esp) + + popl %ebp /* Ebp */ + addl $12, %esp /* ErrorCode and SS:ESP */ /* * Return to the caller. @@ -127,5 +122,26 @@ _KdbEnter: iret +.globl _KdbpStackSwitchAndCall@8 +_KdbpStackSwitchAndCall@8: + pushl %ebp + movl %esp, %ebp + + movl 0x8(%esp), %eax /* New stack */ + movl 0xC(%esp), %ecx /* Function to call */ + movl %esp, %edx /* Old stack */ + + /* Switch stack */ + movl %eax, %esp + pushl %edx + + /* Call function */ + call *%ecx + + /* Switch back to old stack */ + popl %esp + + /* Return */ + popl %ebp + ret $8 - diff --git a/reactos/ntoskrnl/dbg/i386/longjmp.S b/reactos/ntoskrnl/dbg/i386/longjmp.S new file mode 100644 index 00000000000..1edbaaf2ebb --- /dev/null +++ b/reactos/ntoskrnl/dbg/i386/longjmp.S @@ -0,0 +1,70 @@ + .file "longjmp.S" +/* + * Copyright (C) 1998, 1999, Jonathan S. Shapiro. + * + * This file is part of the EROS Operating System. + * + * 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, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + /* + * typedef struct { + * unsigned long ebx, esi, edi; + * unsigned long ebp; + * unsigned long sp; + * unsigned long pc; + * } jmp_buf[1]; + */ + + /* + * On entry, the stack to longjmp looks like: + * + * value + * ptr to jmp_buf + * return PC + */ + +.globl _longjmp +_longjmp: + pushl %ebp + movl %esp,%ebp + + movl 8(%ebp),%ecx /* address of jmp_buf to ecx */ + movl 12(%ebp),%eax /* return value to %eax */ + testl %eax,%eax + jne 1f + incl %eax /* return 1 if handed 0 */ + +1: + movl (%ecx),%ebx /* restore %ebx */ + movl 4(%ecx),%esi /* restore %esi */ + movl 8(%ecx),%edi /* restore %edi */ + + /* + * From this instant on we are not running in a valid frame + */ + + movl 12(%ecx),%ebp /* restore %ebp */ + movl 16(%ecx),%esp /* restore %esp */ + /* movl 20(%ecx),%eax return PC */ + + /* + * Since we are abandoning the stack in any case, + * there isn't much point in doing the usual return + * discipline. + */ + + jmpl *20(%ecx) + diff --git a/reactos/ntoskrnl/dbg/i386/setjmp.S b/reactos/ntoskrnl/dbg/i386/setjmp.S new file mode 100644 index 00000000000..d53e0592f38 --- /dev/null +++ b/reactos/ntoskrnl/dbg/i386/setjmp.S @@ -0,0 +1,59 @@ + .file "setjmp.S" +/* + * Copyright (C) 1998, 1999, Jonathan S. Shapiro. + * + * This file is part of the EROS Operating System. + * + * 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, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* #include */ + + + /* + * typedef struct { + * unsigned long ebx, esi, edi; + * unsigned long ebp; + * unsigned long sp; + * unsigned long pc; + * } jmp_buf[1]; + */ + + /* + * On entry, the stack to setjmp looks like: + * + * ptr to jmp_buf + * return PC + */ +.globl _setjmp +_setjmp: + pushl %ebp + movl %esp,%ebp + + movl 0x8(%ebp),%eax /* address of jmp_buf to eax */ + movl %ebx,(%eax) /* save %ebx */ + movl %esi,4(%eax) /* save %esi */ + movl %edi,8(%eax) /* save %edi */ + leal 8(%ebp),%edx /* calling proc's esp, not ours! */ + movl %edx,16(%eax) + movl 4(%ebp), %edx /* save return PC */ + movl %edx,20(%eax) + movl 0(%ebp),%edx /* calling proc's ebp, not ours! */ + movl %edx,12(%eax) + + xorl %eax,%eax /* return 0 the first time */ + leave + ret $4 + diff --git a/reactos/ntoskrnl/dbg/kdb.c b/reactos/ntoskrnl/dbg/kdb.c index fa97e8879e9..f6a08630398 100644 --- a/reactos/ntoskrnl/dbg/kdb.c +++ b/reactos/ntoskrnl/dbg/kdb.c @@ -12,1786 +12,1495 @@ #include #include "kdb.h" -#include "kjs.h" #define NDEBUG #include /* TYPES *********************************************************************/ +/* DEFINES *******************************************************************/ + +#define KDB_STACK_SIZE (4096*3) +#define KDB_MAXIMUM_BREAKPOINT_COUNT 256 +#define KDB_MAXIMUM_HW_BREAKPOINT_COUNT 4 +#define KDB_MAXIMUM_SW_BREAKPOINT_COUNT 256 + +#define __STRING(x) #x +#define _STRING(x) __STRING(x) + /* GLOBALS *******************************************************************/ -#define BS 8 -#define DEL 127 +STATIC LONG KdbEntryCount = 0; +STATIC CHAR KdbStack[KDB_STACK_SIZE]; -BOOL KbdEchoOn = TRUE; +STATIC ULONG KdbBreakPointCount = 0; /* Number of used breakpoints in the array */ +STATIC KDB_BREAKPOINT KdbBreakPoints[KDB_MAXIMUM_BREAKPOINT_COUNT] = {{0}}; /* Breakpoint array */ +STATIC ULONG KdbSwBreakPointCount = 0; /* Number of enabled software breakpoints */ +STATIC ULONG KdbHwBreakPointCount = 0; /* Number of enabled hardware breakpoints */ +STATIC PKDB_BREAKPOINT KdbSwBreakPoints[KDB_MAXIMUM_SW_BREAKPOINT_COUNT]; /* Enabled software breakpoints, orderless */ +STATIC PKDB_BREAKPOINT KdbHwBreakPoints[KDB_MAXIMUM_HW_BREAKPOINT_COUNT]; /* Enabled hardware breakpoints, orderless */ +STATIC PKDB_BREAKPOINT KdbBreakPointToReenable = NULL; /* Set to a breakpoint struct when single stepping after + a software breakpoint was hit, to reenable it */ +LONG KdbLastBreakPointNr = -1; /* Index of the breakpoint which cause KDB to be entered */ +ULONG KdbNumSingleSteps = 0; /* How many single steps to do */ +BOOLEAN KdbSingleStepOver = FALSE; /* Whether to step over calls/reps. */ -typedef struct +STATIC BOOLEAN KdbEnteredOnSingleStep = FALSE; /* Set to true when KDB was entered because of single step */ +PEPROCESS KdbCurrentProcess = NULL; /* The current process context in which KDB runs */ +PEPROCESS KdbOriginalProcess = NULL; /* The process in whichs context KDB was intered */ +PETHREAD KdbCurrentThread = NULL; /* The current thread context in which KDB runs */ +PETHREAD KdbOriginalThread = NULL; /* The thread in whichs context KDB was entered */ +PKDB_KTRAP_FRAME KdbCurrentTrapFrame = NULL; /* Pointer to the current trapframe */ +STATIC KDB_KTRAP_FRAME KdbTrapFrame = { { 0 } }; /* The trapframe which was passed to KdbEnterDebuggerException */ +STATIC KDB_KTRAP_FRAME KdbThreadTrapFrame = { { 0 } }; /* The trapframe of the current thread (KdbCurrentThread) */ +STATIC KAPC_STATE KdbApcState; + +/* Array of conditions when to enter KDB */ +STATIC KDB_ENTER_CONDITION KdbEnterConditions[][2] = { - BOOLEAN Enabled; - BOOLEAN Temporary; - BOOLEAN Assigned; - ULONG Address; - UCHAR SavedInst; -} KDB_ACTIVE_BREAKPOINT; - -#define KDB_MAXIMUM_BREAKPOINT_COUNT (255) - -static ULONG KdbBreakPointCount = 0; -static KDB_ACTIVE_BREAKPOINT - KdbActiveBreakPoints[KDB_MAXIMUM_BREAKPOINT_COUNT]; - -static BOOLEAN KdbHandleUmode = FALSE; -static BOOLEAN KdbHandleHandled = FALSE; -static BOOLEAN KdbBreakOnModuleLoad = FALSE; - -static BOOLEAN KdbIgnoreNextSingleStep = FALSE; -static ULONG KdbLastSingleStepFrom = 0xFFFFFFFF; -static BOOLEAN KdbEnteredOnSingleStep = FALSE; - -ULONG KdbEntryCount = 0; - -int isalpha( int ); -VOID -PsDumpThreads(BOOLEAN System); -ULONG -DbgContCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgStopCondition(ULONG Aargc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgModuleLoadedAction(ULONG Aargc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgEchoToggle(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgDRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgCRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgBugCheckCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgBackTraceCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgAddrCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgXCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgScriptCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgThreadListCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgProcessListCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgProcessHelpCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgShowFilesCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgEnableFileCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgDisableFileCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgDisassemble(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgSetBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgDeleteBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgSetMemoryBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgStep(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgStepOver(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgFinish(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); - -struct -{ - PCH Name; - PCH Syntax; - PCH Help; - ULONG (*Fn)(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -} DebuggerCommands[] = { - {"cont", "cont", "Exit the debugger", DbgContCommand}, - {"echo", "echo", "Toggle serial echo", DbgEchoToggle}, - {"condition", "condition [all|umode|kmode]", "Kdbg enter condition", DbgStopCondition}, - {"module-loaded", "module-loaded [break|continue]", "Module-loaded action", DbgModuleLoadedAction}, - - {"regs", "regs", "Display general purpose registers", DbgRegsCommand}, - {"dregs", "dregs", "Display debug registers", DbgDRegsCommand}, - {"cregs", "cregs", "Display control registers", DbgCRegsCommand}, - {"bugcheck", "bugcheck", "Bugcheck the system", DbgBugCheckCommand}, - {"bt", "bt [*frame-address]|[thread-id]","Do a backtrace", DbgBackTraceCommand}, - {"addr", "addr
", "Displays symbol info", DbgAddrCommand}, - {"x", "x ", "Displays for ", DbgXCommand}, - {"plist", "plist", "Display processes in the system", DbgProcessListCommand}, - {"tlist", "tlist [sys]", "Display threads in the system", DbgThreadListCommand}, - {"sfiles", "sfiles", "Show files that print debug prints", DbgShowFilesCommand}, - {"efile", "efile ", "Enable debug prints from file", DbgEnableFileCommand}, - {"dfile", "dfile ", "Disable debug prints from file", DbgDisableFileCommand}, - {"js", "js", "Script mode", DbgScriptCommand}, - {"disasm", "disasm
", "Disables 10 instructions at
or " - "eip", DbgDisassemble}, - {"bp", "bp
", "Sets an int3 breakpoint at a given address", - DbgSetBreakPoint}, - {"bc", "bc ", "Deletes a breakpoint", - DbgDeleteBreakPoint}, - {"ba", "ba
", - "Sets a breakpoint using a debug register", DbgSetMemoryBreakPoint}, - {"t", "t", "Steps forward a single instructions", DbgStep}, - {"p", "p", "Steps forward a single instructions skipping calls", - DbgStepOver}, - {"finish", "finish", "Runs until the current function exits", DbgFinish}, - {"help", "help", "Display help screen", DbgProcessHelpCommand}, - {NULL, NULL, NULL} + /* First chance Last chance */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* Zero devide */ + { KdbEnterAlways, KdbDoNotEnter }, /* Debug trap */ + { KdbDoNotEnter, KdbEnterAlways }, /* NMI */ + { KdbEnterFromKmode, KdbDoNotEnter }, /* INT3 */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* Overflow */ + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, /* Invalid opcode */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* No math coprocessor fault */ + { KdbEnterAlways, KdbEnterAlways }, + { KdbEnterAlways, KdbEnterAlways }, + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, /* Stack fault */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* General protection fault */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* Page fault */ + { KdbEnterAlways, KdbEnterAlways }, /* Reserved (15) */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* FPU fault */ + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, /* SIMD fault */ + { KdbDoNotEnter, KdbEnterFromKmode } /* Last entry: used for unknown exceptions */ }; -static const char *ExceptionTypeStrings[] = - { - "Divide Error", - "Debug Trap", - "NMI", - "Breakpoint", - "Overflow", - "BOUND range exceeded", - "Invalid Opcode", - "No Math Coprocessor", - "Double Fault", - "Unknown(9)", - "Invalid TSS", - "Segment Not Present", - "Stack Segment Fault", - "General Protection", - "Page Fault", - "Reserved(15)", - "Math Fault", - "Alignment Check", - "Machine Check", - "SIMD Fault" - }; - -volatile DWORD x_dr0 = 0, x_dr1 = 0, x_dr2 = 0, x_dr3 = 0, x_dr7 = 0; - -extern LONG KdbDisassemble(ULONG Address); -extern LONG KdbGetInstLength(ULONG Address); +/* Exception descriptions */ +STATIC CONST PCHAR ExceptionNrToString[] = +{ + "Divide Error", + "Debug Trap", + "NMI", + "Breakpoint", + "Overflow", + "BOUND range exceeded", + "Invalid Opcode", + "No Math Coprocessor", + "Double Fault", + "Unknown(9)", + "Invalid TSS", + "Segment Not Present", + "Stack Segment Fault", + "General Protection", + "Page Fault", + "Reserved(15)", + "Math Fault", + "Alignment Check", + "Machine Check", + "SIMD Fault" +}; /* FUNCTIONS *****************************************************************/ -/* - * Convert a string to an unsigned long integer. +/*!\brief Overwrites the instruction at \a Address with \a NewInst and stores + * the old instruction in *OldInst. * - * Ignores `locale' stuff. Assumes that the upper and lower case - * alphabets and digits are each contiguous. + * \param Process Process in which's context to overwrite the instruction. + * \param Address Address at which to overwrite the instruction. + * \param NewInst New instruction (written to \a Address) + * \param OldInst Old instruction (read from \a Address) + * + * \returns NTSTATUS */ -unsigned long -strtoul(const char *nptr, char **endptr, int base) +STATIC NTSTATUS +KdbpOverwriteInstruction( + IN PEPROCESS Process, + IN ULONG_PTR Address, + IN UCHAR NewInst, + OUT PUCHAR OldInst OPTIONAL) { - const char *s = nptr; - unsigned long acc; - int c; - unsigned long cutoff; - int neg = 0, any, cutlim; + NTSTATUS Status; + ULONG Protect; + PEPROCESS CurrentProcess = PsGetCurrentProcess(); + KAPC_STATE ApcState; - /* - * See strtol for comments as to the logic used. - */ - do { - c = *s++; - } while (isspace(c)); - if (c == '-') - { - neg = 1; - c = *s++; - } - else if (c == '+') - c = *s++; - if ((base == 0 || base == 16) && - c == '0' && (*s == 'x' || *s == 'X')) - { - c = s[1]; - s += 2; - base = 16; - } - if (base == 0) - base = c == '0' ? 8 : 10; - cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; - cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; - for (acc = 0, any = 0;; c = *s++) - { - if (isdigit(c)) - c -= '0'; - else if (isalpha(c)) - c -= isupper(c) ? 'A' - 10 : 'a' - 10; - else - break; - if (c >= base) - break; - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) - any = -1; - else { - any = 1; - acc *= base; - acc += c; - } - } - if (any < 0) - { - acc = ULONG_MAX; - } - else if (neg) - acc = -acc; - if (endptr != 0) - *endptr = any ? (char *)s - 1 : (char *)nptr; - return acc; -} - - -char* -strpbrk(const char* s, const char* accept) -{ - int i; - for (; (*s) != 0; s++) - { - for (i = 0; accept[i] != 0; i++) - { - if (accept[i] == (*s)) - { - return((char *)s); - } - } - } - return(NULL); -} - - -#if 0 -NTSTATUS -KdbpSafeReadMemory(PVOID dst, PVOID src, INT size) -{ - INT page, page_end; - - /* check source */ - page_end = (((ULONG_PTR)src + size) / PAGE_SIZE); - for (page = ((ULONG_PTR)src / PAGE_SIZE); page <= page_end; page++) - { - if (!MmIsPagePresent(NULL, (PVOID)(page * PAGE_SIZE))) - return STATUS_UNSUCCESSFUL; - } - - /* copy memory */ - RtlCopyMemory(dst, src, size); - return STATUS_SUCCESS; -} - - -NTSTATUS -KdbpSafeWriteMemory(PVOID dst, PVOID src, INT size) -{ - return KdbpSafeWriteMemory(dst, src, size); - INT page, page_end; - - /* check destination */ - page_end = (((ULONG_PTR)dst + size) / PAGE_SIZE); - for (page = ((ULONG_PTR)dst / PAGE_SIZE); page <= page_end; page++) - { - if (!MmIsPagePresent(NULL, (PVOID)(page * PAGE_SIZE))) - return STATUS_UNSUCCESSFUL; - } - - /* copy memory */ - RtlCopyMemory(dst, src, size); - return STATUS_SUCCESS; -} -#endif /* unused */ - - -VOID -KdbGetCommand(PCH Buffer) -{ - CHAR Key; - PCH Orig = Buffer; - static CHAR LastCommand[256] = ""; - ULONG ScanCode = 0; - static CHAR LastKey = '\0'; - - KbdEchoOn = !((KdDebugState & KD_DEBUG_KDNOECHO) != 0); - - for (;;) - { - if (KdDebugState & KD_DEBUG_KDSERIAL) - while ((Key = KdbTryGetCharSerial()) == -1); - else - while ((Key = KdbTryGetCharKeyboard(&ScanCode)) == -1); - - if (Key == '\n' && LastKey == '\r') - { - /* Ignore this key... */ - } - else if (Key == '\r' || Key == '\n') - { - DbgPrint("\n"); - /* - Repeat the last command if the user presses enter. Reduces the - risk of RSI when single-stepping. - */ - if (Buffer == Orig) - { - strcpy(Buffer, LastCommand); - } - else - { - *Buffer = 0; - strcpy(LastCommand, Orig); - } - LastKey = Key; - return; - } - else if (Key == BS || Key == DEL) - { - if (Buffer > Orig) - { - Buffer--; - *Buffer = 0; - if (KbdEchoOn) - DbgPrint("%c %c", BS, BS); - else - DbgPrint(" %c", BS); - } - } - else if (ScanCode == 72) - { - ULONG i; - while (Buffer > Orig) - { - Buffer--; - *Buffer = 0; - if (KbdEchoOn) - DbgPrint("%c %c", BS, BS); - else - DbgPrint(" %c", BS); - } - for (i = 0; LastCommand[i] != 0; i++) - { - if (KbdEchoOn) - DbgPrint("%c", LastCommand[i]); - *Buffer = LastCommand[i]; - Buffer++; - } - } - else - { - if (KbdEchoOn) - DbgPrint("%c", Key); - - *Buffer = Key; - Buffer++; - } - LastKey = Key; - } -} - -BOOLEAN STATIC -KdbDecodeAddress(PCHAR Buffer, PULONG Address) -{ - while (isspace(*Buffer)) - { - Buffer++; - } - if (Buffer[0] == '<') - { - PCHAR ModuleName = Buffer + 1; - PCHAR AddressString = strpbrk(Buffer, ":"); - extern LIST_ENTRY ModuleTextListHead; - PLIST_ENTRY current_entry; - MODULE_TEXT_SECTION* current = NULL; - static WCHAR ModuleNameW[256]; - ULONG i; - - if (AddressString == NULL) - { - DbgPrint("Address %x is malformed.\n", Buffer); - return(FALSE); - } - *AddressString = 0; - AddressString++; - while (isspace(*AddressString)) - { - AddressString++; - } - - for (i = 0; ModuleName[i] != 0 && !isspace(ModuleName[i]); i++) - { - ModuleNameW[i] = (WCHAR)ModuleName[i]; - } - ModuleNameW[i] = 0; - - /* Find the module. */ - current_entry = ModuleTextListHead.Flink; + /* Get the protection for the address. */ + Protect = MmGetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address)); - while (current_entry != &ModuleTextListHead && - current_entry != NULL) - { - current = - CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry); - if (wcscmp(ModuleNameW, current->Name) == 0) - { - break; - } - current_entry = current_entry->Flink; - } - if (current_entry == NULL || current_entry == &ModuleTextListHead) - { - DbgPrint("Couldn't find module %s.\n", ModuleName); - return(FALSE); - } - *Address = current->Base; - *Address += strtoul(AddressString, NULL, 16); - return(TRUE); - } - else - { - *Address = strtoul(Buffer, NULL, 0); - return(TRUE); - } -} + /* Return if that page isn't present. */ + if (Protect & PAGE_NOACCESS) + { + return STATUS_MEMORY_NOT_ALLOCATED; + } + + /* Attach to the process */ + if (CurrentProcess != Process) + { + KeStackAttachProcess(EPROCESS_TO_KPROCESS(Process), &ApcState); + } -NTSTATUS STATIC -KdbOverwriteInst(ULONG Address, PUCHAR PreviousInst, UCHAR NewInst) -{ - NTSTATUS Status; - ULONG Protect; - /* Get the protection for the address. */ - Protect = MmGetPageProtect(PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); - /* Return if that page isn't present. */ - if (Protect & PAGE_NOACCESS) - { - return(STATUS_MEMORY_NOT_ALLOCATED); - } - if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) - { - MmSetPageProtect(PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address), + /* Make the page writeable if it is read only. */ + if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) + { + MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), (Protect & ~(PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) | PAGE_READWRITE); - } - /* Copy the old instruction back to the caller. */ - if (PreviousInst != NULL) - { - Status = KdbpSafeReadMemory(PreviousInst, (PUCHAR)Address, 1); + } + + /* Copy the old instruction back to the caller. */ + if (OldInst != NULL) + { + Status = KdbpSafeReadMemory(OldInst, (PUCHAR)Address, 1); if (!NT_SUCCESS(Status)) - { - if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) - { - MmSetPageProtect(PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address), Protect); - } - return(Status); - } - } - /* Copy the new instruction in its place. */ - Status = KdbpSafeWriteMemory((PUCHAR)Address, &NewInst, 1); - if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) - { - MmSetPageProtect(PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address), Protect); - } - return Status; + { + if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) + { + MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), Protect); + } + /* Detach from process */ + if (CurrentProcess != Process) + { + KeDetachProcess(); + } + return Status; + } + } + + /* Copy the new instruction in its place. */ + Status = KdbpSafeWriteMemory((PUCHAR)Address, &NewInst, 1); + + /* Restore the page protection. */ + if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) + { + MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), Protect); + } + + /* Detach from process */ + if (CurrentProcess != Process) + { + KeUnstackDetachProcess(&ApcState); + } + + return Status; } - -VOID STATIC -KdbRenableBreakPoints(VOID) +/*!\brief Checks whether the given instruction can be single stepped or has to be + * stepped over using a temporary breakpoint. + * + * \retval TRUE Instruction is a call. + * \retval FALSE Instruction is not a call. + */ +BOOLEAN +KdbpShouldStepOverInstruction(ULONG_PTR Eip) { - ULONG i; - for (i = 0; i < KDB_MAXIMUM_BREAKPOINT_COUNT; i++) - { - if (KdbActiveBreakPoints[i].Assigned && - !KdbActiveBreakPoints[i].Enabled) - { - KdbActiveBreakPoints[i].Enabled = TRUE; - (VOID)KdbOverwriteInst(KdbActiveBreakPoints[i].Address, - &KdbActiveBreakPoints[i].SavedInst, - 0xCC); - } - } -} + UCHAR Mem[3]; + INT i = 0; -LONG STATIC -KdbIsBreakPointOurs(PKTRAP_FRAME Tf) -{ - ULONG i; - for (i = 0; i < KDB_MAXIMUM_BREAKPOINT_COUNT; i++) - { - if (KdbActiveBreakPoints[i].Assigned && - KdbActiveBreakPoints[i].Address == (Tf->Eip - 1)) - { - return(i); - } - } - return(-1); -} + if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Eip, sizeof (Mem)))) + { + KdbpPrint("Couldn't access memory at 0x%x\n", (UINT)Eip); + return FALSE; + } -VOID STATIC -KdbDeleteBreakPoint(ULONG BreakPointNr) -{ - KdbBreakPointCount--; - KdbActiveBreakPoints[BreakPointNr].Assigned = FALSE; -} - -NTSTATUS STATIC -KdbInsertBreakPoint(ULONG Address, BOOLEAN Temporary) -{ - NTSTATUS Status; - UCHAR SavedInst; - ULONG i; - if (KdbBreakPointCount == KDB_MAXIMUM_BREAKPOINT_COUNT) - { - return(STATUS_UNSUCCESSFUL); - } - for (i = 0; i < KDB_MAXIMUM_BREAKPOINT_COUNT; i++) - { - if (!KdbActiveBreakPoints[i].Assigned) - { - break; - } - } - Status = KdbOverwriteInst(Address, &SavedInst, 0xCC); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - KdbActiveBreakPoints[i].Assigned = TRUE; - KdbActiveBreakPoints[i].Enabled = TRUE; - KdbActiveBreakPoints[i].Address = Address; - KdbActiveBreakPoints[i].Temporary = Temporary; - KdbActiveBreakPoints[i].SavedInst = SavedInst; - return(STATUS_SUCCESS); -} - -ULONG -DbgSetBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG Addr; - NTSTATUS Status; - ULONG i; - if (Argc < 2) - { - DbgPrint("Need an address to set the breakpoint at.\n"); - return(1); - } - /* Stitch the remaining arguments back into a single string. */ - for (i = 2; i < Argc; i++) - { - Argv[i][-1] = ' '; - } - if (!KdbDecodeAddress(Argv[1], &Addr)) - { - return(1); - } - DbgPrint("Setting breakpoint at 0x%X\n", Addr); - if (!NT_SUCCESS(Status = KdbInsertBreakPoint(Addr, FALSE))) - { - DbgPrint("Failed to set breakpoint (Status %X)\n", Status); - } - return(1); -} - -ULONG -DbgDeleteBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG BreakPointNr; - if (Argc != 2) - { - DbgPrint("Need a breakpoint number to delete.\n"); - return(1); - } - BreakPointNr = strtoul(Argv[1], NULL, 10); - DbgPrint("Deleting breakpoint %d.\n", BreakPointNr); - KdbDeleteBreakPoint(BreakPointNr); - return(1); -} - -ULONG -DbgSetMemoryBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG DebugRegNr; - UCHAR BreakType; - ULONG Length, Address; - ULONG Rw = 0; - ULONG i; - if (Argc != 2 && Argc < 5) - { - DbgPrint("ba <0-3> <1|2|4>
\n"); - return(1); - } - DebugRegNr = strtoul(Argv[1], NULL, 10); - if (DebugRegNr >= 4) - { - DbgPrint("Debug register number should be between 0 and 3.\n"); - return(1); - } - if (Argc == 2) - { - /* Clear the breakpoint. */ - Tf->Dr7 &= ~(0x3 << (DebugRegNr * 2)); - if ((Tf->Dr7 & 0xFF) == 0) - { - /* - If no breakpoints are enabled then - clear the exact match flags. - */ - Tf->Dr7 &= 0xFFFFFCFF; - } - return(1); - } - BreakType = Argv[2][0]; - if (BreakType != 'r' && BreakType != 'w' && BreakType != 'e') - { - DbgPrint("Access type to break on should be either 'r', 'w' or 'e'.\n"); - return(1); - } - Length = strtoul(Argv[3], NULL, 10); - if (Length != 1 && Length != 2 && Length != 4) - { - DbgPrint("Length of the breakpoint should be one, two or four.\n"); - return(1); - } - if (Length != 1 && BreakType == 'e') - { - DbgPrint("The length of an execution breakpoint should be one.\n"); - return(1); - } - /* Stitch the remaining arguments back into a single string. */ - for (i = 4; i < Argc; i++) - { - Argv[i][-1] = ' '; - } - if (!KdbDecodeAddress(Argv[4], &Address)) - { - return(1); - } - if ((Address & (Length - 1)) != 0) - { - DbgPrint("The breakpoint address should be aligned to a multiple of " - "the breakpoint length.\n"); - return(1); - } - - /* Set the breakpoint address. */ - switch (DebugRegNr) - { - case 0: Tf->Dr0 = Address; break; - case 1: Tf->Dr1 = Address; break; - case 2: Tf->Dr2 = Address; break; - case 3: Tf->Dr3 = Address; break; - } - /* Enable the breakpoint. */ - Tf->Dr7 |= (0x3 << (DebugRegNr * 2)); - /* Enable the exact match bits. */ - Tf->Dr7 |= 0x00000300; - /* Clear existing state. */ - Tf->Dr7 &= ~(0xF << (16 + (DebugRegNr * 4))); - /* Set the breakpoint type. */ - switch (BreakType) - { - case 'r': Rw = 3; break; - case 'w': Rw = 1; break; - case 'e': Rw = 0; break; - } - Tf->Dr7 |= (Rw << (16 + (DebugRegNr * 4))); - /* Set the breakpoint length. */ - Tf->Dr7 |= ((Length - 1) << (18 + (DebugRegNr * 4))); - - return(1); -} - -ULONG -DbgStep(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - /* Set the single step flag and return to the interrupted code. */ - Tf->Eflags |= (1 << 8); - KdbIgnoreNextSingleStep = FALSE; - KdbLastSingleStepFrom = Tf->Eip; - return(0); -} - -ULONG -DbgStepOver(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - PUCHAR Eip; - UCHAR Mem[3]; - - if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Tf->Eip, sizeof (Mem)))) - { - DbgPrint("Couldn't access memory at 0x%x\n", (UINT)Tf->Eip); - return(1); - } - Eip = Mem; - - /* Check if the current instruction is a call. */ - while (Eip[0] == 0x66 || Eip[0] == 0x67) - { - Eip++; - } - if (Eip[0] == 0xE8 || Eip[0] == 0x9A || Eip[0] == 0xF2 || Eip[0] == 0xF3 || - (Eip[0] == 0xFF && (Eip[1] & 0x38) == 0x10)) - { - ULONG NextInst = Tf->Eip + KdbGetInstLength(Tf->Eip); - KdbLastSingleStepFrom = Tf->Eip; - KdbInsertBreakPoint(NextInst, TRUE); - return(0); - } - else - { - return(DbgStep(Argc, Argv, Tf)); - } -} - -ULONG -DbgFinish(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - PULONG Ebp = (PULONG)Tf->Ebp; - ULONG ReturnAddress; - NTSTATUS Status; - PKTHREAD CurrentThread; - - /* Check that ebp points onto the stack. */ - CurrentThread = KeGetCurrentThread(); - if (CurrentThread == NULL || - !(Ebp >= (PULONG)CurrentThread->StackLimit && - Ebp <= (PULONG)CurrentThread->StackBase)) - { - DbgPrint("This function doesn't appear to have a valid stack frame.\n"); - return(1); - } - - /* Get the address of the caller. */ - Status = KdbpSafeReadMemory(&ReturnAddress, Ebp + 1, sizeof(ULONG)); - if (!NT_SUCCESS(Status)) - { - DbgPrint("Memory access error (%X) while getting return address.\n", - Status); - return(1); - } - - /* Set a temporary breakpoint at that location. */ - Status = KdbInsertBreakPoint(ReturnAddress, TRUE); - if (!NT_SUCCESS(Status)) - { - DbgPrint("Couldn't set a temporary breakpoint at %X (Status %X)\n", - ReturnAddress, Status); - return(1); - } - - /* - Otherwise start running again and with any luck we will break back into - the debugger when the current function returns. - */ - return(0); -} - -ULONG -DbgDisassemble(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG Address, i; - LONG InstLen; - if (Argc >= 2) - { - /* Stitch the remaining arguments back into a single string. */ - for (i = 2; i < Argc; i++) - { - Argv[i][-1] = ' '; - } - if (!KdbDecodeAddress(Argv[1], &Address)) - { - return(1); - } - } - else - { - Address = Tf->Eip; - } - for (i = 0; i < 10; i++) - { - if (!KdbSymPrintAddress((PVOID)Address)) - { - DbgPrint("<%x>", Address); - } - DbgPrint(": "); - InstLen = KdbDisassemble(Address); - if (InstLen < 0) - { - DbgPrint("\n"); - return(1); - } - DbgPrint("\n"); - Address += InstLen; - } - - return(1); -} - -ULONG -DbgProcessHelpCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG i, j, len; - - DbgPrint("Kernel debugger commands:\n"); - for (i = 0; DebuggerCommands[i].Name != NULL; i++) - { - DbgPrint(" %s", DebuggerCommands[i].Syntax); - len = strlen(DebuggerCommands[i].Syntax); - if (len < 35) - { - for (j = 0; j < 35 - len; j++) - { - DbgPrint(" "); - } - } - DbgPrint(" - %s\n", DebuggerCommands[i].Help); - } - return(1); -} - -ULONG -DbgThreadListCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - BOOL System = FALSE; - if (Argc == 2 && (!strcmp(Argv[1], "sys") || !strcmp(Argv[1], "SYS"))) - System = TRUE; - - PsDumpThreads(System); - return(1); -} - -ULONG -DbgProcessListCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - extern LIST_ENTRY PsActiveProcessHead; - PLIST_ENTRY current_entry; - PEPROCESS current; - ULONG i = 1; - - if (PsActiveProcessHead.Flink == NULL) - { - DbgPrint("No processes.\n"); - return(1); - } - - DbgPrint("Process list: "); - current_entry = PsActiveProcessHead.Flink; - while (current_entry != &PsActiveProcessHead) - { - current = CONTAINING_RECORD(current_entry, EPROCESS, ProcessListEntry); - DbgPrint("%d %.8s", current->UniqueProcessId, - current->ImageFileName); + /* Check if the current instruction is a call. */ + while ((i < sizeof (Mem)) && (Mem[i] == 0x66 || Mem[i] == 0x67)) i++; - if ((i % 4) == 0) - { - DbgPrint("\n"); - } - current_entry = current_entry->Flink; - } - return(1); + if (i == sizeof (Mem)) + return FALSE; + if (Mem[i] == 0xE8 || Mem[i] == 0x9A || Mem[i] == 0xF2 || Mem[i] == 0xF3 || + (((i + 1) < sizeof (Mem)) && Mem[i] == 0xFF && (Mem[i+1] & 0x38) == 0x10)) + { + return TRUE; + } + return FALSE; } -VOID -DbgPrintBackTrace(PULONG Frame, ULONG_PTR StackBase, ULONG_PTR StackLimit) +/*!\brief Steps over an instruction + * + * If the given instruction should be stepped over, this function inserts a + * temporary breakpoint after the instruction and returns TRUE, otherwise it + * returns FALSE. + * + * \retval TRUE Temporary breakpoint set after instruction. + * \retval FALSE No breakpoint was set. + */ +BOOLEAN +KdbpStepOverInstruction(ULONG_PTR Eip) { - PVOID Address; + LONG InstLen; - DbgPrint("Frames:\n"); - while (Frame != NULL) - { - if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, Frame + 1, sizeof (Address)))) - { - DbgPrint("\nCouldn't access memory at 0x%x!\n", (UINT)(Frame + 1)); - break; - } - KdbSymPrintAddress(Address); - DbgPrint("\n"); - if (!NT_SUCCESS(KdbpSafeReadMemory(&Frame, Frame, sizeof (Frame)))) - { - DbgPrint("\nCouldn't access memory at 0x%x!\n", (UINT)Frame); - break; - } - } + if (!KdbpShouldStepOverInstruction(Eip)) + return FALSE; + + InstLen = KdbpGetInstLength(Eip); + if (InstLen < 1) + return FALSE; + + if (!NT_SUCCESS(KdbpInsertBreakPoint(Eip + InstLen, KdbBreakPointTemporary, 0, 0, NULL, FALSE, NULL))) + return FALSE; + + return TRUE; } -ULONG -DbgAddrCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME tf) +/*!\brief Steps into an instruction (interrupts) + * + * If the given instruction should be stepped into, this function inserts a + * temporary breakpoint at the target instruction and returns TRUE, otherwise it + * returns FALSE. + * + * \retval TRUE Temporary breakpoint set at target instruction. + * \retval FALSE No breakpoint was set. + */ +BOOLEAN +KdbpStepIntoInstruction(ULONG_PTR Eip) { - PVOID Addr; + struct __attribute__((packed)) { + USHORT Limit; + ULONG Base; + } Idtr; + UCHAR Mem[2]; + INT IntVect; + ULONG IntDesc[2]; + ULONG_PTR TargetEip; - if (Argc == 2) - { - Addr = (PVOID)strtoul(Argv[1], NULL, 0); - KdbSymPrintAddress(Addr); - } + /* Read memory */ + if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Eip, sizeof (Mem)))) + { + /*KdbpPrint("Couldn't access memory at 0x%x\n", (UINT)Eip);*/ + return FALSE; + } - return(1); + /* Check for INT instruction */ + /* FIXME: Check for iret */ + if (Mem[0] == 0xcc) + IntVect = 3; + else if (Mem[0] == 0xcd) + IntVect = Mem[1]; + else if (Mem[0] == 0xce && KdbCurrentTrapFrame->Tf.Eflags & (1<<11)) /* 1 << 11 is the overflow flag */ + IntVect = 4; + else + return FALSE; + + if (IntVect < 32) /* We should be informed about interrupts < 32 by the kernel, no need to breakpoint them */ + { + return FALSE; + } + + /* Read the interrupt descriptor table register */ + asm volatile("sidt %0" : : "m"(Idtr)); + if (IntVect >= (Idtr.Limit + 1) / 8) + { + /*KdbpPrint("IDT does not contain interrupt vector %d\n.", IntVect);*/ + return TRUE; + } + + /* Get the interrupt descriptor */ + if (!NT_SUCCESS(KdbpSafeReadMemory(IntDesc, (PVOID)(Idtr.Base + (IntVect * 8)), sizeof (IntDesc)))) + { + /*KdbpPrint("Couldn't access memory at 0x%x\n", (UINT)Idtr.Base + (IntVect * 8));*/ + return FALSE; + } + + /* Check descriptor and get target eip (16 bit interrupt/trap gates not supported) */ + if ((IntDesc[1] & (1 << 15)) == 0) /* not present */ + { + return FALSE; + } + if ((IntDesc[1] & 0x1f00) == 0x0500) /* Task gate */ + { + /* FIXME: Task gates not supported */ + return FALSE; + } + else if (((IntDesc[1] & 0x1fe0) == 0x0e00) || /* 32 bit Interrupt gate */ + ((IntDesc[1] & 0x1fe0) == 0x0f00)) /* 32 bit Trap gate */ + { + /* FIXME: Should the segment selector of the interrupt gate be checked? */ + TargetEip = (IntDesc[1] & 0xffff0000) | (IntDesc[0] & 0x0000ffff); + } + else + { + return FALSE; + } + + /* Insert breakpoint */ + if (!NT_SUCCESS(KdbpInsertBreakPoint(TargetEip, KdbBreakPointTemporary, 0, 0, NULL, FALSE, NULL))) + return FALSE; + + return TRUE; } -ULONG -DbgXCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME tf) +/*!\brief Gets the number of the next breakpoint >= Start. + * + * \param Start Breakpoint number to start searching at. -1 if no more breakpoints are found. + * + * \returns Breakpoint number (-1 if no more breakpoints are found) + */ +LONG +KdbpGetNextBreakPointNr( + IN ULONG Start OPTIONAL) { - PDWORD Addr = NULL; - DWORD Items = 1; - DWORD i = 0; - DWORD Item; - - if (Argc >= 2) - Addr = (PDWORD)strtoul(Argv[1], NULL, 0); - if (Argc >= 3) - Items = (DWORD)strtoul(Argv[2], NULL, 0); - - if (Addr == NULL) - return(1); - - for (i = 0; i < Items; i++) - { - if( (i % 4) == 0 ) - { - if (i != 0) - DbgPrint("\n"); - DbgPrint("%08x:", (int)(&Addr[i])); - } - if (!NT_SUCCESS(KdbpSafeReadMemory(&Item, Addr + i, sizeof (Item)))) - { - DbgPrint("\nCouldn't access memory at 0x%x!\n", (UINT)(Addr + i)); - break; - } - DbgPrint("%08x ", Item); - } - - return(1); + for (; Start < RTL_NUMBER_OF(KdbBreakPoints); Start++) + { + if (KdbBreakPoints[Start].Type != KdbBreakPointNone) + return Start; + } + return -1; } -static int KjsReadRegValue( void *context, - JSNode *result, - JSNode *args ) { - PCHAR cp; - PVOID *context_list = context; - PKJS kjs = (PKJS)context_list[0]; - JSVirtualMachine *vm = kjs->vm; - NTSTATUS Status; - RTL_QUERY_REGISTRY_TABLE QueryTable[2] = { { 0 } }; - UNICODE_STRING NameString; - UNICODE_STRING PathString; - UNICODE_STRING DefaultString; - UNICODE_STRING ValueResult; - ANSI_STRING AnsiResult; - - if (args->u.vinteger != 2 || - args[1].type != JS_STRING || args[2].type != JS_STRING) { - return JS_PROPERTY_FOUND; - } - - RtlInitUnicodeString(&PathString,NULL); - RtlInitUnicodeString(&NameString,NULL); - - cp = js_string_to_c_string (vm, &args[1]); - RtlCreateUnicodeStringFromAsciiz(&PathString,cp); - js_free(cp); - cp = js_string_to_c_string (vm, &args[2]); - RtlCreateUnicodeStringFromAsciiz(&NameString,cp); - js_free(cp); - - RtlInitUnicodeString(&ValueResult,NULL); - RtlInitUnicodeString(&DefaultString,L""); - RtlInitAnsiString(&AnsiResult,NULL); - - QueryTable->EntryContext = 0; - QueryTable->Flags = - RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT; - QueryTable->Name = NameString.Buffer; - QueryTable->DefaultType = REG_SZ; - QueryTable->DefaultData = &DefaultString; - QueryTable->EntryContext = &ValueResult; - Status = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE, - PathString.Buffer, - QueryTable, - NULL, - NULL ); - - RtlFreeUnicodeString(&NameString); - RtlFreeUnicodeString(&PathString); - - if (NT_SUCCESS(Status)) { - RtlInitAnsiString(&AnsiResult,NULL); - RtlUnicodeStringToAnsiString(&AnsiResult, - &ValueResult, - TRUE); - js_vm_make_string (vm, result, AnsiResult.Buffer, - strlen(AnsiResult.Buffer)); - RtlFreeAnsiString(&AnsiResult); - } else { - result->type = JS_INTEGER; - result->u.vinteger = Status; - } - - return JS_PROPERTY_FOUND; -} - -static int KjsGetRegister( void *context, - JSNode *result, - JSNode *args ) { - PVOID *context_list = context; - if( args->u.vinteger == 1 && args->type == JS_INTEGER ) { - DWORD Result = ((DWORD *)context_list[1])[args[1].u.vinteger]; - result->type = JS_INTEGER; - result->u.vinteger = Result; - } - - return JS_PROPERTY_FOUND; -} - -static int KjsGetNthModule( void *context, - JSNode *result, - JSNode *args ) { - PVOID *context_list = context; - PKJS kjs = (PKJS)context_list[0]; - JSVirtualMachine *vm = kjs->vm; - PLIST_ENTRY current_entry; - MODULE_TEXT_SECTION *current = NULL; - extern LIST_ENTRY ModuleTextListHead; - int n = 0; - - if (args->u.vinteger != 1 || args[1].type != JS_INTEGER) { - return JS_PROPERTY_FOUND; - } - - current_entry = ModuleTextListHead.Flink; - - while (current_entry != &ModuleTextListHead && - current_entry != NULL && - n <= args[1].u.vinteger) { - current = CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, - ListEntry); - current_entry = current_entry->Flink; - n++; - } - - if (current_entry && current) { - ANSI_STRING NameStringNarrow; - UNICODE_STRING NameUnicodeString; - - RtlInitUnicodeString( &NameUnicodeString, current->Name ); - RtlUnicodeStringToAnsiString( &NameStringNarrow, - &NameUnicodeString, - TRUE ); - - js_vm_make_array (vm, result, 2); - - js_vm_make_string (vm, - &result->u.varray->data[0], - NameStringNarrow.Buffer, - NameStringNarrow.Length); - - RtlFreeAnsiString(&NameStringNarrow); - - result->u.varray->data[1].type = JS_INTEGER; - result->u.varray->data[1].u.vinteger = (DWORD)current->Base; - result->type = JS_ARRAY; - return JS_PROPERTY_FOUND; - } - result->type = JS_UNDEFINED; - return JS_PROPERTY_FOUND; -} - -static BOOL FindJSEndMark( PCHAR Buffer ) { - int i; - - for( i = 0; Buffer[i] && Buffer[i+1]; i++ ) { - if( Buffer[i] == ';' && Buffer[i+1] == ';' ) return TRUE; - } - return FALSE; -} - -ULONG -DbgScriptCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME tf) +/*!\brief Returns information of the specified breakpoint. + * + * \param BreakPointNr Number of the breakpoint to return information of. + * \param Address Receives the address of the breakpoint. + * \param Type Receives the type of the breakpoint (hardware or software) + * \param Size Size - for memory breakpoints. + * \param AccessType Access type - for hardware breakpoints. + * \param DebugReg Debug register - for enabled hardware breakpoints. + * \param Enabled Whether the breakpoint is enabled or not. + * \param Process The owning process of the breakpoint. + * \param ConditionExpression The expression which was given as condition for the bp. + * + * \returns NULL on failure, pointer to a KDB_BREAKPOINT struct on success. + */ +BOOLEAN +KdbpGetBreakPointInfo( + IN ULONG BreakPointNr, + OUT ULONG_PTR *Address OPTIONAL, + OUT KDB_BREAKPOINT_TYPE *Type OPTIONAL, + OUT UCHAR *Size OPTIONAL, + OUT KDB_ACCESS_TYPE *AccessType OPTIONAL, + OUT UCHAR *DebugReg OPTIONAL, + OUT BOOLEAN *Enabled OPTIONAL, + OUT BOOLEAN *Global OPTIONAL, + OUT PEPROCESS *Process OPTIONAL, + OUT PCHAR *ConditionExpression OPTIONAL) { - PCHAR Buffer; - PCHAR BufferStart; - static void *interp = 0; - void *script_cmd_context[2]; + PKDB_BREAKPOINT bp; - if( !interp ) interp = kjs_create_interp(NULL); - if( !interp ) return 1; + if (BreakPointNr >= RTL_NUMBER_OF(KdbBreakPoints) || + KdbBreakPoints[BreakPointNr].Type == KdbBreakPointNone) + { + return FALSE; + } + + bp = KdbBreakPoints + BreakPointNr; + if (Address != NULL) + *Address = bp->Address; + if (Type != NULL) + *Type = bp->Type; + if (bp->Type == KdbBreakPointHardware) + { + if (Size != NULL) + *Size = bp->Data.Hw.Size; + if (AccessType != NULL) + *AccessType = bp->Data.Hw.AccessType; + if (DebugReg != NULL && bp->Enabled) + *DebugReg = bp->Data.Hw.DebugReg; + } + if (Enabled != NULL) + *Enabled = bp->Enabled; + if (Global != NULL) + *Global = bp->Global; + if (Process != NULL) + *Process = bp->Process; + if (ConditionExpression != NULL) + *ConditionExpression = bp->ConditionExpression; - BufferStart = Buffer = ExAllocatePool( NonPagedPool, 4096 ); - if( !Buffer ) return 1; - - script_cmd_context[0] = interp; - script_cmd_context[1] = &tf; - - kjs_system_register( interp, "regs", script_cmd_context, - KjsGetRegister ); - kjs_system_register( interp, "regread", script_cmd_context, - KjsReadRegValue ); - kjs_system_register( interp, "getmodule", script_cmd_context, - KjsGetNthModule ); - - kjs_eval( interp, - "eval(" - "System.regread(" - "'\\\\Registry\\\\Machine\\\\System\\\\" - "CurrentControlSet\\\\Control\\\\Kdb'," - "'kjsinit'));" ); - - DbgPrint("\nKernel Debugger Script Interface (JavaScript :-)\n"); - DbgPrint("Terminate input with ;; and end scripting with .\n"); - do - { - if( Buffer != BufferStart ) - DbgPrint("..... "); - else - DbgPrint("kjs:> "); - KdbGetCommand( BufferStart ); - if( BufferStart[0] == '.' ) { - if( BufferStart != Buffer ) { - DbgPrint("Input Aborted.\n"); - BufferStart = Buffer; - } else { - /* Single dot input -> exit */ - break; - } - } else { - if( FindJSEndMark( Buffer ) ) { - kjs_eval( interp, Buffer ); - BufferStart = Buffer; - DbgPrint("\n"); - } else { - BufferStart = BufferStart + strlen(BufferStart); - } - } - } while (TRUE); - - ExFreePool( Buffer ); - - kjs_system_unregister( interp, script_cmd_context, KjsGetRegister ); - kjs_system_unregister( interp, script_cmd_context, KjsReadRegValue ); - kjs_system_unregister( interp, script_cmd_context, KjsGetNthModule ); - - return(1); + return TRUE; } -ULONG -DbgBackTraceCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Inserts a breakpoint into the breakpoint array. + * + * The \a Process of the breakpoint is set to \a KdbCurrentProcess + * + * \param Address Address at which to set the breakpoint. + * \param Type Type of breakpoint (hardware or software) + * \param Size Size of breakpoint (for hardware/memory breakpoints) + * \param AccessType Access type (for hardware breakpoins) + * \param ConditionExpression Expression which must evaluate to true for conditional breakpoints. + * \param Global Wether the breakpoint is global or local to a process. + * \param BreakPointNumber Receives the breakpoint number on success + * + * \returns NTSTATUS + */ +NTSTATUS +KdbpInsertBreakPoint( + IN ULONG_PTR Address, + IN KDB_BREAKPOINT_TYPE Type, + IN UCHAR Size OPTIONAL, + IN KDB_ACCESS_TYPE AccessType OPTIONAL, + IN PCHAR ConditionExpression OPTIONAL, + IN BOOLEAN Global, + OUT PULONG BreakPointNumber OPTIONAL) { - ULONG_PTR StackBase, StackLimit; - extern unsigned int init_stack, init_stack_top; + LONG i; + PVOID Condition; + PCHAR ConditionExpressionDup; + LONG ErrOffset; + CHAR ErrMsg[128]; - /* Without an argument we print the current stack. */ - if (Argc == 1) - { - if (PsGetCurrentThread() != NULL) - { - StackBase = (ULONG_PTR)PsGetCurrentThread()->Tcb.StackBase; - StackLimit = PsGetCurrentThread()->Tcb.StackLimit; - } - else - { - StackBase = (ULONG_PTR)init_stack_top; - StackLimit = (ULONG_PTR)init_stack; - } - DbgPrintBackTrace((PULONG)&Tf->DebugEbp, StackBase, StackLimit); - } - /* - * If there are two arguments and the second begins with a asterik treat it - * as the address of a frame to start printing the back trace from. - */ - else if (Argc == 2 && Argv[1][0] == '*') - { - PULONG Frame; - Frame = (PULONG)strtoul(&Argv[1][1], NULL, 0); - DbgPrintBackTrace(Frame, ULONG_MAX, 0); - } - /* - * Otherwise treat the argument as the id of a thread whose back trace is to - * be printed. - */ - else - { - } - return(1); -} + ASSERT(Type != KdbBreakPointNone); -VOID -DbgPrintCr0(ULONG Cr0) -{ - ULONG i; - - DbgPrint("CR0:"); - if (Cr0 & (1 << 0)) - { - DbgPrint(" PE"); - } - if (Cr0 & (1 << 1)) - { - DbgPrint(" MP"); - } - if (Cr0 & (1 << 2)) - { - DbgPrint(" EM"); - } - if (Cr0 & (1 << 3)) - { - DbgPrint(" TS"); - } - if (!(Cr0 & (1 << 4))) - { - DbgPrint(" !BIT5"); - } - if (Cr0 & (1 << 5)) - { - DbgPrint(" NE"); - } - for (i = 6; i < 16; i++) - { - if (Cr0 & (1 << i)) - { - DbgPrint(" BIT%d", i); - } - } - if (Cr0 & (1 << 16)) - { - DbgPrint(" WP"); - } - if (Cr0 & (1 << 17)) - { - DbgPrint(" BIT17"); - } - if (Cr0 & (1 << 18)) - { - DbgPrint(" AM"); - } - for (i = 19; i < 29; i++) - { - if (Cr0 & (1 << i)) - { - DbgPrint(" BIT%d", i); - } - } - if (Cr0 & (1 << 29)) - { - DbgPrint(" NW"); - } - if (Cr0 & (1 << 30)) - { - DbgPrint(" CD"); - } - if (Cr0 & (1 << 31)) - { - DbgPrint(" PG"); - } - DbgPrint("\n"); -} - -ULONG -DbgCRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG Cr0, Cr1, Cr2, Cr3, Cr4; - ULONG Ldtr; - USHORT Tr; - - __asm__ __volatile__ ("movl %%cr0, %0\n\t" : "=d" (Cr0)); - /* __asm__ __volatile__ ("movl %%cr1, %0\n\t" : "=d" (Cr1)); */ - Cr1 = 0; - __asm__ __volatile__ ("movl %%cr2, %0\n\t" : "=d" (Cr2)); - __asm__ __volatile__ ("movl %%cr3, %0\n\t" : "=d" (Cr3)); - __asm__ __volatile__ ("movl %%cr4, %0\n\t" : "=d" (Cr4)); - __asm__ __volatile__ ("str %0\n\t" : "=d" (Tr)); - __asm__ __volatile__ ("sldt %0\n\t" : "=d" (Ldtr)); - DbgPrintCr0(Cr0); - DbgPrint("CR1 %.8x CR2 %.8x CR3 %.8x CR4 %.8x TR %.8x LDTR %.8x\n", - Cr1, Cr2, Cr3, Cr4, (ULONG)Tf, Ldtr); - return(1); -} - -ULONG -DbgDRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - DbgPrint("Trap : DR0 %.8x DR1 %.8x DR2 %.8x DR3 %.8x DR6 %.8x DR7 %.8x\n", - Tf->Dr0, Tf->Dr1, Tf->Dr2, Tf->Dr3, Tf->Dr6, Tf->Dr7); - return(1); -} - -ULONG -DbgContCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - /* Not too difficult. */ - return(0); -} - -ULONG -DbgStopCondition(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - if( Argc == 1 ) { - if( KdbHandleHandled ) DbgPrint("all\n"); - else if( KdbHandleUmode ) DbgPrint("umode\n"); - else DbgPrint("kmode\n"); - } - else if( !strcmp(Argv[1],"all") ) - { KdbHandleHandled = TRUE; KdbHandleUmode = TRUE; } - else if( !strcmp(Argv[1],"umode") ) - { KdbHandleHandled = FALSE; KdbHandleUmode = TRUE; } - else if( !strcmp(Argv[1],"kmode") ) - { KdbHandleHandled = FALSE; KdbHandleUmode = FALSE; } - - return(TRUE); -} - -ULONG -DbgModuleLoadedAction(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - if (Argc == 1) + if (Type == KdbBreakPointHardware) + { + if ((Address % Size) != 0) { - if (KdbBreakOnModuleLoad) - DbgPrint("Current setting: break\n"); - else - DbgPrint("Current setting: continue\n"); + KdbpPrint("Address (0x%x) must be aligned to a multiple of the size (%d)\n", Address, Size); + return STATUS_UNSUCCESSFUL; } - else if (!strcmp(Argv[1], "break")) + if (AccessType == KdbAccessExec && Size != 1) { - KdbBreakOnModuleLoad = TRUE; + KdbpPrint("Size must be 1 for execution breakpoints.\n"); + return STATUS_UNSUCCESSFUL; } - else if (!strcmp(Argv[1], "continue")) + } + + if (KdbBreakPointCount == KDB_MAXIMUM_BREAKPOINT_COUNT) + { + return STATUS_UNSUCCESSFUL; + } + + /* Parse conditon expression string and duplicate it */ + if (ConditionExpression != NULL) + { + Condition = KdbpRpnParseExpression(ConditionExpression, &ErrOffset, ErrMsg); + if (Condition == NULL) { - KdbBreakOnModuleLoad = FALSE; + if (ErrOffset >= 0) + KdbpPrint("Couldn't parse expression: %s at character %d\n", ErrMsg, ErrOffset); + else + KdbpPrint("Couldn't parse expression: %s", ErrMsg); + return STATUS_UNSUCCESSFUL; } - else + + i = strlen(ConditionExpression) + 1; + ConditionExpressionDup = ExAllocatePoolWithTag(NonPagedPool, i, TAG_KDBG); + RtlCopyMemory(ConditionExpressionDup, ConditionExpression, i); + + } + else + { + Condition = NULL; + ConditionExpressionDup = NULL; + } + + /* Find unused breakpoint */ + if (Type == KdbBreakPointTemporary) + { + for (i = RTL_NUMBER_OF(KdbBreakPoints) - 1; i >= 0; i--) { - DbgPrint("Unknown setting: %s\n", Argv[1]); + if (KdbBreakPoints[i].Type == KdbBreakPointNone) + break; + } + } + else + { + for (i = 0; i < RTL_NUMBER_OF(KdbBreakPoints); i++) + { + if (KdbBreakPoints[i].Type == KdbBreakPointNone) + break; + } + } + ASSERT(i < RTL_NUMBER_OF(KdbBreakPoints)); + + /* Set the breakpoint */ + ASSERT(KdbCurrentProcess != NULL); + KdbBreakPoints[i].Type = Type; + KdbBreakPoints[i].Address = Address; + KdbBreakPoints[i].Enabled = FALSE; + KdbBreakPoints[i].Global = Global; + KdbBreakPoints[i].Process = KdbCurrentProcess; + KdbBreakPoints[i].ConditionExpression = ConditionExpressionDup; + KdbBreakPoints[i].Condition = Condition; + if (Type == KdbBreakPointHardware) + { + + KdbBreakPoints[i].Data.Hw.Size = Size; + KdbBreakPoints[i].Data.Hw.AccessType = AccessType; + } + KdbBreakPointCount++; + + if (Type != KdbBreakPointTemporary) + KdbpPrint("Breakpoint %d inserted.\n", i); + + /* Try to enable the breakpoint */ + KdbpEnableBreakPoint(i, NULL); + + /* Return the breakpoint number */ + if (BreakPointNumber != NULL) + *BreakPointNumber = i; + + return STATUS_SUCCESS; +} + +/*!\brief Deletes a breakpoint + * + * \param BreakPointNr Number of the breakpoint to delete. Can be -1 + * \param BreakPoint Breakpoint to delete. Can be NULL. + * + * \retval TRUE Success. + * \retval FALSE Failure (invalid breakpoint number) + */ +BOOLEAN +KdbpDeleteBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL) +{ + if (BreakPointNr < 0) + { + ASSERT(BreakPoint != NULL); + BreakPointNr = BreakPoint - KdbBreakPoints; + } + if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + if (BreakPoint == NULL) + { + BreakPoint = KdbBreakPoints + BreakPointNr; + } + if (BreakPoint->Type == KdbBreakPointNone) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + + if (BreakPoint->Enabled && + !KdbpDisableBreakPoint(-1, BreakPoint)) + return FALSE; + + if (BreakPoint->Type != KdbBreakPointTemporary) + KdbpPrint("Breakpoint %d deleted.\n", BreakPointNr); + BreakPoint->Type = KdbBreakPointNone; + KdbBreakPointCount--; + + return TRUE; +} + +/*!\brief Checks if the breakpoint was set by the debugger + * + * Tries to find a breakpoint in the breakpoint array which caused + * the debug exception to happen. + * + * \param ExpNr Exception Number (1 or 3) + * \param TrapFrame Exception trapframe + * + * \returns Breakpoint number, -1 on error. + */ +STATIC LONG +KdbpIsBreakPointOurs( + IN ULONG ExpNr, + IN PKTRAP_FRAME TrapFrame) +{ + INT i; + ASSERT(ExpNr == 1 || ExpNr == 3); + + if (ExpNr == 3) /* Software interrupt */ + { + ULONG_PTR BpEip = (ULONG_PTR)TrapFrame->Eip - 1; /* Get EIP of INT3 instruction */ + for (i = 0; i < KdbSwBreakPointCount; i++) + { + ASSERT((KdbSwBreakPoints[i]->Type == KdbBreakPointSoftware || + KdbSwBreakPoints[i]->Type == KdbBreakPointTemporary)); + ASSERT(KdbSwBreakPoints[i]->Enabled); + if (KdbSwBreakPoints[i]->Address == BpEip) + { + return KdbSwBreakPoints[i] - KdbBreakPoints; + } + } + } + else if (ExpNr == 1) /* Hardware interrupt */ + { + UCHAR DebugReg; + for (i = 0; i < KdbHwBreakPointCount; i++) + { + ASSERT(KdbHwBreakPoints[i]->Type == KdbBreakPointHardware && + KdbHwBreakPoints[i]->Enabled); + DebugReg = KdbHwBreakPoints[i]->Data.Hw.DebugReg; + if ((TrapFrame->Dr6 & (1 << DebugReg)) != 0) + { + return KdbHwBreakPoints[i] - KdbBreakPoints; + } + } + } + + return -1; +} + +/*!\brief Enables a breakpoint. + * + * \param BreakPointNr Number of the breakpoint to enable Can be -1. + * \param BreakPoint Breakpoint to enable. Can be NULL. + * + * \retval TRUE Success. + * \retval FALSE Failure. + * + * \sa KdbpDisableBreakPoint + */ +BOOLEAN +KdbpEnableBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL) +{ + NTSTATUS Status; + INT i; + ULONG ul; + + if (BreakPointNr < 0) + { + ASSERT(BreakPoint != NULL); + BreakPointNr = BreakPoint - KdbBreakPoints; + } + if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + if (BreakPoint == NULL) + { + BreakPoint = KdbBreakPoints + BreakPointNr; + } + if (BreakPoint->Type == KdbBreakPointNone) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + + if (BreakPoint->Enabled == TRUE) + { + KdbpPrint("Breakpoint %d is already enabled.\n", BreakPointNr); + return TRUE; + } + + if (BreakPoint->Type == KdbBreakPointSoftware || + BreakPoint->Type == KdbBreakPointTemporary) + { + if (KdbSwBreakPointCount >= KDB_MAXIMUM_SW_BREAKPOINT_COUNT) + { + KdbpPrint("Maximum number of SW breakpoints (%d) used. " + "Disable another breakpoint in order to enable this one.\n", + KDB_MAXIMUM_SW_BREAKPOINT_COUNT); + return FALSE; + } + Status = KdbpOverwriteInstruction(BreakPoint->Process, BreakPoint->Address, + 0xCC, &BreakPoint->Data.SavedInstruction); + if (!NT_SUCCESS(Status)) + { + KdbpPrint("Couldn't access memory at 0x%x\n", BreakPoint->Address); + return FALSE; + } + KdbSwBreakPoints[KdbSwBreakPointCount++] = BreakPoint; + } + else + { + if (BreakPoint->Data.Hw.AccessType == KdbAccessExec) + ASSERT(BreakPoint->Data.Hw.Size == 1); + ASSERT((BreakPoint->Address % BreakPoint->Data.Hw.Size) == 0); + if (KdbHwBreakPointCount >= KDB_MAXIMUM_HW_BREAKPOINT_COUNT) + { + KdbpPrint("Maximum number of HW breakpoints (%d) already used. " + "Disable another breakpoint in order to enable this one.\n", + KDB_MAXIMUM_HW_BREAKPOINT_COUNT); + return FALSE; } - return(TRUE); + /* Find unused hw breakpoint */ + ASSERT(KDB_MAXIMUM_HW_BREAKPOINT_COUNT == 4); + for (i = 0; i < KDB_MAXIMUM_HW_BREAKPOINT_COUNT; i++) + { + if ((KdbTrapFrame.Tf.Dr7 & (0x3 << (i * 2))) == 0) + break; + } + ASSERT(i < KDB_MAXIMUM_HW_BREAKPOINT_COUNT); + + /* Set the breakpoint address. */ + switch (i) + { + case 0: + KdbTrapFrame.Tf.Dr0 = BreakPoint->Address; + break; + case 1: + KdbTrapFrame.Tf.Dr1 = BreakPoint->Address; + break; + case 2: + KdbTrapFrame.Tf.Dr2 = BreakPoint->Address; + break; + case 3: + KdbTrapFrame.Tf.Dr3 = BreakPoint->Address; + break; + } + + /* Enable the global breakpoint */ + KdbTrapFrame.Tf.Dr7 |= (0x2 << (i * 2)); + + /* Enable the exact match bits. */ + KdbTrapFrame.Tf.Dr7 |= 0x00000300; + + /* Clear existing state. */ + KdbTrapFrame.Tf.Dr7 &= ~(0xF << (16 + (i * 4))); + + /* Set the breakpoint type. */ + switch (BreakPoint->Data.Hw.AccessType) + { + case KdbAccessExec: + ul = 0; + break; + case KdbAccessWrite: + ul = 1; + break; + case KdbAccessRead: + case KdbAccessReadWrite: + ul = 3; + break; + default: + ASSERT(0); + return TRUE; + break; + } + KdbTrapFrame.Tf.Dr7 |= (ul << (16 + (i * 4))); + + /* Set the breakpoint length. */ + KdbTrapFrame.Tf.Dr7 |= ((BreakPoint->Data.Hw.Size - 1) << (18 + (i * 4))); + + /* Update KdbCurrentTrapFrame - values are taken from there by the CLI */ + if (&KdbTrapFrame != KdbCurrentTrapFrame) + { + KdbCurrentTrapFrame->Tf.Dr0 = KdbTrapFrame.Tf.Dr0; + KdbCurrentTrapFrame->Tf.Dr1 = KdbTrapFrame.Tf.Dr1; + KdbCurrentTrapFrame->Tf.Dr2 = KdbTrapFrame.Tf.Dr2; + KdbCurrentTrapFrame->Tf.Dr3 = KdbTrapFrame.Tf.Dr3; + KdbCurrentTrapFrame->Tf.Dr6 = KdbTrapFrame.Tf.Dr6; + KdbCurrentTrapFrame->Tf.Dr7 = KdbTrapFrame.Tf.Dr7; + } + + BreakPoint->Data.Hw.DebugReg = i; + KdbHwBreakPoints[KdbHwBreakPointCount++] = BreakPoint; + } + + BreakPoint->Enabled = TRUE; + if (BreakPoint->Type != KdbBreakPointTemporary) + KdbpPrint("Breakpoint %d enabled.\n", BreakPointNr); + return TRUE; } -ULONG -DbgEchoToggle(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Disables a breakpoint. + * + * \param BreakPointNr Number of the breakpoint to disable. Can be -1 + * \param BreakPoint Breakpoint to disable. Can be NULL. + * + * \retval TRUE Success. + * \retval FALSE Failure. + * + * \sa KdbpEnableBreakPoint + */ +BOOLEAN +KdbpDisableBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL) { - KbdEchoOn = !KbdEchoOn; - return(TRUE); + INT i; + NTSTATUS Status; + + if (BreakPointNr < 0) + { + ASSERT(BreakPoint != NULL); + BreakPointNr = BreakPoint - KdbBreakPoints; + } + if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + if (BreakPoint == NULL) + { + BreakPoint = KdbBreakPoints + BreakPointNr; + } + if (BreakPoint->Type == KdbBreakPointNone) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + + if (BreakPoint->Enabled == FALSE) + { + KdbpPrint("Breakpoint %d is not enabled.\n", BreakPointNr); + return TRUE; + } + + if (BreakPoint->Type == KdbBreakPointSoftware || + BreakPoint->Type == KdbBreakPointTemporary) + { + ASSERT(KdbSwBreakPointCount > 0); + Status = KdbpOverwriteInstruction(BreakPoint->Process, BreakPoint->Address, + BreakPoint->Data.SavedInstruction, NULL); + if (!NT_SUCCESS(Status)) + { + KdbpPrint("Couldn't restore original instruction.\n"); + return FALSE; + } + + for (i = 0; i < KdbSwBreakPointCount; i++) + { + if (KdbSwBreakPoints[i] == BreakPoint) + { + KdbSwBreakPoints[i] = KdbSwBreakPoints[--KdbSwBreakPointCount]; + i = -1; /* if the last breakpoint is disabled dont break with i >= KdbSwBreakPointCount */ + break; + } + } + if (i != -1) /* not found */ + ASSERT(0); + } + else + { + ASSERT(BreakPoint->Type == KdbBreakPointHardware); + + /* Clear the breakpoint. */ + KdbTrapFrame.Tf.Dr7 &= ~(0x3 << (BreakPoint->Data.Hw.DebugReg * 2)); + if ((KdbTrapFrame.Tf.Dr7 & 0xFF) == 0) + { + /* + * If no breakpoints are enabled then clear the exact match flags. + */ + KdbTrapFrame.Tf.Dr7 &= 0xFFFFFCFF; + } + + for (i = 0; i < KdbHwBreakPointCount; i++) + { + if (KdbHwBreakPoints[i] == BreakPoint) + { + KdbHwBreakPoints[i] = KdbHwBreakPoints[--KdbHwBreakPointCount]; + i = -1; /* if the last breakpoint is disabled dont break with i >= KdbHwBreakPointCount */ + break; + } + } + if (i != -1) /* not found */ + ASSERT(0); + } + + BreakPoint->Enabled = FALSE; + if (BreakPoint->Type != KdbBreakPointTemporary) + KdbpPrint("Breakpoint %d disabled.\n", BreakPointNr); + return TRUE; } -VOID -DbgPrintEflags(ULONG Eflags) +/*!\brief Gets the first or last chance enter-condition for exception nr. \a ExceptionNr + * + * \param ExceptionNr Number of the exception to get condition of. + * \param FirstChance Whether to get first or last chance condition. + * \param Condition Receives the condition setting. + * + * \retval TRUE Success. + * \retval FALSE Failure (invalid exception nr) + */ +BOOLEAN +KdbpGetEnterCondition( + IN LONG ExceptionNr, + IN BOOLEAN FirstChance, + OUT KDB_ENTER_CONDITION *Condition) { - DbgPrint("EFLAGS:"); - if (Eflags & (1 << 0)) - { - DbgPrint(" CF"); - } - if (!(Eflags & (1 << 1))) - { - DbgPrint(" !BIT1"); - } - if (Eflags & (1 << 2)) - { - DbgPrint(" PF"); - } - if (Eflags & (1 << 3)) - { - DbgPrint(" BIT3"); - } - if (Eflags & (1 << 4)) - { - DbgPrint(" AF"); - } - if (Eflags & (1 << 5)) - { - DbgPrint(" BIT5"); - } - if (Eflags & (1 << 6)) - { - DbgPrint(" ZF"); - } - if (Eflags & (1 << 7)) - { - DbgPrint(" SF"); - } - if (Eflags & (1 << 8)) - { - DbgPrint(" TF"); - } - if (Eflags & (1 << 9)) - { - DbgPrint(" IF"); - } - if (Eflags & (1 << 10)) - { - DbgPrint(" DF"); - } - if (Eflags & (1 << 11)) - { - DbgPrint(" OF"); - } - if ((Eflags & ((1 << 12) | (1 << 13))) == 0) - { - DbgPrint(" IOPL0"); - } - else if ((Eflags & ((1 << 12) | (1 << 13))) == 1) - { - DbgPrint(" IOPL1"); - } - else if ((Eflags & ((1 << 12) | (1 << 13))) == 2) - { - DbgPrint(" IOPL2"); - } - else if ((Eflags & ((1 << 12) | (1 << 13))) == 3) - { - DbgPrint(" IOPL3"); - } - if (Eflags & (1 << 14)) - { - DbgPrint(" NT"); - } - if (Eflags & (1 << 15)) - { - DbgPrint(" BIT15"); - } - if (Eflags & (1 << 16)) - { - DbgPrint(" RF"); - } - if (Eflags & (1 << 17)) - { - DbgPrint(" VF"); - } - if (Eflags & (1 << 18)) - { - DbgPrint(" AC"); - } - if (Eflags & (1 << 19)) - { - DbgPrint(" VIF"); - } - if (Eflags & (1 << 20)) - { - DbgPrint(" VIP"); - } - if (Eflags & (1 << 21)) - { - DbgPrint(" ID"); - } - if (Eflags & (1 << 22)) - { - DbgPrint(" BIT22"); - } - if (Eflags & (1 << 23)) - { - DbgPrint(" BIT23"); - } - if (Eflags & (1 << 24)) - { - DbgPrint(" BIT24"); - } - if (Eflags & (1 << 25)) - { - DbgPrint(" BIT25"); - } - if (Eflags & (1 << 26)) - { - DbgPrint(" BIT26"); - } - if (Eflags & (1 << 27)) - { - DbgPrint(" BIT27"); - } - if (Eflags & (1 << 28)) - { - DbgPrint(" BIT28"); - } - if (Eflags & (1 << 29)) - { - DbgPrint(" BIT29"); - } - if (Eflags & (1 << 30)) - { - DbgPrint(" BIT30"); - } - if (Eflags & (1 << 31)) - { - DbgPrint(" BIT31"); - } - DbgPrint("\n"); + if (ExceptionNr >= RTL_NUMBER_OF(KdbEnterConditions)) + return FALSE; + + *Condition = KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1]; + return TRUE; } -ULONG -DbgRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Sets the first or last chance enter-condition for exception nr. \a ExceptionNr + * + * \param ExceptionNr Number of the exception to set condition of (-1 for all) + * \param FirstChance Whether to set first or last chance condition. + * \param Condition The new condition setting. + * + * \retval TRUE Success. + * \retval FALSE Failure (invalid exception nr) + */ +BOOLEAN +KdbpSetEnterCondition( + IN LONG ExceptionNr, + IN BOOLEAN FirstChance, + IN KDB_ENTER_CONDITION Condition) { - DbgPrint("CS:EIP %.4x:%.8x, EAX %.8x EBX %.8x ECX %.8x EDX %.8x\n", - Tf->Cs & 0xFFFF, Tf->Eip, Tf->Eax, Tf->Ebx, Tf->Ecx, Tf->Edx); - DbgPrint("ESI %.8x EDI %.8x EBP %.8x SS:ESP %.4x:%.8x\n", - Tf->Esi, Tf->Edi, Tf->Ebp, Tf->Ss & 0xFFFF, Tf->Esp); - DbgPrintEflags(Tf->Eflags); - return(1); + if (ExceptionNr < 0) + { + for (ExceptionNr = 0; ExceptionNr < RTL_NUMBER_OF(KdbEnterConditions); ExceptionNr++) + { + if (ExceptionNr == 1 || ExceptionNr == 8 || + ExceptionNr == 9 || ExceptionNr == 15) /* Reserved exceptions */ + { + continue; + } + KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1] = Condition; + } + } + else + { + if (ExceptionNr >= RTL_NUMBER_OF(KdbEnterConditions) || + ExceptionNr == 1 || ExceptionNr == 8 || /* Do not allow changing of the debug */ + ExceptionNr == 9 || ExceptionNr == 15) /* trap or reserved exceptions */ + { + return FALSE; + } + KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1] = Condition; + } + return TRUE; } -ULONG -DbgBugCheckCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Switches to another thread context + * + * \param ThreadId Id of the thread to switch to. + * + * \retval TRUE Success. + * \retval FALSE Failure (i.e. invalid thread id) + */ +BOOLEAN +KdbpAttachToThread( + PVOID ThreadId) { - KEBUGCHECK(0xDEADDEAD); - return(1); + PETHREAD Thread = NULL; + PEPROCESS Process; + + /* Get a pointer to the thread */ + if (!NT_SUCCESS(PsLookupThreadByThreadId(ThreadId, &Thread))) + { + KdbpPrint("Invalid thread id: 0x%08x\n", (UINT)ThreadId); + return FALSE; + } + Process = Thread->ThreadsProcess; + + if (KeIsExecutingDpc() && Process != KdbCurrentProcess) + { + KdbpPrint("Cannot attach to thread within another process while executing a DPC.\n"); + return FALSE; + } + + /* Save the current thread's context (if we previously attached to a thread) */ + if (KdbCurrentThread != KdbOriginalThread) + { + ASSERT(KdbCurrentTrapFrame == &KdbThreadTrapFrame); + RtlCopyMemory(KdbCurrentThread->Tcb.TrapFrame, &KdbCurrentTrapFrame->Tf, sizeof (KTRAP_FRAME)); + } + else + { + ASSERT(KdbCurrentTrapFrame == &KdbTrapFrame); + } + + /* Switch to the thread's context */ + if (Thread != KdbOriginalThread) + { + ASSERT(Thread->Tcb.TrapFrame != NULL); + RtlCopyMemory(&KdbThreadTrapFrame.Tf, Thread->Tcb.TrapFrame, sizeof (KTRAP_FRAME)); + asm volatile( + "movl %%cr0, %0" "\n\t" + "movl %%cr2, %1" "\n\t" + "movl %%cr3, %2" "\n\t" + "movl %%cr4, %3" "\n\t" + : "=r"(KdbTrapFrame.Cr0), "=r"(KdbTrapFrame.Cr2), + "=r"(KdbTrapFrame.Cr3), "=r"(KdbTrapFrame.Cr4)); + KdbCurrentTrapFrame = &KdbThreadTrapFrame; + } + else /* Switching back to original thread */ + { + KdbCurrentTrapFrame = &KdbTrapFrame; + } + KdbCurrentThread = Thread; + + /* Attach to the thread's process */ + ASSERT(KdbCurrentProcess == PsGetCurrentProcess()); + if (KdbCurrentProcess != Process) + { + if (KdbCurrentProcess != KdbOriginalProcess) /* detach from previously attached process */ + { + KeUnstackDetachProcess(&KdbApcState); + } + if (KdbOriginalProcess != Process) + { + KeStackAttachProcess(EPROCESS_TO_KPROCESS(Process), &KdbApcState); + } + KdbCurrentProcess = Process; + } + + return TRUE; } -ULONG -DbgShowFilesCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Switches to another process/thread context + * + * This function switches to the first thread in the specified process. + * + * \param ProcessId Id of the process to switch to. + * + * \retval TRUE Success. + * \retval FALSE Failure (i.e. invalid process id) + */ +BOOLEAN +KdbpAttachToProcess( + PVOID ProcessId) { - DbgShowFiles(); - return(1); + PEPROCESS Process = NULL; + PETHREAD Thread; + PLIST_ENTRY Entry; + + /* Get a pointer to the process */ + if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) + { + KdbpPrint("Invalid process id: 0x%08x\n", (UINT)ProcessId); + return FALSE; + } + + Entry = Process->ThreadListHead.Flink; + if (Entry == &KdbCurrentProcess->ThreadListHead) + { + KdbpPrint("No threads in process 0x%08x, cannot attach to process!\n", (UINT)ProcessId); + return FALSE; + } + + Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry); + + return KdbpAttachToThread(Thread->Cid.UniqueThread); } -ULONG -DbgEnableFileCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Calls the main loop ... + */ +STATIC VOID +KdbpCallMainLoop() { - if (Argc == 2) - { - if (strlen(Argv[1]) > 0) - { - DbgEnableFile(Argv[1]); - } - } - return(1); + KdbpCliMainLoop(KdbEnteredOnSingleStep); } -ULONG -DbgDisableFileCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - if (Argc == 2) - { - if (strlen(Argv[1]) > 0) - { - DbgDisableFile(Argv[1]); - } - } - return(1); -} - -VOID -KdbCreateThreadHook(PCONTEXT Context) -{ - Context->Dr0 = x_dr0; - Context->Dr1 = x_dr1; - Context->Dr2 = x_dr2; - Context->Dr3 = x_dr3; - Context->Dr7 = x_dr7; -} - -ULONG -KdbDoCommand(PCH CommandLine, PKTRAP_FRAME Tf) -{ - ULONG i; - PCH s1; - PCH s; - static PCH Argv[256]; - ULONG Argc; - static CHAR OrigCommand[256]; - - strcpy(OrigCommand, CommandLine); - - Argc = 0; - s = CommandLine; - while ((s1 = strpbrk(s, "\t ")) != NULL) - { - Argv[Argc] = s; - *s1 = 0; - s = s1 + 1; - Argc++; - } - Argv[Argc] = s; - Argc++; - - for (i = 0; DebuggerCommands[i].Name != NULL; i++) - { - if (strcmp(DebuggerCommands[i].Name, Argv[0]) == 0) - { - return(DebuggerCommands[i].Fn(Argc, Argv, Tf)); - } - } - DbgPrint("Command '%s' is unknown.", OrigCommand); - return(1); -} - -VOID -KdbMainLoop(PKTRAP_FRAME Tf) -{ - CHAR Command[256]; - ULONG s; - - if (!KdbEnteredOnSingleStep) - { - DbgPrint("\nEntered kernel debugger (type \"help\" for a list of commands)\n"); - } - else - { - if (!KdbSymPrintAddress((PVOID)Tf->Eip)) - { - DbgPrint("<%x>", Tf->Eip); - } - DbgPrint(": "); - if (KdbDisassemble(Tf->Eip) < 0) - { - DbgPrint(""); - } - KdbEnteredOnSingleStep = FALSE; - KdbLastSingleStepFrom = 0xFFFFFFFF; - } - - do - { - DbgPrint("\nkdb:> "); - - KdbGetCommand(Command); - s = KdbDoCommand(Command, Tf); - } while (s != 0); -} - -VOID -KdbInternalEnter(PKTRAP_FRAME Tf) +/*!\brief Internal function to enter KDB. + * + * Disables interrupts, releases display ownership, ... + */ +STATIC VOID +KdbpInternalEnter() { - __asm__ __volatile__ ("cli\n\t"); - KbdDisableMouse(); - if (KdDebugState & KD_DEBUG_SCREEN) - { + PETHREAD Thread; + PVOID SavedInitialStack, SavedStackBase, SavedKernelStack; + ULONG SavedStackLimit; + + KbdDisableMouse(); + if (KdDebugState & KD_DEBUG_SCREEN) + { HalReleaseDisplayOwnership(); - } - (VOID)KdbMainLoop(Tf); - KbdEnableMouse(); - __asm__ __volatile__("sti\n\t"); + } + + /* Call the interface's main loop on a different stack */ + Thread = PsGetCurrentThread(); + SavedInitialStack = Thread->Tcb.InitialStack; + SavedStackBase = Thread->Tcb.StackBase; + SavedStackLimit = Thread->Tcb.StackLimit; + SavedKernelStack = Thread->Tcb.KernelStack; + Thread->Tcb.InitialStack = Thread->Tcb.StackBase = (char*)KdbStack + KDB_STACK_SIZE; + Thread->Tcb.StackLimit = (ULONG)KdbStack; + Thread->Tcb.KernelStack = (char*)KdbStack + KDB_STACK_SIZE; + + /*KdbpPrint("Switching to KDB stack 0x%08x-0x%08x\n", Thread->Tcb.StackLimit, Thread->Tcb.StackBase);*/ + + KdbpStackSwitchAndCall(Thread->Tcb.KernelStack, KdbpCallMainLoop); + + Thread->Tcb.InitialStack = SavedInitialStack; + Thread->Tcb.StackBase = SavedStackBase; + Thread->Tcb.StackLimit = SavedStackLimit; + Thread->Tcb.KernelStack = SavedKernelStack; + KbdEnableMouse(); } +/*!\brief KDB Exception filter + * + * Called by the exception dispatcher. + * + * \param ExceptionRecord Unused. + * \param PreviousMode UserMode if the exception was raised from umode, otherwise KernelMode. + * \param Context Unused. + * \param TrapFrame Exception TrapFrame. + * \param FirstChance TRUE when called before exception frames were serached, + * FALSE for the second call. + * + * \returns KD_CONTINUE_TYPE + */ KD_CONTINUE_TYPE -KdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord, - KPROCESSOR_MODE PreviousMode, - PCONTEXT Context, - PKTRAP_FRAME TrapFrame, - BOOLEAN AlwaysHandle) +KdbEnterDebuggerException( + IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, + IN KPROCESSOR_MODE PreviousMode, + IN PCONTEXT Context OPTIONAL, + IN OUT PKTRAP_FRAME TrapFrame, + IN BOOLEAN FirstChance) { - LONG BreakPointNr; - ULONG ExpNr = (ULONG)TrapFrame->DebugArgMark; + ULONG ExpNr = (ULONG)TrapFrame->DebugArgMark; + KDB_ENTER_CONDITION EnterCondition; + KD_CONTINUE_TYPE ContinueType = kdHandleException; + PKDB_BREAKPOINT BreakPoint; + ULONG ul; + ULONGLONG ull; + BOOLEAN Resume = FALSE; + BOOLEAN EnterConditionMet = TRUE; + ULONG OldEflags; - /* Always handle beakpoints */ - if (ExpNr != 1 && ExpNr != 3) - { - DbgPrint(":KDBG:Entered:%s:%s\n", - PreviousMode==KernelMode ? "kmode" : "umode", - AlwaysHandle ? "always" : "if-unhandled"); + KdbCurrentProcess = PsGetCurrentProcess(); - /* If we aren't handling umode exceptions then return */ - if (PreviousMode == UserMode && !KdbHandleUmode && !AlwaysHandle) - { - return kdHandleException; - } + /* Set continue type to kdContinue for single steps and breakpoints */ + if (ExpNr == 1 || ExpNr == 3) + ContinueType = kdContinue; - /* If the exception would be unhandled (and we care) then handle it */ - if (PreviousMode == KernelMode && !KdbHandleHandled && !AlwaysHandle) - { - return kdHandleException; - } - } + /* Check if we should handle the exception. */ + ul = min(ExpNr, RTL_NUMBER_OF(KdbEnterConditions) - 1); + EnterCondition = KdbEnterConditions[ul][FirstChance ? 0 : 1]; + if (EnterCondition == KdbDoNotEnter || + (EnterCondition == KdbEnterFromUmode && PreviousMode != UserMode) || + (EnterCondition == KdbEnterFromKmode && PreviousMode != KernelMode)) + { + EnterConditionMet = FALSE; + } - /* Exception inside the debugger? Game over. */ - if (KdbEntryCount > 0) - { - return(kdHandleException); - } - KdbEntryCount++; + /* If we stopped on one of our breakpoints then let the user know. */ + KdbLastBreakPointNr = -1; + KdbEnteredOnSingleStep = FALSE; - /* Clear the single step flag. */ - TrapFrame->Eflags &= ~(1 << 8); - /* - Reenable any breakpoints we disabled so we could execute the breakpointed - instructions. - */ - KdbRenableBreakPoints(); - /* Silently ignore a debugger initiated single step. */ - if (ExpNr == 1 && KdbIgnoreNextSingleStep) - { - KdbIgnoreNextSingleStep = FALSE; - KdbEntryCount--; - return(kdContinue); - } - /* If we stopped on one of our breakpoints then let the user know. */ - if (ExpNr == 3 && (BreakPointNr = KdbIsBreakPointOurs(TrapFrame)) >= 0) - { - DbgPrint("Entered debugger on breakpoint %d.\n", BreakPointNr); + if (FirstChance && (ExpNr == 1 || ExpNr == 3) && + (KdbLastBreakPointNr = KdbpIsBreakPointOurs(ExpNr, TrapFrame)) >= 0) + { + BreakPoint = KdbBreakPoints + KdbLastBreakPointNr; + + if (ExpNr == 3) + { + /* + * The breakpoint will point to the next instruction by default so + * point it back to the start of original instruction. + */ + TrapFrame->Eip--; + + /* + * ... and restore the original instruction. + */ + if (!NT_SUCCESS(KdbpOverwriteInstruction(KdbCurrentProcess, BreakPoint->Address, + BreakPoint->Data.SavedInstruction, NULL))) + { + DbgPrint("Couldn't restore original instruction after INT3! Cannot continue execution.\n"); + KEBUGCHECK(0); + } + } + + if ((BreakPoint->Type == KdbBreakPointHardware) && + (BreakPoint->Data.Hw.AccessType == KdbAccessExec)) + { + Resume = TRUE; /* Set the resume flag when continuing execution */ + } + /* - The breakpoint will point to the next instruction by default so - point it back to the start of original instruction. - */ - TrapFrame->Eip--; + * When a temporary breakpoint is hit we have to make sure that we are + * in the same context in which it was set, otherwise it could happen + * that another process/thread hits it before and it gets deleted. + */ + else if (BreakPoint->Type == KdbBreakPointTemporary && + BreakPoint->Process == KdbCurrentProcess) + { + ASSERT((TrapFrame->Eflags & X86_EFLAGS_TF) == 0); + + /* + * Delete the temporary breakpoint which was used to step over or into the instruction. + */ + KdbpDeleteBreakPoint(-1, BreakPoint); + + if (--KdbNumSingleSteps > 0) + { + if ((KdbSingleStepOver && !KdbpStepOverInstruction(TrapFrame->Eip)) || + (!KdbSingleStepOver && !KdbpStepIntoInstruction(TrapFrame->Eip))) + { + TrapFrame->Eflags |= X86_EFLAGS_TF; + } + goto continue_execution; /* return */ + } + + KdbEnteredOnSingleStep = TRUE; + } + /* - ..and restore the original instruction. - */ - (VOID)KdbOverwriteInst(TrapFrame->Eip, NULL, - KdbActiveBreakPoints[BreakPointNr].SavedInst); + * If we hit a breakpoint set by the debugger we set the single step flag, + * ignore the next single step and reenable the breakpoint. + */ + else if (BreakPoint->Type == KdbBreakPointSoftware || + BreakPoint->Type == KdbBreakPointTemporary) + { + ASSERT(ExpNr == 3); + TrapFrame->Eflags |= X86_EFLAGS_TF; + KdbBreakPointToReenable = BreakPoint; + } + /* - If this was a breakpoint set by the debugger then delete it otherwise - flag to enable it again after we step over this instruction. - */ - if (KdbActiveBreakPoints[BreakPointNr].Temporary) - { - KdbActiveBreakPoints[BreakPointNr].Assigned = FALSE; - KdbBreakPointCount--; - KdbEnteredOnSingleStep = TRUE; - } + * Make sure that the breakpoint should be triggered in this context + */ + if (!BreakPoint->Global && BreakPoint->Process != KdbCurrentProcess) + { + goto continue_execution; /* return */ + } + + /* + * Check if the condition for the breakpoint is met. + */ + if (BreakPoint->Condition != NULL) + { + /* Setup the KDB trap frame */ + RtlCopyMemory(&KdbTrapFrame.Tf, TrapFrame, sizeof (KTRAP_FRAME)); + asm volatile( + "movl %%cr0, %0" "\n\t" + "movl %%cr2, %1" "\n\t" + "movl %%cr3, %2" "\n\t" + "movl %%cr4, %3" "\n\t" + : "=r"(KdbTrapFrame.Cr0), "=r"(KdbTrapFrame.Cr2), + "=r"(KdbTrapFrame.Cr3), "=r"(KdbTrapFrame.Cr4)); + + ull = 0; + if (!KdbpRpnEvaluateParsedExpression(BreakPoint->Condition, &KdbTrapFrame, &ull, NULL, NULL)) + { + /* FIXME: Print warning? */ + } + else if (ull == 0) /* condition is not met */ + { + goto continue_execution; /* return */ + } + } + + if (BreakPoint->Type == KdbBreakPointSoftware) + { + DbgPrint("Entered debugger on breakpoint #%d: EXEC 0x%04x:0x%08x\n", + KdbLastBreakPointNr, TrapFrame->Cs & 0xffff, TrapFrame->Eip); + } + else if (BreakPoint->Type == KdbBreakPointHardware) + { + DbgPrint("Entered debugger on breakpoint #%d: %s 0x%08x\n", + KdbLastBreakPointNr, + (BreakPoint->Data.Hw.AccessType == KdbAccessRead) ? "READ" : + ((BreakPoint->Data.Hw.AccessType == KdbAccessWrite) ? "WRITE" : + ((BreakPoint->Data.Hw.AccessType == KdbAccessReadWrite) ? "RDWR" : "EXEC") + ), + BreakPoint->Address + ); + + } + } + else if (ExpNr == 1) + { + /* Silently ignore a debugger initiated single step. */ + if ((TrapFrame->Dr6 & 0xf) == 0 && KdbBreakPointToReenable != NULL) + { + /* FIXME: Make sure that the breakpoint was really hit (check bp->Address vs. tf->Eip) */ + BreakPoint = KdbBreakPointToReenable; + KdbBreakPointToReenable = NULL; + ASSERT(BreakPoint->Type == KdbBreakPointSoftware || + BreakPoint->Type == KdbBreakPointTemporary); + + /* + * Reenable the breakpoint we disabled to execute the breakpointed + * instruction. + */ + if (!NT_SUCCESS(KdbpOverwriteInstruction(KdbCurrentProcess, BreakPoint->Address, 0xCC, + &BreakPoint->Data.SavedInstruction))) + { + DbgPrint("Warning: Couldn't reenable breakpoint %d\n", + BreakPoint - KdbBreakPoints); + } + + /* Unset TF if we are no longer single stepping. */ + if (KdbNumSingleSteps == 0) + TrapFrame->Eflags &= ~X86_EFLAGS_TF; + goto continue_execution; /* return */ + } + + /* Check if we expect a single step */ + if ((TrapFrame->Dr6 & 0xf) == 0 && KdbNumSingleSteps > 0) + { + /*ASSERT((TrapFrame->Eflags & X86_EFLAGS_TF) != 0);*/ + if (--KdbNumSingleSteps > 0) + { + if ((KdbSingleStepOver && KdbpStepOverInstruction(TrapFrame->Eip)) || + (!KdbSingleStepOver && KdbpStepIntoInstruction(TrapFrame->Eip))) + { + TrapFrame->Eflags &= ~X86_EFLAGS_TF; + } + else + { + TrapFrame->Eflags |= X86_EFLAGS_TF; + } + goto continue_execution; /* return */ + } + + TrapFrame->Eflags &= ~X86_EFLAGS_TF; + KdbEnteredOnSingleStep = TRUE; + } else - { - KdbActiveBreakPoints[BreakPointNr].Enabled = FALSE; - TrapFrame->Eflags |= (1 << 8); - KdbIgnoreNextSingleStep = TRUE; - } - } - else if (ExpNr == 1) - { - if ((TrapFrame->Dr6 & 0xF) != 0) - { - DbgPrint("Entered debugger on memory breakpoint(s) %s%s%s%s.\n", - (TrapFrame->Dr6 & 0x1) ? "1" : "", - (TrapFrame->Dr6 & 0x2) ? "2" : "", - (TrapFrame->Dr6 & 0x4) ? "3" : "", - (TrapFrame->Dr6 & 0x8) ? "4" : ""); - } - else if (KdbLastSingleStepFrom != 0xFFFFFFFF) - { - KdbEnteredOnSingleStep = TRUE; - } + { + if (!EnterConditionMet) + { + return ContinueType; + } + DbgPrint("Entered debugger on unexpected debug trap!\n"); + } + } + else if (ExpNr == 3) + { + if (KdbInitFileBuffer != NULL) + { + KdbpCliInterpretInitFile(); + EnterConditionMet = FALSE; + } + if (!EnterConditionMet) + { + return ContinueType; + } + + DbgPrint("Entered debugger on embedded INT3 at 0x%04x:0x%08x.\n", + TrapFrame->Cs & 0xffff, TrapFrame->Eip - 1); + } + else + { + CONST PCHAR ExceptionString = (ExpNr < RTL_NUMBER_OF(ExceptionNrToString)) ? + (ExceptionNrToString[ExpNr]) : + ("Unknown/User defined exception"); + + if (!EnterConditionMet) + { + return ContinueType; + } + + DbgPrint("Entered debugger on %s-chance exception number %d (%s)\n", + FirstChance ? "first" : "last", ExpNr, ExceptionString); + if (ExpNr == 14) + { + /* FIXME: Add noexec memory stuff */ + ULONG Cr2, Err; + asm volatile("movl %%cr2, %0" : "=r"(Cr2)); + Err = TrapFrame->ErrorCode; + DbgPrint("Memory at 0x%x could not be %s: ", Cr2, (Err & (1 << 1)) ? "written" : "read"); + if ((Err & (1 << 0)) == 0) + DbgPrint("Page not present.\n"); + else + { + if ((Err & (1 << 3)) != 0) + DbgPrint("Reserved bits in page directory set.\n"); + else + DbgPrint("Page protection violation.\n"); + } + } + } + + /* Once we enter the debugger we do not expect any more single steps to happen */ + KdbNumSingleSteps = 0; + + /* Update the current process pointer */ + KdbCurrentProcess = KdbOriginalProcess = PsGetCurrentProcess(); + KdbCurrentThread = KdbOriginalThread = PsGetCurrentThread(); + KdbCurrentTrapFrame = &KdbTrapFrame; + + /* Setup the KDB trap frame */ + RtlCopyMemory(&KdbTrapFrame.Tf, TrapFrame, sizeof(KTRAP_FRAME)); + asm volatile( + "movl %%cr0, %0" "\n\t" + "movl %%cr2, %1" "\n\t" + "movl %%cr3, %2" "\n\t" + "movl %%cr4, %3" "\n\t" + : "=r"(KdbTrapFrame.Cr0), "=r"(KdbTrapFrame.Cr2), + "=r"(KdbTrapFrame.Cr3), "=r"(KdbTrapFrame.Cr4)); + + /* Enter critical section */ + Ke386SaveFlags(OldEflags); + Ke386DisableInterrupts(); + + /* Exception inside the debugger? Game over. */ + if (InterlockedIncrement(&KdbEntryCount) > 1) + { + Ke386RestoreFlags(OldEflags); + return kdHandleException; + } + + /* Call the main loop. */ + KdbpInternalEnter(); + + /* Check if we should single step */ + if (KdbNumSingleSteps > 0) + { + if ((KdbSingleStepOver && KdbpStepOverInstruction(KdbCurrentTrapFrame->Tf.Eip)) || + (!KdbSingleStepOver && KdbpStepIntoInstruction(KdbCurrentTrapFrame->Tf.Eip))) + { + ASSERT((KdbCurrentTrapFrame->Tf.Eflags & X86_EFLAGS_TF) == 0); + /*KdbCurrentTrapFrame->Tf.Eflags &= ~X86_EFLAGS_TF;*/ + } else - { - DbgPrint("Entered debugger on unexpected debug trap.\n"); - } - } - else - { - const char *ExceptionString = - (ExpNr < (sizeof (ExceptionTypeStrings) / sizeof (ExceptionTypeStrings[0]))) ? - (ExceptionTypeStrings[ExpNr]) : - ("Unknown/User defined exception"); - DbgPrint("Entered debugger on exception number %d (%s)\n", ExpNr, ExceptionString); - } - KdbInternalEnter(TrapFrame); - KdbEntryCount--; - if (ExpNr != 1 && ExpNr != 3) - { - return(kdHandleException); - } - else - { + { + KdbCurrentTrapFrame->Tf.Eflags |= X86_EFLAGS_TF; + } + } + + /* Save the current thread's trapframe */ + if (KdbCurrentTrapFrame == &KdbThreadTrapFrame) + { + RtlCopyMemory(KdbCurrentThread->Tcb.TrapFrame, KdbCurrentTrapFrame, sizeof (KTRAP_FRAME)); + } + + /* Detach from attached process */ + if (KdbCurrentProcess != KdbOriginalProcess) + { + KeUnstackDetachProcess(&KdbApcState); + } + + /* Update the exception TrapFrame */ + RtlCopyMemory(TrapFrame, &KdbTrapFrame.Tf, sizeof(KTRAP_FRAME)); +#if 0 + asm volatile( + "movl %0, %%cr0" "\n\t" + "movl %1, %%cr2" "\n\t" + "movl %2, %%cr3" "\n\t" + "movl %3, %%cr4" "\n\t" + : : "r"(KdbTrapFrame.Cr0), "r"(KdbTrapFrame.Cr2), + "r"(KdbTrapFrame.Cr3), "r"(KdbTrapFrame.Cr4)); +#endif + + /* Decrement the entry count */ + InterlockedDecrement(&KdbEntryCount); + + /* Leave critical section */ + Ke386RestoreFlags(OldEflags); + +continue_execution: + /* Clear debug status */ + if (ExpNr == 1 || ExpNr == 3) /* FIXME: Why clear DR6 on INT3? */ + { + /* Set the RF flag so we don't trigger the same breakpoint again. */ + if (Resume) + { + TrapFrame->Eflags |= X86_EFLAGS_RF; + } + /* Clear dr6 status flags. */ - TrapFrame->Dr6 &= 0xFFFF1F00; - /* Set the RF flag to we don't trigger the same breakpoint again. */ - if (ExpNr == 1) - { - TrapFrame->Eflags |= (1 << 16); - } - return(kdContinue); - } + TrapFrame->Dr6 &= ~0x0000e00f; + + } + + return ContinueType; +} + +VOID +KdbInit() +{ + KdbpCliInit(); +} + +VOID +KdbDeleteProcessHook(IN PEPROCESS Process) +{ + KdbSymFreeProcessSymbols(Process); + + /* FIXME: Delete breakpoints for process */ } VOID KdbModuleLoaded(IN PUNICODE_STRING Name) { - if (!KdbBreakOnModuleLoad) - return; - - DbgPrint("Module %wZ loaded.\n", Name); - DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C); + KdbpCliModuleLoaded(Name); } + diff --git a/reactos/ntoskrnl/dbg/kdb.h b/reactos/ntoskrnl/dbg/kdb.h index 46c08762505..c9b631d51a5 100644 --- a/reactos/ntoskrnl/dbg/kdb.h +++ b/reactos/ntoskrnl/dbg/kdb.h @@ -1,6 +1,82 @@ +#ifndef NTOSKRNL_KDB_H +#define NTOSKRNL_KDB_H + +/* INCLUDES ******************************************************************/ + #define NTOS_MODE_KERNEL #include +#include + +/* DEFINES *******************************************************************/ + +#define TAG_KDBG (('K' << 24) | ('D' << 16) | ('B' << 8) | 'G') + +#ifndef RTL_NUMBER_OF +# define RTL_NUMBER_OF(x) (sizeof(x) / sizeof((x)[0])) +#endif + + +/* TYPES *********************************************************************/ + +/* from kdb.c */ +typedef struct _KDB_KTRAP_FRAME +{ + KTRAP_FRAME Tf; + ULONG Cr0; + ULONG Cr1; /* reserved/unused */ + ULONG Cr2; + ULONG Cr3; + ULONG Cr4; +} KDB_KTRAP_FRAME, *PKDB_KTRAP_FRAME; + +typedef enum _KDB_BREAKPOINT_TYPE +{ + KdbBreakPointNone = 0, + KdbBreakPointSoftware, + KdbBreakPointHardware, + KdbBreakPointTemporary +} KDB_BREAKPOINT_TYPE; + +typedef enum _KDB_ACCESS_TYPE +{ + KdbAccessRead, + KdbAccessWrite, + KdbAccessReadWrite, + KdbAccessExec +} KDB_ACCESS_TYPE; + +typedef struct _KDB_BREAKPOINT +{ + KDB_BREAKPOINT_TYPE Type; /* Type of breakpoint */ + BOOLEAN Enabled; /* Whether the bp is enabled */ + ULONG_PTR Address; /* Address of the breakpoint */ + BOOLEAN Global; /* Whether the breakpoint is global or local to a process */ + PEPROCESS Process; /* Owning process */ + PCHAR ConditionExpression; + PVOID Condition; + union { + /* KdbBreakPointSoftware */ + UCHAR SavedInstruction; + /* KdbBreakPointHardware */ + struct { + UCHAR DebugReg : 2; + UCHAR Size : 3; + KDB_ACCESS_TYPE AccessType; + } Hw; + } Data; +} KDB_BREAKPOINT, *PKDB_BREAKPOINT; + +typedef enum _KDB_ENTER_CONDITION +{ + KdbDoNotEnter, + KdbEnterAlways, + KdbEnterFromKmode, + KdbEnterFromUmode +} KDB_ENTER_CONDITION; + + +/* from kdb_symbols.c */ typedef struct _KDB_MODULE_INFO { WCHAR Name[256]; @@ -9,6 +85,74 @@ typedef struct _KDB_MODULE_INFO PROSSYM_INFO RosSymInfo; } KDB_MODULE_INFO, *PKDB_MODULE_INFO; + +/* FUNCTIONS *****************************************************************/ + +/* from i386/i386-dis.c */ + +LONG +KdbpDisassemble( + IN ULONG Address, + IN ULONG IntelSyntax); + +LONG +KdbpGetInstLength( + IN ULONG Address); + +/* from i386/kdb_help.S */ + +STDCALL VOID +KdbpStackSwitchAndCall( + IN PVOID NewStack, + IN VOID (*Function)(VOID)); + +/* from kdb_cli.c */ + +extern PCHAR KdbInitFileBuffer; + +VOID +KdbpCliInit(); + +VOID +KdbpCliMainLoop( + IN BOOLEAN EnteredOnSingleStep); + +VOID +KdbpCliModuleLoaded( + IN PUNICODE_STRING Name); + +VOID +KdbpCliInterpretInitFile(); + +VOID +KdbpPrint( + IN PCHAR Format, + IN ... OPTIONAL); + +/* from kdb_expr.c */ + +BOOLEAN +KdbpRpnEvaluateExpression( + IN PCHAR Expression, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL); + +PVOID +KdbpRpnParseExpression( + IN PCHAR Expression, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL); + +BOOLEAN +KdbpRpnEvaluateParsedExpression( + IN PVOID Expression, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL); + /* from kdb_symbols.c */ BOOLEAN @@ -33,13 +177,97 @@ KdbSymGetAddressInformation(IN PROSSYM_INFO RosSymInfo, OUT PCH FileName OPTIONAL, OUT PCH FunctionName OPTIONAL); +/* from kdb.c */ + +extern PEPROCESS KdbCurrentProcess; +extern PETHREAD KdbCurrentThread; +extern LONG KdbLastBreakPointNr; +extern ULONG KdbNumSingleSteps; +extern BOOLEAN KdbSingleStepOver; +extern PKDB_KTRAP_FRAME KdbCurrentTrapFrame; + +VOID +KdbInit(); + +VOID +KdbModuleLoaded( + IN PUNICODE_STRING Name); + +LONG +KdbpGetNextBreakPointNr( + IN ULONG Start OPTIONAL); + +BOOLEAN +KdbpGetBreakPointInfo( + IN ULONG BreakPointNr, + OUT ULONG_PTR *Address OPTIONAL, + OUT KDB_BREAKPOINT_TYPE *Type OPTIONAL, + OUT UCHAR *Size OPTIONAL, + OUT KDB_ACCESS_TYPE *AccessType OPTIONAL, + OUT UCHAR *DebugReg OPTIONAL, + OUT BOOLEAN *Enabled OPTIONAL, + OUT BOOLEAN *Global OPTIONAL, + OUT PEPROCESS *Process OPTIONAL, + OUT PCHAR *ConditionExpression OPTIONAL); + +NTSTATUS +KdbpInsertBreakPoint( + IN ULONG_PTR Address, + IN KDB_BREAKPOINT_TYPE Type, + IN UCHAR Size OPTIONAL, + IN KDB_ACCESS_TYPE AccessType OPTIONAL, + IN PCHAR ConditionExpression OPTIONAL, + IN BOOLEAN Global, + OUT PULONG BreakPointNumber OPTIONAL); + +BOOLEAN +KdbpDeleteBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL); + +BOOLEAN +KdbpEnableBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL); + +BOOLEAN +KdbpDisableBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL); + +BOOLEAN +KdbpGetEnterCondition( + IN LONG ExceptionNr, + IN BOOLEAN FirstChance, + OUT KDB_ENTER_CONDITION *Condition); + +BOOLEAN +KdbpSetEnterCondition( + IN LONG ExceptionNr, + IN BOOLEAN FirstChance, + IN KDB_ENTER_CONDITION Condition); + +BOOLEAN +KdbpAttachToThread( + PVOID ThreadId); + +BOOLEAN +KdbpAttachToProcess( + PVOID ProcessId); + /* other functions */ -#define KdbpSafeReadMemory(dst, src, size) MmSafeCopyFromUser(dst, src, size) -#define KdbpSafeWriteMemory(dst, src, size) MmSafeCopyToUser(dst, src, size) + +#define KdbpSafeReadMemory(dst, src, size) MmSafeCopyFromUser(dst, src, size) +#define KdbpSafeWriteMemory(dst, src, size) MmSafeCopyToUser(dst, src, size) + +#define KdbpGetCharKeyboard(ScanCode) KdbpTryGetCharKeyboard(ScanCode, 0) CHAR -KdbTryGetCharKeyboard(PULONG ScanCode); -ULONG -KdbTryGetCharSerial(VOID); +KdbpTryGetCharKeyboard(PULONG ScanCode, UINT Retry); + +#define KdbpGetCharSerial() KdbpTryGetCharSerial(0) +CHAR +KdbpTryGetCharSerial(UINT Retry); + VOID KdbEnter(VOID); VOID @@ -50,24 +278,7 @@ VOID DbgEnableFile(PCH Filename); VOID DbgDisableFile(PCH Filename); -VOID -KdbInitProfiling(); -VOID -KdbInitProfiling2(); -VOID -KdbDisableProfiling(); -VOID -KdbEnableProfiling(); -VOID -KdbProfileInterrupt(ULONG_PTR Eip); - -VOID -KdbModuleLoaded(IN PUNICODE_STRING Name); -struct KDB_BPINFO { - DWORD Addr; - DWORD Type; - DWORD Size; - DWORD Enabled; -}; +#endif /* NTOSKRNL_KDB_H */ + diff --git a/reactos/ntoskrnl/dbg/kdb_cli.c b/reactos/ntoskrnl/dbg/kdb_cli.c new file mode 100644 index 00000000000..75f2c78c75c --- /dev/null +++ b/reactos/ntoskrnl/dbg/kdb_cli.c @@ -0,0 +1,2368 @@ +/* + * ReactOS kernel + * Copyright (C) 2005 ReactOS Team + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* $Id$ + * + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/dbg/kdb_cli.c + * PURPOSE: Kernel debugger command line interface + * PROGRAMMER: Gregor Anich (blight@blight.eu.org) + * UPDATE HISTORY: + * Created 16/01/2005 + */ + +/* INCLUDES ******************************************************************/ + +#include +#include +#include "kdb.h" + +#define NDEBUG +#include + +/* DEFINES *******************************************************************/ + +#define KEY_BS 8 +#define KEY_ESC 27 +#define KEY_DEL 127 + +#define KEY_SCAN_UP 72 +#define KEY_SCAN_DOWN 80 + +#define KDB_ENTER_CONDITION_TO_STRING(cond) \ + ((cond) == KdbDoNotEnter ? "never" : \ + ((cond) == KdbEnterAlways ? "always" : \ + ((cond) == KdbEnterFromKmode ? "kmode" : "umode"))) + +#define KDB_ACCESS_TYPE_TO_STRING(type) \ + ((type) == KdbAccessRead ? "read" : \ + ((type) == KdbAccessWrite ? "write" : \ + ((type) == KdbAccessReadWrite ? "rdwr" : "exec"))) + +#define NPX_STATE_TO_STRING(state) \ + ((state) == NPX_STATE_INVALID ? "Invalid" : \ + ((state) == NPX_STATE_VALID ? "Valid" : \ + ((state) == NPX_STATE_DIRTY ? "Dirty" : "Unknown"))) + +/* PROTOTYPES ****************************************************************/ + +STATIC BOOLEAN KdbpCmdEvalExpression(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdDisassembleX(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdRegs(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdBackTrace(ULONG Argc, PCHAR Argv[]); + +STATIC BOOLEAN KdbpCmdContinue(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdStep(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdBreakPointList(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdEnableDisableClearBreakPoint(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[]); + +STATIC BOOLEAN KdbpCmdThread(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdProc(ULONG Argc, PCHAR Argv[]); + +STATIC BOOLEAN KdbpCmdMod(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdGdtLdtIdt(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdPcr(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdTss(ULONG Argc, PCHAR Argv[]); + +STATIC BOOLEAN KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdSet(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdHelp(ULONG Argc, PCHAR Argv[]); + +/* GLOBALS *******************************************************************/ + +STATIC BOOLEAN KdbUseIntelSyntax = FALSE; /* Set to TRUE for intel syntax */ + +STATIC CHAR KdbCommandHistoryBuffer[2048]; /* Command history string ringbuffer */ +STATIC PCHAR KdbCommandHistory[sizeof(KdbCommandHistoryBuffer) / 8] = { NULL }; /* Command history ringbuffer */ +STATIC LONG KdbCommandHistoryBufferIndex = 0; +STATIC LONG KdbCommandHistoryIndex = 0; + +STATIC ULONG KdbNumberOfRowsPrinted = 0; +STATIC ULONG KdbNumberOfColsPrinted = 0; +STATIC BOOLEAN KdbOutputAborted = FALSE; +STATIC LONG KdbNumberOfRowsTerminal = -1; +STATIC LONG KdbNumberOfColsTerminal = -1; + +PCHAR KdbInitFileBuffer = NULL; /* Buffer where KDBinit file is loaded into during initialization */ + +STATIC CONST struct +{ + PCHAR Name; + PCHAR Syntax; + PCHAR Help; + BOOLEAN (*Fn)(ULONG Argc, PCHAR Argv[]); +} KdbDebuggerCommands[] = { + /* Data */ + { NULL, NULL, "Data", NULL }, + { "?", "? expression", "Evaluate expression.", KdbpCmdEvalExpression }, + { "disasm", "disasm [address] [L count]", "Disassemble count instructions at address.", KdbpCmdDisassembleX }, + { "x", "x [address] [L count]", "Display count dwords, starting at addr.", KdbpCmdDisassembleX }, + { "regs", "regs", "Display general purpose registers.", KdbpCmdRegs }, + { "cregs", "cregs", "Display control registers.", KdbpCmdRegs }, + { "sregs", "sregs", "Display status registers.", KdbpCmdRegs }, + { "dregs", "dregs", "Display debug registers.", KdbpCmdRegs }, + { "bt", "bt [*frameaddr|thread id]", "Prints current backtrace or from given frame addr", KdbpCmdBackTrace }, + + /* Flow control */ + { NULL, NULL, "Flow control", NULL }, + { "cont", "cont", "Continue execution (leave debugger)", KdbpCmdContinue }, + { "step", "step [count]", "Execute single instructions, stepping into interrupts.", KdbpCmdStep }, + { "next", "next [count]", "Execute single instructions, skipping calls and reps.", KdbpCmdStep }, + { "bl", "bl", "List breakpoints.", KdbpCmdBreakPointList }, + { "be", "be [breakpoint]", "Enable breakpoint.", KdbpCmdEnableDisableClearBreakPoint }, + { "bd", "bd [breakpoint]", "Disable breakpoint.", KdbpCmdEnableDisableClearBreakPoint }, + { "bc", "bc [breakpoint]", "Clear breakpoint.", KdbpCmdEnableDisableClearBreakPoint }, + { "bpx", "bpx [address] [IF condition]", "Set software execution breakpoint at address.", KdbpCmdBreakPoint }, + { "bpm", "bpm [r|w|rw|x] [byte|word|dword] [address] [IF condition]", "Set memory breakpoint at address.", KdbpCmdBreakPoint }, + + /* Process/Thread */ + { NULL, NULL, "Process/Thread", NULL }, + { "thread", "thread [list[ pid]|[attach ]tid]", "List threads in current or specified process, display thread with given id or attach to thread.", KdbpCmdThread }, + { "proc", "proc [list|[attach ]pid]", "List processes, display process with given id or attach to process.", KdbpCmdProc }, + + /* System information */ + { NULL, NULL, "System info", NULL }, + { "mod", "mod [address]", "List all modules or the one containing address.", KdbpCmdMod }, + { "gdt", "gdt", "Display global descriptor table.", KdbpCmdGdtLdtIdt }, + { "ldt", "ldt", "Display local descriptor table.", KdbpCmdGdtLdtIdt }, + { "idt", "idt", "Display interrupt descriptor table.", KdbpCmdGdtLdtIdt }, + { "pcr", "pcr", "Display processor control region.", KdbpCmdPcr }, + { "tss", "tss", "Display task state segment.", KdbpCmdTss }, + + /* Others */ + { NULL, NULL, "Others", NULL }, + { "bugcheck", "bugcheck", "Bugchecks the system.", KdbpCmdBugCheck }, + { "set", "set [var] [value]", "Sets var to value or displays value of var.", KdbpCmdSet }, + { "help", "help", "Display help screen.", KdbpCmdHelp } +}; + +/* FUNCTIONS *****************************************************************/ + +/*!\brief Evaluates an expression... + * + * Much like KdbpRpnEvaluateExpression, but prints the error message (if any) + * at the given offset. + * + * \param Expression Expression to evaluate. + * \param ErrOffset Offset (in characters) to print the error message at. + * \param Result Receives the result on success. + * + * \retval TRUE Success. + * \retval FALSE Failure. + */ +STATIC BOOLEAN +KdbpEvaluateExpression( + IN PCHAR Expression, + IN LONG ErrOffset, + OUT PULONGLONG Result) +{ + STATIC CHAR ErrMsgBuffer[130] = "^ "; + LONG ExpressionErrOffset = -1; + PCHAR ErrMsg = ErrMsgBuffer; + BOOLEAN Ok; + + Ok = KdbpRpnEvaluateExpression(Expression, KdbCurrentTrapFrame, Result, + &ExpressionErrOffset, ErrMsgBuffer + 2); + if (!Ok) + { + if (ExpressionErrOffset >= 0) + ExpressionErrOffset += ErrOffset; + else + ErrMsg += 2; + KdbpPrint("%*s%s\n", ExpressionErrOffset, "", ErrMsg); + } + + return Ok; +} + +/*!\brief Evaluates an expression and displays the result. + */ +STATIC BOOLEAN +KdbpCmdEvalExpression(ULONG Argc, PCHAR Argv[]) +{ + INT i, len; + ULONGLONG Result = 0; + ULONG ul; + LONG l = 0; + BOOLEAN Ok; + + if (Argc < 2) + { + KdbpPrint("?: Argument required\n"); + return TRUE; + } + + /* Put the arguments back together */ + Argc--; + for (i = 1; i < Argc; i++) + { + len = strlen(Argv[i]); + Argv[i][len] = ' '; + } + + /* Evaluate the expression */ + Ok = KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result); + if (Ok) + { + if (Result > 0x00000000ffffffffLL) + { + if (Result & 0x8000000000000000LL) + KdbpPrint("0x%016I64x %20I64u %20I64d\n", Result, Result, Result); + else + KdbpPrint("0x%016I64x %20I64u\n", Result, Result); + } + else + { + ul = (ULONG)Result; + if (ul <= 0xff && ul >= 0x80) + l = (LONG)((CHAR)ul); + else if (ul <= 0xffff && ul >= 0x8000) + l = (LONG)((SHORT)ul); + else + l = (LONG)ul; + if (l < 0) + KdbpPrint("0x%08lx %10lu %10ld\n", ul, ul, l); + else + KdbpPrint("0x%08lx %10lu\n", ul, ul); + } + } + + return TRUE; +} + +/*!\brief Disassembles 10 instructions at eip or given address or + * displays 16 dwords from memory at given address. + */ +STATIC BOOLEAN +KdbpCmdDisassembleX(ULONG Argc, PCHAR Argv[]) +{ + ULONG Count; + ULONG ul; + INT i; + ULONGLONG Result = 0; + ULONG_PTR Address = KdbCurrentTrapFrame->Tf.Eip; + LONG InstLen; + + if (Argv[0][0] == 'x') /* display memory */ + Count = 16; + else /* disassemble */ + Count = 10; + + if (Argc >= 2) + { + /* Check for [L count] part */ + ul = 0; + if (strcmp(Argv[Argc-2], "L") == 0) + { + ul = strtoul(Argv[Argc-1], NULL, 0); + if (ul > 0) + { + Count = ul; + Argc -= 2; + } + } + else if (Argv[Argc-1][0] == 'L') + { + ul = strtoul(Argv[Argc-1] + 1, NULL, 0); + if (ul > 0) + { + Count = ul; + Argc--; + } + } + + /* Put the remaining arguments back together */ + Argc--; + for (ul = 1; ul < Argc; ul++) + { + Argv[ul][strlen(Argv[ul])] = ' '; + } + Argc++; + } + + /* Evaluate the expression */ + if (Argc > 1) + { + if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result)) + return TRUE; + if (Result > (ULONGLONG)(~((ULONG_PTR)0))) + KdbpPrint("Warning: Address %I64x is beeing truncated\n"); + Address = (ULONG_PTR)Result; + } + else if (Argv[0][0] == 'x') + { + KdbpPrint("x: Address argument required.\n"); + return TRUE; + } + + if (Argv[0][0] == 'x') + { + /* Display dwords */ + ul = 0; + while (Count > 0) + { + if (!KdbSymPrintAddress((PVOID)Address)) + KdbpPrint("<%x>:", Address); + else + KdbpPrint(":"); + i = min(4, Count); + Count -= i; + while (--i >= 0) + { + if (!NT_SUCCESS(KdbpSafeReadMemory(&ul, (PVOID)Address, sizeof(ul)))) + KdbpPrint(" ????????"); + else + KdbpPrint(" %08x", ul); + Address += sizeof(ul); + } + KdbpPrint("\n"); + } + } + else + { + /* Disassemble */ + while (Count-- > 0) + { + if (!KdbSymPrintAddress((PVOID)Address)) + KdbpPrint("<%08x>: ", Address); + else + KdbpPrint(": "); + InstLen = KdbpDisassemble(Address, KdbUseIntelSyntax); + if (InstLen < 0) + { + KdbpPrint("\n"); + return TRUE; + } + KdbpPrint("\n"); + Address += InstLen; + } + } + + return TRUE; +} + +/*!\brief Displays CPU registers. + */ +STATIC BOOLEAN +KdbpCmdRegs(ULONG Argc, PCHAR Argv[]) +{ + PKTRAP_FRAME Tf = &KdbCurrentTrapFrame->Tf; + INT i; + STATIC CONST PCHAR EflagsBits[32] = { " CF", NULL, " PF", " BIT3", " AF", " BIT5", + " ZF", " SF", " TF", " IF", " DF", " OF", + NULL, NULL, " NT", " BIT15", " RF", " VF", + " AC", " VIF", " VIP", " ID", " BIT22", + " BIT23", " BIT24", " BIT25", " BIT26", + " BIT27", " BIT28", " BIT29", " BIT30", + " BIT31" }; + + if (Argv[0][0] == 'r') /* regs */ + { + KdbpPrint("CS:EIP 0x%04x:0x%08x\n" + "SS:ESP 0x%04x:0x%08x\n" + " EAX 0x%08x EBX 0x%08x\n" + " ECX 0x%08x EDX 0x%08x\n" + " ESI 0x%08x EDI 0x%08x\n" + " EBP 0x%08x\n", + Tf->Cs & 0xFFFF, Tf->Eip, + Tf->Ss, Tf->Esp, + Tf->Eax, Tf->Ebx, + Tf->Ecx, Tf->Edx, + Tf->Esi, Tf->Edi, + Tf->Ebp); + KdbpPrint("EFLAGS 0x%08x ", Tf->Eflags); + for (i = 0; i < 32; i++) + { + if (i == 1) + { + if ((Tf->Eflags & (1 << 1)) == 0) + KdbpPrint(" !BIT1"); + } + else if (i == 12) + { + KdbpPrint(" IOPL%d", (Tf->Eflags >> 12) & 3); + } + else if (i == 13) + { + } + else if ((Tf->Eflags & (1 << i)) != 0) + KdbpPrint(EflagsBits[i]); + } + KdbpPrint("\n"); + } + else if (Argv[0][0] == 'c') /* cregs */ + { + ULONG Cr0, Cr2, Cr3, Cr4; + struct __attribute__((packed)) { + USHORT Limit; + ULONG Base; + } Gdtr, Ldtr, Idtr; + ULONG Tr; + STATIC CONST PCHAR Cr0Bits[32] = { " PE", " MP", " EM", " TS", " ET", " NE", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + " WP", NULL, " AM", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, " NW", " CD", " PG" }; + STATIC CONST PCHAR Cr4Bits[32] = { " VME", " PVI", " TSD", " DE", " PSE", " PAE", " MCE", " PGE", + " PCE", " OSFXSR", " OSXMMEXCPT", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + + Cr0 = KdbCurrentTrapFrame->Cr0; + Cr2 = KdbCurrentTrapFrame->Cr2; + Cr3 = KdbCurrentTrapFrame->Cr3; + Cr4 = KdbCurrentTrapFrame->Cr4; + + /* Get descriptor table regs */ + asm volatile("sgdt %0" : : "m"(Gdtr)); + asm volatile("sldt %0" : : "m"(Ldtr)); + asm volatile("sidt %0" : : "m"(Idtr)); + + /* Get the task register */ + asm volatile("str %0" : "=g"(Tr)); + + /* Display the control registers */ + KdbpPrint("CR0 0x%08x ", Cr0); + for (i = 0; i < 32; i++) + { + if (Cr0Bits[i] == NULL) + continue; + if ((Cr0 & (1 << i)) != 0) + KdbpPrint(Cr0Bits[i]); + } + KdbpPrint("\nCR2 0x%08x\n", Cr2); + KdbpPrint("CR3 0x%08x Pagedir-Base 0x%08x %s%s\n", Cr3, (Cr3 & 0xfffff000), + (Cr3 & (1 << 3)) ? " PWT" : "", (Cr3 & (1 << 4)) ? " PCD" : "" ); + KdbpPrint("CR4 0x%08x ", Cr4); + for (i = 0; i < 32; i++) + { + if (Cr4Bits[i] == NULL) + continue; + if ((Cr4 & (1 << i)) != 0) + KdbpPrint(Cr4Bits[i]); + } + + /* Display the descriptor table regs */ + KdbpPrint("\nGDTR Base 0x%08x Size 0x%04x\n", Gdtr.Base, Gdtr.Limit); + KdbpPrint("LDTR Base 0x%08x Size 0x%04x\n", Ldtr.Base, Ldtr.Limit); + KdbpPrint("IDTR Base 0x%08x Size 0x%04x\n", Idtr.Base, Idtr.Limit); + } + else if (Argv[0][0] == 's') /* sregs */ + { + KdbpPrint("CS 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Cs & 0xffff, (Tf->Cs & 0xffff) >> 3, + (Tf->Cs & (1 << 2)) ? 'L' : 'G', Tf->Cs & 3); + KdbpPrint("DS 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Ds, Tf->Ds >> 3, (Tf->Ds & (1 << 2)) ? 'L' : 'G', Tf->Ds & 3); + KdbpPrint("ES 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Es, Tf->Es >> 3, (Tf->Es & (1 << 2)) ? 'L' : 'G', Tf->Es & 3); + KdbpPrint("FS 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Fs, Tf->Fs >> 3, (Tf->Fs & (1 << 2)) ? 'L' : 'G', Tf->Fs & 3); + KdbpPrint("GS 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Gs, Tf->Gs >> 3, (Tf->Gs & (1 << 2)) ? 'L' : 'G', Tf->Gs & 3); + KdbpPrint("SS 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Ss, Tf->Ss >> 3, (Tf->Ss & (1 << 2)) ? 'L' : 'G', Tf->Ss & 3); + } + else /* dregs */ + { + ASSERT(Argv[0][0] == 'd'); + KdbpPrint("DR0 0x%08x\n" + "DR1 0x%08x\n" + "DR2 0x%08x\n" + "DR3 0x%08x\n" + "DR6 0x%08x\n" + "DR7 0x%08x\n", + Tf->Dr0, Tf->Dr1, Tf->Dr2, Tf->Dr3, + Tf->Dr6, Tf->Dr7); + } + return TRUE; +} + +/*!\brief Displays a backtrace. + */ +STATIC BOOLEAN +KdbpCmdBackTrace(ULONG Argc, PCHAR Argv[]) +{ + ULONG Count; + ULONG ul; + ULONGLONG Result = 0; + ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Ebp; + ULONG_PTR Address; + + if (Argc >= 2) + { + /* Check for [L count] part */ + ul = 0; + if (strcmp(Argv[Argc-2], "L") == 0) + { + ul = strtoul(Argv[Argc-1], NULL, 0); + if (ul > 0) + { + Count = ul; + Argc -= 2; + } + } + else if (Argv[Argc-1][0] == 'L') + { + ul = strtoul(Argv[Argc-1] + 1, NULL, 0); + if (ul > 0) + { + Count = ul; + Argc--; + } + } + + /* Put the remaining arguments back together */ + Argc--; + for (ul = 1; ul < Argc; ul++) + { + Argv[ul][strlen(Argv[ul])] = ' '; + } + Argc++; + } + + /* Check if frame addr or thread id is given. */ + if (Argc > 1) + { + if (Argv[1][0] == '*') + { + Argv[1]++; + /* Evaluate the expression */ + if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result)) + return TRUE; + if (Result > (ULONGLONG)(~((ULONG_PTR)0))) + KdbpPrint("Warning: Address %I64x is beeing truncated\n"); + Frame = (ULONG_PTR)Result; + } + else + { + + KdbpPrint("Thread backtrace not supported yet!\n"); + return TRUE; + } + } + + KdbpPrint("Frames:\n"); + while (Frame != 0) + { + if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, (PVOID)(Frame + sizeof(ULONG_PTR)), sizeof (ULONG_PTR)))) + { + KdbpPrint("Couldn't access memory at 0x%x!\n", Frame + sizeof(ULONG_PTR)); + break; + } + if (!KdbSymPrintAddress((PVOID)Address)) + KdbpPrint("<%08x>\n", Address); + else + KdbpPrint("\n"); + if (!NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof (ULONG_PTR)))) + { + KdbpPrint("Couldn't access memory at 0x%x!\n", Frame); + break; + } + } + + return TRUE; +} + +/*!\brief Continues execution of the system/leaves KDB. + */ +STATIC BOOLEAN +KdbpCmdContinue(ULONG Argc, PCHAR Argv[]) +{ + /* Exit the main loop */ + return FALSE; +} + +/*!\brief Continues execution of the system/leaves KDB. + */ +STATIC BOOLEAN +KdbpCmdStep(ULONG Argc, PCHAR Argv[]) +{ + ULONG Count = 1; + + if (Argc > 1) + { + Count = strtoul(Argv[1], NULL, 0); + if (Count == 0) + { + KdbpPrint("%s: Integer argument required\n", Argv[0]); + return TRUE; + } + } + + if (Argv[0][0] == 'n') + KdbSingleStepOver = TRUE; + else + KdbSingleStepOver = FALSE; + + /* Set the number of single steps and return to the interrupted code. */ + KdbNumSingleSteps = Count; + + return FALSE; +} + +/*!\brief Lists breakpoints. + */ +STATIC BOOLEAN +KdbpCmdBreakPointList(ULONG Argc, PCHAR Argv[]) +{ + LONG l; + ULONG_PTR Address = 0; + KDB_BREAKPOINT_TYPE Type = 0; + KDB_ACCESS_TYPE AccessType = 0; + UCHAR Size = 0; + UCHAR DebugReg = 0; + BOOLEAN Enabled = FALSE; + BOOLEAN Global = FALSE; + PEPROCESS Process = NULL; + PCHAR str1, str2, ConditionExpr, GlobalOrLocal; + CHAR Buffer[20]; + + l = KdbpGetNextBreakPointNr(0); + if (l < 0) + { + KdbpPrint("No breakpoints.\n"); + return TRUE; + } + + KdbpPrint("Breakpoints:\n"); + do + { + if (!KdbpGetBreakPointInfo(l, &Address, &Type, &Size, &AccessType, &DebugReg, + &Enabled, &Global, &Process, &ConditionExpr)) + { + continue; + } + + if (l == KdbLastBreakPointNr) + { + str1 = "\x1b[1m*"; + str2 = "\x1b[0m"; + } + else + { + str1 = " "; + str2 = ""; + } + + if (Global) + GlobalOrLocal = " global"; + else + { + GlobalOrLocal = Buffer; + sprintf(Buffer, " PID 0x%08lx", + (ULONG)(Process ? Process->UniqueProcessId : INVALID_HANDLE_VALUE)); + } + + if (Type == KdbBreakPointSoftware || Type == KdbBreakPointTemporary) + { + KdbpPrint(" %s%03d BPX 0x%08x%s%s%s%s%s\n", + str1, l, Address, + Enabled ? "" : " disabled", + GlobalOrLocal, + ConditionExpr ? " IF " : "", + ConditionExpr ? ConditionExpr : "", + str2); + } + else if (Type == KdbBreakPointHardware) + { + if (!Enabled) + { + KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s disabled%s%s%s%s\n", str1, l, Address, + KDB_ACCESS_TYPE_TO_STRING(AccessType), + Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"), + GlobalOrLocal, + ConditionExpr ? " IF " : "", + ConditionExpr ? ConditionExpr : "", + str2); + } + else + { + KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s DR%d%s%s%s%s\n", str1, l, Address, + KDB_ACCESS_TYPE_TO_STRING(AccessType), + Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"), + DebugReg, + GlobalOrLocal, + ConditionExpr ? " IF " : "", + ConditionExpr ? ConditionExpr : "", + str2); + } + } + } + while ((l = KdbpGetNextBreakPointNr(l+1)) >= 0); + + return TRUE; +} + +/*!\brief Enables, disables or clears a breakpoint. + */ +STATIC BOOLEAN +KdbpCmdEnableDisableClearBreakPoint(ULONG Argc, PCHAR Argv[]) +{ + PCHAR pend; + ULONG BreakPointNr; + + if (Argc < 2) + { + KdbpPrint("%s: argument required\n", Argv[0]); + return TRUE; + } + + pend = Argv[1]; + BreakPointNr = strtoul(Argv[1], &pend, 0); + if (pend == Argv[1] || *pend != '\0') + { + KdbpPrint("%s: integer argument required\n", Argv[0]); + return TRUE; + } + + if (Argv[0][1] == 'e') /* enable */ + { + KdbpEnableBreakPoint(BreakPointNr, NULL); + } + else if (Argv [0][1] == 'd') /* disable */ + { + KdbpDisableBreakPoint(BreakPointNr, NULL); + } + else /* clear */ + { + ASSERT(Argv[0][1] == 'c'); + KdbpDeleteBreakPoint(BreakPointNr, NULL); + } + + return TRUE; +} + +/*!\brief Sets a software or hardware (memory) breakpoint at the given address. + */ +STATIC BOOLEAN +KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[]) +{ + ULONGLONG Result = 0; + ULONG_PTR Address; + KDB_BREAKPOINT_TYPE Type; + UCHAR Size = 0; + KDB_ACCESS_TYPE AccessType = 0; + INT AddressArgIndex, ConditionArgIndex, i; + BOOLEAN Global = TRUE; + + if (Argv[0][2] == 'x') /* software breakpoint */ + { + if (Argc < 2) + { + KdbpPrint("bpx: Address argument required.\n"); + return TRUE; + } + + AddressArgIndex = 1; + Type = KdbBreakPointSoftware; + } + else /* memory breakpoint */ + { + ASSERT(Argv[0][2] == 'm'); + + if (Argc < 2) + { + KdbpPrint("bpm: Access type argument required (one of r, w, rw, x)\n"); + return TRUE; + } + + if (_stricmp(Argv[1], "x") == 0) + AccessType = KdbAccessExec; + else if (_stricmp(Argv[1], "r") == 0) + AccessType = KdbAccessRead; + else if (_stricmp(Argv[1], "w") == 0) + AccessType = KdbAccessWrite; + else if (_stricmp(Argv[1], "rw") == 0) + AccessType = KdbAccessReadWrite; + else + { + KdbpPrint("bpm: Unknown access type '%s'\n", Argv[1]); + return TRUE; + } + + if (Argc < 3) + { + KdbpPrint("bpm: %s argument required.\n", AccessType == KdbAccessExec ? "Address" : "Memory size"); + return TRUE; + } + AddressArgIndex = 3; + if (_stricmp(Argv[2], "byte") == 0) + Size = 1; + else if (_stricmp(Argv[2], "word") == 0) + Size = 2; + else if (_stricmp(Argv[2], "dword") == 0) + Size = 4; + else if (AccessType == KdbAccessExec) + { + Size = 1; + AddressArgIndex--; + } + else + { + KdbpPrint("bpm: Unknown memory size '%s'\n", Argv[2]); + return TRUE; + } + + if (Argc <= AddressArgIndex) + { + KdbpPrint("bpm: Address argument required.\n"); + return TRUE; + } + + Type = KdbBreakPointHardware; + } + + /* Put the arguments back together */ + ConditionArgIndex = -1; + for (i = AddressArgIndex; i < (Argc-1); i++) + { + if (strcmp(Argv[i+1], "IF") == 0) /* IF found */ + { + ConditionArgIndex = i + 2; + if (ConditionArgIndex >= Argc) + { + KdbpPrint("%s: IF requires condition expression.\n", Argv[0]); + return TRUE; + } + for (i = ConditionArgIndex; i < (Argc-1); i++) + Argv[i][strlen(Argv[i])] = ' '; + break; + } + Argv[i][strlen(Argv[i])] = ' '; + } + + /* Evaluate the address expression */ + if (!KdbpEvaluateExpression(Argv[AddressArgIndex], + sizeof("kdb:> ")-1 + (Argv[AddressArgIndex]-Argv[0]), + &Result)) + { + return TRUE; + } + if (Result > (ULONGLONG)(~((ULONG_PTR)0))) + KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0]); + Address = (ULONG_PTR)Result; + + KdbpInsertBreakPoint(Address, Type, Size, AccessType, + (ConditionArgIndex < 0) ? NULL : Argv[ConditionArgIndex], + Global, NULL); + + return TRUE; +} + +/*!\brief Lists threads or switches to another thread context. + */ +STATIC BOOLEAN +KdbpCmdThread(ULONG Argc, PCHAR Argv[]) +{ + PLIST_ENTRY Entry; + PETHREAD Thread = NULL; + PEPROCESS Process = NULL; + PULONG Esp; + PULONG Ebp; + ULONG Eip; + ULONG ul = 0; + PCHAR State, pend, str1, str2; + STATIC CONST PCHAR ThreadStateToString[THREAD_STATE_MAX] = + { "Initialized", "Ready", "Running", + "Suspended", "Frozen", "Terminated1", + "Terminated2", "Blocked" }; + ASSERT(KdbCurrentProcess != NULL); + + if (Argc >= 2 && _stricmp(Argv[1], "list") == 0) + { + Process = KdbCurrentProcess; + + if (Argc >= 3) + { + ul = strtoul(Argv[2], &pend, 0); + if (Argv[2] == pend) + { + KdbpPrint("thread: '%s' is not a valid process id!\n", Argv[2]); + return TRUE; + } + if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process))) + { + KdbpPrint("thread: Invalid process id!\n"); + return TRUE; + } + } + + Entry = Process->ThreadListHead.Flink; + if (Entry == &Process->ThreadListHead) + { + if (Argc >= 3) + KdbpPrint("No threads in process 0x%08x!\n", ul); + else + KdbpPrint("No threads in current process!\n"); + return TRUE; + } + + KdbpPrint(" TID State Prior. Affinity EBP EIP\n"); + do + { + Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry); + + if (Thread == KdbCurrentThread) + { + str1 = "\x1b[1m*"; + str2 = "\x1b[0m"; + } + else + { + str1 = " "; + str2 = ""; + } + + if (Thread->Tcb.TrapFrame != NULL) + { + Esp = (PULONG)Thread->Tcb.TrapFrame->Esp; + Ebp = (PULONG)Thread->Tcb.TrapFrame->Ebp; + Eip = Thread->Tcb.TrapFrame->Eip; + } + else + { + Esp = (PULONG)Thread->Tcb.KernelStack; + Ebp = (PULONG)Esp[4]; + Eip = 0; + if (Ebp != NULL) /* FIXME: Should we attach to the process to read Ebp[1]? */ + KdbpSafeReadMemory(&Eip, Ebp + 1, sizeof (Eip));; + } + if (Thread->Tcb.State < THREAD_STATE_MAX) + State = ThreadStateToString[Thread->Tcb.State]; + else + State = "Unknown"; + + KdbpPrint(" %s0x%08x %-11s %3d 0x%08x 0x%08x 0x%08x%s\n", + str1, + Thread->Cid.UniqueThread, + State, + Thread->Tcb.Priority, + Thread->Tcb.Affinity, + Ebp, + Eip, + str2); + + Entry = Entry->Flink; + } + while (Entry != &Process->ThreadListHead); + } + else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0) + { + if (Argc < 3) + { + KdbpPrint("thread attach: thread id argument required!\n"); + return TRUE; + } + + ul = strtoul(Argv[2], &pend, 0); + if (Argv[2] == pend) + { + KdbpPrint("thread attach: '%s' is not a valid thread id!\n", Argv[2]); + return TRUE; + } + if (!KdbpAttachToThread((PVOID)ul)) + { + return TRUE; + } + KdbpPrint("Attached to thread 0x%08x.\n", ul); + } + else + { + Thread = KdbCurrentThread; + + if (Argc >= 2) + { + ul = strtoul(Argv[1], &pend, 0); + if (Argv[1] == pend) + { + KdbpPrint("thread: '%s' is not a valid thread id!\n", Argv[1]); + return TRUE; + } + if (!NT_SUCCESS(PsLookupThreadByThreadId((PVOID)ul, &Thread))) + { + KdbpPrint("thread: Invalid thread id!\n"); + return TRUE; + } + } + + if (Thread->Tcb.State < THREAD_STATE_MAX) + State = ThreadStateToString[Thread->Tcb.State]; + else + State = "Unknown"; + KdbpPrint("%s" + " TID: 0x%08x\n" + " State: %s (0x%x)\n" + " Priority: %d\n" + " Affinity: 0x%08x\n" + " Initial Stack: 0x%08x\n" + " Stack Limit: 0x%08x\n" + " Stack Base: 0x%08x\n" + " Kernel Stack: 0x%08x\n" + " Trap Frame: 0x%08x\n" + " NPX State: %s (0x%x)\n", + (Argc < 2) ? "Current Thread:\n" : "", + Thread->Cid.UniqueThread, + State, Thread->Tcb.State, + Thread->Tcb.Priority, + Thread->Tcb.Affinity, + Thread->Tcb.InitialStack, + Thread->Tcb.StackLimit, + Thread->Tcb.StackBase, + Thread->Tcb.KernelStack, + Thread->Tcb.TrapFrame, + NPX_STATE_TO_STRING(Thread->Tcb.NpxState), Thread->Tcb.NpxState); + + } + + return TRUE; +} + +/*!\brief Lists processes or switches to another process context. + */ +STATIC BOOLEAN +KdbpCmdProc(ULONG Argc, PCHAR Argv[]) +{ + PLIST_ENTRY Entry; + PEPROCESS Process; + PCHAR State, pend, str1, str2; + ULONG ul; + extern LIST_ENTRY PsActiveProcessHead; + + if (Argc >= 2 && _stricmp(Argv[1], "list") == 0) + { + Entry = PsActiveProcessHead.Flink; + if (Entry == &PsActiveProcessHead) + { + KdbpPrint("No processes in the system!\n"); + return TRUE; + } + + KdbpPrint(" PID State Filename\n"); + do + { + Process = CONTAINING_RECORD(Entry, EPROCESS, ProcessListEntry); + + if (Process == KdbCurrentProcess) + { + str1 = "\x1b[1m*"; + str2 = "\x1b[0m"; + } + else + { + str1 = " "; + str2 = ""; + } + + State = ((Process->Pcb.State == PROCESS_STATE_TERMINATED) ? "Terminated" : + ((Process->Pcb.State == PROCESS_STATE_ACTIVE) ? "Active" : "Unknown")); + + KdbpPrint(" %s0x%08x %-10s %s%s\n", + str1, + Process->UniqueProcessId, + State, + Process->ImageFileName, + str2); + + Entry = Entry->Flink; + } + while(Entry != &PsActiveProcessHead); + } + else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0) + { + if (Argc < 3) + { + KdbpPrint("process attach: process id argument required!\n"); + return TRUE; + } + + ul = strtoul(Argv[2], &pend, 0); + if (Argv[2] == pend) + { + KdbpPrint("process attach: '%s' is not a valid process id!\n", Argv[2]); + return TRUE; + } + if (!KdbpAttachToProcess((PVOID)ul)) + { + return TRUE; + } + KdbpPrint("Attached to process 0x%08x, thread 0x%08x.\n", (UINT)ul, + (UINT)KdbCurrentThread->Cid.UniqueThread); + } + else + { + Process = KdbCurrentProcess; + + if (Argc >= 2) + { + ul = strtoul(Argv[1], &pend, 0); + if (Argv[1] == pend) + { + KdbpPrint("proc: '%s' is not a valid process id!\n", Argv[1]); + return TRUE; + } + if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process))) + { + KdbpPrint("proc: Invalid process id!\n"); + return TRUE; + } + } + + State = ((Process->Pcb.State == PROCESS_STATE_TERMINATED) ? "Terminated" : + ((Process->Pcb.State == PROCESS_STATE_ACTIVE) ? "Active" : "Unknown")); + KdbpPrint("%s" + " PID: 0x%08x\n" + " State: %s (0x%x)\n" + " Image Filename: %s\n", + (Argc < 2) ? "Current process:\n" : "", + Process->UniqueProcessId, + State, Process->Pcb.State, + Process->ImageFileName); + } + + return TRUE; +} + +/*!\brief Lists loaded modules or the one containing the specified address. + */ +STATIC BOOLEAN +KdbpCmdMod(ULONG Argc, PCHAR Argv[]) +{ + ULONGLONG Result = 0; + ULONG_PTR Address; + KDB_MODULE_INFO Info; + BOOLEAN DisplayOnlyOneModule = FALSE; + INT i = 0; + + if (Argc >= 2) + { + /* Put the arguments back together */ + Argc--; + while (--Argc >= 1) + Argv[Argc][strlen(Argv[Argc])] = ' '; + + /* Evaluate the expression */ + if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result)) + { + return TRUE; + } + if (Result > (ULONGLONG)(~((ULONG_PTR)0))) + KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0]); + Address = (ULONG_PTR)Result; + + if (!KdbpSymFindModuleByAddress((PVOID)Address, &Info)) + { + KdbpPrint("No module containing address 0x%x found!\n", Address); + return TRUE; + } + DisplayOnlyOneModule = TRUE; + } + else + { + if (!KdbpSymFindModuleByIndex(0, &Info)) + { + KdbpPrint("No modules.\n"); + return TRUE; + } + i = 1; + } + + KdbpPrint(" Base Size Name\n"); + for (;;) + { + KdbpPrint(" %08x %08x %ws\n", Info.Base, Info.Size, Info.Name); + + if ((!DisplayOnlyOneModule && !KdbpSymFindModuleByIndex(i++, &Info)) || + DisplayOnlyOneModule) + { + break; + } + } + + return TRUE; +} + +/*!\brief Displays GDT, LDT or IDTd. + */ +STATIC BOOLEAN +KdbpCmdGdtLdtIdt(ULONG Argc, PCHAR Argv[]) +{ + struct __attribute__((packed)) { + USHORT Limit; + ULONG Base; + } Reg; + ULONG SegDesc[2]; + ULONG SegBase; + ULONG SegLimit; + PCHAR SegType; + USHORT SegSel; + UCHAR Type, Dpl; + INT i; + ULONG ul; + + if (Argv[0][0] == 'i') + { + /* Read IDTR */ + asm volatile("sidt %0" : : "m"(Reg)); + + if (Reg.Limit < 7) + { + KdbpPrint("Interrupt descriptor table is empty.\n"); + return TRUE; + } + KdbpPrint("IDT Base: 0x%08x Limit: 0x%04x\n", Reg.Base, Reg.Limit); + KdbpPrint(" Idx Type Seg. Sel. Offset DPL\n"); + for (i = 0; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8) + { + if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)(Reg.Base + i), sizeof(SegDesc)))) + { + KdbpPrint("Couldn't access memory at 0x%08x!\n", Reg.Base + i); + return TRUE; + } + + Dpl = ((SegDesc[1] >> 13) & 3); + if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */ + SegType = "TASKGATE"; + else if ((SegDesc[1] & 0x1fe0) == 0x0e00) /* 32 bit Interrupt gate */ + SegType = "INTGATE32"; + else if ((SegDesc[1] & 0x1fe0) == 0x0600) /* 16 bit Interrupt gate */ + SegType = "INTGATE16"; + else if ((SegDesc[1] & 0x1fe0) == 0x0f00) /* 32 bit Trap gate */ + SegType = "TRAPGATE32"; + else if ((SegDesc[1] & 0x1fe0) == 0x0700) /* 16 bit Trap gate */ + SegType = "TRAPGATE16"; + else + SegType = "UNKNOWN"; + + if ((SegDesc[1] & (1 << 15)) == 0) /* not present */ + { + KdbpPrint(" %03d %-10s [NP] [NP] %02d\n", + i / 8, SegType, Dpl); + } + else if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */ + { + SegSel = SegDesc[0] >> 16; + KdbpPrint(" %03d %-10s 0x%04x %02d\n", + i / 8, SegType, SegSel, Dpl); + } + else + { + SegSel = SegDesc[0] >> 16; + SegBase = (SegDesc[1] & 0xffff0000) | (SegDesc[0] & 0x0000ffff); + KdbpPrint(" %03d %-10s 0x%04x 0x%08x %02d\n", + i / 8, SegType, SegSel, SegBase, Dpl); + } + } + } + else + { + ul = 0; + if (Argv[0][0] == 'g') + { + /* Read GDTR */ + asm volatile("sgdt %0" : : "m"(Reg)); + i = 8; + } + else + { + ASSERT(Argv[0][0] == 'l'); + /* Read LDTR */ + asm volatile("sldt %0" : : "m"(Reg)); + i = 0; + ul = 1 << 2; + } + + if (Reg.Limit < 7) + { + KdbpPrint("%s descriptor table is empty.\n", + Argv[0][0] == 'g' ? "Global" : "Local"); + return TRUE; + } + KdbpPrint("%cDT Base: 0x%08x Limit: 0x%04x\n", + Argv[0][0] == 'g' ? 'G' : 'L', Reg.Base, Reg.Limit); + KdbpPrint(" Idx Sel. Type Base Limit DPL Attribs\n"); + for ( ; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8) + { + if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)(Reg.Base + i), sizeof(SegDesc)))) + { + KdbpPrint("Couldn't access memory at 0x%08x!\n", Reg.Base + i); + return TRUE; + } + Dpl = ((SegDesc[1] >> 13) & 3); + Type = ((SegDesc[1] >> 8) & 0xf); + + SegBase = SegDesc[0] >> 16; + SegBase |= (SegDesc[1] & 0xff) << 16; + SegBase |= SegDesc[1] & 0xff000000; + SegLimit = SegDesc[0] & 0x0000ffff; + SegLimit |= (SegDesc[1] >> 16) & 0xf; + if ((SegDesc[1] & (1 << 23)) != 0) + { + SegLimit *= 4096; + SegLimit += 4095; + } + else + { + SegLimit++; + } + + if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */ + { + switch (Type) + { + case 1: SegType = "TSS16(Avl)"; break; + case 2: SegType = "LDT"; break; + case 3: SegType = "TSS16(Busy)"; break; + case 4: SegType = "CALLGATE16"; break; + case 5: SegType = "TASKGATE"; break; + case 6: SegType = "INTGATE16"; break; + case 7: SegType = "TRAPGATE16"; break; + case 9: SegType = "TSS32(Avl)"; break; + case 11: SegType = "TSS32(Busy)"; break; + case 12: SegType = "CALLGATE32"; break; + case 14: SegType = "INTGATE32"; break; + case 15: SegType = "INTGATE32"; break; + default: SegType = "UNKNOWN"; break; + } + if (!(Type >= 1 && Type <= 3) && + Type != 9 && Type != 11) + { + SegBase = 0; + SegLimit = 0; + } + } + else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */ + { + if ((SegDesc[1] & (1 << 22)) != 0) + SegType = "DATA32"; + else + SegType = "DATA16"; + + } + else /* Code segment */ + { + if ((SegDesc[1] & (1 << 22)) != 0) + SegType = "CODE32"; + else + SegType = "CODE16"; + } + + if ((SegDesc[1] & (1 << 15)) == 0) /* not present */ + { + KdbpPrint(" %03d 0x%04x %-11s [NP] [NP] %02d NP\n", + i / 8, i | Dpl | ul, SegType, Dpl); + } + else + { + KdbpPrint(" %03d 0x%04x %-11s 0x%08x 0x%08x %02d ", + i / 8, i | Dpl | ul, SegType, SegBase, SegLimit, Dpl); + if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */ + { + /* FIXME: Display system segment */ + } + else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */ + { + if ((SegDesc[1] & (1 << 10)) != 0) /* Expand-down */ + KdbpPrint(" E"); + KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/W" : " R"); + if ((SegDesc[1] & (1 << 8)) != 0) + KdbpPrint(" A"); + } + else /* Code segment */ + { + if ((SegDesc[1] & (1 << 10)) != 0) /* Conforming */ + KdbpPrint(" C"); + KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/X" : " X"); + if ((SegDesc[1] & (1 << 8)) != 0) + KdbpPrint(" A"); + } + if ((SegDesc[1] & (1 << 20)) != 0) + KdbpPrint(" AVL"); + KdbpPrint("\n"); + } + } + } + + return TRUE; +} + +/*!\brief Displays the KPCR + */ +STATIC BOOLEAN +KdbpCmdPcr(ULONG Argc, PCHAR Argv[]) +{ + PKPCR Pcr = KeGetCurrentKPCR(); + + KdbpPrint("Current PCR is at 0x%08x.\n", (INT)Pcr); + KdbpPrint(" Tib.ExceptionList: 0x%08x\n" + " Tib.StackBase: 0x%08x\n" + " Tib.StackLimit: 0x%08x\n" + " Tib.SubSystemTib: 0x%08x\n" + " Tib.FiberData/Version: 0x%08x\n" + " Tib.ArbitraryUserPointer: 0x%08x\n" + " Tib.Self: 0x%08x\n" + " Self: 0x%08x\n" + " PCRCB: 0x%08x\n" + " Irql: 0x%02x\n" + " IRR: 0x%08x\n" + " IrrActive: 0x%08x\n" + " IDR: 0x%08x\n" + " KdVersionBlock: 0x%08x\n" + " IDT: 0x%08x\n" + " GDT: 0x%08x\n" + " TSS: 0x%08x\n" + " MajorVersion: 0x%04x\n" + " MinorVersion: 0x%04x\n" + " SetMember: 0x%08x\n" + " StallScaleFactor: 0x%08x\n" + " DebugActive: 0x%02x\n" + " ProcessorNumber: 0x%02x\n" + " L2CacheAssociativity: 0x%02x\n" + " VdmAlert: 0x%08x\n" + " L2CacheSize: 0x%08x\n" + " InterruptMode: 0x%08x\n", + Pcr->Tib.ExceptionList, Pcr->Tib.StackBase, Pcr->Tib.StackLimit, + Pcr->Tib.SubSystemTib, Pcr->Tib.FiberData, Pcr->Tib.ArbitraryUserPointer, + Pcr->Tib.Self, Pcr->Self, Pcr->Prcb, Pcr->Irql, Pcr->IRR, Pcr->IrrActive, + Pcr->IDR, Pcr->KdVersionBlock, Pcr->IDT, Pcr->GDT, Pcr->TSS, + Pcr->MajorVersion, Pcr->MinorVersion, Pcr->SetMember, Pcr->StallScaleFactor, + Pcr->DebugActive, Pcr->ProcessorNumber, Pcr->L2CacheAssociativity, + Pcr->VdmAlert, Pcr->L2CacheSize, Pcr->InterruptMode); + + return TRUE; +} + +/*!\brief Displays the TSS + */ +STATIC BOOLEAN +KdbpCmdTss(ULONG Argc, PCHAR Argv[]) +{ + KTSS *Tss = KeGetCurrentKPCR()->TSS; + + KdbpPrint("Current TSS is at 0x%08x.\n", (INT)Tss); + KdbpPrint(" PreviousTask: 0x%08x\n" + " Ss0:Esp0: 0x%04x:0x%08x\n" + " Ss1:Esp1: 0x%04x:0x%08x\n" + " Ss2:Esp2: 0x%04x:0x%08x\n" + " Cr3: 0x%08x\n" + " Eip: 0x%08x\n" + " Eflags: 0x%08x\n" + " Eax: 0x%08x\n" + " Ecx: 0x%08x\n" + " Edx: 0x%08x\n" + " Ebx: 0x%08x\n" + " Esp: 0x%08x\n" + " Ebp: 0x%08x\n" + " Esi: 0x%08x\n" + " Edi: 0x%08x\n" + " Es: 0x%04x\n" + " Cs: 0x%04x\n" + " Ss: 0x%04x\n" + " Ds: 0x%04x\n" + " Fs: 0x%04x\n" + " Gs: 0x%04x\n" + " Ldt: 0x%04x\n" + " Trap: 0x%04x\n" + " IoMapBase: 0x%04x\n", + Tss->PreviousTask, Tss->Ss0, Tss->Esp0, Tss->Ss1, Tss->Esp1, + Tss->Ss2, Tss->Esp2, Tss->Cr3, Tss->Eip, Tss->Eflags, Tss->Eax, + Tss->Ecx, Tss->Edx, Tss->Ebx, Tss->Esp, Tss->Ebp, Tss->Esi, + Tss->Edi, Tss->Es, Tss->Cs, Tss->Ss, Tss->Ds, Tss->Fs, Tss->Gs, + Tss->Ldt, Tss->Trap, Tss->IoMapBase); + return TRUE; +} + +/*!\brief Bugchecks the system. + */ +STATIC BOOLEAN +KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[]) +{ + KEBUGCHECK(0xDEADDEAD); + return TRUE; +} + +/*!\brief Sets or displays a config variables value. + */ +STATIC BOOLEAN +KdbpCmdSet(ULONG Argc, PCHAR Argv[]) +{ + LONG l; + BOOLEAN First; + PCHAR pend = 0; + KDB_ENTER_CONDITION ConditionFirst = KdbDoNotEnter; + KDB_ENTER_CONDITION ConditionLast = KdbDoNotEnter; + STATIC CONST PCHAR ExceptionNames[21] = + { "ZERODEVIDE", "DEBUGTRAP", "NMI", "INT3", "OVERFLOW", "BOUND", "INVALIDOP", + "NOMATHCOP", "DOUBLEFAULT", "RESERVED(9)", "INVALIDTSS", "SEGMENTNOTPRESENT", + "STACKFAULT", "GPF", "PAGEFAULT", "RESERVED(15)", "MATHFAULT", "ALIGNMENTCHECK", + "MACHINECHECK", "SIMDFAULT", "OTHERS" }; + + if (Argc == 1) + { + KdbpPrint("Available settings:\n"); + KdbpPrint(" syntax [intel|at&t]\n"); + KdbpPrint(" condition [exception|*] [first|last] [never|always|kmode|umode]\n"); + } + else if (strcmp(Argv[1], "syntax") == 0) + { + if (Argc == 2) + KdbpPrint("syntax = %s\n", KdbUseIntelSyntax ? "intel" : "at&t"); + else if (Argc >= 3) + { + if (_stricmp(Argv[2], "intel") == 0) + KdbUseIntelSyntax = TRUE; + else if (_stricmp(Argv[2], "at&t") == 0) + KdbUseIntelSyntax = FALSE; + else + KdbpPrint("Unknown syntax '%s'.\n", Argv[2]); + } + } + else if (strcmp(Argv[1], "condition") == 0) + { + if (Argc == 2) + { + KdbpPrint("Conditions: (First) (Last)\n"); + for (l = 0; l < RTL_NUMBER_OF(ExceptionNames) - 1; l++) + { + if (ExceptionNames[l] == NULL) + continue; + if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst)) + ASSERT(0); + if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast)) + ASSERT(0); + KdbpPrint(" #%02d %-20s %-8s %-8s\n", l, ExceptionNames[l], + KDB_ENTER_CONDITION_TO_STRING(ConditionFirst), + KDB_ENTER_CONDITION_TO_STRING(ConditionLast)); + } + ASSERT(l == (RTL_NUMBER_OF(ExceptionNames) - 1)); + KdbpPrint(" %-20s %-8s %-8s\n", ExceptionNames[l], + KDB_ENTER_CONDITION_TO_STRING(ConditionFirst), + KDB_ENTER_CONDITION_TO_STRING(ConditionLast)); + } + else + { + if (Argc >= 5 && strcmp(Argv[2], "*") == 0) /* Allow * only when setting condition */ + l = -1; + else + { + l = (LONG)strtoul(Argv[2], &pend, 0); + if (Argv[2] == pend) + { + for (l = 0; l < RTL_NUMBER_OF(ExceptionNames); l++) + { + if (ExceptionNames[l] == NULL) + continue; + if (_stricmp(ExceptionNames[l], Argv[2]) == 0) + break; + } + } + if (l >= RTL_NUMBER_OF(ExceptionNames)) + { + KdbpPrint("Unknown exception '%s'.\n", Argv[2]); + return TRUE; + } + } + if (Argc > 4) + { + if (_stricmp(Argv[3], "first") == 0) + First = TRUE; + else if (_stricmp(Argv[3], "last") == 0) + First = FALSE; + else + { + KdbpPrint("set condition: second argument must be 'first' or 'last'\n"); + return TRUE; + } + if (_stricmp(Argv[4], "never") == 0) + ConditionFirst = KdbDoNotEnter; + else if (_stricmp(Argv[4], "always") == 0) + ConditionFirst = KdbEnterAlways; + else if (_stricmp(Argv[4], "umode") == 0) + ConditionFirst = KdbEnterFromUmode; + else if (_stricmp(Argv[4], "kmode") == 0) + ConditionFirst = KdbEnterFromKmode; + else + { + KdbpPrint("set condition: third argument must be 'never', 'always', 'umode' or 'kmode'\n"); + return TRUE; + } + if (!KdbpSetEnterCondition(l, First, ConditionFirst)) + { + if (l >= 0) + KdbpPrint("Couldn't change condition for exception #%02d\n", l); + else + KdbpPrint("Couldn't change condition for all exceptions\n", l); + } + } + else /* Argc >= 3 */ + { + if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst)) + ASSERT(0); + if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast)) + ASSERT(0); + if (l < (RTL_NUMBER_OF(ExceptionNames) - 1)) + { + KdbpPrint("Condition for exception #%02d (%s): FirstChance %s LastChance %s\n", + l, ExceptionNames[l], + KDB_ENTER_CONDITION_TO_STRING(ConditionFirst), + KDB_ENTER_CONDITION_TO_STRING(ConditionLast)); + } + else + { + KdbpPrint("Condition for all other exceptions: FirstChance %s LastChance %s\n", + KDB_ENTER_CONDITION_TO_STRING(ConditionFirst), + KDB_ENTER_CONDITION_TO_STRING(ConditionLast)); + } + } + } + } + else + KdbpPrint("Unknown setting '%s'.\n", Argv[1]); + + return TRUE; +} + +/*!\brief Displays help screen. + */ +STATIC BOOLEAN +KdbpCmdHelp(ULONG Argc, PCHAR Argv[]) +{ + ULONG i; + + KdbpPrint("Kernel debugger commands:\n"); + for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++) + { + if (KdbDebuggerCommands[i].Syntax == NULL) /* Command group */ + { + if (i > 0) + KdbpPrint("\n"); + KdbpPrint("\x1b[7m* %s:\x1b[0m\n", KdbDebuggerCommands[i].Help); + continue; + } + + KdbpPrint(" %-20s - %s\n", + KdbDebuggerCommands[i].Syntax, + KdbDebuggerCommands[i].Help); + } + + return TRUE; +} + +/*!\brief Prints the given string with printf-like formatting. + * + * \param Format Format of the string/arguments. + * \param ... Variable number of arguments matching the format specified in \a Format. + * + * \note Doesn't correctly handle \\t and terminal escape sequences when calculating the + * number of lines required to print a single line from the Buffer in the terminal. + */ +VOID +KdbpPrint( + IN PCHAR Format, + IN ... OPTIONAL) +{ + STATIC CHAR Buffer[4096]; + STATIC BOOLEAN TerminalInitialized = FALSE; + STATIC BOOLEAN TerminalConnected = FALSE; + STATIC BOOLEAN TerminalReportsSize = TRUE; + CHAR c = '\0'; + PCHAR p, p2; + INT Length; + INT i, j; + INT RowsPrintedByTerminal; + ULONG ScanCode; + va_list ap; + + /* Check if the user has aborted output of the current command */ + if (KdbOutputAborted) + return; + + /* Initialize the terminal */ + if (!TerminalInitialized) + { + DbgPrint("\x1b[7h"); /* Enable linewrap */ + + /* Query terminal type */ + /*DbgPrint("\x1b[Z");*/ + DbgPrint("\x05"); + + TerminalInitialized = TRUE; + Length = 0; + for (;;) + { + c = KdbpTryGetCharSerial(5000); + if (c == -1) + break; + Buffer[Length++] = c; + if (Length >= (sizeof (Buffer) - 1)) + break; + } + Buffer[Length] = '\0'; + if (Length > 0) + TerminalConnected = TRUE; + } + + /* Get number of rows and columns in terminal */ + if ((KdbNumberOfRowsTerminal < 0) || (KdbNumberOfColsTerminal < 0) || + (KdbNumberOfRowsPrinted) == 0) /* Refresh terminal size each time when number of rows printed is 0 */ + { + if ((KdDebugState & KD_DEBUG_KDSERIAL) && TerminalReportsSize) + { + /* Try to query number of rows from terminal. A reply looks like "\x1b[8;24;80t" */ + TerminalReportsSize = FALSE; + DbgPrint("\x1b[18t"); + c = KdbpTryGetCharSerial(5000); + if (c == KEY_ESC) + { + c = KdbpTryGetCharSerial(5000); + if (c == '[') + { + Length = 0; + for (;;) + { + c = KdbpTryGetCharSerial(5000); + if (c == -1) + break; + Buffer[Length++] = c; + if (isalpha(c) || Length >= (sizeof (Buffer) - 1)) + break; + } + Buffer[Length] = '\0'; + if (Buffer[0] == '8' && Buffer[1] == ';') + { + for (i = 2; (i < Length) && (Buffer[i] != ';'); i++); + if (Buffer[i] == ';') + { + Buffer[i++] = '\0'; + /* Number of rows is now at Buffer + 2 and number of cols at Buffer + i */ + KdbNumberOfRowsTerminal = strtoul(Buffer + 2, NULL, 0); + KdbNumberOfColsTerminal = strtoul(Buffer + i, NULL, 0); + TerminalReportsSize = TRUE; + } + } + } + } + } + + if (KdbNumberOfRowsTerminal <= 0) + { + /* Set number of rows to the default. */ + KdbNumberOfRowsTerminal = 24; + } + else if (KdbNumberOfColsTerminal <= 0) + { + /* Set number of cols to the default. */ + KdbNumberOfColsTerminal = 80; + } + } + + /* Get the string */ + va_start(ap, Format); + Length = _vsnprintf(Buffer, sizeof (Buffer) - 1, Format, ap); + Buffer[Length] = '\0'; + va_end(ap); + + p = Buffer; + while (p[0] != '\0') + { + i = strcspn(p, "\n"); + + /* Calculate the number of lines which will be printed in the terminal + * when outputting the current line + */ + if (i > 0) + RowsPrintedByTerminal = (i + KdbNumberOfColsPrinted - 1) / KdbNumberOfColsTerminal; + else + RowsPrintedByTerminal = 0; + if (p[i] == '\n') + RowsPrintedByTerminal++; + + /*DbgPrint("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);*/ + + /* Display a prompt if we printed one screen full of text */ + if ((KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal) + { + if (KdbNumberOfColsPrinted > 0) + DbgPrint("\n"); + DbgPrint("--- Press q to abort, any other key to continue ---"); + if (KdDebugState & KD_DEBUG_KDSERIAL) + c = KdbpGetCharSerial(); + else + c = KdbpGetCharKeyboard(&ScanCode); + if (c == '\r') + { + /* Try to read '\n' which might follow '\r' - if \n is not received here + * it will be interpreted as "return" when the next command should be read. + */ + if (KdDebugState & KD_DEBUG_KDSERIAL) + c = KdbpTryGetCharSerial(5); + else + c = KdbpTryGetCharKeyboard(&ScanCode, 5); + } + DbgPrint("\n"); + if (c == 'q') + { + KdbOutputAborted = TRUE; + return; + } + KdbNumberOfRowsPrinted = 0; + KdbNumberOfColsPrinted = 0; + } + + /* Insert a NUL after the line and print only the current line. */ + if (p[i] == '\n' && p[i + 1] != '\0') + { + c = p[i + 1]; + p[i + 1] = '\0'; + } + else + { + c = '\0'; + } + + /* Remove escape sequences from the line if there's no terminal connected */ + if (!TerminalConnected) + { + while ((p2 = strrchr(p, '\x1b')) != NULL) /* Look for escape character */ + { + if (p2[1] == '[') + { + j = 2; + while (!isalpha(p2[j++])); + strcpy(p2, p2 + j); + } + } + } + + DbgPrint("%s", p); + + if (c != '\0') + p[i + 1] = c; + + /* Set p to the start of the next line and + * remember the number of rows/cols printed + */ + p += i; + if (p[0] == '\n') + { + p++; + KdbNumberOfColsPrinted = 0; + } + else + { + ASSERT(p[0] == '\0'); + KdbNumberOfColsPrinted += i; + } + KdbNumberOfRowsPrinted += RowsPrintedByTerminal; + } +} + +/*!\brief Appends a command to the command history + * + * \param Command Pointer to the command to append to the history. + */ +STATIC VOID +KdbpCommandHistoryAppend( + IN PCHAR Command) +{ + LONG Length1 = strlen(Command) + 1; + LONG Length2 = 0; + INT i; + PCHAR Buffer; + + ASSERT(Length1 <= RTL_NUMBER_OF(KdbCommandHistoryBuffer)); + + if (Length1 <= 1 || + (KdbCommandHistory[KdbCommandHistoryIndex] != NULL && + strcmp(KdbCommandHistory[KdbCommandHistoryIndex], Command) == 0)) + { + return; + } + + /* Calculate Length1 and Length2 */ + Buffer = KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex; + KdbCommandHistoryBufferIndex += Length1; + if (KdbCommandHistoryBufferIndex >= RTL_NUMBER_OF(KdbCommandHistoryBuffer)) + { + KdbCommandHistoryBufferIndex -= RTL_NUMBER_OF(KdbCommandHistoryBuffer); + Length2 = KdbCommandHistoryBufferIndex; + Length1 -= Length2; + } + + /* Remove previous commands until there is enough space to append the new command */ + for (i = KdbCommandHistoryIndex; KdbCommandHistory[i] != NULL;) + { + if ((Length2 > 0 && + (KdbCommandHistory[i] >= Buffer || + KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))) || + (Length2 <= 0 && + (KdbCommandHistory[i] >= Buffer && + KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex)))) + { + KdbCommandHistory[i] = NULL; + } + i--; + if (i < 0) + i = RTL_NUMBER_OF(KdbCommandHistory) - 1; + if (i == KdbCommandHistoryIndex) + break; + } + + /* Make sure the new command history entry is free */ + KdbCommandHistoryIndex++; + KdbCommandHistoryIndex %= RTL_NUMBER_OF(KdbCommandHistory); + if (KdbCommandHistory[KdbCommandHistoryIndex] != NULL) + { + KdbCommandHistory[KdbCommandHistoryIndex] = NULL; + } + + /* Append command */ + KdbCommandHistory[KdbCommandHistoryIndex] = Buffer; + ASSERT((KdbCommandHistory[KdbCommandHistoryIndex] + Length1) <= KdbCommandHistoryBuffer + RTL_NUMBER_OF(KdbCommandHistoryBuffer)); + memcpy(KdbCommandHistory[KdbCommandHistoryIndex], Command, Length1); + if (Length2 > 0) + { + memcpy(KdbCommandHistoryBuffer, Command + Length1, Length2); + } +} + +/*!\brief Reads a line of user-input. + * + * \param Buffer Buffer to store the input into. Trailing newlines are removed. + * \param Size Size of \a Buffer. + * + * \note Accepts only \n newlines, \r is ignored. + */ +STATIC VOID +KdbpReadCommand( + OUT PCHAR Buffer, + IN ULONG Size) +{ + CHAR Key; + PCHAR Orig = Buffer; + ULONG ScanCode = 0; + BOOLEAN EchoOn; + STATIC CHAR LastCommand[1024] = ""; + STATIC CHAR NextKey = '\0'; + INT CmdHistIndex = -1; + INT i; + + EchoOn = !((KdDebugState & KD_DEBUG_KDNOECHO) != 0); + + for (;;) + { + if (KdDebugState & KD_DEBUG_KDSERIAL) + { + Key = (NextKey == '\0') ? KdbpGetCharSerial() : NextKey; + ScanCode = 0; + if (Key == KEY_ESC) /* ESC */ + { + Key = KdbpGetCharSerial(); + if (Key == '[') + { + Key = KdbpGetCharSerial(); + switch (Key) + { + case 'A': + ScanCode = KEY_SCAN_UP; + break; + case 'B': + ScanCode = KEY_SCAN_DOWN; + break; + case 'C': + break; + case 'D': + break; + } + } + } + } + else + { + ScanCode = 0; + Key = (NextKey == '\0') ? KdbpGetCharKeyboard(&ScanCode) : NextKey; + } + + if ((Buffer - Orig) >= (Size - 1)) + { + /* Buffer is full, accept only newlines */ + if (Key != '\n') + continue; + } + + if (Key == '\r') + { + /* Read the next char - this is to throw away a \n which most clients should + * send after \r. + */ + if (KdDebugState & KD_DEBUG_KDSERIAL) + NextKey = KdbpTryGetCharSerial(5); + else + NextKey = KdbpTryGetCharKeyboard(&ScanCode, 5); + if (NextKey == '\n') + NextKey = '\0'; + DbgPrint("\n"); + /* + * Repeat the last command if the user presses enter. Reduces the + * risk of RSI when single-stepping. + */ + if (Buffer == Orig) + { + strncpy(Buffer, LastCommand, Size); + Buffer[Size - 1] = '\0'; + } + else + { + *Buffer = '\0'; + strncpy(LastCommand, Orig, sizeof (LastCommand)); + LastCommand[sizeof (LastCommand) - 1] = '\0'; + } + return; + } + else if (Key == KEY_BS || Key == KEY_DEL) + { + if (Buffer > Orig) + { + Buffer--; + *Buffer = 0; + if (EchoOn) + DbgPrint("%c %c", KEY_BS, KEY_BS); + else + DbgPrint(" %c", KEY_BS); + } + } + else if (ScanCode == KEY_SCAN_UP) + { + BOOLEAN Print = TRUE; + if (CmdHistIndex < 0) + CmdHistIndex = KdbCommandHistoryIndex; + else + { + i = CmdHistIndex - 1; + if (i < 0) + CmdHistIndex = RTL_NUMBER_OF(KdbCommandHistory) - 1; + if (KdbCommandHistory[i] != NULL && i != KdbCommandHistoryIndex) + CmdHistIndex = i; + else + Print = FALSE; + } + if (Print && KdbCommandHistory[CmdHistIndex] != NULL) + { + while (Buffer > Orig) + { + Buffer--; + *Buffer = 0; + if (EchoOn) + DbgPrint("%c %c", KEY_BS, KEY_BS); + else + DbgPrint(" %c", KEY_BS); + } + i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1); + memcpy(Orig, KdbCommandHistory[CmdHistIndex], i); + Orig[i] = '\0'; + Buffer = Orig + i; + DbgPrint("%s", Orig); + } + } + else if (ScanCode == KEY_SCAN_DOWN) + { + if (CmdHistIndex > 0 && CmdHistIndex != KdbCommandHistoryIndex) + { + i = CmdHistIndex + 1; + if (i >= RTL_NUMBER_OF(KdbCommandHistory)) + i = 0; + if (KdbCommandHistory[i] != NULL) + { + CmdHistIndex = i; + while (Buffer > Orig) + { + Buffer--; + *Buffer = 0; + if (EchoOn) + DbgPrint("%c %c", KEY_BS, KEY_BS); + else + DbgPrint(" %c", KEY_BS); + } + i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1); + memcpy(Orig, KdbCommandHistory[CmdHistIndex], i); + Orig[i] = '\0'; + Buffer = Orig + i; + DbgPrint("%s", Orig); + } + } + } + else + { + if (EchoOn) + DbgPrint("%c", Key); + + *Buffer = Key; + Buffer++; + } + } +} + +/*!\brief Parses command line and executes command if found + * + * \param Command Command line to parse and execute if possible. + * + * \retval TRUE Don't continue execution. + * \retval FALSE Continue execution (leave KDB) + */ +STATIC BOOL +KdbpDoCommand( + IN PCHAR Command) +{ + ULONG i; + PCHAR p; + ULONG Argc; + STATIC PCH Argv[256]; + STATIC CHAR OrigCommand[1024]; + + strncpy(OrigCommand, Command, sizeof(OrigCommand) - 1); + OrigCommand[sizeof(OrigCommand) - 1] = '\0'; + + Argc = 0; + p = Command; + for (;;) + { + while (*p == '\t' || *p == ' ') + p++; + if (*p == '\0') + break; + + i = strcspn(p, "\t "); + Argv[Argc++] = p; + p += i; + if (*p == '\0') + break; + *p = '\0'; + p++; + } + if (Argc < 1) + return TRUE; + + for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++) + { + if (KdbDebuggerCommands[i].Name == NULL) + continue; + + if (strcmp(KdbDebuggerCommands[i].Name, Argv[0]) == 0) + { + return KdbDebuggerCommands[i].Fn(Argc, Argv); + } + } + + KdbpPrint("Command '%s' is unknown.\n", OrigCommand); + return TRUE; +} + +/*!\brief KDB Main Loop. + * + * \param EnteredOnSingleStep TRUE if KDB was entered on single step. + */ +VOID +KdbpCliMainLoop( + IN BOOLEAN EnteredOnSingleStep) +{ + STATIC CHAR Command[1024]; + BOOLEAN Continue; + + if (EnteredOnSingleStep) + { + if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip)) + { + DbgPrint("<%x>", KdbCurrentTrapFrame->Tf.Eip); + } + DbgPrint(": "); + if (KdbpDisassemble(KdbCurrentTrapFrame->Tf.Eip, KdbUseIntelSyntax) < 0) + { + DbgPrint(""); + } + DbgPrint("\n"); + } + + /* Flush the input buffer */ + if (KdDebugState & KD_DEBUG_KDSERIAL) + { + while (KdbpTryGetCharSerial(1) != -1); + } + else + { + ULONG ScanCode; + while (KdbpTryGetCharKeyboard(&ScanCode, 1) != -1); + } + + /* Main loop */ + do + { + /* Print the prompt */ + DbgPrint("kdb:> "); + + /* Read a command and remember it */ + KdbpReadCommand(Command, sizeof (Command)); + KdbpCommandHistoryAppend(Command); + + /* Reset the number of rows/cols printed and output aborted state */ + KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0; + KdbOutputAborted = FALSE; + + /* Call the command */ + Continue = KdbpDoCommand(Command); + } while (Continue); +} + +/*!\brief Called when a module is loaded. + * + * \param Name Filename of the module which was loaded. + */ +VOID +KdbpCliModuleLoaded(IN PUNICODE_STRING Name) +{ + return; + + DbgPrint("Module %wZ loaded.\n", Name); + DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C); +} + +/*!\brief This function is called by KdbEnterDebuggerException... + * + * Used to interpret the init file in a context with a trapframe setup + * (KdbpCliInit call KdbEnter which will call KdbEnterDebuggerException which will + * call this function if KdbInitFileBuffer is not NULL. + */ +VOID +KdbpCliInterpretInitFile() +{ + PCHAR p1, p2; + INT i; + CHAR c; + + /* Execute the commands in the init file */ + DbgPrint("KDB: Executing KDBinit file...\n"); + p1 = KdbInitFileBuffer; + while (p1[0] != '\0') + { + i = strcspn(p1, "\r\n"); + if (i > 0) + { + c = p1[i]; + p1[i] = '\0'; + + /* Look for "break" command and comments */ + p2 = p1; + while (isspace(p2[0])) + p2++; + if (strncmp(p2, "break", sizeof("break")-1) == 0 && + (p2[sizeof("break")-1] == '\0' || isspace(p2[sizeof("break")-1]))) + { + /* break into the debugger */ + KdbpCliMainLoop(FALSE); + } + else if (p2[0] != '#' && p2[0] != '\0') /* Ignore empty lines and comments */ + { + KdbpDoCommand(p1); + } + + p1[i] = c; + } + p1 += i; + while (p1[0] == '\r' || p1[0] == '\n') + p1++; + } + DbgPrint("KDB: KDBinit executed\n"); +} + +/*!\brief Called when KDB is initialized + * + * Reads the KDBinit file from the SystemRoot\system32\drivers\etc directory and executes it. + */ +VOID +KdbpCliInit() +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING FileName; + IO_STATUS_BLOCK Iosb; + FILE_STANDARD_INFORMATION FileStdInfo; + HANDLE hFile = NULL; + INT FileSize; + PCHAR FileBuffer; + ULONG OldEflags; + + /* Initialize the object attributes */ + RtlInitUnicodeString(&FileName, L"\\SystemRoot\\system32\\drivers\\etc\\KDBinit"); + InitializeObjectAttributes(&ObjectAttributes, &FileName, 0, NULL, NULL); + + /* Open the file */ + Status = ZwOpenFile(&hFile, FILE_READ_DATA, &ObjectAttributes, &Iosb, 0, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | + FILE_NO_INTERMEDIATE_BUFFERING); + if (!NT_SUCCESS(Status)) + { + DPRINT("Could not open \\SystemRoot\\system32\\drivers\\etc\\KDBinit (Status 0x%x)", Status); + return; + } + + /* Get the size of the file */ + Status = ZwQueryInformationFile(hFile, &Iosb, &FileStdInfo, sizeof (FileStdInfo), + FileStandardInformation); + if (!NT_SUCCESS(Status)) + { + ZwClose(hFile); + DPRINT("Could not query size of \\SystemRoot\\system32\\drivers\\etc\\KDBinit (Status 0x%x)", Status); + return; + } + FileSize = FileStdInfo.EndOfFile.u.LowPart; + + /* Allocate memory for the file */ + FileBuffer = ExAllocatePool(PagedPool, FileSize + 1); /* add 1 byte for terminating '\0' */ + if (FileBuffer == NULL) + { + ZwClose(hFile); + DPRINT("Could not allocate %d bytes for KDBinit file\n", FileSize); + return; + } + + /* Load file into memory */ + Status = ZwReadFile(hFile, 0, 0, 0, &Iosb, FileBuffer, FileSize, 0, 0); + ZwClose(hFile); + if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE) + { + ExFreePool(FileBuffer); + DPRINT("Could not read KDBinit file into memory (Status 0x%lx)\n", Status); + return; + } + FileSize = min(FileSize, Iosb.Information); + FileBuffer[FileSize] = '\0'; + + /* Enter critical section */ + Ke386SaveFlags(OldEflags); + Ke386DisableInterrupts(); + + /* Interpret the init file... */ + KdbInitFileBuffer = FileBuffer; + KdbEnter(); + KdbInitFileBuffer = NULL; + + /* Leave critical section */ + Ke386RestoreFlags(OldEflags); + + ExFreePool(FileBuffer); +} + diff --git a/reactos/ntoskrnl/dbg/kdb_expr.c b/reactos/ntoskrnl/dbg/kdb_expr.c new file mode 100644 index 00000000000..fb36fedf77c --- /dev/null +++ b/reactos/ntoskrnl/dbg/kdb_expr.c @@ -0,0 +1,1081 @@ +/* + * ReactOS kernel + * Copyright (C) 2005 ReactOS Team + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* $Id$ + * + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/dbg/kdb_expr.c + * PURPOSE: Kernel debugger expression evaluation + * PROGRAMMER: Gregor Anich (blight@blight.eu.org) + * UPDATE HISTORY: + * Created 15/01/2005 + */ + +/* Note: + * + * The given expression is parsed and stored in reverse polish notation, + * then it is evaluated and the result is returned. + */ + +/* INCLUDES ******************************************************************/ + +#include +#include "kdb.h" +#define NDEBUG +#include + +/* TYPES *********************************************************************/ +typedef enum _RPN_OP_TYPE +{ + RpnOpNop, + RpnOpBinaryOperator, + RpnOpUnaryOperator, + RpnOpImmediate, + RpnOpRegister, + RpnOpDereference +} RPN_OP_TYPE; + +typedef ULONGLONG (*RPN_BINARY_OPERATOR)(ULONGLONG a, ULONGLONG b); + +typedef struct _RPN_OP +{ + RPN_OP_TYPE Type; + ULONG CharacterOffset; + union { + /* RpnOpBinaryOperator */ + RPN_BINARY_OPERATOR BinaryOperator; + /* RpnOpImmediate */ + ULONGLONG Immediate; + /* RpnOpRegister */ + UCHAR Register; + /* RpnOpDereference */ + UCHAR DerefMemorySize; + } Data; +} RPN_OP, *PRPN_OP; + +typedef struct _RPN_STACK +{ + ULONG Size; /* Number of RPN_OPs on Ops */ + ULONG Sp; /* Stack pointer */ + RPN_OP Ops[1]; /* Array of RPN_OPs */ +} RPN_STACK, *PRPN_STACK; + +/* DEFINES *******************************************************************/ +#define stricmp _stricmp + +#ifndef RTL_FIELD_SIZE +# define RTL_FIELD_SIZE(type, field) (sizeof(((type *)0)->field)) +#endif + +#define CONST_STRCPY(dst, src) \ + do { if ((dst) != NULL) { memcpy(dst, src, sizeof(src)); } } while (0); + +#define RPN_OP_STACK_SIZE 256 +#define RPN_VALUE_STACK_SIZE 256 + +/* GLOBALS *******************************************************************/ +STATIC struct { ULONG Size; ULONG Sp; RPN_OP Ops[RPN_OP_STACK_SIZE]; } RpnStack = { RPN_OP_STACK_SIZE, 0 }; + +STATIC CONST struct { PCHAR Name; UCHAR Offset; UCHAR Size; } RegisterToTrapFrame[] = +{ + {"eip", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Eip), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Eip)}, + {"eflags", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Eflags), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Eflags)}, + {"eax", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Eax), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Eax)}, + {"ebx", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ebx), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ebx)}, + {"ecx", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ecx), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ecx)}, + {"edx", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Edx), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Edx)}, + {"esi", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Esi), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Esi)}, + {"edi", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Edi), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Edi)}, + {"esp", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Esp), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Esp)}, + {"ebp", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ebp), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ebp)}, + {"cs", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Cs), 2 }, /* Use only the lower 2 bytes */ + {"ds", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ds), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ds)}, + {"es", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Es), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Es)}, + {"fs", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Fs), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Fs)}, + {"gs", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Gs), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Gs)}, + {"ss", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ss), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ss)}, + {"dr0", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr0), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr0)}, + {"dr1", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr1), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr1)}, + {"dr2", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr2), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr2)}, + {"dr3", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr3), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr3)}, + {"dr6", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr6), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr6)}, + {"dr7", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr7), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr7)}, + {"cr0", FIELD_OFFSET(KDB_KTRAP_FRAME, Cr0), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Cr0)}, + {"cr2", FIELD_OFFSET(KDB_KTRAP_FRAME, Cr2), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Cr2)}, + {"cr3", FIELD_OFFSET(KDB_KTRAP_FRAME, Cr3), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Cr3)}, + {"cr4", FIELD_OFFSET(KDB_KTRAP_FRAME, Cr4), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Cr4)} +}; +STATIC CONST INT RegisterToTrapFrameCount = + sizeof (RegisterToTrapFrame) / sizeof (RegisterToTrapFrame[0]); + +/* FUNCTIONS *****************************************************************/ + +ULONGLONG +RpnBinaryOperatorAdd(ULONGLONG a, ULONGLONG b) +{ + return a + b; +} + +ULONGLONG +RpnBinaryOperatorSub(ULONGLONG a, ULONGLONG b) +{ + return a - b; +} + +ULONGLONG +RpnBinaryOperatorMul(ULONGLONG a, ULONGLONG b) +{ + return a * b; +} + +ULONGLONG +RpnBinaryOperatorDiv(ULONGLONG a, ULONGLONG b) +{ + + return a / b; +} + +ULONGLONG +RpnBinaryOperatorMod(ULONGLONG a, ULONGLONG b) +{ + return a % b; +} + +ULONGLONG +RpnBinaryOperatorEquals(ULONGLONG a, ULONGLONG b) +{ + return (a == b); +} + +ULONGLONG +RpnBinaryOperatorNotEquals(ULONGLONG a, ULONGLONG b) +{ + return (a != b); +} + +ULONGLONG +RpnBinaryOperatorLessThan(ULONGLONG a, ULONGLONG b) +{ + return (a < b); +} + +ULONGLONG +RpnBinaryOperatorLessThanOrEquals(ULONGLONG a, ULONGLONG b) +{ + return (a <= b); +} + +ULONGLONG +RpnBinaryOperatorGreaterThan(ULONGLONG a, ULONGLONG b) +{ + return (a > b); +} + +ULONGLONG +RpnBinaryOperatorGreaterThanOrEquals(ULONGLONG a, ULONGLONG b) +{ + return (a >= b); +} + +/*!\brief Dumps the given RPN stack content + * + * \param Stack Pointer to a RPN_STACK structure. + */ +VOID +RpnpDumpStack( + IN PRPN_STACK Stack) +{ + ULONG ul; + + ASSERT(Stack != NULL); + DbgPrint("\nStack size: %ld\n", Stack->Sp); + for (ul = 0; ul < Stack->Sp; ul++) + { + PRPN_OP Op = Stack->Ops + ul; + switch (Op->Type) + { + case RpnOpNop: + DbgPrint("NOP,"); + break; + + case RpnOpImmediate: + DbgPrint("0x%I64x,", Op->Data.Immediate); + break; + + case RpnOpBinaryOperator: + if (Op->Data.BinaryOperator == RpnBinaryOperatorAdd) + DbgPrint("+,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorSub) + DbgPrint("-,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorMul) + DbgPrint("*,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorDiv) + DbgPrint("/,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorMod) + DbgPrint("%%,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorEquals) + DbgPrint("==,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorNotEquals) + DbgPrint("!=,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorLessThan) + DbgPrint("<,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorLessThanOrEquals) + DbgPrint("<=,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorGreaterThan) + DbgPrint(">,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorGreaterThanOrEquals) + DbgPrint(">=,"); + else + DbgPrint("UNKNOWN OP,"); + break; + + case RpnOpRegister: + DbgPrint("%s,", RegisterToTrapFrame[Op->Data.Register].Name); + break; + + case RpnOpDereference: + DbgPrint("[%s],", + (Op->Data.DerefMemorySize == 1) ? ("byte") : + ((Op->Data.DerefMemorySize == 2) ? ("word") : + ((Op->Data.DerefMemorySize == 4) ? ("dword") : ("qword")) + ) + ); + break; + + default: + DbgPrint("\nUnsupported Type: %d\n", Op->Type); + ul = Stack->Sp; + break; + } + } + DbgPrint("\n"); +} + +/*!\brief Clears the given RPN stack. + * + * \param Stack Pointer to a RPN_STACK structure. + */ +STATIC VOID +RpnpClearStack( + OUT PRPN_STACK Stack) +{ + ASSERT(Stack != NULL); + Stack->Sp = 0; +} + +/*!\brief Pushes an RPN_OP onto the stack. + * + * \param Stack Pointer to a RPN_STACK structure. + * \param Op RPN_OP to be copied onto the stack. + */ +STATIC BOOLEAN +RpnpPushStack( + IN OUT PRPN_STACK Stack, + IN PRPN_OP Op) +{ + ASSERT(Stack != NULL); + ASSERT(Op != NULL); + + if (Stack->Sp >= Stack->Size) + return FALSE; + + memcpy(Stack->Ops + Stack->Sp, Op, sizeof (RPN_OP)); + Stack->Sp++; + return TRUE; +} + +/*!\brief Pops the top op from the stack. + * + * \param Stack Pointer to a RPN_STACK structure. + * \param Op Pointer to an RPN_OP to store the popped op into (can be NULL). + * + * \retval TRUE Success. + * \retval FALSE Failure (stack empty) + */ +STATIC BOOLEAN +RpnpPopStack( + IN OUT PRPN_STACK Stack, + OUT PRPN_OP Op OPTIONAL) +{ + ASSERT(Stack != NULL); + + if (Stack->Sp == 0) + return FALSE; + + Stack->Sp--; + if (Op != NULL) + memcpy(Op, Stack->Ops + Stack->Sp, sizeof (RPN_OP)); + return TRUE; +} + +/*!\brief Gets the top op from the stack (not popping it) + * + * \param Stack Pointer to a RPN_STACK structure. + * \param Op Pointer to an RPN_OP to copy the top op into. + * + * \retval TRUE Success. + * \retval FALSE Failure (stack empty) + */ +STATIC BOOLEAN +RpnpTopStack( + IN PRPN_STACK Stack, + OUT PRPN_OP Op) +{ + ASSERT(Stack != NULL); + ASSERT(Op != NULL); + + if (Stack->Sp == 0) + return FALSE; + + memcpy(Op, Stack->Ops + Stack->Sp - 1, sizeof (RPN_OP)); + return TRUE; +} + +/*!\brief Parses an expression. + * + * This functions parses the given expression until the end of string or a closing + * brace is found. As the function parses the string it pushes RPN_OPs onto the + * stack. + * + * Examples: 1+2*3 ; eax+10 ; (eax+16) * (ebx+4) ; dword[eax] + * + * \param Stack Pointer to a RPN_STACK structure. + * \param Expression String to parse. + * \param CharacterOffset Character offset of the subexpression from the beginning of the expression. + * \param End On success End is set to the character at which parsing stopped. + * \param ErrOffset On failure this is set to the character offset at which the error occoured. + * \param ErrMsg On failure a message describing the problem is copied into this buffer (128 bytes) + * + * \retval TRUE Success. + * \retval FALSE Failure. + */ +STATIC BOOLEAN +RpnpParseExpression( + IN PRPN_STACK Stack, + IN PCHAR Expression, + OUT PCHAR *End OPTIONAL, + IN ULONG CharacterOffset, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + PCHAR p = Expression; + PCHAR pend; + PCHAR Operator = NULL; + LONG OperatorOffset = -1; + RPN_OP RpnOp; + RPN_OP PoppedOperator; + BOOLEAN HavePoppedOperator = FALSE; + RPN_OP ComparativeOp; + BOOLEAN ComparativeOpFilled = FALSE; + BOOLEAN IsComparativeOp; + INT i, i2; + ULONG ul; + UCHAR MemorySize; + CHAR Buffer[16]; + BOOLEAN First; + + ASSERT(Stack != NULL); + ASSERT(Expression != NULL); + + First = TRUE; + for (;;) + { + /* Skip whitespace */ + while (isspace(*p)) + { + p++; + CharacterOffset++; + } + + /* Check for end of expression */ + if (p[0] == '\0' || p[0] == ')' || p[0] == ']') + break; + + if (!First) + { + /* Remember operator */ + Operator = p++; + OperatorOffset = CharacterOffset++;; + + /* Pop operator (to get the right operator precedence) */ + HavePoppedOperator = FALSE; + if (*Operator == '*' || *Operator == '/' || *Operator == '%') + { + if (RpnpTopStack(Stack, &PoppedOperator) && + PoppedOperator.Type == RpnOpBinaryOperator && + (PoppedOperator.Data.BinaryOperator == RpnBinaryOperatorAdd || + PoppedOperator.Data.BinaryOperator == RpnBinaryOperatorSub)) + { + RpnpPopStack(Stack, NULL); + HavePoppedOperator = TRUE; + } + else if (PoppedOperator.Type == RpnOpNop) + { + RpnpPopStack(Stack, NULL); + /* Discard the NOP - it was only pushed to indicate there was a + * closing brace, so the previous operator shouldn't be popped. + */ + } + } + else if ((Operator[0] == '=' && Operator[1] == '=') || + (Operator[0] == '!' && Operator[1] == '=') || + Operator[0] == '<' || Operator[0] == '>') + { + if (Operator[0] == '=' || Operator[0] == '!' || + (Operator[0] == '<' && Operator[1] == '=') || + (Operator[0] == '>' && Operator[1] == '=')) + { + p++; + CharacterOffset++; + } +#if 0 + /* Parse rest of expression */ + if (!RpnpParseExpression(Stack, p + 1, &pend, CharacterOffset + 1, + ErrOffset, ErrMsg)) + { + return FALSE; + } + else if (pend == p + 1) + { + CONST_STRCPY(ErrMsg, "Expression expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset + 1; + return FALSE; + } + goto end_of_expression; /* return */ +#endif + } + else if (Operator[0] != '+' && Operator[0] != '-') + { + CONST_STRCPY(ErrMsg, "Operator expected"); + if (ErrOffset != NULL) + *ErrOffset = OperatorOffset; + return FALSE; + } + + /* Skip whitespace */ + while (isspace(*p)) + { + p++; + CharacterOffset++; + } + } + + /* Get operand */ + MemorySize = sizeof(ULONG_PTR); /* default to pointer size */ +get_operand: + i = strcspn(p, "+-*/%()[]<>!="); + if (i > 0) + { + i2 = i; + + /* Copy register name/memory size */ + while (isspace(p[--i2])); + i2 = min(i2 + 1, sizeof (Buffer) - 1); + strncpy(Buffer, p, i2); + Buffer[i2] = '\0'; + + /* Memory size prefix */ + if (p[i] == '[') + { + if (stricmp(Buffer, "byte") == 0) + MemorySize = 1; + else if (stricmp(Buffer, "word") == 0) + MemorySize = 2; + else if (stricmp(Buffer, "dword") == 0) + MemorySize = 4; + else if (stricmp(Buffer, "qword") == 0) + MemorySize = 8; + else + { + CONST_STRCPY(ErrMsg, "Invalid memory size prefix"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset; + return FALSE; + } + + p += i; + CharacterOffset += i; + goto get_operand; + } + + /* Try to find register */ + for (i = 0; i < RegisterToTrapFrameCount; i++) + { + if (stricmp(RegisterToTrapFrame[i].Name, Buffer) == 0) + break; + } + if (i < RegisterToTrapFrameCount) + { + RpnOp.Type = RpnOpRegister; + RpnOp.CharacterOffset = CharacterOffset; + RpnOp.Data.Register = i; + i = strlen(RegisterToTrapFrame[i].Name); + CharacterOffset += i; + p += i; + } + else + { + /* Immediate value */ + /* FIXME: Need string to ULONGLONG function */ + ul = strtoul(p, &pend, 0); + if (p != pend) + { + RpnOp.Type = RpnOpImmediate; + RpnOp.CharacterOffset = CharacterOffset; + RpnOp.Data.Immediate = (ULONGLONG)ul; + CharacterOffset += pend - p; + p = pend; + } + else + { + CONST_STRCPY(ErrMsg, "Operand expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset; + return FALSE; + } + } + + /* Push operand */ + if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + } + else if (i == 0) + { + if (p[0] == '(' || p[0] == '[') /* subexpression */ + { + if (!RpnpParseExpression(Stack, p + 1, &pend, CharacterOffset + 1, + ErrOffset, ErrMsg)) + { + return FALSE; + } + else if (pend == p + 1) + { + CONST_STRCPY(ErrMsg, "Expression expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset + 1; + return FALSE; + } + + if (p[0] == '[') /* dereference */ + { + ASSERT(MemorySize == 1 || MemorySize == 2 || + MemorySize == 4 || MemorySize == 8); + if (pend[0] != ']') + { + CONST_STRCPY(ErrMsg, "']' expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset + (pend - p); + return FALSE; + } + RpnOp.Type = RpnOpDereference; + RpnOp.CharacterOffset = CharacterOffset; + RpnOp.Data.DerefMemorySize = MemorySize; + if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + } + else /* p[0] == '(' */ + { + if (pend[0] != ')') + { + CONST_STRCPY(ErrMsg, "')' expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset + (pend - p); + return FALSE; + } + } + + /* Push a "nop" to prevent popping of the + operator (which would + * result in (10+10)/2 beeing evaluated as 15) + */ + RpnOp.Type = RpnOpNop; + if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + /* Skip closing brace/bracket */ + pend++; + + CharacterOffset += pend - p; + p = pend; + } + else if (First && p[0] == '-') /* Allow expressions like "- eax" */ + { + RpnOp.Type = RpnOpImmediate; + RpnOp.CharacterOffset = CharacterOffset; + RpnOp.Data.Immediate = 0; + if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + } + else + { + CONST_STRCPY(ErrMsg, "Operand expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset; + return FALSE; + } + } + else + { + CONST_STRCPY(ErrMsg, "strcspn() failed"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + if (!First) + { + /* Push operator */ + RpnOp.CharacterOffset = OperatorOffset; + RpnOp.Type = RpnOpBinaryOperator; + IsComparativeOp = FALSE; + switch (*Operator) + { + case '+': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorAdd; + break; + + case '-': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorSub; + break; + + case '*': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorMul; + break; + + case '/': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorDiv; + break; + + case '%': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorMod; + break; + + case '=': + ASSERT(Operator[1] == '='); + IsComparativeOp = TRUE; + RpnOp.Data.BinaryOperator = RpnBinaryOperatorEquals; + break; + + case '!': + ASSERT(Operator[1] == '='); + IsComparativeOp = TRUE; + RpnOp.Data.BinaryOperator = RpnBinaryOperatorNotEquals; + break; + + case '<': + IsComparativeOp = TRUE; + if (Operator[1] == '=') + RpnOp.Data.BinaryOperator = RpnBinaryOperatorLessThanOrEquals; + else + RpnOp.Data.BinaryOperator = RpnBinaryOperatorLessThan; + break; + + case '>': + IsComparativeOp = TRUE; + if (Operator[1] == '=') + RpnOp.Data.BinaryOperator = RpnBinaryOperatorGreaterThanOrEquals; + else + RpnOp.Data.BinaryOperator = RpnBinaryOperatorGreaterThan; + break; + + default: + ASSERT(0); + break; + } + if (IsComparativeOp) + { + if (ComparativeOpFilled && !RpnpPushStack(Stack, &ComparativeOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + memcpy(&ComparativeOp, &RpnOp, sizeof(RPN_OP)); + ComparativeOpFilled = TRUE; + } + else if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + /* Push popped operator */ + if (HavePoppedOperator) + { + if (!RpnpPushStack(Stack, &PoppedOperator)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + } + } + + First = FALSE; + } + +//end_of_expression: + + if (ComparativeOpFilled && !RpnpPushStack(Stack, &ComparativeOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + /* Skip whitespace */ + while (isspace(*p)) + { + p++; + CharacterOffset++; + } + + if (End != NULL) + *End = p; + + return TRUE; +} + +/*!\brief Evaluates the RPN op stack and returns the result. + * + * \param Stack Pointer to a RPN_STACK structure. + * \param TrapFrame Register values. + * \param Result Pointer to an ULONG to store the result into. + * \param ErrOffset On failure this is set to the character offset at which the error occoured. + * \param ErrMsg Buffer which receives an error message on failure (128 bytes) + * + * \retval TRUE Success. + * \retval FALSE Failure. + */ +STATIC BOOLEAN +RpnpEvaluateStack( + IN PRPN_STACK Stack, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + ULONGLONG ValueStack[RPN_VALUE_STACK_SIZE]; + ULONG ValueStackPointer = 0; + ULONG index; + ULONGLONG ull; + ULONG ul; + USHORT us; + UCHAR uc; + PVOID p; + BOOLEAN Ok; +#ifdef DEBUG_RPN + ULONG ValueStackPointerMax = 0; +#endif + + ASSERT(Stack != NULL); + ASSERT(TrapFrame != NULL); + ASSERT(Result != NULL); + + for (index = 0; index < Stack->Sp; index++) + { + PRPN_OP Op = Stack->Ops + index; + +#ifdef DEBUG_RPN + ValueStackPointerMax = max(ValueStackPointerMax, ValueStackPointer); +#endif + + switch (Op->Type) + { + case RpnOpNop: + /* No operation */ + break; + + case RpnOpImmediate: + if (ValueStackPointer == RPN_VALUE_STACK_SIZE) + { + CONST_STRCPY(ErrMsg, "Value stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + ValueStack[ValueStackPointer++] = Op->Data.Immediate; + break; + + case RpnOpRegister: + if (ValueStackPointer == RPN_VALUE_STACK_SIZE) + { + CONST_STRCPY(ErrMsg, "Value stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + ul = Op->Data.Register; + p = (PVOID)((ULONG_PTR)TrapFrame + RegisterToTrapFrame[ul].Offset); + switch (RegisterToTrapFrame[ul].Size) + { + case 1: ull = (ULONGLONG)(*(PUCHAR)p); break; + case 2: ull = (ULONGLONG)(*(PUSHORT)p); break; + case 4: ull = (ULONGLONG)(*(PULONG)p); break; + case 8: ull = (ULONGLONG)(*(PULONGLONG)p); break; + default: ASSERT(0); return FALSE; break; + } + ValueStack[ValueStackPointer++] = ull; + break; + + case RpnOpDereference: + if (ValueStackPointer < 1) + { + CONST_STRCPY(ErrMsg, "Value stack underflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + /* FIXME: Print a warning when address is out of range */ + p = (PVOID)(ULONG_PTR)ValueStack[ValueStackPointer - 1]; + Ok = FALSE; + switch (Op->Data.DerefMemorySize) + { + case 1: + if (NT_SUCCESS(KdbpSafeReadMemory(&uc, p, sizeof (uc)))) + { + Ok = TRUE; + ull = (ULONGLONG)uc; + } + break; + case 2: + if (NT_SUCCESS(KdbpSafeReadMemory(&us, p, sizeof (us)))) + { + Ok = TRUE; + ull = (ULONGLONG)us; + } + break; + case 4: + if (NT_SUCCESS(KdbpSafeReadMemory(&ul, p, sizeof (ul)))) + { + Ok = TRUE; + ull = (ULONGLONG)ul; + } + break; + case 8: + if (NT_SUCCESS(KdbpSafeReadMemory(&ull, p, sizeof (ull)))) + { + Ok = TRUE; + } + break; + default: + ASSERT(0); + return FALSE; + break; + } + if (!Ok) + { + _snprintf(ErrMsg, 128, "Couldn't access memory at 0x%lx", (ULONG)p); + if (ErrOffset != NULL) + *ErrOffset = Op->CharacterOffset; + return FALSE; + } + ValueStack[ValueStackPointer - 1] = ull; + break; + + case RpnOpBinaryOperator: + if (ValueStackPointer < 2) + { + CONST_STRCPY(ErrMsg, "Value stack underflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + ValueStackPointer--; + ull = ValueStack[ValueStackPointer]; + if (ull == 0 && (Op->Data.BinaryOperator == RpnBinaryOperatorDiv || + Op->Data.BinaryOperator == RpnBinaryOperatorDiv)) + { + CONST_STRCPY(ErrMsg, "Devision by zero"); + if (ErrOffset != NULL) + *ErrOffset = Op->CharacterOffset; + return FALSE; + } + ull = Op->Data.BinaryOperator(ValueStack[ValueStackPointer - 1], ull); + ValueStack[ValueStackPointer - 1] = ull; + break; + + default: + ASSERT(0); + return FALSE; + } + } +#ifdef DEBUG_RPN + DPRINT1("Max value stack pointer: %d\n", ValueStackPointerMax); +#endif + if (ValueStackPointer != 1) + { + CONST_STRCPY(ErrMsg, "Stack not empty after evaluation"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + *Result = ValueStack[0]; + return TRUE; +} + +/*!\brief Evaluates the given expression + * + * \param Expression Expression to evaluate. + * \param TrapFrame Register values. + * \param Result Variable which receives the result on success. + * \param ErrOffset Variable which receives character offset on parse error (-1 on other errors) + * \param ErrMsg Buffer which receives an error message on failure (128 bytes) + * + * \retval TRUE Success. + * \retval FALSE Failure. + */ +BOOLEAN +KdbpRpnEvaluateExpression( + IN PCHAR Expression, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + PRPN_STACK Stack = (PRPN_STACK)&RpnStack; + + ASSERT(Expression != NULL); + ASSERT(TrapFrame != NULL); + ASSERT(Result != NULL); + + /* Clear the stack and parse the expression */ + RpnpClearStack(Stack); + if (!RpnpParseExpression(Stack, Expression, NULL, 0, ErrOffset, ErrMsg)) + { + return FALSE; + } +#ifdef DEBUG_RPN + RpnpDumpStack(Stack); +#endif + + /* Evaluate the stack */ + if (!RpnpEvaluateStack(Stack, TrapFrame, Result, ErrOffset, ErrMsg)) + { + return FALSE; + } + + return TRUE; +} + +/*!\brief Parses the given expression and returns a "handle" to it. + * + * \param Expression Expression to evaluate. + * \param ErrOffset Variable which receives character offset on parse error (-1 on other errors) + * \param ErrMsg Buffer which receives an error message on failure (128 bytes) + * + * \returns "Handle" for the expression, NULL on failure. + * + * \sa KdbpRpnEvaluateExpression + */ +PVOID +KdbpRpnParseExpression( + IN PCHAR Expression, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + LONG Size; + PRPN_STACK Stack = (PRPN_STACK)&RpnStack; + PRPN_STACK NewStack; + + ASSERT(Expression != NULL); + + /* Clear the stack and parse the expression */ + RpnpClearStack(Stack); + if (!RpnpParseExpression(Stack, Expression, NULL, 0, ErrOffset, ErrMsg)) + { + return FALSE; + } +#ifdef DEBUG_RPN + RpnpDumpStack(Stack); +#endif + + /* Duplicate the stack and return a pointer/handle to it */ + ASSERT(Stack->Sp >= 1); + Size = sizeof (RPN_STACK) + (RTL_FIELD_SIZE(RPN_STACK, Ops[0]) * (Stack->Sp - 1)); + NewStack = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_KDBG); + if (NewStack == NULL) + { + CONST_STRCPY(ErrMsg, "Out of memory"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return NULL; + } + memcpy(NewStack, Stack, Size); + NewStack->Size = NewStack->Sp; + + return NewStack; +} + +/*!\brief Evaluates the given expression and returns the result. + * + * \param Expression Expression "handle" returned by KdbpRpnParseExpression. + * \param TrapFrame Register values. + * \param Result Variable which receives the result on success. + * \param ErrOffset Variable which receives character offset on parse error (-1 on other errors) + * \param ErrMsg Buffer which receives an error message on failure (128 bytes) + * + * \returns "Handle" for the expression, NULL on failure. + * + * \sa KdbpRpnParseExpression + */ +BOOLEAN +KdbpRpnEvaluateParsedExpression( + IN PVOID Expression, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + PRPN_STACK Stack = (PRPN_STACK)Expression; + + ASSERT(Expression != NULL); + ASSERT(TrapFrame != NULL); + ASSERT(Result != NULL); + + /* Evaluate the stack */ + return RpnpEvaluateStack(Stack, TrapFrame, Result, ErrOffset, ErrMsg); +} + diff --git a/reactos/ntoskrnl/dbg/kdb_keyboard.c b/reactos/ntoskrnl/dbg/kdb_keyboard.c index bc0c6b357d9..bf3bfdc843e 100644 --- a/reactos/ntoskrnl/dbg/kdb_keyboard.c +++ b/reactos/ntoskrnl/dbg/kdb_keyboard.c @@ -62,12 +62,13 @@ VOID KbdDisableMouse() } CHAR -KdbTryGetCharKeyboard(PULONG ScanCode) +KdbpTryGetCharKeyboard(PULONG ScanCode, UINT Retry) { static byte_t last_key = 0; static byte_t shift = 0; char c; - while(1) { + BOOLEAN KeepRetrying = (Retry == 0); + while (KeepRetrying || Retry-- > 0) { unsigned char status = kbd_read_status(); while (status & KBD_STAT_OBF) { byte_t scancode; @@ -91,6 +92,8 @@ KdbTryGetCharKeyboard(PULONG ScanCode) } } } + + return -1; } #endif @@ -305,7 +308,7 @@ static char keymap[128][2] = { * Yes, this is horrible. */ ULONG -KdbTryGetCharKeyboard(VOID) +KdbpTryGetCharKeyboard(VOID) { static unsigned shift_state, ctrl_state, meta_state; unsigned scan_code, ch; diff --git a/reactos/ntoskrnl/dbg/kdb_serial.c b/reactos/ntoskrnl/dbg/kdb_serial.c index bd2d8f2f854..519e3eca108 100644 --- a/reactos/ntoskrnl/dbg/kdb_serial.c +++ b/reactos/ntoskrnl/dbg/kdb_serial.c @@ -20,11 +20,14 @@ extern KD_PORT_INFORMATION LogPortInfo; CHAR -KdbTryGetCharSerial() +KdbpTryGetCharSerial(UINT Retry) { - UCHAR Result; + CHAR Result = -1; - while( !KdPortGetByteEx (&LogPortInfo, &Result) ); + if (Retry == 0) + while (!KdPortGetByteEx(&LogPortInfo, (PUCHAR)&Result)); + else + while (!KdPortGetByteEx(&LogPortInfo, (PUCHAR)&Result) && Retry-- > 0); return Result; } diff --git a/reactos/ntoskrnl/dbg/kdb_string.c b/reactos/ntoskrnl/dbg/kdb_string.c new file mode 100644 index 00000000000..52f3ed47770 --- /dev/null +++ b/reactos/ntoskrnl/dbg/kdb_string.c @@ -0,0 +1,124 @@ +/* + * ReactOS kernel + * Copyright (C) 2005 ReactOS Team + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* $Id$ + * + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/dbg/kdb_string.c + * PURPOSE: Kernel debugger string functions + * PROGRAMMER: Gregor Anich (blight@blight.eu.org) + * UPDATE HISTORY: + * Created 17/01/2005 + */ + +/* INCLUDES ******************************************************************/ +#include +#include + +/* FUNCTIONS *****************************************************************/ + +#if 0 +int +_stricmp( + const char *s1, + const char *s2) +{ + char c1, c2; + for (;;) + { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + if (c1 < c2) + return -1; + else if (c1 > c2) + return 1; + if (c1 == '\0') + break; + } + return 0; +} +#endif /* unused */ + +/* + * Convert a string to an unsigned long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +unsigned long +strtoul(const char *nptr, char **endptr, int base) +{ + const char *s = nptr; + unsigned long acc; + int c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') + { + neg = 1; + c = *s++; + } + else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) + { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; + cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) + { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) + { + acc = ULONG_MAX; + } + else if (neg) + acc = -acc; + if (endptr != 0) + *endptr = any ? (char *)s - 1 : (char *)nptr; + return acc; +} + diff --git a/reactos/ntoskrnl/dbg/kdb_symbols.c b/reactos/ntoskrnl/dbg/kdb_symbols.c index 618711e6be0..c95259ef4bc 100644 --- a/reactos/ntoskrnl/dbg/kdb_symbols.c +++ b/reactos/ntoskrnl/dbg/kdb_symbols.c @@ -243,12 +243,12 @@ KdbSymPrintAddress(IN PVOID Address) FunctionName); if (NT_SUCCESS(Status)) { - DbgPrint("<%ws: %x (%s:%d (%s))>", + DbgPrint("<%ws:%x (%s:%d (%s))>", Info.Name, RelativeAddress, FileName, LineNumber, FunctionName); } else { - DbgPrint("<%ws: %x>", Info.Name, RelativeAddress); + DbgPrint("<%ws:%x>", Info.Name, RelativeAddress); } return TRUE; @@ -541,7 +541,7 @@ KdbSymFreeProcessSymbols(IN PEPROCESS Process) CurrentProcess = PsGetCurrentProcess(); if (CurrentProcess != Process) { - KeAttachProcess(&Process->Pcb); + KeAttachProcess(EPROCESS_TO_KPROCESS(Process)); } Peb = Process->Peb; ASSERT(Peb); diff --git a/reactos/ntoskrnl/dbg/profile.c b/reactos/ntoskrnl/dbg/profile.c deleted file mode 100755 index 3b2350541b5..00000000000 --- a/reactos/ntoskrnl/dbg/profile.c +++ /dev/null @@ -1,478 +0,0 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/dbg/profile.c - * PURPOSE: Kernel profiling - * - * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) - */ - -/* INCLUDES *****************************************************************/ - -#include -#include "kdb.h" - -#define NDEBUG -#include - -/* FUNCTIONS *****************************************************************/ - -#define PROFILE_SESSION_LENGTH 30 /* Session length in seconds */ - -typedef struct _PROFILE_DATABASE_ENTRY -{ - ULONG_PTR Address; -} PROFILE_DATABASE_ENTRY, *PPROFILE_DATABASE_ENTRY; - -#define PDE_BLOCK_ENTRIES ((PAGE_SIZE - (sizeof(LIST_ENTRY) + sizeof(ULONG))) / sizeof(PROFILE_DATABASE_ENTRY)) - -typedef struct _PROFILE_DATABASE_BLOCK -{ - LIST_ENTRY ListEntry; - ULONG UsedEntries; - PROFILE_DATABASE_ENTRY Entries[PDE_BLOCK_ENTRIES]; -} PROFILE_DATABASE_BLOCK, *PPROFILE_DATABASE_BLOCK; - -typedef struct _PROFILE_DATABASE -{ - LIST_ENTRY ListHead; -} PROFILE_DATABASE, *PPROFILE_DATABASE; - -typedef struct _SAMPLE_GROUP_INFO -{ - ULONG_PTR Address; - ULONG Count; - CHAR Description[128]; - LIST_ENTRY ListEntry; -} SAMPLE_GROUP_INFO, *PSAMPLE_GROUP_INFO; - -static volatile BOOLEAN KdbProfilingInitialized = FALSE; -static volatile BOOLEAN KdbProfilingEnabled = FALSE; -static volatile BOOLEAN KdbProfilingSuspended = FALSE; -static PPROFILE_DATABASE KdbProfileDatabase = NULL; -static KDPC KdbProfilerCollectorDpc; -static HANDLE KdbProfilerThreadHandle; -static CLIENT_ID KdbProfilerThreadCid; -static HANDLE KdbProfilerLogFile; -static KTIMER KdbProfilerTimer; -static KMUTEX KdbProfilerLock; -static BOOLEAN KdbEnableProfiler = FALSE; - -VOID -KdbDeleteProfileDatabase(PPROFILE_DATABASE ProfileDatabase) -{ - PLIST_ENTRY current = NULL; - - current = RemoveHeadList(&ProfileDatabase->ListHead); - while (current != &ProfileDatabase->ListHead) - { - PPROFILE_DATABASE_BLOCK block = CONTAINING_RECORD( - current, PROFILE_DATABASE_BLOCK, ListEntry); - ExFreePool(block); - current = RemoveHeadList(&ProfileDatabase->ListHead); - } -} - -VOID -KdbAddEntryToProfileDatabase(PPROFILE_DATABASE ProfileDatabase, ULONG_PTR Address) -{ - PPROFILE_DATABASE_BLOCK block; - - if (IsListEmpty(&ProfileDatabase->ListHead)) - { - block = ExAllocatePool(NonPagedPool, sizeof(PROFILE_DATABASE_BLOCK)); - ASSERT(block); - block->UsedEntries = 0; - InsertTailList(&ProfileDatabase->ListHead, &block->ListEntry); - block->Entries[block->UsedEntries++].Address = Address; - return; - } - - block = CONTAINING_RECORD(ProfileDatabase->ListHead.Blink, PROFILE_DATABASE_BLOCK, ListEntry); - if (block->UsedEntries >= PDE_BLOCK_ENTRIES) - { - block = ExAllocatePool(NonPagedPool, sizeof(PROFILE_DATABASE_BLOCK)); - ASSERT(block); - block->UsedEntries = 0; - InsertTailList(&ProfileDatabase->ListHead, &block->ListEntry); - } - block->Entries[block->UsedEntries++].Address = Address; -} - -VOID INIT_FUNCTION -KdbInitProfiling() -{ - KdbEnableProfiler = TRUE; -} - -VOID INIT_FUNCTION -KdbInitProfiling2() -{ - if (KdbEnableProfiler) - { - KdbEnableProfiling(); - KdbProfilingInitialized = TRUE; - } -} - -VOID -KdbSuspendProfiling() -{ - KdbProfilingSuspended = TRUE; -} - -VOID -KdbResumeProfiling() -{ - KdbProfilingSuspended = FALSE; -} - -BOOLEAN -KdbProfilerGetSymbolInfo(PVOID address, OUT PCH NameBuffer) -{ - PLIST_ENTRY current_entry; - MODULE_TEXT_SECTION* current; - extern LIST_ENTRY ModuleTextListHead; - ULONG_PTR RelativeAddress; - NTSTATUS Status; - CHAR FileName[256]; - CHAR FunctionName[256]; - - current_entry = ModuleTextListHead.Flink; - - while (current_entry != &ModuleTextListHead && - current_entry != NULL) - { - current = - CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry); - - if (address >= (PVOID)current->Base && - address < (PVOID)(current->Base + current->Length)) - { - RelativeAddress = (ULONG_PTR) address - current->Base; - Status = KdbSymGetAddressInformation(current->RosSymInfo, - RelativeAddress, - NULL, - FileName, - FunctionName); - if (NT_SUCCESS(Status)) - { - sprintf(NameBuffer, "%s (%s)", FunctionName, FileName); - return(TRUE); - } - return(TRUE); - } - current_entry = current_entry->Flink; - } - return(FALSE); -} - -PLIST_ENTRY -KdbProfilerLargestSampleGroup(PLIST_ENTRY SamplesListHead) -{ - PLIST_ENTRY current; - PLIST_ENTRY largest; - ULONG count; - - count = 0; - largest = SamplesListHead->Flink; - current = SamplesListHead->Flink; - while (current != SamplesListHead) - { - PSAMPLE_GROUP_INFO sgi = CONTAINING_RECORD( - current, SAMPLE_GROUP_INFO, ListEntry); - - if (sgi->Count > count) - { - largest = current; - count = sgi->Count; - } - - current = current->Flink; - } - if (count == 0) - { - return NULL; - } - return largest; -} - -VOID -KdbProfilerWriteString(PCH String) -{ - IO_STATUS_BLOCK Iosb; - NTSTATUS Status; - ULONG Length; - - Length = strlen(String); - Status = NtWriteFile(KdbProfilerLogFile, - NULL, - NULL, - NULL, - &Iosb, - String, - Length, - NULL, - NULL); - - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWriteFile() failed with status 0x%.08x\n", Status); - } -} - -NTSTATUS -KdbProfilerWriteSampleGroups(PLIST_ENTRY SamplesListHead) -{ - CHAR Buffer[256]; - PLIST_ENTRY current = NULL; - PLIST_ENTRY Largest; - - KdbProfilerWriteString("\r\n\r\n"); - KdbProfilerWriteString("Count Symbol\r\n"); - KdbProfilerWriteString("--------------------------------------------------\r\n"); - - current = SamplesListHead->Flink; - while (current != SamplesListHead) - { - Largest = KdbProfilerLargestSampleGroup(SamplesListHead); - if (Largest != NULL) - { - PSAMPLE_GROUP_INFO sgi = CONTAINING_RECORD( - Largest, SAMPLE_GROUP_INFO, ListEntry); - - //DbgPrint("%.08lu %s\n", sgi->Count, sgi->Description); - - sprintf(Buffer, "%.08lu %s\r\n", sgi->Count, sgi->Description); - KdbProfilerWriteString(Buffer); - - RemoveEntryList(Largest); - ExFreePool(sgi); - } - else - { - break; - } - - current = SamplesListHead->Flink; - } - - return STATUS_SUCCESS; -} - -LONG STDCALL -KdbProfilerKeyCompare(IN PVOID Key1, - IN PVOID Key2) -{ - int value = strcmp(Key1, Key2); - - if (value == 0) - return 0; - - return (value < 0) ? -1 : 1; -} - - -NTSTATUS -KdbProfilerAnalyzeSamples() -{ - CHAR NameBuffer[512]; - ULONG KeyLength; - PLIST_ENTRY current = NULL; - HASH_TABLE Hashtable; - LIST_ENTRY SamplesListHead; - ULONG Index; - ULONG_PTR Address; - - if (!ExInitializeHashTable(&Hashtable, 12, KdbProfilerKeyCompare, TRUE)) - { - DPRINT1("ExInitializeHashTable() failed."); - KEBUGCHECK(0); - } - - InitializeListHead(&SamplesListHead); - - current = RemoveHeadList(&KdbProfileDatabase->ListHead); - while (current != &KdbProfileDatabase->ListHead) - { - PPROFILE_DATABASE_BLOCK block; - - block = CONTAINING_RECORD(current, PROFILE_DATABASE_BLOCK, ListEntry); - - for (Index = 0; Index < block->UsedEntries; Index++) - { - PSAMPLE_GROUP_INFO sgi; - Address = block->Entries[Index].Address; - if (KdbProfilerGetSymbolInfo((PVOID) Address, (PCH) &NameBuffer)) - { - } - else - { - sprintf(NameBuffer, "(0x%.08lx)", (ULONG) Address); - } - - KeyLength = strlen(NameBuffer); - if (!ExSearchHashTable(&Hashtable, (PVOID) NameBuffer, KeyLength, (PVOID *) &sgi)) - { - sgi = ExAllocatePool(NonPagedPool, sizeof(SAMPLE_GROUP_INFO)); - ASSERT(sgi); - sgi->Address = Address; - sgi->Count = 1; - strcpy(sgi->Description, NameBuffer); - InsertTailList(&SamplesListHead, &sgi->ListEntry); - ExInsertHashTable(&Hashtable, sgi->Description, KeyLength, (PVOID) sgi); - } - else - { - sgi->Count++; - } - } - - ExFreePool(block); - - current = RemoveHeadList(&KdbProfileDatabase->ListHead); - } - - KdbProfilerWriteSampleGroups(&SamplesListHead); - - ExDeleteHashTable(&Hashtable); - - KdbDeleteProfileDatabase(KdbProfileDatabase); - - return STATUS_SUCCESS; -} - -VOID STDCALL -KdbProfilerThreadMain(PVOID Context) -{ - for (;;) - { - KeWaitForSingleObject(&KdbProfilerTimer, Executive, KernelMode, TRUE, NULL); - - KeWaitForSingleObject(&KdbProfilerLock, Executive, KernelMode, FALSE, NULL); - - KdbSuspendProfiling(); - - KdbProfilerAnalyzeSamples(); - - KdbResumeProfiling(); - - KeReleaseMutex(&KdbProfilerLock, FALSE); - } -} - -VOID -KdbDisableProfiling() -{ - if (KdbProfilingEnabled == TRUE) - { - /* FIXME: Implement */ -#if 0 - KdbProfilingEnabled = FALSE; - /* Stop timer */ - /* Close file */ - if (KdbProfileDatabase != NULL) - { - KdbDeleteProfileDatabase(KdbProfileDatabase); - ExFreePool(KdbProfileDatabase); - KdbProfileDatabase = NULL; - } -#endif - } -} - -/* - * SystemArgument1 = EIP - */ -static VOID STDCALL -KdbProfilerCollectorDpcRoutine(PKDPC Dpc, PVOID DeferredContext, - PVOID SystemArgument1, PVOID SystemArgument2) -{ - ULONG_PTR address = (ULONG_PTR) SystemArgument1; - - KdbAddEntryToProfileDatabase(KdbProfileDatabase, address); -} - -VOID INIT_FUNCTION -KdbEnableProfiling() -{ - if (KdbProfilingEnabled == FALSE) - { - NTSTATUS Status; - OBJECT_ATTRIBUTES ObjectAttributes; - UNICODE_STRING FileName; - IO_STATUS_BLOCK Iosb; - LARGE_INTEGER DueTime; - - RtlInitUnicodeString(&FileName, L"\\SystemRoot\\profiler.log"); - InitializeObjectAttributes(&ObjectAttributes, - &FileName, - 0, - NULL, - NULL); - - Status = NtCreateFile(&KdbProfilerLogFile, - FILE_ALL_ACCESS, - &ObjectAttributes, - &Iosb, - NULL, - FILE_ATTRIBUTE_NORMAL, - 0, - FILE_SUPERSEDE, - FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT, - NULL, - 0); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to create profiler log file\n"); - return; - } - KeInitializeMutex(&KdbProfilerLock, 0); - - KdbProfileDatabase = ExAllocatePool(NonPagedPool, sizeof(PROFILE_DATABASE)); - ASSERT(KdbProfileDatabase); - InitializeListHead(&KdbProfileDatabase->ListHead); - KeInitializeDpc(&KdbProfilerCollectorDpc, KdbProfilerCollectorDpcRoutine, NULL); - - /* Initialize our periodic timer and its associated DPC object. When the timer - expires, the KdbProfilerSessionEndDpc deferred procedure call (DPC) is queued */ - KeInitializeTimerEx(&KdbProfilerTimer, SynchronizationTimer); - - Status = PsCreateSystemThread(&KdbProfilerThreadHandle, - THREAD_ALL_ACCESS, - NULL, - NULL, - &KdbProfilerThreadCid, - KdbProfilerThreadMain, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to create profiler thread\n"); - return; - } - - /* Start the periodic timer with an initial and periodic - relative expiration time of PROFILE_SESSION_LENGTH seconds */ - DueTime.QuadPart = -(LONGLONG) PROFILE_SESSION_LENGTH * 1000 * 10000; - KeSetTimerEx(&KdbProfilerTimer, DueTime, PROFILE_SESSION_LENGTH * 1000, NULL); - - KdbProfilingEnabled = TRUE; - } -} - -VOID -KdbProfileInterrupt(ULONG_PTR Address) -{ - ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL); - - if (KdbProfilingInitialized != TRUE) - { - return; - } - - if ((KdbProfilingEnabled) && (!KdbProfilingSuspended)) - { - (BOOLEAN) KeInsertQueueDpc(&KdbProfilerCollectorDpc, (PVOID) Address, NULL); - } -} diff --git a/reactos/ntoskrnl/ex/btree.c b/reactos/ntoskrnl/ex/btree.c deleted file mode 100644 index abefbee15f5..00000000000 --- a/reactos/ntoskrnl/ex/btree.c +++ /dev/null @@ -1,644 +0,0 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ex/btree.c - * PURPOSE: Binary tree support - * - * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) - */ - -#include -#define NDEBUG -#include - -/* DATA **********************************************************************/ - -typedef struct _BINARY_TREE_NODE -{ - struct _BINARY_TREE_NODE * Parent; - struct _BINARY_TREE_NODE * LeftChild; - struct _BINARY_TREE_NODE * RightChild; - PVOID Key; - PVOID Value; -} BINARY_TREE_NODE, *PBINARY_TREE_NODE; - -typedef struct _TRAVERSE_CONTEXT { - PTRAVERSE_ROUTINE Routine; - PVOID Context; -} TRAVERSE_CONTEXT, *PTRAVERSE_CONTEXT; - -/* FUNCTIONS ****************************************************************/ - -#define ExpBinaryTreeRootNode(Tree)(((PBINARY_TREE) (Tree))->RootNode) -#define ExpBinaryTreeIsExternalNode(Node)(((Node)->LeftChild == NULL) && ((Node)->RightChild == NULL)) -#define ExpBinaryTreeIsInternalNode(Node)(!ExpBinaryTreeIsExternalNode(Node)) -#define ExpBinaryTreeNodeKey(Node)((Node)->Key) -#define ExpBinaryTreeNodeValue(Node)((Node)->Value) -#define ExpBinaryTreeParentNode(Node)((Node)->Parent) -#define ExpBinaryTreeLeftChildNode(Node)((Node)->LeftChild) -#define ExpBinaryTreeRightChildNode(Node)((Node)->RightChild) -#define ExpBinaryTreeNodeEqual(Equality)((Equality) == 0) -#define ExpBinaryTreeNodeLess(Equality)((Equality) < 0) -#define ExpBinaryTreeNodeMore(Equality)((Equality) > 0) - - -/* - * Lock the binary tree - */ -inline VOID -ExpLockBinaryTree(PBINARY_TREE Tree, - PKIRQL OldIrql) -{ - if (Tree->UseNonPagedPool) - { - KeAcquireSpinLock(&Tree->Lock.NonPaged, OldIrql); - } - else - { - ExAcquireFastMutex(&Tree->Lock.Paged); - } -} - - -/* - * Unlock the binary tree - */ -inline VOID -ExpUnlockBinaryTree(PBINARY_TREE Tree, - PKIRQL OldIrql) -{ - if (Tree->UseNonPagedPool) - { - KeReleaseSpinLock(&Tree->Lock.NonPaged, *OldIrql); - } - else - { - ExReleaseFastMutex(&Tree->Lock.Paged); - } -} - - -/* - * Allocate resources for a new node and initialize it. - */ -inline PBINARY_TREE_NODE -ExpCreateBinaryTreeNode(PBINARY_TREE Tree, - PBINARY_TREE_NODE Parent, - PVOID Value) -{ - PBINARY_TREE_NODE Node; - - if (Tree->UseNonPagedPool) - { - Node = (PBINARY_TREE_NODE) ExAllocateFromNPagedLookasideList(&Tree->List.NonPaged); - } - else - { - Node = (PBINARY_TREE_NODE) ExAllocateFromPagedLookasideList(&Tree->List.Paged); - } - - if (Node) - { - ExpBinaryTreeParentNode(Node) = Parent; - ExpBinaryTreeLeftChildNode(Node) = NULL; - ExpBinaryTreeRightChildNode(Node) = NULL; - ExpBinaryTreeNodeValue(Node) = Value; - } - - return Node; -} - - -/* - * Release resources for the node. - */ -inline VOID -ExpDestroyBinaryTreeNode(PBINARY_TREE Tree, - PBINARY_TREE_NODE Node) -{ - if (Tree->UseNonPagedPool) - { - ExFreeToNPagedLookasideList(&Tree->List.NonPaged, Node); - } - else - { - ExFreeToPagedLookasideList(&Tree->List.Paged, Node); - } -} - - -/* - * Replaces a child node of a node with a new node. - * The lock for the tree must be acquired when this routine is called. - */ -inline VOID -ExpBinaryTreeReplaceChildNode(PBINARY_TREE_NODE Child, - PBINARY_TREE_NODE NewChild) -{ - if (ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Child)) == Child) - { - ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Child)) = NewChild; - } - else - { - ExpBinaryTreeRightChildNode(ExpBinaryTreeParentNode(Child)) = NewChild; - } -} - - -/* - * Returns the sibling node of a node. - * The lock for the tree must be acquired when this routine is called. - */ -inline PBINARY_TREE_NODE -ExpSiblingBinaryTreeNode(PBINARY_TREE Tree, - PBINARY_TREE_NODE Node) -{ - if (Node == ExpBinaryTreeRootNode(Tree)) - { - return NULL; - } - else - { - if (ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Node)) == Node) - { - return ExpBinaryTreeRightChildNode(ExpBinaryTreeParentNode(Node)); - } - else - { - return ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Node)); - } - } -} - - -/* - * Expands an external node to an internal node. - * The lock for the tree must be acquired when this routine is called. - */ -VOID -ExpExpandExternalBinaryTreeNode(PBINARY_TREE Tree, - PBINARY_TREE_NODE Node) -{ - ExpBinaryTreeLeftChildNode(Node) = ExpCreateBinaryTreeNode(Tree, Node, NULL); - - if (!ExpBinaryTreeLeftChildNode(Node)) - { - /* FIXME: Throw exception */ - DbgPrint("ExpCreateBinaryTreeNode() failed\n"); - } - - ExpBinaryTreeRightChildNode(Node) = ExpCreateBinaryTreeNode(Tree, Node, NULL); - - if (!ExpBinaryTreeRightChildNode(Node)) - { - ExpDestroyBinaryTreeNode(Tree, ExpBinaryTreeLeftChildNode(Node)); - /* FIXME: Throw exception */ - DbgPrint("ExpCreateBinaryTreeNode() failed\n"); - } -} - - -/* - * Searches a tree for a node with the specified key. If a node with the - * specified key is not found, the external node where it should be is - * returned. - * The lock for the tree must be acquired when this routine is called. - */ -inline PBINARY_TREE_NODE -ExpSearchBinaryTree(PBINARY_TREE Tree, - PVOID Key, - PBINARY_TREE_NODE Node) -{ - LONG Equality; - - /* FIXME: Possibly do this iteratively due to the small kernel-mode stack */ - - if (ExpBinaryTreeIsExternalNode(Node)) - { - return Node; - } - - Equality = (*Tree->Compare)(Key, ExpBinaryTreeNodeKey(Node)); - - if (ExpBinaryTreeNodeEqual(Equality)) - { - return Node; - } - - if (ExpBinaryTreeNodeLess(Equality)) - { - return ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeLeftChildNode(Node)); - } - -/* if (ExpBinaryTreeNodeMore(Equality)) */ - { - return ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeRightChildNode(Node)); - } -} - - -/* - * Removes an external node and it's parent node from the tree. - * The lock for the tree must be acquired when this routine is called. - */ -VOID -ExpRemoveAboveExternalBinaryTreeNode(PBINARY_TREE Tree, - PBINARY_TREE_NODE Node) -{ - ASSERTMSG(ExpBinaryTreeIsExternalNode(Node), ("Node is not external")); - - if (Node == ExpBinaryTreeRootNode(Tree)) - { - return; - } - else - { - PBINARY_TREE_NODE GrandParent; - PBINARY_TREE_NODE NewChild; - - GrandParent = ExpBinaryTreeParentNode(ExpBinaryTreeParentNode(Node)); - NewChild = ExpSiblingBinaryTreeNode(Tree, Node); - - if (GrandParent != NULL) - { - ExpBinaryTreeReplaceChildNode(ExpBinaryTreeParentNode(Node), NewChild); - } - - ExpDestroyBinaryTreeNode(Tree, ExpBinaryTreeParentNode(Node)); - ExpDestroyBinaryTreeNode(Tree, Node); - } -} - - -/* - * Release resources used by nodes of a binary (sub)tree. - */ -VOID -ExpDeleteBinaryTree(PBINARY_TREE Tree, - PBINARY_TREE_NODE Node) -{ - /* FIXME: Possibly do this iteratively due to the small kernel-mode stack */ - - if (ExpBinaryTreeIsInternalNode(Node)) - { - ExpDeleteBinaryTree(Tree, ExpBinaryTreeLeftChildNode(Node)); - ExpDeleteBinaryTree(Tree, ExpBinaryTreeRightChildNode(Node)); - } - - ExpDestroyBinaryTreeNode(Tree, Node); -} - - -/* - * Traverse a binary tree using preorder traversal method. - * Returns FALSE, if the traversal was terminated prematurely or - * TRUE if the callback routine did not request that the traversal - * be terminated prematurely. - * The lock for the tree must be acquired when this routine is called. - */ -BOOLEAN -ExpTraverseBinaryTreePreorder(PTRAVERSE_CONTEXT Context, - PBINARY_TREE_NODE Node) -{ - if (ExpBinaryTreeIsInternalNode(Node)) - { - /* Call the traversal routine */ - if (!(*Context->Routine)(Context->Context, - ExpBinaryTreeNodeKey(Node), - ExpBinaryTreeNodeValue(Node))) - { - return FALSE; - } - - /* Traverse left subtree */ - ExpTraverseBinaryTreePreorder(Context, ExpBinaryTreeLeftChildNode(Node)); - - /* Traverse right subtree */ - ExpTraverseBinaryTreePreorder(Context, ExpBinaryTreeRightChildNode(Node)); - } - - return TRUE; -} - - -/* - * Traverse a binary tree using inorder traversal method. - * Returns FALSE, if the traversal was terminated prematurely or - * TRUE if the callback routine did not request that the traversal - * be terminated prematurely. - * The lock for the tree must be acquired when this routine is called. - */ -BOOLEAN -ExpTraverseBinaryTreeInorder(PTRAVERSE_CONTEXT Context, - PBINARY_TREE_NODE Node) -{ - if (ExpBinaryTreeIsInternalNode(Node)) - { - /* Traverse left subtree */ - ExpTraverseBinaryTreeInorder(Context, ExpBinaryTreeLeftChildNode(Node)); - - /* Call the traversal routine */ - if (!(*Context->Routine)(Context->Context, - ExpBinaryTreeNodeKey(Node), - ExpBinaryTreeNodeValue(Node))) - { - return FALSE; - } - - /* Traverse right subtree */ - ExpTraverseBinaryTreeInorder(Context, ExpBinaryTreeRightChildNode(Node)); - } - - return TRUE; -} - - -/* - * Traverse a binary tree using postorder traversal method. - * Returns FALSE, if the traversal was terminated prematurely or - * TRUE if the callback routine did not request that the traversal - * be terminated prematurely. - * The lock for the tree must be acquired when this routine is called. - */ -BOOLEAN -ExpTraverseBinaryTreePostorder(PTRAVERSE_CONTEXT Context, - PBINARY_TREE_NODE Node) -{ - if (ExpBinaryTreeIsInternalNode(Node)) - { - /* Traverse left subtree */ - ExpTraverseBinaryTreePostorder(Context, ExpBinaryTreeLeftChildNode(Node)); - - /* Traverse right subtree */ - ExpTraverseBinaryTreePostorder(Context, ExpBinaryTreeRightChildNode(Node)); - - /* Call the traversal routine */ - return (*Context->Routine)(Context->Context, - ExpBinaryTreeNodeKey(Node), - ExpBinaryTreeNodeValue(Node)); - } - - return TRUE; -} - - -/* - * Default key compare function. Compares the integer values of the two keys. - */ -LONG STDCALL -ExpBinaryTreeDefaultCompare(PVOID Key1, - PVOID Key2) -{ - if (Key1 == Key2) - return 0; - - return (((LONG_PTR) Key1 < (LONG_PTR) Key2) ? -1 : 1); -} - - -/* - * Initializes a binary tree. - */ -BOOLEAN STDCALL -ExInitializeBinaryTree(IN PBINARY_TREE Tree, - IN PKEY_COMPARATOR Compare, - IN BOOLEAN UseNonPagedPool) -{ - RtlZeroMemory(Tree, sizeof(BINARY_TREE)); - - Tree->Compare = (Compare == NULL) - ? ExpBinaryTreeDefaultCompare : Compare; - - Tree->UseNonPagedPool = UseNonPagedPool; - - if (UseNonPagedPool) - { - ExInitializeNPagedLookasideList( - &Tree->List.NonPaged, /* Lookaside list */ - NULL, /* Allocate routine */ - NULL, /* Free routine */ - 0, /* Flags */ - sizeof(BINARY_TREE_NODE), /* Size of each entry */ - TAG('E','X','B','T'), /* Tag */ - 0); /* Depth */ - - KeInitializeSpinLock(&Tree->Lock.NonPaged); - } - else - { - ExInitializePagedLookasideList( - &Tree->List.Paged, /* Lookaside list */ - NULL, /* Allocate routine */ - NULL, /* Free routine */ - 0, /* Flags */ - sizeof(BINARY_TREE_NODE), /* Size of each entry */ - TAG('E','X','B','T'), /* Tag */ - 0); /* Depth */ - - ExInitializeFastMutex(&Tree->Lock.Paged); - } - - ExpBinaryTreeRootNode(Tree) = ExpCreateBinaryTreeNode(Tree, NULL, NULL); - - if (ExpBinaryTreeRootNode(Tree) == NULL) - { - if (UseNonPagedPool) - { - ExDeleteNPagedLookasideList(&Tree->List.NonPaged); - } - else - { - ExDeletePagedLookasideList(&Tree->List.Paged); - } - return FALSE; - } - else - { - return TRUE; - } -} - - -/* - * Release resources used by a binary tree. - */ -VOID STDCALL -ExDeleteBinaryTree(IN PBINARY_TREE Tree) -{ - /* Remove all nodes */ - ExpDeleteBinaryTree(Tree, ExpBinaryTreeRootNode(Tree)); - - if (Tree->UseNonPagedPool) - { - ExDeleteNPagedLookasideList(&Tree->List.NonPaged); - } - else - { - ExDeletePagedLookasideList(&Tree->List.Paged); - } -} - - -/* - * Insert a value in a binary tree. - */ -VOID STDCALL -ExInsertBinaryTree(IN PBINARY_TREE Tree, - IN PVOID Key, - IN PVOID Value) -{ - PBINARY_TREE_NODE Node; - KIRQL OldIrql; - - /* FIXME: Use SEH for error reporting */ - - ExpLockBinaryTree(Tree, &OldIrql); - Node = ExpBinaryTreeRootNode(Tree); - do - { - Node = ExpSearchBinaryTree(Tree, Key, Node); - - if (ExpBinaryTreeIsExternalNode(Node)) - { - break; - } - else - { - Node = ExpBinaryTreeRightChildNode(Node); - } - } while (TRUE); - ExpExpandExternalBinaryTreeNode(Tree, Node); - ExpBinaryTreeNodeKey(Node) = Key; - ExpBinaryTreeNodeValue(Node) = Value; - ExpUnlockBinaryTree(Tree, &OldIrql); -} - - -/* - * Search for a value associated with a given key in a binary tree. - */ -BOOLEAN STDCALL -ExSearchBinaryTree(IN PBINARY_TREE Tree, - IN PVOID Key, - OUT PVOID * Value) -{ - PBINARY_TREE_NODE Node; - KIRQL OldIrql; - - ExpLockBinaryTree(Tree, &OldIrql); - Node = ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeRootNode(Tree)); - - if (ExpBinaryTreeIsInternalNode(Node)) - { - *Value = ExpBinaryTreeNodeValue(Node); - ExpUnlockBinaryTree(Tree, &OldIrql); - return TRUE; - } - else - { - ExpUnlockBinaryTree(Tree, &OldIrql); - return FALSE; - } -} - - -/* - * Remove a value associated with a given key from a binary tree. - */ -BOOLEAN STDCALL -ExRemoveBinaryTree(IN PBINARY_TREE Tree, - IN PVOID Key, - IN PVOID * Value) -{ - PBINARY_TREE_NODE Node; - KIRQL OldIrql; - - ExpLockBinaryTree(Tree, &OldIrql); - - Node = ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeRootNode(Tree)); - - if (ExpBinaryTreeIsExternalNode(Node)) - { - ExpUnlockBinaryTree(Tree, &OldIrql); - return FALSE; - } - else - { - *Value = ExpBinaryTreeNodeValue(Node); - if (ExpBinaryTreeIsExternalNode(ExpBinaryTreeLeftChildNode(Node))) - { - Node = ExpBinaryTreeLeftChildNode(Node); - } - else if (ExpBinaryTreeIsExternalNode(ExpBinaryTreeRightChildNode(Node))) - { - Node = ExpBinaryTreeRightChildNode(Node); - } - else - { - // Node has internal children - PBINARY_TREE_NODE SwapNode; - - SwapNode = Node; - Node = ExpBinaryTreeRightChildNode(SwapNode); - do - { - Node = ExpBinaryTreeLeftChildNode(Node); - } while (ExpBinaryTreeIsInternalNode(Node)); - } - - ExpRemoveAboveExternalBinaryTreeNode(Tree, Node); - ExpUnlockBinaryTree(Tree, &OldIrql); - return TRUE; - } -} - - -/* - * Traverse a binary tree using either preorder, inorder or postorder - * traversal method. - * Returns FALSE, if the traversal was terminated prematurely or - * TRUE if the callback routine did not request that the traversal - * be terminated prematurely. - */ -BOOLEAN STDCALL -ExTraverseBinaryTree(IN PBINARY_TREE Tree, - IN TRAVERSE_METHOD Method, - IN PTRAVERSE_ROUTINE Routine, - IN PVOID Context) -{ - TRAVERSE_CONTEXT tc; - BOOLEAN Status; - KIRQL OldIrql; - - tc.Routine = Routine; - tc.Context = Context; - - ExpLockBinaryTree(Tree, &OldIrql); - - switch (Method) - { - case TraverseMethodPreorder: - Status = ExpTraverseBinaryTreePreorder(&tc, ExpBinaryTreeRootNode(Tree)); - break; - - case TraverseMethodInorder: - Status = ExpTraverseBinaryTreeInorder(&tc, ExpBinaryTreeRootNode(Tree)); - break; - - case TraverseMethodPostorder: - Status = ExpTraverseBinaryTreePostorder(&tc, ExpBinaryTreeRootNode(Tree)); - break; - - default: - Status = FALSE; - break; - } - - ExpUnlockBinaryTree(Tree, &OldIrql); - - return Status; -} - -/* EOF */ diff --git a/reactos/ntoskrnl/ex/error.c b/reactos/ntoskrnl/ex/error.c new file mode 100644 index 00000000000..d1d99a57751 --- /dev/null +++ b/reactos/ntoskrnl/ex/error.c @@ -0,0 +1,163 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/ex/error.c + * PURPOSE: Error Functions and Status/Exception Dispatching/Raising + * + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Created File + */ + +/* INCLUDES *****************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ****************************************************************/ + +BOOLEAN ExReadyForErrors = FALSE; +PEPORT ExpDefaultErrorPort = NULL; +PEPROCESS ExpDefaultErrorPortProcess = NULL; + +/* FUNCTIONS ****************************************************************/ + +/* + * @implemented + */ +VOID +STDCALL +ExRaiseAccessViolation(VOID) +{ + /* Raise the Right Status */ + ExRaiseStatus (STATUS_ACCESS_VIOLATION); +} + +/* + * @implemented + */ +VOID +STDCALL +ExRaiseDatatypeMisalignment (VOID) +{ + /* Raise the Right Status */ + ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT); +} + +/* + * @implemented + */ +VOID +STDCALL +ExRaiseStatus(IN NTSTATUS Status) +{ + EXCEPTION_RECORD ExceptionRecord; + + DPRINT("ExRaiseStatus(%x)\n", Status); + + /* Set up an Exception Record */ + ExceptionRecord.ExceptionRecord = NULL; + ExceptionRecord.NumberParameters = 0; + ExceptionRecord.ExceptionCode = Status; + ExceptionRecord.ExceptionFlags = 0; + + /* Call the Rtl Function */ + RtlRaiseException(&ExceptionRecord); +} + +/* + * @implemented + */ +VOID +STDCALL +ExRaiseException (PEXCEPTION_RECORD ExceptionRecord) +{ + /* Call the Rtl function */ + RtlRaiseException(ExceptionRecord); +} + +/* + * @implemented + */ +BOOLEAN +STDCALL +ExSystemExceptionFilter(VOID) +{ + return KeGetPreviousMode() != KernelMode ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} + +/* + * @unimplemented + */ +VOID +STDCALL +ExRaiseHardError(IN NTSTATUS ErrorStatus, + IN ULONG NumberOfParameters, + IN PUNICODE_STRING UnicodeStringParameterMask OPTIONAL, + IN PVOID *Parameters, + IN HARDERROR_RESPONSE_OPTION ResponseOption, + OUT PHARDERROR_RESPONSE Response) +{ + UNIMPLEMENTED; +} + +NTSTATUS +STDCALL +NtRaiseHardError(IN NTSTATUS ErrorStatus, + IN ULONG NumberOfParameters, + IN PUNICODE_STRING UnicodeStringParameterMask OPTIONAL, + IN PVOID *Parameters, + IN HARDERROR_RESPONSE_OPTION ResponseOption, + OUT PHARDERROR_RESPONSE Response) +{ + DPRINT1("Hard error %x\n", ErrorStatus); + + /* Call the Executive Function (WE SHOULD PUT SEH HERE/CAPTURE!) */ + ExRaiseHardError(ErrorStatus, + NumberOfParameters, + UnicodeStringParameterMask, + Parameters, + ResponseOption, + Response); + + /* Return Success */ + return STATUS_SUCCESS; +} + +NTSTATUS +STDCALL +NtSetDefaultHardErrorPort(IN HANDLE PortHandle) +{ + + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + /* Check if we have the Privilege */ + if(!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode)) { + + DPRINT1("NtSetDefaultHardErrorPort: Caller requires the SeTcbPrivilege privilege!\n"); + return STATUS_PRIVILEGE_NOT_HELD; + } + + /* Only called once during bootup, make sure we weren't called yet */ + if(!ExReadyForErrors) { + + Status = ObReferenceObjectByHandle(PortHandle, + 0, + LpcPortObjectType, + PreviousMode, + (PVOID*)&ExpDefaultErrorPort, + NULL); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Save the data */ + ExpDefaultErrorPortProcess = PsGetCurrentProcess(); + ExReadyForErrors = TRUE; + } + } + + return Status; +} + +/* EOF */ diff --git a/reactos/ntoskrnl/ex/event.c b/reactos/ntoskrnl/ex/event.c index 4db20f50ba5..0e52ef76240 100644 --- a/reactos/ntoskrnl/ex/event.c +++ b/reactos/ntoskrnl/ex/event.c @@ -1,11 +1,11 @@ -/* $Id:$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/nt/event.c * PURPOSE: Named event support * - * PROGRAMMERS: Philip Susi and David Welch + * PROGRAMMERS: Alex Ionescu(alex@relsoft.net) - Fixed bugs/commented + * Philip Susi and David Welch */ /* INCLUDES *****************************************************************/ @@ -19,531 +19,491 @@ POBJECT_TYPE EXPORTED ExEventObjectType = NULL; static GENERIC_MAPPING ExpEventMapping = { - STANDARD_RIGHTS_READ | SYNCHRONIZE | EVENT_QUERY_STATE, - STANDARD_RIGHTS_WRITE | SYNCHRONIZE | EVENT_MODIFY_STATE, - STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | EVENT_QUERY_STATE, - EVENT_ALL_ACCESS}; + STANDARD_RIGHTS_READ | SYNCHRONIZE | EVENT_QUERY_STATE, + STANDARD_RIGHTS_WRITE | SYNCHRONIZE | EVENT_MODIFY_STATE, + STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | EVENT_QUERY_STATE, + EVENT_ALL_ACCESS}; -static const INFORMATION_CLASS_INFO ExEventInfoClass[] = -{ - ICI_SQ_SAME( sizeof(EVENT_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* EventBasicInformation */ +static const INFORMATION_CLASS_INFO ExEventInfoClass[] = { + + /* EventBasicInformation */ + ICI_SQ_SAME( sizeof(EVENT_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY), }; /* FUNCTIONS *****************************************************************/ -NTSTATUS STDCALL -NtpCreateEvent(PVOID ObjectBody, - PVOID Parent, - PWSTR RemainingPath, - POBJECT_ATTRIBUTES ObjectAttributes) -{ - DPRINT("NtpCreateEvent(ObjectBody %x, Parent %x, RemainingPath %S)\n", - ObjectBody, Parent, RemainingPath); - - if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL) - { - return(STATUS_UNSUCCESSFUL); - } - - return(STATUS_SUCCESS); -} - - -VOID INIT_FUNCTION +VOID +INIT_FUNCTION ExpInitializeEventImplementation(VOID) { - ExEventObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE)); - - RtlpCreateUnicodeString(&ExEventObjectType->TypeName, L"Event", NonPagedPool); - - ExEventObjectType->Tag = TAG('E', 'V', 'T', 'T'); - ExEventObjectType->PeakObjects = 0; - ExEventObjectType->PeakHandles = 0; - ExEventObjectType->TotalObjects = 0; - ExEventObjectType->TotalHandles = 0; - ExEventObjectType->PagedPoolCharge = 0; - ExEventObjectType->NonpagedPoolCharge = sizeof(KEVENT); - ExEventObjectType->Mapping = &ExpEventMapping; - ExEventObjectType->Dump = NULL; - ExEventObjectType->Open = NULL; - ExEventObjectType->Close = NULL; - ExEventObjectType->Delete = NULL; - ExEventObjectType->Parse = NULL; - ExEventObjectType->Security = NULL; - ExEventObjectType->QueryName = NULL; - ExEventObjectType->OkayToClose = NULL; - ExEventObjectType->Create = NtpCreateEvent; - ExEventObjectType->DuplicationNotify = NULL; - - ObpCreateTypeObject(ExEventObjectType); + /* Create the Event Object Type */ + ExEventObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE)); + RtlInitUnicodeString(&ExEventObjectType->TypeName, L"Event"); + ExEventObjectType->Tag = TAG('E', 'V', 'T', 'T'); + ExEventObjectType->PeakObjects = 0; + ExEventObjectType->PeakHandles = 0; + ExEventObjectType->TotalObjects = 0; + ExEventObjectType->TotalHandles = 0; + ExEventObjectType->PagedPoolCharge = 0; + ExEventObjectType->NonpagedPoolCharge = sizeof(KEVENT); + ExEventObjectType->Mapping = &ExpEventMapping; + ExEventObjectType->Dump = NULL; + ExEventObjectType->Open = NULL; + ExEventObjectType->Close = NULL; + ExEventObjectType->Delete = NULL; + ExEventObjectType->Parse = NULL; + ExEventObjectType->Security = NULL; + ExEventObjectType->QueryName = NULL; + ExEventObjectType->OkayToClose = NULL; + ExEventObjectType->Create = NULL; + ExEventObjectType->DuplicationNotify = NULL; + ObpCreateTypeObject(ExEventObjectType); } - /* * @implemented */ -NTSTATUS STDCALL +NTSTATUS +STDCALL NtClearEvent(IN HANDLE EventHandle) { - PKEVENT Event; - NTSTATUS Status; + PKEVENT Event; + NTSTATUS Status; + + PAGED_CODE(); - PAGED_CODE(); + /* Reference the Object */ + Status = ObReferenceObjectByHandle(EventHandle, + EVENT_MODIFY_STATE, + ExEventObjectType, + ExGetPreviousMode(), + (PVOID*)&Event, + NULL); - Status = ObReferenceObjectByHandle(EventHandle, - EVENT_MODIFY_STATE, - ExEventObjectType, - ExGetPreviousMode(), - (PVOID*)&Event, - NULL); - if(NT_SUCCESS(Status)) - { - KeClearEvent(Event); - ObDereferenceObject(Event); - } + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Clear the Event and Dereference */ + KeClearEvent(Event); + ObDereferenceObject(Event); + } - return Status; + /* Return Status */ + return Status; } /* * @implemented */ -NTSTATUS STDCALL -NtCreateEvent(OUT PHANDLE EventHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN EVENT_TYPE EventType, - IN BOOLEAN InitialState) -{ - KPROCESSOR_MODE PreviousMode; - PKEVENT Event; - HANDLE hEvent; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(EventHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObCreateObject(PreviousMode, - ExEventObjectType, - ObjectAttributes, - PreviousMode, - NULL, - sizeof(KEVENT), - 0, - 0, - (PVOID*)&Event); - if(NT_SUCCESS(Status)) - { - KeInitializeEvent(Event, - EventType, - InitialState); - - - Status = ObInsertObject((PVOID)Event, - NULL, - DesiredAccess, - 0, - NULL, - &hEvent); - ObDereferenceObject(Event); - - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *EventHandle = hEvent; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtOpenEvent(OUT PHANDLE EventHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes) -{ - HANDLE hEvent; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - DPRINT("NtOpenEvent(0x%x, 0x%x, 0x%x)\n", EventHandle, DesiredAccess, ObjectAttributes); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(EventHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObOpenObjectByName(ObjectAttributes, - ExEventObjectType, - NULL, - PreviousMode, - DesiredAccess, - NULL, - &hEvent); - - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *EventHandle = hEvent; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtPulseEvent(IN HANDLE EventHandle, - OUT PLONG PreviousState OPTIONAL) -{ - PKEVENT Event; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - DPRINT("NtPulseEvent(EventHandle 0%x PreviousState 0%x)\n", - EventHandle, PreviousState); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousState != NULL && PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(PreviousState, - sizeof(LONG), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObReferenceObjectByHandle(EventHandle, - EVENT_MODIFY_STATE, - ExEventObjectType, - PreviousMode, - (PVOID*)&Event, - NULL); - if(NT_SUCCESS(Status)) - { - LONG Prev = KePulseEvent(Event, EVENT_INCREMENT, FALSE); - ObDereferenceObject(Event); - - if(PreviousState != NULL) - { - _SEH_TRY - { - *PreviousState = Prev; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtQueryEvent(IN HANDLE EventHandle, - IN EVENT_INFORMATION_CLASS EventInformationClass, - OUT PVOID EventInformation, - IN ULONG EventInformationLength, - OUT PULONG ReturnLength OPTIONAL) -{ - PKEVENT Event; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - DefaultQueryInfoBufferCheck(EventInformationClass, - ExEventInfoClass, - EventInformation, - EventInformationLength, - ReturnLength, - PreviousMode, - &Status); - if(!NT_SUCCESS(Status)) - { - DPRINT1("NtQueryEvent() failed, Status: 0x%x\n", Status); - return Status; - } - - Status = ObReferenceObjectByHandle(EventHandle, - EVENT_QUERY_STATE, - ExEventObjectType, - PreviousMode, - (PVOID*)&Event, - NULL); - if(NT_SUCCESS(Status)) - { - switch(EventInformationClass) - { - case EventBasicInformation: - { - PEVENT_BASIC_INFORMATION BasicInfo = (PEVENT_BASIC_INFORMATION)EventInformation; - - _SEH_TRY - { - if (Event->Header.Type == InternalNotificationEvent) - BasicInfo->EventType = NotificationEvent; - else - BasicInfo->EventType = SynchronizationEvent; - BasicInfo->EventState = KeReadStateEvent(Event); - - if(ReturnLength != NULL) - { - *ReturnLength = sizeof(EVENT_BASIC_INFORMATION); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - default: - Status = STATUS_NOT_IMPLEMENTED; - break; - } - - ObDereferenceObject(Event); - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtResetEvent(IN HANDLE EventHandle, - OUT PLONG PreviousState OPTIONAL) -{ - PKEVENT Event; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - DPRINT("NtResetEvent(EventHandle 0%x PreviousState 0%x)\n", - EventHandle, PreviousState); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousState != NULL && PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(PreviousState, - sizeof(LONG), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObReferenceObjectByHandle(EventHandle, - EVENT_MODIFY_STATE, - ExEventObjectType, - PreviousMode, - (PVOID*)&Event, - NULL); - if(NT_SUCCESS(Status)) - { - LONG Prev = KeResetEvent(Event); - ObDereferenceObject(Event); - - if(PreviousState != NULL) - { - _SEH_TRY - { - *PreviousState = Prev; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtSetEvent(IN HANDLE EventHandle, - OUT PLONG PreviousState OPTIONAL) -{ - PKEVENT Event; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - DPRINT("NtSetEvent(EventHandle 0%x PreviousState 0%x)\n", - EventHandle, PreviousState); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousState != NULL && PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(PreviousState, - sizeof(LONG), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObReferenceObjectByHandle(EventHandle, - EVENT_MODIFY_STATE, - ExEventObjectType, - PreviousMode, - (PVOID*)&Event, - NULL); - if(NT_SUCCESS(Status)) - { - LONG Prev = KeSetEvent(Event, EVENT_INCREMENT, FALSE); - ObDereferenceObject(Event); - - if(PreviousState != NULL) - { - _SEH_TRY - { - *PreviousState = Prev; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } - - return Status; -} - - -/* - * @unimplemented - */ -NTSTATUS +NTSTATUS STDCALL -NtTraceEvent( - IN ULONG TraceHandle, - IN ULONG Flags, - IN ULONG TraceHeaderLength, - IN struct _EVENT_TRACE_HEADER* TraceHeader - ) +NtCreateEvent(OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN EVENT_TYPE EventType, + IN BOOLEAN InitialState) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PKEVENT Event; + HANDLE hEvent; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + DPRINT("NtCreateEvent(0x%x, 0x%x, 0x%x)\n", EventHandle, DesiredAccess, ObjectAttributes); + + /* Check Output Safety */ + if(PreviousMode != KernelMode) { + + _SEH_TRY { + + ProbeForWrite(EventHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + /* Create the Object */ + Status = ObCreateObject(PreviousMode, + ExEventObjectType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(KEVENT), + 0, + 0, + (PVOID*)&Event); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Initalize the Event */ + KeInitializeEvent(Event, + EventType, + InitialState); + + /* Insert it */ + Status = ObInsertObject((PVOID)Event, + NULL, + DesiredAccess, + 0, + NULL, + &hEvent); + ObDereferenceObject(Event); + + /* Check for success and return handle */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + *EventHandle = hEvent; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } + + /* Return Status */ + return Status; } +/* + * @implemented + */ +NTSTATUS +STDCALL +NtOpenEvent(OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes) +{ + HANDLE hEvent; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + DPRINT("NtOpenEvent(0x%x, 0x%x, 0x%x)\n", EventHandle, DesiredAccess, ObjectAttributes); + + /* Check Output Safety */ + if(PreviousMode != KernelMode) { + + _SEH_TRY { + + ProbeForWrite(EventHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + /* Open the Object */ + Status = ObOpenObjectByName(ObjectAttributes, + ExEventObjectType, + NULL, + PreviousMode, + DesiredAccess, + NULL, + &hEvent); + + /* Check for success and return handle */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + *EventHandle = hEvent; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + + /* Return status */ + return Status; +} + +/* + * @implemented + */ +NTSTATUS +STDCALL +NtPulseEvent(IN HANDLE EventHandle, + OUT PLONG PreviousState OPTIONAL) +{ + PKEVENT Event; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + DPRINT("NtPulseEvent(EventHandle 0%x PreviousState 0%x)\n", + EventHandle, PreviousState); + + /* Check buffer validity */ + if(PreviousState && PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(PreviousState, + sizeof(LONG), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + /* Open the Object */ + Status = ObReferenceObjectByHandle(EventHandle, + EVENT_MODIFY_STATE, + ExEventObjectType, + PreviousMode, + (PVOID*)&Event, + NULL); + + /* Check for success */ + if(NT_SUCCESS(Status)) { + + /* Pulse the Event */ + LONG Prev = KePulseEvent(Event, EVENT_INCREMENT, FALSE); + ObDereferenceObject(Event); + + /* Return it */ + if(PreviousState) { + + _SEH_TRY { + + *PreviousState = Prev; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } + + /* Return Status */ + return Status; +} + + +/* + * @implemented + */ +NTSTATUS +STDCALL +NtQueryEvent(IN HANDLE EventHandle, + IN EVENT_INFORMATION_CLASS EventInformationClass, + OUT PVOID EventInformation, + IN ULONG EventInformationLength, + OUT PULONG ReturnLength OPTIONAL) +{ + PKEVENT Event; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + PEVENT_BASIC_INFORMATION BasicInfo = (PEVENT_BASIC_INFORMATION)EventInformation; + + PAGED_CODE(); + DPRINT("NtQueryEvent(0x%x, 0x%x, 0x%x)\n", EventHandle, EventInformationClass); + + /* Check buffers and class validity */ + DefaultQueryInfoBufferCheck(EventInformationClass, + ExEventInfoClass, + EventInformation, + EventInformationLength, + ReturnLength, + PreviousMode, + &Status); + if(!NT_SUCCESS(Status)) { + + /* Invalid buffers */ + DPRINT("NtQuerySemaphore() failed, Status: 0x%x\n", Status); + return Status; + } + + /* Get the Object */ + Status = ObReferenceObjectByHandle(EventHandle, + EVENT_QUERY_STATE, + ExEventObjectType, + PreviousMode, + (PVOID*)&Event, + NULL); + + /* Check for success */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + /* Return Event Type and State */ + BasicInfo->EventType = Event->Header.Type; + BasicInfo->EventState = KeReadStateEvent(Event); + + /* Return length */ + if(ReturnLength) *ReturnLength = sizeof(EVENT_BASIC_INFORMATION); + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + /* Dereference the Object */ + ObDereferenceObject(Event); + } + + /* Return status */ + return Status; +} + +/* + * @implemented + */ +NTSTATUS +STDCALL +NtResetEvent(IN HANDLE EventHandle, + OUT PLONG PreviousState OPTIONAL) +{ + PKEVENT Event; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + DPRINT("NtResetEvent(EventHandle 0%x PreviousState 0%x)\n", + EventHandle, PreviousState); + + /* Check buffer validity */ + if(PreviousState && PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(PreviousState, + sizeof(LONG), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + /* Open the Object */ + Status = ObReferenceObjectByHandle(EventHandle, + EVENT_MODIFY_STATE, + ExEventObjectType, + PreviousMode, + (PVOID*)&Event, + NULL); + + /* Check for success */ + if(NT_SUCCESS(Status)) { + + /* Reset the Event */ + LONG Prev = KeResetEvent(Event); + ObDereferenceObject(Event); + + /* Return it */ + if(PreviousState) { + + _SEH_TRY { + + *PreviousState = Prev; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } + + /* Return Status */ + return Status; +} + +/* + * @implemented + */ +NTSTATUS +STDCALL +NtSetEvent(IN HANDLE EventHandle, + OUT PLONG PreviousState OPTIONAL) +{ + PKEVENT Event; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + DPRINT("NtSetEvent(EventHandle 0%x PreviousState 0%x)\n", + EventHandle, PreviousState); + + /* Check buffer validity */ + if(PreviousState != NULL && PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(PreviousState, + sizeof(LONG), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + /* Open the Object */ + Status = ObReferenceObjectByHandle(EventHandle, + EVENT_MODIFY_STATE, + ExEventObjectType, + PreviousMode, + (PVOID*)&Event, + NULL); + + /* Check for success */ + if(NT_SUCCESS(Status)) { + + /* Set the Event */ + LONG Prev = KeSetEvent(Event, EVENT_INCREMENT, FALSE); + ObDereferenceObject(Event); + + /* Return it */ + if(PreviousState) { + + _SEH_TRY { + + *PreviousState = Prev; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } + + /* Return Status */ + return Status; +} /* EOF */ diff --git a/reactos/ntoskrnl/ex/evtpair.c b/reactos/ntoskrnl/ex/evtpair.c index 15b05e6bbc3..5bcfb7603ab 100644 --- a/reactos/ntoskrnl/ex/evtpair.c +++ b/reactos/ntoskrnl/ex/evtpair.c @@ -1,11 +1,12 @@ -/* $Id: evtpair.c 12779 2005-01-04 04:45:00Z gdalsnes $ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ex/evtpair.c * PURPOSE: Support for event pairs * - * PROGRAMMERS: David Welch (welch@mcmail.com) + * PROGRAMMERS: Alex Ionescu (Commented, reorganized, removed Thread Pair, used + * KeInitializeEventPair, added SEH) + * David Welch (welch@mcmail.com) * Skywing (skywing@valhallalegends.com) */ @@ -15,588 +16,404 @@ #define NDEBUG #include -#ifndef NTSYSAPI -#define NTSYSAPI -#endif - -#ifndef NTAPI -#define NTAPI STDCALL -#endif - - /* GLOBALS *******************************************************************/ POBJECT_TYPE EXPORTED ExEventPairObjectType = NULL; static GENERIC_MAPPING ExEventPairMapping = { - STANDARD_RIGHTS_READ, - STANDARD_RIGHTS_WRITE, - STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, - EVENT_PAIR_ALL_ACCESS}; + STANDARD_RIGHTS_READ, + STANDARD_RIGHTS_WRITE, + STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, + EVENT_PAIR_ALL_ACCESS}; -static KSPIN_LOCK ExThreadEventPairSpinLock; /* FUNCTIONS *****************************************************************/ -NTSTATUS STDCALL -ExpCreateEventPair(PVOID ObjectBody, - PVOID Parent, - PWSTR RemainingPath, - POBJECT_ATTRIBUTES ObjectAttributes) -{ - DPRINT("ExpCreateEventPair(ObjectBody %x, Parent %x, RemainingPath %S)\n", - ObjectBody, Parent, RemainingPath); - - if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL) - { - return(STATUS_UNSUCCESSFUL); - } - - return(STATUS_SUCCESS); -} - -VOID INIT_FUNCTION +VOID +INIT_FUNCTION ExpInitializeEventPairImplementation(VOID) { - ExEventPairObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE)); - - RtlpCreateUnicodeString(&ExEventPairObjectType->TypeName, L"EventPair", NonPagedPool); - ExEventPairObjectType->Tag = TAG('E', 'v', 'P', 'a'); - ExEventPairObjectType->PeakObjects = 0; - ExEventPairObjectType->PeakHandles = 0; - ExEventPairObjectType->TotalObjects = 0; - ExEventPairObjectType->TotalHandles = 0; - ExEventPairObjectType->PagedPoolCharge = 0; - ExEventPairObjectType->NonpagedPoolCharge = sizeof(KEVENT_PAIR); - ExEventPairObjectType->Mapping = &ExEventPairMapping; - ExEventPairObjectType->Dump = NULL; - ExEventPairObjectType->Open = NULL; - ExEventPairObjectType->Close = NULL; - ExEventPairObjectType->Delete = NULL; - ExEventPairObjectType->Parse = NULL; - ExEventPairObjectType->Security = NULL; - ExEventPairObjectType->QueryName = NULL; - ExEventPairObjectType->OkayToClose = NULL; - ExEventPairObjectType->Create = ExpCreateEventPair; - ExEventPairObjectType->DuplicationNotify = NULL; - - KeInitializeSpinLock(&ExThreadEventPairSpinLock); - ObpCreateTypeObject(ExEventPairObjectType); + /* Create the Event Pair Object Type */ + ExEventPairObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE)); + RtlInitUnicodeString(&ExEventPairObjectType->TypeName, L"EventPair"); + ExEventPairObjectType->Tag = TAG('E', 'v', 'P', 'a'); + ExEventPairObjectType->PeakObjects = 0; + ExEventPairObjectType->PeakHandles = 0; + ExEventPairObjectType->TotalObjects = 0; + ExEventPairObjectType->TotalHandles = 0; + ExEventPairObjectType->PagedPoolCharge = 0; + ExEventPairObjectType->NonpagedPoolCharge = sizeof(KEVENT_PAIR); + ExEventPairObjectType->Mapping = &ExEventPairMapping; + ExEventPairObjectType->Dump = NULL; + ExEventPairObjectType->Open = NULL; + ExEventPairObjectType->Close = NULL; + ExEventPairObjectType->Delete = NULL; + ExEventPairObjectType->Parse = NULL; + ExEventPairObjectType->Security = NULL; + ExEventPairObjectType->QueryName = NULL; + ExEventPairObjectType->OkayToClose = NULL; + ExEventPairObjectType->Create = NULL; + ExEventPairObjectType->DuplicationNotify = NULL; + ObpCreateTypeObject(ExEventPairObjectType); } - -NTSTATUS STDCALL +NTSTATUS +STDCALL NtCreateEventPair(OUT PHANDLE EventPairHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes) + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes) { - PKEVENT_PAIR EventPair; - HANDLE hEventPair; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + PKEVENT_PAIR EventPair; + HANDLE hEventPair; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + DPRINT("NtCreateEventPair: %x\n", EventPairHandle); - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(EventPairHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* Check Output Safety */ + if(PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(EventPairHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; - if(!NT_SUCCESS(Status)) - { - return Status; - } - } + if(!NT_SUCCESS(Status)) return Status; + } + + /* Create the Object */ + DPRINT("Creating EventPair\n"); + Status = ObCreateObject(PreviousMode, + ExEventPairObjectType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(KEVENT_PAIR), + 0, + 0, + (PVOID*)&EventPair); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Initalize the Event */ + DPRINT("Initializing EventPair\n"); + KeInitializeEventPair(EventPair); + + /* Insert it */ + Status = ObInsertObject((PVOID)EventPair, + NULL, + DesiredAccess, + 0, + NULL, + &hEventPair); + ObDereferenceObject(EventPair); + + /* Check for success and return handle */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + *EventPairHandle = hEventPair; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } - Status = ObCreateObject(PreviousMode, - ExEventPairObjectType, - ObjectAttributes, - PreviousMode, - NULL, - sizeof(KEVENT_PAIR), - 0, - 0, - (PVOID*)&EventPair); - if(NT_SUCCESS(Status)) - { - KeInitializeEvent(&EventPair->LowEvent, - SynchronizationEvent, - FALSE); - KeInitializeEvent(&EventPair->HighEvent, - SynchronizationEvent, - FALSE); - - Status = ObInsertObject ((PVOID)EventPair, - NULL, - DesiredAccess, - 0, - NULL, - &hEventPair); - ObDereferenceObject(EventPair); - - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *EventPairHandle = hEventPair; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } - return Status; + /* Return Status */ + return Status; } - -NTSTATUS STDCALL +NTSTATUS +STDCALL NtOpenEventPair(OUT PHANDLE EventPairHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes) + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes) { - HANDLE hEventPair; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + HANDLE hEventPair; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(EventPairHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* Check Output Safety */ + if(PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(EventPairHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObOpenObjectByName(ObjectAttributes, - ExEventPairObjectType, - NULL, - PreviousMode, - DesiredAccess, - NULL, - &hEventPair); - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *EventPairHandle = hEventPair; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } + if(!NT_SUCCESS(Status)) return Status; + } + + /* Open the Object */ + Status = ObOpenObjectByName(ObjectAttributes, + ExEventPairObjectType, + NULL, + PreviousMode, + DesiredAccess, + NULL, + &hEventPair); + + /* Check for success and return handle */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + *EventPairHandle = hEventPair; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } - return Status; + /* Return status */ + return Status; } -NTSTATUS STDCALL +NTSTATUS +STDCALL NtSetHighEventPair(IN HANDLE EventPairHandle) { - PKEVENT_PAIR EventPair; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; + PKEVENT_PAIR EventPair; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status; + + PAGED_CODE(); + DPRINT("NtSetHighEventPair(EventPairHandle %x)\n", EventPairHandle); - PAGED_CODE(); + /* Open the Object */ + Status = ObReferenceObjectByHandle(EventPairHandle, + SYNCHRONIZE, + ExEventPairObjectType, + PreviousMode, + (PVOID*)&EventPair, + NULL); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Set the Event */ + KeSetEvent(&EventPair->HighEvent, EVENT_INCREMENT, FALSE); - DPRINT("NtSetHighEventPair(EventPairHandle %x)\n", - EventPairHandle); - - PreviousMode = ExGetPreviousMode(); + /* Dereference Object */ + ObDereferenceObject(EventPair); + } - Status = ObReferenceObjectByHandle(EventPairHandle, - SYNCHRONIZE, - ExEventPairObjectType, - PreviousMode, - (PVOID*)&EventPair, - NULL); - if(NT_SUCCESS(Status)) - { - KeSetEvent(&EventPair->HighEvent, - EVENT_INCREMENT, - FALSE); - - ObDereferenceObject(EventPair); - } - - return Status; + /* Return status */ + return Status; } - -NTSTATUS STDCALL +NTSTATUS +STDCALL NtSetHighWaitLowEventPair(IN HANDLE EventPairHandle) { - PKEVENT_PAIR EventPair; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; + PKEVENT_PAIR EventPair; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status; + + PAGED_CODE(); + DPRINT("NtSetHighWaitLowEventPair(EventPairHandle %x)\n", EventPairHandle); + + /* Open the Object */ + Status = ObReferenceObjectByHandle(EventPairHandle, + SYNCHRONIZE, + ExEventPairObjectType, + PreviousMode, + (PVOID*)&EventPair, + NULL); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Set the Event */ + KeSetEvent(&EventPair->HighEvent, EVENT_INCREMENT, FALSE); + + /* Wait for the Other one */ + KeWaitForSingleObject(&EventPair->LowEvent, + WrEventPair, + PreviousMode, + FALSE, + NULL); + + /* Dereference Object */ + ObDereferenceObject(EventPair); + } - PAGED_CODE(); - - DPRINT("NtSetHighWaitLowEventPair(EventPairHandle %x)\n", - EventPairHandle); - - PreviousMode = ExGetPreviousMode(); - - Status = ObReferenceObjectByHandle(EventPairHandle, - SYNCHRONIZE, - ExEventPairObjectType, - PreviousMode, - (PVOID*)&EventPair, - NULL); - if(NT_SUCCESS(Status)) - { - KeSetEvent(&EventPair->HighEvent, - EVENT_INCREMENT, - TRUE); - - KeWaitForSingleObject(&EventPair->LowEvent, - WrEventPair, - PreviousMode, - FALSE, - NULL); - - ObDereferenceObject(EventPair); - } - - return Status; + /* Return status */ + return Status; } - -NTSTATUS STDCALL +NTSTATUS +STDCALL NtSetLowEventPair(IN HANDLE EventPairHandle) { - PKEVENT_PAIR EventPair; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; - - PAGED_CODE(); + PKEVENT_PAIR EventPair; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status; + + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); - DPRINT("NtSetLowEventPair(EventPairHandle %x)\n", - EventPairHandle); - - PreviousMode = ExGetPreviousMode(); + DPRINT1("NtSetHighEventPair(EventPairHandle %x)\n", EventPairHandle); - Status = ObReferenceObjectByHandle(EventPairHandle, - SYNCHRONIZE, - ExEventPairObjectType, - PreviousMode, - (PVOID*)&EventPair, - NULL); - if(NT_SUCCESS(Status)) - { - KeSetEvent(&EventPair->LowEvent, - EVENT_INCREMENT, - FALSE); + /* Open the Object */ + Status = ObReferenceObjectByHandle(EventPairHandle, + SYNCHRONIZE, + ExEventPairObjectType, + PreviousMode, + (PVOID*)&EventPair, + NULL); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Set the Event */ + KeSetEvent(&EventPair->LowEvent, EVENT_INCREMENT, FALSE); - ObDereferenceObject(EventPair); - } - - return Status; + /* Dereference Object */ + ObDereferenceObject(EventPair); + } + + /* Return status */ + return Status; } NTSTATUS STDCALL NtSetLowWaitHighEventPair(IN HANDLE EventPairHandle) { - PKEVENT_PAIR EventPair; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; + PKEVENT_PAIR EventPair; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status; + + PAGED_CODE(); + DPRINT("NtSetHighWaitLowEventPair(EventPairHandle %x)\n", EventPairHandle); + + /* Open the Object */ + Status = ObReferenceObjectByHandle(EventPairHandle, + SYNCHRONIZE, + ExEventPairObjectType, + PreviousMode, + (PVOID*)&EventPair, + NULL); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Set the Event */ + KeSetEvent(&EventPair->LowEvent, EVENT_INCREMENT, FALSE); + + /* Wait for the Other one */ + KeWaitForSingleObject(&EventPair->HighEvent, + WrEventPair, + PreviousMode, + FALSE, + NULL); + + /* Dereference Object */ + ObDereferenceObject(EventPair); + } - PAGED_CODE(); - - DPRINT("NtSetLowWaitHighEventPair(EventPairHandle %x)\n", - EventPairHandle); - - PreviousMode = ExGetPreviousMode(); - - Status = ObReferenceObjectByHandle(EventPairHandle, - SYNCHRONIZE, - ExEventPairObjectType, - PreviousMode, - (PVOID*)&EventPair, - NULL); - if(NT_SUCCESS(Status)) - { - KeSetEvent(&EventPair->LowEvent, - EVENT_INCREMENT, - TRUE); - - KeWaitForSingleObject(&EventPair->HighEvent, - WrEventPair, - PreviousMode, - FALSE, - NULL); - - ObDereferenceObject(EventPair); - } - - return Status; + /* Return status */ + return Status; } -NTSTATUS STDCALL +NTSTATUS +STDCALL NtWaitLowEventPair(IN HANDLE EventPairHandle) { - PKEVENT_PAIR EventPair; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; - - PAGED_CODE(); + PKEVENT_PAIR EventPair; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status; + + PAGED_CODE(); + DPRINT("NtSetHighWaitLowEventPair(EventPairHandle %x)\n", EventPairHandle); + + /* Open the Object */ + Status = ObReferenceObjectByHandle(EventPairHandle, + SYNCHRONIZE, + ExEventPairObjectType, + PreviousMode, + (PVOID*)&EventPair, + NULL); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Wait for the Event */ + KeWaitForSingleObject(&EventPair->LowEvent, + WrEventPair, + PreviousMode, + FALSE, + NULL); - DPRINT("NtWaitLowEventPair(EventPairHandle %x)\n", - EventPairHandle); - - PreviousMode = ExGetPreviousMode(); + /* Dereference Object */ + ObDereferenceObject(EventPair); + } - Status = ObReferenceObjectByHandle(EventPairHandle, - SYNCHRONIZE, - ExEventPairObjectType, - PreviousMode, - (PVOID*)&EventPair, - NULL); - if(NT_SUCCESS(Status)) - { - KeWaitForSingleObject(&EventPair->LowEvent, - WrEventPair, - PreviousMode, - FALSE, - NULL); - - ObDereferenceObject(EventPair); - } - - return Status; + /* Return status */ + return Status; } - -NTSTATUS STDCALL +NTSTATUS +STDCALL NtWaitHighEventPair(IN HANDLE EventPairHandle) { - PKEVENT_PAIR EventPair; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; + PKEVENT_PAIR EventPair; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status; + + PAGED_CODE(); + DPRINT("NtSetHighWaitLowEventPair(EventPairHandle %x)\n", EventPairHandle); + + /* Open the Object */ + Status = ObReferenceObjectByHandle(EventPairHandle, + SYNCHRONIZE, + ExEventPairObjectType, + PreviousMode, + (PVOID*)&EventPair, + NULL); + + /* Check for Success */ + if(NT_SUCCESS(Status)) { + + /* Wait for the Event */ + KeWaitForSingleObject(&EventPair->HighEvent, + WrEventPair, + PreviousMode, + FALSE, + NULL); + + /* Dereference Object */ + ObDereferenceObject(EventPair); + } - PAGED_CODE(); - - DPRINT("NtWaitHighEventPair(EventPairHandle %x)\n", - EventPairHandle); - - PreviousMode = ExGetPreviousMode(); - - Status = ObReferenceObjectByHandle(EventPairHandle, - SYNCHRONIZE, - ExEventPairObjectType, - PreviousMode, - (PVOID*)&EventPair, - NULL); - if(NT_SUCCESS(Status)) - { - KeWaitForSingleObject(&EventPair->HighEvent, - WrEventPair, - PreviousMode, - FALSE, - NULL); - - ObDereferenceObject(EventPair); - } - - return Status; + /* Return status */ + return Status; } -#ifdef _ENABLE_THRDEVTPAIR - -/* - * Author: Skywing (skywing@valhallalegends.com), 09/08/2003 - * Note that the eventpair spinlock must be acquired when setting the thread - * eventpair via NtSetInformationThread. - * @implemented - */ -NTSTATUS -NTSYSAPI -NTAPI -NtSetLowWaitHighThread( - VOID - ) -{ - PETHREAD Thread; - PKEVENT_PAIR EventPair; - NTSTATUS Status; - KIRQL Irql; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(!Thread->EventPair) - return STATUS_NO_EVENT_PAIR; - - KeAcquireSpinLock(&ExThreadEventPairSpinLock, &Irql); - - EventPair = Thread->EventPair; - - if(EventPair) - ObReferenceObjectByPointer(EventPair, - EVENT_PAIR_ALL_ACCESS, - ExEventPairObjectType, - UserMode); - - KeReleaseSpinLock(&ExThreadEventPairSpinLock, Irql); - - if(EventPair == NULL) - return STATUS_NO_EVENT_PAIR; - - KeSetEvent(&EventPair->LowEvent, - EVENT_INCREMENT, - TRUE); - - Status = KeWaitForSingleObject(&EventPair->HighEvent, - WrEventPair, - UserMode, - FALSE, - NULL); - - ObDereferenceObject(EventPair); - - return Status; -} - - -/* - * Author: Skywing (skywing@valhallalegends.com), 09/08/2003 - * Note that the eventpair spinlock must be acquired when setting the thread - * eventpair via NtSetInformationThread. - * @implemented - */ -NTSTATUS -NTSYSAPI -NTAPI -NtSetHighWaitLowThread( - VOID - ) -{ - PETHREAD Thread; - PKEVENT_PAIR EventPair; - NTSTATUS Status; - KIRQL Irql; - - PAGED_CODE(); - - Thread = PsGetCurrentThread(); - - if(!Thread->EventPair) - return STATUS_NO_EVENT_PAIR; - - KeAcquireSpinLock(&ExThreadEventPairSpinLock, &Irql); - - EventPair = PsGetCurrentThread()->EventPair; - - if(EventPair) - ObReferenceObjectByPointer(EventPair, - EVENT_PAIR_ALL_ACCESS, - ExEventPairObjectType, - UserMode); - - KeReleaseSpinLock(&ExThreadEventPairSpinLock, Irql); - - if(EventPair == NULL) - return STATUS_NO_EVENT_PAIR; - - KeSetEvent(&EventPair->HighEvent, - EVENT_INCREMENT, - TRUE); - - Status = KeWaitForSingleObject(&EventPair->LowEvent, - WrEventPair, - UserMode, - FALSE, - NULL); - - ObDereferenceObject(EventPair); - - return Status; -} - -/* - * Author: Skywing (skywing@valhallalegends.com), 09/08/2003 - * Note that the eventpair spinlock must be acquired when waiting on the - * eventpair via NtSetLow/HighWaitHigh/LowThread. Additionally, when - * deleting a thread object, NtpSwapThreadEventPair(Thread, NULL) should - * be called to release any preexisting eventpair object associated with - * the thread. The Microsoft name for this function is not known. - */ -VOID -ExpSwapThreadEventPair( - IN PETHREAD Thread, - IN PKEVENT_PAIR EventPair - ) -{ - PKEVENT_PAIR OriginalEventPair; - KIRQL Irql; - - KeAcquireSpinLock(&ExThreadEventPairSpinLock, &Irql); - - OriginalEventPair = Thread->EventPair; - Thread->EventPair = EventPair; - - if(OriginalEventPair) - ObDereferenceObject(OriginalEventPair); - - KeReleaseSpinLock(&ExThreadEventPairSpinLock, Irql); -} - -#else /* !_ENABLE_THRDEVTPAIR */ - -NTSTATUS -NTSYSAPI -NTAPI -NtSetLowWaitHighThread( - VOID - ) -{ - DPRINT1("NtSetLowWaitHighThread() not supported anymore (NT4 only)!\n"); - return STATUS_NOT_IMPLEMENTED; -} - -NTSTATUS -NTSYSAPI -NTAPI -NtSetHighWaitLowThread( - VOID - ) -{ - DPRINT1("NtSetHighWaitLowThread() not supported anymore (NT4 only)!\n"); - return STATUS_NOT_IMPLEMENTED; -} - -#endif /* _ENABLE_THRDEVTPAIR */ - /* EOF */ diff --git a/reactos/ntoskrnl/ex/handle.c b/reactos/ntoskrnl/ex/handle.c new file mode 100644 index 00000000000..1c0d9d39445 --- /dev/null +++ b/reactos/ntoskrnl/ex/handle.c @@ -0,0 +1,958 @@ +/* $Id$ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/ex/handle.c + * PURPOSE: Generic Executive Handle Tables + * + * PROGRAMMERS: Thomas Weidenmueller + * + * TODO: + * + * - the last entry of a subhandle list should be reserved for auditing + * + * ExSweepHandleTable (???) + * ExReferenceHandleDebugInfo + * ExSnapShotHandleTables + * ExpMoveFreeHandles (???) + */ + +/* INCLUDES *****************************************************************/ + +#include + +#define NDEBUG +#include + +static LIST_ENTRY ExpHandleTableHead; +static FAST_MUTEX ExpHandleTableListLock; +static LARGE_INTEGER ExpHandleShortWait; + +#define ExAcquireHandleTableListLock() \ + ExAcquireFastMutexUnsafe(&ExpHandleTableListLock) + +#define ExReleaseHandleTableListLock() \ + ExReleaseFastMutexUnsafe(&ExpHandleTableListLock) + +#define ExAcquireHandleTableLockExclusive(HandleTable) \ + ExAcquireResourceExclusiveLite(&(HandleTable)->HandleTableLock, TRUE) + +#define ExAcquireHandleTableLockShared(HandleTable) \ + ExAcquireResourceSharedLite(&(HandleTable)->HandleTableLock, TRUE) + +#define ExReleaseHandleTableLock(HandleTable) \ + ExReleaseResourceLite(&(HandleTable)->HandleTableLock) + +/* + 5 bits: reserved + 8 bits: top level index + 10 bits: middle level index + 9 bits: sub handle index +*/ +#define N_TLI_BITS 8 /* top level index */ +#define N_MLI_BITS 10 /* middle level index */ +#define N_EI_BITS 9 /* sub handle index */ +#define TLI_OFFSET (N_MLI_BITS + N_EI_BITS) +#define MLI_OFFSET N_EI_BITS +#define EI_OFFSET 0 + +#define N_TOPLEVEL_POINTERS (1 << N_TLI_BITS) +#define N_MIDDLELEVEL_POINTERS (1 << N_MLI_BITS) +#define N_SUBHANDLE_ENTRIES (1 << N_EI_BITS) +#define EX_MAX_HANDLES (N_TOPLEVEL_POINTERS * N_MIDDLELEVEL_POINTERS * N_SUBHANDLE_ENTRIES) + +#define VALID_HANDLE_MASK (((N_TOPLEVEL_POINTERS - 1) << TLI_OFFSET) | \ + ((N_MIDDLELEVEL_POINTERS - 1) << MLI_OFFSET) | ((N_SUBHANDLE_ENTRIES - 1) << EI_OFFSET)) +#define TLI_FROM_HANDLE(index) (ULONG)(((index) >> TLI_OFFSET) & (N_TOPLEVEL_POINTERS - 1)) +#define MLI_FROM_HANDLE(index) (ULONG)(((index) >> MLI_OFFSET) & (N_MIDDLELEVEL_POINTERS - 1)) +#define ELI_FROM_HANDLE(index) (ULONG)(((index) >> EI_OFFSET) & (N_SUBHANDLE_ENTRIES - 1)) + +#define N_MAX_HANDLE (N_TOPLEVEL_POINTERS * N_MIDDLELEVEL_POINTERS * N_SUBHANDLE_ENTRIES) + +#define BUILD_HANDLE(tli, mli, eli) ((((tli) & (N_TOPLEVEL_POINTERS - 1)) << TLI_OFFSET) | \ + (((mli) & (N_MIDDLELEVEL_POINTERS - 1)) << MLI_OFFSET) | (((eli) & (N_SUBHANDLE_ENTRIES - 1)) << EI_OFFSET)) + +#define IS_INVALID_EX_HANDLE(index) \ + (((index) & ~VALID_HANDLE_MASK) != 0) +#define IS_VALID_EX_HANDLE(index) \ + (((index) & ~VALID_HANDLE_MASK) == 0) + +static BOOLEAN ExpInitialized = FALSE; + +/******************************************************************************/ + +VOID +ExpInitializeHandleTables(VOID) +{ + ExpHandleShortWait.QuadPart = -50000; + InitializeListHead(&ExpHandleTableHead); + ExInitializeFastMutex(&ExpHandleTableListLock); + + ExpInitialized = TRUE; +} + +PHANDLE_TABLE +ExCreateHandleTable(IN PEPROCESS QuotaProcess OPTIONAL) +{ + PHANDLE_TABLE HandleTable; + + PAGED_CODE(); + + if(!ExpInitialized) + { + KEBUGCHECK(0); + } + + if(QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + /* allocate enough memory for the handle table and the lowest level */ + HandleTable = ExAllocatePoolWithTag(NonPagedPool, + sizeof(HANDLE_TABLE) + (N_TOPLEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY*)), + TAG('E', 'x', 'H', 't')); + if(HandleTable != NULL) + { + /* initialize the handle table */ + HandleTable->Flags = 0; + HandleTable->HandleCount = 0; + HandleTable->Table = (PHANDLE_TABLE_ENTRY**)(HandleTable + 1); + HandleTable->QuotaProcess = QuotaProcess; + HandleTable->FirstFreeTableEntry = -1; /* no entries freed so far */ + HandleTable->NextIndexNeedingPool = 0; /* no entries freed so far, so we have to allocate already for the first handle */ + HandleTable->UniqueProcessId = (QuotaProcess ? QuotaProcess->UniqueProcessId : NULL); + + ExInitializeResource(&HandleTable->HandleTableLock); + + KeInitializeEvent(&HandleTable->HandleContentionEvent, + NotificationEvent, + FALSE); + + RtlZeroMemory(HandleTable->Table, N_TOPLEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY*)); + + /* during bootup KeGetCurrentThread() might be NULL, needs to be fixed... */ + if(KeGetCurrentThread() != NULL) + { + /* insert it into the global handle table list */ + KeEnterCriticalRegion(); + + ExAcquireHandleTableListLock(); + InsertTailList(&ExpHandleTableHead, + &HandleTable->HandleTableList); + ExReleaseHandleTableListLock(); + + KeLeaveCriticalRegion(); + } + else + { + InsertTailList(&ExpHandleTableHead, + &HandleTable->HandleTableList); + } + } + else + { + /* FIXME - return the quota to the process */ + } + + return HandleTable; +} + +static BOOLEAN +ExLockHandleTableEntryNoDestructionCheck(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry) +{ + ULONG_PTR Current, New; + + PAGED_CODE(); + + DPRINT("Entering handle table entry 0x%x lock...\n", Entry); + + ASSERT(HandleTable); + ASSERT(Entry); + + for(;;) + { + Current = (volatile ULONG_PTR)Entry->u1.Object; + + if(!Current) + { + DPRINT("Attempted to lock empty handle table entry 0x%x or handle table shut down\n", Entry); + break; + } + + if(!(Current & EX_HANDLE_ENTRY_LOCKED)) + { + New = Current | EX_HANDLE_ENTRY_LOCKED; + if(InterlockedCompareExchangePointer(&Entry->u1.Object, + (PVOID)New, + (PVOID)Current) == (PVOID)Current) + { + DPRINT("SUCCESS handle table 0x%x entry 0x%x lock\n", HandleTable, Entry); + /* we acquired the lock */ + return TRUE; + } + } + + /* wait about 5ms at maximum so we don't wait forever in unfortunate + co-incidences where releasing the lock in another thread happens right + before we're waiting on the contention event to get pulsed, which might + never happen again... */ + KeWaitForSingleObject(&HandleTable->HandleContentionEvent, + Executive, + KernelMode, + FALSE, + &ExpHandleShortWait); + } + + return FALSE; +} + +VOID +ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable, + IN PEX_DESTROY_HANDLE_CALLBACK DestroyHandleCallback OPTIONAL, + IN PVOID Context OPTIONAL) +{ + PHANDLE_TABLE_ENTRY **tlp, **lasttlp, *mlp, *lastmlp; + PEPROCESS QuotaProcess; + + PAGED_CODE(); + + ASSERT(HandleTable); + + KeEnterCriticalRegion(); + + /* ensure there's no other operations going by acquiring an exclusive lock */ + ExAcquireHandleTableLockExclusive(HandleTable); + + ASSERT(!(HandleTable->Flags & EX_HANDLE_TABLE_CLOSING)); + + HandleTable->Flags |= EX_HANDLE_TABLE_CLOSING; + + KePulseEvent(&HandleTable->HandleContentionEvent, + EVENT_INCREMENT, + FALSE); + + /* remove the handle table from the global handle table list */ + ExAcquireHandleTableListLock(); + RemoveEntryList(&HandleTable->HandleTableList); + ExReleaseHandleTableListLock(); + + /* call the callback function to cleanup the objects associated with the + handle table */ + if(DestroyHandleCallback != NULL) + { + for(tlp = HandleTable->Table, lasttlp = HandleTable->Table + N_TOPLEVEL_POINTERS; + tlp != lasttlp; + tlp++) + { + if((*tlp) != NULL) + { + for(mlp = *tlp, lastmlp = (*tlp) + N_MIDDLELEVEL_POINTERS; + mlp != lastmlp; + mlp++) + { + if((*mlp) != NULL) + { + PHANDLE_TABLE_ENTRY curee, laste; + + for(curee = *mlp, laste = *mlp + N_SUBHANDLE_ENTRIES; + curee != laste; + curee++) + { + if(curee->u1.Object != NULL && ExLockHandleTableEntryNoDestructionCheck(HandleTable, curee)) + { + DestroyHandleCallback(HandleTable, curee->u1.Object, curee->u2.GrantedAccess, Context); + ExUnlockHandleTableEntry(HandleTable, curee); + } + } + } + } + } + } + } + + QuotaProcess = HandleTable->QuotaProcess; + + /* free the tables */ + for(tlp = HandleTable->Table, lasttlp = HandleTable->Table + N_TOPLEVEL_POINTERS; + tlp != lasttlp; + tlp++) + { + if((*tlp) != NULL) + { + for(mlp = *tlp, lastmlp = (*tlp) + N_MIDDLELEVEL_POINTERS; + mlp != lastmlp; + mlp++) + { + if((*mlp) != NULL) + { + ExFreePool(*mlp); + + if(QuotaProcess != NULL) + { + /* FIXME - return the quota to the process */ + } + } + } + + ExFreePool(*tlp); + + if(QuotaProcess != NULL) + { + /* FIXME - return the quota to the process */ + } + } + } + + ExReleaseHandleTableLock(HandleTable); + + KeLeaveCriticalRegion(); + + /* free the handle table */ + ExDeleteResource(&HandleTable->HandleTableLock); + ExFreePool(HandleTable); + + if(QuotaProcess != NULL) + { + /* FIXME - return the quota to the process */ + } +} + +PHANDLE_TABLE +ExDupHandleTable(IN PEPROCESS QuotaProcess OPTIONAL, + IN PEX_DUPLICATE_HANDLE_CALLBACK DuplicateHandleCallback OPTIONAL, + IN PVOID Context OPTIONAL, + IN PHANDLE_TABLE SourceHandleTable) +{ + PHANDLE_TABLE HandleTable; + + PAGED_CODE(); + + ASSERT(SourceHandleTable); + + HandleTable = ExCreateHandleTable(QuotaProcess); + if(HandleTable != NULL) + { + PHANDLE_TABLE_ENTRY **tlp, **srctlp, **etlp, *mlp, *srcmlp, *emlp, stbl, srcstbl, estbl; + LONG tli, mli, eli; + + tli = mli = eli = 0; + + /* make sure the other handle table isn't being changed during the duplication */ + ExAcquireHandleTableLockShared(SourceHandleTable); + + /* allocate enough tables */ + etlp = SourceHandleTable->Table + N_TOPLEVEL_POINTERS; + for(srctlp = SourceHandleTable->Table, tlp = HandleTable->Table; + srctlp != etlp; + srctlp++, tlp++) + { + if(*srctlp != NULL) + { + /* allocate middle level entry tables */ + if(QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + *tlp = ExAllocatePoolWithTag(PagedPool, + N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY), + TAG('E', 'x', 'H', 't')); + if(*tlp != NULL) + { + RtlZeroMemory(*tlp, N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY)); + + KeMemoryBarrier(); + + emlp = *srctlp + N_MIDDLELEVEL_POINTERS; + for(srcmlp = *srctlp, mlp = *tlp; + srcmlp != emlp; + srcmlp++, mlp++) + { + if(*srcmlp != NULL) + { + /* allocate subhandle tables */ + if(QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + *mlp = ExAllocatePoolWithTag(PagedPool, + N_SUBHANDLE_ENTRIES * sizeof(HANDLE_TABLE_ENTRY), + TAG('E', 'x', 'H', 't')); + if(*mlp != NULL) + { + RtlZeroMemory(*mlp, N_SUBHANDLE_ENTRIES * sizeof(HANDLE_TABLE_ENTRY)); + } + else + { + goto freehandletable; + } + } + else + { + *mlp = NULL; + } + } + } + else + { +freehandletable: + DPRINT1("Failed to duplicate handle table 0x%x\n", SourceHandleTable); + + ExReleaseHandleTableLock(SourceHandleTable); + + ExDestroyHandleTable(HandleTable, + NULL, + NULL); + /* allocate an empty handle table */ + return ExCreateHandleTable(QuotaProcess); + } + } + } + + /* duplicate the handles */ + HandleTable->HandleCount = SourceHandleTable->HandleCount; + HandleTable->FirstFreeTableEntry = SourceHandleTable->FirstFreeTableEntry; + HandleTable->NextIndexNeedingPool = SourceHandleTable->NextIndexNeedingPool; + + /* make sure all tables are zeroed */ + KeMemoryBarrier(); + + etlp = SourceHandleTable->Table + N_TOPLEVEL_POINTERS; + for(srctlp = SourceHandleTable->Table, tlp = HandleTable->Table; + srctlp != etlp; + srctlp++, tlp++, tli++) + { + if(*srctlp != NULL) + { + ASSERT(*tlp != NULL); + + emlp = *srctlp + N_MIDDLELEVEL_POINTERS; + for(srcmlp = *srctlp, mlp = *tlp; + srcmlp != emlp; + srcmlp++, mlp++, mli++) + { + if(*srcmlp != NULL) + { + ASSERT(*mlp != NULL); + + /* walk all handle entries and duplicate them if wanted */ + estbl = *srcmlp + N_SUBHANDLE_ENTRIES; + for(srcstbl = *srcmlp, stbl = *mlp; + srcstbl != estbl; + srcstbl++, stbl++, eli++) + { + /* try to duplicate the source handle */ + if(srcstbl->u1.Object != NULL && + ExLockHandleTableEntry(SourceHandleTable, + srcstbl)) + { + /* ask the caller if this handle should be duplicated */ + if(DuplicateHandleCallback != NULL && + !DuplicateHandleCallback(HandleTable, + srcstbl, + Context)) + { + /* free the entry and chain it into the free list */ + HandleTable->HandleCount--; + stbl->u1.Object = NULL; + stbl->u2.NextFreeTableEntry = HandleTable->FirstFreeTableEntry; + HandleTable->FirstFreeTableEntry = BUILD_HANDLE(tli, mli, eli); + } + else + { + /* duplicate the handle and unlock it */ + stbl->u2.GrantedAccess = srcstbl->u2.GrantedAccess; + stbl->u1.ObAttributes = srcstbl->u1.ObAttributes & ~EX_HANDLE_ENTRY_LOCKED; + } + ExUnlockHandleTableEntry(SourceHandleTable, + srcstbl); + } + else + { + /* this is a free handle table entry, copy over the entire + structure as-is */ + *stbl = *srcstbl; + } + } + } + } + } + } + + /* release the source handle table */ + ExReleaseHandleTableLock(SourceHandleTable); + } + + return HandleTable; +} + +static PHANDLE_TABLE_ENTRY +ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable, + OUT PLONG Handle) +{ + PHANDLE_TABLE_ENTRY Entry = NULL; + + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Handle); + ASSERT(KeGetCurrentThread() != NULL); + + DPRINT("HT[0x%x]: HandleCount: %d\n", HandleTable, HandleTable->HandleCount); + + if(HandleTable->HandleCount < EX_MAX_HANDLES) + { + ULONG tli, mli, eli; + + if(HandleTable->FirstFreeTableEntry != -1) + { + /* there's a free handle entry we can just grab and use */ + tli = TLI_FROM_HANDLE(HandleTable->FirstFreeTableEntry); + mli = MLI_FROM_HANDLE(HandleTable->FirstFreeTableEntry); + eli = ELI_FROM_HANDLE(HandleTable->FirstFreeTableEntry); + + /* the pointer should be valid in any way!!! */ + ASSERT(HandleTable->Table[tli]); + ASSERT(HandleTable->Table[tli][mli]); + + Entry = &HandleTable->Table[tli][mli][eli]; + + *Handle = HandleTable->FirstFreeTableEntry; + + /* save the index to the next free handle (if available) */ + HandleTable->FirstFreeTableEntry = Entry->u2.NextFreeTableEntry; + Entry->u2.NextFreeTableEntry = 0; + Entry->u1.Object = NULL; + + HandleTable->HandleCount++; + } + else + { + /* we need to allocate a new subhandle table first */ + PHANDLE_TABLE_ENTRY cure, laste, ntbl, *nmtbl; + ULONG i; + BOOLEAN AllocatedMtbl; + + ASSERT(HandleTable->NextIndexNeedingPool <= N_MAX_HANDLE); + + /* the index of the next table to be allocated was saved in + NextIndexNeedingPool the last time a handle entry was allocated and + the subhandle entry list was full. the subhandle entry index of + NextIndexNeedingPool should be 0 here! */ + tli = TLI_FROM_HANDLE(HandleTable->NextIndexNeedingPool); + mli = MLI_FROM_HANDLE(HandleTable->NextIndexNeedingPool); + DPRINT("HandleTable->NextIndexNeedingPool: 0x%x\n", HandleTable->NextIndexNeedingPool); + DPRINT("tli: 0x%x mli: 0x%x eli: 0x%x\n", tli, mli, ELI_FROM_HANDLE(HandleTable->NextIndexNeedingPool)); + + ASSERT(ELI_FROM_HANDLE(HandleTable->NextIndexNeedingPool) == 0); + + DPRINT("HandleTable->Table[%d] == 0x%x\n", tli, HandleTable->Table[tli]); + + /* allocate a middle level entry list if required */ + nmtbl = HandleTable->Table[tli]; + if(nmtbl == NULL) + { + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + nmtbl = ExAllocatePoolWithTag(PagedPool, + N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY), + TAG('E', 'x', 'H', 't')); + if(nmtbl == NULL) + { + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - return the quota to the process */ + } + + return NULL; + } + + /* clear the middle level entry list */ + RtlZeroMemory(nmtbl, N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY)); + + /* make sure the table was zeroed before we set one item */ + KeMemoryBarrier(); + + /* note, don't set the the pointer in the top level list yet because we + might screw up lookups if allocating a subhandle entry table failed + and this newly allocated table might get freed again */ + AllocatedMtbl = TRUE; + } + else + { + AllocatedMtbl = FALSE; + + /* allocate a subhandle entry table in any case! */ + ASSERT(nmtbl[mli] == NULL); + } + + DPRINT("HandleTable->Table[%d][%d] == 0x%x\n", tli, mli, nmtbl[mli]); + + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + ntbl = ExAllocatePoolWithTag(PagedPool, + N_SUBHANDLE_ENTRIES * sizeof(HANDLE_TABLE_ENTRY), + TAG('E', 'x', 'H', 't')); + if(ntbl == NULL) + { + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - Return process quota charged */ + } + + /* free the middle level entry list, if allocated, because it's empty and + unused */ + if(AllocatedMtbl) + { + ExFreePool(nmtbl); + + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - Return process quota charged */ + } + } + + return NULL; + } + + /* let's just use the very first entry */ + Entry = ntbl; + Entry->u1.ObAttributes = EX_HANDLE_ENTRY_LOCKED; + Entry->u2.NextFreeTableEntry = 0; + + *Handle = HandleTable->NextIndexNeedingPool; + + HandleTable->HandleCount++; + + /* set the FirstFreeTableEntry member to the second entry and chain the + free entries */ + HandleTable->FirstFreeTableEntry = HandleTable->NextIndexNeedingPool + 1; + for(cure = Entry + 1, laste = Entry + N_SUBHANDLE_ENTRIES, i = HandleTable->FirstFreeTableEntry + 1; + cure != laste; + cure++, i++) + { + cure->u1.Object = NULL; + cure->u2.NextFreeTableEntry = i; + } + /* truncate the free entry list */ + (cure - 1)->u2.NextFreeTableEntry = -1; + + /* save the pointers to the allocated list(s) */ + InterlockedExchangePointer(&nmtbl[mli], ntbl); + if(AllocatedMtbl) + { + InterlockedExchangePointer(&HandleTable->Table[tli], nmtbl); + } + + /* increment the NextIndexNeedingPool to the next index where we need to + allocate new memory */ + HandleTable->NextIndexNeedingPool += N_SUBHANDLE_ENTRIES; + } + } + else + { + DPRINT1("Can't allocate any more handles in handle table 0x%x!\n", HandleTable); + } + + return Entry; +} + +static VOID +ExpFreeHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry, + IN LONG Handle) +{ + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Entry); + ASSERT(IS_VALID_EX_HANDLE(Handle)); + + DPRINT("ExpFreeHandleTableEntry HT:0x%x Entry:0x%x\n", HandleTable, Entry); + + /* automatically unlock the entry if currently locked. We however don't notify + anyone who waited on the handle because we're holding an exclusive lock after + all and these locks will fail then */ + InterlockedExchangePointer(&Entry->u1.Object, NULL); + Entry->u2.NextFreeTableEntry = HandleTable->FirstFreeTableEntry; + HandleTable->FirstFreeTableEntry = Handle; + + HandleTable->HandleCount--; +} + +static PHANDLE_TABLE_ENTRY +ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN LONG Handle) +{ + PHANDLE_TABLE_ENTRY Entry = NULL; + + PAGED_CODE(); + + ASSERT(HandleTable); + + if(IS_VALID_EX_HANDLE(Handle)) + { + ULONG tli, mli, eli; + PHANDLE_TABLE_ENTRY *mlp; + + tli = TLI_FROM_HANDLE(Handle); + mli = MLI_FROM_HANDLE(Handle); + eli = ELI_FROM_HANDLE(Handle); + + mlp = HandleTable->Table[tli]; + if(Handle < HandleTable->NextIndexNeedingPool && + mlp != NULL && mlp[mli] != NULL && mlp[mli][eli].u1.Object != NULL) + { + Entry = &mlp[mli][eli]; + DPRINT("handle lookup 0x%x -> entry 0x%x [HT:0x%x] ptr: 0x%x\n", Handle, Entry, HandleTable, mlp[mli][eli].u1.Object); + } + } + else + { + DPRINT("Looking up invalid handle 0x%x\n", Handle); + } + + return Entry; +} + +BOOLEAN +ExLockHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry) +{ + ULONG_PTR Current, New; + + PAGED_CODE(); + + DPRINT("Entering handle table entry 0x%x lock...\n", Entry); + + ASSERT(HandleTable); + ASSERT(Entry); + + for(;;) + { + Current = (volatile ULONG_PTR)Entry->u1.Object; + + if(!Current || (HandleTable->Flags & EX_HANDLE_TABLE_CLOSING)) + { + DPRINT("Attempted to lock empty handle table entry 0x%x or handle table shut down\n", Entry); + break; + } + + if(!(Current & EX_HANDLE_ENTRY_LOCKED)) + { + New = Current | EX_HANDLE_ENTRY_LOCKED; + if(InterlockedCompareExchangePointer(&Entry->u1.Object, + (PVOID)New, + (PVOID)Current) == (PVOID)Current) + { + DPRINT("SUCCESS handle table 0x%x entry 0x%x lock\n", HandleTable, Entry); + /* we acquired the lock */ + return TRUE; + } + } + + /* wait about 5ms at maximum so we don't wait forever in unfortunate + co-incidences where releasing the lock in another thread happens right + before we're waiting on the contention event to get pulsed, which might + never happen again... */ + KeWaitForSingleObject(&HandleTable->HandleContentionEvent, + Executive, + KernelMode, + FALSE, + &ExpHandleShortWait); + } + + return FALSE; +} + +VOID +ExUnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry) +{ + ULONG_PTR Current, New; + + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Entry); + + DPRINT("ExUnlockHandleTableEntry HT:0x%x Entry:0x%x\n", HandleTable, Entry); + + Current = (volatile ULONG_PTR)Entry->u1.Object; + + ASSERT(Current & EX_HANDLE_ENTRY_LOCKED); + + New = Current & ~EX_HANDLE_ENTRY_LOCKED; + + InterlockedExchangePointer(&Entry->u1.Object, + (PVOID)New); + + /* we unlocked the entry, pulse the contention event so threads who're waiting + on the release can continue */ + KePulseEvent(&HandleTable->HandleContentionEvent, + EVENT_INCREMENT, + FALSE); +} + +LONG +ExCreateHandle(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry) +{ + PHANDLE_TABLE_ENTRY NewHandleTableEntry; + LONG Handle = EX_INVALID_HANDLE; + + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Entry); + + /* The highest bit in Entry->u1.Object has to be 1 so we make sure it's a + pointer to kmode memory. It will cleared though because it also indicates + the lock */ + ASSERT((ULONG_PTR)Entry->u1.Object & EX_HANDLE_ENTRY_LOCKED); + + KeEnterCriticalRegion(); + ExAcquireHandleTableLockExclusive(HandleTable); + + NewHandleTableEntry = ExpAllocateHandleTableEntry(HandleTable, + &Handle); + if(NewHandleTableEntry != NULL) + { + *NewHandleTableEntry = *Entry; + + ExUnlockHandleTableEntry(HandleTable, + NewHandleTableEntry); + } + + ExReleaseHandleTableLock(HandleTable); + KeLeaveCriticalRegion(); + + return Handle; +} + +BOOLEAN +ExDestroyHandle(IN PHANDLE_TABLE HandleTable, + IN LONG Handle) +{ + PHANDLE_TABLE_ENTRY HandleTableEntry; + BOOLEAN Ret = FALSE; + + PAGED_CODE(); + + ASSERT(HandleTable); + + KeEnterCriticalRegion(); + ExAcquireHandleTableLockExclusive(HandleTable); + + HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, + Handle); + + if(HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry)) + { + /* free and automatically unlock the handle. However we don't need to pulse + the contention event since other locks on this entry will fail */ + ExpFreeHandleTableEntry(HandleTable, + HandleTableEntry, + Handle); + Ret = TRUE; + } + + ExReleaseHandleTableLock(HandleTable); + KeLeaveCriticalRegion(); + + return Ret; +} + +VOID +ExDestroyHandleByEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry, + IN LONG Handle) +{ + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Entry); + + /* This routine requires the entry to be locked */ + ASSERT((ULONG_PTR)Entry->u1.Object & EX_HANDLE_ENTRY_LOCKED); + + DPRINT("DestroyHandleByEntry HT:0x%x Entry:0x%x\n", HandleTable, Entry); + + KeEnterCriticalRegion(); + ExAcquireHandleTableLockExclusive(HandleTable); + + /* free and automatically unlock the handle. However we don't need to pulse + the contention event since other locks on this entry will fail */ + ExpFreeHandleTableEntry(HandleTable, + Entry, + Handle); + + ExReleaseHandleTableLock(HandleTable); + KeLeaveCriticalRegion(); +} + +PHANDLE_TABLE_ENTRY +ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable, + IN LONG Handle) +{ + PHANDLE_TABLE_ENTRY HandleTableEntry; + + PAGED_CODE(); + + ASSERT(HandleTable); + + HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, + Handle); + if (HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry)) + { + DPRINT("ExMapHandleToPointer HT:0x%x Entry:0x%x locked\n", HandleTable, HandleTableEntry); + return HandleTableEntry; + } + + return NULL; +} + +BOOLEAN +ExChangeHandle(IN PHANDLE_TABLE HandleTable, + IN LONG Handle, + IN PEX_CHANGE_HANDLE_CALLBACK ChangeHandleCallback, + IN PVOID Context) +{ + PHANDLE_TABLE_ENTRY HandleTableEntry; + BOOLEAN Ret = FALSE; + + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(ChangeHandleCallback); + + KeEnterCriticalRegion(); + + HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, + Handle); + + if(HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry)) + { + Ret = ChangeHandleCallback(HandleTable, + HandleTableEntry, + NULL); + + ExUnlockHandleTableEntry(HandleTable, + HandleTableEntry); + } + + KeLeaveCriticalRegion(); + + return Ret; +} + +/* EOF */ diff --git a/reactos/ntoskrnl/ex/hashtab.c b/reactos/ntoskrnl/ex/hashtab.c deleted file mode 100644 index 82954c1767c..00000000000 --- a/reactos/ntoskrnl/ex/hashtab.c +++ /dev/null @@ -1,306 +0,0 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ex/hashtab.c - * PURPOSE: Hash table support - * - * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) - */ - -/* - * NOTES: The hash function is from: - * Bob Jenkins - * http://burtleburtle.net/bob/hash/doobs.html - */ - -#include - -#define NDEBUG -#include - -/* FUNCTIONS ****************************************************************/ - -#define ExpHashTableSize(n) ((ULONG)1 << (n)) -#define ExpHashTableMask(n) (ExpHashTableSize(n) - 1) - -#define ExpHashMix(a, b, c) \ -{ \ - a -= b; a -= c; a ^= (c >> 13); \ - b -= c; b -= a; b ^= (a << 8); \ - c -= a; c -= b; c ^= (b >> 13); \ - a -= b; a -= c; a ^= (c >> 12); \ - b -= c; b -= a; b ^= (a << 16); \ - c -= a; c -= b; c ^= (b >> 5); \ - a -= b; a -= c; a ^= (c >> 3); \ - b -= c; b -= a; b ^= (a << 10); \ - c -= a; c -= b; c ^= (b >> 15); \ -} - - -ULONG -ExpHash(PUCHAR Key, - ULONG KeyLength) -{ - register ULONG a, b, c, len; - - /* Set up the internal state */ - len = KeyLength; - a = b = 0x9e3779b9; /* The golden ratio; an arbitrary value */ - c = 0; - - /* Handle most of the key */ - while (len >= 12) - { - a += (Key[0] + ((ULONG)Key[1]<<8) + ((ULONG)Key[2]<<16) + ((ULONG)Key[3]<<24)); - b += (Key[4] + ((ULONG)Key[5]<<8) + ((ULONG)Key[6]<<16) + ((ULONG)Key[7]<<24)); - c += (Key[8] + ((ULONG)Key[9]<<8) + ((ULONG)Key[10]<<16)+ ((ULONG)Key[11]<<24)); - ExpHashMix(a, b, c); - Key += 12; len -= 12; - } - - /* Handle the last 11 bytes */ - c += KeyLength; - switch(len) /* All the case statements fall through */ - { - case 11: c += ((ULONG)Key[10] << 24); - case 10: c += ((ULONG)Key[9] << 16); - case 9 : c += ((ULONG)Key[8] << 8); - /* The first byte of c is reserved for the length */ - case 8 : b += ((ULONG)Key[7] << 24); - case 7 : b += ((ULONG)Key[6] << 16); - case 6 : b += ((ULONG)Key[5] << 8); - case 5 : b += Key[4]; - case 4 : a += ((ULONG)Key[3] << 24); - case 3 : a += ((ULONG)Key[2] << 16); - case 2 : a += ((ULONG)Key[1] << 8); - case 1 : a += Key[0]; - /* case 0: nothing left to add */ - } - - ExpHashMix(a, b, c); - - return c; -} - - -/* - * Lock the hash table - */ -inline VOID -ExpLockHashTable(PHASH_TABLE HashTable, - PKIRQL OldIrql) -{ - if (HashTable->UseNonPagedPool) - { - KeAcquireSpinLock(&HashTable->Lock.NonPaged, OldIrql); - } - else - { - ExAcquireFastMutex(&HashTable->Lock.Paged); - } -} - - -/* - * Unlock the hash table - */ -inline VOID -ExpUnlockHashTable(PHASH_TABLE HashTable, - PKIRQL OldIrql) -{ - if (HashTable->UseNonPagedPool) - { - KeReleaseSpinLock(&HashTable->Lock.NonPaged, *OldIrql); - } - else - { - ExReleaseFastMutex(&HashTable->Lock.Paged); - } -} - - -/* - * Insert a value in a hash table. - */ -inline VOID STDCALL -ExpInsertHashTable(IN PHASH_TABLE HashTable, - IN PVOID Key, - IN ULONG KeyLength, - IN PVOID Value) -{ - ULONG Index; - - Index = (ExpHash(Key, KeyLength) & ExpHashTableMask(HashTable->HashTableSize)); - - ExInsertSplayTree(&HashTable->HashTrees[Index], Key, Value); -} - - -/* - * Search for a value associated with a given key in a hash table. - */ -inline BOOLEAN STDCALL -ExpSearchHashTable(IN PHASH_TABLE HashTable, - IN PVOID Key, - IN ULONG KeyLength, - OUT PVOID * Value) -{ - ULONG Index; - - Index = (ExpHash(Key, KeyLength) & ExpHashTableMask(HashTable->HashTableSize)); - - return ExSearchSplayTree(&HashTable->HashTrees[Index], Key, Value); -} - - -/* - * Remove a value associated with a given key from a hash table. - */ -BOOLEAN STDCALL -ExpRemoveHashTable(IN PHASH_TABLE HashTable, - IN PVOID Key, - IN ULONG KeyLength, - IN PVOID * Value) -{ - ULONG Index; - - Index = (ExpHash(Key, KeyLength) & ExpHashTableMask(HashTable->HashTableSize)); - - return ExRemoveSplayTree(&HashTable->HashTrees[Index], Key, Value); -} - - -/* - * Initializes a hash table. - */ -BOOLEAN STDCALL -ExInitializeHashTable(IN PHASH_TABLE HashTable, - IN ULONG HashTableSize, - IN PKEY_COMPARATOR Compare OPTIONAL, - IN BOOLEAN UseNonPagedPool) -{ - BOOLEAN Status; - ULONG Index; - - RtlZeroMemory(HashTable, sizeof(HASH_TABLE)); - - HashTable->HashTableSize = HashTableSize; - - HashTable->UseNonPagedPool = UseNonPagedPool; - - if (UseNonPagedPool) - { - KeInitializeSpinLock(&HashTable->Lock.NonPaged); - HashTable->HashTrees = ExAllocatePool(NonPagedPool, ExpHashTableSize(HashTableSize) * sizeof(SPLAY_TREE)); - } - else - { - ExInitializeFastMutex(&HashTable->Lock.Paged); - HashTable->HashTrees = ExAllocatePool(PagedPool, ExpHashTableSize(HashTableSize) * sizeof(SPLAY_TREE)); - } - - if (HashTable->HashTrees == NULL) - { - return FALSE; - } - - for (Index = 0; Index < ExpHashTableSize(HashTableSize); Index++) - { - Status = ExInitializeSplayTree(&HashTable->HashTrees[Index], Compare, FALSE, UseNonPagedPool); - - if (!Status) - { - LONG i; - - for (i = Index - 1; i >= 0; i--) - { - ExDeleteSplayTree(&HashTable->HashTrees[i]); - } - - ExFreePool(HashTable->HashTrees); - - return FALSE; - } - } - - return TRUE; -} - - -/* - * Release resources used by a hash table. - */ -VOID STDCALL -ExDeleteHashTable(IN PHASH_TABLE HashTable) -{ - ULONG Index; - - for (Index = 0; Index < ExpHashTableSize(HashTable->HashTableSize); Index++) - { - ExDeleteSplayTree(&HashTable->HashTrees[Index]); - } - - ExFreePool(HashTable->HashTrees); -} - - -/* - * Insert a value in a hash table. - */ -VOID STDCALL -ExInsertHashTable(IN PHASH_TABLE HashTable, - IN PVOID Key, - IN ULONG KeyLength, - IN PVOID Value) -{ - KIRQL OldIrql; - - /* FIXME: Use SEH for error reporting */ - - ExpLockHashTable(HashTable, &OldIrql); - ExpInsertHashTable(HashTable, Key, KeyLength, Value); - ExpUnlockHashTable(HashTable, &OldIrql); -} - - -/* - * Search for a value associated with a given key in a hash table. - */ -BOOLEAN STDCALL -ExSearchHashTable(IN PHASH_TABLE HashTable, - IN PVOID Key, - IN ULONG KeyLength, - OUT PVOID * Value) -{ - BOOLEAN Status; - KIRQL OldIrql; - - ExpLockHashTable(HashTable, &OldIrql); - Status = ExpSearchHashTable(HashTable, Key, KeyLength, Value); - ExpUnlockHashTable(HashTable, &OldIrql); - - return Status; -} - - -/* - * Remove a value associated with a given key from a hash table. - */ -BOOLEAN STDCALL -ExRemoveHashTable(IN PHASH_TABLE HashTable, - IN PVOID Key, - IN ULONG KeyLength, - IN PVOID * Value) -{ - BOOLEAN Status; - KIRQL OldIrql; - - ExpLockHashTable(HashTable, &OldIrql); - Status = ExpRemoveHashTable(HashTable, Key, KeyLength, Value); - ExpUnlockHashTable(HashTable, &OldIrql); - - return Status; -} - -/* EOF */ diff --git a/reactos/ntoskrnl/ex/init.c b/reactos/ntoskrnl/ex/init.c index 03951a885d7..a5982fed154 100644 --- a/reactos/ntoskrnl/ex/init.c +++ b/reactos/ntoskrnl/ex/init.c @@ -1,21 +1,662 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ex/init.c * PURPOSE: Executive initalization * - * PROGRAMMERS: Eric Kohl (ekohl@abo.rhein-zeitung.de) + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Added ExpInitializeExecutive + * and optimized/cleaned it. + * Eric Kohl (ekohl@abo.rhein-zeitung.de) */ #include +#include +#include "../dbg/kdb.h" #define NDEBUG #include /* DATA **********************************************************************/ +extern ULONG MmCoreDumpType; +extern CHAR KiTimerSystemAuditing; +extern PVOID Ki386InitialStackArray[MAXIMUM_PROCESSORS]; +extern ADDRESS_RANGE KeMemoryMap[64]; +extern ULONG KeMemoryMapRangeCount; +extern ULONG_PTR FirstKrnlPhysAddr; +extern ULONG_PTR LastKrnlPhysAddr; +extern ULONG_PTR LastKernelAddress; +extern LOADER_MODULE KeLoaderModules[64]; +extern PRTL_MESSAGE_RESOURCE_DATA KiBugCodeMessages; +extern LIST_ENTRY KiProfileListHead; +extern LIST_ENTRY KiProfileSourceListHead; +extern KSPIN_LOCK KiProfileLock; + +VOID PspPostInitSystemProcess(VOID); + /* FUNCTIONS ****************************************************************/ +static +VOID +INIT_FUNCTION +InitSystemSharedUserPage (PCSZ ParameterLine) +{ + UNICODE_STRING ArcDeviceName; + UNICODE_STRING ArcName; + UNICODE_STRING BootPath; + UNICODE_STRING DriveDeviceName; + UNICODE_STRING DriveName; + WCHAR DriveNameBuffer[20]; + PCHAR ParamBuffer; + PWCHAR ArcNameBuffer; + PCHAR p; + NTSTATUS Status; + ULONG Length; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE Handle; + ULONG i; + BOOLEAN BootDriveFound = FALSE; + + /* + * NOTE: + * The shared user page has been zeroed-out right after creation. + * There is NO need to do this again. + */ + Ki386SetProcessorFeatures(); + + /* Set the Version Data */ + SharedUserData->NtProductType = NtProductWinNt; + SharedUserData->ProductTypeIsValid = TRUE; + SharedUserData->NtMajorVersion = 5; + SharedUserData->NtMinorVersion = 0; + + /* + * Retrieve the current dos system path + * (e.g.: C:\reactos) from the given arc path + * (e.g.: multi(0)disk(0)rdisk(0)partititon(1)\reactos) + * Format: "\ [options...]" + */ + + /* Create local parameter line copy */ + ParamBuffer = ExAllocatePool(PagedPool, 256); + strcpy (ParamBuffer, (char *)ParameterLine); + DPRINT("%s\n", ParamBuffer); + + /* Cut options off */ + p = strchr (ParamBuffer, ' '); + if (p) *p = 0; + DPRINT("%s\n", ParamBuffer); + + /* Extract path */ + p = strchr (ParamBuffer, '\\'); + if (p) { + + DPRINT("Boot path: %s\n", p); + RtlCreateUnicodeStringFromAsciiz (&BootPath, p); + *p = 0; + + } else { + + DPRINT("Boot path: %s\n", "\\"); + RtlCreateUnicodeStringFromAsciiz (&BootPath, "\\"); + } + DPRINT("Arc name: %s\n", ParamBuffer); + + /* Only ARC Name left - Build full ARC Name */ + ArcNameBuffer = ExAllocatePool (PagedPool, 256 * sizeof(WCHAR)); + swprintf (ArcNameBuffer, L"\\ArcName\\%S", ParamBuffer); + RtlInitUnicodeString (&ArcName, ArcNameBuffer); + DPRINT("Arc name: %wZ\n", &ArcName); + + /* Free ParamBuffer */ + ExFreePool (ParamBuffer); + + /* Allocate ARC Device Name string */ + ArcDeviceName.Length = 0; + ArcDeviceName.MaximumLength = 256 * sizeof(WCHAR); + ArcDeviceName.Buffer = ExAllocatePool (PagedPool, 256 * sizeof(WCHAR)); + + /* Open the Symbolic Link */ + InitializeObjectAttributes(&ObjectAttributes, + &ArcName, + OBJ_OPENLINK, + NULL, + NULL); + Status = NtOpenSymbolicLinkObject(&Handle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes); + + /* Free the String */ + RtlFreeUnicodeString(&ArcName); + + /* Check for Success */ + if (!NT_SUCCESS(Status)) { + + /* Free the Strings */ + RtlFreeUnicodeString(&BootPath); + RtlFreeUnicodeString(&ArcDeviceName); + CPRINT("NtOpenSymbolicLinkObject() failed (Status %x)\n", Status); + KEBUGCHECK(0); + } + + /* Query the Link */ + Status = NtQuerySymbolicLinkObject(Handle, + &ArcDeviceName, + &Length); + NtClose (Handle); + + /* Check for Success */ + if (!NT_SUCCESS(Status)) { + + /* Free the Strings */ + RtlFreeUnicodeString(&BootPath); + RtlFreeUnicodeString(&ArcDeviceName); + CPRINT("NtQuerySymbolicLinkObject() failed (Status %x)\n", Status); + KEBUGCHECK(0); + } + DPRINT("Length: %lu ArcDeviceName: %wZ\n", Length, &ArcDeviceName); + + /* Allocate Device Name string */ + DriveDeviceName.Length = 0; + DriveDeviceName.MaximumLength = 256 * sizeof(WCHAR); + DriveDeviceName.Buffer = ExAllocatePool (PagedPool, 256 * sizeof(WCHAR)); + + /* Loop Drives */ + for (i = 0; i < 26; i++) { + + /* Setup the String */ + swprintf (DriveNameBuffer, L"\\??\\%C:", 'A' + i); + RtlInitUnicodeString(&DriveName, + DriveNameBuffer); + + /* Open the Symbolic Link */ + InitializeObjectAttributes(&ObjectAttributes, + &DriveName, + OBJ_OPENLINK, + NULL, + NULL); + Status = NtOpenSymbolicLinkObject(&Handle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes); + + /* If it failed, skip to the next drive */ + if (!NT_SUCCESS(Status)) { + DPRINT("Failed to open link %wZ\n", &DriveName); + continue; + } + + /* Query it */ + Status = NtQuerySymbolicLinkObject(Handle, + &DriveDeviceName, + &Length); + + /* If it failed, skip to the next drive */ + if (!NT_SUCCESS(Status)) { + DPRINT("Failed to query link %wZ\n", &DriveName); + continue; + } + DPRINT("Opened link: %wZ ==> %wZ\n", &DriveName, &DriveDeviceName); + + /* See if we've found the boot drive */ + if (!RtlCompareUnicodeString (&ArcDeviceName, &DriveDeviceName, FALSE)) { + + DPRINT("DOS Boot path: %c:%wZ\n", 'A' + i, &BootPath); + swprintf(SharedUserData->NtSystemRoot, L"%C:%wZ", 'A' + i, &BootPath); + BootDriveFound = TRUE; + } + + /* Close this Link */ + NtClose (Handle); + } + + /* Free all the Strings we have in memory */ + RtlFreeUnicodeString (&BootPath); + RtlFreeUnicodeString (&DriveDeviceName); + RtlFreeUnicodeString (&ArcDeviceName); + + /* Make sure we found the Boot Drive */ + if (BootDriveFound == FALSE) { + + DbgPrint("No system drive found!\n"); + KEBUGCHECK (NO_BOOT_DEVICE); + } +} + +inline +VOID +STDCALL +ExecuteRuntimeAsserts(VOID) +{ + /* + * Fail at runtime if someone has changed various structures without + * updating the offsets used for the assembler code. + */ + ASSERT(FIELD_OFFSET(KTHREAD, InitialStack) == KTHREAD_INITIAL_STACK); + ASSERT(FIELD_OFFSET(KTHREAD, Teb) == KTHREAD_TEB); + ASSERT(FIELD_OFFSET(KTHREAD, KernelStack) == KTHREAD_KERNEL_STACK); + ASSERT(FIELD_OFFSET(KTHREAD, NpxState) == KTHREAD_NPX_STATE); + ASSERT(FIELD_OFFSET(KTHREAD, ServiceTable) == KTHREAD_SERVICE_TABLE); + ASSERT(FIELD_OFFSET(KTHREAD, PreviousMode) == KTHREAD_PREVIOUS_MODE); + ASSERT(FIELD_OFFSET(KTHREAD, TrapFrame) == KTHREAD_TRAP_FRAME); + ASSERT(FIELD_OFFSET(KTHREAD, CallbackStack) == KTHREAD_CALLBACK_STACK); + ASSERT(FIELD_OFFSET(KTHREAD, ApcState.Process) == KTHREAD_APCSTATE_PROCESS); + ASSERT(FIELD_OFFSET(KPROCESS, DirectoryTableBase) == KPROCESS_DIRECTORY_TABLE_BASE); + ASSERT(FIELD_OFFSET(KPROCESS, IopmOffset) == KPROCESS_IOPM_OFFSET); + ASSERT(FIELD_OFFSET(KPROCESS, LdtDescriptor) == KPROCESS_LDT_DESCRIPTOR0); + ASSERT(FIELD_OFFSET(KTRAP_FRAME, Reserved9) == KTRAP_FRAME_RESERVED9); + ASSERT(FIELD_OFFSET(KV86M_TRAP_FRAME, SavedExceptionStack) == TF_SAVED_EXCEPTION_STACK); + ASSERT(FIELD_OFFSET(KV86M_TRAP_FRAME, regs) == TF_REGS); + ASSERT(FIELD_OFFSET(KV86M_TRAP_FRAME, orig_ebp) == TF_ORIG_EBP); + ASSERT(FIELD_OFFSET(KPCR, Tib.ExceptionList) == KPCR_EXCEPTION_LIST); + ASSERT(FIELD_OFFSET(KPCR, Self) == KPCR_SELF); + ASSERT(FIELD_OFFSET(KPCR, PrcbData) + FIELD_OFFSET(KPRCB, CurrentThread) == KPCR_CURRENT_THREAD); + ASSERT(FIELD_OFFSET(KPCR, PrcbData) + FIELD_OFFSET(KPRCB, NpxThread) == KPCR_NPX_THREAD); + ASSERT(FIELD_OFFSET(KTSS, Esp0) == KTSS_ESP0); + ASSERT(FIELD_OFFSET(KTSS, Eflags) == KTSS_EFLAGS); + ASSERT(FIELD_OFFSET(KTSS, IoMapBase) == KTSS_IOMAPBASE); + ASSERT(sizeof(FX_SAVE_AREA) == SIZEOF_FX_SAVE_AREA); +} + +inline +VOID +STDCALL +ParseAndCacheLoadedModules(PBOOLEAN SetupBoot) +{ + ULONG i; + PCHAR Name; + + /* Loop the Module List and get the modules we want */ + for (i = 1; i < KeLoaderBlock.ModsCount; i++) { + + /* Get the Name of this Module */ + if (!(Name = strrchr((PCHAR)KeLoaderModules[i].String, '\\'))) { + + /* Save the name */ + Name = (PCHAR)KeLoaderModules[i].String; + + } else { + + /* No name, skip */ + Name++; + } + + /* Now check for any of the modules we will need later */ + if (!_stricmp(Name, "ansi.nls")) { + + CachedModules[AnsiCodepage] = &KeLoaderModules[i]; + + } else if (!_stricmp(Name, "oem.nls")) { + + CachedModules[OemCodepage] = &KeLoaderModules[i]; + + } else if (!_stricmp(Name, "casemap.nls")) { + + CachedModules[UnicodeCasemap] = &KeLoaderModules[i]; + + } else if (!_stricmp(Name, "system") || !_stricmp(Name, "system.hiv")) { + + CachedModules[SystemRegistry] = &KeLoaderModules[i]; + *SetupBoot = FALSE; + + } else if (!_stricmp(Name, "hardware") || !_stricmp(Name, "hardware.hiv")) { + + CachedModules[HardwareRegistry] = &KeLoaderModules[i]; + } + } +} + +inline +VOID +STDCALL +ParseCommandLine(PULONG MaxMem, + PBOOLEAN NoGuiBoot, + PBOOLEAN BootLog, + PBOOLEAN ForceAcpiDisable) +{ + PCHAR p1, p2; + + p1 = (PCHAR)KeLoaderBlock.CommandLine; + while(*p1 && (p2 = strchr(p1, '/'))) { + + p2++; + if (!_strnicmp(p2, "MAXMEM", 6)) { + + p2 += 6; + while (isspace(*p2)) p2++; + + if (*p2 == '=') { + + p2++; + + while(isspace(*p2)) p2++; + + if (isdigit(*p2)) { + while (isdigit(*p2)) { + *MaxMem = *MaxMem * 10 + *p2 - '0'; + p2++; + } + break; + } + } + } else if (!_strnicmp(p2, "NOGUIBOOT", 9)) { + + p2 += 9; + *NoGuiBoot = TRUE; + + } else if (!_strnicmp(p2, "CRASHDUMP", 9)) { + + p2 += 9; + if (*p2 == ':') { + + p2++; + if (!_strnicmp(p2, "FULL", 4)) { + + MmCoreDumpType = MM_CORE_DUMP_TYPE_FULL; + + } else { + + MmCoreDumpType = MM_CORE_DUMP_TYPE_NONE; + } + } + } else if (!_strnicmp(p2, "BOOTLOG", 7)) { + + p2 += 7; + *BootLog = TRUE; + } else if (!_strnicmp(p2, "NOACPI", 6)) { + + p2 += 6; + *ForceAcpiDisable = TRUE; + } + + p1 = p2; + } +} + +VOID +INIT_FUNCTION +STDCALL +ExpInitializeExecutive(VOID) +{ + CHAR str[50]; + UNICODE_STRING EventName; + HANDLE InitDoneEventHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + BOOLEAN NoGuiBoot = FALSE; + BOOLEAN BootLog = FALSE; + ULONG MaxMem = 0; + BOOLEAN SetupBoot = TRUE; + BOOLEAN ForceAcpiDisable = FALSE; + LARGE_INTEGER Timeout; + HANDLE ProcessHandle; + HANDLE ThreadHandle; + NTSTATUS Status; + + /* Check if the structures match the ASM offset constants */ + ExecuteRuntimeAsserts(); + + /* Sets up the Text Sections of the Kernel and HAL for debugging */ + LdrInit1(); + + /* Lower the IRQL to Dispatch Level */ + KeLowerIrql(DISPATCH_LEVEL); + + /* Sets up the VDM Data */ + NtEarlyInitVdm(); + + /* Parse Command Line Settings */ + ParseCommandLine(&MaxMem, &NoGuiBoot, &BootLog, &ForceAcpiDisable); + + /* Initialize Kernel Memory Address Space */ + MmInit1(FirstKrnlPhysAddr, + LastKrnlPhysAddr, + LastKernelAddress, + (PADDRESS_RANGE)&KeMemoryMap, + KeMemoryMapRangeCount, + MaxMem > 8 ? MaxMem : 4096); + + /* Parse the Loaded Modules (by FreeLoader) and cache the ones we'll need */ + ParseAndCacheLoadedModules(&SetupBoot); + + /* Initialize the kernel debugger */ + KdInitSystem (1, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); + + /* Initialize the Dispatcher, Clock and Bug Check Mechanisms. */ + KeInit2(); + + /* Bring back the IRQL to Passive */ + KeLowerIrql(PASSIVE_LEVEL); + + /* Initialize Profiling */ + InitializeListHead(&KiProfileListHead); + InitializeListHead(&KiProfileSourceListHead); + KeInitializeSpinLock(&KiProfileLock); + + /* Load basic Security for other Managers */ + if (!SeInit1()) KEBUGCHECK(SECURITY_INITIALIZATION_FAILED); + + /* Create the Basic Object Manager Types to allow new Object Types */ + ObInit(); + + /* Initialize Lookaside Lists */ + ExInit2(); + + /* Set up Region Maps, Sections and the Paging File */ + MmInit2(); + + /* Initialize Tokens now that the Object Manager is ready */ + if (!SeInit2()) KEBUGCHECK(SECURITY1_INITIALIZATION_FAILED); + + /* Set 1 CPU for now, we'll increment this later */ + KeNumberProcessors = 1; + + /* Initalize the Process Manager */ + PiInitProcessManager(); + + /* Break into the Debugger if requested */ + if (KdPollBreakIn()) DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C); + + /* Initialize all processors */ + while (!HalAllProcessorsStarted()) { + + PVOID ProcessorStack; + + /* Set up the Kernel and Process Manager for this CPU */ + KePrepareForApplicationProcessorInit(KeNumberProcessors); + PsPrepareForApplicationProcessorInit(KeNumberProcessors); + + /* Allocate a stack for use when booting the processor */ + ProcessorStack = Ki386InitialStackArray[((int)KeNumberProcessors)] + MM_STACK_SIZE; + + /* Tell HAL a new CPU is being started */ + HalStartNextProcessor(0, (ULONG)ProcessorStack - 2*sizeof(FX_SAVE_AREA)); + KeNumberProcessors++; + } + + /* Do Phase 1 HAL Initalization */ + HalInitSystem(1, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); + + /* Initialize Basic System Objects and Worker Threads */ + ExInit3(); + + /* Create the system handle table, assign it to the system process, create + the client id table and assign a PID for the system process. This needs + to be done before the worker threads are initialized so the system + process gets the first PID (4) */ + PspPostInitSystemProcess(); + + /* initialize the worker threads */ + ExpInitializeWorkerThreads(); + + /* initialize callbacks */ + ExpInitializeCallbacks(); + + /* Initialize the GDB Stub and break */ + KdInit1(); + + /* Initialize I/O Objects, Filesystems, Error Logging and Shutdown */ + IoInit(); + + /* TBD */ + PoInit((PLOADER_PARAMETER_BLOCK)&KeLoaderBlock, ForceAcpiDisable); + + /* Initialize the Registry (Hives are NOT yet loaded!) */ + CmInitializeRegistry(); + + /* Unmap Low memory, initialize the Page Zeroing and the Balancer Thread */ + MmInit3(); + + /* Initialize Cache Views */ + CcInit(); + + /* Hook System Interrupt for the Debugger */ + KdInit2(); + + /* Initialize File Locking */ + FsRtlpInitFileLockingImplementation(); + + /* Report all resources used by hal */ + HalReportResourceUsage(); + + /* Clear the screen to blue */ + HalInitSystem(2, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); + + /* Display version number and copyright/warranty message */ + HalDisplayString("Starting ReactOS "KERNEL_VERSION_STR" (Build " + KERNEL_VERSION_BUILD_STR")\n"); + HalDisplayString(RES_STR_LEGAL_COPYRIGHT); + HalDisplayString("\n\nReactOS is free software, covered by the GNU General " + "Public License, and you\n"); + HalDisplayString("are welcome to change it and/or distribute copies of it " + "under certain\n"); + HalDisplayString("conditions. There is absolutely no warranty for " + "ReactOS.\n\n"); + + /* Display number of Processors */ + sprintf(str, + "Found %d system processor(s). [%lu MB Memory]\n", + KeNumberProcessors, + (KeLoaderBlock.MemHigher + 1088)/ 1024); + HalDisplayString(str); + + /* Print which Debugger is being used */ + KdInit3(); + + /* Import and create NLS Data and Sections */ + RtlpInitNls(); + + /* Import and Load Registry Hives */ + CmInitHives(SetupBoot); + + /* Initialize the time zone information from the registry */ + ExpInitTimeZoneInfo(); + + /* Enter the kernel debugger before starting up the boot drivers */ +#ifdef KDBG + KdbEnter(); +#endif /* KDBG */ + + /* Setup Drivers and Root Device Node */ + IoInit2(BootLog); + + /* Display the boot screen image if not disabled */ + if (!NoGuiBoot) InbvEnableBootDriver(TRUE); + + /* Create ARC Names, SystemRoot SymLink, Load Drivers and Assign Letters */ + IoInit3(); + + /* Initialize the Default Locale */ + PiInitDefaultLocale(); + + /* Initialize shared user page. Set dos system path, dos device map, etc. */ + InitSystemSharedUserPage ((PCHAR)KeLoaderBlock.CommandLine); + + /* Create 'ReactOSInitDone' event */ + RtlInitUnicodeString(&EventName, L"\\ReactOSInitDone"); + InitializeObjectAttributes(&ObjectAttributes, + &EventName, + 0, + NULL, + NULL); + Status = ZwCreateEvent(&InitDoneEventHandle, + EVENT_ALL_ACCESS, + &ObjectAttributes, + SynchronizationEvent, + FALSE); + + /* Check for Success */ + if (!NT_SUCCESS(Status)) { + + DPRINT1("Failed to create 'ReactOSInitDone' event (Status 0x%x)\n", Status); + InitDoneEventHandle = INVALID_HANDLE_VALUE; + } + + /* Launch initial process */ + Status = LdrLoadInitialProcess(&ProcessHandle, + &ThreadHandle); + + /* Check for success, Bugcheck if we failed */ + if (!NT_SUCCESS(Status)) { + + KEBUGCHECKEX(SESSION4_INITIALIZATION_FAILED, Status, 0, 0, 0); + } + + /* Wait on the Completion Event */ + if (InitDoneEventHandle != INVALID_HANDLE_VALUE) { + + HANDLE Handles[2]; /* Init event, Initial process */ + + /* Setup the Handles to wait on */ + Handles[0] = InitDoneEventHandle; + Handles[1] = ProcessHandle; + + /* Wait for the system to be initialized */ + Timeout.QuadPart = (LONGLONG)-1200000000; /* 120 second timeout */ + Status = ZwWaitForMultipleObjects(2, + Handles, + WaitAny, + FALSE, + &Timeout); + if (!NT_SUCCESS(Status)) { + + DPRINT1("NtWaitForMultipleObjects failed with status 0x%x!\n", Status); + + } else if (Status == STATUS_TIMEOUT) { + + DPRINT1("WARNING: System not initialized after 120 seconds.\n"); + + } else if (Status == STATUS_WAIT_0 + 1) { + + /* Crash the system if the initial process was terminated. */ + KEBUGCHECKEX(SESSION5_INITIALIZATION_FAILED, Status, 0, 0, 0); + } + + /* Disable the Boot Logo */ + if (!NoGuiBoot) InbvEnableBootDriver(FALSE); + + /* Signal the Event and close the handle */ + ZwSetEvent(InitDoneEventHandle, NULL); + ZwClose(InitDoneEventHandle); + + } else { + + /* On failure to create 'ReactOSInitDone' event, go to text mode ASAP */ + if (!NoGuiBoot) InbvEnableBootDriver(FALSE); + + /* Crash the system if the initial process terminates within 5 seconds. */ + Timeout.QuadPart = (LONGLONG)-50000000; /* 5 second timeout */ + Status = ZwWaitForSingleObject(ProcessHandle, + FALSE, + &Timeout); + + /* Check for timeout, crash if the initial process didn't initalize */ + if (Status != STATUS_TIMEOUT) KEBUGCHECKEX(SESSION5_INITIALIZATION_FAILED, Status, 1, 0, 0); + } + + /* Enable the Clock, close remaining handles */ + KiTimerSystemAuditing = 1; + ZwClose(ThreadHandle); + ZwClose(ProcessHandle); +} + VOID INIT_FUNCTION ExInit2(VOID) { @@ -25,7 +666,6 @@ ExInit2(VOID) VOID INIT_FUNCTION ExInit3 (VOID) { - ExInitializeWorkerThreads(); ExpInitializeEventImplementation(); ExpInitializeEventPairImplementation(); ExpInitializeMutantImplementation(); @@ -35,28 +675,7 @@ ExInit3 (VOID) ExpInitializeProfileImplementation(); ExpWin32kInit(); ExpInitUuids(); -} - - -/* - * @implemented - */ -BOOLEAN STDCALL -ExIsProcessorFeaturePresent(IN ULONG ProcessorFeature) -{ - if (ProcessorFeature >= PROCESSOR_FEATURE_MAX) - return(FALSE); - - return(SharedUserData->ProcessorFeatures[ProcessorFeature]); -} - - -VOID STDCALL -ExPostSystemEvent (ULONG Unknown1, - ULONG Unknown2, - ULONG Unknown3) -{ - /* doesn't do anything */ + ExpInitializeHandleTables(); } /* EOF */ diff --git a/reactos/ntoskrnl/ex/list.c b/reactos/ntoskrnl/ex/list.c index ddb57fc3692..59095978ebd 100644 --- a/reactos/ntoskrnl/ex/list.c +++ b/reactos/ntoskrnl/ex/list.c @@ -449,7 +449,7 @@ InterlockedPopEntrySList(IN PSLIST_HEADER ListHead) do { - oldslh = *ListHead; + oldslh = *(volatile SLIST_HEADER *)ListHead; le = oldslh.Next.Next; if(le == NULL) { @@ -481,7 +481,7 @@ InterlockedPushEntrySList(IN PSLIST_HEADER ListHead, do { - oldslh = *ListHead; + oldslh = *(volatile SLIST_HEADER *)ListHead; newslh.Depth = oldslh.Depth + 1; newslh.Sequence = oldslh.Sequence + 1; ListEntry->Next = oldslh.Next.Next; diff --git a/reactos/ntoskrnl/ex/mutant.c b/reactos/ntoskrnl/ex/mutant.c index f6a2d4b36c7..d0dc507e53a 100644 --- a/reactos/ntoskrnl/ex/mutant.c +++ b/reactos/ntoskrnl/ex/mutant.c @@ -1,11 +1,12 @@ -/* $Id:$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ex/mutant.c - * PURPOSE: Synchronization primitives + * PURPOSE: Executive Management of Mutants * - * PROGRAMMERS: David Welch (welch@cwcom.net) + * PROGRAMMERS: Alex Ionescu - Fix tab/space mismatching, tiny fixes to query function and + * add more debug output. + * David Welch (welch@cwcom.net) */ /* INCLUDES *****************************************************************/ @@ -17,360 +18,357 @@ POBJECT_TYPE ExMutantObjectType = NULL; static GENERIC_MAPPING ExpMutantMapping = { - STANDARD_RIGHTS_READ | SYNCHRONIZE | MUTANT_QUERY_STATE, - STANDARD_RIGHTS_WRITE | SYNCHRONIZE, - STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | MUTANT_QUERY_STATE, - MUTANT_ALL_ACCESS}; + STANDARD_RIGHTS_READ | SYNCHRONIZE | MUTANT_QUERY_STATE, + STANDARD_RIGHTS_WRITE | SYNCHRONIZE, + STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | MUTANT_QUERY_STATE, + MUTANT_ALL_ACCESS}; -static const INFORMATION_CLASS_INFO ExMutantInfoClass[] = -{ - ICI_SQ_SAME( sizeof(MUTANT_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* MutantBasicInformation */ +static const INFORMATION_CLASS_INFO ExMutantInfoClass[] = { + + /* MutantBasicInformation */ + ICI_SQ_SAME( sizeof(MUTANT_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), }; /* FUNCTIONS *****************************************************************/ - -NTSTATUS STDCALL -ExpCreateMutant(PVOID ObjectBody, - PVOID Parent, - PWSTR RemainingPath, - POBJECT_ATTRIBUTES ObjectAttributes) -{ - DPRINT("NtpCreateMutant(ObjectBody %x, Parent %x, RemainingPath %S)\n", - ObjectBody, Parent, RemainingPath); - - if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL) - { - return(STATUS_UNSUCCESSFUL); - } - - return(STATUS_SUCCESS); -} - - -VOID STDCALL +VOID +STDCALL ExpDeleteMutant(PVOID ObjectBody) { - DPRINT("NtpDeleteMutant(ObjectBody %x)\n", ObjectBody); - KeReleaseMutant((PKMUTANT)ObjectBody, - MUTANT_INCREMENT, - TRUE, - FALSE); + DPRINT("ExpDeleteMutant(ObjectBody %x)\n", ObjectBody); + + /* Make sure to release the Mutant */ + KeReleaseMutant((PKMUTANT)ObjectBody, + MUTANT_INCREMENT, + TRUE, + FALSE); } - -VOID INIT_FUNCTION +VOID +INIT_FUNCTION ExpInitializeMutantImplementation(VOID) { - ExMutantObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE)); - RtlpCreateUnicodeString(&ExMutantObjectType->TypeName, L"Mutant", NonPagedPool); + /* Allocate the Object Type */ + ExMutantObjectType = ExAllocatePoolWithTag(NonPagedPool, sizeof(OBJECT_TYPE), TAG('M', 't', 'n', 't')); - ExMutantObjectType->Tag = TAG('M', 'T', 'N', 'T'); - ExMutantObjectType->PeakObjects = 0; - ExMutantObjectType->PeakHandles = 0; - ExMutantObjectType->TotalObjects = 0; - ExMutantObjectType->TotalHandles = 0; - ExMutantObjectType->PagedPoolCharge = 0; - ExMutantObjectType->NonpagedPoolCharge = sizeof(KMUTANT); - ExMutantObjectType->Mapping = &ExpMutantMapping; - ExMutantObjectType->Dump = NULL; - ExMutantObjectType->Open = NULL; - ExMutantObjectType->Close = NULL; - ExMutantObjectType->Delete = ExpDeleteMutant; - ExMutantObjectType->Parse = NULL; - ExMutantObjectType->Security = NULL; - ExMutantObjectType->QueryName = NULL; - ExMutantObjectType->OkayToClose = NULL; - ExMutantObjectType->Create = ExpCreateMutant; - ExMutantObjectType->DuplicationNotify = NULL; - - ObpCreateTypeObject(ExMutantObjectType); + /* Create the Object Type */ + RtlInitUnicodeString(&ExMutantObjectType->TypeName, L"Mutant"); + ExMutantObjectType->Tag = TAG('M', 't', 'n', 't'); + ExMutantObjectType->PeakObjects = 0; + ExMutantObjectType->PeakHandles = 0; + ExMutantObjectType->TotalObjects = 0; + ExMutantObjectType->TotalHandles = 0; + ExMutantObjectType->PagedPoolCharge = 0; + ExMutantObjectType->NonpagedPoolCharge = sizeof(KMUTANT); + ExMutantObjectType->Mapping = &ExpMutantMapping; + ExMutantObjectType->Dump = NULL; + ExMutantObjectType->Open = NULL; + ExMutantObjectType->Close = NULL; + ExMutantObjectType->Delete = ExpDeleteMutant; + ExMutantObjectType->Parse = NULL; + ExMutantObjectType->Security = NULL; + ExMutantObjectType->QueryName = NULL; + ExMutantObjectType->OkayToClose = NULL; + ExMutantObjectType->Create = NULL; + ExMutantObjectType->DuplicationNotify = NULL; + ObpCreateTypeObject(ExMutantObjectType); } - /* * @implemented */ -NTSTATUS STDCALL +NTSTATUS +STDCALL NtCreateMutant(OUT PHANDLE MutantHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN BOOLEAN InitialOwner) + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN BOOLEAN InitialOwner) { - KPROCESSOR_MODE PreviousMode; - HANDLE hMutant; - PKMUTEX Mutant; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(MutantHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObCreateObject(PreviousMode, - ExMutantObjectType, - ObjectAttributes, - PreviousMode, - NULL, - sizeof(KMUTANT), - 0, - 0, - (PVOID*)&Mutant); - if(NT_SUCCESS(Status)) - { - KeInitializeMutant(Mutant, - InitialOwner); - - Status = ObInsertObject((PVOID)Mutant, - NULL, - DesiredAccess, - 0, - NULL, - &hMutant); - ObDereferenceObject(Mutant); + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + HANDLE hMutant; + PKMUTANT Mutant; + NTSTATUS Status = STATUS_SUCCESS; - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *MutantHandle = hMutant; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtOpenMutant(OUT PHANDLE MutantHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes) -{ - HANDLE hMutant; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + PAGED_CODE(); + DPRINT("NtCreateMutant(0x%x, 0x%x, 0x%x)\n", MutantHandle, DesiredAccess, ObjectAttributes); - PAGED_CODE(); + /* Check Output Safety */ + if(PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(MutantHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; - DPRINT("NtOpenMutant(0x%x, 0x%x, 0x%x)\n", MutantHandle, DesiredAccess, ObjectAttributes); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(MutantHandle, - sizeof(HANDLE), - sizeof(ULONG)); + if(!NT_SUCCESS(Status)) return Status; } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + + /* Create the Mutant Object*/ + Status = ObCreateObject(PreviousMode, + ExMutantObjectType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(KMUTANT), + 0, + 0, + (PVOID*)&Mutant); + + /* Check for success */ + if(NT_SUCCESS(Status)) { + + /* Initalize the Kernel Mutant */ + DPRINT("Initializing the Mutant\n"); + KeInitializeMutant(Mutant, InitialOwner); - if(!NT_SUCCESS(Status)) - { - return Status; + /* Insert the Object */ + Status = ObInsertObject((PVOID)Mutant, + NULL, + DesiredAccess, + 0, + NULL, + &hMutant); + ObDereferenceObject(Mutant); + + /* Check for success and return handle */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + *MutantHandle = hMutant; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } } - } - Status = ObOpenObjectByName(ObjectAttributes, - ExMutantObjectType, - NULL, - PreviousMode, - DesiredAccess, - NULL, - &hMutant); - - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *MutantHandle = hMutant; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - - return Status; + /* Return Status */ + return Status; } - /* * @implemented */ -NTSTATUS STDCALL -NtQueryMutant(IN HANDLE MutantHandle, - IN MUTANT_INFORMATION_CLASS MutantInformationClass, - OUT PVOID MutantInformation, - IN ULONG MutantInformationLength, - OUT PULONG ResultLength OPTIONAL) +NTSTATUS +STDCALL +NtOpenMutant(OUT PHANDLE MutantHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes) { - PKMUTANT Mutant; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); + HANDLE hMutant; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + DPRINT("NtOpenMutant(0x%x, 0x%x, 0x%x)\n", MutantHandle, DesiredAccess, ObjectAttributes); - PreviousMode = ExGetPreviousMode(); - - DefaultQueryInfoBufferCheck(MutantInformationClass, - ExMutantInfoClass, - MutantInformation, - MutantInformationLength, - ResultLength, - PreviousMode, - &Status); - if(!NT_SUCCESS(Status)) - { - DPRINT1("NtQueryMutant() failed, Status: 0x%x\n", Status); - return Status; - } - - Status = ObReferenceObjectByHandle(MutantHandle, - MUTANT_QUERY_STATE, - ExMutantObjectType, - PreviousMode, - (PVOID*)&Mutant, - NULL); - if(NT_SUCCESS(Status)) - { - switch(MutantInformationClass) - { - case MutantBasicInformation: - { - PMUTANT_BASIC_INFORMATION BasicInfo = (PMUTANT_BASIC_INFORMATION)MutantInformation; - - _SEH_TRY - { - BasicInfo->Count = KeReadStateMutant(Mutant); - BasicInfo->Owned = (Mutant->OwnerThread != NULL); - BasicInfo->Abandoned = Mutant->Abandoned; - - if(ResultLength != NULL) - { - *ResultLength = sizeof(MUTANT_BASIC_INFORMATION); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - default: - Status = STATUS_NOT_IMPLEMENTED; - break; - } - - ObDereferenceObject(Mutant); - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtReleaseMutant(IN HANDLE MutantHandle, - IN PLONG PreviousCount OPTIONAL) -{ - PKMUTANT Mutant; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - DPRINT("NtReleaseMutant(MutantHandle 0%x PreviousCount 0%x)\n", - MutantHandle, PreviousCount); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousCount != NULL && PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(PreviousCount, - sizeof(LONG), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* Check Output Safety */ + if(PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(MutantHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; - if(!NT_SUCCESS(Status)) - { - return Status; - } - } + if(!NT_SUCCESS(Status)) return Status; + } + + /* Open the Object */ + Status = ObOpenObjectByName(ObjectAttributes, + ExMutantObjectType, + NULL, + PreviousMode, + DesiredAccess, + NULL, + &hMutant); - Status = ObReferenceObjectByHandle(MutantHandle, - MUTANT_QUERY_STATE, - ExMutantObjectType, - PreviousMode, - (PVOID*)&Mutant, - NULL); - if(NT_SUCCESS(Status)) - { - LONG Prev = KeReleaseMutant(Mutant, MUTANT_INCREMENT, 0, FALSE); - ObDereferenceObject(Mutant); + /* Check for success and return handle */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + *MutantHandle = hMutant; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } - if(PreviousCount != NULL) - { - _SEH_TRY - { - *PreviousCount = Prev; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } + /* Return Status */ + return Status; +} - return Status; +/* + * @implemented + */ +NTSTATUS +STDCALL +NtQueryMutant(IN HANDLE MutantHandle, + IN MUTANT_INFORMATION_CLASS MutantInformationClass, + OUT PVOID MutantInformation, + IN ULONG MutantInformationLength, + OUT PULONG ResultLength OPTIONAL) +{ + PKMUTANT Mutant; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + PMUTANT_BASIC_INFORMATION BasicInfo = (PMUTANT_BASIC_INFORMATION)MutantInformation; + + PAGED_CODE(); + + /* Check buffers and parameters */ + DefaultQueryInfoBufferCheck(MutantInformationClass, + ExMutantInfoClass, + MutantInformation, + MutantInformationLength, + ResultLength, + PreviousMode, + &Status); + if(!NT_SUCCESS(Status)) { + + DPRINT("NtQueryMutant() failed, Status: 0x%x\n", Status); + return Status; + } + + /* Open the Object */ + Status = ObReferenceObjectByHandle(MutantHandle, + MUTANT_QUERY_STATE, + ExMutantObjectType, + PreviousMode, + (PVOID*)&Mutant, + NULL); + /* Check for Status */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + /* Fill out the Basic Information Requested */ + DPRINT("Returning Mutant Information\n"); + BasicInfo->CurrentCount = KeReadStateMutant(Mutant); + BasicInfo->OwnedByCaller = (Mutant->OwnerThread == KeGetCurrentThread()); + BasicInfo->AbandonedState = Mutant->Abandoned; + + /* Return the Result Length if requested */ + if(ResultLength) *ResultLength = sizeof(MUTANT_BASIC_INFORMATION); + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + /* Release the Object */ + ObDereferenceObject(Mutant); + } + + /* Return Status */ + return Status; +} + + +/* + * @implemented + */ +NTSTATUS +STDCALL +NtReleaseMutant(IN HANDLE MutantHandle, + IN PLONG PreviousCount OPTIONAL) +{ + PKMUTANT Mutant; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + DPRINT("NtReleaseMutant(MutantHandle 0%x PreviousCount 0%x)\n", + MutantHandle, + PreviousCount); + + /* Check Output Safety */ + if(PreviousMode != KernelMode && PreviousCount) { + + _SEH_TRY { + + ProbeForWrite(PreviousCount, + sizeof(LONG), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + /* Open the Object */ + Status = ObReferenceObjectByHandle(MutantHandle, + MUTANT_QUERY_STATE, + ExMutantObjectType, + PreviousMode, + (PVOID*)&Mutant, + NULL); + + /* Check for Success and release if such */ + if(NT_SUCCESS(Status)) { + + LONG Prev = 0; + + /* release the mutant. doing so might raise an exception which we're + required to catch! */ + _SEH_TRY { + + Prev = KeReleaseMutant(Mutant, MUTANT_INCREMENT, FALSE, FALSE); + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + ObDereferenceObject(Mutant); + + if(NT_SUCCESS(Status)) { + + /* Return it */ + if(PreviousCount) { + + _SEH_TRY { + + *PreviousCount = Prev; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } + } + + /* Return Status */ + return Status; } /* EOF */ diff --git a/reactos/ntoskrnl/ex/napi.c b/reactos/ntoskrnl/ex/napi.c deleted file mode 100644 index 76a82a41a88..00000000000 --- a/reactos/ntoskrnl/ex/napi.c +++ /dev/null @@ -1,36 +0,0 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ex/napi.c - * PURPOSE: Native API support routines - * - * PROGRAMMERS: David Welch (welch@cwcom.net) - */ - -/* INCLUDES *****************************************************************/ - -#include -#include -#include - -/* GLOBALS ******************************************************************/ - -SSDT_ENTRY -__declspec(dllexport) -KeServiceDescriptorTable[SSDT_MAX_ENTRIES] = -{ - { MainSSDT, NULL, NUMBER_OF_SYSCALLS, MainSSPT }, - { NULL, NULL, 0, NULL }, - { NULL, NULL, 0, NULL }, - { NULL, NULL, 0, NULL } -}; - -SSDT_ENTRY -KeServiceDescriptorTableShadow[SSDT_MAX_ENTRIES] = -{ - { MainSSDT, NULL, NUMBER_OF_SYSCALLS, MainSSPT }, - { NULL, NULL, 0, NULL }, - { NULL, NULL, 0, NULL }, - { NULL, NULL, 0, NULL } -}; diff --git a/reactos/ntoskrnl/ex/profile.c b/reactos/ntoskrnl/ex/profile.c index 863add88d9e..cb9c86b532d 100644 --- a/reactos/ntoskrnl/ex/profile.c +++ b/reactos/ntoskrnl/ex/profile.c @@ -1,11 +1,10 @@ -/* $Id:$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel - * FILE: ntoskrnl/nt/profile.c - * PURPOSE: Support for profiling + * FILE: ntoskrnl/ex/profile.c + * PURPOSE: Support for Executive Profile Objects * - * PROGRAMMERS: No programmer listed. + * PROGRAMMERS: Alex Ionescu */ /* INCLUDES *****************************************************************/ @@ -13,369 +12,448 @@ #include #include -/* TYPES ********************************************************************/ +/* This structure is a *GUESS* -- Alex */ +typedef struct _EPROFILE { + PEPROCESS Process; + PVOID ImageBase; + ULONG ImageSize; + ULONG BucketSize; + PVOID Buffer; + ULONG BufferSize; + PKPROFILE KeProfile; + KPROFILE_SOURCE ProfileSource; + KAFFINITY Affinity; + PMDL Mdl; + PVOID LockedBuffer; +} EPROFILE, *PEPROFILE; /* GLOBALS *******************************************************************/ POBJECT_TYPE EXPORTED ExProfileObjectType = NULL; +static KMUTEX ExpProfileMutex; + +#define PROFILE_CONTROL 1 + static GENERIC_MAPPING ExpProfileMapping = { - STANDARD_RIGHTS_READ, - STANDARD_RIGHTS_WRITE, - STANDARD_RIGHTS_EXECUTE, - STANDARD_RIGHTS_ALL}; + STANDARD_RIGHTS_READ | PROFILE_CONTROL, + STANDARD_RIGHTS_WRITE | PROFILE_CONTROL, + STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL, + STANDARD_RIGHTS_ALL}; -/* - * Size of the profile hash table. - */ -#define PROFILE_HASH_TABLE_SIZE (32) +VOID +STDCALL +ExpDeleteProfile(PVOID ObjectBody) +{ + PEPROFILE Profile; -/* - * Table of lists of per-process profiling data structures hashed by PID. - */ -LIST_ENTRY ProcessProfileListHashTable[PROFILE_HASH_TABLE_SIZE]; + /* Typecast the Object */ + Profile = (PEPROFILE)ObjectBody; + + /* Check if there if the Profile was started */ + if (Profile->LockedBuffer) { + + /* Stop the Profile */ + KeStopProfile(Profile->KeProfile); + + /* Unmap the Locked Buffer */ + MmUnmapLockedPages(Profile->LockedBuffer, Profile->Mdl); + MmUnlockPages(Profile->Mdl); + ExFreePool(Profile->Mdl); + } + + /* Check if a Process is associated */ + if (Profile->Process != NULL) { + + /* Dereference it */ + ObDereferenceObject(Profile->Process); + Profile->Process = NULL; + } +} -/* - * Head of the list of profile data structures for the kernel. - */ -LIST_ENTRY SystemProfileList; - -/* - * Lock that protects the profiling data structures. - */ -KSPIN_LOCK ProfileListLock; - -/* - * Timer interrupts happen before we have initialized the profiling - * data structures so just ignore them before that. - */ -BOOLEAN ProfileInitDone = FALSE; - -VOID INIT_FUNCTION +VOID +INIT_FUNCTION ExpInitializeProfileImplementation(VOID) { - ULONG i; - - InitializeListHead(&SystemProfileList); - - for (i = 0; i < PROFILE_HASH_TABLE_SIZE; i++) - { - InitializeListHead(&ProcessProfileListHashTable[i]); - } - - KeInitializeSpinLock(&ProfileListLock); - ProfileInitDone = TRUE; - - ExProfileObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE)); - - RtlpCreateUnicodeString(&ExProfileObjectType->TypeName, L"Profile", NonPagedPool); - - ExProfileObjectType->Tag = TAG('P', 'R', 'O', 'F'); - ExProfileObjectType->PeakObjects = 0; - ExProfileObjectType->PeakHandles = 0; - ExProfileObjectType->TotalObjects = 0; - ExProfileObjectType->TotalHandles = 0; - ExProfileObjectType->PagedPoolCharge = 0; - ExProfileObjectType->NonpagedPoolCharge = sizeof(KPROFILE); - ExProfileObjectType->Mapping = &ExpProfileMapping; - ExProfileObjectType->Dump = NULL; - ExProfileObjectType->Open = NULL; - ExProfileObjectType->Close = NULL; - ExProfileObjectType->Delete = KiDeleteProfile; - ExProfileObjectType->Parse = NULL; - ExProfileObjectType->Security = NULL; - ExProfileObjectType->QueryName = NULL; - ExProfileObjectType->OkayToClose = NULL; - ExProfileObjectType->Create = NULL; - - ObpCreateTypeObject(ExProfileObjectType); + /* Initialize the Mutex to lock the States */ + KeInitializeMutex(&ExpProfileMutex, 0x40); + + /* Create the Object Type */ + ExProfileObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE)); + RtlInitUnicodeString(&ExProfileObjectType->TypeName, L"Profile"); + ExProfileObjectType->Tag = TAG('P', 'R', 'O', 'F'); + ExProfileObjectType->PeakObjects = 0; + ExProfileObjectType->PeakHandles = 0; + ExProfileObjectType->TotalObjects = 0; + ExProfileObjectType->TotalHandles = 0; + ExProfileObjectType->PagedPoolCharge = 0; + ExProfileObjectType->NonpagedPoolCharge = sizeof(EPROFILE); + ExProfileObjectType->Mapping = &ExpProfileMapping; + ExProfileObjectType->Dump = NULL; + ExProfileObjectType->Open = NULL; + ExProfileObjectType->Close = NULL; + ExProfileObjectType->Delete = ExpDeleteProfile; + ExProfileObjectType->Parse = NULL; + ExProfileObjectType->Security = NULL; + ExProfileObjectType->QueryName = NULL; + ExProfileObjectType->OkayToClose = NULL; + ExProfileObjectType->Create = NULL; + ObpCreateTypeObject(ExProfileObjectType); } -NTSTATUS STDCALL +NTSTATUS +STDCALL NtCreateProfile(OUT PHANDLE ProfileHandle, - IN HANDLE Process OPTIONAL, - IN PVOID ImageBase, - IN ULONG ImageSize, - IN ULONG BucketSize, - IN PVOID Buffer, - IN ULONG BufferSize, - IN KPROFILE_SOURCE ProfileSource, - IN KAFFINITY Affinity) + IN HANDLE Process OPTIONAL, + IN PVOID ImageBase, + IN ULONG ImageSize, + IN ULONG BucketSize, + IN PVOID Buffer, + IN ULONG BufferSize, + IN KPROFILE_SOURCE ProfileSource, + IN KAFFINITY Affinity) { - HANDLE hProfile; - PKPROFILE Profile; - PEPROCESS pProcess; - KPROCESSOR_MODE PreviousMode; - OBJECT_ATTRIBUTES ObjectAttributes; - NTSTATUS Status = STATUS_SUCCESS; + HANDLE hProfile; + PEPROFILE Profile; + PEPROCESS pProcess; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(BufferSize == 0) - { - return STATUS_INVALID_PARAMETER_7; - } - - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForWrite(ProfileHandle, - sizeof(HANDLE), - sizeof(ULONG)); - ProbeForWrite(Buffer, - BufferSize, - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + PAGED_CODE(); + + /* Easy way out */ + if(BufferSize == 0) return STATUS_INVALID_PARAMETER_7; + + /* Check the Parameters for validity */ + if(PreviousMode != KernelMode) { + + _SEH_TRY { + + ProbeForWrite(ProfileHandle, + sizeof(HANDLE), + sizeof(ULONG)); + + ProbeForWrite(Buffer, + BufferSize, + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - /* - * Reference the associated process - */ - if (Process != NULL) - { - Status = ObReferenceObjectByHandle(Process, - PROCESS_QUERY_INFORMATION, - PsProcessType, - PreviousMode, - (PVOID*)&pProcess, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - } - else - { - pProcess = NULL; - if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, - PreviousMode)) - { - DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n"); - return STATUS_PRIVILEGE_NOT_HELD; - } + if(!NT_SUCCESS(Status)) return Status; } - /* - * Check the parameters - */ - if ((pProcess == NULL && ImageBase < (PVOID)KERNEL_BASE) || - (pProcess != NULL && ImageBase >= (PVOID)KERNEL_BASE)) - { - return(STATUS_INVALID_PARAMETER_3); - } - if (((ImageSize >> BucketSize) * 4) >= BufferSize) - { - return(STATUS_BUFFER_TOO_SMALL); - } - if (ProfileSource != ProfileTime) - { - return(STATUS_INVALID_PARAMETER_9); - } - if (Affinity != 0) - { - return(STATUS_INVALID_PARAMETER_10); + /* Check if a process was specified */ + if (Process) { + + /* Reference it */ + Status = ObReferenceObjectByHandle(Process, + PROCESS_QUERY_INFORMATION, + PsProcessType, + PreviousMode, + (PVOID*)&pProcess, + NULL); + if (!NT_SUCCESS(Status)) return(Status); + + } else { + + /* No process was specified, which means a System-Wide Profile */ + pProcess = NULL; + + /* For this, we need to check the Privilege */ + if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode)) { + + DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n"); + return STATUS_PRIVILEGE_NOT_HELD; + } } - /* - * Create the object - */ - InitializeObjectAttributes(&ObjectAttributes, + /* Create the object */ + InitializeObjectAttributes(&ObjectAttributes, + NULL, + 0, + NULL, + NULL); + Status = ObCreateObject(KernelMode, + ExProfileObjectType, + &ObjectAttributes, + PreviousMode, + NULL, + sizeof(EPROFILE), + 0, + 0, + (PVOID*)&Profile); + if (!NT_SUCCESS(Status)) return(Status); + + /* Initialize it */ + Profile->ImageBase = ImageBase; + Profile->ImageSize = ImageSize; + Profile->Buffer = Buffer; + Profile->BufferSize = BufferSize; + Profile->BucketSize = BucketSize; + Profile->LockedBuffer = NULL; + Profile->Affinity = Affinity; + Profile->Process = pProcess; + + /* Insert into the Object Tree */ + Status = ObInsertObject ((PVOID)Profile, NULL, + PROFILE_CONTROL, 0, NULL, - NULL); - - Status = ObCreateObject(KernelMode, - ExProfileObjectType, - &ObjectAttributes, - PreviousMode, - NULL, - sizeof(KPROFILE), - 0, - 0, - (PVOID*)&Profile); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - - /* - * Initialize it - */ - Profile->Base = ImageBase; - Profile->Size = ImageSize; - Profile->BucketShift = BucketSize; - Profile->BufferMdl = MmCreateMdl(NULL, Buffer, BufferSize); - if(Profile->BufferMdl == NULL) { - DPRINT("MmCreateMdl: Out of memory!"); - ObDereferenceObject (Profile); - return(STATUS_NO_MEMORY); - } - MmProbeAndLockPages(Profile->BufferMdl, UserMode, IoWriteAccess); - Profile->Buffer = MmGetSystemAddressForMdl(Profile->BufferMdl); - Profile->BufferSize = BufferSize; - Profile->ProcessorMask = Affinity; - Profile->Started = FALSE; - Profile->Process = pProcess; - - /* - * Insert the profile into the profile list data structures - */ - KiInsertProfile(Profile); - - Status = ObInsertObject ((PVOID)Profile, - NULL, - STANDARD_RIGHTS_ALL, - 0, - NULL, - &hProfile); - if (!NT_SUCCESS(Status)) - { - ObDereferenceObject (Profile); - return Status; - } - - /* - * Copy the created handle back to the caller - */ - _SEH_TRY - { - *ProfileHandle = hProfile; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - ObDereferenceObject(Profile); - - return Status; -} - -NTSTATUS STDCALL -NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource, - OUT PULONG Interval) -{ - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForWrite(Interval, - sizeof(ULONG), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + &hProfile); + ObDereferenceObject(Profile); - if(!NT_SUCCESS(Status)) - { - return Status; + /* Check for Success */ + if (!NT_SUCCESS(Status)) { + + /* Dereference Process on failure */ + if (pProcess) ObDereferenceObject(pProcess); + return Status; } - } - - if (ProfileSource == ProfileTime) - { - ULONG ReturnInterval; - - /* FIXME: What units does this use, for now nanoseconds */ - ReturnInterval = 100; - - _SEH_TRY - { - *Interval = ReturnInterval; - } - _SEH_HANDLE - { + + /* Copy the created handle back to the caller*/ + _SEH_TRY { + + *ProfileHandle = hProfile; + + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - return Status; - } - return STATUS_INVALID_PARAMETER_2; + } _SEH_END; + + /* Return Status */ + return Status; } -NTSTATUS STDCALL -NtSetIntervalProfile(IN ULONG Interval, - IN KPROFILE_SOURCE Source) +NTSTATUS +STDCALL +NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter, + OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL) { - return(STATUS_NOT_IMPLEMENTED); + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + LARGE_INTEGER PerfFrequency; + NTSTATUS Status = STATUS_SUCCESS; + + /* Check the Parameters for validity */ + if(PreviousMode != KernelMode) { + + _SEH_TRY { + + ProbeForWrite(PerformanceCounter, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + + ProbeForWrite(PerformanceFrequency, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + _SEH_TRY { + + /* Query the Kernel */ + *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency); + + /* Return Frequency if requested */ + if(PerformanceFrequency) { + + *PerformanceFrequency = PerfFrequency; + } + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + return Status; } -NTSTATUS STDCALL +NTSTATUS +STDCALL NtStartProfile(IN HANDLE ProfileHandle) { - PKPROFILE Profile; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; + PEPROFILE Profile; + PKPROFILE KeProfile; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PVOID TempLockedBuffer; + NTSTATUS Status; - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); + PAGED_CODE(); - Status = ObReferenceObjectByHandle(ProfileHandle, - STANDARD_RIGHTS_ALL, - ExProfileObjectType, - PreviousMode, - (PVOID*)&Profile, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); + /* Get the Object */ + Status = ObReferenceObjectByHandle(ProfileHandle, + PROFILE_CONTROL, + ExProfileObjectType, + PreviousMode, + (PVOID*)&Profile, + NULL); + if (!NT_SUCCESS(Status)) return(Status); + + /* To avoid a Race, wait on the Mutex */ + KeWaitForSingleObject(&ExpProfileMutex, + Executive, + KernelMode, + FALSE, + NULL); + + /* The Profile can still be enabled though, so handle that */ + if (Profile->LockedBuffer) { + + /* Release our lock, dereference and return */ + KeReleaseMutex(&ExpProfileMutex, FALSE); + ObDereferenceObject(Profile); + return STATUS_PROFILING_NOT_STOPPED; } - Profile->Started = TRUE; - ObDereferenceObject(Profile); - return(STATUS_SUCCESS); + + /* Allocate a Kernel Profile Object. */ + KeProfile = ExAllocatePoolWithTag(NonPagedPool, + sizeof(EPROFILE), + TAG('P', 'r', 'o', 'f')); + + /* Allocate the Mdl Structure */ + Profile->Mdl = MmCreateMdl(NULL, Profile->Buffer, Profile->BufferSize); + + /* Probe and Lock for Write Access */ + MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess); + + /* Map the pages */ + TempLockedBuffer = MmMapLockedPages(Profile->Mdl, KernelMode); + + /* Initialize the Kernel Profile Object */ + Profile->KeProfile = KeProfile; + KeInitializeProfile(KeProfile, + (PKPROCESS)Profile->Process, + Profile->ImageBase, + Profile->ImageSize, + Profile->BucketSize, + Profile->ProfileSource, + Profile->Affinity); + + /* Start the Profiling */ + KeStartProfile(KeProfile, TempLockedBuffer); + + /* Now it's safe to save this */ + Profile->LockedBuffer = TempLockedBuffer; + + /* Release mutex, dereference and return */ + KeReleaseMutex(&ExpProfileMutex, FALSE); + ObDereferenceObject(Profile); + return STATUS_SUCCESS; } -NTSTATUS STDCALL +NTSTATUS +STDCALL NtStopProfile(IN HANDLE ProfileHandle) { - PKPROFILE Profile; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; + PEPROFILE Profile; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status; - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); + PAGED_CODE(); - Status = ObReferenceObjectByHandle(ProfileHandle, - STANDARD_RIGHTS_ALL, - ExProfileObjectType, - PreviousMode, - (PVOID*)&Profile, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); + /* Get the Object */ + Status = ObReferenceObjectByHandle(ProfileHandle, + PROFILE_CONTROL, + ExProfileObjectType, + PreviousMode, + (PVOID*)&Profile, + NULL); + if (!NT_SUCCESS(Status)) return(Status); + + /* Get the Mutex */ + KeWaitForSingleObject(&ExpProfileMutex, + Executive, + KernelMode, + FALSE, + NULL); + + /* Make sure the Profile Object is really Started */ + if (!Profile->LockedBuffer) { + + Status = STATUS_PROFILING_NOT_STARTED; + goto Exit; } - Profile->Started = FALSE; - ObDereferenceObject(Profile); - return(STATUS_SUCCESS); + + /* Stop the Profile */ + KeStopProfile(Profile->KeProfile); + + /* Unlock the Buffer */ + MmUnmapLockedPages(Profile->LockedBuffer, Profile->Mdl); + MmUnlockPages(Profile->Mdl); + ExFreePool(Profile->KeProfile); + + /* Clear the Locked Buffer pointer, meaning the Object is Stopped */ + Profile->LockedBuffer = NULL; + +Exit: + /* Release Mutex, Dereference and Return */ + KeReleaseMutex(&ExpProfileMutex, FALSE); + ObDereferenceObject(Profile); + return Status; } +NTSTATUS +STDCALL +NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource, + OUT PULONG Interval) +{ + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + ULONG ReturnInterval; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + /* Check the Parameters for validity */ + if(PreviousMode != KernelMode) { + + _SEH_TRY { + + ProbeForWrite(Interval, + sizeof(ULONG), + sizeof(ULONG)); + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + /* Query the Interval */ + ReturnInterval = KeQueryIntervalProfile(ProfileSource); + /* Return the data */ + _SEH_TRY { + + *Interval = ReturnInterval; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + /* Return Success */ + return STATUS_SUCCESS; +} + +NTSTATUS +STDCALL +NtSetIntervalProfile(IN ULONG Interval, + IN KPROFILE_SOURCE Source) +{ + /* Let the Kernel do the job */ + KeSetIntervalProfile(Interval, Source); + + /* Nothing can go wrong */ + return STATUS_SUCCESS; +} diff --git a/reactos/ntoskrnl/ex/rundown.c b/reactos/ntoskrnl/ex/rundown.c index baf8a38248b..580d9bb4e4e 100644 --- a/reactos/ntoskrnl/ex/rundown.c +++ b/reactos/ntoskrnl/ex/rundown.c @@ -1,11 +1,10 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ex/rundown.c * PURPOSE: Rundown Protection Functions * - * PROGRAMMERS: No programmer listed. + * PROGRAMMERS: Alex Ionescu & Thomas Weidenmueller - Implementation */ /* INCLUDES *****************************************************************/ diff --git a/reactos/ntoskrnl/ex/sem.c b/reactos/ntoskrnl/ex/sem.c index 274ce8aed1d..2ec3d0e474e 100644 --- a/reactos/ntoskrnl/ex/sem.c +++ b/reactos/ntoskrnl/ex/sem.c @@ -1,11 +1,11 @@ -/* $Id: ntsem.c 12779 2005-01-04 04:45:00Z gdalsnes $ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ex/sem.c * PURPOSE: Synchronization primitives * - * PROGRAMMERS: David Welch (welch@mcmail.com) + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)- Reformatting, bug fixes. + * David Welch (welch@mcmail.com) */ /* INCLUDES *****************************************************************/ @@ -19,345 +19,336 @@ POBJECT_TYPE ExSemaphoreObjectType; static GENERIC_MAPPING ExSemaphoreMapping = { - STANDARD_RIGHTS_READ | SEMAPHORE_QUERY_STATE, - STANDARD_RIGHTS_WRITE | SEMAPHORE_MODIFY_STATE, - STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | SEMAPHORE_QUERY_STATE, - SEMAPHORE_ALL_ACCESS}; + STANDARD_RIGHTS_READ | SEMAPHORE_QUERY_STATE, + STANDARD_RIGHTS_WRITE | SEMAPHORE_MODIFY_STATE, + STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | SEMAPHORE_QUERY_STATE, + SEMAPHORE_ALL_ACCESS}; -static const INFORMATION_CLASS_INFO ExSemaphoreInfoClass[] = -{ - ICI_SQ_SAME( sizeof(SEMAPHORE_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SemaphoreBasicInformation */ +static const INFORMATION_CLASS_INFO ExSemaphoreInfoClass[] = { + + /* SemaphoreBasicInformation */ + ICI_SQ_SAME( sizeof(SEMAPHORE_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), }; -/* FUNCTIONS *****************************************************************/ - -NTSTATUS STDCALL -ExpCreateSemaphore(PVOID ObjectBody, - PVOID Parent, - PWSTR RemainingPath, - POBJECT_ATTRIBUTES ObjectAttributes) -{ - DPRINT("NtpCreateSemaphore(ObjectBody %x, Parent %x, RemainingPath %S)\n", - ObjectBody, Parent, RemainingPath); - - if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL) - { - return(STATUS_UNSUCCESSFUL); - } - - return(STATUS_SUCCESS); -} - -VOID INIT_FUNCTION +VOID +INIT_FUNCTION ExpInitializeSemaphoreImplementation(VOID) { - ExSemaphoreObjectType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); - - RtlpCreateUnicodeString(&ExSemaphoreObjectType->TypeName, L"Semaphore", NonPagedPool); - - ExSemaphoreObjectType->Tag = TAG('S', 'E', 'M', 'T'); - ExSemaphoreObjectType->PeakObjects = 0; - ExSemaphoreObjectType->PeakHandles = 0; - ExSemaphoreObjectType->TotalObjects = 0; - ExSemaphoreObjectType->TotalHandles = 0; - ExSemaphoreObjectType->PagedPoolCharge = 0; - ExSemaphoreObjectType->NonpagedPoolCharge = sizeof(KSEMAPHORE); - ExSemaphoreObjectType->Mapping = &ExSemaphoreMapping; - ExSemaphoreObjectType->Dump = NULL; - ExSemaphoreObjectType->Open = NULL; - ExSemaphoreObjectType->Close = NULL; - ExSemaphoreObjectType->Delete = NULL; - ExSemaphoreObjectType->Parse = NULL; - ExSemaphoreObjectType->Security = NULL; - ExSemaphoreObjectType->QueryName = NULL; - ExSemaphoreObjectType->OkayToClose = NULL; - ExSemaphoreObjectType->Create = ExpCreateSemaphore; - ExSemaphoreObjectType->DuplicationNotify = NULL; - - ObpCreateTypeObject(ExSemaphoreObjectType); + + /* Create the Semaphore Object */ + ExSemaphoreObjectType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); + RtlInitUnicodeString(&ExSemaphoreObjectType->TypeName, L"Semaphore"); + ExSemaphoreObjectType->Tag = TAG('S', 'E', 'M', 'T'); + ExSemaphoreObjectType->PeakObjects = 0; + ExSemaphoreObjectType->PeakHandles = 0; + ExSemaphoreObjectType->TotalObjects = 0; + ExSemaphoreObjectType->TotalHandles = 0; + ExSemaphoreObjectType->PagedPoolCharge = 0; + ExSemaphoreObjectType->NonpagedPoolCharge = sizeof(KSEMAPHORE); + ExSemaphoreObjectType->Mapping = &ExSemaphoreMapping; + ExSemaphoreObjectType->Dump = NULL; + ExSemaphoreObjectType->Open = NULL; + ExSemaphoreObjectType->Close = NULL; + ExSemaphoreObjectType->Delete = NULL; + ExSemaphoreObjectType->Parse = NULL; + ExSemaphoreObjectType->Security = NULL; + ExSemaphoreObjectType->QueryName = NULL; + ExSemaphoreObjectType->OkayToClose = NULL; + ExSemaphoreObjectType->Create = NULL; + ExSemaphoreObjectType->DuplicationNotify = NULL; + ObpCreateTypeObject(ExSemaphoreObjectType); } /* * @implemented */ -NTSTATUS STDCALL +NTSTATUS +STDCALL NtCreateSemaphore(OUT PHANDLE SemaphoreHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN LONG InitialCount, - IN LONG MaximumCount) + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN LONG InitialCount, + IN LONG MaximumCount) { - PKSEMAPHORE Semaphore; - HANDLE hSemaphore; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + PKSEMAPHORE Semaphore; + HANDLE hSemaphore; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); + PAGED_CODE(); - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(SemaphoreHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObCreateObject(PreviousMode, - ExSemaphoreObjectType, - ObjectAttributes, - PreviousMode, - NULL, - sizeof(KSEMAPHORE), - 0, - 0, - (PVOID*)&Semaphore); - if (NT_SUCCESS(Status)) - { - KeInitializeSemaphore(Semaphore, - InitialCount, - MaximumCount); - - Status = ObInsertObject ((PVOID)Semaphore, - NULL, - DesiredAccess, - 0, - NULL, - &hSemaphore); - - ObDereferenceObject(Semaphore); - - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *SemaphoreHandle = hSemaphore; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtOpenSemaphore(OUT PHANDLE SemaphoreHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes) -{ - HANDLE hSemaphore; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(SemaphoreHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObOpenObjectByName(ObjectAttributes, - ExSemaphoreObjectType, - NULL, - PreviousMode, - DesiredAccess, - NULL, - &hSemaphore); - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *SemaphoreHandle = hSemaphore; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtQuerySemaphore(IN HANDLE SemaphoreHandle, - IN SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass, - OUT PVOID SemaphoreInformation, - IN ULONG SemaphoreInformationLength, - OUT PULONG ReturnLength OPTIONAL) -{ - PKSEMAPHORE Semaphore; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - DefaultQueryInfoBufferCheck(SemaphoreInformationClass, - ExSemaphoreInfoClass, - SemaphoreInformation, - SemaphoreInformationLength, - ReturnLength, - PreviousMode, - &Status); - if(!NT_SUCCESS(Status)) - { - DPRINT1("NtQuerySemaphore() failed, Status: 0x%x\n", Status); - return Status; - } - - Status = ObReferenceObjectByHandle(SemaphoreHandle, - SEMAPHORE_QUERY_STATE, - ExSemaphoreObjectType, - PreviousMode, - (PVOID*)&Semaphore, - NULL); - if(NT_SUCCESS(Status)) - { - switch(SemaphoreInformationClass) - { - case SemaphoreBasicInformation: - { - PSEMAPHORE_BASIC_INFORMATION BasicInfo = (PSEMAPHORE_BASIC_INFORMATION)SemaphoreInformation; - - _SEH_TRY - { - BasicInfo->CurrentCount = KeReadStateSemaphore(Semaphore); - BasicInfo->MaximumCount = Semaphore->Limit; - - if(ReturnLength != NULL) - { - *ReturnLength = sizeof(SEMAPHORE_BASIC_INFORMATION); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - default: - Status = STATUS_NOT_IMPLEMENTED; - break; - } - - ObDereferenceObject(Semaphore); - } - - return Status; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -NtReleaseSemaphore(IN HANDLE SemaphoreHandle, - IN LONG ReleaseCount, - OUT PLONG PreviousCount OPTIONAL) -{ - KPROCESSOR_MODE PreviousMode; - PKSEMAPHORE Semaphore; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousCount != NULL && PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(PreviousCount, - sizeof(LONG), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObReferenceObjectByHandle(SemaphoreHandle, - SEMAPHORE_MODIFY_STATE, - ExSemaphoreObjectType, - PreviousMode, - (PVOID*)&Semaphore, - NULL); - if (NT_SUCCESS(Status)) - { - LONG PrevCount = KeReleaseSemaphore(Semaphore, - IO_NO_INCREMENT, - ReleaseCount, - FALSE); - ObDereferenceObject(Semaphore); + /* Check Output Safety */ + if(PreviousMode != KernelMode) { + + _SEH_TRY { + + ProbeForWrite(SemaphoreHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; - if(PreviousCount != NULL) - { - _SEH_TRY - { - *PreviousCount = PrevCount; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } + if(!NT_SUCCESS(Status)) return Status; + } + + /* Make sure the counts make sense */ + if (!MaximumCount || InitialCount < 0 || InitialCount > MaximumCount) { + + DPRINT("Invalid Count Data!\n"); + return STATUS_INVALID_PARAMETER; + } + + /* Create the Semaphore Object */ + Status = ObCreateObject(PreviousMode, + ExSemaphoreObjectType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(KSEMAPHORE), + 0, + 0, + (PVOID*)&Semaphore); + + /* Check for Success */ + if (NT_SUCCESS(Status)) { + + /* Initialize it */ + KeInitializeSemaphore(Semaphore, + InitialCount, + MaximumCount); + + /* Insert it into the Object Tree */ + Status = ObInsertObject((PVOID)Semaphore, + NULL, + DesiredAccess, + 0, + NULL, + &hSemaphore); + ObDereferenceObject(Semaphore); + + /* Check for success and return handle */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + *SemaphoreHandle = hSemaphore; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } + + /* Return Status */ + return Status; +} + +/* + * @implemented + */ +NTSTATUS +STDCALL +NtOpenSemaphore(OUT PHANDLE SemaphoreHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes) +{ + HANDLE hSemaphore; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + /* Check Output Safety */ + if(PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(SemaphoreHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + /* Open the Object */ + Status = ObOpenObjectByName(ObjectAttributes, + ExSemaphoreObjectType, + NULL, + PreviousMode, + DesiredAccess, + NULL, + &hSemaphore); + + /* Check for success and return handle */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + *SemaphoreHandle = hSemaphore; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + + /* Return Status */ + return Status; +} + +/* + * @implemented + */ +NTSTATUS +STDCALL +NtQuerySemaphore(IN HANDLE SemaphoreHandle, + IN SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass, + OUT PVOID SemaphoreInformation, + IN ULONG SemaphoreInformationLength, + OUT PULONG ReturnLength OPTIONAL) +{ + PKSEMAPHORE Semaphore; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + /* Check buffers and class validity */ + DefaultQueryInfoBufferCheck(SemaphoreInformationClass, + ExSemaphoreInfoClass, + SemaphoreInformation, + SemaphoreInformationLength, + ReturnLength, + PreviousMode, + &Status); + if(!NT_SUCCESS(Status)) { + + /* Invalid buffers */ + DPRINT("NtQuerySemaphore() failed, Status: 0x%x\n", Status); + return Status; + } + + /* Get the Object */ + Status = ObReferenceObjectByHandle(SemaphoreHandle, + SEMAPHORE_QUERY_STATE, + ExSemaphoreObjectType, + PreviousMode, + (PVOID*)&Semaphore, + NULL); + + /* Check for success */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + PSEMAPHORE_BASIC_INFORMATION BasicInfo = (PSEMAPHORE_BASIC_INFORMATION)SemaphoreInformation; + + /* Return the basic information */ + BasicInfo->CurrentCount = KeReadStateSemaphore(Semaphore); + BasicInfo->MaximumCount = Semaphore->Limit; + + /* Return length */ + if(ReturnLength) *ReturnLength = sizeof(SEMAPHORE_BASIC_INFORMATION); + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + /* Dereference the Object */ + ObDereferenceObject(Semaphore); } + /* Return status */ return Status; } +/* + * @implemented + */ +NTSTATUS +STDCALL +NtReleaseSemaphore(IN HANDLE SemaphoreHandle, + IN LONG ReleaseCount, + OUT PLONG PreviousCount OPTIONAL) +{ + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PKSEMAPHORE Semaphore; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + /* Check buffer validity */ + if(PreviousCount != NULL && PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(PreviousCount, + sizeof(LONG), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; + } + + /* Make sure count makes sense */ + if (!ReleaseCount) { + + DPRINT("Invalid Release Count\n"); + return STATUS_INVALID_PARAMETER; + } + + /* Get the Object */ + Status = ObReferenceObjectByHandle(SemaphoreHandle, + SEMAPHORE_MODIFY_STATE, + ExSemaphoreObjectType, + PreviousMode, + (PVOID*)&Semaphore, + NULL); + + /* Check for success */ + if (NT_SUCCESS(Status)) { + + /* Release the semaphore */ + LONG PrevCount = KeReleaseSemaphore(Semaphore, + IO_NO_INCREMENT, + ReleaseCount, + FALSE); + ObDereferenceObject(Semaphore); + + /* Return it */ + if(PreviousCount) { + + _SEH_TRY { + + *PreviousCount = PrevCount; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } + + /* Return Status */ + return Status; +} + /* EOF */ diff --git a/reactos/ntoskrnl/ex/stree.c b/reactos/ntoskrnl/ex/stree.c deleted file mode 100644 index f889d379507..00000000000 --- a/reactos/ntoskrnl/ex/stree.c +++ /dev/null @@ -1,1339 +0,0 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ex/stree.c - * PURPOSE: Splay tree support - * - * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) - * - * NOTES: Based on a splay tree implementation by - * Daniel Stenberg - * http://www.contactor.se/~dast/stuff/dsplay-1.2.tar.gz - */ - -#include -#define NDEBUG -#include - -/* DATA **********************************************************************/ - -#define WEIGHT 1 -#undef UNIQUE_KEYS - -typedef struct _SPLAY_TREE_NODE -{ - /* Children on this branch that has smaller keys than this node */ - struct _SPLAY_TREE_NODE * SmallerChild; - - /* Children on this branch that has larger keys than this node */ - struct _SPLAY_TREE_NODE * LargerChild; - - /* Points to a node with identical key */ - struct _SPLAY_TREE_NODE * Same; - - /* Key of this node */ - PVOID Key; - - /* Value of this node */ - PVOID Value; - - /* The number of nodes rooted here */ - LONG Weight; -} SPLAY_TREE_NODE, *PSPLAY_TREE_NODE; - -typedef struct _TRAVERSE_CONTEXT { - PTRAVERSE_ROUTINE Routine; - PVOID Context; -} TRAVERSE_CONTEXT, *PTRAVERSE_CONTEXT; - -#define SPLAY_INDEX 0 -#define SEARCH_INDEX 1 -#define INSERT_INDEX 2 -#define REMOVE_INDEX 3 - -typedef PSPLAY_TREE_NODE (*PSPLAY_TREE_SPLAY)(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node); - -typedef BOOLEAN (*PSPLAY_TREE_SEARCH)(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE * ReturnNode); - -typedef PSPLAY_TREE_NODE (*PSPLAY_TREE_INSERT)(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE New); - -typedef PSPLAY_TREE_NODE (*PSPLAY_TREE_REMOVE)(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE * RemovedNode); - -/* FUNCTIONS ****************************************************************/ - -#define ExpSplayTreeRootNode(Tree)(((PSPLAY_TREE) (Tree))->RootNode) -#define ExpSplayTreeNodeKey(Node)((Node)->Key) -#define ExpSplayTreeNodeValue(Node)((Node)->Value) -#define ExpSplayTreeSmallerChildNode(Node)((Node)->SmallerChild) -#define ExpSplayTreeLargerChildNode(Node)((Node)->LargerChild) -#define ExpSplayTreeNodeEqual(Equality)((Equality) == 0) -#define ExpSplayTreeNodeLess(Equality)((Equality) < 0) -#define ExpSplayTreeNodeMore(Equality)((Equality) > 0) -#define ExpSplayTreeNodeSame(Node)((Node)->Same) -#define ExpSplayTreeNodeWeight(Node)((Node)->Weight) -#define ExpSplayTreeNodeGetWeight(Node)(((Node) == NULL) ? 0 : ((Node)->Weight)) -#define ExpSplayTreeNodeSetWeight(Node, _Weight)((Node)->Weight = (_Weight)) - -#define KEY_NOTUSED (PVOID)-1 - - -/* - * Lock the splay tree - */ -inline VOID -ExpLockSplayTree(PSPLAY_TREE Tree, - PKIRQL OldIrql) -{ - if (Tree->UseNonPagedPool) - { - KeAcquireSpinLock(&Tree->Lock.NonPaged, OldIrql); - } - else - { - ExAcquireFastMutex(&Tree->Lock.Paged); - } -} - - -/* - * Unlock the splay tree - */ -inline VOID -ExpUnlockSplayTree(PSPLAY_TREE Tree, - PKIRQL OldIrql) -{ - if (Tree->UseNonPagedPool) - { - KeReleaseSpinLock(&Tree->Lock.NonPaged, *OldIrql); - } - else - { - ExReleaseFastMutex(&Tree->Lock.Paged); - } -} - - -/* - * Allocate resources for a new node and initialize it. - */ -inline PSPLAY_TREE_NODE -ExpCreateSplayTreeNode(PSPLAY_TREE Tree, - PVOID Value) -{ - PSPLAY_TREE_NODE Node; - - if (Tree->UseNonPagedPool) - { - Node = (PSPLAY_TREE_NODE) ExAllocateFromNPagedLookasideList(&Tree->List.NonPaged); - } - else - { - Node = (PSPLAY_TREE_NODE) ExAllocateFromPagedLookasideList(&Tree->List.Paged); - } - - if (Node) - { - ExpSplayTreeSmallerChildNode(Node) = NULL; - ExpSplayTreeLargerChildNode(Node) = NULL; - ExpSplayTreeNodeValue(Node) = Value; - } - - return Node; -} - -/* - * Release resources for the node. - */ -inline VOID -ExpDestroySplayTreeNode(PSPLAY_TREE Tree, - PSPLAY_TREE_NODE Node) -{ - if (Tree->UseNonPagedPool) - { - ExFreeToNPagedLookasideList(&Tree->List.NonPaged, Node); - } - else - { - ExFreeToPagedLookasideList(&Tree->List.Paged, Node); - } -} - - -/* - * Splay using the key 'Key' (which may or may not be in the tree). The starting - * root is Node. - * The lock for the tree must be acquired when this routine is called. - * This routine does not maintain weight information. - */ -PSPLAY_TREE_NODE -ExpSplayTreeNoWeight(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node) -{ - PSPLAY_TREE_NODE l; - PSPLAY_TREE_NODE r; - PSPLAY_TREE_NODE y; - LONG ChildEquality; - SPLAY_TREE_NODE N; - LONG Equality; - - if (Node == NULL) - return Node; - - ExpSplayTreeSmallerChildNode(&N) = NULL; - ExpSplayTreeLargerChildNode(&N) = NULL; - l = &N; - r = &N; - - for (;;) - { - Equality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)); - - if (ExpSplayTreeNodeLess(Equality)) - { - if (ExpSplayTreeSmallerChildNode(Node) == NULL) - break; - - ChildEquality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(ExpSplayTreeSmallerChildNode(Node))); - if (ExpSplayTreeNodeLess(ChildEquality)) - { - y = ExpSplayTreeSmallerChildNode(Node); /* Rotate smaller */ - ExpSplayTreeSmallerChildNode(Node) = ExpSplayTreeLargerChildNode(y); - ExpSplayTreeLargerChildNode(y) = Node; - - Node = y; - if (ExpSplayTreeSmallerChildNode(Node) == NULL) - break; - } - - ExpSplayTreeSmallerChildNode(r) = Node; /* Link smaller */ - r = Node; - Node = ExpSplayTreeSmallerChildNode(Node); - } - else if (ExpSplayTreeNodeMore(Equality)) - { - if (ExpSplayTreeLargerChildNode(Node) == NULL) - break; - - ChildEquality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(ExpSplayTreeLargerChildNode(Node))); - if (ExpSplayTreeNodeMore(ChildEquality)) - { - y = ExpSplayTreeLargerChildNode(Node); /* Rotate larger */ - ExpSplayTreeLargerChildNode(Node) = ExpSplayTreeSmallerChildNode(y); - ExpSplayTreeSmallerChildNode(y) = Node; - - Node = y; - if (ExpSplayTreeLargerChildNode(Node) == NULL) - break; - } - - ExpSplayTreeLargerChildNode(l) = Node; /* Link larger */ - l = Node; - Node = ExpSplayTreeLargerChildNode(Node); - } - else - { - break; - } - } - - ExpSplayTreeLargerChildNode(l) = NULL; - ExpSplayTreeSmallerChildNode(r) = NULL; - - ExpSplayTreeLargerChildNode(l) = ExpSplayTreeSmallerChildNode(Node); /* Assemble */ - ExpSplayTreeSmallerChildNode(r) = ExpSplayTreeLargerChildNode(Node); - ExpSplayTreeSmallerChildNode(Node) = ExpSplayTreeLargerChildNode(&N); - ExpSplayTreeLargerChildNode(Node) = ExpSplayTreeSmallerChildNode(&N); - - return Node; -} - - -/* - * Splay using the key 'Key' (which may or may not be in the tree). The starting - * root is Node. - * The lock for the tree must be acquired when this routine is called. - * This routine maintains weight information. - */ -PSPLAY_TREE_NODE -ExpSplayTreeWeight(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node) -{ - PSPLAY_TREE_NODE l; - PSPLAY_TREE_NODE r; - PSPLAY_TREE_NODE y; - LONG ChildEquality; - SPLAY_TREE_NODE N; - LONG Equality; -#ifdef WEIGHT - LONG RootWeight; - LONG Weight1; - LONG Weight2; -#endif - - if (Node == NULL) - return Node; - - ExpSplayTreeSmallerChildNode(&N) = NULL; - ExpSplayTreeLargerChildNode(&N) = NULL; - l = &N; - r = &N; - -#ifdef WEIGHT - RootWeight = ExpSplayTreeNodeGetWeight(Node); - Weight1 = 0; - Weight2 = 0; -#endif - - for (;;) - { - Equality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)); - - if (ExpSplayTreeNodeLess(Equality)) - { - if (ExpSplayTreeSmallerChildNode(Node) == NULL) - break; - - ChildEquality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(ExpSplayTreeSmallerChildNode(Node))); - if (ExpSplayTreeNodeLess(ChildEquality)) - { - y = ExpSplayTreeSmallerChildNode(Node); /* Rotate smaller */ - ExpSplayTreeSmallerChildNode(Node) = ExpSplayTreeLargerChildNode(y); - ExpSplayTreeLargerChildNode(y) = Node; - -#ifdef WEIGHT - ExpSplayTreeNodeSetWeight(Node, ExpSplayTreeNodeGetWeight(ExpSplayTreeSmallerChildNode(Node)) - + ExpSplayTreeNodeGetWeight(ExpSplayTreeLargerChildNode(Node)) + 1); -#endif - - Node = y; - if (ExpSplayTreeSmallerChildNode(Node) == NULL) - break; - } - - ExpSplayTreeSmallerChildNode(r) = Node; /* Link smaller */ - r = Node; - Node = ExpSplayTreeSmallerChildNode(Node); - -#ifdef WEIGHT - Weight2 += 1 + ExpSplayTreeNodeGetWeight(ExpSplayTreeLargerChildNode(r)); -#endif - } - else if (ExpSplayTreeNodeMore(Equality)) - { - if (ExpSplayTreeLargerChildNode(Node) == NULL) - break; - - ChildEquality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(ExpSplayTreeLargerChildNode(Node))); - if (ExpSplayTreeNodeMore(ChildEquality)) - { - y = ExpSplayTreeLargerChildNode(Node); /* Rotate larger */ - ExpSplayTreeLargerChildNode(Node) = ExpSplayTreeSmallerChildNode(y); - ExpSplayTreeSmallerChildNode(y) = Node; - -#ifdef WEIGHT - ExpSplayTreeNodeSetWeight(Node, ExpSplayTreeNodeGetWeight(ExpSplayTreeSmallerChildNode(Node)) - + ExpSplayTreeNodeGetWeight(ExpSplayTreeLargerChildNode(Node)) + 1); -#endif - - Node = y; - if (ExpSplayTreeLargerChildNode(Node) == NULL) - break; - } - - ExpSplayTreeLargerChildNode(l) = Node; /* Link larger */ - l = Node; - Node = ExpSplayTreeLargerChildNode(Node); - -#ifdef WEIGHT - Weight1 += 1 + ExpSplayTreeNodeGetWeight(ExpSplayTreeSmallerChildNode(l)); -#endif - - } - else - { - break; - } - } - -#ifdef WEIGHT - Weight1 += ExpSplayTreeNodeGetWeight(ExpSplayTreeSmallerChildNode(Node)); /* Now Weight1 and Weight2 are the weights of */ - Weight2 += ExpSplayTreeNodeGetWeight(ExpSplayTreeLargerChildNode(Node)); /* The 'smaller' and 'larger' trees we just built. */ - ExpSplayTreeNodeSetWeight(Node, Weight1 + Weight2 + 1); -#endif - - ExpSplayTreeLargerChildNode(l) = NULL; - ExpSplayTreeSmallerChildNode(r) = NULL; - -#ifdef WEIGHT - /* The following two loops correct the weight fields of the right path from - * the left child of the root and the right path from the left child of the - * root. - */ - for (y = ExpSplayTreeLargerChildNode(&N); y != NULL; y = ExpSplayTreeLargerChildNode(y)) { - ExpSplayTreeNodeSetWeight(y, Weight1); - Weight1 -= 1 + ExpSplayTreeNodeGetWeight(ExpSplayTreeSmallerChildNode(y)); - } - for (y = ExpSplayTreeSmallerChildNode(&N); y != NULL; y = ExpSplayTreeSmallerChildNode(y)) { - ExpSplayTreeNodeSetWeight(y, Weight2); - Weight2 -= 1 + ExpSplayTreeNodeGetWeight(ExpSplayTreeLargerChildNode(y)); - } -#endif - - ExpSplayTreeLargerChildNode(l) = ExpSplayTreeSmallerChildNode(Node); /* Assemble */ - ExpSplayTreeSmallerChildNode(r) = ExpSplayTreeLargerChildNode(Node); - ExpSplayTreeSmallerChildNode(Node) = ExpSplayTreeLargerChildNode(&N); - ExpSplayTreeLargerChildNode(Node) = ExpSplayTreeSmallerChildNode(&N); - - return Node; -} - - -inline PSPLAY_TREE_NODE -ExpSplayTree(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node) -{ - return (*((PSPLAY_TREE_SPLAY)Tree->Reserved[SPLAY_INDEX]))(Tree, Key, Node); -} - - -/* - * The lock for the tree must be acquired when this routine is called. - * This routine does not maintain weight information. - */ -BOOLEAN -ExpSearchSplayTreeNoWeight(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE * ReturnNode) -{ - LONG Equality; - - if (Node == NULL) - return FALSE; - - Node = ExpSplayTree(Tree, Key, Node); - - Equality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)); - if (ExpSplayTreeNodeEqual(Equality)) - { - /* Found the key */ - - *ReturnNode = Node; - return TRUE; - } - else - { - *ReturnNode = NULL; /* No match */ - return FALSE; /* It wasn't there */ - } -} - - -/* - * The lock for the tree must be acquired when this routine is called. - * This routine maintains weight information. - */ -BOOLEAN -ExpSearchSplayTreeWeight(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE * ReturnNode) -{ - PSPLAY_TREE_NODE x = NULL; - LONG Equality; -#ifdef WEIGHT - LONG tweight; -#endif - - if (Node == NULL) - return FALSE; - -#ifdef WEIGHT - tweight = ExpSplayTreeNodeGetWeight(Node); -#endif - - Node = ExpSplayTree(Tree, Key, Node); - - Equality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)); - if (ExpSplayTreeNodeEqual(Equality)) - { - /* Found the key */ - -#ifdef WEIGHT - if (x != NULL) - { - ExpSplayTreeNodeSetWeight(x, tweight - 1); - } -#endif - - *ReturnNode = Node; - return TRUE; - } - else - { - *ReturnNode = NULL; /* No match */ - return FALSE; /* It wasn't there */ - } -} - - -inline BOOLEAN -ExpSearchSplayTree(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE * ReturnNode) -{ - return (*((PSPLAY_TREE_SEARCH)Tree->Reserved[SEARCH_INDEX]))(Tree, Key, Node, ReturnNode); -} - - -/* - * The lock for the tree must be acquired when this routine is called. - * This routine does not maintain weight information. - */ -PSPLAY_TREE_NODE -ExpInsertSplayTreeNoWeight(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE New) -{ - if (New == NULL) - return Node; - - if (Node != NULL) - { - LONG Equality; - - Node = ExpSplayTree(Tree, Key, Node); - - Equality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)); - if (ExpSplayTreeNodeEqual(Equality)) - { - -#ifdef UNIQUE_KEYS - - /* This is how to prevent the same node key getting added twice */ - return NULL; - -#else - - /* It already exists one of this size */ - - ExpSplayTreeNodeSame(New) = Node; - ExpSplayTreeNodeKey(New) = Key; - ExpSplayTreeSmallerChildNode(New) = ExpSplayTreeSmallerChildNode(Node); - ExpSplayTreeLargerChildNode(New) = ExpSplayTreeLargerChildNode(Node); - - ExpSplayTreeSmallerChildNode(Node) = New; - ExpSplayTreeNodeKey(Node) = KEY_NOTUSED; - - /* New root node */ - return New; - -#endif - - } - } - - if (Node == NULL) - { - ExpSplayTreeSmallerChildNode(New) = NULL; - ExpSplayTreeLargerChildNode(New) = NULL; - } - else if (ExpSplayTreeNodeLess((*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)))) - { - ExpSplayTreeSmallerChildNode(New) = ExpSplayTreeSmallerChildNode(Node); - ExpSplayTreeLargerChildNode(New) = Node; - ExpSplayTreeSmallerChildNode(Node) = NULL; - } - else - { - ExpSplayTreeLargerChildNode(New) = ExpSplayTreeLargerChildNode(Node); - ExpSplayTreeSmallerChildNode(New) = Node; - ExpSplayTreeLargerChildNode(Node) = NULL; - } - - ExpSplayTreeNodeKey(New) = Key; - -#ifndef UNIQUE_KEYS - /* No identical nodes (yet) */ - ExpSplayTreeNodeSame(New) = NULL; -#endif - - return New; -} - - -/* - * The lock for the tree must be acquired when this routine is called. - * This routine maintains weight information. - */ -PSPLAY_TREE_NODE -ExpInsertSplayTreeWeight(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE New) -{ - if (New == NULL) - return Node; - - if (Node != NULL) - { - LONG Equality; - - Node = ExpSplayTree(Tree, Key, Node); - - Equality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)); - if (ExpSplayTreeNodeEqual(Equality)) - { - -#ifdef UNIQUE_KEYS - - /* This is how to prevent the same node key getting added twice */ - return NULL; - -#else - - /* It already exists one of this size */ - - ExpSplayTreeNodeSame(New) = Node; - ExpSplayTreeNodeKey(New) = Key; - ExpSplayTreeSmallerChildNode(New) = ExpSplayTreeSmallerChildNode(Node); - ExpSplayTreeLargerChildNode(New) = ExpSplayTreeLargerChildNode(Node); - -#ifdef WEIGHT - ExpSplayTreeNodeSetWeight(New, ExpSplayTreeNodeGetWeight(Node)); -#endif - - ExpSplayTreeSmallerChildNode(Node) = New; - ExpSplayTreeNodeKey(Node) = KEY_NOTUSED; - - /* New root node */ - return New; - -#endif - - } - } - - if (Node == NULL) - { - ExpSplayTreeSmallerChildNode(New) = NULL; - ExpSplayTreeLargerChildNode(New) = NULL; - } - else if (ExpSplayTreeNodeLess((*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)))) - { - ExpSplayTreeSmallerChildNode(New) = ExpSplayTreeSmallerChildNode(Node); - ExpSplayTreeLargerChildNode(New) = Node; - ExpSplayTreeSmallerChildNode(Node) = NULL; - -#ifdef WEIGHT - ExpSplayTreeNodeSetWeight(Node, 1 + ExpSplayTreeNodeGetWeight(ExpSplayTreeLargerChildNode(Node))); -#endif - - } - else - { - ExpSplayTreeLargerChildNode(New) = ExpSplayTreeLargerChildNode(Node); - ExpSplayTreeSmallerChildNode(New) = Node; - ExpSplayTreeLargerChildNode(Node) = NULL; - -#ifdef WEIGHT - ExpSplayTreeNodeSetWeight(Node, 1 + ExpSplayTreeNodeGetWeight(ExpSplayTreeSmallerChildNode(Node))); -#endif - - } - - ExpSplayTreeNodeKey(New) = Key; - -#ifdef WEIGHT - ExpSplayTreeNodeSetWeight(New, 1 + ExpSplayTreeNodeGetWeight(ExpSplayTreeSmallerChildNode(New)) - + ExpSplayTreeNodeGetWeight(ExpSplayTreeLargerChildNode(New))); -#endif - -#ifndef UNIQUE_KEYS - /* No identical nodes (yet) */ - ExpSplayTreeNodeSame(New) = NULL; -#endif - - return New; -} - - -inline PSPLAY_TREE_NODE -ExpInsertSplayTree(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE New) -{ - return (*((PSPLAY_TREE_INSERT)Tree->Reserved[INSERT_INDEX]))(Tree, Key, Node, New); -} - - -/* - * Deletes the node with key 'Key' from the tree if it's there. - * Return a pointer to the resulting tree. - * The lock for the tree must be acquired when this routine is called. - * This routine does not maintain weight information. - */ -PSPLAY_TREE_NODE -ExpRemoveSplayTreeNoWeight(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE * RemovedNode) -{ - PSPLAY_TREE_NODE x; - LONG Equality; - - if (Node == NULL) - return NULL; - - Node = ExpSplayTree(Tree, Key, Node); - - Equality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)); - if (ExpSplayTreeNodeEqual(Equality)) - { - /* Found the key */ - -#ifndef UNIQUE_KEYS - /* FIRST! Check if there is a list with identical sizes */ - x = ExpSplayTreeNodeSame(Node); - if (x) - { - /* There is several, pick one from the list */ - - /* 'x' is the new root node */ - - ExpSplayTreeNodeKey(x) = ExpSplayTreeNodeKey(Node); - ExpSplayTreeLargerChildNode(x) = ExpSplayTreeLargerChildNode(Node); - ExpSplayTreeSmallerChildNode(x) = ExpSplayTreeSmallerChildNode(Node); - - *RemovedNode = Node; - return x; - } -#endif - - if (ExpSplayTreeSmallerChildNode(Node) == NULL) - { - x = ExpSplayTreeLargerChildNode(Node); - } - else - { - x = ExpSplayTree(Tree, Key, ExpSplayTreeSmallerChildNode(Node)); - ExpSplayTreeLargerChildNode(x) = ExpSplayTreeLargerChildNode(Node); - } - *RemovedNode = Node; - - return x; - } - else - { - *RemovedNode = NULL; /* No match */ - return Node; /* It wasn't there */ - } -} - - -/* - * Deletes the node with key 'Key' from the tree if it's there. - * Return a pointer to the resulting tree. - * The lock for the tree must be acquired when this routine is called. - * This routine maintains weight information. - */ -PSPLAY_TREE_NODE -ExpRemoveSplayTreeWeight(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE * RemovedNode) -{ - PSPLAY_TREE_NODE x; - LONG Equality; - -#ifdef WEIGHT - LONG tweight; -#endif - - if (Node == NULL) - return NULL; - -#ifdef WEIGHT - tweight = ExpSplayTreeNodeGetWeight(Node); -#endif - - Node = ExpSplayTree(Tree, Key, Node); - - Equality = (*Tree->Compare)(Key, ExpSplayTreeNodeKey(Node)); - if (ExpSplayTreeNodeEqual(Equality)) - { - /* Found the key */ - -#ifndef UNIQUE_KEYS - /* FIRST! Check if there is a list with identical sizes */ - x = ExpSplayTreeNodeSame(Node); - if (x) - { - /* There is several, pick one from the list */ - - /* 'x' is the new root node */ - - ExpSplayTreeNodeKey(x) = ExpSplayTreeNodeKey(Node); - ExpSplayTreeLargerChildNode(x) = ExpSplayTreeLargerChildNode(Node); - ExpSplayTreeSmallerChildNode(x) = ExpSplayTreeSmallerChildNode(Node); - -#ifdef WEIGHT - ExpSplayTreeNodeSetWeight(x, ExpSplayTreeNodeGetWeight(Node)); -#endif - - *RemovedNode = Node; - return x; - } -#endif - - if (ExpSplayTreeSmallerChildNode(Node) == NULL) - { - x = ExpSplayTreeLargerChildNode(Node); - } - else - { - x = ExpSplayTree(Tree, Key, ExpSplayTreeSmallerChildNode(Node)); - ExpSplayTreeLargerChildNode(x) = ExpSplayTreeLargerChildNode(Node); - } - *RemovedNode = Node; - -#ifdef WEIGHT - if (x != NULL) - { - ExpSplayTreeNodeSetWeight(x, tweight - 1); - } -#endif - - return x; - } - else - { - *RemovedNode = NULL; /* No match */ - return Node; /* It wasn't there */ - } -} - - -inline PSPLAY_TREE_NODE -ExpRemoveSplayTree(PSPLAY_TREE Tree, - PVOID Key, - PSPLAY_TREE_NODE Node, - PSPLAY_TREE_NODE * RemovedNode) -{ - return (*((PSPLAY_TREE_REMOVE)Tree->Reserved[REMOVE_INDEX]))(Tree, Key, Node, RemovedNode); -} - - -/* - * The lock for the tree must be acquired when this routine is called. - */ -ULONG -ExpPrintSplayTree(PSPLAY_TREE Tree, - PSPLAY_TREE_NODE Node, - ULONG Distance) -{ - PSPLAY_TREE_NODE n; - ULONG d = 0; - ULONG i; - - if (Node == NULL) - return 0; - - Distance += ExpPrintSplayTree(Tree, ExpSplayTreeLargerChildNode(Node), Distance + 1); - - for (i = 0; i < Distance; i++) - { - DbgPrint(" "); - } - - if (Tree->Weighted) - { - DbgPrint("%d(%d)[%d]", ExpSplayTreeNodeKey(Node), ExpSplayTreeNodeGetWeight(Node), i); - } - else - { - DbgPrint("%d[%d]", ExpSplayTreeNodeKey(Node), i); - } - - for (n = ExpSplayTreeNodeSame(Node); n; n = ExpSplayTreeNodeSame(n)) - { - d += i; - - DbgPrint(" [+]"); - } - - d += i; - - d += ExpPrintSplayTree(Tree, ExpSplayTreeSmallerChildNode(Node), Distance + 1); - - return d; -} - - -#define MAX_DIFF 4 - -#ifdef WEIGHT - -/* - * The lock for the tree must be acquired when this routine is called. - * Returns the new root of the tree. - * Use of this routine could improve performance, or it might not. - * FIXME: Do some performance tests - */ -PSPLAY_TREE_NODE -ExpSplayTreeMaxTreeWeight(PSPLAY_TREE Tree, - PSPLAY_TREE_NODE Node) -{ - PSPLAY_TREE_NODE First = Node; - LONG Diff; - - do - { - Diff = ExpSplayTreeNodeGetWeight(ExpSplayTreeSmallerChildNode(Node)) - - ExpSplayTreeNodeGetWeight(ExpSplayTreeLargerChildNode(Node)); - - if (Diff >= MAX_DIFF) - { - Node = ExpSplayTreeSmallerChildNode(Node); - } - else if (Diff <= -MAX_DIFF) - { - Node = ExpSplayTreeLargerChildNode(Node); - } - else - break; - } while (abs(Diff) >= MAX_DIFF); - - if (Node != First) - return ExpSplayTree(Tree, ExpSplayTreeNodeKey(Node), First); - else - return First; -} - -#endif - - -/* - * Traverse a splay tree using preorder traversal method. - * Returns FALSE, if the traversal was terminated prematurely or - * TRUE if the callback routine did not request that the traversal - * be terminated prematurely. - * The lock for the tree must be acquired when this routine is called. - */ -BOOLEAN -ExpTraverseSplayTreePreorder(PTRAVERSE_CONTEXT Context, - PSPLAY_TREE_NODE Node) -{ - PSPLAY_TREE_NODE n; - - if (Node == NULL) - return TRUE; - - /* Call the traversal routine */ - if (!(*Context->Routine)(Context->Context, - ExpSplayTreeNodeKey(Node), - ExpSplayTreeNodeValue(Node))) - { - return FALSE; - } - - for (n = ExpSplayTreeNodeSame(Node); n; n = ExpSplayTreeNodeSame(n)) - { - /* Call the traversal routine */ - if (!(*Context->Routine)(Context->Context, - ExpSplayTreeNodeKey(n), - ExpSplayTreeNodeValue(n))) - { - return FALSE; - } - } - - /* Traverse 'smaller' subtree */ - ExpTraverseSplayTreePreorder(Context, ExpSplayTreeSmallerChildNode(Node)); - - /* Traverse 'larger' subtree */ - ExpTraverseSplayTreePreorder(Context, ExpSplayTreeLargerChildNode(Node)); - - return TRUE; -} - - -/* - * Traverse a splay tree using inorder traversal method. - * Returns FALSE, if the traversal was terminated prematurely or - * TRUE if the callback routine did not request that the traversal - * be terminated prematurely. - * The lock for the tree must be acquired when this routine is called. - */ -BOOLEAN -ExpTraverseSplayTreeInorder(PTRAVERSE_CONTEXT Context, - PSPLAY_TREE_NODE Node) -{ - PSPLAY_TREE_NODE n; - - if (Node == NULL) - return TRUE; - - /* Traverse 'smaller' subtree */ - ExpTraverseSplayTreeInorder(Context, ExpSplayTreeSmallerChildNode(Node)); - - /* Call the traversal routine */ - if (!(*Context->Routine)(Context->Context, - ExpSplayTreeNodeKey(Node), - ExpSplayTreeNodeValue(Node))) - { - return FALSE; - } - - for (n = ExpSplayTreeNodeSame(Node); n; n = ExpSplayTreeNodeSame(n)) - { - /* Call the traversal routine */ - if (!(*Context->Routine)(Context->Context, - ExpSplayTreeNodeKey(n), - ExpSplayTreeNodeValue(n))) - { - return FALSE; - } - } - - /* Traverse right subtree */ - ExpTraverseSplayTreeInorder(Context, ExpSplayTreeLargerChildNode(Node)); - - return TRUE; -} - - -/* - * Traverse a splay tree using postorder traversal method. - * Returns FALSE, if the traversal was terminated prematurely or - * TRUE if the callback routine did not request that the traversal - * be terminated prematurely. - * The lock for the tree must be acquired when this routine is called. - */ -BOOLEAN -ExpTraverseSplayTreePostorder(PTRAVERSE_CONTEXT Context, - PSPLAY_TREE_NODE Node) -{ - PSPLAY_TREE_NODE n; - - if (Node == NULL) - return TRUE; - - /* Traverse 'smaller' subtree */ - ExpTraverseSplayTreePostorder(Context, ExpSplayTreeSmallerChildNode(Node)); - - /* Traverse 'larger' subtree */ - ExpTraverseSplayTreePostorder(Context, ExpSplayTreeLargerChildNode(Node)); - - /* Call the traversal routine */ - if (!(*Context->Routine)(Context->Context, - ExpSplayTreeNodeKey(Node), - ExpSplayTreeNodeValue(Node))) - { - return FALSE; - } - - for (n = ExpSplayTreeNodeSame(Node); n; n = ExpSplayTreeNodeSame(n)) - { - /* Call the traversal routine */ - if (!(*Context->Routine)(Context->Context, - ExpSplayTreeNodeKey(n), - ExpSplayTreeNodeValue(n))) - { - return FALSE; - } - } - - return TRUE; -} - - -/* - * Default key compare function. Compares the integer values of the two keys. - */ -LONG STDCALL -ExpSplayTreeDefaultCompare(IN PVOID Key1, - IN PVOID Key2) -{ - if (Key1 == Key2) - return 0; - - return (((LONG_PTR) Key1 < (LONG_PTR) Key2) ? -1 : 1); -} - - -/* - * Initializes a splay tree. - */ -BOOLEAN STDCALL -ExInitializeSplayTree(IN PSPLAY_TREE Tree, - IN PKEY_COMPARATOR Compare, - IN BOOLEAN Weighted, - IN BOOLEAN UseNonPagedPool) -{ - RtlZeroMemory(Tree, sizeof(SPLAY_TREE)); - - Tree->Compare = (Compare == NULL) - ? ExpSplayTreeDefaultCompare : Compare; - - Tree->Weighted = Weighted; - - if (Weighted) - { - Tree->Reserved[SPLAY_INDEX] = (PVOID) ExpSplayTreeWeight; - Tree->Reserved[SEARCH_INDEX] = (PVOID) ExpSearchSplayTreeWeight; - Tree->Reserved[INSERT_INDEX] = (PVOID) ExpInsertSplayTreeWeight; - Tree->Reserved[REMOVE_INDEX] = (PVOID) ExpRemoveSplayTreeWeight; - } - else - { - Tree->Reserved[SPLAY_INDEX] = (PVOID) ExpSplayTreeNoWeight; - Tree->Reserved[SEARCH_INDEX] = (PVOID) ExpSearchSplayTreeNoWeight; - Tree->Reserved[INSERT_INDEX] = (PVOID) ExpInsertSplayTreeNoWeight; - Tree->Reserved[REMOVE_INDEX] = (PVOID) ExpRemoveSplayTreeNoWeight; - } - - Tree->UseNonPagedPool = UseNonPagedPool; - - if (UseNonPagedPool) - { - ExInitializeNPagedLookasideList( - &Tree->List.NonPaged, /* Lookaside list */ - NULL, /* Allocate routine */ - NULL, /* Free routine */ - 0, /* Flags */ - sizeof(SPLAY_TREE_NODE), /* Size of each entry */ - TAG('E','X','S','T'), /* Tag */ - 0); /* Depth */ - - KeInitializeSpinLock(&Tree->Lock.NonPaged); - } - else - { - ExInitializePagedLookasideList( - &Tree->List.Paged, /* Lookaside list */ - NULL, /* Allocate routine */ - NULL, /* Free routine */ - 0, /* Flags */ - sizeof(SPLAY_TREE_NODE), /* Size of each entry */ - TAG('E','X','S','T'), /* Tag */ - 0); /* Depth */ - - ExInitializeFastMutex(&Tree->Lock.Paged); - } - - return TRUE; -} - - -/* - * Release resources used by a splay tree. - */ -VOID STDCALL -ExDeleteSplayTree(IN PSPLAY_TREE Tree) -{ - PSPLAY_TREE_NODE RemovedNode; - PSPLAY_TREE_NODE Node; - - /* Remove all nodes */ - Node = ExpSplayTreeRootNode(Tree); - while (Node != NULL) - { - Node = ExpRemoveSplayTree(Tree, Node->Key, Node, &RemovedNode); - - if (RemovedNode != NULL) - { - ExpDestroySplayTreeNode(Tree, RemovedNode); - } - } - - if (Tree->UseNonPagedPool) - { - ExDeleteNPagedLookasideList(&Tree->List.NonPaged); - } - else - { - ExDeletePagedLookasideList(&Tree->List.Paged); - } -} - - -/* - * Insert a value in a splay tree. - */ -VOID STDCALL -ExInsertSplayTree(IN PSPLAY_TREE Tree, - IN PVOID Key, - IN PVOID Value) -{ - PSPLAY_TREE_NODE Node; - PSPLAY_TREE_NODE NewNode; - KIRQL OldIrql; - - /* FIXME: Use SEH for error reporting */ - - NewNode = ExpCreateSplayTreeNode(Tree, Value); - - ExpLockSplayTree(Tree, &OldIrql); - Node = ExpInsertSplayTree(Tree, Key, ExpSplayTreeRootNode(Tree), NewNode); - ExpSplayTreeRootNode(Tree) = Node; - ExpUnlockSplayTree(Tree, &OldIrql); -} - - -/* - * Search for a value associated with a given key in a splay tree. - */ -BOOLEAN STDCALL -ExSearchSplayTree(IN PSPLAY_TREE Tree, - IN PVOID Key, - OUT PVOID * Value) -{ - PSPLAY_TREE_NODE Node; - BOOLEAN Status; - KIRQL OldIrql; - - ExpLockSplayTree(Tree, &OldIrql); - Status = ExpSearchSplayTree(Tree, Key, ExpSplayTreeRootNode(Tree), &Node); - - if (Status) - { - ExpSplayTreeRootNode(Tree) = Node; - *Value = ExpSplayTreeNodeValue(Node); - ExpUnlockSplayTree(Tree, &OldIrql); - return TRUE; - } - else - { - ExpUnlockSplayTree(Tree, &OldIrql); - return FALSE; - } -} - - -/* - * Remove a value associated with a given key from a splay tree. - */ -BOOLEAN STDCALL -ExRemoveSplayTree(IN PSPLAY_TREE Tree, - IN PVOID Key, - IN PVOID * Value) -{ - PSPLAY_TREE_NODE RemovedNode; - PSPLAY_TREE_NODE Node; - KIRQL OldIrql; - - ExpLockSplayTree(Tree, &OldIrql); - Node = ExpRemoveSplayTree(Tree, Key, ExpSplayTreeRootNode(Tree), &RemovedNode); - ExpSplayTreeRootNode(Tree) = Node; - ExpUnlockSplayTree(Tree, &OldIrql); - - if (RemovedNode != NULL) - { - *Value = ExpSplayTreeNodeValue(RemovedNode); - ExpDestroySplayTreeNode(Tree, RemovedNode); - return TRUE; - } - else - { - return FALSE; - } -} - - -/* - * Return the weight of the root node in the splay tree. - * The returned value is the number of nodes in the tree. - */ -BOOLEAN STDCALL -ExWeightOfSplayTree(IN PSPLAY_TREE Tree, - OUT PULONG Weight) -{ - KIRQL OldIrql; - - ExpLockSplayTree(Tree, &OldIrql); - - if (!Tree->Weighted) - { - ExpUnlockSplayTree(Tree, &OldIrql); - return FALSE; - } - - *Weight = ExpSplayTreeNodeWeight(ExpSplayTreeRootNode(Tree)); - ExpUnlockSplayTree(Tree, &OldIrql); - - return TRUE; -} - - -/* - * Traverse a splay tree using either preorder, inorder or postorder - * traversal method. - * Returns FALSE, if the traversal was terminated prematurely or - * TRUE if the callback routine did not request that the traversal - * be terminated prematurely. - */ -BOOLEAN STDCALL -ExTraverseSplayTree(IN PSPLAY_TREE Tree, - IN TRAVERSE_METHOD Method, - IN PTRAVERSE_ROUTINE Routine, - IN PVOID Context) -{ - TRAVERSE_CONTEXT tc; - BOOLEAN Status; - KIRQL OldIrql; - - tc.Routine = Routine; - tc.Context = Context; - - ExpLockSplayTree(Tree, &OldIrql); - - if (ExpSplayTreeRootNode(Tree) == NULL) - { - ExpUnlockSplayTree(Tree, &OldIrql); - return TRUE; - } - - switch (Method) - { - case TraverseMethodPreorder: - Status = ExpTraverseSplayTreePreorder(&tc, ExpSplayTreeRootNode(Tree)); - break; - - case TraverseMethodInorder: - Status = ExpTraverseSplayTreeInorder(&tc, ExpSplayTreeRootNode(Tree)); - break; - - case TraverseMethodPostorder: - Status = ExpTraverseSplayTreePostorder(&tc, ExpSplayTreeRootNode(Tree)); - break; - - default: - Status = FALSE; - break; - } - - ExpUnlockSplayTree(Tree, &OldIrql); - - return Status; -} - -/* EOF */ diff --git a/reactos/ntoskrnl/ex/sysinfo.c b/reactos/ntoskrnl/ex/sysinfo.c index bf4c528484f..3165f1ec7b5 100644 --- a/reactos/ntoskrnl/ex/sysinfo.c +++ b/reactos/ntoskrnl/ex/sysinfo.c @@ -15,6 +15,7 @@ #define NDEBUG #include +extern PEPROCESS PsIdleProcess; extern ULONG NtGlobalFlag; /* FIXME: it should go in a ddk/?.h */ ULONGLONG STDCALL KeQueryInterruptTime(VOID); @@ -46,17 +47,18 @@ ExGetCurrentProcessorCpuUsage ( PULONG CpuUsage ) { - PKPCR Pcr; + PKPRCB Prcb; ULONG TotalTime; - ULONG PercentTime = 0; ULONGLONG ScaledIdle; - Pcr = KeGetCurrentKPCR(); + Prcb = KeGetCurrentPrcb(); - ScaledIdle = Pcr->PrcbData.IdleThread->KernelTime * 100; - TotalTime = Pcr->PrcbData.KernelTime + Pcr->PrcbData.UserTime; - if (TotalTime) PercentTime = 100 - (ScaledIdle / TotalTime); - CpuUsage = &PercentTime; + ScaledIdle = Prcb->IdleThread->KernelTime * 100; + TotalTime = Prcb->KernelTime + Prcb->UserTime; + if (TotalTime != 0) + *CpuUsage = 100 - (ScaledIdle / TotalTime); + else + *CpuUsage = 0; } /* @@ -70,20 +72,27 @@ ExGetCurrentProcessorCounts ( PULONG ProcessorNumber ) { - PKPCR Pcr; - ULONG TotalTime; - ULONG ThreadTime; - ULONG ProcNumber; + PKPRCB Prcb; - Pcr = KeGetCurrentKPCR(); + Prcb = KeGetCurrentPrcb(); - TotalTime = Pcr->PrcbData.KernelTime + Pcr->PrcbData.UserTime; - ThreadTime = Pcr->PrcbData.CurrentThread->KernelTime; - ProcNumber = Pcr->ProcessorNumber; + *ThreadKernelTime = Prcb->KernelTime + Prcb->UserTime; + *TotalCpuTime = Prcb->CurrentThread->KernelTime; + *ProcessorNumber = KeGetCurrentKPCR()->ProcessorNumber; +} - ThreadKernelTime = &ThreadTime; - TotalCpuTime = &TotalTime; - ProcessorNumber = &ProcNumber; +/* + * @implemented + */ +BOOLEAN +STDCALL +ExIsProcessorFeaturePresent(IN ULONG ProcessorFeature) +{ + /* Quick check to see if it exists at all */ + if (ProcessorFeature >= PROCESSOR_FEATURE_MAX) return(FALSE); + + /* Return our support for it */ + return(SharedUserData->ProcessorFeatures[ProcessorFeature]); } NTSTATUS STDCALL @@ -363,7 +372,7 @@ QSI_DEF(SystemProcessorInformation) { PSYSTEM_PROCESSOR_INFORMATION Spi = (PSYSTEM_PROCESSOR_INFORMATION) Buffer; - PKPCR Pcr; + PKPRCB Prcb; *ReqSize = sizeof (SYSTEM_PROCESSOR_INFORMATION); /* * Check user buffer's size @@ -372,12 +381,12 @@ QSI_DEF(SystemProcessorInformation) { return (STATUS_INFO_LENGTH_MISMATCH); } - Pcr = KeGetCurrentKPCR(); + Prcb = KeGetCurrentPrcb(); Spi->ProcessorArchitecture = 0; /* Intel Processor */ - Spi->ProcessorLevel = Pcr->PrcbData.CpuType; - Spi->ProcessorRevision = Pcr->PrcbData.CpuStep; + Spi->ProcessorLevel = Prcb->CpuType; + Spi->ProcessorRevision = Prcb->CpuStep; Spi->Unknown = 0; - Spi->FeatureBits = Pcr->PrcbData.FeatureBits; + Spi->FeatureBits = Prcb->FeatureBits; DPRINT("Arch %d Level %d Rev 0x%x\n", Spi->ProcessorArchitecture, Spi->ProcessorLevel, Spi->ProcessorRevision); @@ -402,7 +411,7 @@ QSI_DEF(SystemPerformanceInformation) return (STATUS_INFO_LENGTH_MISMATCH); } - TheIdleProcess = PsInitialSystemProcess; /* FIXME */ + TheIdleProcess = PsIdleProcess; Spi->IdleTime.QuadPart = TheIdleProcess->Pcb.KernelTime * 100000LL; @@ -603,13 +612,20 @@ QSI_DEF(SystemProcessInformation) SpiCur->ProcessName.Buffer = (void*)(pCur+curSize); // copy name to the end of the struct - RtlInitAnsiString(&imgName, pr->ImageFileName); - RtlAnsiStringToUnicodeString(&SpiCur->ProcessName, &imgName, FALSE); + if(pr != PsIdleProcess) + { + RtlInitAnsiString(&imgName, pr->ImageFileName); + RtlAnsiStringToUnicodeString(&SpiCur->ProcessName, &imgName, FALSE); + } + else + { + RtlInitUnicodeString(&SpiCur->ProcessName, NULL); + } SpiCur->BasePriority = pr->Pcb.BasePriority; SpiCur->ProcessId = pr->UniqueProcessId; SpiCur->InheritedFromProcessId = pr->InheritedFromUniqueProcessId; - SpiCur->HandleCount = ObpGetHandleCountByHandleTable(&pr->HandleTable); + SpiCur->HandleCount = (pr->ObjectTable ? ObpGetHandleCountByHandleTable(pr->ObjectTable) : 0); SpiCur->VmCounters.PeakVirtualSize = pr->PeakVirtualSize; SpiCur->VmCounters.VirtualSize = pr->VirtualSize.QuadPart; SpiCur->VmCounters.PageFaultCount = pr->LastFaultCount; @@ -660,12 +676,13 @@ QSI_DEF(SystemProcessInformation) else pCur = pCur + curSize + inLen; } while ((pr != syspr) && (pr != NULL)); + + if(pr != NULL) + { + ObDereferenceObject(pr); + } *ReqSize = ovlSize; - if (pr != NULL) - { - ObDereferenceObject(pr); - } return (STATUS_SUCCESS); } @@ -708,38 +725,36 @@ QSI_DEF(SystemDeviceInformation) /* Class 8 - Processor Performance Information */ QSI_DEF(SystemProcessorPerformanceInformation) { - PSYSTEM_PROCESSORTIME_INFO Spi - = (PSYSTEM_PROCESSORTIME_INFO) Buffer; + PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION Spi + = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) Buffer; ULONG i; LARGE_INTEGER CurrentTime; - PKPCR Pcr; + PKPRCB Prcb; - *ReqSize = KeNumberProcessors * sizeof (SYSTEM_PROCESSORTIME_INFO); + *ReqSize = KeNumberProcessors * sizeof (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); /* - * Check user buffer's size + * Check user buffer's size */ - if (Size < KeNumberProcessors * sizeof(SYSTEM_PROCESSORTIME_INFO)) + if (Size < KeNumberProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) { return (STATUS_INFO_LENGTH_MISMATCH); } CurrentTime.QuadPart = KeQueryInterruptTime(); - Pcr = (PKPCR)KPCR_BASE; + Prcb = ((PKPCR)KPCR_BASE)->Prcb; for (i = 0; i < KeNumberProcessors; i++) { - - Spi->TotalProcessorRunTime.QuadPart = (Pcr->PrcbData.IdleThread->KernelTime + Pcr->PrcbData.IdleThread->UserTime) * 100000LL; // IdleTime - Spi->TotalProcessorTime.QuadPart = Pcr->PrcbData.KernelTime * 100000LL; // KernelTime - Spi->TotalProcessorUserTime.QuadPart = Pcr->PrcbData.UserTime * 100000LL; - Spi->TotalDPCTime.QuadPart = Pcr->PrcbData.DpcTime * 100000LL; - Spi->TotalInterruptTime.QuadPart = Pcr->PrcbData.InterruptTime * 100000LL; - Spi->TotalInterrupts = Pcr->PrcbData.InterruptCount; // Interrupt Count + Spi->IdleTime.QuadPart = (Prcb->IdleThread->KernelTime + Prcb->IdleThread->UserTime) * 100000LL; // IdleTime + Spi->KernelTime.QuadPart = Prcb->KernelTime * 100000LL; // KernelTime + Spi->UserTime.QuadPart = Prcb->UserTime * 100000LL; + Spi->DpcTime.QuadPart = Prcb->DpcTime * 100000LL; + Spi->InterruptTime.QuadPart = Prcb->InterruptTime * 100000LL; + Spi->InterruptCount = Prcb->InterruptCount; // Interrupt Count Spi++; -// Pcr++; - Pcr = (PKPCR)((ULONG_PTR)Pcr + PAGE_SIZE); + Prcb = (PKPRCB)((ULONG_PTR)Prcb + PAGE_SIZE); } - + return (STATUS_SUCCESS); } @@ -843,20 +858,20 @@ QSI_DEF(SystemHandleInformation) do { - hCount = hCount + ObpGetHandleCountByHandleTable(&pr->HandleTable); + hCount = hCount + (pr->ObjectTable ? ObpGetHandleCountByHandleTable(pr->ObjectTable) : 0); pr = PsGetNextProcess(pr); if ((pr == syspr) || (pr == NULL)) break; } while ((pr != syspr) && (pr != NULL)); + + if(pr != NULL) + { + ObDereferenceObject(pr); + } DPRINT("SystemHandleInformation 2\n"); - if (pr != NULL) - { - ObDereferenceObject(pr); - } - curSize = sizeof(SYSTEM_HANDLE_INFORMATION)+ ( (sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO) * hCount) - (sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO) )); @@ -877,9 +892,9 @@ QSI_DEF(SystemHandleInformation) do { - int Count = 0, HandleCount = 0; + int Count = 0, HandleCount; - HandleCount = ObpGetHandleCountByHandleTable(&pr->HandleTable); + HandleCount = (pr->ObjectTable ? ObpGetHandleCountByHandleTable(pr->ObjectTable) : 0); for (Count = 0; HandleCount > 0 ; HandleCount--) { @@ -894,11 +909,10 @@ QSI_DEF(SystemHandleInformation) break; } while ((pr != syspr) && (pr != NULL)); - - if (pr != NULL) - { - ObDereferenceObject(pr); - } + if(pr != NULL) + { + ObDereferenceObject(pr); + } DPRINT("SystemHandleInformation 4\n"); return (STATUS_SUCCESS); @@ -1003,12 +1017,35 @@ QSI_DEF(SystemPoolTagInformation) return (STATUS_NOT_IMPLEMENTED); } -/* Class 23 - Interrupt Information */ +/* Class 23 - Interrupt Information for all processors */ QSI_DEF(SystemInterruptInformation) { - /* FIXME */ - DPRINT1("NtQuerySystemInformation - SystemInterruptInformation not implemented\n"); - return (STATUS_NOT_IMPLEMENTED); + PKPRCB Prcb; + UINT i; + ULONG ti; + PSYSTEM_INTERRUPT_INFORMATION sii = (PSYSTEM_INTERRUPT_INFORMATION)Buffer; + + if(Size < KeNumberProcessors * sizeof(SYSTEM_INTERRUPT_INFORMATION)) + { + return (STATUS_INFO_LENGTH_MISMATCH); + } + + ti = KeQueryTimeIncrement(); + + Prcb = ((PKPCR)KPCR_BASE)->Prcb; + for (i = 0; i < KeNumberProcessors; i++) + { + sii->ContextSwitches = Prcb->KeContextSwitches; + sii->DpcCount = 0; /* FIXME */ + sii->DpcRate = 0; /* FIXME */ + sii->TimeIncrement = ti; + sii->DpcBypassCount = 0; /* FIXME */ + sii->ApcBypassCount = 0; /* FIXME */ + sii++; + Prcb = (PKPRCB)((ULONG_PTR)Prcb + PAGE_SIZE); + } + + return STATUS_SUCCESS; } /* Class 24 - DPC Behaviour Information */ @@ -1041,7 +1078,7 @@ QSI_DEF(SystemFullMemoryInformation) } DPRINT("SystemFullMemoryInformation\n"); - TheIdleProcess = PsInitialSystemProcess; /* FIXME */ + TheIdleProcess = PsIdleProcess; DPRINT("PID: %d, KernelTime: %u PFFree: %d PFUsed: %d\n", TheIdleProcess->UniqueProcessId, diff --git a/reactos/ntoskrnl/ex/timer.c b/reactos/ntoskrnl/ex/timer.c index fa0a2540d3d..b54e6c3466e 100644 --- a/reactos/ntoskrnl/ex/timer.c +++ b/reactos/ntoskrnl/ex/timer.c @@ -1,5 +1,4 @@ -/* $Id: nttimer.c 12779 2005-01-04 04:45:00Z gdalsnes $ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ex/timer.c @@ -12,6 +11,8 @@ /* INCLUDES *****************************************************************/ #include + +#define NDEBUG #include /* TYPES ********************************************************************/ @@ -23,7 +24,6 @@ typedef struct _ETIMER { KDPC TimerDpc; LIST_ENTRY ActiveTimerListEntry; KSPIN_LOCK Lock; - LONG Period; BOOLEAN ApcAssociated; BOOLEAN WakeTimer; LIST_ENTRY WakeTimerListEntry; @@ -54,6 +54,58 @@ static const INFORMATION_CLASS_INFO ExTimerInfoClass[] = { /* FUNCTIONS *****************************************************************/ +VOID +STDCALL +ExTimerRundown(VOID) +{ + PETHREAD Thread = PsGetCurrentThread(); + KIRQL OldIrql; + PLIST_ENTRY CurrentEntry; + PETIMER Timer; + + /* Lock the Thread's Active Timer List*/ + KeAcquireSpinLock(&Thread->ActiveTimerListLock, &OldIrql); + + while (!IsListEmpty(&Thread->ActiveTimerListHead)) + { + + /* Remove a Timer */ + CurrentEntry = RemoveTailList(&Thread->ActiveTimerListHead); + + /* Get the Timer */ + Timer = CONTAINING_RECORD(CurrentEntry, ETIMER, ActiveTimerListEntry); + + ASSERT (Timer->ApcAssociated); + Timer->ApcAssociated = FALSE; + + DPRINT("Timer, ThreadList: %x, %x\n", Timer, Thread); + + /* Unlock the list */ + KeReleaseSpinLockFromDpcLevel(&Thread->ActiveTimerListLock); + + /* Lock the Timer */ + KeAcquireSpinLockAtDpcLevel(&Timer->Lock); + + ASSERT (&Thread->Tcb == Timer->TimerApc.Thread); + + KeCancelTimer(&Timer->KeTimer); + KeRemoveQueueDpc(&Timer->TimerDpc); + KeRemoveQueueApc(&Timer->TimerApc); + + + /* Unlock the Timer */ + KeReleaseSpinLock(&Timer->Lock, OldIrql); + + /* Dereference it, if needed */ + ObDereferenceObject(Timer); + + /* Loop again */ + KeAcquireSpinLock(&Thread->ActiveTimerListLock, &OldIrql); + } + + KeReleaseSpinLock(&Thread->ActiveTimerListLock, OldIrql); +} + VOID STDCALL ExpDeleteTimer(PVOID ObjectBody) @@ -67,11 +119,12 @@ ExpDeleteTimer(PVOID ObjectBody) KeAcquireSpinLock(&ExpWakeListLock, &OldIrql); /* Check if it has a Wait List */ - if (!IsListEmpty(&Timer->WakeTimerListEntry)) { + if (Timer->WakeTimer) { /* Remove it from the Wait List */ DPRINT("Removing wake list\n"); RemoveEntryList(&Timer->WakeTimerListEntry); + Timer->WakeTimer = FALSE; } /* Release the Wake List */ @@ -124,9 +177,9 @@ ExpTimerApcKernelRoutine(PKAPC Apc, PVOID* SystemArguemnt2) { PETIMER Timer; - PETHREAD CurrentThread = PsGetCurrentThread(); KIRQL OldIrql; - + PETHREAD CurrentThread = PsGetCurrentThread(); + /* We need to find out which Timer we are */ Timer = CONTAINING_RECORD(Apc, ETIMER, TimerApc); DPRINT("ExpTimerApcKernelRoutine(Apc: %x. Timer: %x)\n", Apc, Timer); @@ -143,8 +196,8 @@ ExpTimerApcKernelRoutine(PKAPC Apc, */ if ((Timer->ApcAssociated) && (&CurrentThread->Tcb == Timer->TimerApc.Thread) && - (!Timer->Period)) { - + (!Timer->KeTimer.Period)) { + /* Remove it from the Active Timers List */ DPRINT("Removing Timer\n"); RemoveEntryList(&Timer->ActiveTimerListEntry); @@ -176,7 +229,7 @@ ExpInitializeTimerImplementation(VOID) ExTimerType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); /* Create the Executive Timer Object */ - RtlpCreateUnicodeString(&ExTimerType->TypeName, L"Timer", NonPagedPool); + RtlInitUnicodeString(&ExTimerType->TypeName, L"Timer"); ExTimerType->Tag = TAG('T', 'I', 'M', 'T'); ExTimerType->PeakObjects = 0; ExTimerType->PeakHandles = 0; @@ -209,7 +262,7 @@ NtCancelTimer(IN HANDLE TimerHandle, OUT PBOOLEAN CurrentState OPTIONAL) { PETIMER Timer; - KPROCESSOR_MODE PreviousMode; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); BOOLEAN State; KIRQL OldIrql; PETHREAD TimerThread; @@ -217,9 +270,6 @@ NtCancelTimer(IN HANDLE TimerHandle, NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - DPRINT("NtCancelTimer(0x%x, 0x%x)\n", TimerHandle, CurrentState); /* Check Parameter Validity */ @@ -301,10 +351,15 @@ NtCancelTimer(IN HANDLE TimerHandle, /* Make sure it's safe to write to the handle */ if(CurrentState != NULL) { + _SEH_TRY { + *CurrentState = State; + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; } } @@ -323,28 +378,27 @@ NtCreateTimer(OUT PHANDLE TimerHandle, { PETIMER Timer; HANDLE hTimer; - KPROCESSOR_MODE PreviousMode; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - DPRINT("NtCreateTimer(Handle: %x, Type: %d)\n", TimerHandle, TimerType); /* Check Parameter Validity */ if (PreviousMode != KernelMode) { + _SEH_TRY { + ProbeForWrite(TimerHandle, sizeof(HANDLE), sizeof(ULONG)); } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; - if(!NT_SUCCESS(Status)) { - return Status; - } + if(!NT_SUCCESS(Status)) return Status; } /* Create the Object */ @@ -373,7 +427,6 @@ NtCreateTimer(OUT PHANDLE TimerHandle, /* Set Initial State */ Timer->ApcAssociated = FALSE; - InitializeListHead(&Timer->WakeTimerListEntry); Timer->WakeTimer = FALSE; /* Insert the Timer */ @@ -388,9 +441,13 @@ NtCreateTimer(OUT PHANDLE TimerHandle, /* Make sure it's safe to write to the handle */ _SEH_TRY { + *TimerHandle = hTimer; + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; } @@ -406,28 +463,28 @@ NtOpenTimer(OUT PHANDLE TimerHandle, IN POBJECT_ATTRIBUTES ObjectAttributes) { HANDLE hTimer; - KPROCESSOR_MODE PreviousMode; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - DPRINT("NtOpenTimer(TimerHandle: %x)\n", TimerHandle); /* Check Parameter Validity */ if (PreviousMode != KernelMode) { + _SEH_TRY { + ProbeForWrite(TimerHandle, sizeof(HANDLE), sizeof(ULONG)); + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; - if(!NT_SUCCESS(Status)) { - return Status; - } + if(!NT_SUCCESS(Status)) return Status; } /* Open the Timer */ @@ -444,9 +501,13 @@ NtOpenTimer(OUT PHANDLE TimerHandle, /* Make sure it's safe to write to the handle */ _SEH_TRY { + *TimerHandle = hTimer; + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; } @@ -464,16 +525,13 @@ NtQueryTimer(IN HANDLE TimerHandle, OUT PULONG ReturnLength OPTIONAL) { PETIMER Timer; - KPROCESSOR_MODE PreviousMode; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status = STATUS_SUCCESS; PTIMER_BASIC_INFORMATION BasicInfo = (PTIMER_BASIC_INFORMATION)TimerInformation; PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - DPRINT("NtQueryTimer(TimerHandle: %x, Class: %d)\n", TimerHandle, TimerInformationClass); - + /* Check Validity */ DefaultQueryInfoBufferCheck(TimerInformationClass, ExTimerInfoClass, @@ -498,27 +556,24 @@ NtQueryTimer(IN HANDLE TimerHandle, /* Check for Success */ if(NT_SUCCESS(Status)) { + + /* Return the Basic Information */ + _SEH_TRY { + + /* FIXME: Interrupt correction based on Interrupt Time */ + DPRINT("Returning Information for Timer: %x. Time Remaining: %d\n", Timer, Timer->KeTimer.DueTime.QuadPart); + BasicInfo->TimeRemaining.QuadPart = Timer->KeTimer.DueTime.QuadPart; + BasicInfo->SignalState = KeReadStateTimer(&Timer->KeTimer); + + if(ReturnLength != NULL) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION); + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; - switch(TimerInformationClass) { - case TimerBasicInformation: { - /* Return the Basic Information */ - _SEH_TRY { - - /* FIXME: Interrupt correction based on Interrupt Time */ - DPRINT("Returning Information for Timer: %x. Time Remaining: %d\n", Timer, Timer->KeTimer.DueTime.QuadPart); - BasicInfo->TimeRemaining.QuadPart = Timer->KeTimer.DueTime.QuadPart; - BasicInfo->SignalState = KeReadStateTimer(&Timer->KeTimer); - - if(ReturnLength != NULL) { - *ReturnLength = sizeof(TIMER_BASIC_INFORMATION); - } - - } _SEH_HANDLE { - Status = _SEH_GetExceptionCode(); - } _SEH_END; - } - } - + /* Dereference Object */ ObDereferenceObject(Timer); } @@ -539,41 +594,40 @@ NtSetTimer(IN HANDLE TimerHandle, PETIMER Timer; KIRQL OldIrql; BOOLEAN State; - KPROCESSOR_MODE PreviousMode; - PETHREAD CurrentThread; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PETHREAD CurrentThread = PsGetCurrentThread(); LARGE_INTEGER TimerDueTime; PETHREAD TimerThread; BOOLEAN KillTimer = FALSE; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - CurrentThread = PsGetCurrentThread(); - DPRINT("NtSetTimer(TimerHandle: %x, DueTime: %d, Apc: %x, Period: %d)\n", TimerHandle, DueTime->QuadPart, TimerApcRoutine, Period); /* Check Parameter Validity */ if (PreviousMode != KernelMode) { + _SEH_TRY { + ProbeForRead(DueTime, sizeof(LARGE_INTEGER), sizeof(ULONG)); TimerDueTime = *DueTime; if(PreviousState != NULL) { + ProbeForWrite(PreviousState, sizeof(BOOLEAN), sizeof(BOOLEAN)); } } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; - if(!NT_SUCCESS(Status)) { - return Status; - } + if(!NT_SUCCESS(Status)) return Status; } /* Get the Timer Object */ @@ -630,16 +684,16 @@ NtSetTimer(IN HANDLE TimerHandle, /* Handle Wake Timers */ DPRINT("Doing Wake Semantics\n"); KeAcquireSpinLockAtDpcLevel(&ExpWakeListLock); - if (WakeTimer) { + if (WakeTimer && !Timer->WakeTimer) { /* Insert it into the list */ + Timer->WakeTimer = TRUE; InsertTailList(&ExpWakeList, &Timer->WakeTimerListEntry); - - } else { + } else if (!WakeTimer && Timer->WakeTimer) { /* Remove it from the list */ RemoveEntryList(&Timer->WakeTimerListEntry); - Timer->WakeTimerListEntry.Flink = NULL; + Timer->WakeTimer = FALSE; } KeReleaseSpinLockFromDpcLevel(&ExpWakeListLock); @@ -679,9 +733,6 @@ NtSetTimer(IN HANDLE TimerHandle, /* Dereference the Object */ ObDereferenceObject(Timer); - /* Unlock the Timer */ - KeReleaseSpinLock(&Timer->Lock, OldIrql); - /* Dereference if it was previously enabled */ if (!TimerApcRoutine) ObDereferenceObject(Timer); if (KillTimer) ObDereferenceObject(Timer); @@ -689,10 +740,15 @@ NtSetTimer(IN HANDLE TimerHandle, /* Make sure it's safe to write to the handle */ if(PreviousState != NULL) { + _SEH_TRY { + *PreviousState = State; + } _SEH_HANDLE { + Status = _SEH_GetExceptionCode(); + } _SEH_END; } } diff --git a/reactos/ntoskrnl/ex/win32k.c b/reactos/ntoskrnl/ex/win32k.c index 7fc3ed9a645..6a93e857465 100644 --- a/reactos/ntoskrnl/ex/win32k.c +++ b/reactos/ntoskrnl/ex/win32k.c @@ -5,7 +5,8 @@ * FILE: ntoskrnl/ex/win32k.c * PURPOSE: Executive Win32 subsystem support * - * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Moved callbacks to win32k and cleanup. + * Casper S. Hornstrup (chorns@users.sourceforge.net) */ #include @@ -14,320 +15,160 @@ /* DATA **********************************************************************/ -#define WINSTA_ACCESSCLIPBOARD (0x4L) -#define WINSTA_ACCESSGLOBALATOMS (0x20L) -#define WINSTA_CREATEDESKTOP (0x8L) -#define WINSTA_ENUMDESKTOPS (0x1L) -#define WINSTA_ENUMERATE (0x100L) -#define WINSTA_EXITWINDOWS (0x40L) -#define WINSTA_READATTRIBUTES (0x2L) -#define WINSTA_READSCREEN (0x200L) -#define WINSTA_WRITEATTRIBUTES (0x10L) - -#define DF_ALLOWOTHERACCOUNTHOOK (0x1L) -#define DESKTOP_CREATEMENU (0x4L) -#define DESKTOP_CREATEWINDOW (0x2L) -#define DESKTOP_ENUMERATE (0x40L) -#define DESKTOP_HOOKCONTROL (0x8L) -#define DESKTOP_JOURNALPLAYBACK (0x20L) -#define DESKTOP_JOURNALRECORD (0x10L) -#define DESKTOP_READOBJECTS (0x1L) -#define DESKTOP_SWITCHDESKTOP (0x100L) -#define DESKTOP_WRITEOBJECTS (0x80L) - POBJECT_TYPE EXPORTED ExWindowStationObjectType = NULL; POBJECT_TYPE EXPORTED ExDesktopObjectType = NULL; static GENERIC_MAPPING ExpWindowStationMapping = { - STANDARD_RIGHTS_READ | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_READATTRIBUTES | WINSTA_READSCREEN, - STANDARD_RIGHTS_WRITE | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | WINSTA_WRITEATTRIBUTES, - STANDARD_RIGHTS_EXECUTE | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS, - STANDARD_RIGHTS_REQUIRED | WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS | WINSTA_CREATEDESKTOP | - WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_EXITWINDOWS | - WINSTA_READATTRIBUTES | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES + + STANDARD_RIGHTS_READ | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_READATTRIBUTES | WINSTA_READSCREEN, + STANDARD_RIGHTS_WRITE | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | WINSTA_WRITEATTRIBUTES, + STANDARD_RIGHTS_EXECUTE | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS, + STANDARD_RIGHTS_REQUIRED | WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS | WINSTA_CREATEDESKTOP | + WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_EXITWINDOWS | + WINSTA_READATTRIBUTES | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES }; static GENERIC_MAPPING ExpDesktopMapping = { - STANDARD_RIGHTS_READ | DESKTOP_ENUMERATE | DESKTOP_READOBJECTS, - STANDARD_RIGHTS_WRITE | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_HOOKCONTROL | - DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | DESKTOP_WRITEOBJECTS, - STANDARD_RIGHTS_EXECUTE | DESKTOP_SWITCHDESKTOP, - STANDARD_RIGHTS_REQUIRED | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE | - DESKTOP_HOOKCONTROL | DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | - DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS + + STANDARD_RIGHTS_READ | DESKTOP_ENUMERATE | DESKTOP_READOBJECTS, + STANDARD_RIGHTS_WRITE | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_HOOKCONTROL | + DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | DESKTOP_WRITEOBJECTS, + STANDARD_RIGHTS_EXECUTE | DESKTOP_SWITCHDESKTOP, + STANDARD_RIGHTS_REQUIRED | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE | + DESKTOP_HOOKCONTROL | DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | + DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS }; +OBJECT_CREATE_ROUTINE ExpWindowStationObjectCreate = NULL; +OBJECT_PARSE_ROUTINE ExpWindowStationObjectParse = NULL; +OBJECT_DELETE_ROUTINE ExpWindowStationObjectDelete = NULL; +OBJECT_FIND_ROUTINE ExpWindowStationObjectFind = NULL; +OBJECT_CREATE_ROUTINE ExpDesktopObjectCreate = NULL; +OBJECT_DELETE_ROUTINE ExpDesktopObjectDelete = NULL; + /* FUNCTIONS ****************************************************************/ - -NTSTATUS STDCALL +NTSTATUS +STDCALL ExpWinStaObjectCreate(PVOID ObjectBody, - PVOID Parent, - PWSTR RemainingPath, - struct _OBJECT_ATTRIBUTES* ObjectAttributes) + PVOID Parent, + PWSTR RemainingPath, + struct _OBJECT_ATTRIBUTES* ObjectAttributes) { - PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)ObjectBody; - UNICODE_STRING UnicodeString; - NTSTATUS Status; - - if (RemainingPath == NULL) - { - return STATUS_SUCCESS; - } - - if (wcschr((RemainingPath + 1), '\\') != NULL) - { - return STATUS_UNSUCCESSFUL; - } - - RtlInitUnicodeString(&UnicodeString, (RemainingPath + 1)); - - DPRINT("Creating window station (0x%X) Name (%wZ)\n", WinSta, &UnicodeString); - - Status = RtlpCreateUnicodeString(&WinSta->Name, UnicodeString.Buffer, NonPagedPool); - if (!NT_SUCCESS(Status)) - { - return Status; - } - - KeInitializeSpinLock(&WinSta->Lock); - - InitializeListHead(&WinSta->DesktopListHead); - -#if 1 - WinSta->AtomTable = NULL; -#endif - - Status = RtlCreateAtomTable(37, &WinSta->AtomTable); - if (!NT_SUCCESS(Status)) - { - RtlFreeUnicodeString(&WinSta->Name); - return Status; - } - - WinSta->SystemMenuTemplate = (HANDLE)0; - - DPRINT("Window station successfully created. Name (%wZ)\n", &WinSta->Name); - - return STATUS_SUCCESS; + /* Call the Registered Callback */ + return ExpWindowStationObjectCreate(ObjectBody, + Parent, + RemainingPath, + ObjectAttributes); } -VOID STDCALL +VOID +STDCALL ExpWinStaObjectDelete(PVOID DeletedObject) { - PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)DeletedObject; - - DPRINT("Deleting window station (0x%X)\n", WinSta); - - RtlDestroyAtomTable(WinSta->AtomTable); - - RtlFreeUnicodeString(&WinSta->Name); + /* Call the Registered Callback */ + ExpWindowStationObjectDelete(DeletedObject); } PVOID +STDCALL ExpWinStaObjectFind(PWINSTATION_OBJECT WinStaObject, - PWSTR Name, - ULONG Attributes) + PWSTR Name, + ULONG Attributes) { - PLIST_ENTRY Current; - PDESKTOP_OBJECT CurrentObject; - - DPRINT("WinStaObject (0x%X) Name (%wS)\n", WinStaObject, Name); - - if (Name[0] == 0) - { - return NULL; - } - - Current = WinStaObject->DesktopListHead.Flink; - while (Current != &WinStaObject->DesktopListHead) - { - CurrentObject = CONTAINING_RECORD(Current, DESKTOP_OBJECT, ListEntry); - DPRINT("Scanning %wZ for %wS\n", &CurrentObject->Name, Name); - if (Attributes & OBJ_CASE_INSENSITIVE) - { - if (_wcsicmp(CurrentObject->Name.Buffer, Name) == 0) - { - DPRINT("Found desktop at (0x%X)\n", CurrentObject); - return CurrentObject; - } - } - else - { - if (wcscmp(CurrentObject->Name.Buffer, Name) == 0) - { - DPRINT("Found desktop at (0x%X)\n", CurrentObject); - return CurrentObject; - } - } - Current = Current->Flink; - } - - DPRINT("Returning NULL\n"); - - return NULL; + /* Call the Registered Callback */ + return ExpWindowStationObjectFind(WinStaObject, + Name, + Attributes); } -NTSTATUS STDCALL +NTSTATUS +STDCALL ExpWinStaObjectParse(PVOID Object, - PVOID *NextObject, - PUNICODE_STRING FullPath, - PWSTR *Path, - ULONG Attributes) + PVOID *NextObject, + PUNICODE_STRING FullPath, + PWSTR *Path, + ULONG Attributes) { - PVOID FoundObject; - NTSTATUS Status; - PWSTR End; - - DPRINT("Object (0x%X) Path (0x%X) *Path (%wS)\n", Object, Path, *Path); - - *NextObject = NULL; - - if ((Path == NULL) || ((*Path) == NULL)) - { - return STATUS_SUCCESS; - } - - End = wcschr((*Path) + 1, '\\'); - if (End != NULL) - { - DPRINT("Name contains illegal characters\n"); - return STATUS_UNSUCCESSFUL; - } - - FoundObject = ExpWinStaObjectFind(Object, (*Path) + 1, Attributes); - if (FoundObject == NULL) - { - DPRINT("Name was not found\n"); - return STATUS_UNSUCCESSFUL; - } - - Status = ObReferenceObjectByPointer( - FoundObject, - STANDARD_RIGHTS_REQUIRED, - NULL, - UserMode); - - *NextObject = FoundObject; - *Path = NULL; - - return Status; + /* Call the Registered Callback */ + return ExpWindowStationObjectParse(Object, + NextObject, + FullPath, + Path, + Attributes); } -NTSTATUS STDCALL -ExpDesktopObjectCreate(PVOID ObjectBody, - PVOID Parent, - PWSTR RemainingPath, - struct _OBJECT_ATTRIBUTES* ObjectAttributes) +NTSTATUS +STDCALL +ExpDesktopCreate(PVOID ObjectBody, + PVOID Parent, + PWSTR RemainingPath, + struct _OBJECT_ATTRIBUTES* ObjectAttributes) { - PDESKTOP_OBJECT Desktop = (PDESKTOP_OBJECT)ObjectBody; - UNICODE_STRING UnicodeString; - - if (RemainingPath == NULL) - { - return STATUS_SUCCESS; - } - - if (wcschr((RemainingPath + 1), '\\') != NULL) - { - return STATUS_UNSUCCESSFUL; - } - - RtlInitUnicodeString(&UnicodeString, (RemainingPath + 1)); - - DPRINT("Creating desktop (0x%X) Name (%wZ)\n", Desktop, &UnicodeString); - - KeInitializeSpinLock(&Desktop->Lock); - - Desktop->WindowStation = (PWINSTATION_OBJECT)Parent; - - /* Put the desktop on the window station's list of associcated desktops */ - ExInterlockedInsertTailList( - &Desktop->WindowStation->DesktopListHead, - &Desktop->ListEntry, - &Desktop->WindowStation->Lock); - - return RtlpCreateUnicodeString(&Desktop->Name, UnicodeString.Buffer, NonPagedPool); + /* Call the Registered Callback */ + return ExpDesktopObjectCreate(ObjectBody, + Parent, + RemainingPath, + ObjectAttributes); } -VOID STDCALL -ExpDesktopObjectDelete(PVOID DeletedObject) +VOID +STDCALL +ExpDesktopDelete(PVOID DeletedObject) { - PDESKTOP_OBJECT Desktop = (PDESKTOP_OBJECT)DeletedObject; - KIRQL OldIrql; - - DPRINT("Deleting desktop (0x%X)\n", Desktop); - - /* Remove the desktop from the window station's list of associcated desktops */ - KeAcquireSpinLock(&Desktop->WindowStation->Lock, &OldIrql); - RemoveEntryList(&Desktop->ListEntry); - KeReleaseSpinLock(&Desktop->WindowStation->Lock, OldIrql); - - RtlFreeUnicodeString(&Desktop->Name); + /* Call the Registered Callback */ + ExpDesktopObjectDelete(DeletedObject); } -VOID INIT_FUNCTION +VOID +INIT_FUNCTION ExpWin32kInit(VOID) { - /* Create window station object type */ - ExWindowStationObjectType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); - if (ExWindowStationObjectType == NULL) - { - CPRINT("Could not create window station object type\n"); - KEBUGCHECK(0); - } + /* Create window station object type */ + ExWindowStationObjectType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); + ExWindowStationObjectType->Tag = TAG('W', 'I', 'N', 'S'); + ExWindowStationObjectType->TotalObjects = 0; + ExWindowStationObjectType->TotalHandles = 0; + ExWindowStationObjectType->PeakObjects = 0; + ExWindowStationObjectType->PeakHandles = 0; + ExWindowStationObjectType->PagedPoolCharge = 0; + ExWindowStationObjectType->NonpagedPoolCharge = sizeof(WINSTATION_OBJECT); + ExWindowStationObjectType->Mapping = &ExpWindowStationMapping; + ExWindowStationObjectType->Dump = NULL; + ExWindowStationObjectType->Open = NULL; + ExWindowStationObjectType->Close = NULL; + ExWindowStationObjectType->Delete = ExpWinStaObjectDelete; + ExWindowStationObjectType->Parse = ExpWinStaObjectParse; + ExWindowStationObjectType->Security = NULL; + ExWindowStationObjectType->QueryName = NULL; + ExWindowStationObjectType->OkayToClose = NULL; + ExWindowStationObjectType->Create = ExpWinStaObjectCreate; + ExWindowStationObjectType->DuplicationNotify = NULL; + RtlInitUnicodeString(&ExWindowStationObjectType->TypeName, L"WindowStation"); + ObpCreateTypeObject(ExWindowStationObjectType); - ExWindowStationObjectType->Tag = TAG('W', 'I', 'N', 'S'); - ExWindowStationObjectType->TotalObjects = 0; - ExWindowStationObjectType->TotalHandles = 0; - ExWindowStationObjectType->PeakObjects = 0; - ExWindowStationObjectType->PeakHandles = 0; - ExWindowStationObjectType->PagedPoolCharge = 0; - ExWindowStationObjectType->NonpagedPoolCharge = sizeof(WINSTATION_OBJECT); - ExWindowStationObjectType->Mapping = &ExpWindowStationMapping; - ExWindowStationObjectType->Dump = NULL; - ExWindowStationObjectType->Open = NULL; - ExWindowStationObjectType->Close = NULL; - ExWindowStationObjectType->Delete = ExpWinStaObjectDelete; - ExWindowStationObjectType->Parse = ExpWinStaObjectParse; - ExWindowStationObjectType->Security = NULL; - ExWindowStationObjectType->QueryName = NULL; - ExWindowStationObjectType->OkayToClose = NULL; - ExWindowStationObjectType->Create = ExpWinStaObjectCreate; - ExWindowStationObjectType->DuplicationNotify = NULL; - RtlInitUnicodeString(&ExWindowStationObjectType->TypeName, L"WindowStation"); - - ObpCreateTypeObject(ExWindowStationObjectType); - - /* Create desktop object type */ - ExDesktopObjectType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); - if (ExDesktopObjectType == NULL) - { - CPRINT("Could not create desktop object type\n"); - KEBUGCHECK(0); - } - - ExDesktopObjectType->Tag = TAG('D', 'E', 'S', 'K'); - ExDesktopObjectType->TotalObjects = 0; - ExDesktopObjectType->TotalHandles = 0; - ExDesktopObjectType->PeakObjects = 0; - ExDesktopObjectType->PeakHandles = 0; - ExDesktopObjectType->PagedPoolCharge = 0; - ExDesktopObjectType->NonpagedPoolCharge = sizeof(DESKTOP_OBJECT); - ExDesktopObjectType->Mapping = &ExpDesktopMapping; - ExDesktopObjectType->Dump = NULL; - ExDesktopObjectType->Open = NULL; - ExDesktopObjectType->Close = NULL; - ExDesktopObjectType->Delete = ExpDesktopObjectDelete; - ExDesktopObjectType->Parse = NULL; - ExDesktopObjectType->Security = NULL; - ExDesktopObjectType->QueryName = NULL; - ExDesktopObjectType->OkayToClose = NULL; - ExDesktopObjectType->Create = ExpDesktopObjectCreate; - ExDesktopObjectType->DuplicationNotify = NULL; - RtlInitUnicodeString(&ExDesktopObjectType->TypeName, L"Desktop"); - - ObpCreateTypeObject(ExDesktopObjectType); + /* Create desktop object type */ + ExDesktopObjectType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); + ExDesktopObjectType->Tag = TAG('D', 'E', 'S', 'K'); + ExDesktopObjectType->TotalObjects = 0; + ExDesktopObjectType->TotalHandles = 0; + ExDesktopObjectType->PeakObjects = 0; + ExDesktopObjectType->PeakHandles = 0; + ExDesktopObjectType->PagedPoolCharge = 0; + ExDesktopObjectType->NonpagedPoolCharge = sizeof(DESKTOP_OBJECT); + ExDesktopObjectType->Mapping = &ExpDesktopMapping; + ExDesktopObjectType->Dump = NULL; + ExDesktopObjectType->Open = NULL; + ExDesktopObjectType->Close = NULL; + ExDesktopObjectType->Delete = ExpDesktopDelete; + ExDesktopObjectType->Parse = NULL; + ExDesktopObjectType->Security = NULL; + ExDesktopObjectType->QueryName = NULL; + ExDesktopObjectType->OkayToClose = NULL; + ExDesktopObjectType->Create = ExpDesktopCreate; + ExDesktopObjectType->DuplicationNotify = NULL; + RtlInitUnicodeString(&ExDesktopObjectType->TypeName, L"Desktop"); + ObpCreateTypeObject(ExDesktopObjectType); } /* EOF */ diff --git a/reactos/ntoskrnl/ex/work.c b/reactos/ntoskrnl/ex/work.c index 8d7bac152fa..0a5f82f1588 100644 --- a/reactos/ntoskrnl/ex/work.c +++ b/reactos/ntoskrnl/ex/work.c @@ -1,11 +1,11 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ex/work.c * PURPOSE: Manage system work queues * - * PROGRAMMERS: David Welch (welch@mcmail.com) + * PROGRAMMERS: Alex Ionescu - Used correct work queue array and added some fixes and checks. + * Gunnar Dalsnes - Implemented */ /* INCLUDES ******************************************************************/ @@ -25,17 +25,10 @@ /* * PURPOSE: Queue of items waiting to be processed at normal priority */ -KQUEUE EiNormalWorkQueue; - -KQUEUE EiCriticalWorkQueue; - -KQUEUE EiHyperCriticalWorkQueue; +EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue]; /* FUNCTIONS ****************************************************************/ -//static NTSTATUS STDCALL -static VOID STDCALL -ExWorkerThreadEntryPoint(IN PVOID context) /* * FUNCTION: Entry point for a worker thread * ARGUMENTS: @@ -44,138 +37,138 @@ ExWorkerThreadEntryPoint(IN PVOID context) * NOTE: To kill a worker thread you must queue an item whose callback * calls PsTerminateSystemThread */ +static +VOID +STDCALL +ExpWorkerThreadEntryPoint(IN PVOID Context) { - - PWORK_QUEUE_ITEM item; - PLIST_ENTRY current; + PWORK_QUEUE_ITEM WorkItem; + PLIST_ENTRY QueueEntry; + WORK_QUEUE_TYPE WorkQueueType; + PEX_WORK_QUEUE WorkQueue; - while (TRUE) - { - current = KeRemoveQueue( (PKQUEUE)context, KernelMode, NULL ); - - /* can't happend since we do a KernelMode wait (and we're a system thread) */ - ASSERT((NTSTATUS)current != STATUS_USER_APC); - - /* this should never happend either, since we wait with NULL timeout, - * but there's a slight possibility that STATUS_TIMEOUT is returned - * at queue rundown in NT (unlikely) -Gunnar - */ - ASSERT((NTSTATUS)current != STATUS_TIMEOUT); - - /* based on INVALID_WORK_QUEUE_ITEM bugcheck desc. */ - if (current->Flink == NULL || current->Blink == NULL) - { - KeBugCheck(INVALID_WORK_QUEUE_ITEM); - } - - /* "reinitialize" item (same as done in ExInitializeWorkItem) */ - current->Flink = NULL; - - item = CONTAINING_RECORD( current, WORK_QUEUE_ITEM, List); - item->WorkerRoutine(item->Parameter); - - if (KeGetCurrentIrql() != PASSIVE_LEVEL) - { - KeBugCheck(IRQL_NOT_LESS_OR_EQUAL); - } - } - -} - -static VOID ExInitializeWorkQueue(PKQUEUE WorkQueue, - KPRIORITY Priority) -{ - ULONG i; - PETHREAD Thread; - HANDLE hThread; - - - for (i=0; iTcb, - Priority); - ObDereferenceObject(Thread); - ZwClose(hThread); - } + /* Wait for Something to Happen on the Queue */ + QueueEntry = KeRemoveQueue(&WorkQueue->WorkerQueue, KernelMode, NULL); + + /* Can't happen since we do a KernelMode wait (and we're a system thread) */ + ASSERT((NTSTATUS)QueueEntry != STATUS_USER_APC); + + /* this should never happen either, since we wait with NULL timeout, + * but there's a slight possibility that STATUS_TIMEOUT is returned + * at queue rundown in NT (unlikely) -Gunnar + */ + ASSERT((NTSTATUS)QueueEntry != STATUS_TIMEOUT); + + /* Increment Processed Work Items */ + InterlockedIncrement(&WorkQueue->WorkItemsProcessed); + + /* Get the Work Item */ + WorkItem = CONTAINING_RECORD(QueueEntry, WORK_QUEUE_ITEM, List); + + /* Call the Worker Routine */ + WorkItem->WorkerRoutine(WorkItem->Parameter); + + /* Make sure it returned at right IRQL */ + if (KeGetCurrentIrql() != PASSIVE_LEVEL) { + + /* FIXME: Make this an Ex */ + KEBUGCHECK(WORKER_THREAD_RETURNED_AT_BAD_IRQL); + } + + /* Make sure it returned with Impersionation Disabled */ + if (PsGetCurrentThread()->ActiveImpersonationInfo) { + + /* FIXME: Make this an Ex */ + KEBUGCHECK(IMPERSONATING_WORKER_THREAD); + } + } } -VOID INIT_FUNCTION -ExInitializeWorkerThreads(VOID) +static +VOID +STDCALL +ExpInitializeWorkQueue(WORK_QUEUE_TYPE WorkQueueType, + KPRIORITY Priority) { - KeInitializeQueue( &EiNormalWorkQueue, NUMBER_OF_WORKER_THREADS ); - KeInitializeQueue( &EiCriticalWorkQueue , NUMBER_OF_WORKER_THREADS ); - KeInitializeQueue( &EiHyperCriticalWorkQueue , NUMBER_OF_WORKER_THREADS ); + ULONG i; + PETHREAD Thread; + HANDLE hThread; + + /* Loop through how many threads we need to create */ + for (i = 0; i < NUMBER_OF_WORKER_THREADS; i++) { + + /* Create the System Thread */ + PsCreateSystemThread(&hThread, + THREAD_ALL_ACCESS, + NULL, + NULL, + NULL, + ExpWorkerThreadEntryPoint, + (PVOID)WorkQueueType); + + /* Get the Thread */ + ObReferenceObjectByHandle(hThread, + THREAD_SET_INFORMATION, + PsThreadType, + KernelMode, + (PVOID*)&Thread, + NULL); + + /* Set the Priority */ + KeSetPriorityThread(&Thread->Tcb, Priority); + + /* Dereference and close handle */ + ObDereferenceObject(Thread); + ZwClose(hThread); + } +} - ExInitializeWorkQueue(&EiNormalWorkQueue, - LOW_PRIORITY); - ExInitializeWorkQueue(&EiCriticalWorkQueue, - LOW_REALTIME_PRIORITY); - ExInitializeWorkQueue(&EiHyperCriticalWorkQueue, - HIGH_PRIORITY); +VOID +INIT_FUNCTION +ExpInitializeWorkerThreads(VOID) +{ + ULONG WorkQueueType; + + /* Initialize the Array */ + for (WorkQueueType = 0; WorkQueueType < MaximumWorkQueue; WorkQueueType++) { + + RtlZeroMemory(&ExWorkerQueue[WorkQueueType], sizeof(EX_WORK_QUEUE)); + KeInitializeQueue(&ExWorkerQueue[WorkQueueType].WorkerQueue, 0); + } + + /* Create the built-in worker threads for each work queue */ + ExpInitializeWorkQueue(CriticalWorkQueue, LOW_REALTIME_PRIORITY); + ExpInitializeWorkQueue(DelayedWorkQueue, LOW_PRIORITY); + ExpInitializeWorkQueue(HyperCriticalWorkQueue, HIGH_PRIORITY); } /* * @implemented - */ -VOID STDCALL -ExQueueWorkItem (PWORK_QUEUE_ITEM WorkItem, - WORK_QUEUE_TYPE QueueType) -/* + * * FUNCTION: Inserts a work item in a queue for one of the system worker * threads to process * ARGUMENTS: * WorkItem = Item to insert * QueueType = Queue to insert it in */ +VOID +STDCALL +ExQueueWorkItem(PWORK_QUEUE_ITEM WorkItem, + WORK_QUEUE_TYPE QueueType) { ASSERT(WorkItem!=NULL); ASSERT_IRQL(DISPATCH_LEVEL); ASSERT(WorkItem->List.Flink == NULL); - /* - * Insert the item in the appropiate queue and wake up any thread - * waiting for something to do - */ - switch(QueueType) - { - case DelayedWorkQueue: - KeInsertQueue ( - &EiNormalWorkQueue, - &WorkItem->List - ); - break; - - case CriticalWorkQueue: - KeInsertQueue ( - &EiCriticalWorkQueue, - &WorkItem->List - ); - break; - - case HyperCriticalWorkQueue: - KeInsertQueue ( - &EiHyperCriticalWorkQueue, - &WorkItem->List - ); - break; - - default: - break; - - } + + /* Insert the Queue */ + KeInsertQueue(&ExWorkerQueue[QueueType].WorkerQueue, &WorkItem->List); } /* EOF */ diff --git a/reactos/ntoskrnl/fs/notify.c b/reactos/ntoskrnl/fs/notify.c index 837ed5a6c24..8a84e77bd9d 100644 --- a/reactos/ntoskrnl/fs/notify.c +++ b/reactos/ntoskrnl/fs/notify.c @@ -23,7 +23,6 @@ typedef struct _NOTIFY_ENTRY LIST_ENTRY ListEntry; PSTRING FullDirectoryName; BOOLEAN WatchTree; - BOOLEAN IgnoreBuffer; BOOLEAN PendingChanges; ULONG CompletionFilter; LIST_ENTRY IrpQueue; @@ -395,7 +394,7 @@ FsRtlNotifyFullChangeDirectory ( IN BOOLEAN WatchTree, IN BOOLEAN IgnoreBuffer, IN ULONG CompletionFilter, - IN PIRP NotifyIrp, + IN PIRP Irp, IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL, IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL ) @@ -404,7 +403,7 @@ FsRtlNotifyFullChangeDirectory ( PNOTIFY_ENTRY NotifyEntry; ULONG IrpBuffLen; - if (!NotifyIrp) + if (!Irp) { /* all other params are ignored if NotifyIrp == NULL */ FsRtlpWatchedDirectoryWasDeleted(NotifySync, NotifyList, FsContext); @@ -415,14 +414,14 @@ FsRtlNotifyFullChangeDirectory ( ExAcquireFastMutex((PFAST_MUTEX)NotifySync); - IrpStack = IoGetCurrentIrpStackLocation(NotifyIrp); + IrpStack = IoGetCurrentIrpStackLocation(Irp); if (IrpStack->FileObject->Flags & FO_CLEANUP_COMPLETE) { ExReleaseFastMutex((PFAST_MUTEX)NotifySync); - NotifyIrp->IoStatus.Information = 0; - NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP; - IoCompleteRequest(NotifyIrp, IO_NO_INCREMENT); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_NOTIFY_CLEANUP; + IoCompleteRequest(Irp, IO_NO_INCREMENT); return; } @@ -463,12 +462,13 @@ FsRtlNotifyFullChangeDirectory ( FSRTL_NOTIFY_TAG ); - NotifyEntry->PrevEntry = NotifyEntry->Buffer; NotifyEntry->BufferSize = IrpBuffLen; } _SEH_HANDLE { - /* ExAllocatePoolWithQuotaTag raised exception */ + /* ExAllocatePoolWithQuotaTag raised exception but we dont care. + The impl. doesnt require a buffer, so well continue as usual. + */ } _SEH_END; } @@ -485,26 +485,29 @@ FsRtlNotifyFullChangeDirectory ( /* No changes are pending. Queue the irp */ /* Irp cancelation boilerplate */ - IoSetCancelRoutine(NotifyIrp, FsRtlpNotifyCancelRoutine); - if (NotifyIrp->Cancel && IoSetCancelRoutine(NotifyIrp, NULL)) + + /* save NotifySych for use in the cancel routine */ + Irp->Tail.Overlay.DriverContext[3] = NotifySync; + + IoSetCancelRoutine(Irp, FsRtlpNotifyCancelRoutine); + if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL)) { //irp was canceled ExReleaseFastMutex((PFAST_MUTEX)NotifySync); - NotifyIrp->IoStatus.Status = STATUS_CANCELLED; - NotifyIrp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; - IoCompleteRequest(NotifyIrp, IO_NO_INCREMENT); + IoCompleteRequest(Irp, IO_NO_INCREMENT); return; } - IoMarkIrpPending(NotifyIrp); + IoMarkIrpPending(Irp); //FIXME: any point in setting irp status/information before queueing? + Irp->IoStatus.Status = STATUS_PENDING; - /* save NotifySych for use in the cancel routine */ - NotifyIrp->Tail.Overlay.DriverContext[3] = NotifySync; - InsertTailList(&NotifyEntry->IrpQueue, &NotifyIrp->Tail.Overlay.ListEntry); + InsertTailList(&NotifyEntry->IrpQueue, &Irp->Tail.Overlay.ListEntry); ExReleaseFastMutex((PFAST_MUTEX)NotifySync); return; @@ -524,24 +527,29 @@ FsRtlNotifyFullChangeDirectory ( -Current irp buff was not large enough */ - NotifyIrp->IoStatus.Information = 0; - NotifyIrp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR; + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR; } else { - /* terminate last entry */ - NotifyEntry->PrevEntry->NextEntryOffset = 0; - - //FIXME: copy data correctly to user - memcpy(NotifyIrp->UserBuffer, NotifyEntry->Buffer, NotifyEntry->NextEntryOffset); + PVOID Adr = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority); - NotifyIrp->IoStatus.Information = NotifyEntry->NextEntryOffset; - NotifyIrp->IoStatus.Status = STATUS_SUCCESS; + if (Adr) + { + memcpy(Adr, NotifyEntry->Buffer, NotifyEntry->NextEntryOffset); + Irp->IoStatus.Information = NotifyEntry->NextEntryOffset; + } + else + { + Irp->IoStatus.Information = 0; + } + + Irp->IoStatus.Status = STATUS_SUCCESS; } /* reset buffer */ - NotifyEntry->PrevEntry = NotifyEntry->Buffer; + NotifyEntry->PrevEntry = NULL; NotifyEntry->NextEntryOffset = 0; NotifyEntry->BufferExhausted = FALSE; @@ -549,7 +557,9 @@ FsRtlNotifyFullChangeDirectory ( ExReleaseFastMutex((PFAST_MUTEX)NotifySync); - IoCompleteRequest(NotifyIrp, IO_NO_INCREMENT); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + /* caller must return STATUS_PENDING */ } @@ -671,13 +681,17 @@ FsRtlNotifyFullReportChange ( LIST_FOR_EACH_SAFE(EnumEntry, NotifyList, NotifyEntry, NOTIFY_ENTRY, ListEntry ) { ASSERT(NotifyEntry->Unicode == FsRtlpIsUnicodePath(FullTargetName)); - + /* rule out some easy cases */ - /* FIXME: short vs. long names??? */ + /* FIXME: short vs. long names??? lower case/upper case/mixed case? */ if (!(FilterMatch & NotifyEntry->CompletionFilter)) continue; - FullDirLen = TargetNameOffset - (NotifyEntry->Unicode?sizeof(WCHAR):sizeof(char)); - + FullDirLen = TargetNameOffset - (NotifyEntry->Unicode ? sizeof(WCHAR) : sizeof(char)); + if (FullDirLen == 0) + { + /* special case for root dir */ + FullDirLen = (NotifyEntry->Unicode ? sizeof(WCHAR) : sizeof(char)); + } if (FullDirLen < NotifyEntry->FullDirectoryName->Length) continue; @@ -685,6 +699,7 @@ FsRtlNotifyFullReportChange ( DPRINT("NotifyEntry->FullDirectoryName: %wZ\n", NotifyEntry->FullDirectoryName); + /* FIXME: short vs. long names??? lower case/upper case/mixed case? */ if (memcmp(NotifyEntry->FullDirectoryName->Buffer, FullTargetName->Buffer, NotifyEntry->FullDirectoryName->Length) != 0) continue; @@ -751,22 +766,31 @@ FsRtlNotifyFullReportChange ( } else { - //FIXME: copy data to user correctly - CurrentEntry = (PFILE_NOTIFY_INFORMATION)Irp->UserBuffer; - - CurrentEntry->Action = Action; - CurrentEntry->NameLength = NameLenU; - CurrentEntry->NextEntryOffset = 0; + CurrentEntry = (PFILE_NOTIFY_INFORMATION) + MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority); + + if (CurrentEntry) + { + CurrentEntry->Action = Action; + CurrentEntry->NameLength = NameLenU; + CurrentEntry->NextEntryOffset = 0; - FsRtlpCopyName( - CurrentEntry, - NotifyEntry->Unicode, - &RelativeName, - StreamName - ); + FsRtlpCopyName( + CurrentEntry, + NotifyEntry->Unicode, + &RelativeName, + StreamName + ); + + Irp->IoStatus.Information = RecordLen; + } + else + { + Irp->IoStatus.Information = 0; + } + Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = RecordLen; } /* avoid holding lock while completing irp */ @@ -803,8 +827,10 @@ FsRtlNotifyFullReportChange ( StreamName ); - /* adjust buffer */ - NotifyEntry->PrevEntry->NextEntryOffset = (char*)CurrentEntry - (char*)NotifyEntry->PrevEntry; + if (NotifyEntry->PrevEntry) + { + NotifyEntry->PrevEntry->NextEntryOffset = (char*)CurrentEntry - (char*)NotifyEntry->PrevEntry; + } NotifyEntry->PrevEntry = CurrentEntry; NotifyEntry->NextEntryOffset += RecordLen; diff --git a/reactos/ntoskrnl/include/internal/debug.h b/reactos/ntoskrnl/include/internal/debug.h index ce8c2f5d18e..31324131183 100644 --- a/reactos/ntoskrnl/include/internal/debug.h +++ b/reactos/ntoskrnl/include/internal/debug.h @@ -38,11 +38,11 @@ /* Assert only on "checked" version */ #ifndef NASSERT #ifdef CONFIG_SMP -#define assert(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d for CPU%d\n", __FILE__,__LINE__, KeGetCurrentKPCR()->ProcessorNumber), KeBugCheck(0); } -#define ASSERT(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d for CPU%d\n", __FILE__,__LINE__, KeGetCurrentKPCR()->ProcessorNumber), KeBugCheck(0); } +#define assert(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d for CPU%d\n", __FILE__,__LINE__, KeGetCurrentKPCR()->ProcessorNumber), DbgBreakPoint(); } +#define ASSERT(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d for CPU%d\n", __FILE__,__LINE__, KeGetCurrentKPCR()->ProcessorNumber), DbgBreakPoint(); } #else -#define assert(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d\n", __FILE__,__LINE__); KeBugCheck(0); } -#define ASSERT(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d\n", __FILE__,__LINE__); KeBugCheck(0); } +#define assert(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d\n", __FILE__,__LINE__); DbgBreakPoint(); } +#define ASSERT(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d\n", __FILE__,__LINE__); DbgBreakPoint(); } #endif #define assertmsg(_c_, _m_) \ diff --git a/reactos/ntoskrnl/include/internal/ex.h b/reactos/ntoskrnl/include/internal/ex.h index 3596bfe2420..4fe107e5618 100644 --- a/reactos/ntoskrnl/include/internal/ex.h +++ b/reactos/ntoskrnl/include/internal/ex.h @@ -68,6 +68,8 @@ typedef struct _DESKTOP_OBJECT HANDLE PrevActiveWindow; /* Thread blocking input */ PVOID BlockInputThread; + + LIST_ENTRY ShellHookWindows; } DESKTOP_OBJECT, *PDESKTOP_OBJECT; @@ -84,7 +86,9 @@ extern LARGE_INTEGER ExpTimeZoneBias; extern ULONG ExpTimeZoneId; extern POBJECT_TYPE ExEventPairObjectType; - +extern POBJECT_TYPE EXPORTED ExMutantObjectType; +extern POBJECT_TYPE EXPORTED ExSemaphoreObjectType; +extern POBJECT_TYPE EXPORTED ExTimerType; /* INITIALIZATION FUNCTIONS *************************************************/ @@ -98,24 +102,78 @@ ExInit3(VOID); VOID ExpInitTimeZoneInfo(VOID); VOID -ExInitializeWorkerThreads(VOID); +ExpInitializeWorkerThreads(VOID); VOID ExpInitLookasideLists(VOID); VOID ExpInitializeCallbacks(VOID); VOID ExpInitUuids(VOID); +VOID +STDCALL +ExpInitializeExecutive(VOID); + +/* HANDLE TABLE FUNCTIONS ***************************************************/ + +#define EX_HANDLE_ENTRY_LOCKED (1 << ((sizeof(PVOID) * 8) - 1)) +#define EX_HANDLE_ENTRY_PROTECTFROMCLOSE (1 << 0) +#define EX_HANDLE_ENTRY_INHERITABLE (1 << 1) +#define EX_HANDLE_ENTRY_AUDITONCLOSE (1 << 2) + +#define EX_HANDLE_TABLE_CLOSING 0x1 + +#define EX_INVALID_HANDLE (~0) + +#define EX_HANDLE_ENTRY_FLAGSMASK (EX_HANDLE_ENTRY_LOCKED | \ + EX_HANDLE_ENTRY_PROTECTFROMCLOSE | \ + EX_HANDLE_ENTRY_INHERITABLE | \ + EX_HANDLE_ENTRY_AUDITONCLOSE) + +typedef VOID (STDCALL PEX_DESTROY_HANDLE_CALLBACK)(PHANDLE_TABLE HandleTable, PVOID Object, ULONG GrantedAccess, PVOID Context); +typedef BOOLEAN (STDCALL PEX_DUPLICATE_HANDLE_CALLBACK)(PHANDLE_TABLE HandleTable, PHANDLE_TABLE_ENTRY HandleTableEntry, PVOID Context); +typedef BOOLEAN (STDCALL PEX_CHANGE_HANDLE_CALLBACK)(PHANDLE_TABLE HandleTable, PHANDLE_TABLE_ENTRY HandleTableEntry, PVOID Context); + +VOID +ExpInitializeHandleTables(VOID); +PHANDLE_TABLE +ExCreateHandleTable(IN PEPROCESS QuotaProcess OPTIONAL); +VOID +ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable, + IN PEX_DESTROY_HANDLE_CALLBACK DestroyHandleCallback OPTIONAL, + IN PVOID Context OPTIONAL); +PHANDLE_TABLE +ExDupHandleTable(IN PEPROCESS QuotaProcess OPTIONAL, + IN PEX_DUPLICATE_HANDLE_CALLBACK DuplicateHandleCallback OPTIONAL, + IN PVOID Context OPTIONAL, + IN PHANDLE_TABLE SourceHandleTable); +BOOLEAN +ExLockHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry); +VOID +ExUnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry); +LONG +ExCreateHandle(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry); +BOOLEAN +ExDestroyHandle(IN PHANDLE_TABLE HandleTable, + IN LONG Handle); +VOID +ExDestroyHandleByEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry, + IN LONG Handle); +PHANDLE_TABLE_ENTRY +ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable, + IN LONG Handle); + +BOOLEAN +ExChangeHandle(IN PHANDLE_TABLE HandleTable, + IN LONG Handle, + IN PEX_CHANGE_HANDLE_CALLBACK ChangeHandleCallback, + IN PVOID Context); /* OTHER FUNCTIONS **********************************************************/ -#ifdef _ENABLE_THRDEVTPAIR -VOID -ExpSwapThreadEventPair( - IN struct _ETHREAD* Thread, - IN struct _KEVENT_PAIR* EventPair - ); -#endif /* _ENABLE_THRDEVTPAIR */ - LONGLONG FASTCALL ExfpInterlockedExchange64(LONGLONG volatile * Destination, @@ -127,6 +185,10 @@ ExpSetTimeZoneInformation(PTIME_ZONE_INFORMATION TimeZoneInformation); NTSTATUS ExpAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId); +VOID +STDCALL +ExTimerRundown(VOID); + #define InterlockedDecrementUL(Addend) \ (ULONG)InterlockedDecrement((PLONG)(Addend)) diff --git a/reactos/ntoskrnl/include/internal/i386/fpu.h b/reactos/ntoskrnl/include/internal/i386/fpu.h index 14475e2b43b..30267a828de 100644 --- a/reactos/ntoskrnl/include/internal/i386/fpu.h +++ b/reactos/ntoskrnl/include/internal/i386/fpu.h @@ -20,6 +20,11 @@ #ifndef __NTOSKRNL_INCLUDE_INTERNAL_I386_FPU_H #define __NTOSKRNL_INCLUDE_INTERNAL_I386_FPU_H +#define FN_CONTROL_WORD 0x0 +#define FN_STATUS_WORD 0x4 +#define FN_TAG_WORD 0x8 +#define FN_DATA_SELECTOR 0x18 +#define FN_CR0_NPX_STATE 0x20C #define SIZEOF_FX_SAVE_AREA 528 #ifndef __ASM__ diff --git a/reactos/ntoskrnl/include/internal/i386/ke.h b/reactos/ntoskrnl/include/internal/i386/ke.h index 06612b5c5a0..5efe824339d 100644 --- a/reactos/ntoskrnl/include/internal/i386/ke.h +++ b/reactos/ntoskrnl/include/internal/i386/ke.h @@ -69,9 +69,11 @@ #define KTRAP_FRAME_RESERVED9 (0x8A) #define KTRAP_FRAME_SIZE (0x8C) +#define X86_EFLAGS_TF 0x00000100 /* Trap flag */ #define X86_EFLAGS_IF 0x00000200 /* Interrupt Enable flag */ #define X86_EFLAGS_IOPL 0x00003000 /* I/O Privilege Level bits */ #define X86_EFLAGS_NT 0x00004000 /* Nested Task flag */ +#define X86_EFLAGS_RF 0x00010000 /* Resume flag */ #define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */ #define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */ @@ -97,6 +99,7 @@ #define X86_FEATURE_FXSR 0x01000000 /* FXSAVE/FXRSTOR instructions present */ #define X86_FEATURE_SSE 0x02000000 /* SSE extension present */ #define X86_FEATURE_SSE2 0x04000000 /* SSE2 extension present */ +#define X86_FEATURE_HT 0x10000000 /* Hyper-Threading present */ #define X86_EXT_FEATURE_SSE3 0x00000001 /* SSE3 extension present */ #define X86_EXT_FEATURE_3DNOW 0x40000000 /* 3DNOW! extension present */ diff --git a/reactos/ntoskrnl/include/internal/i386/ps.h b/reactos/ntoskrnl/include/internal/i386/ps.h index f6cf2dc6bc7..6eb1c4125fb 100644 --- a/reactos/ntoskrnl/include/internal/i386/ps.h +++ b/reactos/ntoskrnl/include/internal/i386/ps.h @@ -43,6 +43,7 @@ #define KPCR_BASE 0xFF000000 #define KPCR_EXCEPTION_LIST 0x0 +#define KPCR_INITIAL_STACK 0x4 #define KPCR_SELF 0x1C #define KPCR_TSS 0x40 #define KPCR_CURRENT_THREAD 0x124 @@ -102,7 +103,7 @@ typedef struct _KPRCB { ULONG CcCopyReadWait; ULONG CcCopyReadNoWaitMiss; ULONG KeAlignmentFixupCount; - ULONG SpareCounter0; + ULONG KeContextSwitches; ULONG KeDcacheFlushCount; ULONG KeExceptionDispatchCount; ULONG KeFirstLevelTbFills; @@ -217,7 +218,7 @@ typedef struct _KPCR_TIB { typedef struct _KPCR { KPCR_TIB Tib; /* 00 */ struct _KPCR *Self; /* 1C */ - struct _KPRCB *PCRCB; /* 20 */ + struct _KPRCB *Prcb; /* 20 */ KIRQL Irql; /* 24 */ ULONG IRR; /* 28 */ ULONG IrrActive; /* 2C */ @@ -269,9 +270,28 @@ static inline PKPCR KeGetCurrentKPCR(VOID) return((PKPCR)value); } +static inline PKPRCB KeGetCurrentPrcb(VOID) +{ + ULONG value; + +#if defined(__GNUC__) + __asm__ __volatile__ ("movl %%fs:0x20, %0\n\t" + : "=r" (value) + : /* no inputs */ + ); +#elif defined(_MSC_VER) + __asm mov eax, fs:0x20; + __asm mov value, eax; +#else +#error Unknown compiler for inline assembler +#endif + return((PKPRCB)value); +} + #else #define KeGetCurrentKPCR(X) ((PKPCR)KPCR_BASE) +#define KeGetCurrentPrcb() (((PKPCR)KPCR_BASE)->Prcb) #endif diff --git a/reactos/ntoskrnl/include/internal/id.h b/reactos/ntoskrnl/include/internal/id.h deleted file mode 100644 index 43a29588163..00000000000 --- a/reactos/ntoskrnl/include/internal/id.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Structure ids - */ - -#define InternalBaseType (0xcc) -#define InternalNotificationEvent (InternalBaseType + 1) -#define InternalSynchronizationEvent (InternalBaseType + 2) -#define InternalSemaphoreType (InternalBaseType + 3) -#define InternalProcessType (InternalBaseType + 4) -#define InternalThreadType (InternalBaseType + 5) -#define InternalFileType (InternalBaseType + 6) -#define InternalDriverType (InternalBaseType + 7) -#define InternalDeviceType (InternalBaseType + 8) -#define InternalMutexType (InternalBaseType + 9) -#define InternalNotificationTimer (InternalBaseType + 10) -#define InternalSynchronizationTimer (InternalBaseType + 11) -#define InternalQueueType (InternalBaseType + 12) - - diff --git a/reactos/ntoskrnl/include/internal/io.h b/reactos/ntoskrnl/include/internal/io.h index b23f6121742..3d2c432f0c8 100644 --- a/reactos/ntoskrnl/include/internal/io.h +++ b/reactos/ntoskrnl/include/internal/io.h @@ -412,7 +412,7 @@ IoDestroyDriverList(VOID); /* bootlog.c */ VOID -IopInitBootLog(VOID); +IopInitBootLog(BOOLEAN StartBootLog); VOID IopStartBootLog(VOID); diff --git a/reactos/ntoskrnl/include/internal/kd.h b/reactos/ntoskrnl/include/internal/kd.h index 4594dd47ee4..e74855b8ae1 100644 --- a/reactos/ntoskrnl/include/internal/kd.h +++ b/reactos/ntoskrnl/include/internal/kd.h @@ -16,7 +16,7 @@ #define KD_DEBUG_SCREEN 0x04 #define KD_DEBUG_SERIAL 0x08 #define KD_DEBUG_BOCHS 0x10 -#define KD_DEBUG_BOOTLOG 0x20 +#define KD_DEBUG_FILELOG 0x20 #define KD_DEBUG_MDA 0x40 #define KD_DEBUG_KDB 0x80 #define KD_DEBUG_KDSERIAL 0x100 @@ -98,9 +98,6 @@ KdGdbDebugPrint (LPSTR Message); VOID KdDebugPrint (LPSTR Message); -VOID -KdbCreateThreadHook(PCONTEXT Context); - KD_CONTINUE_TYPE KdEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context, @@ -110,7 +107,6 @@ VOID KdPrintMda(PCH pch); #if !defined(KDBG) && !defined(DBG) # define KDB_LOADUSERMODULE_HOOK(LDRMOD) do { } while (0) -# define KDB_DELETEPROCESS_HOOK(PROCESS) do { } while (0) # define KDB_LOADDRIVER_HOOK(FILENAME, MODULE) do { } while (0) # define KDB_UNLOADDRIVER_HOOK(MODULE) do { } while (0) # define KDB_LOADERINIT_HOOK(NTOS, HAL) do { } while (0) @@ -118,7 +114,6 @@ VOID KdPrintMda(PCH pch); # define KDB_CREATE_THREAD_HOOK(CONTEXT) do { } while (0) #else # define KDB_LOADUSERMODULE_HOOK(LDRMOD) KdbSymLoadUserModuleSymbols(LDRMOD) -# define KDB_DELETEPROCESS_HOOK(PROCESS) KdbSymFreeProcessSymbols(PROCESS) # define KDB_LOADDRIVER_HOOK(FILENAME, MODULE) KdbSymLoadDriverSymbols(FILENAME, MODULE) # define KDB_UNLOADDRIVER_HOOK(MODULE) KdbSymUnloadDriverSymbols(MODULE) # define KDB_LOADERINIT_HOOK(NTOS, HAL) KdbSymInit(NTOS, HAL) @@ -126,6 +121,7 @@ VOID KdPrintMda(PCH pch); /*#define KDB_CREATE_THREAD_HOOK(CONTEXT) \ KdbCreateThreadHook(CONTEXT) */ + VOID KdbSymLoadUserModuleSymbols(IN PLDR_MODULE LdrModule); @@ -155,10 +151,18 @@ KdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord, KPROCESSOR_MODE PreviousMode, PCONTEXT Context, PKTRAP_FRAME TrapFrame, - BOOLEAN HandleAlways); + BOOLEAN FirstChance); #endif /* KDBG || DBG */ +#if !defined(KDBG) +# define KDB_DELETEPROCESS_HOOK(PROCESS) do { } while (0) +#else +# define KDB_DELETEPROCESS_HOOK(PROCESS) KdbDeleteProcessHook(PROCESS) +VOID +KdbDeleteProcessHook(IN PEPROCESS Process); +#endif /* KDBG */ + VOID DebugLogDumpMessages(VOID); diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index e9e78f6d9ef..305d46f4490 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -41,11 +41,42 @@ VOID KeSetGdtSelector(ULONG Entry, ULONG Value1, ULONG Value2); struct _KTHREAD; struct _KIRQ_TRAPFRAME; struct _KPCR; +struct _KPRCB; struct _KEXCEPTION_FRAME; #define IPI_REQUEST_FUNCTIONCALL 0 #define IPI_REQUEST_APC 1 #define IPI_REQUEST_DPC 2 +#define IPI_REQUEST_FREEZE 3 + +/* threadsch.c ********************************************************************/ + +/* Thread Scheduler Functions */ + +/* Readies a Thread for Execution. */ +VOID +STDCALL +KiDispatchThreadNoLock(ULONG NewThreadStatus); + +/* Readies a Thread for Execution. */ +VOID +STDCALL +KiDispatchThread(ULONG NewThreadStatus); + +/* Puts a Thread into a block state. */ +VOID +STDCALL +KiBlockThread(PNTSTATUS Status, + UCHAR Alertable, + ULONG WaitMode, + UCHAR WaitReason); + +/* Removes a thread out of a block state. */ +VOID +STDCALL +KiUnblockThread(PKTHREAD Thread, + PNTSTATUS WaitStatus, + KPRIORITY Increment); /* ipi.c ********************************************************************/ @@ -63,61 +94,67 @@ KeIpiGenericCall(VOID (STDCALL *WorkerRoutine)(PVOID), /* next file ***************************************************************/ -typedef struct _KPROCESS_PROFILE -/* - * List of the profile data structures associated with a process. - */ -{ - LIST_ENTRY ProfileListHead; - LIST_ENTRY ListEntry; - HANDLE Pid; -} KPROCESS_PROFILE, *PKPROCESS_PROFILE; +typedef struct _KPROFILE_SOURCE_OBJECT { + KPROFILE_SOURCE Source; + LIST_ENTRY ListEntry; +} KPROFILE_SOURCE_OBJECT, *PKPROFILE_SOURCE_OBJECT; -typedef struct _KPROFILE -/* - * Describes a contiguous region of process memory that is being profiled. - */ -{ - CSHORT Type; - CSHORT Name; - - /* Entry in the list of profile data structures for this process. */ - LIST_ENTRY ListEntry; - - /* Base of the region being profiled. */ - PVOID Base; - - /* Size of the region being profiled. */ - ULONG Size; - - /* Shift of offsets from the region to buckets in the profiling buffer. */ - ULONG BucketShift; - - /* MDL which described the buffer that receives profiling data. */ - struct _MDL *BufferMdl; - - /* System alias for the profiling buffer. */ - PULONG Buffer; - - /* Size of the buffer for profiling data. */ - ULONG BufferSize; - - /* - * Mask of processors for which profiling data should be collected. - * Currently unused. - */ - ULONG ProcessorMask; - - /* TRUE if profiling has been started for this region. */ - BOOLEAN Started; - - /* Pointer (and reference) to the process which is being profiled. */ - struct _EPROCESS *Process; +typedef struct _KPROFILE { + CSHORT Type; + CSHORT Size; + LIST_ENTRY ListEntry; + PVOID RegionStart; + PVOID RegionEnd; + ULONG BucketShift; + PVOID Buffer; + CSHORT Source; + ULONG Affinity; + BOOLEAN Active; + struct _KPROCESS *Process; } KPROFILE, *PKPROFILE; +/* Cached modules from the loader block */ +typedef enum _CACHED_MODULE_TYPE { + AnsiCodepage, + OemCodepage, + UnicodeCasemap, + SystemRegistry, + HardwareRegistry, + MaximumCachedModuleType, +} CACHED_MODULE_TYPE, *PCACHED_MODULE_TYPE; +extern PLOADER_MODULE CachedModules[MaximumCachedModuleType]; + VOID STDCALL DbgBreakPointNoBugCheck(VOID); +STDCALL +VOID +KeInitializeProfile(struct _KPROFILE* Profile, + struct _KPROCESS* Process, + PVOID ImageBase, + ULONG ImageSize, + ULONG BucketSize, + KPROFILE_SOURCE ProfileSource, + KAFFINITY Affinity); + +STDCALL +VOID +KeStartProfile(struct _KPROFILE* Profile, + PVOID Buffer); + +STDCALL +VOID +KeStopProfile(struct _KPROFILE* Profile); + +STDCALL +ULONG +KeQueryIntervalProfile(KPROFILE_SOURCE ProfileSource); + +STDCALL +VOID +KeSetIntervalProfile(KPROFILE_SOURCE ProfileSource, + ULONG Interval); + VOID STDCALL KeProfileInterrupt( @@ -131,43 +168,91 @@ KeProfileInterruptWithSource( IN KPROFILE_SOURCE Source ); -VOID KiAddProfileEventToProcess(PLIST_ENTRY ListHead, PVOID Eip); -VOID KiAddProfileEvent(KPROFILE_SOURCE Source, ULONG Eip); -VOID KiInsertProfileIntoProcess(PLIST_ENTRY ListHead, PKPROFILE Profile); -VOID KiInsertProfile(PKPROFILE Profile); -VOID KiRemoveProfile(PKPROFILE Profile); -VOID STDCALL KiDeleteProfile(PVOID ObjectBody); - VOID STDCALL KeUpdateSystemTime(PKTRAP_FRAME TrapFrame, KIRQL Irql); VOID STDCALL KeUpdateRunTime(PKTRAP_FRAME TrapFrame, KIRQL Irql); VOID STDCALL KiExpireTimers(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2); -KIRQL KeAcquireDispatcherDatabaseLock(VOID); -VOID KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID); -VOID KeReleaseDispatcherDatabaseLock(KIRQL Irql); -VOID KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID); +KIRQL inline FASTCALL KeAcquireDispatcherDatabaseLock(VOID); +VOID inline FASTCALL KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID); +VOID inline FASTCALL KeReleaseDispatcherDatabaseLock(KIRQL Irql); +VOID inline FASTCALL KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID); + +VOID +STDCALL +KeInitializeThread(struct _KPROCESS* Process, PKTHREAD Thread, BOOLEAN First); + +VOID +STDCALL +KeRundownThread(VOID); + +NTSTATUS KeReleaseThread(PKTHREAD Thread); + +VOID +STDCALL +KeStackAttachProcess ( + IN struct _KPROCESS* Process, + OUT PKAPC_STATE ApcState + ); + +VOID +STDCALL +KeUnstackDetachProcess ( + IN PKAPC_STATE ApcState + ); BOOLEAN KiDispatcherObjectWake(DISPATCHER_HEADER* hdr, KPRIORITY increment); VOID STDCALL KeExpireTimers(PKDPC Apc, PVOID Arg1, PVOID Arg2, PVOID Arg3); -VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header, ULONG Type, - ULONG Size, ULONG SignalState); +VOID inline FASTCALL KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header, ULONG Type, + ULONG Size, ULONG SignalState); VOID KeDumpStackFrames(PULONG Frame); BOOLEAN KiTestAlert(VOID); -BOOLEAN KiAbortWaitThread(struct _KTHREAD* Thread, NTSTATUS WaitStatus); +VOID +FASTCALL +KiAbortWaitThread(PKTHREAD Thread, + NTSTATUS WaitStatus, + KPRIORITY Increment); + +ULONG +STDCALL +KeForceResumeThread(IN PKTHREAD Thread); + +BOOLEAN STDCALL KiInsertTimer(PKTIMER Timer, LARGE_INTEGER DueTime); + +VOID inline FASTCALL KiSatisfyObjectWait(PDISPATCHER_HEADER Object, PKTHREAD Thread); + +BOOLEAN inline FASTCALL KiIsObjectSignaled(PDISPATCHER_HEADER Object, PKTHREAD Thread); + +VOID inline FASTCALL KiSatisifyMultipleObjectWaits(PKWAIT_BLOCK WaitBlock); + +VOID FASTCALL KiWaitTest(PDISPATCHER_HEADER Object, KPRIORITY Increment); PULONG KeGetStackTopThread(struct _ETHREAD* Thread); VOID KeContextToTrapFrame(PCONTEXT Context, PKTRAP_FRAME TrapFrame); VOID STDCALL KiDeliverApc(KPROCESSOR_MODE PreviousMode, PVOID Reserved, PKTRAP_FRAME TrapFrame); - -VOID KiInitializeUserApc(IN PVOID Reserved, + +LONG +STDCALL +KiInsertQueue(IN PKQUEUE Queue, + IN PLIST_ENTRY Entry, + BOOLEAN Head); + +ULONG +STDCALL +KeSetProcess(struct _KPROCESS* Process, + KPRIORITY Increment); + + +VOID STDCALL KeInitializeEventPair(PKEVENT_PAIR EventPair); + +VOID STDCALL KiInitializeUserApc(IN PVOID Reserved, IN PKTRAP_FRAME TrapFrame, IN PKNORMAL_ROUTINE NormalRoutine, IN PVOID NormalContext, @@ -183,6 +268,7 @@ STDCALL KeTestAlertThread(IN KPROCESSOR_MODE AlertMode); BOOLEAN STDCALL KeRemoveQueueApc (PKAPC Apc); +VOID FASTCALL KiWakeQueue(IN PKQUEUE Queue); PLIST_ENTRY STDCALL KeRundownQueue(IN PKQUEUE Queue); extern LARGE_INTEGER SystemBootTime; @@ -192,11 +278,11 @@ extern LARGE_INTEGER SystemBootTime; VOID KeInitExceptions(VOID); VOID KeInitInterrupts(VOID); VOID KeInitTimer(VOID); -VOID KeInitDpc(struct _KPCR* Pcr); +VOID KeInitDpc(struct _KPRCB* Prcb); VOID KeInitDispatcher(VOID); -VOID KeInitializeDispatcher(VOID); +VOID inline FASTCALL KeInitializeDispatcher(VOID); VOID KiInitializeSystemClock(VOID); -VOID KeInitializeBugCheck(VOID); +VOID KiInitializeBugCheck(VOID); VOID Phase1Initialization(PVOID Context); VOID KeInit1(PCHAR CommandLine, PULONG LastKernelAddress); diff --git a/reactos/ntoskrnl/include/internal/mm.h b/reactos/ntoskrnl/include/internal/mm.h index 31b0cac99dd..91fc8d9967a 100644 --- a/reactos/ntoskrnl/include/internal/mm.h +++ b/reactos/ntoskrnl/include/internal/mm.h @@ -628,6 +628,7 @@ PFN_TYPE MmGetLRUFirstUserPage(VOID); VOID MmSetLRULastPage(PFN_TYPE Page); VOID MmLockPage(PFN_TYPE Page); +VOID MmLockPageUnsafe(PFN_TYPE Page); VOID MmUnlockPage(PFN_TYPE Page); @@ -712,6 +713,7 @@ LONG MmAllocPagesSpecifyRange(ULONG Consumer, VOID MmDereferencePage(PFN_TYPE Page); VOID MmReferencePage(PFN_TYPE Page); +VOID MmReferencePageUnsafe(PFN_TYPE Page); BOOLEAN MmIsAccessedAndResetAccessPage(struct _EPROCESS* Process, PVOID Address); diff --git a/reactos/ntoskrnl/include/internal/nls.h b/reactos/ntoskrnl/include/internal/nls.h index 46906e37767..84787cba712 100644 --- a/reactos/ntoskrnl/include/internal/nls.h +++ b/reactos/ntoskrnl/include/internal/nls.h @@ -29,6 +29,7 @@ extern ULONG NlsUnicodeTableOffset; extern PUSHORT NlsUnicodeUpcaseTable; extern PUSHORT NlsUnicodeLowercaseTable; +VOID STDCALL RtlpInitNls(VOID); VOID RtlpImportAnsiCodePage(PUSHORT TableBase, ULONG Size); VOID RtlpImportOemCodePage(PUSHORT TableBase, ULONG Size); VOID RtlpImportUnicodeCasemap(PUSHORT TableBase, ULONG Size); diff --git a/reactos/ntoskrnl/include/internal/ntoskrnl.h b/reactos/ntoskrnl/include/internal/ntoskrnl.h index a98537fe3c0..972b42a09e8 100644 --- a/reactos/ntoskrnl/include/internal/ntoskrnl.h +++ b/reactos/ntoskrnl/include/internal/ntoskrnl.h @@ -49,10 +49,12 @@ VOID ExpInitializeProfileImplementation(VOID); */ VOID MmInitSystem(ULONG Phase, PLOADER_PARAMETER_BLOCK LoaderBlock, ULONG LastKernelAddress); VOID IoInit(VOID); -VOID IoInit2(VOID); +VOID IoInit2(BOOLEAN BootLog); +VOID STDCALL IoInit3(VOID); VOID ObInit(VOID); VOID PsInit(VOID); VOID CmInitializeRegistry(VOID); +VOID STDCALL CmInitHives(BOOLEAN SetupBoot); VOID CmInit2(PCHAR CommandLine); VOID CmShutdownRegistry(VOID); BOOLEAN CmImportSystemHive(PCHAR ChunkBase, ULONG ChunkSize); diff --git a/reactos/ntoskrnl/include/internal/ob.h b/reactos/ntoskrnl/include/internal/ob.h index 59255bac675..7e539064202 100644 --- a/reactos/ntoskrnl/include/internal/ob.h +++ b/reactos/ntoskrnl/include/internal/ob.h @@ -157,7 +157,6 @@ typedef struct _OBJECT_HEADER LIST_ENTRY Entry; LONG RefCount; LONG HandleCount; - BOOLEAN CloseInProcess; BOOLEAN Permanent; BOOLEAN Inherit; struct _DIRECTORY_OBJECT* Parent; @@ -228,17 +227,22 @@ enum OBJTYP_MAX, }; +#define HEADER_TO_BODY(objhdr) \ + (PVOID)((ULONG_PTR)objhdr + sizeof(OBJECT_HEADER) - sizeof(COMMON_BODY_HEADER)) + +#define BODY_TO_HEADER(objbdy) \ + CONTAINING_RECORD(&(((PCOMMON_BODY_HEADER)objbdy)->Type), OBJECT_HEADER, Type) #define OBJECT_ALLOC_SIZE(ObjectSize) ((ObjectSize)+sizeof(OBJECT_HEADER)-sizeof(COMMON_BODY_HEADER)) +#define HANDLE_TO_EX_HANDLE(handle) \ + (LONG)(((LONG)(handle) >> 2) - 1) +#define EX_HANDLE_TO_HANDLE(exhandle) \ + (HANDLE)(((exhandle) + 1) << 2) extern PDIRECTORY_OBJECT NameSpaceRoot; extern POBJECT_TYPE ObSymbolicLinkType; - -POBJECT_HEADER BODY_TO_HEADER(PVOID body); -PVOID HEADER_TO_BODY(POBJECT_HEADER obj); - VOID ObpAddEntryDirectory(PDIRECTORY_OBJECT Parent, POBJECT_HEADER Header, PWSTR Name); @@ -260,13 +264,11 @@ NTSTATUS ObFindObject(POBJECT_ATTRIBUTES ObjectAttributes, PVOID* ReturnedObject, PUNICODE_STRING RemainingPath, POBJECT_TYPE ObjectType); -VOID ObCloseAllHandles(struct _EPROCESS* Process); VOID ObDeleteHandleTable(struct _EPROCESS* Process); NTSTATUS ObDeleteHandle(PEPROCESS Process, - HANDLE Handle, - PVOID *ObjectBody); + HANDLE Handle); NTSTATUS ObpQueryHandleAttributes(HANDLE Handle, @@ -300,7 +302,9 @@ ObQueryDeviceMapInformation(PEPROCESS Process, PPROCESS_DEVICEMAP_INFORMATION De VOID FASTCALL ObpSetPermanentObject (IN PVOID ObjectBody, IN BOOLEAN Permanent); - +VOID +STDCALL +ObKillProcess(PEPROCESS Process); /* Security descriptor cache functions */ NTSTATUS @@ -326,7 +330,7 @@ typedef struct _CAPTURED_OBJECT_ATTRIBUTES HANDLE RootDirectory; ULONG Attributes; PSECURITY_DESCRIPTOR SecurityDescriptor; - /* PVOID SecurityQualityOfService; */ + PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; } CAPTURED_OBJECT_ATTRIBUTES, *PCAPTURED_OBJECT_ATTRIBUTES; NTSTATUS @@ -377,10 +381,8 @@ typedef struct _INFORMATION_CLASS_INFO else if(ClassList[Class].RequiredSize##Mode > 0 && \ (BufferLen) != ClassList[Class].RequiredSize##Mode) \ { \ - if((!(ClassList[Class].Flags & ICIF_##Mode##_SIZE_VARIABLE) && \ - (BufferLen) != ClassList[Class].RequiredSize##Mode) || \ - ((ClassList[Class].Flags & ICIF_##Mode##_SIZE_VARIABLE) && \ - (BufferLen) < ClassList[Class].RequiredSize##Mode)) \ + if(!(ClassList[Class].Flags & ICIF_##Mode##_SIZE_VARIABLE) && \ + (BufferLen) != ClassList[Class].RequiredSize##Mode) \ { \ *(StatusVar) = STATUS_INFO_LENGTH_MISMATCH; \ } \ diff --git a/reactos/ntoskrnl/include/internal/po.h b/reactos/ntoskrnl/include/internal/po.h index ffed3760d13..19387ebba6f 100644 --- a/reactos/ntoskrnl/include/internal/po.h +++ b/reactos/ntoskrnl/include/internal/po.h @@ -18,7 +18,7 @@ extern PDEVICE_NODE PopSystemPowerDeviceNode; VOID -PoInit(VOID); +PoInit(PLOADER_PARAMETER_BLOCK LoaderBlock, BOOLEAN ForceAcpiDisable); NTSTATUS PopSetSystemPowerState( diff --git a/reactos/ntoskrnl/include/internal/port.h b/reactos/ntoskrnl/include/internal/port.h index c987f67c811..4491247c70f 100644 --- a/reactos/ntoskrnl/include/internal/port.h +++ b/reactos/ntoskrnl/include/internal/port.h @@ -29,14 +29,6 @@ typedef struct _EPORT ULONG MaxPoolUsage; /* size of NP zone */ } EPORT, * PEPORT; - -typedef struct _EPORT_TERMINATION_REQUEST -{ - LIST_ENTRY ThreadListEntry; - PEPORT Port; -} EPORT_TERMINATION_REQUEST, *PEPORT_TERMINATION_REQUEST; - - typedef struct _EPORT_CONNECT_REQUEST_MESSAGE { LPC_MESSAGE MessageHeader; @@ -59,6 +51,11 @@ typedef struct _EPORT_CONNECT_REPLY_MESSAGE UCHAR ConnectData[0]; } EPORT_CONNECT_REPLY_MESSAGE, *PEPORT_CONNECT_REPLY_MESSAGE; +typedef struct _TERMINATION_PORT { + struct _TERMINATION_PORT *Next; + PVOID Port; +} TERMINATION_PORT, *PTERMINATION_PORT; + NTSTATUS STDCALL LpcRequestPort (PEPORT Port, PLPC_MESSAGE LpcMessage); diff --git a/reactos/ntoskrnl/include/internal/ps.h b/reactos/ntoskrnl/include/internal/ps.h index 1be0eeefe19..75e851a2d9e 100644 --- a/reactos/ntoskrnl/include/internal/ps.h +++ b/reactos/ntoskrnl/include/internal/ps.h @@ -156,6 +156,7 @@ typedef struct #include +/* This needs to be fixed ASAP! */ typedef struct _ETHREAD { KTHREAD Tcb; @@ -169,7 +170,10 @@ typedef struct _ETHREAD NTSTATUS ExitStatus; PVOID OfsChain; LIST_ENTRY PostBlockList; - LIST_ENTRY TerminationPortList; + union { + struct _TERMINATION_PORT *TerminationPort; + struct _ETHREAD* ReaperLink; + }; KSPIN_LOCK ActiveTimerListLock; LIST_ENTRY ActiveTimerListHead; CLIENT_ID Cid; @@ -201,6 +205,7 @@ typedef struct _ETHREAD UCHAR ActiveImpersonationInfo; ULONG PerformanceCountHigh; LIST_ENTRY ThreadListEntry; + BOOLEAN SystemThread; } ETHREAD; #include @@ -345,7 +350,7 @@ struct _EPROCESS LIST_ENTRY SessionProcessLinks; struct _EPORT *DebugPort; struct _EPORT *ExceptionPort; - HANDLE_TABLE HandleTable; + PHANDLE_TABLE ObjectTable; PVOID Token; FAST_MUTEX WorkingSetLock; ULONG WorkingSetPage; @@ -416,13 +421,13 @@ struct _EPROCESS PRTL_BITMAP VadPhysicalPagesBitMap; ULONG VadPhysicalPages; KSPIN_LOCK AweLock; + ULONG Cookie; /* * FIXME - ReactOS specified - remove the following fields ASAP!!! */ MADDRESS_SPACE AddressSpace; LIST_ENTRY ProcessListEntry; - FAST_MUTEX TebLock; PVOID TebBlock; PVOID TebLastAllocated; }; @@ -436,7 +441,6 @@ VOID PiShutdownProcessManager(VOID); VOID PsInitThreadManagment(VOID); VOID PsInitProcessManagment(VOID); VOID PsInitIdleThread(VOID); -VOID PsDispatchThreadNoLock(ULONG NewThreadStatus); VOID PiTerminateProcessThreads(PEPROCESS Process, NTSTATUS ExitStatus); VOID PsTerminateCurrentThread(NTSTATUS ExitStatus); VOID PsTerminateOtherThread(PETHREAD Thread, NTSTATUS ExitStatus); @@ -453,22 +457,27 @@ VOID PsQueueThreadReap(PETHREAD Thread); NTSTATUS PsInitializeThread(PEPROCESS Process, PETHREAD* ThreadPtr, - PHANDLE ThreadHandle, - ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, + KPROCESSOR_MODE AccessMode, BOOLEAN First); -PACCESS_TOKEN PsReferenceEffectiveToken(PETHREAD Thread, +PACCESS_TOKEN STDCALL PsReferenceEffectiveToken(PETHREAD Thread, PTOKEN_TYPE TokenType, PUCHAR b, PSECURITY_IMPERSONATION_LEVEL Level); -NTSTATUS PsOpenTokenOfProcess(HANDLE ProcessHandle, +NTSTATUS STDCALL PsOpenTokenOfProcess(HANDLE ProcessHandle, PACCESS_TOKEN* Token); - +VOID +STDCALL +PspTerminateProcessThreads(PEPROCESS Process, + NTSTATUS ExitStatus); NTSTATUS PsSuspendThread(PETHREAD Thread, PULONG PreviousCount); NTSTATUS PsResumeThread(PETHREAD Thread, PULONG PreviousCount); - +NTSTATUS +STDCALL +PspAssignPrimaryToken(PEPROCESS Process, + HANDLE TokenHandle); VOID STDCALL PsExitSpecialApc(PKAPC Apc, PKNORMAL_ROUTINE *NormalRoutine, PVOID *NormalContext, @@ -497,27 +506,25 @@ VOID STDCALL PsExitSpecialApc(PKAPC Apc, #define PROCESS_PRIO_RT 18 -VOID -KeInitializeThread(PKPROCESS Process, PKTHREAD Thread, BOOLEAN First); -NTSTATUS KeReleaseThread(PKTHREAD Thread); - -VOID -STDCALL -KeStackAttachProcess ( - IN PKPROCESS Process, - OUT PKAPC_STATE ApcState - ); - -VOID -STDCALL -KeUnstackDetachProcess ( - IN PKAPC_STATE ApcState - ); - VOID STDCALL PiDeleteProcess(PVOID ObjectBody); -VOID PsReapThreads(VOID); -VOID PsInitializeThreadReaper(VOID); -VOID PsQueueThreadReap(PETHREAD Thread); + +VOID +STDCALL +PspReapRoutine(PVOID Context); + +VOID +STDCALL +PspExitThread(NTSTATUS ExitStatus); + +extern LIST_ENTRY PspReaperListHead; +extern WORK_QUEUE_ITEM PspReaperWorkItem; +extern BOOLEAN PspReaping; + +VOID +STDCALL +PspTerminateThreadByPointer(PETHREAD Thread, + NTSTATUS ExitStatus); + VOID PsUnfreezeOtherThread(PETHREAD Thread); VOID PsFreezeOtherThread(PETHREAD Thread); VOID PsFreezeProcessThreads(PEPROCESS Process); @@ -525,11 +532,6 @@ VOID PsUnfreezeProcessThreads(PEPROCESS Process); ULONG PsEnumThreadsByProcess(PEPROCESS Process); PEPROCESS PsGetNextProcess(PEPROCESS OldProcess); VOID -PsBlockThread(PNTSTATUS Status, UCHAR Alertable, ULONG WaitMode, - BOOLEAN DispatcherLock, KIRQL WaitIrql, UCHAR WaitReason); -VOID -PsUnblockThread(PETHREAD Thread, PNTSTATUS WaitStatus, KPRIORITY Increment); -VOID PsApplicationProcessorInit(VOID); VOID PsPrepareForApplicationProcessorInit(ULONG Id); @@ -548,10 +550,19 @@ VOID STDCALL PiSuspendThreadNormalRoutine(PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2); -VOID STDCALL -PsDispatchThread(ULONG NewThreadStatus); VOID PsInitialiseSuspendImplementation(VOID); +NTSTATUS +STDCALL +PspExitProcess(PEPROCESS Process); + +VOID +STDCALL +PspDeleteProcess(PVOID ObjectBody); + +VOID +STDCALL +PspDeleteThread(PVOID ObjectBody); extern LONG PiNrThreadsAwaitingReaping; @@ -640,27 +651,13 @@ typedef struct _EJOB VOID INIT_FUNCTION PsInitJobManagment(VOID); -/* CID */ - -typedef struct _CID_OBJECT -{ - LONG ref; - HANDLE Handle; - LIST_ENTRY Entry; - FAST_MUTEX Lock; - union - { - struct _EPROCESS *Process; - struct _ETHREAD *Thread; - PVOID Object; - } Obj; -} CID_OBJECT, *PCID_OBJECT; +/* CLIENT ID */ NTSTATUS PsCreateCidHandle(PVOID Object, POBJECT_TYPE ObjectType, PHANDLE Handle); NTSTATUS PsDeleteCidHandle(HANDLE CidHandle, POBJECT_TYPE ObjectType); -PCID_OBJECT PsLockCidHandle(HANDLE CidHandle, POBJECT_TYPE ObjectType); -VOID PsUnlockCidObject(PCID_OBJECT CidObject); -NTSTATUS PsLockProcess(PEPROCESS Process, BOOL Timeout); +PHANDLE_TABLE_ENTRY PsLookupCidHandle(HANDLE CidHandle, POBJECT_TYPE ObjectType, PVOID *Object); +VOID PsUnlockCidHandle(PHANDLE_TABLE_ENTRY CidEntry); +NTSTATUS PsLockProcess(PEPROCESS Process, BOOLEAN Timeout); VOID PsUnlockProcess(PEPROCESS Process); #define ETHREAD_TO_KTHREAD(pEThread) (&(pEThread)->Tcb) diff --git a/reactos/ntoskrnl/include/internal/se.h b/reactos/ntoskrnl/include/internal/se.h index 8d3e269906a..58e231a3ec4 100644 --- a/reactos/ntoskrnl/include/internal/se.h +++ b/reactos/ntoskrnl/include/internal/se.h @@ -148,6 +148,60 @@ SepPrivilegeCheck(PTOKEN Token, ULONG PrivilegeControl, KPROCESSOR_MODE PreviousMode); +NTSTATUS +SepCaptureSecurityQualityOfService(IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN KPROCESSOR_MODE AccessMode, + IN POOL_TYPE PoolType, + IN BOOLEAN CaptureIfKernel, + OUT PSECURITY_QUALITY_OF_SERVICE *CapturedSecurityQualityOfService, + OUT PBOOLEAN Present); + +VOID +SepReleaseSecurityQualityOfService(IN PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService OPTIONAL, + IN KPROCESSOR_MODE AccessMode, + IN BOOLEAN CaptureIfKernel); + +NTSTATUS +SepCaptureSid(IN PSID InputSid, + IN KPROCESSOR_MODE AccessMode, + IN POOL_TYPE PoolType, + IN BOOLEAN CaptureIfKernel, + OUT PSID *CapturedSid); + +VOID +SepReleaseSid(IN PSID CapturedSid, + IN KPROCESSOR_MODE AccessMode, + IN BOOLEAN CaptureIfKernel); + +NTSTATUS +SepCaptureAcl(IN PACL InputAcl, + IN KPROCESSOR_MODE AccessMode, + IN POOL_TYPE PoolType, + IN BOOLEAN CaptureIfKernel, + OUT PACL *CapturedAcl); + +VOID +SepReleaseAcl(IN PACL CapturedAcl, + IN KPROCESSOR_MODE AccessMode, + IN BOOLEAN CaptureIfKernel); + +#define SepAcquireTokenLockExclusive(Token) \ + do { \ + KeEnterCriticalRegion(); \ + ExAcquireResourceExclusive(((PTOKEN)Token)->TokenLock, TRUE); \ + while(0) + +#define SepAcquireTokenLockShared(Token) \ + do { \ + KeEnterCriticalRegion(); \ + ExAcquireResourceShared(((PTOKEN)Token)->TokenLock, TRUE); \ + while(0) + +#define SepReleaseTokenLock(Token) \ + do { \ + ExReleaseResource(((PTOKEN)Token)->TokenLock); \ + KeLeaveCriticalRegion(); \ + while(0) #endif /* __NTOSKRNL_INCLUDE_INTERNAL_SE_H */ diff --git a/reactos/ntoskrnl/include/ntoskrnl.h b/reactos/ntoskrnl/include/ntoskrnl.h index c4b8a1ab400..ef8e5ee09be 100755 --- a/reactos/ntoskrnl/include/ntoskrnl.h +++ b/reactos/ntoskrnl/include/ntoskrnl.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/reactos/ntoskrnl/io/bootlog.c b/reactos/ntoskrnl/io/bootlog.c index 71dc17b194a..9324dd9f6de 100644 --- a/reactos/ntoskrnl/io/bootlog.c +++ b/reactos/ntoskrnl/io/bootlog.c @@ -27,13 +27,14 @@ static ERESOURCE IopBootLogResource; /* FUNCTIONS ****************************************************************/ VOID INIT_FUNCTION -IopInitBootLog(VOID) +IopInitBootLog(BOOLEAN StartBootLog) { ExInitializeResourceLite(&IopBootLogResource); + if (StartBootLog) IopStartBootLog(); } -VOID +VOID INIT_FUNCTION IopStartBootLog(VOID) { IopBootLogCreate = TRUE; diff --git a/reactos/ntoskrnl/io/buildirp.c b/reactos/ntoskrnl/io/buildirp.c index 99fdc41cbbe..8bfdf453bd8 100644 --- a/reactos/ntoskrnl/io/buildirp.c +++ b/reactos/ntoskrnl/io/buildirp.c @@ -37,7 +37,6 @@ NTSTATUS IoPrepareIrpBuffer(PIRP Irp, (PVOID)ExAllocatePoolWithTag(NonPagedPool,Length, TAG_SYS_BUF); if (Irp->AssociatedIrp.SystemBuffer==NULL) { - IoFreeIrp(Irp); return(STATUS_NOT_IMPLEMENTED); } /* FIXME: should copy buffer in on other ops */ @@ -121,13 +120,34 @@ IoBuildAsynchronousFsdRequest(ULONG MajorFunction, StackPtr->FileObject = NULL; StackPtr->CompletionRoutine = NULL; - if (Buffer != NULL) + if (Length > 0) { - IoPrepareIrpBuffer(Irp, - DeviceObject, - Buffer, - Length, - MajorFunction); + NTSTATUS Status = STATUS_SUCCESS; + + _SEH_FILTER(FreeAndGoOn) + { + IoFreeIrp(Irp); + return EXCEPTION_CONTINUE_SEARCH; + } + _SEH_TRY_FILTER(FreeAndGoOn) + { + Status = IoPrepareIrpBuffer(Irp, + DeviceObject, + Buffer, + Length, + MajorFunction); + } + _SEH_HANDLE + { + KEBUGCHECK(0); + } + _SEH_END; + + if (!NT_SUCCESS(Status)) + { + IoFreeIrp(Irp); + return NULL; + } } if (MajorFunction == IRP_MJ_READ) @@ -296,7 +316,27 @@ IoBuildDeviceIoControlRequest(ULONG IoControlCode, FALSE, FALSE, Irp); - MmProbeAndLockPages(Irp->MdlAddress,UserMode,IoReadAccess); + if (Irp->MdlAddress == NULL) + { + IoFreeIrp(Irp); + return NULL; + } + + _SEH_FILTER(FreeAndGoOn) + { + IoFreeIrp(Irp); + return EXCEPTION_CONTINUE_SEARCH; + } + _SEH_TRY_FILTER(FreeAndGoOn) + { + MmProbeAndLockPages(Irp->MdlAddress,UserMode,IoReadAccess); + } + _SEH_HANDLE + { + KEBUGCHECK(0); + } + _SEH_END; + } break; @@ -329,7 +369,26 @@ IoBuildDeviceIoControlRequest(ULONG IoControlCode, FALSE, FALSE, Irp); - MmProbeAndLockPages(Irp->MdlAddress,UserMode,IoWriteAccess); + if (Irp->MdlAddress == NULL) + { + IoFreeIrp(Irp); + return NULL; + } + + _SEH_FILTER(FreeAndGoOn) + { + IoFreeIrp(Irp); + return EXCEPTION_CONTINUE_SEARCH; + } + _SEH_TRY_FILTER(FreeAndGoOn) + { + MmProbeAndLockPages(Irp->MdlAddress,UserMode,IoWriteAccess); + } + _SEH_HANDLE + { + KEBUGCHECK(0); + } + _SEH_END; } break; @@ -352,12 +411,12 @@ IoBuildDeviceIoControlRequest(ULONG IoControlCode, */ PIRP STDCALL IoBuildSynchronousFsdRequest(ULONG MajorFunction, - PDEVICE_OBJECT DeviceObject, - PVOID Buffer, - ULONG Length, - PLARGE_INTEGER StartingOffset, - PKEVENT Event, - PIO_STATUS_BLOCK IoStatusBlock) + PDEVICE_OBJECT DeviceObject, + PVOID Buffer, + ULONG Length, + PLARGE_INTEGER StartingOffset, + PKEVENT Event, + PIO_STATUS_BLOCK IoStatusBlock) /* * FUNCTION: Allocates and builds an IRP to be sent synchronously to lower * level driver(s) diff --git a/reactos/ntoskrnl/io/cancel.c b/reactos/ntoskrnl/io/cancel.c index 1ce6919fb34..252fbd2955c 100644 --- a/reactos/ntoskrnl/io/cancel.c +++ b/reactos/ntoskrnl/io/cancel.c @@ -204,18 +204,23 @@ BOOLEAN STDCALL IoCancelIrp(PIRP Irp) { KIRQL oldlvl; + PDRIVER_CANCEL CancelRoutine; DPRINT("IoCancelIrp(Irp %x)\n",Irp); IoAcquireCancelSpinLock(&oldlvl); + Irp->Cancel = TRUE; - if (Irp->CancelRoutine == NULL) + + CancelRoutine = IoSetCancelRoutine(Irp, NULL); + if (CancelRoutine == NULL) { IoReleaseCancelSpinLock(oldlvl); return(FALSE); } + Irp->CancelIrql = oldlvl; - Irp->CancelRoutine(IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Irp); + CancelRoutine(IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Irp); return(TRUE); } diff --git a/reactos/ntoskrnl/io/cleanup.c b/reactos/ntoskrnl/io/cleanup.c index c085eb1bb2f..ef2097c0dc5 100644 --- a/reactos/ntoskrnl/io/cleanup.c +++ b/reactos/ntoskrnl/io/cleanup.c @@ -109,7 +109,10 @@ VOID IoReadWriteCompletion(PDEVICE_OBJECT DeviceObject, if (DeviceObject->Flags & DO_DIRECT_IO) { - IoFreeMdl(Irp->MdlAddress); + if (Irp->MdlAddress) + { + IoFreeMdl(Irp->MdlAddress); + } } } diff --git a/reactos/ntoskrnl/io/create.c b/reactos/ntoskrnl/io/create.c index b507fc51f93..08d46b0efbc 100644 --- a/reactos/ntoskrnl/io/create.c +++ b/reactos/ntoskrnl/io/create.c @@ -176,7 +176,7 @@ IopCreateFile(PVOID ObjectBody, FileObject, DeviceObject); FileObject->Vpb = DeviceObject->Vpb; - FileObject->Type = InternalFileType; + FileObject->Type = IO_TYPE_FILE; return(STATUS_SUCCESS); } @@ -240,7 +240,7 @@ IoCreateStreamFileObject(PFILE_OBJECT FileObject, CreatedFileObject->DeviceObject = DeviceObject->Vpb->DeviceObject; CreatedFileObject->Vpb = DeviceObject->Vpb; - CreatedFileObject->Type = InternalFileType; + CreatedFileObject->Type = IO_TYPE_FILE; CreatedFileObject->Flags |= FO_DIRECT_DEVICE_OPEN; // shouldn't we initialize the lock event, and several other things here too? @@ -448,9 +448,9 @@ IoCreateFile(OUT PHANDLE FileHandle, return Status; } - RtlMapGenericMask(&DesiredAccess, - BODY_TO_HEADER(FileObject)->ObjectType->Mapping); - + RtlMapGenericMask(&DesiredAccess, + BODY_TO_HEADER(FileObject)->ObjectType->Mapping); + Status = ObInsertObject ((PVOID)FileObject, NULL, DesiredAccess, diff --git a/reactos/ntoskrnl/io/device.c b/reactos/ntoskrnl/io/device.c index d68f4d89a4a..ca5a739e07b 100644 --- a/reactos/ntoskrnl/io/device.c +++ b/reactos/ntoskrnl/io/device.c @@ -82,7 +82,6 @@ IopInitializeDevice( return Status; } -#ifdef ACPI if (Fdo->DeviceType == FILE_DEVICE_ACPI) { static BOOLEAN SystemPowerDeviceNodeCreated = FALSE; @@ -94,7 +93,6 @@ IopInitializeDevice( SystemPowerDeviceNodeCreated = TRUE; } } -#endif /* ACPI */ if (Fdo->DeviceType == FILE_DEVICE_BUS_EXTENDER || Fdo->DeviceType == FILE_DEVICE_ACPI) @@ -126,7 +124,7 @@ IopCreateDevice( ObjectBody, Parent, RemainingPath); if (RemainingPath != NULL && wcschr(RemainingPath + 1, '\\') != NULL) - return STATUS_UNSUCCESSFUL; + return STATUS_OBJECT_PATH_NOT_FOUND; return STATUS_SUCCESS; } @@ -168,8 +166,11 @@ IoAttachDeviceToDeviceStackSafe( OUT PDEVICE_OBJECT *AttachedToDeviceObject ) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + /* FIXME: IoAttachDeviceToDeviceStackSafe must not call + * IoAttachDeviceToDeviceStack, but the other way around! */ + DPRINT1("IoAttachDeviceToDeviceStackSafe() badly implemented!\n"); + *AttachedToDeviceObject = IoAttachDeviceToDeviceStack(SourceDevice, TargetDevice); + return STATUS_SUCCESS; } /* @@ -348,7 +349,10 @@ IoGetDeviceObjectPointer( FILE_NON_DIRECTORY_FILE); if (!NT_SUCCESS(Status)) + { + DPRINT1("NtOpenFile failed, Status: 0x%x\n", Status); return Status; + } Status = ObReferenceObjectByHandle( FileHandle, diff --git a/reactos/ntoskrnl/io/dir.c b/reactos/ntoskrnl/io/dir.c index 90b91800927..7d09e476253 100644 --- a/reactos/ntoskrnl/io/dir.c +++ b/reactos/ntoskrnl/io/dir.c @@ -38,13 +38,41 @@ NtNotifyChangeDirectoryFile ( PIRP Irp; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; - NTSTATUS Status; PIO_STACK_LOCATION IoStack; KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; DPRINT("NtNotifyChangeDirectoryFile()\n"); + + PAGED_CODE(); PreviousMode = ExGetPreviousMode(); + + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForWrite(IoStatusBlock, + sizeof(IO_STATUS_BLOCK), + sizeof(ULONG)); + if(BufferSize != 0) + { + ProbeForWrite(Buffer, + BufferSize, + sizeof(ULONG)); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } Status = ObReferenceObjectByHandle(FileHandle, FILE_LIST_DIRECTORY, @@ -155,13 +183,38 @@ NtQueryDirectoryFile( PIRP Irp; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; - NTSTATUS Status; PIO_STACK_LOCATION IoStack; KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; DPRINT("NtQueryDirectoryFile()\n"); + + PAGED_CODE(); PreviousMode = ExGetPreviousMode(); + + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForWrite(IoStatusBlock, + sizeof(IO_STATUS_BLOCK), + sizeof(ULONG)); + ProbeForWrite(FileInformation, + Length, + sizeof(ULONG)); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } Status = ObReferenceObjectByHandle(FileHandle, FILE_LIST_DIRECTORY, diff --git a/reactos/ntoskrnl/io/driver.c b/reactos/ntoskrnl/io/driver.c index 7cab4e63911..e2452677368 100644 --- a/reactos/ntoskrnl/io/driver.c +++ b/reactos/ntoskrnl/io/driver.c @@ -160,7 +160,7 @@ IopCreateDriver( RtlZeroMemory(Object->DriverExtension, sizeof(DRIVER_EXTENSION)); - Object->Type = InternalDriverType; + Object->Type = IO_TYPE_DRIVER; for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) Object->MajorFunction[i] = IopInvalidDeviceRequest; @@ -960,7 +960,7 @@ IoCreateDriverList(VOID) NULL); Status = ZwOpenKey(&KeyHandle, - 0x10001, + KEY_ENUMERATE_SUB_KEYS, &ObjectAttributes); if (!NT_SUCCESS(Status)) { @@ -1252,11 +1252,12 @@ IopInitializeBootDrivers(VOID) */ for (i = 1; i < KeLoaderBlock.ModsCount; i++) { - MiFreeBootDriverMemory((PVOID)KeLoaderModules[i].ModStart, KeLoaderModules[i].ModEnd - KeLoaderModules[i].ModStart); } + KeLoaderBlock.ModsCount = 0; + if (BootDriverCount == 0) { DbgPrint("No boot drivers available.\n"); diff --git a/reactos/ntoskrnl/io/file.c b/reactos/ntoskrnl/io/file.c index 21d4a116962..f95a5fc9f39 100644 --- a/reactos/ntoskrnl/io/file.c +++ b/reactos/ntoskrnl/io/file.c @@ -50,7 +50,7 @@ NtQueryInformationFile(HANDLE FileHandle, PreviousMode = ExGetPreviousMode(); Status = ObReferenceObjectByHandle(FileHandle, - FILE_READ_ATTRIBUTES, + 0, /* FIXME - access depends on the information class! */ IoFileObjectType, PreviousMode, (PVOID *)&FileObject, @@ -402,7 +402,7 @@ NtSetInformationFile(HANDLE FileHandle, /* Get the file object from the file handle */ Status = ObReferenceObjectByHandle(FileHandle, - FILE_WRITE_ATTRIBUTES, + 0, /* FIXME - depends on the information class */ IoFileObjectType, PreviousMode, (PVOID *)&FileObject, diff --git a/reactos/ntoskrnl/io/iocomp.c b/reactos/ntoskrnl/io/iocomp.c index de412e59036..fe54d181772 100644 --- a/reactos/ntoskrnl/io/iocomp.c +++ b/reactos/ntoskrnl/io/iocomp.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/io/iocomp.c @@ -18,66 +17,54 @@ POBJECT_TYPE ExIoCompletionType; -NPAGED_LOOKASIDE_LIST IoCompletionPacketLookaside; +NPAGED_LOOKASIDE_LIST IoCompletionPacketLookaside; static GENERIC_MAPPING ExIoCompletionMapping = { - STANDARD_RIGHTS_READ | IO_COMPLETION_QUERY_STATE, - STANDARD_RIGHTS_WRITE | IO_COMPLETION_MODIFY_STATE, - STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | IO_COMPLETION_QUERY_STATE, - IO_COMPLETION_ALL_ACCESS + STANDARD_RIGHTS_READ | IO_COMPLETION_QUERY_STATE, + STANDARD_RIGHTS_WRITE | IO_COMPLETION_MODIFY_STATE, + STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE | IO_COMPLETION_QUERY_STATE, + IO_COMPLETION_ALL_ACCESS +}; + +static const INFORMATION_CLASS_INFO ExIoCompletionInfoClass[] = { + + /* IoCompletionBasicInformation */ + ICI_SQ_SAME( sizeof(IO_COMPLETION_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), }; /* FUNCTIONS *****************************************************************/ -NTSTATUS +VOID STDCALL -IopCreateIoCompletion( - PVOID ObjectBody, - PVOID Parent, - PWSTR RemainingPath, - POBJECT_ATTRIBUTES ObjectAttributes - ) -{ - DPRINT("IopCreateIoCompletion(ObjectBody %x, Parent %x, RemainingPath %S)\n", - ObjectBody, Parent, RemainingPath); - - if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL) - { - return STATUS_UNSUCCESSFUL; - } - - return STATUS_SUCCESS; -} - -VOID STDCALL IopDeleteIoCompletion(PVOID ObjectBody) { - PKQUEUE Queue = ObjectBody; + PKQUEUE Queue = ObjectBody; + PLIST_ENTRY FirstEntry; + PLIST_ENTRY CurrentEntry; + PIO_COMPLETION_PACKET Packet; - DPRINT("IopDeleteIoCompletion()\n"); + DPRINT("IopDeleteIoCompletion()\n"); - KeRundownQueue(Queue); -} - - -/* - * @unimplemented - */ -NTSTATUS -STDCALL -IoSetCompletionRoutineEx( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PIO_COMPLETION_ROUTINE CompletionRoutine, - IN PVOID Context, - IN BOOLEAN InvokeOnSuccess, - IN BOOLEAN InvokeOnError, - IN BOOLEAN InvokeOnCancel - ) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + /* Rundown the Queue */ + FirstEntry = KeRundownQueue(Queue); + + /* Clean up the IRPs */ + if (FirstEntry) { + + CurrentEntry = FirstEntry; + do { + + /* Get the Packet */ + Packet = CONTAINING_RECORD(CurrentEntry, IO_COMPLETION_PACKET, ListEntry); + + /* Go to next Entry */ + CurrentEntry = CurrentEntry->Flink; + + /* Free it */ + ExFreeToNPagedLookasideList(&IoCompletionPacketLookaside, Packet); + } while (FirstEntry != CurrentEntry); + } } /* @@ -85,305 +72,412 @@ IoSetCompletionRoutineEx( */ NTSTATUS STDCALL -IoSetIoCompletion ( - IN PVOID IoCompletion, - IN PVOID KeyContext, - IN PVOID ApcContext, - IN NTSTATUS IoStatus, - IN ULONG_PTR IoStatusInformation, - IN BOOLEAN Quota - ) +IoSetIoCompletion(IN PVOID IoCompletion, + IN PVOID KeyContext, + IN PVOID ApcContext, + IN NTSTATUS IoStatus, + IN ULONG_PTR IoStatusInformation, + IN BOOLEAN Quota) { - PKQUEUE Queue = (PKQUEUE) IoCompletion; - PIO_COMPLETION_PACKET Packet; + PKQUEUE Queue = (PKQUEUE)IoCompletion; + PIO_COMPLETION_PACKET Packet; - Packet = ExAllocateFromNPagedLookasideList(&IoCompletionPacketLookaside); - if (NULL == Packet) - { - return STATUS_NO_MEMORY; - } + /* Allocate the Packet */ + Packet = ExAllocateFromNPagedLookasideList(&IoCompletionPacketLookaside); + if (NULL == Packet) return STATUS_NO_MEMORY; + + /* Set up the Packet */ + Packet->Key = KeyContext; + Packet->Context = ApcContext; + Packet->IoStatus.Status = IoStatus; + Packet->IoStatus.Information = IoStatusInformation; + + /* Insert the Queue */ + KeInsertQueue(Queue, &Packet->ListEntry); - Packet->Key = KeyContext; - Packet->Context = ApcContext; - Packet->IoStatus.Status = IoStatus; - Packet->IoStatus.Information = IoStatusInformation; - - KeInsertQueue(Queue, &Packet->ListEntry); + /* Return Success */ + return STATUS_SUCCESS; +} - return STATUS_SUCCESS; +/* + * @unimplemented + */ +NTSTATUS +STDCALL +IoSetCompletionRoutineEx(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_COMPLETION_ROUTINE CompletionRoutine, + IN PVOID Context, + IN BOOLEAN InvokeOnSuccess, + IN BOOLEAN InvokeOnError, + IN BOOLEAN InvokeOnCancel) +{ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; } VOID FASTCALL IopInitIoCompletionImplementation(VOID) { - ExIoCompletionType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); - - RtlpCreateUnicodeString(&ExIoCompletionType->TypeName, L"IoCompletion", NonPagedPool); - - ExIoCompletionType->Tag = IOC_TAG; - ExIoCompletionType->PeakObjects = 0; - ExIoCompletionType->PeakHandles = 0; - ExIoCompletionType->TotalObjects = 0; - ExIoCompletionType->TotalHandles = 0; - ExIoCompletionType->PagedPoolCharge = 0; - ExIoCompletionType->NonpagedPoolCharge = sizeof(KQUEUE); - ExIoCompletionType->Mapping = &ExIoCompletionMapping; - ExIoCompletionType->Dump = NULL; - ExIoCompletionType->Open = NULL; - ExIoCompletionType->Close = NULL; - ExIoCompletionType->Delete = IopDeleteIoCompletion; - ExIoCompletionType->Parse = NULL; - ExIoCompletionType->Security = NULL; - ExIoCompletionType->QueryName = NULL; - ExIoCompletionType->OkayToClose = NULL; - ExIoCompletionType->Create = IopCreateIoCompletion; - ExIoCompletionType->DuplicationNotify = NULL; + /* Create the IO Completion Type */ + ExIoCompletionType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); + RtlInitUnicodeString(&ExIoCompletionType->TypeName, L"IoCompletion"); + ExIoCompletionType->Tag = IOC_TAG; + ExIoCompletionType->PeakObjects = 0; + ExIoCompletionType->PeakHandles = 0; + ExIoCompletionType->TotalObjects = 0; + ExIoCompletionType->TotalHandles = 0; + ExIoCompletionType->PagedPoolCharge = 0; + ExIoCompletionType->NonpagedPoolCharge = sizeof(KQUEUE); + ExIoCompletionType->Mapping = &ExIoCompletionMapping; + ExIoCompletionType->Dump = NULL; + ExIoCompletionType->Open = NULL; + ExIoCompletionType->Close = NULL; + ExIoCompletionType->Delete = IopDeleteIoCompletion; + ExIoCompletionType->Parse = NULL; + ExIoCompletionType->Security = NULL; + ExIoCompletionType->QueryName = NULL; + ExIoCompletionType->OkayToClose = NULL; + ExIoCompletionType->Create = NULL; + ExIoCompletionType->DuplicationNotify = NULL; - ExInitializeNPagedLookasideList(&IoCompletionPacketLookaside, - NULL, - NULL, - 0, - sizeof(IO_COMPLETION_PACKET), - IOC_TAG, - 0); + /* Initialize the Lookaside List we'll use for packets */ + ExInitializeNPagedLookasideList(&IoCompletionPacketLookaside, + NULL, + NULL, + 0, + sizeof(IO_COMPLETION_PACKET), + IOC_TAG, + 0); } - NTSTATUS STDCALL -NtCreateIoCompletion( - OUT PHANDLE IoCompletionHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN ULONG NumberOfConcurrentThreads - ) +NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG NumberOfConcurrentThreads) { - PKQUEUE Queue; - NTSTATUS Status; + PKQUEUE Queue; + HANDLE hIoCompletionHandle; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); - Status = ObCreateObject(ExGetPreviousMode(), - ExIoCompletionType, - ObjectAttributes, - ExGetPreviousMode(), - NULL, - sizeof(KQUEUE), - 0, - 0, - (PVOID*)&Queue); - if (!NT_SUCCESS(Status)) - { - return Status; - } + if (PreviousMode != KernelMode) { - Status = ObInsertObject ((PVOID)Queue, - NULL, - DesiredAccess, - 0, - NULL, - IoCompletionHandle); - if (!NT_SUCCESS(Status)) - { - ObDereferenceObject(Queue); - return Status; - } + _SEH_TRY { - KeInitializeQueue(Queue, NumberOfConcurrentThreads); - ObDereferenceObject(Queue); + ProbeForWrite(IoCompletionHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { - return STATUS_SUCCESS; - /* + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + if (!NT_SUCCESS(Status)) { - CompletionPort = NULL OR ExistingCompletionPort + return Status; + } + } - */ - - -} - -/* -DesiredAccess: -ZERO -IO_COMPLETION_QUERY_STATE Query access -IO_COMPLETION_MODIFY_STATE Modify access -IO_COMPLETION_ALL_ACCESS All of the preceding + STANDARD_RIGHTS_ALL - -ObjectAttributes -OBJ_OPENLINK and OBJ_PERMANENT are not valid attributes - -Return Value -STATUS_SUCCESS or an error status, such as STATUS_ACCESS_DENIED or -STATUS_OBJECT_NAME_NOT_FOUND. -*/ -NTSTATUS -STDCALL -NtOpenIoCompletion( - OUT PHANDLE IoCompletionHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes - ) -{ - NTSTATUS Status; + /* Create the Object */ + Status = ObCreateObject(PreviousMode, + ExIoCompletionType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(KQUEUE), + 0, + 0, + (PVOID*)&Queue); + + /* Check for success */ + if (NT_SUCCESS(Status)) { - Status = ObOpenObjectByName(ObjectAttributes, - ExIoCompletionType, - NULL, - UserMode, - DesiredAccess, - NULL, - IoCompletionHandle); //<- ??? + /* Initialize the Queue */ + KeInitializeQueue(Queue, NumberOfConcurrentThreads); + + /* Insert it */ + Status = ObInsertObject(Queue, + NULL, + DesiredAccess, + 0, + NULL, + &hIoCompletionHandle); + ObDereferenceObject(Queue); + + if (NT_SUCCESS(Status)) { + + _SEH_TRY { + + *IoCompletionHandle = hIoCompletionHandle; + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; + } + } + /* Return Status */ return Status; } +NTSTATUS +STDCALL +NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes) +{ + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + HANDLE hIoCompletionHandle; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + if(PreviousMode != KernelMode) { + + _SEH_TRY { + + ProbeForWrite(IoCompletionHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + if(!NT_SUCCESS(Status)) { + + return Status; + } + } + + /* Open the Object */ + Status = ObOpenObjectByName(ObjectAttributes, + ExIoCompletionType, + NULL, + PreviousMode, + DesiredAccess, + NULL, + &hIoCompletionHandle); + + if (NT_SUCCESS(Status)) { + + _SEH_TRY { + + *IoCompletionHandle = hIoCompletionHandle; + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; + } + + /* Return Status */ + return Status; +} + NTSTATUS STDCALL -NtQueryIoCompletion( - IN HANDLE IoCompletionHandle, - IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, - OUT PVOID IoCompletionInformation, - IN ULONG IoCompletionInformationLength, - OUT PULONG ResultLength OPTIONAL - ) +NtQueryIoCompletion(IN HANDLE IoCompletionHandle, + IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, + OUT PVOID IoCompletionInformation, + IN ULONG IoCompletionInformationLength, + OUT PULONG ResultLength OPTIONAL) { - PKQUEUE Queue; - NTSTATUS Status; + PKQUEUE Queue; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); - if (IoCompletionInformationClass != IoCompletionBasicInformation) - { - return STATUS_INVALID_INFO_CLASS; - } - if (IoCompletionInformationLength < sizeof(IO_COMPLETION_BASIC_INFORMATION)) - { - return STATUS_INFO_LENGTH_MISMATCH; - } + /* Check buffers and parameters */ + DefaultQueryInfoBufferCheck(IoCompletionInformationClass, + ExIoCompletionInfoClass, + IoCompletionInformation, + IoCompletionInformationLength, + ResultLength, + PreviousMode, + &Status); + if(!NT_SUCCESS(Status)) { - Status = ObReferenceObjectByHandle( IoCompletionHandle, + DPRINT1("NtQueryMutant() failed, Status: 0x%x\n", Status); + return Status; + } + + /* Get the Object */ + Status = ObReferenceObjectByHandle(IoCompletionHandle, IO_COMPLETION_QUERY_STATE, ExIoCompletionType, - UserMode, + PreviousMode, (PVOID*)&Queue, NULL); - if (NT_SUCCESS(Status)) - { - ((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->Depth = - Queue->Header.SignalState; + + /* Check for Success */ + if (NT_SUCCESS(Status)) { + + _SEH_TRY { - ObDereferenceObject(Queue); + /* Return Info */ + ((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->Depth = KeReadStateQueue(Queue); + ObDereferenceObject(Queue); - if (ResultLength) *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION); - } + /* Return Result Length if needed */ + if (ResultLength) { - return Status; + *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION); + } + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; + } + + /* Return Status */ + return Status; } - /* * Dequeues an I/O completion message from an I/O completion object */ NTSTATUS STDCALL -NtRemoveIoCompletion( - IN HANDLE IoCompletionHandle, - OUT PVOID *CompletionKey, - OUT PVOID *CompletionContext, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PLARGE_INTEGER Timeout OPTIONAL - ) +NtRemoveIoCompletion(IN HANDLE IoCompletionHandle, + OUT PVOID *CompletionKey, + OUT PVOID *CompletionContext, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER Timeout OPTIONAL) { - PKQUEUE Queue; - NTSTATUS Status; - PIO_COMPLETION_PACKET Packet; - PLIST_ENTRY ListEntry; + LARGE_INTEGER SafeTimeout; + PKQUEUE Queue; + PIO_COMPLETION_PACKET Packet; + PLIST_ENTRY ListEntry; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); - Status = ObReferenceObjectByHandle( IoCompletionHandle, + if (PreviousMode != KernelMode) { + + _SEH_TRY { + + ProbeForWrite(CompletionKey, + sizeof(PVOID), + sizeof(ULONG)); + ProbeForWrite(CompletionContext, + sizeof(PVOID), + sizeof(ULONG)); + ProbeForWrite(IoStatusBlock, + sizeof(IO_STATUS_BLOCK), + sizeof(ULONG)); + if (Timeout != NULL) { + + ProbeForRead(Timeout, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + SafeTimeout = *Timeout; + Timeout = &SafeTimeout; + } + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + if (!NT_SUCCESS(Status)) { + + return Status; + } + } + + /* Open the Object */ + Status = ObReferenceObjectByHandle(IoCompletionHandle, IO_COMPLETION_MODIFY_STATE, ExIoCompletionType, - UserMode, + PreviousMode, (PVOID*)&Queue, NULL); - if (!NT_SUCCESS(Status)) - { - return Status; - } + + /* Check for success */ + if (NT_SUCCESS(Status)) { - /* - Try 2 remove packet from queue. Wait (optionaly) if - no packet in queue or max num of threads allready running. - */ - - do { - - ListEntry = KeRemoveQueue(Queue, UserMode, Timeout ); + /* Remove queue */ + ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout); - /* Nebbets book says nothing about NtRemoveIoCompletion returning STATUS_USER_APC, - and the umode equivalent GetQueuedCompletionStatus says nothing about this either, - so my guess it we should restart the operation. Need further investigation. -Gunnar - */ + /* If we got a timeout or user_apc back, return the status */ + if ((NTSTATUS)ListEntry == STATUS_TIMEOUT || (NTSTATUS)ListEntry == STATUS_USER_APC) { + + Status = (NTSTATUS)ListEntry; + + } else { + + /* Get the Packet Data */ + Packet = CONTAINING_RECORD(ListEntry, IO_COMPLETION_PACKET, ListEntry); + + _SEH_TRY { - } while((NTSTATUS)ListEntry == STATUS_USER_APC); + /* Return it */ + *CompletionKey = Packet->Key; + *CompletionContext = Packet->Context; + *IoStatusBlock = Packet->IoStatus; + + } _SEH_HANDLE { - ObDereferenceObject(Queue); + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + /* Free packet */ + ExFreeToNPagedLookasideList(&IoCompletionPacketLookaside, Packet); + } - if ((NTSTATUS)ListEntry == STATUS_TIMEOUT) - { - return STATUS_TIMEOUT; - } - - ASSERT(ListEntry); - - Packet = CONTAINING_RECORD(ListEntry, IO_COMPLETION_PACKET, ListEntry); - - if (CompletionKey) *CompletionKey = Packet->Key; - if (CompletionContext) *CompletionContext = Packet->Context; - if (IoStatusBlock) *IoStatusBlock = Packet->IoStatus; - - ExFreeToNPagedLookasideList(&IoCompletionPacketLookaside, Packet); - - return STATUS_SUCCESS; + /* Dereference the Object */ + ObDereferenceObject(Queue); + } + + /* Return status */ + return Status; } - -/* -ASSOSIERT MED FOB's IoCompletionContext - -typedef struct _IO_COMPLETION_CONTEXT { - PVOID Port; - ULONG Key; -} IO_COMPLETION_CONTEXT, *PIO_COMPLETION_CONTEXT; - -*/ - - /* * Queues an I/O completion message to an I/O completion object */ NTSTATUS STDCALL -NtSetIoCompletion( - IN HANDLE IoCompletionPortHandle, - IN PVOID CompletionKey, - IN PVOID CompletionContext, - IN NTSTATUS CompletionStatus, - IN ULONG CompletionInformation - ) +NtSetIoCompletion(IN HANDLE IoCompletionPortHandle, + IN PVOID CompletionKey, + IN PVOID CompletionContext, + IN NTSTATUS CompletionStatus, + IN ULONG CompletionInformation) { - NTSTATUS Status; - PKQUEUE Queue; - - Status = ObReferenceObjectByHandle( IoCompletionPortHandle, + NTSTATUS Status; + PKQUEUE Queue; + + PAGED_CODE(); + + /* Get the Object */ + Status = ObReferenceObjectByHandle(IoCompletionPortHandle, IO_COMPLETION_MODIFY_STATE, ExIoCompletionType, - UserMode, + ExGetPreviousMode(), (PVOID*)&Queue, NULL); - if (NT_SUCCESS(Status)) - { - Status = IoSetIoCompletion(Queue, CompletionKey, CompletionContext, - CompletionStatus, CompletionInformation, TRUE); - ObDereferenceObject(Queue); - } - - return Status; + + /* Check for Success */ + if (NT_SUCCESS(Status)) { + + /* Set the Completion */ + Status = IoSetIoCompletion(Queue, + CompletionKey, + CompletionContext, + CompletionStatus, + CompletionInformation, + TRUE); + ObDereferenceObject(Queue); + } + + /* Return status */ + return Status; } diff --git a/reactos/ntoskrnl/io/iomgr.c b/reactos/ntoskrnl/io/iomgr.c index eff2a12bcc0..0cc528d837d 100644 --- a/reactos/ntoskrnl/io/iomgr.c +++ b/reactos/ntoskrnl/io/iomgr.c @@ -11,6 +11,7 @@ /* INCLUDES ****************************************************************/ #include +#include "../dbg/kdb.h" #define NDEBUG #include @@ -34,9 +35,9 @@ ULONGLONG IoOtherTransferCount = 0; KSPIN_LOCK EXPORTED IoStatisticsLock = 0; static GENERIC_MAPPING IopFileMapping = {FILE_GENERIC_READ, - FILE_GENERIC_WRITE, - FILE_GENERIC_EXECUTE, - FILE_ALL_ACCESS}; + FILE_GENERIC_WRITE, + FILE_GENERIC_EXECUTE, + FILE_ALL_ACCESS}; /* FUNCTIONS ****************************************************************/ @@ -51,7 +52,7 @@ IopCloseFile(PVOID ObjectBody, DPRINT("IopCloseFile()\n"); - if (HandleCount > 0 || FileObject->DeviceObject == NULL) + if (HandleCount > 1 || FileObject->DeviceObject == NULL) { return; } @@ -562,14 +563,17 @@ IoInit (VOID) } -VOID INIT_FUNCTION -IoInit2(VOID) +VOID +INIT_FUNCTION +IoInit2(BOOLEAN BootLog) { PDEVICE_NODE DeviceNode; PDRIVER_OBJECT DriverObject; MODULE_OBJECT ModuleObject; NTSTATUS Status; + IoCreateDriverList(); + KeInitializeSpinLock (&IoStatisticsLock); /* Initialize raw filesystem driver */ @@ -614,6 +618,56 @@ IoInit2(VOID) IopInvalidateDeviceRelations( IopRootDeviceNode, BusRelations); + + /* Start boot logging */ + IopInitBootLog(BootLog); + + /* Load boot start drivers */ + IopInitializeBootDrivers(); +} + +VOID +STDCALL +INIT_FUNCTION +IoInit3(VOID) +{ + NTSTATUS Status; + + /* Create ARC names for boot devices */ + IoCreateArcNames(); + + /* Create the SystemRoot symbolic link */ + CPRINT("CommandLine: %s\n", (PCHAR)KeLoaderBlock.CommandLine); + Status = IoCreateSystemRootLink((PCHAR)KeLoaderBlock.CommandLine); + if (!NT_SUCCESS(Status)) { + DbgPrint("IoCreateSystemRootLink FAILED: (0x%x) - ", Status); + DbgPrintErrorMessage (Status); + KEBUGCHECK(INACCESSIBLE_BOOT_DEVICE); + } + + /* Start Profiling on a Debug Build */ +#if defined(KDBG) + KdbInit(); +#endif /* KDBG */ + + /* I/O is now setup for disk access, so start the debugging logger thread. */ + if (KdDebugState & KD_DEBUG_FILELOG) DebugLogInit2(); + + /* Load services for devices found by PnP manager */ + IopInitializePnpServices(IopRootDeviceNode, FALSE); + + /* Load system start drivers */ + IopInitializeSystemDrivers(); + IoDestroyDriverList(); + + /* Stop boot logging */ + IopStopBootLog(); + + /* Assign drive letters */ + IoAssignDriveLetters((PLOADER_PARAMETER_BLOCK)&KeLoaderBlock, + NULL, + NULL, + NULL); } /* diff --git a/reactos/ntoskrnl/io/irp.c b/reactos/ntoskrnl/io/irp.c index ed98e86d973..7f3a7701689 100644 --- a/reactos/ntoskrnl/io/irp.c +++ b/reactos/ntoskrnl/io/irp.c @@ -410,7 +410,16 @@ IofCompleteRequest(PIRP Irp, if (Irp->UserIosb) { - *Irp->UserIosb = Irp->IoStatus; + _SEH_TRY + { + *Irp->UserIosb = Irp->IoStatus; + } + _SEH_HANDLE + { + DPRINT1("Unable to set UserIosb (at 0x%x) to 0x%x, Error: 0x%x\n", + Irp->UserIosb, Irp->IoStatus, _SEH_GetExceptionCode()); + } + _SEH_END; } if (Irp->UserEvent) diff --git a/reactos/ntoskrnl/io/irq.c b/reactos/ntoskrnl/io/irq.c index 0bb060bd0ff..6b0cd50524a 100644 --- a/reactos/ntoskrnl/io/irq.c +++ b/reactos/ntoskrnl/io/irq.c @@ -121,7 +121,10 @@ IoConnectInterrupt(PKINTERRUPT* InterruptObject, { for (i = 0; i < count; i++) { - KeDisconnectInterrupt(&Interrupt[i]); + if (ProcessorEnableMask & (1 << i)) + { + KeDisconnectInterrupt(&Interrupt[i]); + } } ExFreePool(Interrupt); return STATUS_INVALID_PARAMETER; diff --git a/reactos/ntoskrnl/io/mdl.c b/reactos/ntoskrnl/io/mdl.c index da6a4dbc7e0..b4b423f3560 100644 --- a/reactos/ntoskrnl/io/mdl.c +++ b/reactos/ntoskrnl/io/mdl.c @@ -4,7 +4,7 @@ * PROJECT: ReactOS kernel * FILE: ntoskrnl/io/mdl.c * PURPOSE: Io manager mdl functions - * + * * PROGRAMMERS: David Welch (welch@mcmail.com) */ @@ -32,7 +32,7 @@ IoAllocateMdl(PVOID VirtualAddress, PIRP Irp) { PMDL Mdl; - + if (ChargeQuota) { // Mdl = ExAllocatePoolWithQuota(NonPagedPool, @@ -48,27 +48,27 @@ IoAllocateMdl(PVOID VirtualAddress, TAG_MDL); } MmInitializeMdl(Mdl, (char*)VirtualAddress, Length); - + if (Irp) { if (SecondaryBuffer) { ASSERT(Irp->MdlAddress); - + /* FIXME: add to end of list maybe?? */ Mdl->Next = Irp->MdlAddress->Next; Irp->MdlAddress->Next = Mdl; } else { - /* + /* * What if there's allready an mdl at Irp->MdlAddress? * Is that bad and should we do something about it? */ Irp->MdlAddress = Mdl; } } - + return(Mdl); } @@ -122,13 +122,16 @@ IoBuildPartialMdl(PMDL SourceMdl, */ VOID STDCALL IoFreeMdl(PMDL Mdl) -{ - /* +{ + /* * This unmaps partial mdl's from kernel space but also asserts that non-partial * mdl's isn't still mapped into kernel space. */ + ASSERT(Mdl); + ASSERT_IRQL(DISPATCH_LEVEL); + MmPrepareMdlForReuse(Mdl); - + ExFreePool(Mdl); } diff --git a/reactos/ntoskrnl/io/pnpmgr.c b/reactos/ntoskrnl/io/pnpmgr.c index 9ee95cd5aa5..d706ba06031 100644 --- a/reactos/ntoskrnl/io/pnpmgr.c +++ b/reactos/ntoskrnl/io/pnpmgr.c @@ -508,7 +508,7 @@ IopCreateDeviceNode(PDEVICE_NODE ParentNode, DPRINT("ParentNode %x PhysicalDeviceObject %x\n", ParentNode, PhysicalDeviceObject); - Node = (PDEVICE_NODE)ExAllocatePool(PagedPool, sizeof(DEVICE_NODE)); + Node = (PDEVICE_NODE)ExAllocatePool(NonPagedPool, sizeof(DEVICE_NODE)); if (!Node) { return STATUS_INSUFFICIENT_RESOURCES; @@ -764,16 +764,15 @@ IopCreateDeviceKeyPath(PWSTR Path, } wcsncpy (KeyBuffer, Path, MAX_PATH-1); - RtlInitUnicodeString (&KeyName, KeyBuffer); /* Skip \\Registry\\ */ - Current = KeyName.Buffer; - Current = wcschr (Current, '\\') + 1; - Current = wcschr (Current, '\\') + 1; + Current = KeyBuffer; + Current = wcschr (Current, L'\\') + 1; + Current = wcschr (Current, L'\\') + 1; while (TRUE) { - Next = wcschr (Current, '\\'); + Next = wcschr (Current, L'\\'); if (Next == NULL) { /* The end */ @@ -783,6 +782,7 @@ IopCreateDeviceKeyPath(PWSTR Path, *Next = 0; } + RtlInitUnicodeString (&KeyName, KeyBuffer); InitializeObjectAttributes (&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, @@ -1740,10 +1740,13 @@ IopInvalidateDeviceRelations( &IoStatusBlock, 0, 0); - - BootDrivers = NT_SUCCESS(Status) ? FALSE : TRUE; - - ZwClose(Handle); + if(NT_SUCCESS(Status)) + { + BootDrivers = FALSE; + ZwClose(Handle); + } + else + BootDrivers = TRUE; /* * Initialize services for discovered children. Only boot drivers will diff --git a/reactos/ntoskrnl/io/pnpreport.c b/reactos/ntoskrnl/io/pnpreport.c index 95b06f00175..775e1da6997 100644 --- a/reactos/ntoskrnl/io/pnpreport.c +++ b/reactos/ntoskrnl/io/pnpreport.c @@ -10,13 +10,14 @@ /* INCLUDES ******************************************************************/ +#define NDEBUG #include #include /* FUNCTIONS *****************************************************************/ /* - * @unimplemented + * @implemented */ NTSTATUS STDCALL @@ -30,8 +31,39 @@ IoReportDetectedDevice( IN BOOLEAN ResourceAssigned, IN OUT PDEVICE_OBJECT *DeviceObject) { - DPRINT("IoReportDetectedDevice called (UNIMPLEMENTED)\n"); - return STATUS_NOT_IMPLEMENTED; + PDEVICE_NODE DeviceNode; + PDEVICE_OBJECT Pdo; + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n", + DeviceObject, DeviceObject ? *DeviceObject : NULL); + + /* if *DeviceObject is not NULL, we must use it as a PDO, + * and don't create a new one. + */ + if (DeviceObject && *DeviceObject) + Pdo = *DeviceObject; + else + { + /* create a new PDO and return it in *DeviceObject */ + Status = IopCreateDeviceNode(IopRootDeviceNode, NULL, &DeviceNode); + if (!NT_SUCCESS(Status)) + { + DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status); + return Status; + } + Pdo = DeviceNode->PhysicalDeviceObject; + if (DeviceObject) + *DeviceObject = Pdo; + } + + /* we don't need to call AddDevice and send IRP_MN_START_DEVICE */ + + /* FIXME: save this device into the root-enumerated list, so this + * device would be detected as a PnP device during next startups. + */ + + return Status; } /* @@ -48,8 +80,50 @@ IoReportResourceForDetection( IN ULONG DeviceListSize OPTIONAL, OUT PBOOLEAN ConflictDetected) { - DPRINT("IoReportResourceForDetection called (UNIMPLEMENTED)\n"); - return STATUS_NOT_IMPLEMENTED; + *ConflictDetected = FALSE; + DPRINT1("IoReportResourceForDetection unimplemented\n"); + + /* HACK: check if serial debug output is enabled. If yes, + * prevent serial port driver to detect this serial port + * by indicating a conflict + */ + if ((KdDebugState & KD_DEBUG_SERIAL) && DriverList != NULL) + { + ULONG ComPortBase = 0; + ULONG i; + PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor; + + switch (LogPortInfo.ComPort) + { + case 1: ComPortBase = 0x3f8; break; + case 2: ComPortBase = 0x2f8; break; + case 3: ComPortBase = 0x3e8; break; + case 4: ComPortBase = 0x2e8; break; + } + + /* search for this port address in DriverList */ + for (i = 0; i < DriverList->List[0].PartialResourceList.Count; i++) + { + ResourceDescriptor = &DriverList->List[0].PartialResourceList.PartialDescriptors[i]; + if (ResourceDescriptor->Type == CmResourceTypePort) + { + if (ResourceDescriptor->u.Port.Start.u.LowPart <= ComPortBase + && ResourceDescriptor->u.Port.Start.u.LowPart + ResourceDescriptor->u.Port.Length > ComPortBase) + { + *ConflictDetected = TRUE; + return STATUS_CONFLICTING_ADDRESSES; + } + } + } + } + + if (PopSystemPowerDeviceNode != NULL && DriverListSize > 0) + { + /* We hope legacy devices will be enumerated by ACPI */ + *ConflictDetected = TRUE; + return STATUS_CONFLICTING_ADDRESSES; + } + return STATUS_SUCCESS; } /* @@ -61,7 +135,7 @@ IoReportTargetDeviceChange( IN PDEVICE_OBJECT PhysicalDeviceObject, IN PVOID NotificationStructure) { - DPRINT("IoReportTargetDeviceChange called (UNIMPLEMENTED)\n"); + DPRINT1("IoReportTargetDeviceChange called (UNIMPLEMENTED)\n"); return STATUS_NOT_IMPLEMENTED; } @@ -76,7 +150,7 @@ IoReportTargetDeviceChangeAsynchronous( IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL, IN PVOID Context OPTIONAL) { - DPRINT("IoReportTargetDeviceChangeAsynchronous called (UNIMPLEMENTED)\n"); + DPRINT1("IoReportTargetDeviceChangeAsynchronous called (UNIMPLEMENTED)\n"); return STATUS_NOT_IMPLEMENTED; } diff --git a/reactos/ntoskrnl/io/rw.c b/reactos/ntoskrnl/io/rw.c index 07af4e53701..8acddcae173 100644 --- a/reactos/ntoskrnl/io/rw.c +++ b/reactos/ntoskrnl/io/rw.c @@ -44,7 +44,7 @@ NtReadFile (IN HANDLE FileHandle, { NTSTATUS Status; PFILE_OBJECT FileObject; - PIRP Irp; + PIRP Irp = NULL; PIO_STACK_LOCATION StackPtr; KPROCESSOR_MODE PreviousMode; PKEVENT EventObject = NULL; @@ -92,24 +92,46 @@ NtReadFile (IN HANDLE FileHandle, PreviousMode, (PVOID*)&EventObject, NULL); - if (!NT_SUCCESS(Status)) - { - ObDereferenceObject(FileObject); - return Status; - } + if (!NT_SUCCESS(Status)) + { + ObDereferenceObject(FileObject); + return Status; + } KeClearEvent(EventObject); } - KeClearEvent(&FileObject->Event); + _SEH_TRY + { + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, + FileObject->DeviceObject, + Buffer, + Length, + ByteOffset, + EventObject, + IoStatusBlock); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; - Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, - FileObject->DeviceObject, - Buffer, - Length, - ByteOffset, - EventObject, - IoStatusBlock); + if (!NT_SUCCESS(Status) || Irp == NULL) + { + if (Event) + { + ObDereferenceObject(&EventObject); + } + ObDereferenceObject(FileObject); + if (Irp) + { + IoFreeIrp(Irp); + } + return NT_SUCCESS(Status) ? STATUS_INSUFFICIENT_RESOURCES : Status; + } + + KeClearEvent(&FileObject->Event); /* Trigger FileObject/Event dereferencing */ Irp->Tail.Overlay.OriginalFileObject = FileObject; @@ -172,7 +194,7 @@ NtWriteFile (IN HANDLE FileHandle, OBJECT_HANDLE_INFORMATION HandleInformation; NTSTATUS Status; PFILE_OBJECT FileObject; - PIRP Irp; + PIRP Irp = NULL; PIO_STACK_LOCATION StackPtr; KPROCESSOR_MODE PreviousMode; PKEVENT EventObject = NULL; @@ -253,15 +275,37 @@ NtWriteFile (IN HANDLE FileHandle, KeClearEvent(EventObject); } - KeClearEvent(&FileObject->Event); + _SEH_TRY + { + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, + FileObject->DeviceObject, + Buffer, + Length, + ByteOffset, + EventObject, + IoStatusBlock); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; - Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, - FileObject->DeviceObject, - Buffer, - Length, - ByteOffset, - EventObject, - IoStatusBlock); + if (!NT_SUCCESS(Status) || Irp == NULL) + { + if (Event) + { + ObDereferenceObject(&EventObject); + } + ObDereferenceObject(FileObject); + if (Irp) + { + IoFreeIrp(Irp); + } + return NT_SUCCESS(Status) ? STATUS_INSUFFICIENT_RESOURCES : Status; + } + + KeClearEvent(&FileObject->Event); /* Trigger FileObject/Event dereferencing */ Irp->Tail.Overlay.OriginalFileObject = FileObject; diff --git a/reactos/ntoskrnl/io/share.c b/reactos/ntoskrnl/io/share.c index cac060f1c21..566561a067b 100644 --- a/reactos/ntoskrnl/io/share.c +++ b/reactos/ntoskrnl/io/share.c @@ -22,43 +22,20 @@ VOID STDCALL IoUpdateShareAccess(PFILE_OBJECT FileObject, PSHARE_ACCESS ShareAccess) { - if ((FileObject->ReadAccess == FALSE) && - (FileObject->WriteAccess == FALSE) && - (FileObject->DeleteAccess == FALSE)) + PAGED_CODE(); + + if (FileObject->ReadAccess || + FileObject->WriteAccess || + FileObject->DeleteAccess) { - return; - } + ShareAccess->OpenCount++; - ShareAccess->OpenCount++; - - if (FileObject->ReadAccess == TRUE) - { - ShareAccess->Readers++; - } - - if (FileObject->WriteAccess == TRUE) - { - ShareAccess->Writers++; - } - - if (FileObject->DeleteAccess == TRUE) - { - ShareAccess->Deleters++; - } - - if (FileObject->SharedRead == TRUE) - { - ShareAccess->SharedRead++; - } - - if (FileObject->SharedWrite == TRUE) - { - ShareAccess->SharedWrite++; - } - - if (FileObject->SharedDelete == TRUE) - { - ShareAccess->SharedDelete++; + ShareAccess->Readers += FileObject->ReadAccess; + ShareAccess->Writers += FileObject->WriteAccess; + ShareAccess->Deleters += FileObject->DeleteAccess; + ShareAccess->SharedRead += FileObject->SharedRead; + ShareAccess->SharedWrite += FileObject->SharedWrite; + ShareAccess->SharedDelete += FileObject->SharedDelete; } } @@ -79,85 +56,48 @@ IoCheckShareAccess(IN ACCESS_MASK DesiredAccess, BOOLEAN SharedRead; BOOLEAN SharedWrite; BOOLEAN SharedDelete; + + PAGED_CODE(); - ReadAccess = (DesiredAccess & (FILE_READ_DATA | FILE_EXECUTE)); - WriteAccess = (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)); - DeleteAccess = (DesiredAccess & DELETE); + ReadAccess = (DesiredAccess & (FILE_READ_DATA | FILE_EXECUTE)) != 0; + WriteAccess = (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0; + DeleteAccess = (DesiredAccess & DELETE) != 0; FileObject->ReadAccess = ReadAccess; FileObject->WriteAccess = WriteAccess; FileObject->DeleteAccess = DeleteAccess; - if (!ReadAccess && !WriteAccess && !DeleteAccess) + if (ReadAccess || WriteAccess || DeleteAccess) { - return(STATUS_SUCCESS); - } + SharedRead = (DesiredShareAccess & FILE_SHARE_READ) != 0; + SharedWrite = (DesiredShareAccess & FILE_SHARE_WRITE) != 0; + SharedDelete = (DesiredShareAccess & FILE_SHARE_DELETE) != 0; - SharedRead = (DesiredShareAccess & FILE_SHARE_READ); - SharedWrite = (DesiredShareAccess & FILE_SHARE_WRITE); - SharedDelete = (DesiredShareAccess & FILE_SHARE_DELETE); + FileObject->SharedRead = SharedRead; + FileObject->SharedWrite = SharedWrite; + FileObject->SharedDelete = SharedDelete; - FileObject->SharedRead = SharedRead; - FileObject->SharedWrite = SharedWrite; - FileObject->SharedDelete = SharedDelete; + if ((ReadAccess && (ShareAccess->SharedRead < ShareAccess->OpenCount)) || + (WriteAccess && (ShareAccess->SharedWrite < ShareAccess->OpenCount)) || + (DeleteAccess && (ShareAccess->SharedDelete < ShareAccess->OpenCount)) || + ((ShareAccess->Readers != 0) && !SharedRead) || + ((ShareAccess->Writers != 0) && !SharedWrite) || + ((ShareAccess->Deleters != 0) && !SharedDelete)) + { + return(STATUS_SHARING_VIOLATION); + } - if (ReadAccess) - { - if (ShareAccess->SharedRead < ShareAccess->OpenCount) - return(STATUS_SHARING_VIOLATION); - } + if (Update) + { + ShareAccess->OpenCount++; - if (WriteAccess) - { - if (ShareAccess->SharedWrite < ShareAccess->OpenCount) - return(STATUS_SHARING_VIOLATION); - } - - if (DeleteAccess) - { - if (ShareAccess->SharedDelete < ShareAccess->OpenCount) - return(STATUS_SHARING_VIOLATION); - } - - if (ShareAccess->Readers != 0) - { - if (SharedRead == FALSE) - return(STATUS_SHARING_VIOLATION); - } - - if (ShareAccess->Writers != 0) - { - if (SharedWrite == FALSE) - return(STATUS_SHARING_VIOLATION); - } - - if (ShareAccess->Deleters != 0) - { - if (SharedDelete == FALSE) - return(STATUS_SHARING_VIOLATION); - } - - if (Update == TRUE) - { - ShareAccess->OpenCount++; - - if (ReadAccess == TRUE) - ShareAccess->Readers++; - - if (WriteAccess == TRUE) - ShareAccess->Writers++; - - if (DeleteAccess == TRUE) - ShareAccess->Deleters++; - - if (SharedRead == TRUE) - ShareAccess->SharedRead++; - - if (SharedWrite == TRUE) - ShareAccess->SharedWrite++; - - if (SharedDelete == TRUE) - ShareAccess->SharedDelete++; + ShareAccess->Readers += ReadAccess; + ShareAccess->Writers += WriteAccess; + ShareAccess->Deleters += DeleteAccess; + ShareAccess->SharedRead += SharedRead; + ShareAccess->SharedWrite += SharedWrite; + ShareAccess->SharedDelete += SharedDelete; + } } return(STATUS_SUCCESS); @@ -171,43 +111,20 @@ VOID STDCALL IoRemoveShareAccess(IN PFILE_OBJECT FileObject, IN PSHARE_ACCESS ShareAccess) { - if ((FileObject->ReadAccess == FALSE) && - (FileObject->WriteAccess == FALSE) && - (FileObject->DeleteAccess == FALSE)) - { - return; - } + PAGED_CODE(); - ShareAccess->OpenCount--; - - if (FileObject->ReadAccess == TRUE) + if (FileObject->ReadAccess || + FileObject->WriteAccess || + FileObject->DeleteAccess) { - ShareAccess->Readers--; - } + ShareAccess->OpenCount--; - if (FileObject->WriteAccess == TRUE) - { - ShareAccess->Writers--; - } - - if (FileObject->DeleteAccess == TRUE) - { - ShareAccess->Deleters--; - } - - if (FileObject->SharedRead == TRUE) - { - ShareAccess->SharedRead--; - } - - if (FileObject->SharedWrite == TRUE) - { - ShareAccess->SharedWrite--; - } - - if (FileObject->SharedDelete == TRUE) - { - ShareAccess->SharedDelete--; + ShareAccess->Readers -= FileObject->ReadAccess; + ShareAccess->Writers -= FileObject->WriteAccess; + ShareAccess->Deleters -= FileObject->DeleteAccess; + ShareAccess->SharedRead -= FileObject->SharedRead; + ShareAccess->SharedWrite -= FileObject->SharedWrite; + ShareAccess->SharedDelete -= FileObject->SharedDelete; } } @@ -227,10 +144,12 @@ IoSetShareAccess(IN ACCESS_MASK DesiredAccess, BOOLEAN SharedRead; BOOLEAN SharedWrite; BOOLEAN SharedDelete; + + PAGED_CODE(); - ReadAccess = (DesiredAccess & (FILE_READ_DATA | FILE_EXECUTE)); - WriteAccess = (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)); - DeleteAccess = (DesiredAccess & DELETE); + ReadAccess = (DesiredAccess & (FILE_READ_DATA | FILE_EXECUTE)) != 0; + WriteAccess = (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0; + DeleteAccess = (DesiredAccess & DELETE) != 0; FileObject->ReadAccess = ReadAccess; FileObject->WriteAccess = WriteAccess; @@ -238,10 +157,6 @@ IoSetShareAccess(IN ACCESS_MASK DesiredAccess, if (!ReadAccess && !WriteAccess && !DeleteAccess) { - FileObject->SharedRead = FALSE; - FileObject->SharedWrite = FALSE; - FileObject->SharedDelete = FALSE; - ShareAccess->OpenCount = 0; ShareAccess->Readers = 0; ShareAccess->Writers = 0; @@ -253,22 +168,22 @@ IoSetShareAccess(IN ACCESS_MASK DesiredAccess, } else { - SharedRead = (DesiredShareAccess & FILE_SHARE_READ); - SharedWrite = (DesiredShareAccess & FILE_SHARE_WRITE); - SharedDelete = (DesiredShareAccess & FILE_SHARE_DELETE); + SharedRead = (DesiredShareAccess & FILE_SHARE_READ) != 0; + SharedWrite = (DesiredShareAccess & FILE_SHARE_WRITE) != 0; + SharedDelete = (DesiredShareAccess & FILE_SHARE_DELETE) != 0; FileObject->SharedRead = SharedRead; FileObject->SharedWrite = SharedWrite; FileObject->SharedDelete = SharedDelete; ShareAccess->OpenCount = 1; - ShareAccess->Readers = (ReadAccess) ? 1 : 0; - ShareAccess->Writers = (WriteAccess) ? 1 : 0; - ShareAccess->Deleters = (DeleteAccess) ? 1 : 0; + ShareAccess->Readers = ReadAccess; + ShareAccess->Writers = WriteAccess; + ShareAccess->Deleters = DeleteAccess; - ShareAccess->SharedRead = (SharedRead) ? 1 : 0; - ShareAccess->SharedWrite = (SharedWrite) ? 1 : 0; - ShareAccess->SharedDelete = (SharedDelete) ? 1 : 0; + ShareAccess->SharedRead = SharedRead; + ShareAccess->SharedWrite = SharedWrite; + ShareAccess->SharedDelete = SharedDelete; } } @@ -280,12 +195,15 @@ NTSTATUS STDCALL IoCheckDesiredAccess(IN OUT PACCESS_MASK DesiredAccess, IN ACCESS_MASK GrantedAccess) { + PAGED_CODE(); + RtlMapGenericMask(DesiredAccess, IoFileObjectType->Mapping); - if ((*DesiredAccess & GrantedAccess) != GrantedAccess) - return(STATUS_ACCESS_DENIED); - return(STATUS_SUCCESS); + if ((~(*DesiredAccess) & GrantedAccess) != 0) + return STATUS_ACCESS_DENIED; + else + return STATUS_SUCCESS; } @@ -343,7 +261,7 @@ IoSetInformation(IN PFILE_OBJECT FileObject, Status = ObReferenceObjectByPointer(FileObject, - FILE_WRITE_ATTRIBUTES, + 0, /* FIXME - depends on the information class */ IoFileObjectType, KernelMode); if (!NT_SUCCESS(Status)) diff --git a/reactos/ntoskrnl/io/vpb.c b/reactos/ntoskrnl/io/vpb.c index df168e91393..c6ff88de451 100644 --- a/reactos/ntoskrnl/io/vpb.c +++ b/reactos/ntoskrnl/io/vpb.c @@ -107,7 +107,7 @@ NtQueryVolumeInformationFile(IN HANDLE FileHandle, PreviousMode = ExGetPreviousMode(); Status = ObReferenceObjectByHandle(FileHandle, - FILE_READ_ATTRIBUTES, + 0, /* FIXME - depends on the information class! */ IoFileObjectType, PreviousMode, (PVOID*)&FileObject, diff --git a/reactos/ntoskrnl/io/wmi.c b/reactos/ntoskrnl/io/wmi.c index 8b9088270f5..9d01c6b1f55 100644 --- a/reactos/ntoskrnl/io/wmi.c +++ b/reactos/ntoskrnl/io/wmi.c @@ -249,4 +249,18 @@ IoWMIDeviceObjectToInstanceName( return STATUS_NOT_IMPLEMENTED; } +/* + * @unimplemented + */ +NTSTATUS +STDCALL +NtTraceEvent(IN ULONG TraceHandle, + IN ULONG Flags, + IN ULONG TraceHeaderLength, + IN struct _EVENT_TRACE_HEADER* TraceHeader) +{ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + /*Eof*/ diff --git a/reactos/ntoskrnl/kd/dlog.c b/reactos/ntoskrnl/kd/dlog.c index 2e86dcfee0e..3b8e48afedf 100644 --- a/reactos/ntoskrnl/kd/dlog.c +++ b/reactos/ntoskrnl/kd/dlog.c @@ -36,11 +36,11 @@ DebugLogDumpMessages(VOID) ULONG Offset; ULONG Length; - if (!(KdDebugState & KD_DEBUG_BOOTLOG)) + if (!(KdDebugState & KD_DEBUG_FILELOG)) { return; } - KdDebugState &= ~KD_DEBUG_BOOTLOG; + KdDebugState &= ~KD_DEBUG_FILELOG; Offset = (DebugLogEnd + 1) % DEBUGLOG_SIZE; do diff --git a/reactos/ntoskrnl/kd/gdbstub.c b/reactos/ntoskrnl/kd/gdbstub.c index 9085cc7c4b1..498ddb03636 100644 --- a/reactos/ntoskrnl/kd/gdbstub.c +++ b/reactos/ntoskrnl/kd/gdbstub.c @@ -327,7 +327,9 @@ GspMem2Hex (PCHAR Address, ch = *Address; GspAccessLocation = NULL; if (MayFault && GspMemoryError) - return (Buffer); + { + return (Buffer); + } *Buffer++ = HexChars[(ch >> 4) & 0xf]; *Buffer++ = HexChars[ch & 0xf]; Address++; @@ -377,7 +379,7 @@ GspHex2Mem (PCHAR Buffer, ch = (CHAR)(HexValue (*Buffer++) << 4); ch = (CHAR)(ch + HexValue (*Buffer++)); - GspAccessLocation = Address; + GspAccessLocation = current; *current = ch; GspAccessLocation = NULL; current++; @@ -386,7 +388,9 @@ GspHex2Mem (PCHAR Buffer, { MmSetPageProtect (NULL, page, oldprot); if (GspMemoryError) - return (Buffer); + { + return (Buffer); + } } } @@ -1053,7 +1057,6 @@ KdEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord, LONG NewPC; PCHAR ptr; LONG Esp; - KIRQL OldIrql; /* FIXME: Stop on other CPUs too */ /* Disable hardware debugging while we are inside the stub */ @@ -1072,16 +1075,11 @@ KdEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord, { GspAccessLocation = NULL; GspMemoryError = TRUE; - Context->Eip += 2; + TrapFrame->Eip += 2; } else { /* Don't switch threads */ - OldIrql = KeGetCurrentIrql(); - if (OldIrql < DISPATCH_LEVEL) - { - KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); - } /* Always use the current thread when entering the exception handler */ if (NULL != GspDbgThread) @@ -1284,10 +1282,6 @@ KdEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord, #else #error Unknown compiler for inline assembler #endif - if (OldIrql < DISPATCH_LEVEL) - { - KeLowerIrql(OldIrql); - } KeContextToTrapFrame(Context, TrapFrame); return ((SigVal == 5) ? (kdContinue) : (kdHandleException)); @@ -1425,17 +1419,17 @@ KdGdbStubInit(ULONG Phase) if (Phase == 0) { - DbgPrint("Module 'hal.dll' loaded at 0x%.08x.\n", LdrHalBase); - - GspInitialized = TRUE; - GspRunThread = PsGetCurrentThread(); + GspInitialized = TRUE; + GspRunThread = PsGetCurrentThread(); - ObReferenceObject(GspRunThread); + ObReferenceObject(GspRunThread); -/* GspDbgThread = PsGetCurrentThread(); */ - GspDbgThread = NULL; - GspEnumThread = NULL; +/* GspDbgThread = PsGetCurrentThread(); */ + GspDbgThread = NULL; + GspEnumThread = NULL; + HalDisplayString("Waiting for GDB to attach\n"); + DbgPrint("Module 'hal.dll' loaded at 0x%.08x.\n", LdrHalBase); DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C); } else if (Phase == 1) @@ -1493,8 +1487,6 @@ KdGdbDebugPrint(LPSTR Message) GspOutBuffer[3 + Length] = '\0'; GspPutPacketNoWait (&GspOutBuffer[0]); } -#else - HalDisplayString(Message); #endif } diff --git a/reactos/ntoskrnl/kd/kdebug.c b/reactos/ntoskrnl/kd/kdebug.c index 68004e5d230..6e823db8837 100644 --- a/reactos/ntoskrnl/kd/kdebug.c +++ b/reactos/ntoskrnl/kd/kdebug.c @@ -118,13 +118,11 @@ KdInitSystem(ULONG BootPhase, if (!_strnicmp(p2, "SCREEN", 6) && BootPhase > 0) { p2 += 6; - KdDebuggerEnabled = TRUE; KdDebugState |= KD_DEBUG_SCREEN; } else if (!_strnicmp(p2, "BOCHS", 5) && BootPhase == 0) { p2 += 5; - KdDebuggerEnabled = TRUE; KdDebugState |= KD_DEBUG_BOCHS; } else if (!_strnicmp(p2, "GDB", 3) && BootPhase == 0) @@ -150,21 +148,18 @@ KdInitSystem(ULONG BootPhase, Value = (ULONG)atol(p2); if (Value > 0 && Value < 5) { - KdDebuggerEnabled = TRUE; - KdDebugState |= KD_DEBUG_SERIAL; + KdDebugState |= KD_DEBUG_SERIAL; LogPortInfo.ComPort = Value; } } - else if (!_strnicmp(p2, "BOOTLOG", 4) && BootPhase > 0) + else if (!_strnicmp(p2, "FILE", 4) && BootPhase > 0) { p2 += 4; - KdDebuggerEnabled = TRUE; - KdDebugState |= KD_DEBUG_BOOTLOG; + KdDebugState |= KD_DEBUG_FILELOG; } else if (!_strnicmp(p2, "MDA", 3) && BootPhase > 0) { p2 += 3; - KdDebuggerEnabled = TRUE; KdDebugState |= KD_DEBUG_MDA; } } @@ -172,13 +167,11 @@ KdInitSystem(ULONG BootPhase, else if (!_strnicmp(p2, "KDSERIAL", 8) && BootPhase > 0) { p2 += 8; - KdDebuggerEnabled = TRUE; KdDebugState |= KD_DEBUG_SERIAL | KD_DEBUG_KDSERIAL; } else if (!_strnicmp(p2, "KDNOECHO", 8) && BootPhase > 0) { p2 += 8; - KdDebuggerEnabled = TRUE; KdDebugState |= KD_DEBUG_KDNOECHO; } else if (!_strnicmp(p2, "DEBUG", 5) && BootPhase == 0) @@ -243,30 +236,21 @@ KdInitSystem(ULONG BootPhase, } } } -#if defined(KDBG) || defined(DBG) - else if (!_strnicmp(p2, "PROFILE", 7) && BootPhase > 0) - { - KdbInitProfiling(); - } -#endif /* KDBG */ p1 = p2; } /* Perform any initialization nescessary */ - if (KdDebuggerEnabled == TRUE) - { if (KdDebugState & KD_DEBUG_GDB && BootPhase == 0) KdPortInitializeEx(&GdbPortInfo, 0, 0); if (KdDebugState & KD_DEBUG_SERIAL && BootPhase == 0) KdPortInitializeEx(&LogPortInfo, 0, 0); - if (KdDebugState & KD_DEBUG_BOOTLOG && BootPhase > 0) + if (KdDebugState & KD_DEBUG_FILELOG && BootPhase > 0) DebugLogInit(); if (KdDebugState & KD_DEBUG_MDA && BootPhase > 0) KdInitializeMda(); - } } @@ -274,7 +258,7 @@ VOID INIT_FUNCTION KdInit1(VOID) { /* Initialize kernel debugger (phase 0) */ - if ((KdDebuggerEnabled == TRUE) && + if ((KdDebuggerEnabled) && (KdDebugState & KD_DEBUG_GDB)) { KdGdbStubInit(0); @@ -286,7 +270,7 @@ VOID INIT_FUNCTION KdInit2(VOID) { /* Initialize kernel debugger (phase 1) */ - if ((KdDebuggerEnabled == TRUE) && + if ((KdDebuggerEnabled) && (KdDebugState & KD_DEBUG_GDB)) { KdGdbStubInit(1); @@ -298,8 +282,6 @@ VOID INIT_FUNCTION KdInit3(VOID) { /* Print some information */ - if (KdDebuggerEnabled == TRUE) - { if (KdDebugState & KD_DEBUG_GDB) PrintString("\n GDB debugging enabled. COM%ld %ld Baud\n\n", GdbPortInfo.ComPort, GdbPortInfo.BaudRate); @@ -317,11 +299,10 @@ KdInit3(VOID) PrintString("\n Serial debugging enabled. COM%ld %ld Baud\n\n", LogPortInfo.ComPort, LogPortInfo.BaudRate); - if (KdDebugState & KD_DEBUG_BOOTLOG) + if (KdDebugState & KD_DEBUG_FILELOG) PrintString("\n File log debugging enabled\n\n"); if (KdDebugState & KD_DEBUG_MDA) PrintString("\n MDA debugging enabled\n\n"); - } } @@ -374,7 +355,7 @@ KdpPrintString(PANSI_STRING String) if (KdDebugState & KD_DEBUG_BOCHS) KdBochsDebugPrint(pch); - if (KdDebugState & KD_DEBUG_BOOTLOG) + if (KdDebugState & KD_DEBUG_FILELOG) DebugLogWrite(pch); if (KdDebugState & KD_DEBUG_MDA) @@ -455,7 +436,7 @@ KdSystemDebugControl(ULONG Code) /* B - Bug check the system. */ else if (Code == 1) { - KEBUGCHECK(0xDEADDEAD); + KEBUGCHECK(MANUALLY_INITIATED_CRASH); } /* * C - Dump statistics about the distribution of tagged blocks in diff --git a/reactos/ntoskrnl/ke/alert.c b/reactos/ntoskrnl/ke/alert.c deleted file mode 100644 index 99084a862e4..00000000000 --- a/reactos/ntoskrnl/ke/alert.c +++ /dev/null @@ -1,150 +0,0 @@ -/* $Id:$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ke/alert.c - * PURPOSE: Alerts - * - * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - */ - -/* INCLUDES *****************************************************************/ - -#include -#define NDEBUG -#include - -/* GLOBALS *******************************************************************/ - - -/* FUNCTIONS *****************************************************************/ - - -BOOLEAN -STDCALL -KeTestAlertThread(IN KPROCESSOR_MODE AlertMode) -/* - * FUNCTION: Tests whether there are any pending APCs for the current thread - * and if so the APCs will be delivered on exit from kernel mode - */ -{ - KIRQL OldIrql; - PKTHREAD Thread = KeGetCurrentThread(); - BOOLEAN OldState; - - ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); - - OldIrql = KeAcquireDispatcherDatabaseLock(); - KiAcquireSpinLock(&Thread->ApcQueueLock); - - OldState = Thread->Alerted[AlertMode]; - - /* If the Thread is Alerted, Clear it */ - if (OldState) { - Thread->Alerted[AlertMode] = FALSE; - } else if ((AlertMode == UserMode) && (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) { - /* If the mode is User and the Queue isn't empty, set Pending */ - Thread->ApcState.UserApcPending = TRUE; - } - - KiReleaseSpinLock(&Thread->ApcQueueLock); - KeReleaseDispatcherDatabaseLock(OldIrql); - return OldState; -} - - -VOID -KeAlertThread(PKTHREAD Thread, KPROCESSOR_MODE AlertMode) -{ - KIRQL oldIrql; - - - oldIrql = KeAcquireDispatcherDatabaseLock(); - - - /* Return if thread is already alerted. */ - if (Thread->Alerted[AlertMode] == FALSE) - { - if (Thread->State == THREAD_STATE_BLOCKED && - (AlertMode == KernelMode || Thread->WaitMode == AlertMode) && - Thread->Alertable) - { - KiAbortWaitThread(Thread, STATUS_ALERTED); - } - else - { - Thread->Alerted[AlertMode] = TRUE; - } - } - - KeReleaseDispatcherDatabaseLock(oldIrql); - -} - - -/* - * - * NOT EXPORTED - */ -NTSTATUS STDCALL -NtAlertResumeThread(IN HANDLE ThreadHandle, - OUT PULONG SuspendCount) -{ - UNIMPLEMENTED; - return(STATUS_NOT_IMPLEMENTED); - -} - -/* - * @implemented - * - * EXPORTED - */ -NTSTATUS STDCALL -NtAlertThread (IN HANDLE ThreadHandle) -{ - KPROCESSOR_MODE PreviousMode; - PETHREAD Thread; - NTSTATUS Status; - - PreviousMode = ExGetPreviousMode(); - - Status = ObReferenceObjectByHandle(ThreadHandle, - THREAD_SUSPEND_RESUME, - PsThreadType, - PreviousMode, - (PVOID*)&Thread, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - - /* do an alert depending on the processor mode. If some kmode code wants to - enforce a umode alert it should call KeAlertThread() directly. If kmode - code wants to do a kmode alert it's sufficient to call it with Zw or just - use KeAlertThread() directly */ - - KeAlertThread(&Thread->Tcb, PreviousMode); - - ObDereferenceObject(Thread); - return(STATUS_SUCCESS); -} - - -/* - * NOT EXPORTED - */ -NTSTATUS -STDCALL -NtTestAlert(VOID) -{ - KPROCESSOR_MODE PreviousMode; - - PreviousMode = ExGetPreviousMode(); - - /* Check and Alert Thread if needed */ - - return KeTestAlertThread(PreviousMode) ? STATUS_ALERTED : STATUS_SUCCESS; -} - diff --git a/reactos/ntoskrnl/ke/apc.c b/reactos/ntoskrnl/ke/apc.c index 5b47908713f..b2f06266117 100644 --- a/reactos/ntoskrnl/ke/apc.c +++ b/reactos/ntoskrnl/ke/apc.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/apc.c @@ -15,585 +14,856 @@ #define NDEBUG #include -/* GLOBALS *******************************************************************/ - -VOID PsTerminateCurrentThread(NTSTATUS ExitStatus); - -#define TAG_KAPC TAG('K', 'A', 'P', 'C') - /* FUNCTIONS *****************************************************************/ -/* - * @implemented - */ +/*++ + * KeEnterCriticalRegion + * @implemented NT4 + * + * The KeEnterCriticalRegion routine temporarily disables the delivery of + * normal kernel APCs; special kernel-mode APCs are still delivered. + * + * Params: + * None. + * + * Returns: + * None. + * + * Remarks: + * Highest-level drivers can call this routine while running in the context + * of the thread that requested the current I/O operation. Any caller of + * this routine should call KeLeaveCriticalRegion as quickly as possible. + * + * Callers of KeEnterCriticalRegion must be running at IRQL <= APC_LEVEL. + * + *--*/ +VOID +STDCALL +KeEnterCriticalRegion(VOID) +{ + /* Disable Kernel APCs */ + PKTHREAD Thread = KeGetCurrentThread(); + if (Thread) Thread->KernelApcDisable--; +} + +/*++ + * KeInitializeApc + * @implemented NT4 + * + * The The KeInitializeApc routine initializes an APC object, and registers + * the Kernel, Rundown and Normal routines for that object. + * + * Params: + * Apc - Pointer to a KAPC structure that represents the APC object to + * initialize. The caller must allocate storage for the structure + * from resident memory. + * + * Thread - Thread to which to deliver the APC. + * + * TargetEnvironment - APC Environment to be used. + * + * KernelRoutine - Points to the KernelRoutine to associate with the APC. + * This routine is executed for all APCs. + * + * RundownRoutine - Points to the RundownRoutine to associate with the APC. + * This routine is executed when the Thread exists with + * the APC executing. + * + * NormalRoutine - Points to the NormalRoutine to associate with the APC. + * This routine is executed at PASSIVE_LEVEL. If this is + * not specifed, the APC becomes a Special APC and the + * Mode and Context parameters are ignored. + * + * Mode - Specifies the processor mode at which to run the Normal Routine. + * + * Context - Specifices the value to pass as Context parameter to the + * registered routines. + * + * Returns: + * None. + * + * Remarks: + * The caller can queue an initialized APC with KeInsertQueueApc. + * + * Storage for the APC object must be resident, such as nonpaged pool + * allocated by the caller. + * + * Callers of this routine must be running at IRQL = PASSIVE_LEVEL. + * + *--*/ VOID STDCALL -KeInitializeApc( - IN PKAPC Apc, - IN PKTHREAD Thread, - IN KAPC_ENVIRONMENT TargetEnvironment, - IN PKKERNEL_ROUTINE KernelRoutine, - IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, - IN PKNORMAL_ROUTINE NormalRoutine, - IN KPROCESSOR_MODE Mode, - IN PVOID Context) -/* - * FUNCTION: Initialize an APC object - * ARGUMENTS: - * Apc = Pointer to the APC object to initialized - * Thread = Thread the APC is to be delivered to - * TargetEnvironment = APC environment to use - * KernelRoutine = Routine to be called for a kernel-mode APC - * RundownRoutine = Routine to be called if the thread has exited with - * the APC being executed - * NormalRoutine = Routine to be called for a user-mode APC - * Mode = APC mode - * Context = Parameter to be passed to the APC routine - */ +KeInitializeApc(IN PKAPC Apc, + IN PKTHREAD Thread, + IN KAPC_ENVIRONMENT TargetEnvironment, + IN PKKERNEL_ROUTINE KernelRoutine, + IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, + IN PKNORMAL_ROUTINE NormalRoutine, + IN KPROCESSOR_MODE Mode, + IN PVOID Context) { - DPRINT ("KeInitializeApc(Apc %x, Thread %x, Environment %d, " - "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, " - "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine, - NormalRoutine,Mode,Context); + DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, " + "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, " + "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine, + NormalRoutine,Mode,Context); - /* Set up the basic APC Structure Data */ - RtlZeroMemory(Apc, sizeof(KAPC)); - Apc->Type = KApc; - Apc->Size = sizeof(KAPC); - - /* Set the Environment */ - if (TargetEnvironment == CurrentApcEnvironment) { - Apc->ApcStateIndex = Thread->ApcStateIndex; - } else { - Apc->ApcStateIndex = TargetEnvironment; - } - - /* Set the Thread and Routines */ - Apc->Thread = Thread; - Apc->KernelRoutine = KernelRoutine; - Apc->RundownRoutine = RundownRoutine; - Apc->NormalRoutine = NormalRoutine; + /* Set up the basic APC Structure Data */ + RtlZeroMemory(Apc, sizeof(KAPC)); + Apc->Type = ApcObject; + Apc->Size = sizeof(KAPC); + + /* Set the Environment */ + if (TargetEnvironment == CurrentApcEnvironment) { + + Apc->ApcStateIndex = Thread->ApcStateIndex; + + } else { + + Apc->ApcStateIndex = TargetEnvironment; + } + + /* Set the Thread and Routines */ + Apc->Thread = Thread; + Apc->KernelRoutine = KernelRoutine; + Apc->RundownRoutine = RundownRoutine; + Apc->NormalRoutine = NormalRoutine; - /* Check if this is a Special APC, in which case we use KernelMode and no Context */ - if (ARGUMENT_PRESENT(NormalRoutine)) { - Apc->ApcMode = Mode; - Apc->NormalContext = Context; - } else { - Apc->ApcMode = KernelMode; - } + /* Check if this is a Special APC, in which case we use KernelMode and no Context */ + if (ARGUMENT_PRESENT(NormalRoutine)) { + + Apc->ApcMode = Mode; + Apc->NormalContext = Context; + + } else { + + Apc->ApcMode = KernelMode; + } } -/* - * @implemented - */ +/*++ + * KiInsertQueueApc + * + * The KiInsertQueueApc routine queues a APC for execution when the right + * scheduler environment exists. + * + * Params: + * Apc - Pointer to an initialized control object of type DPC for which the + * caller provides the storage. + * + * PriorityBoost - Priority Boost to apply to the Thread. + * + * Returns: + * If the APC is already inserted or APC queueing is disabled, FALSE. + * Otherwise, TRUE. + * + * Remarks: + * The APC will execute at APC_LEVEL for the KernelRoutine registered, and + * at PASSIVE_LEVEL for the NormalRoutine registered. + * + * Callers of this routine must be running at IRQL = PASSIVE_LEVEL. + * + *--*/ BOOLEAN STDCALL -KeInsertQueueApc (PKAPC Apc, - PVOID SystemArgument1, - PVOID SystemArgument2, - KPRIORITY PriorityBoost) -/* - * FUNCTION: Queues an APC for execution - * ARGUMENTS: - * Apc = APC to be queued - * SystemArgument[1-2] = Arguments we ignore and simply pass on. - * PriorityBoost = Priority Boost to give to the Thread - */ +KiInsertQueueApc(PKAPC Apc, + KPRIORITY PriorityBoost) { - KIRQL OldIrql; - PKTHREAD Thread; - PLIST_ENTRY ApcListEntry; - PKAPC QueuedApc; - - ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); - DPRINT ("KeInsertQueueApc(Apc %x, SystemArgument1 %x, " - "SystemArgument2 %x)\n",Apc,SystemArgument1, - SystemArgument2); - - OldIrql = KeAcquireDispatcherDatabaseLock(); - - /* Get the Thread specified in the APC */ - Thread = Apc->Thread; - - /* Make sure the thread allows APC Queues. - * The thread is not apc queueable, for instance, when it's (about to be) terminated. + PKTHREAD Thread = Apc->Thread; + PLIST_ENTRY ApcListEntry; + PKAPC QueuedApc; + + /* Don't do anything if the APC is already inserted */ + if (Apc->Inserted) { + + return FALSE; + } + + /* Three scenarios: + 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List + 2) User APC which is PsExitSpecialApc = Put it at the front of the List + 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list */ - if (Thread->ApcQueueable == FALSE) { - DPRINT("Thread doesn't allow APC Queues\n"); - KeReleaseDispatcherDatabaseLock(OldIrql); - return FALSE; - } - - /* Set the System Arguments */ - Apc->SystemArgument1 = SystemArgument1; - Apc->SystemArgument2 = SystemArgument2; + if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) { + + DPRINT ("Inserting the Process Exit APC into the Queue\n"); + Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = TRUE; + InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode], + &Apc->ApcListEntry); + + } else if (Apc->NormalRoutine == NULL) { + + DPRINT ("Inserting Special APC %x into the Queue\n", Apc); + + for (ApcListEntry = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode].Flink; + ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode]; + ApcListEntry = ApcListEntry->Flink) { - /* Don't do anything if the APC is already inserted */ - if (Apc->Inserted) { - KeReleaseDispatcherDatabaseLock(OldIrql); - return FALSE; - } - - /* Three scenarios: - 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List - 2) User APC which is PsExitSpecialApc = Put it at the front of the List - 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list - */ - if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) { - DPRINT ("Inserting the Process Exit APC into the Queue\n"); - Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = TRUE; - InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode], - &Apc->ApcListEntry); - } else if (Apc->NormalRoutine == NULL) { - DPRINT ("Inserting Special APC %x into the Queue\n", Apc); - for (ApcListEntry = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode].Flink; - ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode]; - ApcListEntry = ApcListEntry->Flink) { + QueuedApc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); + if (Apc->NormalRoutine != NULL) break; + } + + /* We found the first "Normal" APC, so write right before it */ + ApcListEntry = ApcListEntry->Blink; + InsertHeadList(ApcListEntry, &Apc->ApcListEntry); + + } else { + + DPRINT ("Inserting Normal APC %x into the %x Queue\n", Apc, Apc->ApcMode); + InsertTailList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode], + &Apc->ApcListEntry); + } + + /* Confirm Insertion */ + Apc->Inserted = TRUE; - QueuedApc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); - if (Apc->NormalRoutine != NULL) break; - } - - /* We found the first "Normal" APC, so write right before it */ - ApcListEntry = ApcListEntry->Blink; - InsertHeadList(ApcListEntry, &Apc->ApcListEntry); - } else { - DPRINT ("Inserting Normal APC %x into the %x Queue\n", Apc, Apc->ApcMode); - InsertTailList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode], - &Apc->ApcListEntry); - } - - /* Confirm Insertion */ - Apc->Inserted = TRUE; - - /* Three possibilites here again: - 1) Kernel APC, The thread is Running: Request an Interrupt - 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread - 3) User APC, Unwait the Thread if it is alertable - */ - if (Apc->ApcMode == KernelMode) { - Thread->ApcState.KernelApcPending = TRUE; - if (Thread->State == THREAD_STATE_RUNNING) { - /* FIXME: Use IPI */ - DPRINT ("Requesting APC Interrupt for Running Thread \n"); - HalRequestSoftwareInterrupt(APC_LEVEL); - } else if ((Thread->State == THREAD_STATE_BLOCKED) && - (Thread->WaitIrql < APC_LEVEL) && - (Apc->NormalRoutine == NULL)) - { - DPRINT ("Waking up Thread for Kernel-Mode APC Delivery \n"); - KiAbortWaitThread(Thread, STATUS_KERNEL_APC); - } + /* + * Three possibilites here again: + * 1) Kernel APC, The thread is Running: Request an Interrupt + * 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread + * 3) User APC, Unwait the Thread if it is alertable + */ + if (Apc->ApcMode == KernelMode) { + + /* Set Kernel APC pending */ + Thread->ApcState.KernelApcPending = TRUE; + + /* Check the Thread State */ + if (Thread->State == THREAD_STATE_RUNNING) { + + /* FIXME: Use IPI */ + DPRINT ("Requesting APC Interrupt for Running Thread \n"); + HalRequestSoftwareInterrupt(APC_LEVEL); + + } else if ((Thread->State == THREAD_STATE_BLOCKED) && (Thread->WaitIrql == PASSIVE_LEVEL) && + ((Apc->NormalRoutine == NULL) || + ((!Thread->KernelApcDisable) && (!Thread->ApcState.KernelApcInProgress)))) { + + DPRINT("Waking up Thread for Kernel-Mode APC Delivery \n"); + KiAbortWaitThread(Thread, STATUS_KERNEL_APC, PriorityBoost); + } + } else if ((Thread->State == THREAD_STATE_BLOCKED) && (Thread->WaitMode == UserMode) && - (Thread->Alertable)) - { - DPRINT ("Waking up Thread for User-Mode APC Delivery \n"); - Thread->ApcState.UserApcPending = TRUE; - KiAbortWaitThread(Thread, STATUS_USER_APC); - } - - /* Return Sucess if we are here */ - KeReleaseDispatcherDatabaseLock(OldIrql); - return TRUE; + (Thread->Alertable)) { + + DPRINT("Waking up Thread for User-Mode APC Delivery \n"); + Thread->ApcState.UserApcPending = TRUE; + KiAbortWaitThread(Thread, STATUS_USER_APC, PriorityBoost); + } + + return TRUE; } + +/*++ + * KeInsertQueueApc + * @implemented NT4 + * + * The KeInsertQueueApc routine queues a APC for execution when the right + * scheduler environment exists. + * + * Params: + * Apc - Pointer to an initialized control object of type DPC for which the + * caller provides the storage. + * + * SystemArgument[1,2] - Pointer to a set of two parameters that contain + * untyped data. + * + * PriorityBoost - Priority Boost to apply to the Thread. + * + * Returns: + * If the APC is already inserted or APC queueing is disabled, FALSE. + * Otherwise, TRUE. + * + * Remarks: + * The APC will execute at APC_LEVEL for the KernelRoutine registered, and + * at PASSIVE_LEVEL for the NormalRoutine registered. + * + * Callers of this routine must be running at IRQL = PASSIVE_LEVEL. + * + *--*/ +BOOLEAN +STDCALL +KeInsertQueueApc(PKAPC Apc, + PVOID SystemArgument1, + PVOID SystemArgument2, + KPRIORITY PriorityBoost) -BOOLEAN STDCALL -KeRemoveQueueApc (PKAPC Apc) -/* - * FUNCTION: Removes APC object from the apc queue - * ARGUMENTS: - * Apc = APC to remove - * RETURNS: TRUE if the APC was in the queue - * FALSE otherwise - * NOTE: This function is not exported. - */ { - KIRQL OldIrql; - PKTHREAD Thread = Apc->Thread; + KIRQL OldIrql; + PKTHREAD Thread; + BOOLEAN Inserted; + + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, " + "SystemArgument2 %x)\n",Apc,SystemArgument1, + SystemArgument2); - ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); - DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc); - - OldIrql = KeAcquireDispatcherDatabaseLock(); - KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); - - /* Remove it from the Queue if it's inserted */ - if (!Apc->Inserted == FALSE) { - RemoveEntryList(&Apc->ApcListEntry); - Apc->Inserted = FALSE; - - /* If the Queue is completely empty, then no more APCs are pending */ - if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) { - if (Apc->ApcMode == KernelMode) { - Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE; - } else { - Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE; - } - } - } else { - KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); - KeReleaseDispatcherDatabaseLock(OldIrql); - return(FALSE); - } - - /* Restore IRQL and Return */ - KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); - KeReleaseDispatcherDatabaseLock(OldIrql); - return(TRUE); + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Get the Thread specified in the APC */ + Thread = Apc->Thread; + + /* Make sure the thread allows APC Queues. + * The thread is not apc queueable, for instance, when it's (about to be) terminated. + */ + if (Thread->ApcQueueable == FALSE) { + DPRINT("Thread doesn't allow APC Queues\n"); + KeReleaseDispatcherDatabaseLock(OldIrql); + return FALSE; + } + + /* Set the System Arguments */ + Apc->SystemArgument1 = SystemArgument1; + Apc->SystemArgument2 = SystemArgument2; + + /* Call the Internal Function */ + Inserted = KiInsertQueueApc(Apc, PriorityBoost); + + /* Return Sucess if we are here */ + KeReleaseDispatcherDatabaseLock(OldIrql); + return Inserted; } +/*++ + * KeLeaveCriticalRegion + * @implemented NT4 + * + * The KeLeaveCriticalRegion routine reenables the delivery of normal + * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion. + * + * Params: + * None. + * + * Returns: + * None. + * + * Remarks: + * Highest-level drivers can call this routine while running in the context + * of the thread that requested the current I/O operation. + * + * Callers of KeLeaveCriticalRegion must be running at IRQL <= DISPATCH_LEVEL. + * + *--*/ +VOID +STDCALL +KeLeaveCriticalRegion (VOID) +{ + PKTHREAD Thread = KeGetCurrentThread(); -/* - * @implemented - */ + /* Check if Kernel APCs are now enabled */ + if((Thread) && (++Thread->KernelApcDisable == 0)) { + + /* Check if we need to request an APC Delivery */ + if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) { + + /* Set APC Pending */ + Thread->ApcState.KernelApcPending = TRUE; + HalRequestSoftwareInterrupt(APC_LEVEL); + } + } +} + +/*++ + * KeRemoveQueueApc + * + * The KeRemoveQueueApc routine removes a given APC object from the system + * APC queue. + * + * Params: + * APC - Pointer to an initialized APC object that was queued by calling + * KeInsertQueueApc. + * + * Returns: + * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is + * performed and FALSE is returned. + * + * Remarks: + * If the given APC Object is currently queued, it is removed from the queue + * and any calls to the registered routines are cancelled. + * + * Callers of KeLeaveCriticalRegion can be running at any IRQL. + * + *--*/ +BOOLEAN +STDCALL +KeRemoveQueueApc(PKAPC Apc) +{ + KIRQL OldIrql; + PKTHREAD Thread = Apc->Thread; + + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc); + + OldIrql = KeAcquireDispatcherDatabaseLock(); + KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); + + /* Check if it's inserted */ + if (Apc->Inserted) { + + /* Remove it from the Queue*/ + RemoveEntryList(&Apc->ApcListEntry); + Apc->Inserted = FALSE; + + /* If the Queue is completely empty, then no more APCs are pending */ + if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) { + + /* Set the correct State based on the Apc Mode */ + if (Apc->ApcMode == KernelMode) { + + Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE; + + } else { + + Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE; + } + } + + } else { + + /* It's not inserted, fail */ + KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); + KeReleaseDispatcherDatabaseLock(OldIrql); + return(FALSE); + } + + /* Restore IRQL and Return */ + KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); + KeReleaseDispatcherDatabaseLock(OldIrql); + return(TRUE); +} + +/*++ + * KiDeliverApc + * @implemented @NT4 + * + * The KiDeliverApc routine is called from IRQL switching code if the + * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are + * pending. + * + * Params: + * DeliveryMode - Specifies the current processor mode. + * + * Reserved - Pointer to the Exception Frame on non-i386 builds. + * + * TrapFrame - Pointer to the Trap Frame. + * + * Returns: + * None. + * + * Remarks: + * First, Special APCs are delivered, followed by Kernel-Mode APCs and + * User-Mode APCs. Note that the TrapFrame is only valid if the previous + * mode is User. + * + * Upon entry, this routine executes at APC_LEVEL. + * + *--*/ VOID STDCALL KiDeliverApc(KPROCESSOR_MODE DeliveryMode, PVOID Reserved, PKTRAP_FRAME TrapFrame) -/* - * FUNCTION: Deliver an APC to the current thread. - * NOTES: This is called from the IRQL switching code if the current thread - * is returning from an IRQL greater than or equal to APC_LEVEL to - * PASSIVE_LEVEL and there are kernel-mode APCs pending. This means any - * pending APCs will be delivered after a thread gets a new quantum and - * after it wakes from a wait. Note that the TrapFrame is only valid if - * the previous mode is User. - */ { - PKTHREAD Thread = KeGetCurrentThread(); - PLIST_ENTRY ApcListEntry; - PKAPC Apc; - KIRQL OldIrql; - PKKERNEL_ROUTINE KernelRoutine; - PVOID NormalContext; - PKNORMAL_ROUTINE NormalRoutine; - PVOID SystemArgument1; - PVOID SystemArgument2; + PKTHREAD Thread = KeGetCurrentThread(); + PLIST_ENTRY ApcListEntry; + PKAPC Apc; + KIRQL OldIrql; + PKKERNEL_ROUTINE KernelRoutine; + PVOID NormalContext; + PKNORMAL_ROUTINE NormalRoutine; + PVOID SystemArgument1; + PVOID SystemArgument2; - ASSERT_IRQL_EQUAL(APC_LEVEL); + ASSERT_IRQL_EQUAL(APC_LEVEL); - /* Lock the APC Queue and Raise IRQL to Synch */ - KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); + /* Lock the APC Queue and Raise IRQL to Synch */ + KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); - /* Clear APC Pending */ - Thread->ApcState.KernelApcPending = FALSE; - - /* Do the Kernel APCs first */ - while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) { - - /* Get the next Entry */ - ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink; - Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); - - /* Save Parameters so that it's safe to free the Object in Kernel Routine*/ - NormalRoutine = Apc->NormalRoutine; - KernelRoutine = Apc->KernelRoutine; - NormalContext = Apc->NormalContext; - SystemArgument1 = Apc->SystemArgument1; - SystemArgument2 = Apc->SystemArgument2; + /* Clear APC Pending */ + Thread->ApcState.KernelApcPending = FALSE; + + /* Do the Kernel APCs first */ + while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) { + + /* Get the next Entry */ + ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink; + Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); + + /* Save Parameters so that it's safe to free the Object in Kernel Routine*/ + NormalRoutine = Apc->NormalRoutine; + KernelRoutine = Apc->KernelRoutine; + NormalContext = Apc->NormalContext; + SystemArgument1 = Apc->SystemArgument1; + SystemArgument2 = Apc->SystemArgument2; - /* Special APC */ - if (NormalRoutine == NULL) { - /* Remove the APC from the list */ - Apc->Inserted = FALSE; - RemoveEntryList(ApcListEntry); - - /* Go back to APC_LEVEL */ - KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); - - /* Call the Special APC */ - DPRINT("Delivering a Special APC: %x\n", Apc); - KernelRoutine(Apc, - &NormalRoutine, - &NormalContext, - &SystemArgument1, - &SystemArgument2); - - /* Raise IRQL and Lock again */ - KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); - } else { - /* Normal Kernel APC */ - if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) { + /* Special APC */ + if (NormalRoutine == NULL) { + + /* Remove the APC from the list */ + Apc->Inserted = FALSE; + RemoveEntryList(ApcListEntry); - /* - * DeliveryMode must be KernelMode in this case, since one may not - * return to umode while being inside a critical section or while - * a regular kmode apc is running (the latter should be impossible btw). - * -Gunnar - */ - ASSERT(DeliveryMode == KernelMode); + /* Go back to APC_LEVEL */ + KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); + + /* Call the Special APC */ + DPRINT("Delivering a Special APC: %x\n", Apc); + KernelRoutine(Apc, + &NormalRoutine, + &NormalContext, + &SystemArgument1, + &SystemArgument2); - KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); - return; - } - - /* Dequeue the APC */ - RemoveEntryList(ApcListEntry); - Apc->Inserted = FALSE; - - /* Go back to APC_LEVEL */ - KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); - - /* Call the Kernel APC */ - DPRINT("Delivering a Normal APC: %x\n", Apc); - KernelRoutine(Apc, - &NormalRoutine, - &NormalContext, - &SystemArgument1, - &SystemArgument2); - - /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */ - if (NormalRoutine != NULL) { - /* At Passive Level, this APC can be prempted by a Special APC */ - Thread->ApcState.KernelApcInProgress = TRUE; - KeLowerIrql(PASSIVE_LEVEL); - - /* Call and Raise IRQ back to APC_LEVEL */ - DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc); - NormalRoutine(&NormalContext, &SystemArgument1, &SystemArgument2); - KeRaiseIrql(APC_LEVEL, &OldIrql); - } + /* Raise IRQL and Lock again */ + KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); + + } else { + + /* Normal Kernel APC */ + if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) { + + /* + * DeliveryMode must be KernelMode in this case, since one may not + * return to umode while being inside a critical section or while + * a regular kmode apc is running (the latter should be impossible btw). + * -Gunnar + */ + ASSERT(DeliveryMode == KernelMode); - /* Raise IRQL and Lock again */ - KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); - Thread->ApcState.KernelApcInProgress = FALSE; - } - } - - /* Now we do the User APCs */ - if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) && - (DeliveryMode == UserMode) && - (Thread->ApcState.UserApcPending == TRUE)) { - - /* It's not pending anymore */ - Thread->ApcState.UserApcPending = FALSE; + KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); + return; + } + + /* Dequeue the APC */ + RemoveEntryList(ApcListEntry); + Apc->Inserted = FALSE; + + /* Go back to APC_LEVEL */ + KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); + + /* Call the Kernel APC */ + DPRINT("Delivering a Normal APC: %x\n", Apc); + KernelRoutine(Apc, + &NormalRoutine, + &NormalContext, + &SystemArgument1, + &SystemArgument2); + + /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */ + if (NormalRoutine != NULL) { + + /* At Passive Level, this APC can be prempted by a Special APC */ + Thread->ApcState.KernelApcInProgress = TRUE; + KeLowerIrql(PASSIVE_LEVEL); + + /* Call and Raise IRQ back to APC_LEVEL */ + DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc); + NormalRoutine(&NormalContext, &SystemArgument1, &SystemArgument2); + KeRaiseIrql(APC_LEVEL, &OldIrql); + } - /* Get the APC Object */ - ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink; - Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); - - /* Save Parameters so that it's safe to free the Object in Kernel Routine*/ - NormalRoutine = Apc->NormalRoutine; - KernelRoutine = Apc->KernelRoutine; - NormalContext = Apc->NormalContext; - SystemArgument1 = Apc->SystemArgument1; - SystemArgument2 = Apc->SystemArgument2; - - /* Remove the APC from Queue, restore IRQL and call the APC */ - RemoveEntryList(ApcListEntry); - Apc->Inserted = FALSE; + /* Raise IRQL and Lock again */ + KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); + Thread->ApcState.KernelApcInProgress = FALSE; + } + } + + /* Now we do the User APCs */ + if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) && + (DeliveryMode == UserMode) && (Thread->ApcState.UserApcPending == TRUE)) { + + /* It's not pending anymore */ + Thread->ApcState.UserApcPending = FALSE; + + /* Get the APC Object */ + ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink; + Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); + + /* Save Parameters so that it's safe to free the Object in Kernel Routine*/ + NormalRoutine = Apc->NormalRoutine; + KernelRoutine = Apc->KernelRoutine; + NormalContext = Apc->NormalContext; + SystemArgument1 = Apc->SystemArgument1; + SystemArgument2 = Apc->SystemArgument2; + + /* Remove the APC from Queue, restore IRQL and call the APC */ + RemoveEntryList(ApcListEntry); + Apc->Inserted = FALSE; - KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); - DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc); - KernelRoutine(Apc, - &NormalRoutine, - &NormalContext, - &SystemArgument1, - &SystemArgument2); + KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); + DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc); + KernelRoutine(Apc, + &NormalRoutine, + &NormalContext, + &SystemArgument1, + &SystemArgument2); - if (NormalRoutine == NULL) { - /* Check if more User APCs are Pending */ - KeTestAlertThread(UserMode); - } else { - /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */ - DPRINT("Delivering a User APC: %x\n", Apc); - KiInitializeUserApc(Reserved, - TrapFrame, - NormalRoutine, - NormalContext, - SystemArgument1, - SystemArgument2); - } - } else { - /* Go back to APC_LEVEL */ - KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); - } + if (NormalRoutine == NULL) { + + /* Check if more User APCs are Pending */ + KeTestAlertThread(UserMode); + + } else { + + /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */ + DPRINT("Delivering a User APC: %x\n", Apc); + KiInitializeUserApc(Reserved, + TrapFrame, + NormalRoutine, + NormalContext, + SystemArgument1, + SystemArgument2); + } + + } else { + + /* Go back to APC_LEVEL */ + KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); + } } VOID STDCALL KiFreeApcRoutine(PKAPC Apc, - PKNORMAL_ROUTINE* NormalRoutine, - PVOID* NormalContext, - PVOID* SystemArgument1, - PVOID* SystemArgument2) + PKNORMAL_ROUTINE* NormalRoutine, + PVOID* NormalContext, + PVOID* SystemArgument1, + PVOID* SystemArgument2) { - /* Free the APC and do nothing else */ - ExFreePool(Apc); + /* Free the APC and do nothing else */ + ExFreePool(Apc); } -VOID +/*++ + * KiInitializeUserApc + * + * Prepares the Context for a User-Mode APC called through NTDLL.DLL + * + * Params: + * Reserved - Pointer to the Exception Frame on non-i386 builds. + * + * TrapFrame - Pointer to the Trap Frame. + * + * NormalRoutine - Pointer to the NormalRoutine to call. + * + * NormalContext - Pointer to the context to send to the Normal Routine. + * + * SystemArgument[1-2] - Pointer to a set of two parameters that contain + * untyped data. + * + * Returns: + * None. + * + * Remarks: + * None. + * + *--*/ +VOID +STDCALL KiInitializeUserApc(IN PVOID Reserved, - IN PKTRAP_FRAME TrapFrame, - IN PKNORMAL_ROUTINE NormalRoutine, - IN PVOID NormalContext, - IN PVOID SystemArgument1, - IN PVOID SystemArgument2) -/* - * FUNCTION: Prepares the Context for a user mode APC through ntdll.dll - */ + IN PKTRAP_FRAME TrapFrame, + IN PKNORMAL_ROUTINE NormalRoutine, + IN PVOID NormalContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) { - PCONTEXT Context; - PULONG Esp; + PCONTEXT Context; + PULONG Esp; - DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame, KeGetCurrentThread()->TrapFrame); + DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame, KeGetCurrentThread()->TrapFrame); - /* - * Save the thread's current context (in other words the registers - * that will be restored when it returns to user mode) so the - * APC dispatcher can restore them later - */ - Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT)); - RtlZeroMemory(Context, sizeof(CONTEXT)); - Context->ContextFlags = CONTEXT_FULL; - Context->SegGs = TrapFrame->Gs; - Context->SegFs = TrapFrame->Fs; - Context->SegEs = TrapFrame->Es; - Context->SegDs = TrapFrame->Ds; - Context->Edi = TrapFrame->Edi; - Context->Esi = TrapFrame->Esi; - Context->Ebx = TrapFrame->Ebx; - Context->Edx = TrapFrame->Edx; - Context->Ecx = TrapFrame->Ecx; - Context->Eax = TrapFrame->Eax; - Context->Ebp = TrapFrame->Ebp; - Context->Eip = TrapFrame->Eip; - Context->SegCs = TrapFrame->Cs; - Context->EFlags = TrapFrame->Eflags; - Context->Esp = TrapFrame->Esp; - Context->SegSs = TrapFrame->Ss; + /* + * Save the thread's current context (in other words the registers + * that will be restored when it returns to user mode) so the + * APC dispatcher can restore them later + */ + Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT)); + RtlZeroMemory(Context, sizeof(CONTEXT)); + Context->ContextFlags = CONTEXT_FULL; + Context->SegGs = TrapFrame->Gs; + Context->SegFs = TrapFrame->Fs; + Context->SegEs = TrapFrame->Es; + Context->SegDs = TrapFrame->Ds; + Context->Edi = TrapFrame->Edi; + Context->Esi = TrapFrame->Esi; + Context->Ebx = TrapFrame->Ebx; + Context->Edx = TrapFrame->Edx; + Context->Ecx = TrapFrame->Ecx; + Context->Eax = TrapFrame->Eax; + Context->Ebp = TrapFrame->Ebp; + Context->Eip = TrapFrame->Eip; + Context->SegCs = TrapFrame->Cs; + Context->EFlags = TrapFrame->Eflags; + Context->Esp = TrapFrame->Esp; + Context->SegSs = TrapFrame->Ss; - /* - * Setup the trap frame so the thread will start executing at the - * APC Dispatcher when it returns to user-mode - */ - Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) - (sizeof(CONTEXT) + (6 * sizeof(ULONG)))); - Esp[0] = 0xdeadbeef; - Esp[1] = (ULONG)NormalRoutine; - Esp[2] = (ULONG)NormalContext; - Esp[3] = (ULONG)SystemArgument1; - Esp[4] = (ULONG)SystemArgument2; - Esp[5] = (ULONG)Context; - TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher(); - TrapFrame->Esp = (ULONG)Esp; + /* + * Setup the trap frame so the thread will start executing at the + * APC Dispatcher when it returns to user-mode + */ + Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) - (sizeof(CONTEXT) + (6 * sizeof(ULONG)))); + Esp[0] = 0xdeadbeef; + Esp[1] = (ULONG)NormalRoutine; + Esp[2] = (ULONG)NormalContext; + Esp[3] = (ULONG)SystemArgument1; + Esp[4] = (ULONG)SystemArgument2; + Esp[5] = (ULONG)Context; + TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher(); + TrapFrame->Esp = (ULONG)Esp; } -/* - * @implemented - */ +/*++ + * KeAreApcsDisabled + * @implemented NT4 + * + * Prepares the Context for a User-Mode APC called through NTDLL.DLL + * + * Params: + * None. + * + * Returns: + * KeAreApcsDisabled returns TRUE if the thread is within a critical region + * or a guarded region, and FALSE otherwise. + * + * Remarks: + * A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled to + * determine if normal kernel APCs are disabled. A thread that is inside a + * critical region has both user APCs and normal kernel APCs disabled, but + * not special kernel APCs. A thread that is inside a guarded region has + * all APCs disabled, including special kernel APCs. + * + * Callers of this routine must be running at IRQL <= APC_LEVEL. + * + *--*/ BOOLEAN STDCALL -KeAreApcsDisabled( - VOID - ) +KeAreApcsDisabled(VOID) { - return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE; + /* Return the Kernel APC State */ + return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE; } +/*++ + * NtQueueApcThread + * NT4 + * + * This routine is used to queue an APC from user-mode for the specified + * thread. + * + * Params: + * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges. + * + * ApcRoutine - Pointer to the APC Routine to call when the APC executes. + * + * NormalContext - Pointer to the context to send to the Normal Routine. + * + * SystemArgument[1-2] - Pointer to a set of two parameters that contain + * untyped data. + * + * Returns: + * STATUS_SUCCESS or failure cute from associated calls. + * + * Remarks: + * The thread must enter an alertable wait before the APC will be + * delivered. + * + *--*/ NTSTATUS STDCALL -NtQueueApcThread(HANDLE ThreadHandle, - PKNORMAL_ROUTINE ApcRoutine, - PVOID NormalContext, - PVOID SystemArgument1, - PVOID SystemArgument2) -/* - * FUNCTION: - * This function is used to queue an APC from user-mode for the specified thread. - * The thread must enter an alertable wait before the APC will be delivered. - * - * ARGUMENTS: - * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges. - * ApcRoutine - Pointer to the APC Routine to call when the APC executes. - * NormalContext - User-defined value to pass to the APC Routine - * SystemArgument1 - User-defined value to pass to the APC Routine - * SystemArgument2 - User-defined value to pass to the APC Routine - * - * RETURNS: NTSTATUS SUCCESS or Failure Code from included calls. - */ +NtQueueApcThread(HANDLE ThreadHandle, + PKNORMAL_ROUTINE ApcRoutine, + PVOID NormalContext, + PVOID SystemArgument1, + PVOID SystemArgument2) { + PKAPC Apc; + PETHREAD Thread; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status; - PKAPC Apc; - PETHREAD Thread; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; - - PreviousMode = ExGetPreviousMode(); - - /* Get ETHREAD from Handle */ - Status = ObReferenceObjectByHandle(ThreadHandle, - THREAD_SET_CONTEXT, - PsThreadType, - PreviousMode, - (PVOID)&Thread, - NULL); - - /* Fail if the Handle is invalid for some reason */ - if (!NT_SUCCESS(Status)) { - return(Status); - } - - /* If this is a Kernel or System Thread, then fail */ - if (Thread->Tcb.Teb == NULL) { - ObDereferenceObject(Thread); - return STATUS_INVALID_HANDLE; - } + /* Get ETHREAD from Handle */ + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_SET_CONTEXT, + PsThreadType, + PreviousMode, + (PVOID)&Thread, + NULL); + + /* Fail if the Handle is invalid for some reason */ + if (!NT_SUCCESS(Status)) { + + return(Status); + } + + /* If this is a Kernel or System Thread, then fail */ + if (Thread->Tcb.Teb == NULL) { + + ObDereferenceObject(Thread); + return STATUS_INVALID_HANDLE; + } - /* Allocate an APC */ - Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_KAPC); - if (Apc == NULL) { - ObDereferenceObject(Thread); - return(STATUS_NO_MEMORY); - } + /* Allocate an APC */ + Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('P', 's', 'a', 'p')); + if (Apc == NULL) { + + ObDereferenceObject(Thread); + return(STATUS_NO_MEMORY); + } - /* Initialize and Queue a user mode apc (always!) */ - KeInitializeApc(Apc, - &Thread->Tcb, - OriginalApcEnvironment, - KiFreeApcRoutine, - NULL, - ApcRoutine, - UserMode, - NormalContext); - if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) { - Status = STATUS_UNSUCCESSFUL; - } else { - Status = STATUS_SUCCESS; - } + /* Initialize and Queue a user mode apc (always!) */ + KeInitializeApc(Apc, + &Thread->Tcb, + OriginalApcEnvironment, + KiFreeApcRoutine, + NULL, + ApcRoutine, + UserMode, + NormalContext); + + if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) { + + Status = STATUS_UNSUCCESSFUL; + + } else { + + Status = STATUS_SUCCESS; + } - /* Dereference Thread and Return */ - ObDereferenceObject(Thread); - return Status; + /* Dereference Thread and Return */ + ObDereferenceObject(Thread); + return Status; } - -static inline VOID RepairList(PLIST_ENTRY Original, - PLIST_ENTRY Copy, - KPROCESSOR_MODE Mode) +static inline +VOID RepairList(PLIST_ENTRY Original, + PLIST_ENTRY Copy, + KPROCESSOR_MODE Mode) { - /* Copy Source to Desination */ - if (IsListEmpty(&Original[(int)Mode])) { - InitializeListHead(&Copy[(int)Mode]); - } else { - Copy[(int)Mode].Flink = Original[(int)Mode].Flink; - Copy[(int)Mode].Blink = Original[(int)Mode].Blink; - Original[(int)Mode].Flink->Blink = &Copy[(int)Mode]; - Original[(int)Mode].Blink->Flink = &Copy[(int)Mode]; - } + /* Copy Source to Desination */ + if (IsListEmpty(&Original[(int)Mode])) { + + InitializeListHead(&Copy[(int)Mode]); + + } else { + + Copy[(int)Mode].Flink = Original[(int)Mode].Flink; + Copy[(int)Mode].Blink = Original[(int)Mode].Blink; + Original[(int)Mode].Flink->Blink = &Copy[(int)Mode]; + Original[(int)Mode].Blink->Flink = &Copy[(int)Mode]; + } } VOID STDCALL -KiMoveApcState (PKAPC_STATE OldState, - PKAPC_STATE NewState) +KiMoveApcState(PKAPC_STATE OldState, + PKAPC_STATE NewState) { - /* Restore backup of Original Environment */ - *NewState = *OldState; + /* Restore backup of Original Environment */ + *NewState = *OldState; - /* Repair Lists */ - RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode); - RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode); + /* Repair Lists */ + RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode); + RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode); } diff --git a/reactos/ntoskrnl/ke/bug.c b/reactos/ntoskrnl/ke/bug.c index 2af3e26b721..2604b47c020 100644 --- a/reactos/ntoskrnl/ke/bug.c +++ b/reactos/ntoskrnl/ke/bug.c @@ -1,200 +1,445 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/bug.c * PURPOSE: Graceful system shutdown if a bug is detected * - * PROGRAMMERS: David Welch (welch@cwcom.net) + * PROGRAMMERS: Alex Ionescu - Rewrote Bugcheck Routines and implemented Reason Callbacks. + * David Welch (welch@cwcom.net) * Phillip Susi */ /* INCLUDES *****************************************************************/ #include -#include +#define NDEBUG #include -#include "../../hal/halx86/include/hal.h" /* GLOBALS ******************************************************************/ static LIST_ENTRY BugcheckCallbackListHead = {NULL,NULL}; +static LIST_ENTRY BugcheckReasonCallbackListHead = {NULL,NULL}; static ULONG InBugCheck; +static PRTL_MESSAGE_RESOURCE_DATA KiBugCodeMessages; +static ULONG KeBugCheckCount = 1; /* FUNCTIONS *****************************************************************/ -VOID INIT_FUNCTION -KeInitializeBugCheck(VOID) +VOID +INIT_FUNCTION +KiInitializeBugCheck(VOID) { - InitializeListHead(&BugcheckCallbackListHead); - InBugCheck = 0; + PRTL_MESSAGE_RESOURCE_DATA BugCheckData; + LDR_RESOURCE_INFO ResourceInfo; + PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry; + NTSTATUS Status; + + /* Initialize Callbadk Listhead and State */ + InitializeListHead(&BugcheckCallbackListHead); + InitializeListHead(&BugcheckReasonCallbackListHead); + InBugCheck = 0; + + /* Cache the Bugcheck Message Strings. Prepare the Lookup Data */ + ResourceInfo.Type = 11; + ResourceInfo.Name = 1; + ResourceInfo.Language = 9; + + /* Do the lookup. */ + Status = LdrFindResource_U((PVOID)KERNEL_BASE, + &ResourceInfo, + RESOURCE_DATA_LEVEL, + &ResourceDataEntry); + + /* Make sure it worked */ + if (NT_SUCCESS(Status)) { + + DPRINT1("Found Bugcheck Resource Data!\n"); + + /* Now actually get a pointer to it */ + Status = LdrAccessResource((PVOID)KERNEL_BASE, + ResourceDataEntry, + (PVOID*)&BugCheckData, + NULL); + + /* Make sure it worked */ + if (NT_SUCCESS(Status)) { + + DPRINT1("Got Pointer to Bugcheck Resource Data!\n"); + KiBugCodeMessages = BugCheckData; + } + } } /* * @implemented */ -BOOLEAN STDCALL +BOOLEAN +STDCALL KeDeregisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord) { - /* Check the Current State */ - if (CallbackRecord->State == BufferInserted) { - CallbackRecord->State = BufferEmpty; - RemoveEntryList(&CallbackRecord->Entry); - return TRUE; - } - - /* The callback wasn't registered */ - return FALSE; + KIRQL OldIrql; + BOOLEAN Status = FALSE; + + /* Raise IRQL to High */ + KeRaiseIrql(HIGH_LEVEL, &OldIrql); + + /* Check the Current State */ + if (CallbackRecord->State == BufferInserted) { + + /* Reset state and remove from list */ + CallbackRecord->State = BufferEmpty; + RemoveEntryList(&CallbackRecord->Entry); + + Status = TRUE; + } + + /* Lower IRQL and return */ + KeLowerIrql(OldIrql); + return Status; } /* * @implemented */ -BOOLEAN STDCALL -KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord, - PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine, - PVOID Buffer, - ULONG Length, - PUCHAR Component) +BOOLEAN +STDCALL +KeDeregisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord) { + KIRQL OldIrql; + BOOLEAN Status = FALSE; + + /* Raise IRQL to High */ + KeRaiseIrql(HIGH_LEVEL, &OldIrql); + + /* Check the Current State */ + if (CallbackRecord->State == BufferInserted) { - /* Check the Current State first so we don't double-register */ - if (CallbackRecord->State == BufferEmpty) { - CallbackRecord->Length = Length; - CallbackRecord->Buffer = Buffer; - CallbackRecord->Component = Component; - CallbackRecord->CallbackRoutine = CallbackRoutine; - CallbackRecord->State = BufferInserted; - InsertTailList(&BugcheckCallbackListHead, &CallbackRecord->Entry); - - return TRUE; - } - - /* The Callback was already registered */ - return(FALSE); + /* Reset state and remove from list */ + CallbackRecord->State = BufferEmpty; + RemoveEntryList(&CallbackRecord->Entry); + + Status = TRUE; + } + + /* Lower IRQL and return */ + KeLowerIrql(OldIrql); + return Status; } -VOID STDCALL -KeBugCheckWithTf(ULONG BugCheckCode, - ULONG BugCheckParameter1, - ULONG BugCheckParameter2, - ULONG BugCheckParameter3, - ULONG BugCheckParameter4, - PKTRAP_FRAME Tf) +/* + * @implemented + */ +BOOLEAN +STDCALL +KeRegisterBugCheckCallback(PKBUGCHECK_CALLBACK_RECORD CallbackRecord, + PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine, + PVOID Buffer, + ULONG Length, + PUCHAR Component) { - PRTL_MESSAGE_RESOURCE_ENTRY Message; - NTSTATUS Status; - ULONG Mask; - KIRQL OldIrql; - - /* Make sure we're switching back to the blue screen and print messages on it */ - HalReleaseDisplayOwnership(); - if (0 == (KdDebugState & KD_DEBUG_GDB)) - { - KdDebugState |= KD_DEBUG_SCREEN; + KIRQL OldIrql; + BOOLEAN Status = FALSE; + + /* Raise IRQL to High */ + KeRaiseIrql(HIGH_LEVEL, &OldIrql); + + /* Check the Current State first so we don't double-register */ + if (CallbackRecord->State == BufferEmpty) { + + /* Set the Callback Settings and insert into the list */ + CallbackRecord->Length = Length; + CallbackRecord->Buffer = Buffer; + CallbackRecord->Component = Component; + CallbackRecord->CallbackRoutine = CallbackRoutine; + CallbackRecord->State = BufferInserted; + InsertTailList(&BugcheckCallbackListHead, &CallbackRecord->Entry); + + Status = TRUE; } - Ke386DisableInterrupts(); - DebugLogDumpMessages(); + /* Lower IRQL and return */ + KeLowerIrql(OldIrql); + return Status; +} - if (MmGetKernelAddressSpace()->Lock.Owner == KeGetCurrentThread()) - { - MmUnlockAddressSpace(MmGetKernelAddressSpace()); +/* + * @implemented + */ +BOOLEAN +STDCALL +KeRegisterBugCheckReasonCallback(IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord, + IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine, + IN KBUGCHECK_CALLBACK_REASON Reason, + IN PUCHAR Component) +{ + KIRQL OldIrql; + BOOLEAN Status = FALSE; + + /* Raise IRQL to High */ + KeRaiseIrql(HIGH_LEVEL, &OldIrql); + + /* Check the Current State first so we don't double-register */ + if (CallbackRecord->State == BufferEmpty) { + + /* Set the Callback Settings and insert into the list */ + CallbackRecord->Component = Component; + CallbackRecord->CallbackRoutine = CallbackRoutine; + CallbackRecord->State = BufferInserted; + CallbackRecord->Reason = Reason; + InsertTailList(&BugcheckReasonCallbackListHead, &CallbackRecord->Entry); + + Status = TRUE; } - if (KeGetCurrentIrql() < DISPATCH_LEVEL) - { - KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); - } - DbgPrint("Bug detected (code %x param %x %x %x %x)\n", - BugCheckCode, - BugCheckParameter1, - BugCheckParameter2, - BugCheckParameter3, - BugCheckParameter4); + /* Lower IRQL and return */ + KeLowerIrql(OldIrql); + return Status; +} - Status = RtlFindMessage((PVOID)KERNEL_BASE, //0xC0000000, - 11, //RT_MESSAGETABLE, - 0x09, //0x409, - BugCheckCode, - &Message); - if (NT_SUCCESS(Status)) - { - if (Message->Flags == 0) - DbgPrint(" %s\n", Message->Text); - else - DbgPrint(" %S\n", (PWSTR)Message->Text); +VOID +STDCALL +KeGetBugMessageText(ULONG BugCheckCode, PANSI_STRING OutputString) +{ + ULONG i; + ULONG IdOffset; + ULONG_PTR MessageEntry; + PCHAR BugCode; + + /* Find the message. This code is based on RtlFindMesssage -- Alex */ + for (i = 0; i < KiBugCodeMessages->NumberOfBlocks; i++) { + + /* Check if the ID Matches */ + if ((BugCheckCode >= KiBugCodeMessages->Blocks[i].LowId) && + (BugCheckCode <= KiBugCodeMessages->Blocks[i].HighId)) { + + /* Get Offset to Entry */ + MessageEntry = (ULONG_PTR)KiBugCodeMessages + KiBugCodeMessages->Blocks[i].OffsetToEntries; + IdOffset = BugCheckCode - KiBugCodeMessages->Blocks[i].LowId; + + /* Get offset to ID */ + for (i = 0; i < IdOffset; i++) { + + /* Advance in the Entries */ + MessageEntry += ((PRTL_MESSAGE_RESOURCE_ENTRY)MessageEntry)->Length; + } + + /* Get the final Code */ + BugCode = ((PRTL_MESSAGE_RESOURCE_ENTRY)MessageEntry)->Text; + + /* Return it in the OutputString */ + if (OutputString) { + + OutputString->Buffer = BugCode; + OutputString->Length = strlen(BugCode) + 1; + OutputString->MaximumLength = strlen(BugCode) + 1; + + } else { + + /* Direct Output to Screen */ + DbgPrint("%s\n", BugCode); + break; + } + } } - else - { - DbgPrint(" No message text found!\n\n"); +} + +VOID +STDCALL +KiDoBugCheckCallbacks(VOID) +{ + PKBUGCHECK_CALLBACK_RECORD CurrentRecord; + PLIST_ENTRY ListHead; + PLIST_ENTRY NextEntry; + + /* FIXME: Check Checksum and add support for WithReason Callbacks */ + + /* First make sure that the list is Initialized... it might not be */ + ListHead = &BugcheckCallbackListHead; + if (ListHead->Flink && ListHead->Blink) { + + /* Loop the list */ + NextEntry = ListHead->Flink; + while (NextEntry != ListHead) { + + /* Get the Callback Record */ + CurrentRecord = CONTAINING_RECORD(NextEntry, + KBUGCHECK_CALLBACK_RECORD, + Entry); + + /* Make sure it's inserted */ + if (CurrentRecord->State == BufferInserted) { + + /* Call the routine */ + CurrentRecord->State = BufferStarted; + (CurrentRecord->CallbackRoutine)(CurrentRecord->Buffer, + CurrentRecord->Length); + CurrentRecord->State = BufferFinished; + } + + /* Move to next Entry */ + NextEntry = NextEntry->Flink; + } } - Mask = 1 << KeGetCurrentProcessorNumber(); - if (InBugCheck & Mask) - { -#ifdef MP - DbgPrint("Recursive bug check on CPU%d, halting now\n", KeGetCurrentProcessorNumber()); - /* - * FIXME: - * Send an ipi to all other processors which halt them too. - */ -#else - DbgPrint("Recursive bug check halting now\n"); +} + +VOID +STDCALL +KeBugCheckWithTf(ULONG BugCheckCode, + ULONG BugCheckParameter1, + ULONG BugCheckParameter2, + ULONG BugCheckParameter3, + ULONG BugCheckParameter4, + PKTRAP_FRAME Tf) +{ + KIRQL OldIrql; + BOOLEAN GotExtendedCrashInfo = FALSE; + PVOID Address = 0; + PLIST_ENTRY CurrentEntry; + MODULE_TEXT_SECTION* CurrentSection = NULL; + extern LIST_ENTRY ModuleTextListHead; + + /* Make sure we're switching back to the blue screen and print messages on it */ + HalReleaseDisplayOwnership(); + if (0 == (KdDebugState & KD_DEBUG_GDB)) KdDebugState |= KD_DEBUG_SCREEN; + + /* Try to find out who did this. For this, we need a Trap Frame. + * Note: Some special BSODs pass the Frame/EIP as a Param. MSDN has the + * info so it eventually needs to be supported. + */ + if (Tf) { + + /* For now, get Address from EIP */ + Address = (PVOID)Tf->Eip; + + /* Try to get information on the module */ + CurrentEntry = ModuleTextListHead.Flink; + while (CurrentEntry != &ModuleTextListHead && CurrentEntry != NULL) { + + /* Get the current Section */ + CurrentSection = CONTAINING_RECORD(CurrentEntry, + MODULE_TEXT_SECTION, + ListEntry); + + /* Check if this is the right one */ + if ((Address != NULL && (Address >= (PVOID)CurrentSection->Base && + Address < (PVOID)(CurrentSection->Base + CurrentSection->Length)))) { + + /* We got it */ + GotExtendedCrashInfo = TRUE; + break; + } + + /* Loop again */ + CurrentEntry = CurrentEntry->Flink; + } + } + + /* Raise IRQL to HIGH_LEVEL */ + Ke386DisableInterrupts(); + KeRaiseIrql(HIGH_LEVEL, &OldIrql); + + /* Disable Interrupts, Dump Debug Messages */ + DebugLogDumpMessages(); + + /* Unload the Kernel Adress Space if we own it */ + if (MmGetKernelAddressSpace()->Lock.Owner == KeGetCurrentThread()) + MmUnlockAddressSpace(MmGetKernelAddressSpace()); + + /* FIXMEs: Use inbv to clear, fill and write to screen. */ + + /* Show the STOP Message */ + DbgPrint("A problem has been detected and ReactOS has been shut down to prevent " + "damage to your computer.\n\n"); + + /* Show the module name of who caused this */ + if (GotExtendedCrashInfo) { + + DbgPrint("The problem seems to be caused by the following file: %S\n\n", CurrentSection->Name); + } + + /* Find the Bug Code String */ + KeGetBugMessageText(BugCheckCode, NULL); + + /* Show the techincal Data */ + DbgPrint("Technical information:\n\n*** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p)\n\n", + BugCheckCode, + BugCheckParameter1, + BugCheckParameter2, + BugCheckParameter3, + BugCheckParameter4); + + /* Show the module name and more data of who caused this */ + if (GotExtendedCrashInfo) { + + DbgPrint("*** %S - Address 0x%p base at 0x%p, DateStamp 0x%x\n\n", + CurrentSection->Name, + Address, + CurrentSection->Base, + 0); + } + + /* There can only be one Bugcheck per Bootup */ + if (!InterlockedDecrement(&KeBugCheckCount)) { + +#ifdef CONFIG_SMP + ULONG i; + /* Freeze the other CPUs */ + for (i = 0; i < KeNumberProcessors; i++) { + if (i != KeGetCurrentProcessorNumber()) { + + /* Send the IPI and give them one second to catch up */ + KiIpiSendRequest(1 << i, IPI_REQUEST_FREEZE); + KeStallExecutionProcessor(1000000); + } + } #endif - Ke386HaltProcessor(); - } - /* - * FIXME: - * Use InterlockedOr or InterlockedBitSet. - */ - InBugCheck |= Mask; - if (Tf != NULL) - { - KiDumpTrapFrame(Tf, BugCheckParameter1, BugCheckParameter2); - } - else - { + + /* Check if we got a Trap Frame */ + if (Tf) { + + /* Dump it */ + KiDumpTrapFrame(Tf, BugCheckParameter1, BugCheckParameter2); + + } else { + + /* We can only dump the frames */ #if defined(__GNUC__) - KeDumpStackFrames((PULONG)__builtin_frame_address(0)); + KeDumpStackFrames((PULONG)__builtin_frame_address(0)); #elif defined(_MSC_VER) - __asm push ebp - __asm call KeDumpStackFrames - __asm add esp, 4 + __asm push ebp + __asm call KeDumpStackFrames + __asm add esp, 4 #else #error Unknown compiler for inline assembler #endif - } - MmDumpToPagingFile(BugCheckCode, BugCheckParameter1, - BugCheckParameter2, BugCheckParameter3, - BugCheckParameter4, Tf); + } + + /* Call the Callbacks */; + KiDoBugCheckCallbacks(); - if (KdDebuggerEnabled) - { - Ke386EnableInterrupts(); - DbgBreakPointNoBugCheck(); - Ke386DisableInterrupts(); + /* Dump the BSOD to the Paging File */ + MmDumpToPagingFile(BugCheckCode, + BugCheckParameter1, + BugCheckParameter2, + BugCheckParameter3, + BugCheckParameter4, + Tf); + + /* Wake up the Debugger */ + if (KdDebuggerEnabled) { + Ke386EnableInterrupts(); + DbgBreakPointWithStatus(DBG_STATUS_BUGCHECK_SECOND); + Ke386DisableInterrupts(); + } } - for (;;) - { - /* - * FIXME: - * Send an ipi to all other processors which halt them too. - */ - Ke386HaltProcessor(); - } + /* Halt this CPU now */ + for (;;) Ke386HaltProcessor(); } /* * @implemented - */ -VOID STDCALL -KeBugCheckEx(ULONG BugCheckCode, - ULONG BugCheckParameter1, - ULONG BugCheckParameter2, - ULONG BugCheckParameter3, - ULONG BugCheckParameter4) -/* + * * FUNCTION: Brings the system down in a controlled manner when an * inconsistency that might otherwise cause corruption has been detected * ARGUMENTS: @@ -202,23 +447,34 @@ KeBugCheckEx(ULONG BugCheckCode, * BugCheckParameter[1-4] = Additional information about bug * RETURNS: Doesn't */ +VOID +STDCALL +KeBugCheckEx(ULONG BugCheckCode, + ULONG BugCheckParameter1, + ULONG BugCheckParameter2, + ULONG BugCheckParameter3, + ULONG BugCheckParameter4) { - KeBugCheckWithTf(BugCheckCode, BugCheckParameter1, BugCheckParameter2, - BugCheckParameter3, BugCheckParameter4, NULL); + /* Call the Trap Frame version without a Trap Frame */ + KeBugCheckWithTf(BugCheckCode, + BugCheckParameter1, + BugCheckParameter2, + BugCheckParameter3, + BugCheckParameter4, + NULL); } /* * @implemented - */ -VOID STDCALL -KeBugCheck(ULONG BugCheckCode) -/* + * * FUNCTION: Brings the system down in a controlled manner when an * inconsistency that might otherwise cause corruption has been detected * ARGUMENTS: * BugCheckCode = Specifies the reason for the bug check * RETURNS: Doesn't */ +VOID STDCALL +KeBugCheck(ULONG BugCheckCode) { KeBugCheckEx(BugCheckCode, 0, 0, 0, 0); } diff --git a/reactos/ntoskrnl/ke/catch.c b/reactos/ntoskrnl/ke/catch.c index 1d4ca92a486..50bf4c70df3 100644 --- a/reactos/ntoskrnl/ke/catch.c +++ b/reactos/ntoskrnl/ke/catch.c @@ -1,11 +1,11 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/catch.c * PURPOSE: Exception handling * - * PROGRAMMERS: David Welch (welch@mcmail.com) + * PROGRAMMERS: Anich Gregor + * David Welch (welch@mcmail.com) * Casper S. Hornstrup (chorns@users.sourceforge.net) */ @@ -19,284 +19,225 @@ ULONG RtlpDispatchException(IN PEXCEPTION_RECORD ExceptionRecord, - IN PCONTEXT Context); + IN PCONTEXT Context); + +/* + * @unimplemented + */ +VOID +STDCALL +KiCoprocessorError(VOID) +{ + UNIMPLEMENTED; +} + +/* + * @unimplemented + */ +VOID +STDCALL +KiUnexpectedInterrupt(VOID) +{ + UNIMPLEMENTED; +} VOID KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, - PCONTEXT Context, - PKTRAP_FRAME Tf, - KPROCESSOR_MODE PreviousMode, - BOOLEAN SearchFrames) + PCONTEXT Context, + PKTRAP_FRAME Tf, + KPROCESSOR_MODE PreviousMode, + BOOLEAN SearchFrames) { - EXCEPTION_DISPOSITION Value; - CONTEXT TContext; - KD_CONTINUE_TYPE Action = kdHandleException; + EXCEPTION_DISPOSITION Value; + CONTEXT TContext; + KD_CONTINUE_TYPE Action = kdHandleException; - DPRINT("KiDispatchException() called\n"); + DPRINT("KiDispatchException() called\n"); + /* Increase number of Exception Dispatches */ + KeGetCurrentPrcb()->KeExceptionDispatchCount++; - /* PCR->KeExceptionDispatchCount++; */ - - if (Context == NULL) + if (!Context) { - TContext.ContextFlags = CONTEXT_FULL; - if (PreviousMode == UserMode) - { - TContext.ContextFlags = TContext.ContextFlags | CONTEXT_DEBUGGER; - } + + /* Assume Full context */ + TContext.ContextFlags = CONTEXT_FULL; + + /* Check the mode */ + if (PreviousMode == UserMode) + { + + /* Add Debugger Registers if this is User Mode */ + TContext.ContextFlags = TContext.ContextFlags | CONTEXT_DEBUGGER; + } - KeTrapFrameToContext(Tf, &TContext); + /* Convert the Trapframe into a Context */ + KeTrapFrameToContext(Tf, &TContext); - Context = &TContext; + /* Use local stack context */ + Context = &TContext; } -#if 0 - if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) +#if 0 /* FIXME: Isn't this right? With a break after? */ + if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) { - Context->Eip--; + Context->Eip--; } #endif - - if (KdDebuggerEnabled && KdDebugState & KD_DEBUG_GDB) + + /* Check if a Debugger is enabled */ + if (KdDebuggerEnabled && KdDebugState & KD_DEBUG_GDB) { - Action = KdEnterDebuggerException (ExceptionRecord, Context, Tf); + + /* Break into it */ + Action = KdEnterDebuggerException (ExceptionRecord, Context, Tf); } - - if (Action == kdContinue) + + /* If the debugger said continue, then continue */ + if (Action == kdContinue) return; + + /* If the Debugger couldn't handle it... */ + if (Action != kdDoNotHandleException) { - return; - } - - if (Action != kdDoNotHandleException) - { - if (PreviousMode == UserMode) - { - if (SearchFrames) - { - PULONG Stack; - ULONG CDest; - char temp_space[12 + sizeof(EXCEPTION_RECORD) + sizeof(CONTEXT)]; /* FIXME: HACKHACK */ - PULONG pNewUserStack = (PULONG)(Tf->Esp - (12 + sizeof(EXCEPTION_RECORD) + sizeof(CONTEXT))); - NTSTATUS StatusOfCopy; - -#ifdef KDBG - Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode, - Context, Tf, FALSE); - if (Action == kdContinue) - { - return; - } -#endif - - /* FIXME: Forward exception to user mode debugger */ - - /* FIXME: Check user mode stack for enough space */ - - /* - * Let usermode try and handle the exception - */ - Stack = (PULONG)temp_space; - CDest = 3 + (ROUND_UP(sizeof(EXCEPTION_RECORD), 4) / 4); - /* Return address */ - Stack[0] = 0; - /* Pointer to EXCEPTION_RECORD structure */ - Stack[1] = (ULONG)&pNewUserStack[3]; - /* Pointer to CONTEXT structure */ - Stack[2] = (ULONG)&pNewUserStack[CDest]; - memcpy(&Stack[3], ExceptionRecord, sizeof(EXCEPTION_RECORD)); - memcpy(&Stack[CDest], Context, sizeof(CONTEXT)); - - StatusOfCopy = MmCopyToCaller(pNewUserStack, - temp_space, - (12 + sizeof(EXCEPTION_RECORD) + sizeof(CONTEXT))); - if (NT_SUCCESS(StatusOfCopy)) - { - Tf->Esp = (ULONG)pNewUserStack; - } - else - { - /* Now it really hit the ventilation device. Sorry, - * can do nothing but kill the sucker. - */ - ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode); - DPRINT1("User-mode stack was invalid. Terminating target thread\n"); - } - Tf->Eip = (ULONG)LdrpGetSystemDllExceptionDispatcher(); - return; - } - - /* FIXME: Forward the exception to the debugger */ - - /* FIXME: Forward the exception to the process exception port */ - -#ifdef KDBG - Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode, - Context, Tf, TRUE); - if (Action == kdContinue) + + /* See what kind of Exception this is */ + if (PreviousMode == UserMode) + { + + /* User mode exception, search the frames if we have to */ + if (SearchFrames) { - return; - } + + PULONG Stack; + ULONG CDest; + char temp_space[12 + sizeof(EXCEPTION_RECORD) + sizeof(CONTEXT)]; /* FIXME: HACKHACK */ + PULONG pNewUserStack = (PULONG)(Tf->Esp - (12 + sizeof(EXCEPTION_RECORD) + sizeof(CONTEXT))); + NTSTATUS StatusOfCopy; + +#ifdef KDBG + /* Enter KDB if available */ + Action = KdbEnterDebuggerException(ExceptionRecord, + PreviousMode, + Context, + Tf, + TRUE); + + /* Exit if we're continuing */ + if (Action == kdContinue) return; #endif - /* Terminate the offending thread */ - DPRINT1("Unhandled UserMode exception, terminating thread\n"); - ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode); - } - else - { - /* PreviousMode == KernelMode */ -#ifdef KDBG - Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode, - Context, Tf, FALSE); - if (Action == kdContinue) - { - return; - } -#endif + /* FIXME: Forward exception to user mode debugger */ - Value = RtlpDispatchException (ExceptionRecord, Context); - - DPRINT("RtlpDispatchException() returned with 0x%X\n", Value); - /* - * If RtlpDispatchException() does not handle the exception then - * bugcheck - */ - if (Value != ExceptionContinueExecution || - 0 != (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)) - { - DPRINT("ExceptionRecord->ExceptionAddress = 0x%x\n", - ExceptionRecord->ExceptionAddress ); -#ifdef KDBG - Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode, - Context, Tf, TRUE); - if (Action == kdContinue) + /* FIXME: Check user mode stack for enough space */ + + /* Let usermode try and handle the exception. Setup Stack */ + Stack = (PULONG)temp_space; + CDest = 3 + (ROUND_UP(sizeof(EXCEPTION_RECORD), 4) / 4); + /* Return Address */ + Stack[0] = 0; + /* Pointer to EXCEPTION_RECORD structure */ + Stack[1] = (ULONG)&pNewUserStack[3]; + /* Pointer to CONTEXT structure */ + Stack[2] = (ULONG)&pNewUserStack[CDest]; + memcpy(&Stack[3], ExceptionRecord, sizeof(EXCEPTION_RECORD)); + memcpy(&Stack[CDest], Context, sizeof(CONTEXT)); + + /* Copy Stack */ + StatusOfCopy = MmCopyToCaller(pNewUserStack, + temp_space, + (12 + sizeof(EXCEPTION_RECORD) + sizeof(CONTEXT))); + + /* Check for success */ + if (NT_SUCCESS(StatusOfCopy)) { - return; + + /* Set new Stack Pointer */ + Tf->Esp = (ULONG)pNewUserStack; + + } + else + { + + /* + * Now it really hit the ventilation device. Sorry, + * can do nothing but kill the sucker. + */ + ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode); + DPRINT1("User-mode stack was invalid. Terminating target thread\n"); } + + /* Set EIP to the User-mode Dispathcer */ + Tf->Eip = (ULONG)LdrpGetSystemDllExceptionDispatcher(); + return; + } + + /* FIXME: Forward the exception to the debugger */ + + /* FIXME: Forward the exception to the process exception port */ + +#ifdef KDBG + /* Enter KDB if available */ + Action = KdbEnterDebuggerException(ExceptionRecord, + PreviousMode, + Context, + Tf, + FALSE); + + /* Exit if we're continuing */ + if (Action == kdContinue) return; #endif - KEBUGCHECKWITHTF(KMODE_EXCEPTION_NOT_HANDLED, 0, 0, 0, 0, Tf); - } - } + + /* Terminate the offending thread */ + DPRINT1("Unhandled UserMode exception, terminating thread\n"); + ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode); + + } + else + { + + /* This is Kernel Mode */ +#ifdef KDBG + /* Enter KDB if available */ + Action = KdbEnterDebuggerException(ExceptionRecord, + PreviousMode, + Context, + Tf, + TRUE); + + /* Exit if we're continuing */ + if (Action == kdContinue) return; +#endif + + /* Dispatch the Exception */ + Value = RtlpDispatchException (ExceptionRecord, Context); + DPRINT("RtlpDispatchException() returned with 0x%X\n", Value); + + /* If RtlpDispatchException() did not handle the exception then bugcheck */ + if (Value != ExceptionContinueExecution || + 0 != (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)) + { + + DPRINT("ExceptionRecord->ExceptionAddress = 0x%x\n", ExceptionRecord->ExceptionAddress); +#ifdef KDBG + /* Enter KDB if available */ + Action = KdbEnterDebuggerException(ExceptionRecord, + PreviousMode, + Context, + Tf, + FALSE); + + /* Exit if we're continuing */ + if (Action == kdContinue) return; +#endif + KEBUGCHECKWITHTF(KMODE_EXCEPTION_NOT_HANDLED, + ExceptionRecord->ExceptionCode, + (ULONG)ExceptionRecord->ExceptionAddress, + ExceptionRecord->ExceptionInformation[0], + ExceptionRecord->ExceptionInformation[1], + Tf); + } + } } } -/* - * @implemented - */ -VOID STDCALL -ExRaiseAccessViolation (VOID) -{ - ExRaiseStatus (STATUS_ACCESS_VIOLATION); -} - -/* - * @implemented - */ -VOID STDCALL -ExRaiseDatatypeMisalignment (VOID) -{ - ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT); -} - -/* - * @implemented - */ -VOID STDCALL -ExRaiseStatus (IN NTSTATUS Status) -{ - EXCEPTION_RECORD ExceptionRecord; - - DPRINT("ExRaiseStatus(%x)\n", Status); - - ExceptionRecord.ExceptionRecord = NULL; - ExceptionRecord.NumberParameters = 0; - ExceptionRecord.ExceptionCode = Status; - ExceptionRecord.ExceptionFlags = 0; - - RtlRaiseException(&ExceptionRecord); -} - - - -/* - * @implemented - */ -VOID -STDCALL -ExRaiseException ( - PEXCEPTION_RECORD ExceptionRecord - ) -{ - RtlRaiseException(ExceptionRecord); -} - -/* - * @implemented - */ -BOOLEAN -STDCALL -ExSystemExceptionFilter(VOID) -{ - return KeGetPreviousMode() != KernelMode ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; -} - -/* - * @unimplemented - */ -VOID -STDCALL -ExRaiseHardError ( - IN NTSTATUS ErrorStatus, - IN ULONG NumberOfParameters, - IN PUNICODE_STRING UnicodeStringParameterMask OPTIONAL, - IN PVOID *Parameters, - IN HARDERROR_RESPONSE_OPTION ResponseOption, - OUT PHARDERROR_RESPONSE Response - ) -{ - UNIMPLEMENTED; -} - -/* - * @unimplemented - */ -BOOLEAN -STDCALL -KeDeregisterBugCheckReasonCallback( - IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord - ) -{ - UNIMPLEMENTED; - return FALSE; -} - -/* - * @unimplemented - */ -ULONG -STDCALL -KeGetRecommendedSharedDataAlignment( - VOID - ) -{ - UNIMPLEMENTED; - return 0; -} - -/* - * @unimplemented - */ -BOOLEAN -STDCALL -KeRegisterBugCheckReasonCallback( - IN PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord, - IN PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine, - IN KBUGCHECK_CALLBACK_REASON Reason, - IN PUCHAR Component - ) -{ - UNIMPLEMENTED; - return FALSE; -} - /* EOF */ diff --git a/reactos/ntoskrnl/ke/clock.c b/reactos/ntoskrnl/ke/clock.c index e9424cbed4f..4a03c0ed363 100644 --- a/reactos/ntoskrnl/ke/clock.c +++ b/reactos/ntoskrnl/ke/clock.c @@ -242,22 +242,22 @@ KeUpdateRunTime( IN KIRQL Irql ) { - PKPCR Pcr; + PKPRCB Prcb; PKTHREAD CurrentThread; PKPROCESS CurrentProcess; #if 0 ULONG DpcLastCount; #endif - Pcr = KeGetCurrentKPCR(); + Prcb = KeGetCurrentPrcb(); /* Make sure we don't go further if we're in early boot phase. */ - if (Pcr == NULL || Pcr->PrcbData.CurrentThread == NULL) + if (Prcb == NULL || Prcb->CurrentThread == NULL) return; - DPRINT("KernelTime %u, UserTime %u \n", Pcr->PrcbData.KernelTime, Pcr->PrcbData.UserTime); + DPRINT("KernelTime %u, UserTime %u \n", Prcb->KernelTime, Prcb->UserTime); - CurrentThread = Pcr->PrcbData.CurrentThread; + CurrentThread = Prcb->CurrentThread; CurrentProcess = CurrentThread->ApcState.Process; /* @@ -269,36 +269,36 @@ KeUpdateRunTime( { InterlockedIncrementUL(&CurrentThread->UserTime); InterlockedIncrementUL(&CurrentProcess->UserTime); - Pcr->PrcbData.UserTime++; + Prcb->UserTime++; } else { if (Irql > DISPATCH_LEVEL) { - Pcr->PrcbData.InterruptTime++; + Prcb->InterruptTime++; } else if (Irql == DISPATCH_LEVEL) { - Pcr->PrcbData.DpcTime++; + Prcb->DpcTime++; } else { InterlockedIncrementUL(&CurrentThread->KernelTime); InterlockedIncrementUL(&CurrentProcess->KernelTime); - Pcr->PrcbData.KernelTime++; + Prcb->KernelTime++; } } #if 0 - DpcLastCount = Pcr->PrcbData.DpcLastCount; - Pcr->PrcbData.DpcLastCount = Pcr->PrcbData.DpcCount; - Pcr->PrcbData.DpcRequestRate = ((Pcr->PrcbData.DpcCount - DpcLastCount) + - Pcr->PrcbData.DpcRequestRate) / 2; + DpcLastCount = Prcb->DpcLastCount; + Prcb->DpcLastCount = Prcb->DpcCount; + Prcb->DpcRequestRate = ((Prcb->DpcCount - DpcLastCount) + + Prcb->DpcRequestRate) / 2; #endif - if (Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0 && - Pcr->PrcbData.DpcRoutineActive == FALSE && - Pcr->PrcbData.DpcInterruptRequested == FALSE) + if (Prcb->DpcData[0].DpcQueueDepth > 0 && + Prcb->DpcRoutineActive == FALSE && + Prcb->DpcInterruptRequested == FALSE) { HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } @@ -311,7 +311,7 @@ KeUpdateRunTime( */ if ((CurrentThread->Quantum -= 3) <= 0) { - Pcr->PrcbData.QuantumEnd = TRUE; + Prcb->QuantumEnd = TRUE; HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } } diff --git a/reactos/ntoskrnl/ke/critical.c b/reactos/ntoskrnl/ke/critical.c deleted file mode 100644 index e2428549b45..00000000000 --- a/reactos/ntoskrnl/ke/critical.c +++ /dev/null @@ -1,56 +0,0 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ke/critical.c - * PURPOSE: Implement critical regions - * - * PROGRAMMERS: David Welch (welch@mcmail.com) - */ - -/* INCLUDES *****************************************************************/ - -#include -#define NDEBUG -#include - -/* FUNCTIONS *****************************************************************/ - -/* - * @implemented - */ -VOID STDCALL KeEnterCriticalRegion (VOID) -{ - PKTHREAD Thread = KeGetCurrentThread(); - - DPRINT("KeEnterCriticalRegion()\n"); - - if (!Thread) return; /* <-Early in the boot process the current thread is obseved to be NULL */ - - Thread->KernelApcDisable--; -} - -/* - * @implemented - */ -VOID STDCALL KeLeaveCriticalRegion (VOID) -{ - PKTHREAD Thread = KeGetCurrentThread(); - - DPRINT("KeLeaveCriticalRegion()\n"); - - if (!Thread) return; /* <-Early in the boot process the current thread is obseved to be NULL */ - - /* Reference: http://www.ntfsd.org/archive/ntfsd0104/msg0203.html */ - if(++Thread->KernelApcDisable == 0) - { - if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) - { - Thread->ApcState.KernelApcPending = TRUE; - HalRequestSoftwareInterrupt(APC_LEVEL); - } - } - -} - -/* EOF */ diff --git a/reactos/ntoskrnl/ke/device.c b/reactos/ntoskrnl/ke/device.c index ad1c8bd32dd..31c95551f8e 100644 --- a/reactos/ntoskrnl/ke/device.c +++ b/reactos/ntoskrnl/ke/device.c @@ -57,17 +57,17 @@ KeFlushEntireTb( { KIRQL OldIrql; PKPROCESS Process = NULL; - PKPCR Pcr = NULL; + PKPRCB Prcb = NULL; /* Raise the IRQL for the TB Flush */ OldIrql = KeRaiseIrqlToSynchLevel(); /* All CPUs need to have the TB flushed. */ if (CurrentCpuOnly == FALSE) { - Pcr = KeGetCurrentKPCR(); + Prcb = KeGetCurrentPrcb(); /* How many CPUs is our caller using? */ - Process = Pcr->PrcbData.CurrentThread->ApcState.Process; + Process = Prcb->CurrentThread->ApcState.Process; /* More then one, so send an IPI */ if (Process->ActiveProcessors > 1) { @@ -83,7 +83,7 @@ KeFlushEntireTb( /* Did we send an IPI? If so, wait for completion */ if (Process->ActiveProcessors > 1) { do { - } while (Pcr->PrcbData.TargetSet != 0); + } while (Prcb->TargetSet != 0); } } diff --git a/reactos/ntoskrnl/ke/dpc.c b/reactos/ntoskrnl/ke/dpc.c index 2aee866c39d..b65e96133db 100644 --- a/reactos/ntoskrnl/ke/dpc.c +++ b/reactos/ntoskrnl/ke/dpc.c @@ -24,22 +24,28 @@ /* TYPES *******************************************************************/ #define MAX_QUANTUM 0x7F -/* GLOBALS ******************************************************************/ /* FUNCTIONS ****************************************************************/ -VOID INIT_FUNCTION -KeInitDpc(PKPCR Pcr) /* * FUNCTION: Initialize DPC handling */ +VOID +INIT_FUNCTION +KeInitDpc(PKPRCB Prcb) { - InitializeListHead(&Pcr->PrcbData.DpcData[0].DpcListHead); - KeInitializeEvent(Pcr->PrcbData.DpcEvent, 0, 0); - KeInitializeSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); - Pcr->PrcbData.MaximumDpcQueueDepth = 4; - Pcr->PrcbData.MinimumDpcRate = 3; - Pcr->PrcbData.DpcData[0].DpcQueueDepth = 0; + InitializeListHead(&Prcb->DpcData[0].DpcListHead); +#if 0 + /* + * FIXME: + * Prcb->DpcEvent is a NULL pointer. + */ + KeInitializeEvent(Prcb->DpcEvent, 0, 0); +#endif + KeInitializeSpinLock(&Prcb->DpcData[0].DpcLock); + Prcb->MaximumDpcQueueDepth = 4; + Prcb->MinimumDpcRate = 3; + Prcb->DpcData[0].DpcQueueDepth = 0; } /* @@ -47,9 +53,9 @@ KeInitDpc(PKPCR Pcr) */ VOID STDCALL -KeInitializeThreadedDpc(PKDPC Dpc, - PKDEFERRED_ROUTINE DeferredRoutine, - PVOID DeferredContext) +KeInitializeThreadedDpc(PKDPC Dpc, + PKDEFERRED_ROUTINE DeferredRoutine, + PVOID DeferredContext) /* * FUNCTION: * Initalizes a Threaded DPC and registers the DeferredRoutine for it. @@ -60,24 +66,18 @@ KeInitializeThreadedDpc(PKDPC Dpc, * NOTE: Callers can be running at any IRQL. */ { - DPRINT("Threaded DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine); - //Dpc->Type = KThreadedDpc; - Dpc->Number= 0; - Dpc->Importance= MediumImportance; - Dpc->DeferredRoutine = DeferredRoutine; - Dpc->DeferredContext = DeferredContext; - Dpc->DpcData = NULL; + DPRINT("Threaded DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine); + Dpc->Type = ThreadedDpcObject; + Dpc->Number= 0; + Dpc->Importance= MediumImportance; + Dpc->DeferredRoutine = DeferredRoutine; + Dpc->DeferredContext = DeferredContext; + Dpc->DpcData = NULL; } /* * @implemented - */ -VOID -STDCALL -KeInitializeDpc (PKDPC Dpc, - PKDEFERRED_ROUTINE DeferredRoutine, - PVOID DeferredContext) -/* + * * FUNCTION: * Initalizes a DPC and registers the DeferredRoutine for it. * ARGUMENTS: @@ -86,24 +86,24 @@ KeInitializeDpc (PKDPC Dpc, * DeferredContext = Parameter to be passed to the callback routine. * NOTE: Callers can be running at any IRQL. */ +VOID +STDCALL +KeInitializeDpc(PKDPC Dpc, + PKDEFERRED_ROUTINE DeferredRoutine, + PVOID DeferredContext) { - DPRINT("DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine); - Dpc->Type = KDpc; - Dpc->Number= 0; - Dpc->Importance= MediumImportance; - Dpc->DeferredRoutine = DeferredRoutine; - Dpc->DeferredContext = DeferredContext; - Dpc->DpcData = NULL; + DPRINT("DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine); + Dpc->Type = DpcObject; + Dpc->Number= 0; + Dpc->Importance= MediumImportance; + Dpc->DeferredRoutine = DeferredRoutine; + Dpc->DeferredContext = DeferredContext; + Dpc->DpcData = NULL; } /* * @implemented - */ -BOOLEAN STDCALL -KeInsertQueueDpc (PKDPC Dpc, - PVOID SystemArgument1, - PVOID SystemArgument2) -/* + * * FUNCTION: * Queues a DPC for execution when the IRQL of a processor * drops below DISPATCH_LEVEL @@ -155,124 +155,141 @@ KeInsertQueueDpc (PKDPC Dpc, * is greater that the target depth or the minimum DPC rate is less than the * target rate. */ +BOOLEAN +STDCALL +KeInsertQueueDpc(PKDPC Dpc, + PVOID SystemArgument1, + PVOID SystemArgument2) { - KIRQL OldIrql; - PKPCR Pcr; + KIRQL OldIrql; + PKPRCB Prcb; - DPRINT("KeInsertQueueDpc(DPC %x, SystemArgument1 %x, SystemArgument2 %x)\n", - Dpc, SystemArgument1, SystemArgument2); + DPRINT("KeInsertQueueDpc(DPC %x, SystemArgument1 %x, SystemArgument2 %x)\n", + Dpc, SystemArgument1, SystemArgument2); - /* Check IRQL and Raise it to HIGH_LEVEL */ - ASSERT(KeGetCurrentIrql()>=DISPATCH_LEVEL); - KeRaiseIrql(HIGH_LEVEL, &OldIrql); - - /* Check if this is a Thread DPC, which we don't support (yet) */ - //if (Dpc->Type == KThreadedDpc) { - // return FALSE; - // KeLowerIrql(OldIrql); - //} + /* Check IRQL and Raise it to HIGH_LEVEL */ + ASSERT(KeGetCurrentIrql()>=DISPATCH_LEVEL); + KeRaiseIrql(HIGH_LEVEL, &OldIrql); + + /* Check if this is a Thread DPC, which we don't support (yet) */ + if (Dpc->Type == ThreadedDpcObject) { + return FALSE; + KeLowerIrql(OldIrql); + } #ifdef CONFIG_SMP - /* Get the right PCR for this CPU */ - if (Dpc->Number >= MAXIMUM_PROCESSORS) { - ASSERT (Dpc->Number - MAXIMUM_PROCESSORS < KeNumberProcessors); - Pcr = (PKPCR)(KPCR_BASE + (Dpc->Number - MAXIMUM_PROCESSORS) * PAGE_SIZE); - } else { - ASSERT (Dpc->Number < KeNumberProcessors); - Pcr = KeGetCurrentKPCR(); - Dpc->Number = KeGetCurrentProcessorNumber(); - } - KiAcquireSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); + /* Get the right PCR for this CPU */ + if (Dpc->Number >= MAXIMUM_PROCESSORS) { + + ASSERT (Dpc->Number - MAXIMUM_PROCESSORS < KeNumberProcessors); + Prcb = ((PKPCR)((ULONG_PTR)KPCR_BASE + ((Dpc->Number - MAXIMUM_PROCESSORS) * PAGE_SIZE)))->Prcb; + + } else { + + ASSERT (Dpc->Number < KeNumberProcessors); + Prcb = KeGetCurrentPrcb(); + Dpc->Number = KeGetCurrentProcessorNumber(); + } + + KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock); #else - Pcr = (PKPCR)KPCR_BASE; + Prcb = ((PKPCR)KPCR_BASE)->Prcb; #endif - /* Get the DPC Data */ - if (InterlockedCompareExchangeUL(&Dpc->DpcData, &Pcr->PrcbData.DpcData[0].DpcLock, 0)) { - DPRINT("DPC Already Inserted"); + /* Get the DPC Data */ + if (InterlockedCompareExchangeUL(&Dpc->DpcData, &Prcb->DpcData[0].DpcLock, 0)) { + + DPRINT("DPC Already Inserted"); #ifdef CONFIG_SMP - KiReleaseSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); + KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock); #endif - KeLowerIrql(OldIrql); - return(FALSE); - } - - /* Make sure the lists are free if the Queue is 0 */ - if (Pcr->PrcbData.DpcData[0].DpcQueueDepth == 0) { - ASSERT(IsListEmpty(&Pcr->PrcbData.DpcData[0].DpcListHead)); - } else { - ASSERT(!IsListEmpty(&Pcr->PrcbData.DpcData[0].DpcListHead)); - } + KeLowerIrql(OldIrql); + return(FALSE); + } + + /* Make sure the lists are free if the Queue is 0 */ + if (Prcb->DpcData[0].DpcQueueDepth == 0) { + + ASSERT(IsListEmpty(&Prcb->DpcData[0].DpcListHead)); + } else { + + ASSERT(!IsListEmpty(&Prcb->DpcData[0].DpcListHead)); + } - /* Now we can play with the DPC safely */ - Dpc->SystemArgument1=SystemArgument1; - Dpc->SystemArgument2=SystemArgument2; - Pcr->PrcbData.DpcData[0].DpcQueueDepth++; - Pcr->PrcbData.DpcData[0].DpcCount++; - - /* Insert the DPC into the list. HighImportance DPCs go at the beginning */ - if (Dpc->Importance == HighImportance) { - InsertHeadList(&Pcr->PrcbData.DpcData[0].DpcListHead, &Dpc->DpcListEntry); - } else { - InsertTailList(&Pcr->PrcbData.DpcData[0].DpcListHead, &Dpc->DpcListEntry); - } - DPRINT("New DPC Added. Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink); + /* Now we can play with the DPC safely */ + Dpc->SystemArgument1=SystemArgument1; + Dpc->SystemArgument2=SystemArgument2; + Prcb->DpcData[0].DpcQueueDepth++; + Prcb->DpcData[0].DpcCount++; + + /* Insert the DPC into the list. HighImportance DPCs go at the beginning */ + if (Dpc->Importance == HighImportance) { + + InsertHeadList(&Prcb->DpcData[0].DpcListHead, &Dpc->DpcListEntry); + } else { + + InsertTailList(&Prcb->DpcData[0].DpcListHead, &Dpc->DpcListEntry); + } + DPRINT("New DPC Added. Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink); - /* Make sure a DPC isn't executing already and respect rules outlined above. */ - if ((!Pcr->PrcbData.DpcRoutineActive) && (!Pcr->PrcbData.DpcInterruptRequested)) { - -#ifdef CONFIG_SMP - /* Check if this is the same CPU */ - if (Pcr != KeGetCurrentKPCR()) { - /* Send IPI if High Importance */ - if ((Dpc->Importance == HighImportance) || - (Pcr->PrcbData.DpcData[0].DpcQueueDepth >= Pcr->PrcbData.MaximumDpcQueueDepth)) { - if (Dpc->Number >= MAXIMUM_PROCESSORS) { - KiIpiSendRequest(1 << (Dpc->Number - MAXIMUM_PROCESSORS), IPI_REQUEST_DPC); - } else { - KiIpiSendRequest(1 << Dpc->Number, IPI_REQUEST_DPC); - } + /* Make sure a DPC isn't executing already and respect rules outlined above. */ + if ((!Prcb->DpcRoutineActive) && (!Prcb->DpcInterruptRequested)) { + +#ifdef CONFIG_SMP + /* Check if this is the same CPU */ + if (Prcb != KeGetCurrentPrcb()) { + + /* Send IPI if High Importance */ + if ((Dpc->Importance == HighImportance) || + (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth)) { + + if (Dpc->Number >= MAXIMUM_PROCESSORS) { + + KiIpiSendRequest(1 << (Dpc->Number - MAXIMUM_PROCESSORS), IPI_REQUEST_DPC); + } else { + + KiIpiSendRequest(1 << Dpc->Number, IPI_REQUEST_DPC); + } - } - } else { - /* Request an Interrupt only if the DPC isn't low priority */ - if ((Dpc->Importance != LowImportance) || - (Pcr->PrcbData.DpcData[0].DpcQueueDepth >= Pcr->PrcbData.MaximumDpcQueueDepth) || - (Pcr->PrcbData.DpcRequestRate < Pcr->PrcbData.MinimumDpcRate)) { - - /* Request Interrupt */ - HalRequestSoftwareInterrupt(DISPATCH_LEVEL); - Pcr->PrcbData.DpcInterruptRequested = TRUE; - } - } + } + } else { + + /* Request an Interrupt only if the DPC isn't low priority */ + if ((Dpc->Importance != LowImportance) || + (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) || + (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) { + + /* Request Interrupt */ + HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + Prcb->DpcInterruptRequested = TRUE; + } + } #else - DPRINT("Requesting Interrupt. Importance: %x. QueueDepth: %x. MaxQueue: %x . RequestRate: %x. MinRate:%x \n", Dpc->Importance, Pcr->PrcbData.DpcData[0].DpcQueueDepth, Pcr->PrcbData.MaximumDpcQueueDepth, Pcr->PrcbData.DpcRequestRate, Pcr->PrcbData.MinimumDpcRate); - /* Request an Interrupt only if the DPC isn't low priority */ - if ((Dpc->Importance != LowImportance) || - (Pcr->PrcbData.DpcData[0].DpcQueueDepth >= Pcr->PrcbData.MaximumDpcQueueDepth) || - (Pcr->PrcbData.DpcRequestRate < Pcr->PrcbData.MinimumDpcRate)) { - - /* Request Interrupt */ - DPRINT("Requesting Interrupt\n"); - HalRequestSoftwareInterrupt(DISPATCH_LEVEL); - Pcr->PrcbData.DpcInterruptRequested = TRUE; - } + DPRINT("Requesting Interrupt. Importance: %x. QueueDepth: %x. MaxQueue: %x . RequestRate: %x. MinRate:%x \n", Dpc->Importance, Prcb->DpcData[0].DpcQueueDepth, Prcb->MaximumDpcQueueDepth, Prcb->DpcRequestRate, Prcb->MinimumDpcRate); + + /* Request an Interrupt only if the DPC isn't low priority */ + if ((Dpc->Importance != LowImportance) || + (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) || + (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) { + + /* Request Interrupt */ + DPRINT("Requesting Interrupt\n"); + HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + Prcb->DpcInterruptRequested = TRUE; + } #endif - } + } #ifdef CONFIG_SMP - KiReleaseSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); + KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock); #endif - /* Lower IRQL */ - KeLowerIrql(OldIrql); - return(TRUE); + /* Lower IRQL */ + KeLowerIrql(OldIrql); + return(TRUE); } /* * @implemented - */ -BOOLEAN STDCALL -KeRemoveQueueDpc (PKDPC Dpc) -/* + * * FUNCTION: * Removes DPC object from the system dpc queue * ARGUMENTS: @@ -281,33 +298,36 @@ KeRemoveQueueDpc (PKDPC Dpc) * TRUE if the DPC was in the queue * FALSE otherwise */ +BOOLEAN +STDCALL +KeRemoveQueueDpc(PKDPC Dpc) { - BOOLEAN WasInQueue; - KIRQL OldIrql; - - /* Raise IRQL */ - DPRINT("Removing DPC: %x\n", Dpc); - KeRaiseIrql(HIGH_LEVEL, &OldIrql); + BOOLEAN WasInQueue; + KIRQL OldIrql; + + /* Raise IRQL */ + DPRINT("Removing DPC: %x\n", Dpc); + KeRaiseIrql(HIGH_LEVEL, &OldIrql); #ifdef CONFIG_SMP - KiAcquireSpinLock(&((PKDPC_DATA)Dpc->DpcData)->DpcLock); + KiAcquireSpinLock(&((PKDPC_DATA)Dpc->DpcData)->DpcLock); #endif - - /* First make sure the DPC lock isn't being held */ - WasInQueue = Dpc->DpcData ? TRUE : FALSE; - if (Dpc->DpcData) { - - /* Remove the DPC */ - ((PKDPC_DATA)Dpc->DpcData)->DpcQueueDepth--; - RemoveEntryList(&Dpc->DpcListEntry); + + /* First make sure the DPC lock isn't being held */ + WasInQueue = Dpc->DpcData ? TRUE : FALSE; + if (Dpc->DpcData) { + + /* Remove the DPC */ + ((PKDPC_DATA)Dpc->DpcData)->DpcQueueDepth--; + RemoveEntryList(&Dpc->DpcListEntry); - } + } #ifdef CONFIG_SMP KiReleaseSpinLock(&((PKDPC_DATA)Dpc->DpcData)->DpcLock); #endif - /* Return if the DPC was in the queue or not */ - KeLowerIrql(OldIrql); - return WasInQueue; + /* Return if the DPC was in the queue or not */ + KeLowerIrql(OldIrql); + return WasInQueue; } /* @@ -323,7 +343,8 @@ KeFlushQueuedDpcs(VOID) * Called when deleting a Driver. */ { - if (KeGetCurrentKPCR()->PrcbData.DpcData[0].DpcQueueDepth) HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + /* Request an interrupt if needed */ + if (KeGetCurrentPrcb()->DpcData[0].DpcQueueDepth) HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } /* @@ -332,10 +353,11 @@ KeFlushQueuedDpcs(VOID) BOOLEAN STDCALL KeIsExecutingDpc( - VOID + VOID ) { - return KeGetCurrentKPCR()->PrcbData.DpcRoutineActive; + /* Return if the Dpc Routine is active */ + return KeGetCurrentPrcb()->DpcRoutineActive; } /* @@ -349,39 +371,41 @@ KeIsExecutingDpc( */ VOID STDCALL -KeSetImportanceDpc (IN PKDPC Dpc, - IN KDPC_IMPORTANCE Importance) +KeSetImportanceDpc (IN PKDPC Dpc, + IN KDPC_IMPORTANCE Importance) { - Dpc->Importance = Importance; + /* Set the DPC Importance */ + Dpc->Importance = Importance; } /* + * @implemented + * * FUNCTION: Specifies on which processor the DPC will run * ARGUMENTS: * Dpc = Initalizes DPC * Number = Processor number * RETURNS: None - * - * @implemented */ -VOID STDCALL -KeSetTargetProcessorDpc (IN PKDPC Dpc, - IN CCHAR Number) +VOID +STDCALL +KeSetTargetProcessorDpc(IN PKDPC Dpc, + IN CCHAR Number) { - if (Number >= MAXIMUM_PROCESSORS) - { - Dpc->Number = 0; - } - else - { - ASSERT(Number < KeNumberProcessors); - Dpc->Number = Number + MAXIMUM_PROCESSORS; - } + /* Check how many CPUs are on the system */ + if (Number >= MAXIMUM_PROCESSORS) { + + /* No CPU Number */ + Dpc->Number = 0; + + } else { + + /* Set the Number Specified */ + ASSERT(Number < KeNumberProcessors); + Dpc->Number = Number + MAXIMUM_PROCESSORS; + } } -VOID -STDCALL -KiQuantumEnd(VOID) /* * FUNCTION: * Called when a quantum end occurs to check if priority should be changed @@ -389,152 +413,178 @@ KiQuantumEnd(VOID) * NOTES: * Called when deleting a Driver. */ +VOID +STDCALL +KiQuantumEnd(VOID) { - PKPRCB Prcb; - PKTHREAD CurrentThread; - KIRQL OldIrql; - PKPROCESS Process; - KPRIORITY OldPriority; - KPRIORITY NewPriority; - - /* Lock dispatcher, get current thread */ - Prcb = &KeGetCurrentKPCR()->PrcbData; - CurrentThread = KeGetCurrentThread(); - OldIrql = KeRaiseIrqlToSynchLevel(); - - /* Get the Thread's Process */ - Process = CurrentThread->ApcState.Process; - - /* Set DPC Event if requested */ - if (Prcb->DpcSetEventRequest) { - KeSetEvent(Prcb->DpcEvent, 0, 0); - } - - /* Check if Quantum expired */ - if (CurrentThread->Quantum <= 0) { - /* Set the new Quantum */ - CurrentThread->Quantum = Process->ThreadQuantum; - - /* Calculate new priority */ - OldPriority = CurrentThread->Priority; - if (OldPriority < LOW_REALTIME_PRIORITY) { - NewPriority = OldPriority - CurrentThread->PriorityDecrement - 1; - if (NewPriority < CurrentThread->BasePriority) { - NewPriority = CurrentThread->BasePriority; - } - CurrentThread->PriorityDecrement = 0; - if (OldPriority != NewPriority) { - /* Set new Priority */ - CurrentThread->Priority = NewPriority; - } else { - /* Queue new thread if none is already */ - if (Prcb->NextThread == NULL) { - /* FIXME: Schedule a New Thread, when ROS will have NT Scheduler */ - } else { - /* Make the current thread non-premeptive if a new thread is queued */ - CurrentThread->Preempted = FALSE; - } - } - } else { - /* Set the Quantum back to Maximum */ - //if (CurrentThread->DisableQuantum) { - // CurrentThread->Quantum = MAX_QUANTUM; - //} - } - } - /* Dispatch the Thread */ - KeLowerIrql(DISPATCH_LEVEL); - PsDispatchThread(THREAD_STATE_READY); -} + PKPRCB Prcb; + PKTHREAD CurrentThread; + KIRQL OldIrql; + PKPROCESS Process; + KPRIORITY OldPriority; + KPRIORITY NewPriority; + + /* Lock dispatcher, get current thread */ + Prcb = KeGetCurrentPrcb(); + CurrentThread = KeGetCurrentThread(); + OldIrql = KeRaiseIrqlToSynchLevel(); + + /* Get the Thread's Process */ + Process = CurrentThread->ApcState.Process; + + /* Set DPC Event if requested */ + if (Prcb->DpcSetEventRequest) { + /* + * FIXME: + * Prcb->DpcEvent is not initialized. + */ + KEBUGCHECK(0); + KeSetEvent(Prcb->DpcEvent, 0, 0); + } + + /* Check if Quantum expired */ + if (CurrentThread->Quantum <= 0) { + /* Set the new Quantum */ + CurrentThread->Quantum = Process->ThreadQuantum; + + /* Calculate new priority */ + OldPriority = CurrentThread->Priority; + if (OldPriority < LOW_REALTIME_PRIORITY) { + + /* Set the New Priority and add the Priority Decrement */ + NewPriority = OldPriority - CurrentThread->PriorityDecrement - 1; + + /* Don't go out of bounds */ + if (NewPriority < CurrentThread->BasePriority) NewPriority = CurrentThread->BasePriority; + + /* Reset the priority decrement */ + CurrentThread->PriorityDecrement = 0; + + /* Set a new priority if needed */ + if (OldPriority != NewPriority) { + + /* Set new Priority */ + CurrentThread->Priority = NewPriority; + + } else { + + /* Queue new thread if none is already */ + if (Prcb->NextThread == NULL) { + + /* FIXME: Schedule a New Thread, when ROS will have NT Scheduler */ + + } else { + + /* Make the current thread non-premeptive if a new thread is queued */ + CurrentThread->Preempted = FALSE; + } + } + + + } else { + /* Set the Quantum back to Maximum */ + //if (CurrentThread->DisableQuantum) { + // CurrentThread->Quantum = MAX_QUANTUM; + //} + } + } + + /* Dispatch the Thread */ + KeLowerIrql(DISPATCH_LEVEL); + KiDispatchThread(THREAD_STATE_READY); +} /* * @implemented - */ -VOID -STDCALL -KiDispatchInterrupt(VOID) -/* + * * FUNCTION: * Called whenever a system interrupt is generated at DISPATCH_LEVEL. * It delivers queued DPCs and dispatches a new thread if need be. */ +VOID +STDCALL +KiDispatchInterrupt(VOID) { - PLIST_ENTRY DpcEntry; - PKDPC Dpc; - KIRQL OldIrql; - PKPCR Pcr; + PLIST_ENTRY DpcEntry; + PKDPC Dpc; + KIRQL OldIrql; + PKPRCB Prcb; - DPRINT("Dispatching Interrupts\n"); - ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + DPRINT("Dispatching Interrupts\n"); + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); - /* Set DPC Deliver to Active */ - Pcr = KeGetCurrentKPCR(); + /* Set DPC Deliver to Active */ + Prcb = KeGetCurrentPrcb(); - if (Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0) { - /* Raise IRQL */ - KeRaiseIrql(HIGH_LEVEL, &OldIrql); -#ifdef CONFIG_SMP - KiAcquireSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); + if (Prcb->DpcData[0].DpcQueueDepth > 0) { + /* Raise IRQL */ + KeRaiseIrql(HIGH_LEVEL, &OldIrql); +#ifdef CONFIG_SMP + KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock); #endif - Pcr->PrcbData.DpcRoutineActive = TRUE; + Prcb->DpcRoutineActive = TRUE; - DPRINT("&Pcr->PrcbData.DpcData[0].DpcListHead: %x\n", &Pcr->PrcbData.DpcData[0].DpcListHead); - /* Loop while we have entries */ - while (!IsListEmpty(&Pcr->PrcbData.DpcData[0].DpcListHead)) { - ASSERT(Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0); - DPRINT("Queue Depth: %x\n", Pcr->PrcbData.DpcData[0].DpcQueueDepth); - - /* Get the DPC call it */ - DpcEntry = RemoveHeadList(&Pcr->PrcbData.DpcData[0].DpcListHead); - Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry); - DPRINT("Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink); - Dpc->DpcData = NULL; - Pcr->PrcbData.DpcData[0].DpcQueueDepth--; + DPRINT("&Prcb->DpcData[0].DpcListHead: %x\n", &Prcb->DpcData[0].DpcListHead); + /* Loop while we have entries */ + while (!IsListEmpty(&Prcb->DpcData[0].DpcListHead)) { + + ASSERT(Prcb->DpcData[0].DpcQueueDepth > 0); + DPRINT("Queue Depth: %x\n", Prcb->DpcData[0].DpcQueueDepth); + + /* Get the DPC call it */ + DpcEntry = RemoveHeadList(&Prcb->DpcData[0].DpcListHead); + Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry); + DPRINT("Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink); + Dpc->DpcData = NULL; + Prcb->DpcData[0].DpcQueueDepth--; #ifdef CONFIG_SMP - KiReleaseSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); + KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock); #endif - /* Disable/Enabled Interrupts and Call the DPC */ - KeLowerIrql(OldIrql); - DPRINT("Calling DPC: %x\n", Dpc); - Dpc->DeferredRoutine(Dpc, - Dpc->DeferredContext, - Dpc->SystemArgument1, - Dpc->SystemArgument2); - KeRaiseIrql(HIGH_LEVEL, &OldIrql); - + /* Disable/Enabled Interrupts and Call the DPC */ + KeLowerIrql(OldIrql); + DPRINT("Calling DPC: %x\n", Dpc); + Dpc->DeferredRoutine(Dpc, + Dpc->DeferredContext, + Dpc->SystemArgument1, + Dpc->SystemArgument2); + KeRaiseIrql(HIGH_LEVEL, &OldIrql); + #ifdef CONFIG_SMP - KiAcquireSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); - /* - * If the dpc routine drops the irql below DISPATCH_LEVEL, - * a thread switch can occur and after the next thread switch - * the execution may start on an other processor. - */ - if (Pcr != KeGetCurrentKPCR()) { - Pcr->PrcbData.DpcRoutineActive = FALSE; - KiReleaseSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); - Pcr = KeGetCurrentKPCR(); - KiAcquireSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); - Pcr->PrcbData.DpcRoutineActive = TRUE; - } + KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock); + /* + * If the dpc routine drops the irql below DISPATCH_LEVEL, + * a thread switch can occur and after the next thread switch + * the execution may start on an other processor. + */ + if (Prcb != KeGetCurrentPrcb()) { + + Prcb->DpcRoutineActive = FALSE; + KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock); + Prcb = KeGetCurrentPrcb(); + KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock); + Prcb->DpcRoutineActive = TRUE; + } #endif - } - /* Clear DPC Flags */ - Pcr->PrcbData.DpcRoutineActive = FALSE; - Pcr->PrcbData.DpcInterruptRequested = FALSE; + } + /* Clear DPC Flags */ + Prcb->DpcRoutineActive = FALSE; + Prcb->DpcInterruptRequested = FALSE; #ifdef CONFIG_SMP - KiReleaseSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock); + KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock); #endif - - /* DPC Dispatching Ended, re-enable interrupts */ - KeLowerIrql(OldIrql); - } - - DPRINT("Checking for Quantum End\n"); - /* If we have Quantum End, call the function */ - if (Pcr->PrcbData.QuantumEnd) { - Pcr->PrcbData.QuantumEnd = FALSE; - KiQuantumEnd(); - } + + /* DPC Dispatching Ended, re-enable interrupts */ + KeLowerIrql(OldIrql); + } + + DPRINT("Checking for Quantum End\n"); + + /* If we have Quantum End, call the function */ + if (Prcb->QuantumEnd) { + + Prcb->QuantumEnd = FALSE; + KiQuantumEnd(); + } } /* EOF */ diff --git a/reactos/ntoskrnl/ke/error.c b/reactos/ntoskrnl/ke/error.c deleted file mode 100644 index d6172aa1687..00000000000 --- a/reactos/ntoskrnl/ke/error.c +++ /dev/null @@ -1,98 +0,0 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ke/error.c - * PURPOSE: Error reason setting/getting - * - * PROGRAMMERS: David Welch - */ - -/* INCLUDE *****************************************************************/ - -#include -#include - -/* FUNCTIONS ***************************************************************/ - -BOOLEAN ExReadyForErrors = FALSE; -PEPORT ExpDefaultErrorPort = NULL; -PEPROCESS ExpDefaultErrorPortProcess = NULL; - -/* - * @unimplemented - */ -VOID -STDCALL -KiCoprocessorError( - VOID -) -{ - UNIMPLEMENTED; -} - -/* - * @unimplemented - */ -VOID -STDCALL -KiUnexpectedInterrupt( - VOID -) -{ - UNIMPLEMENTED; -} - -NTSTATUS STDCALL -NtRaiseHardError(IN NTSTATUS ErrorStatus, - IN ULONG NumberOfParameters, - IN PUNICODE_STRING UnicodeStringParameterMask OPTIONAL, - IN PVOID *Parameters, - IN HARDERROR_RESPONSE_OPTION ResponseOption, - OUT PHARDERROR_RESPONSE Response) -{ - DPRINT1("Hard error %x\n", ErrorStatus); - return(STATUS_SUCCESS); -} - -NTSTATUS STDCALL -NtSetDefaultHardErrorPort(IN HANDLE PortHandle) -{ - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; - - PreviousMode = ExGetPreviousMode(); - - if(!SeSinglePrivilegeCheck(SeTcbPrivilege, - PreviousMode)) - { - DPRINT1("NtSetDefaultHardErrorPort: Caller requires the SeTcbPrivilege privilege!\n"); - return STATUS_PRIVILEGE_NOT_HELD; - } - - /* serialization shouldn't be required here as it usually is just called once - during startup */ - - if(!ExReadyForErrors) - { - Status = ObReferenceObjectByHandle(PortHandle, - 0, - LpcPortObjectType, - PreviousMode, - (PVOID*)&ExpDefaultErrorPort, - NULL); - if(NT_SUCCESS(Status)) - { - ExpDefaultErrorPortProcess = PsGetCurrentProcess(); - ExReadyForErrors = TRUE; - } - } - else - { - Status = STATUS_UNSUCCESSFUL; - } - - return Status; -} - -/* EOF */ diff --git a/reactos/ntoskrnl/ke/event.c b/reactos/ntoskrnl/ke/event.c index 5ef0e626bf4..6dd65338c36 100644 --- a/reactos/ntoskrnl/ke/event.c +++ b/reactos/ntoskrnl/ke/event.c @@ -1,11 +1,12 @@ -/* $Id$ +/* * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/event.c * PURPOSE: Implements events * - * PROGRAMMERS: David Welch (welch@mcmail.com) + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + * David Welch (welch@mcmail.com) */ /* INCLUDES *****************************************************************/ @@ -19,121 +20,211 @@ /* * @implemented */ -VOID STDCALL KeClearEvent (PKEVENT Event) +VOID +STDCALL +KeClearEvent(PKEVENT Event) { - DPRINT("KeClearEvent(Event %x)\n", Event); - Event->Header.SignalState = FALSE; + DPRINT("KeClearEvent(Event %x)\n", Event); + + /* Reset Signal State */ + Event->Header.SignalState = FALSE; } /* * @implemented */ -VOID STDCALL KeInitializeEvent (PKEVENT Event, - EVENT_TYPE Type, - BOOLEAN State) +VOID +STDCALL +KeInitializeEvent(PKEVENT Event, + EVENT_TYPE Type, + BOOLEAN State) { - ULONG IType; - - if (Type == NotificationEvent) - { - IType = InternalNotificationEvent; - } - else if (Type == SynchronizationEvent) - { - IType = InternalSynchronizationEvent; - } - else - { - ASSERT(FALSE); - return; - } - - KeInitializeDispatcherHeader(&(Event->Header), - IType, - sizeof(Event)/sizeof(ULONG),State); - InitializeListHead(&(Event->Header.WaitListHead)); + DPRINT("KeInitializeEvent(Event %x)\n", Event); + + /* Initialize the Dispatcher Header */ + KeInitializeDispatcherHeader(&Event->Header, + Type, + sizeof(Event) / sizeof(ULONG), + State); } /* * @implemented */ -LONG STDCALL KeReadStateEvent (PKEVENT Event) +VOID +STDCALL +KeInitializeEventPair(PKEVENT_PAIR EventPair) { - return(Event->Header.SignalState); -} - -/* - * @implemented - */ -LONG STDCALL KeResetEvent (PKEVENT Event) -{ - /* FIXME: must use interlocked func. everywhere! (wait.c) - * or use dispather lock instead - * -Gunnar */ - return(InterlockedExchange(&(Event->Header.SignalState),0)); -} - -/* - * @implemented - */ -LONG STDCALL KeSetEvent (PKEVENT Event, - KPRIORITY Increment, - BOOLEAN Wait) -{ - KIRQL OldIrql; - int ret; - - DPRINT("KeSetEvent(Event %x, Wait %x)\n",Event,Wait); - - OldIrql = KeAcquireDispatcherDatabaseLock(); - - ret = InterlockedExchange(&Event->Header.SignalState,1); - - KiDispatcherObjectWake(&Event->Header, Increment); - - if (Wait == FALSE) - { - KeReleaseDispatcherDatabaseLock(OldIrql); - } - else - { - KTHREAD *Thread = KeGetCurrentThread(); - Thread->WaitNext = TRUE; - Thread->WaitIrql = OldIrql; - } - - return(ret); + DPRINT("KeInitializeEventPair(Event %x)\n", EventPair); + + /* Initialize the Event Pair Type and Size */ + EventPair->Type = EventPairObject; + EventPair->Size = sizeof(KEVENT_PAIR); + + /* Initialize the two Events */ + KeInitializeEvent(&EventPair->LowEvent, SynchronizationEvent, FALSE); + KeInitializeEvent(&EventPair->HighEvent, SynchronizationEvent, FALSE); } /* * @implemented */ LONG STDCALL -KePulseEvent (IN PKEVENT Event, - IN KPRIORITY Increment, - IN BOOLEAN Wait) +KePulseEvent(IN PKEVENT Event, + IN KPRIORITY Increment, + IN BOOLEAN Wait) { - KIRQL OldIrql; - LONG Ret; + KIRQL OldIrql; + LONG PreviousState; - DPRINT("KePulseEvent(Event %x, Wait %x)\n",Event,Wait); - OldIrql = KeAcquireDispatcherDatabaseLock(); - Ret = InterlockedExchange(&Event->Header.SignalState,1); - KiDispatcherObjectWake(&Event->Header, Increment); - InterlockedExchange(&(Event->Header.SignalState),0); - - if (Wait == FALSE) - { - KeReleaseDispatcherDatabaseLock(OldIrql); + DPRINT("KePulseEvent(Event %x, Wait %x)\n",Event,Wait); + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save the Old State */ + PreviousState = Event->Header.SignalState; + + /* Check if we are non-signaled and we have stuff in the Wait Queue */ + if (!PreviousState && !IsListEmpty(&Event->Header.WaitListHead)) { + + /* Set the Event to Signaled */ + Event->Header.SignalState = 1; + + /* Wake the Event */ + KiWaitTest(&Event->Header, Increment); } - else - { - KTHREAD *Thread = KeGetCurrentThread(); - Thread->WaitNext = TRUE; - Thread->WaitIrql = OldIrql; + + /* Unsignal it */ + Event->Header.SignalState = 0; + + /* Check what wait state was requested */ + if (Wait == FALSE) { + + /* Wait not requested, release Dispatcher Database and return */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + } else { + + /* Return Locked and with a Wait */ + KTHREAD *Thread = KeGetCurrentThread(); + Thread->WaitNext = TRUE; + Thread->WaitIrql = OldIrql; } - return Ret; + /* Return the previous State */ + return PreviousState; +} + +/* + * @implemented + */ +LONG +STDCALL +KeReadStateEvent(PKEVENT Event) +{ + /* Return the Signal State */ + return Event->Header.SignalState; +} + +/* + * @implemented + */ +LONG +STDCALL +KeResetEvent(PKEVENT Event) +{ + KIRQL OldIrql; + LONG PreviousState; + + DPRINT("KeResetEvent(Event %x)\n",Event); + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save the Previous State */ + PreviousState = Event->Header.SignalState; + + /* Set it to zero */ + Event->Header.SignalState = 0; + + /* Release Dispatcher Database and return previous state */ + KeReleaseDispatcherDatabaseLock(OldIrql); + return PreviousState; +} + +/* + * @implemented + */ +LONG +STDCALL +KeSetEvent(PKEVENT Event, + KPRIORITY Increment, + BOOLEAN Wait) +{ + KIRQL OldIrql; + LONG PreviousState; + PKWAIT_BLOCK WaitBlock; + + DPRINT("KeSetEvent(Event %x, Wait %x)\n",Event,Wait); + + /* Lock the Dispathcer Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save the Previous State */ + PreviousState = Event->Header.SignalState; + + /* Check if we have stuff in the Wait Queue */ + if (IsListEmpty(&Event->Header.WaitListHead)) { + + /* Set the Event to Signaled */ + DPRINT("Empty Wait Queue, Signal the Event\n"); + Event->Header.SignalState = 1; + + } else { + + /* Get the Wait Block */ + WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink, + KWAIT_BLOCK, + WaitListEntry); + + + /* Check the type of event */ + if (Event->Header.Type == NotificationEvent || WaitBlock->WaitType == WaitAll) { + + if (PreviousState == 0) { + + /* We must do a full wait satisfaction */ + DPRINT("Notification Event or WaitAll, Wait on the Event and Signal\n"); + Event->Header.SignalState = 1; + KiWaitTest(&Event->Header, Increment); + } + + } else { + + /* We can satisfy wait simply by waking the thread, since our signal state is 0 now */ + DPRINT("WaitAny or Sync Event, just unwait the thread\n"); + KiAbortWaitThread(WaitBlock->Thread, WaitBlock->WaitKey, Increment); + } + } + + /* Check what wait state was requested */ + if (Wait == FALSE) { + + /* Wait not requested, release Dispatcher Database and return */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + } else { + + /* Return Locked and with a Wait */ + KTHREAD *Thread = KeGetCurrentThread(); + Thread->WaitNext = TRUE; + Thread->WaitIrql = OldIrql; + } + + /* Return the previous State */ + DPRINT("Done: %d\n", PreviousState); + return PreviousState; } /* @@ -141,27 +232,39 @@ KePulseEvent (IN PKEVENT Event, */ VOID STDCALL -KeSetEventBoostPriority( - IN PKEVENT Event, - IN PKTHREAD *Thread OPTIONAL -) +KeSetEventBoostPriority(IN PKEVENT Event, + IN PKTHREAD *Thread OPTIONAL) { - PKTHREAD WaitingThread; - KIRQL OldIrql; - - OldIrql = KeAcquireDispatcherDatabaseLock(); + PKTHREAD WaitingThread; + KIRQL OldIrql; + + DPRINT("KeSetEventBoostPriority(Event %x, Thread %x)\n",Event,Thread); + + /* Acquire Dispatcher Database Lock */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* If our wait list is empty, then signal the event and return */ + if (IsListEmpty(&Event->Header.WaitListHead)) { + + Event->Header.SignalState = 1; + + } else { + + /* Get Thread that is currently waiting. First get the Wait Block, then the Thread */ + WaitingThread = CONTAINING_RECORD(Event->Header.WaitListHead.Flink, + KWAIT_BLOCK, + WaitListEntry)->Thread; + + /* Return it to caller if requested */ + if ARGUMENT_PRESENT(Thread) *Thread = WaitingThread; + + /* Reset the Quantum and Unwait the Thread */ + WaitingThread->Quantum = WaitingThread->ApcState.Process->ThreadQuantum; + KiAbortWaitThread(WaitingThread, STATUS_SUCCESS, EVENT_INCREMENT); + } - /* Get Thread that is currently waiting. First get the Wait Block, then the Thread */ - WaitingThread = CONTAINING_RECORD(Event->Header.WaitListHead.Flink, KWAIT_BLOCK, WaitListEntry)->Thread; - - /* Return it to caller if requested */ - if ARGUMENT_PRESENT(Thread) *Thread = WaitingThread; - - /* Reset the Quantum and Unwait the Thread */ - WaitingThread->Quantum = WaitingThread->ApcState.Process->ThreadQuantum; - KiAbortWaitThread(WaitingThread, STATUS_SUCCESS); - - KeReleaseDispatcherDatabaseLock(OldIrql); + /* Release the Dispatcher Database Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); } /* EOF */ diff --git a/reactos/ntoskrnl/ke/i386/brkpoint.c b/reactos/ntoskrnl/ke/i386/brkpoint.c index e2c668a34db..c967faec31e 100644 --- a/reactos/ntoskrnl/ke/i386/brkpoint.c +++ b/reactos/ntoskrnl/ke/i386/brkpoint.c @@ -34,10 +34,10 @@ DbgBreakPoint(VOID) * @implemented */ #if defined(__GNUC__) -__asm__(".globl _DbgBreakPointNoBugCheck@0\n\t" - "_DbgBreakPointNoBugCheck@0:\n\t" - "int $3\n\t" - "ret\n\t"); + __asm__(".globl _DbgBreakPointNoBugCheck@0\n\t" + "_DbgBreakPointNoBugCheck@0:\n\t" + "int $3\n\t" + "ret\n\t"); #endif /* diff --git a/reactos/ntoskrnl/ke/i386/exp.c b/reactos/ntoskrnl/ke/i386/exp.c index 5f8c9a8751c..e907280ba75 100644 --- a/reactos/ntoskrnl/ke/i386/exp.c +++ b/reactos/ntoskrnl/ke/i386/exp.c @@ -1,9 +1,9 @@ -/* +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/i386/exp.c * PURPOSE: Handling exceptions - * + * * PROGRAMMERS: David Welch (welch@cwcom.net) * Skywing (skywing@valhallalegends.com) */ @@ -120,24 +120,31 @@ KeRosPrintAddress(PVOID address) MODULE_TEXT_SECTION* current; extern LIST_ENTRY ModuleTextListHead; ULONG_PTR RelativeAddress; + ULONG i = 0; - current_entry = ModuleTextListHead.Flink; + do + { + current_entry = ModuleTextListHead.Flink; - while (current_entry != &ModuleTextListHead && - current_entry != NULL) - { - current = - CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry); + while (current_entry != &ModuleTextListHead && + current_entry != NULL) + { + current = + CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry); + + if (address >= (PVOID)current->Base && + address < (PVOID)(current->Base + current->Length)) + { + RelativeAddress = (ULONG_PTR) address - current->Base; + DbgPrint("<%ws: %x>", current->Name, RelativeAddress); + return(TRUE); + } + current_entry = current_entry->Flink; + } + + address = (PVOID)((ULONG_PTR)address & ~0xC0000000); + } while(++i <= 1); - if (address >= (PVOID)current->Base && - address < (PVOID)(current->Base + current->Length)) - { - RelativeAddress = (ULONG_PTR) address - current->Base; - DbgPrint("<%ws: %x>", current->Name, RelativeAddress); - return(TRUE); - } - current_entry = current_entry->Flink; - } return(FALSE); } #endif /* KDBG */ @@ -509,6 +516,21 @@ KiTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr) ExceptionNr = 12; } + if (ExceptionNr == 15) + { + /* + * FIXME: + * This exception should never occur. The P6 has a bug, which does sometimes deliver + * the apic spurious interrupt as exception 15. On an athlon64, I get one exception + * in the early boot phase in apic mode (using the smp build). I've looked to the linux + * sources. Linux does ignore this exception. + * + * Hartmut Birr + */ + DPRINT1("Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); + return(0); + } + /* * Maybe handle the page fault and return */ @@ -566,6 +588,125 @@ KiTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr) } } +VOID +KeContextToTrapFrame(PCONTEXT Context, + PKTRAP_FRAME TrapFrame) +{ + if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) + { + TrapFrame->Esp = Context->Esp; + TrapFrame->Ss = Context->SegSs; + TrapFrame->Cs = Context->SegCs; + TrapFrame->Eip = Context->Eip; + TrapFrame->Eflags = Context->EFlags; + TrapFrame->Ebp = Context->Ebp; + } + if ((Context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) + { + TrapFrame->Eax = Context->Eax; + TrapFrame->Ebx = Context->Ebx; + TrapFrame->Ecx = Context->Ecx; + TrapFrame->Edx = Context->Edx; + TrapFrame->Esi = Context->Esi; + TrapFrame->Edi = Context->Edi; + } + if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) + { + TrapFrame->Ds = Context->SegDs; + TrapFrame->Es = Context->SegEs; + TrapFrame->Fs = Context->SegFs; + TrapFrame->Gs = Context->SegGs; + } + if ((Context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) + { + /* + * Not handled + * + * This should be handled separately I think. + * - blight + */ + } + if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS) + { + /* + * Not handled + */ + } +} + +VOID +KeTrapFrameToContext(PKTRAP_FRAME TrapFrame, + PCONTEXT Context) +{ + if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) + { + Context->SegSs = TrapFrame->Ss; + Context->Esp = TrapFrame->Esp; + Context->SegCs = TrapFrame->Cs; + Context->Eip = TrapFrame->Eip; + Context->EFlags = TrapFrame->Eflags; + Context->Ebp = TrapFrame->Ebp; + } + if ((Context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) + { + Context->Eax = TrapFrame->Eax; + Context->Ebx = TrapFrame->Ebx; + Context->Ecx = TrapFrame->Ecx; + /* + * NOTE: In the trap frame which is built on entry to a system + * call TrapFrame->Edx will actually hold the address of the + * previous TrapFrame. I don't believe leaking this information + * has security implications. Also EDX holds the address of the + * arguments to the system call in progress so it isn't of much + * interest to the debugger. + */ + Context->Edx = TrapFrame->Edx; + Context->Esi = TrapFrame->Esi; + Context->Edi = TrapFrame->Edi; + } + if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) + { + Context->SegDs = TrapFrame->Ds; + Context->SegEs = TrapFrame->Es; + Context->SegFs = TrapFrame->Fs; + Context->SegGs = TrapFrame->Gs; + } + if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS) + { + /* + * FIXME: Implement this case + */ + Context->ContextFlags &= (~CONTEXT_DEBUG_REGISTERS) | CONTEXT_i386; + } + if ((Context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) + { + /* + * FIXME: Implement this case + * + * I think this should only be filled for FPU exceptions, otherwise I + * would not know where to get it from as it can be the current state + * of the FPU or already saved in the thread's FPU save area. + * -blight + */ + Context->ContextFlags &= (~CONTEXT_FLOATING_POINT) | CONTEXT_i386; + } +#if 0 + if ((Context->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) + { + /* + * FIXME: Investigate this + * + * This is the XMM state (first 512 bytes of FXSAVE_FORMAT/FX_SAVE_AREA) + * This should only be filled in case of a SIMD exception I think, so + * this is not the right place (like for FPU the state could already be + * saved in the thread's FX_SAVE_AREA or still be in the CPU) + * -blight + */ + Context->ContextFlags &= ~CONTEXT_EXTENDED_REGISTERS; + } +#endif +} + VOID KeDumpStackFrames(PULONG Frame) { @@ -807,7 +948,7 @@ KeRaiseUserException(IN NTSTATUS ExceptionCode) } _SEH_HANDLE { return(ExceptionCode); } _SEH_END; - + OldEip = Thread->TrapFrame->Eip; Thread->TrapFrame->Eip = (ULONG_PTR)LdrpGetSystemDllRaiseExceptionDispatcher(); return((NTSTATUS)OldEip); @@ -838,7 +979,7 @@ NtRaiseException ( /* Restore the user context */ Thread->TrapFrame = PrevTrapFrame; __asm__("mov %%ebx, %%esp;\n" "jmp _KiServiceExit": : "b" (TrapFrame)); - + /* We never get here */ return(STATUS_SUCCESS); } diff --git a/reactos/ntoskrnl/ke/i386/fpu.c b/reactos/ntoskrnl/ke/i386/fpu.c index cb94a7078af..59195b40c72 100644 --- a/reactos/ntoskrnl/ke/i386/fpu.c +++ b/reactos/ntoskrnl/ke/i386/fpu.c @@ -228,7 +228,7 @@ KiCheckFPU(VOID) unsigned short int status; int cr0; ULONG Flags; - PKPCR Pcr = KeGetCurrentKPCR(); + PKPRCB Prcb = KeGetCurrentPrcb(); Ke386SaveFlags(Flags); Ke386DisableInterrupts(); @@ -275,7 +275,7 @@ KiCheckFPU(VOID) HardwareMathSupport = 1; /* check for and enable MMX/SSE support if possible */ - if ((Pcr->PrcbData.FeatureBits & X86_FEATURE_FXSR) != 0) + if ((Prcb->FeatureBits & X86_FEATURE_FXSR) != 0) { BYTE DummyArea[sizeof(FX_SAVE_AREA) + 15]; PFX_SAVE_AREA FxSaveArea; @@ -300,7 +300,7 @@ KiCheckFPU(VOID) } } /* FIXME: Check for SSE3 in Ke386CpuidFlags2! */ - if (Pcr->PrcbData.FeatureBits & (X86_FEATURE_SSE | X86_FEATURE_SSE2)) + if (Prcb->FeatureBits & (X86_FEATURE_SSE | X86_FEATURE_SSE2)) { Ke386SetCr4(Ke386GetCr4() | X86_CR4_OSXMMEXCPT); @@ -401,7 +401,7 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) CurrentThread = KeGetCurrentThread(); #ifndef CONFIG_SMP - NpxThread = KeGetCurrentKPCR()->PrcbData.NpxThread; + NpxThread = KeGetCurrentPrcb()->NpxThread; #endif ASSERT(CurrentThread != NULL); @@ -414,7 +414,7 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) /* save the FPU state into the owner's save area */ if (NpxThread != NULL) { - KeGetCurrentKPCR()->PrcbData.NpxThread = NULL; + KeGetCurrentPrcb()->NpxThread = NULL; FxSaveArea = (PFX_SAVE_AREA)((char *)NpxThread->InitialStack - sizeof (FX_SAVE_AREA)); /* the fnsave might raise a delayed #MF exception */ if (FxsrSupport) @@ -463,7 +463,7 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) asm volatile("finit"); } } - KeGetCurrentKPCR()->PrcbData.NpxThread = CurrentThread; + KeGetCurrentPrcb()->NpxThread = CurrentThread; #ifndef CONFIG_SMP } #endif @@ -487,7 +487,7 @@ KiHandleFpuFault(PKTRAP_FRAME Tf, ULONG ExceptionNr) KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); - NpxThread = KeGetCurrentKPCR()->PrcbData.NpxThread; + NpxThread = KeGetCurrentPrcb()->NpxThread; CurrentThread = KeGetCurrentThread(); if (NpxThread == NULL) { diff --git a/reactos/ntoskrnl/ke/i386/irq.c b/reactos/ntoskrnl/ke/i386/irq.c index cf90d44695f..22d269b69ac 100644 --- a/reactos/ntoskrnl/ke/i386/irq.c +++ b/reactos/ntoskrnl/ke/i386/irq.c @@ -315,7 +315,7 @@ KiInterruptDispatch (ULONG vector, PKIRQ_TRAPFRAME Trapframe) * the PIC. */ - KeGetCurrentKPCR()->PrcbData.InterruptCount++; + KeGetCurrentPrcb()->InterruptCount++; /* * Notify the rest of the kernel of the raised irq level. For the @@ -340,9 +340,6 @@ KiInterruptDispatch (ULONG vector, PKIRQ_TRAPFRAME Trapframe) { KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame); KeUpdateSystemTime(&KernelTrapFrame, old_level); -#if defined(KDBG) || defined(DBG) - KdbProfileInterrupt(Trapframe->Eip); -#endif /* KDBG */ } else #endif diff --git a/reactos/ntoskrnl/ke/i386/kernel.c b/reactos/ntoskrnl/ke/i386/kernel.c index 37852a5f0e7..1d07b534158 100644 --- a/reactos/ntoskrnl/ke/i386/kernel.c +++ b/reactos/ntoskrnl/ke/i386/kernel.c @@ -18,7 +18,7 @@ ULONG KiPcrInitDone = 0; static ULONG PcrsAllocated = 0; -static ULONG Ke386CpuidFlags2, Ke386CpuidExFlags; +static ULONG Ke386CpuidFlags2, Ke386CpuidExFlags, Ke386CpuidExMisc; ULONG Ke386CacheAlignment; CHAR Ke386CpuidModel[49] = {0,}; ULONG Ke386L1CacheSize; @@ -34,7 +34,7 @@ Ki386GetCpuId(VOID) { ULONG OrigFlags, Flags, FinalFlags; ULONG MaxCpuidLevel; - ULONG Dummy, Eax, Ebx, Ecx, Edx; + ULONG Dummy, Eax, Ecx, Edx; PKPCR Pcr = KeGetCurrentKPCR(); Ke386CpuidFlags2 = Ke386CpuidExFlags = 0; @@ -45,6 +45,10 @@ Ki386GetCpuId(VOID) Flags = OrigFlags ^ X86_EFLAGS_ID; Ke386RestoreFlags(Flags); Ke386SaveFlags(FinalFlags); + + Pcr->PrcbData.LogicalProcessorsPerPhysicalProcessor = 1; + Pcr->PrcbData.InitialApicId = 0xff; + if ((OrigFlags & X86_EFLAGS_ID) == (FinalFlags & X86_EFLAGS_ID)) { /* No cpuid supported. */ @@ -59,14 +63,24 @@ Ki386GetCpuId(VOID) if (MaxCpuidLevel > 0) { /* Get the feature flags. */ - Ki386Cpuid(1, &Eax, &Ebx, &Ke386CpuidFlags2, &Pcr->PrcbData.FeatureBits); + Ki386Cpuid(1, &Eax, &Ke386CpuidExMisc, &Ke386CpuidFlags2, &Pcr->PrcbData.FeatureBits); /* Get the cache alignment, if it is available */ if (Pcr->PrcbData.FeatureBits & (1<<19)) { - Ke386CacheAlignment = ((Ebx >> 8) & 0xff) * 8; + Ke386CacheAlignment = ((Ke386CpuidExMisc >> 8) & 0xff) * 8; } Pcr->PrcbData.CpuType = (Eax >> 8) & 0xf; Pcr->PrcbData.CpuStep = (Eax & 0xf) | ((Eax << 4) & 0xf00); + + Pcr->PrcbData.InitialApicId = (Ke386CpuidExMisc >> 24) & 0xff; + + /* detect Hyper-Threading on Pentium 4 CPUs or later */ + if ((Pcr->PrcbData.CpuType == 0xf || (Eax & 0x0f00000)) && + !strncmp(Pcr->PrcbData.VendorString, "GenuineIntel", 12) && + Pcr->PrcbData.FeatureBits & X86_FEATURE_HT) + { + Pcr->PrcbData.LogicalProcessorsPerPhysicalProcessor = (Ke386CpuidExMisc >> 16) & 0xff; + } } else { @@ -132,6 +146,7 @@ KePrepareForApplicationProcessorInit(ULONG Id) Pcr->ProcessorNumber = Id; Pcr->Tib.Self = &Pcr->Tib; Pcr->Self = Pcr; + Pcr->Prcb = &Pcr->PrcbData; Pcr->Irql = SYNCH_LEVEL; Pcr->PrcbData.MHz = BootPcr->PrcbData.MHz; @@ -172,7 +187,7 @@ KeApplicationProcessorInit(VOID) /* Check FPU/MMX/SSE support. */ KiCheckFPU(); - KeInitDpc(Pcr); + KeInitDpc(Pcr->Prcb); if (Pcr->PrcbData.FeatureBits & X86_FEATURE_SYSCALL) { @@ -224,6 +239,7 @@ KeInit1(PCHAR CommandLine, PULONG LastKernelAddress) KPCR = (PKPCR)KPCR_BASE; memset(KPCR, 0, PAGE_SIZE); KPCR->Self = KPCR; + KPCR->Prcb = &KPCR->PrcbData; KPCR->Irql = SYNCH_LEVEL; KPCR->Tib.Self = &KPCR->Tib; KPCR->GDT = KiBootGdt; @@ -246,7 +262,7 @@ KeInit1(PCHAR CommandLine, PULONG LastKernelAddress) /* Mark the end of the exception handler list */ KPCR->Tib.ExceptionList = (PVOID)-1; - KeInitDpc(KPCR); + KeInitDpc(KPCR->Prcb); KeInitExceptions (); KeInitInterrupts (); @@ -339,7 +355,7 @@ KeInit2(VOID) { PKPCR Pcr = KeGetCurrentKPCR(); - KeInitializeBugCheck(); + KiInitializeBugCheck(); KeInitializeDispatcher(); KiInitializeSystemClock(); diff --git a/reactos/ntoskrnl/ke/i386/trap.s b/reactos/ntoskrnl/ke/i386/trap.s index 280720de5c8..5e24374e060 100644 --- a/reactos/ntoskrnl/ke/i386/trap.s +++ b/reactos/ntoskrnl/ke/i386/trap.s @@ -26,7 +26,9 @@ /* INCLUDES ******************************************************************/ +#include #include +#include #include #include #include @@ -74,6 +76,29 @@ _KiTrapRet: popl %edi popl %esi popl %ebx + +#ifdef DBG + /* + * Cleanup the stack which was used to setup a trapframe with SS:ESP when called + * from kmode. + */ + movw 0xC(%esp), %bp /* Get CS from trapframe */ + cmpw $KERNEL_CS, %bp + jne 0f + + /* Copy EBP, CS:EIP and EFLAGS from the trapframe back onto the top of our stack. */ + movl 0x00(%esp), %ebp /* EBP */ + movl %ebp, 0x24(%esp) + movl 0x08(%esp), %ebp /* EIP */ + movl %ebp, 0x2C(%esp) + movl 0x0C(%esp), %ebp /* CS */ + movl %ebp, 0x30(%esp) + movl 0x10(%esp), %ebp /* EFLAGS */ + movl %ebp, 0x34(%esp) + + addl $0x24, %esp +0: +#endif /* DBG */ popl %ebp addl $0x4, %esp /* Ignore error code */ @@ -81,10 +106,32 @@ _KiTrapRet: .globl _KiTrapProlog _KiTrapProlog: +#ifdef DBG + /* + * If we were called from kmode we start setting up a new trapframe (with SS:ESP at the end) + */ + movw 0x14(%esp), %bx /* Get old CS */ + cmpw $KERNEL_CS, %bx + + jne 0f + + leal 0x1C(%esp), %ebp + pushl %ss /* Old SS */ + pushl %ebp /* Old ESP */ + pushl 0x20(%esp) /* Old EFLAGS */ + pushl 0x20(%esp) /* Old CS */ + pushl 0x20(%esp) /* Old EIP */ + pushl 0x20(%esp) /* ErrorCode */ + pushl 0x20(%esp) /* Ebp */ + pushl 0x20(%esp) /* Ebx */ + pushl 0x20(%esp) /* Esi */ +0: +#endif /* DBG */ + pushl %edi pushl %fs - /* + /* * Check that the PCR exists, very early in the boot process it may * not */ diff --git a/reactos/ntoskrnl/ke/i386/tskswitch.S b/reactos/ntoskrnl/ke/i386/tskswitch.S index a97577fa5ee..4084b18e163 100644 --- a/reactos/ntoskrnl/ke/i386/tskswitch.S +++ b/reactos/ntoskrnl/ke/i386/tskswitch.S @@ -45,6 +45,10 @@ _Ki386ContextSwitch: * Thread = Thread to switch to * OldThread = Thread to switch from */ +#ifdef KDBG + jmp SaveTrapFrameForKDB +SaveTrapFrameForKDB_Return: +#endif pushl %ebp movl %esp, %ebp @@ -200,12 +204,7 @@ _Ki386ContextSwitch: */ sti - call _KeReleaseDispatcherDatabaseLockFromDpcLevel - - cmpl $0, _PiNrThreadsAwaitingReaping - je 5f - call _PiWakeupReaperThread@0 -5: + call @KeReleaseDispatcherDatabaseLockFromDpcLevel@0 /* * Restore the saved register and exit @@ -218,3 +217,128 @@ _Ki386ContextSwitch: ret .endfunc + + +#ifdef KDBG + +SaveTrapFrameForKDB: + /* + * Set up a trap frame. + */ + /* Ss - space already reserved by return EIP */ + pushl %esp /* Esp */ + pushfl /* Eflags */ + pushl %cs /* Cs */ + pushl 12(%esp) /* Eip */ + movl %ss, 16(%esp) /* Save Ss */ + pushl $0 /* ErrorCode */ + pushl %ebp /* Ebp */ + pushl %ebx /* Ebx */ + pushl %esi /* Esi */ + pushl %edi /* Edi */ + pushl %fs /* Fs */ + pushl $0 /* ExceptionList */ + pushl $0 /* PreviousMode */ + pushl %eax /* Eax */ + pushl %ecx /* Ecx */ + pushl %edx /* Edx */ + pushl %ds /* Ds */ + pushl %es /* Es */ + pushl %gs /* Gs */ + movl %dr7, %eax + pushl %eax /* Dr7 */ + /* Clear breakpoint enables in dr7. */ + andl $~0xffff, %eax + movl %eax, %dr7 + movl %dr6, %eax + pushl %eax /* Dr6 */ + movl %dr3, %eax + pushl %eax /* Dr3 */ + movl %dr2, %eax + pushl %eax /* Dr2 */ + movl %dr1, %eax + pushl %eax /* Dr1 */ + movl %dr0, %eax + pushl %eax /* Dr0 */ + pushl $0 /* TempEip */ + pushl $0 /* TempCs */ + pushl $0 /* DebugPointer */ + pushl $0xffffffff /* DebugArgMark (Exception number) */ + pushl 0x60(%esp) /* DebugEip */ + pushl %ebp /* DebugEbp */ + + movl %esp, %ebp /* Save pointer to new TrapFrame */ + + /* Save the old trapframe and set pointer to the new one */ + movl 0x80(%esp), %ebx /* Get pointer to OldThread */ + pushl KTHREAD_TRAP_FRAME(%ebx) + movl %ebp, KTHREAD_TRAP_FRAME(%ebx) + + /* Copy the arguments which were passed to Ki386ContextSwitch */ + pushl 0x80(%ebp) /* OldThread */ + pushl 0x7c(%ebp) /* NewThread */ + pushl $RestoreTrapFrameForKDB /* Return address */ + + /* Restore clobbered registers */ + movl KTRAP_FRAME_EBX(%ebp), %ebx + movl KTRAP_FRAME_EBP(%ebp), %ebp + + /* Return */ + jmp SaveTrapFrameForKDB_Return + + +RestoreTrapFrameForKDB: + addl $8, %esp /* Remove NewThread and OldThread arguments from the stack */ + movl 0x84(%esp), %ebx /* Get pointer to OldThread */ + + /* Restore the old trapframe */ + popl KTHREAD_TRAP_FRAME(%ebx) + + /* + * Pop unused portions of the trap frame: + * DebugEbp + * DebugEip + * DebugArgMark + * DebugPointer + * TempCs + * TempEip + * Dr0-3 + * Dr6-7 + */ + addl $(12*4), %esp + + /* + * Restore registers including any that might have been changed + * inside the debugger. + */ + popl %gs /* Gs */ + popl %es /* Es */ + popl %ds /* Ds */ + popl %edx /* Edx */ + popl %ecx /* Ecx */ + popl %eax /* Eax */ + addl $4, %esp /* PreviousMode */ + addl $4, %esp /* ExceptionList */ + popl %fs /* Fs */ + popl %edi /* Edi */ + popl %esi /* Esi */ + popl %ebx /* Ebx */ + + /* Remove SS:ESP from the stack */ + movl 16(%esp), %ebp + movl %ebp, 24(%esp) + movl 12(%esp), %ebp + movl %ebp, 20(%esp) + movl 8(%esp), %ebp + movl %ebp, 16(%esp) + + popl %ebp /* Ebp */ + addl $12, %esp /* ErrorCode and SS:ESP */ + + /* + * Return to the caller. + */ + iret + +#endif /* KDBG */ + diff --git a/reactos/ntoskrnl/ke/i386/usercall.S b/reactos/ntoskrnl/ke/i386/usercall.S new file mode 100644 index 00000000000..ad5dd5fbe5d --- /dev/null +++ b/reactos/ntoskrnl/ke/i386/usercall.S @@ -0,0 +1,59 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/ke/i386s/usercall.S + * PURPOSE: User-Mode callbacks and return. + * + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +.intel_syntax noprefix + +/* GLOBALS ****************************************************************/ +.extern PVOID _SystemDllCallbackDispatcher + +#define CBSTACK_BUFFER_ADDRESS 0x20 +#define CBSTACK_BUFFER_LENGTH 0x24 +/* FUNCTIONS ****************************************************************/ + +/*++ + * KiSwitchToUserMode + * + * The KiSwitchToUserMode routine sets up a Trap Frame and a Callback stack + * for the purpose of switching to user mode. The actual final jump is done + * by KiServiceExit which will treat this as a syscall return. + * + * Params: + * OutputBuffer - Pointer to a caller-allocated buffer where to receive + * the return data from the user-mode function + * + * OutputLength - Size of the Output Buffer described above. + * + * Returns: + * Jumps into KiServiceExit. + * + * Remarks: + * If there is not enough Kernel Stack space, the routine will increase the + * Kernel Stack. + * + * User mode execution resumes at ntdll!KiUserCallbackDispatcher. + * + * This call MUST be paired by interrupt 0x2B or NtCallbackReturn. + * + *--*/ +.globl _KiSwitchToUserMode@8 +.func KiSwitchToUserMode@8 +_KiSwitchToUserMode@8: + +.endfunc + + diff --git a/reactos/ntoskrnl/ke/i386/usercall.c b/reactos/ntoskrnl/ke/i386/usercall.c deleted file mode 100644 index 46aa853ef50..00000000000 --- a/reactos/ntoskrnl/ke/i386/usercall.c +++ /dev/null @@ -1,36 +0,0 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ke/i386/usercall.c - * PURPOSE: 2E interrupt handler - * - * PROGRAMMERS: David Welch (david.welch@seh.ox.ac.uk) - */ - -/* INCLUDES ******************************************************************/ - -#include -#define NDEBUG -#include - -/* FUNCTIONS *****************************************************************/ - -/* - * @unimplemented - */ -NTSTATUS -STDCALL -KeUserModeCallback( - IN ULONG FunctionID, - IN PVOID InputBuffer, - IN ULONG InputLength, - OUT PVOID *OutputBuffer, - OUT PULONG OutputLength -) -{ - UNIMPLEMENTED; - return 0; -} - -/* EOF */ diff --git a/reactos/ntoskrnl/ke/ipi.c b/reactos/ntoskrnl/ke/ipi.c index 3798d44acdf..25843863125 100644 --- a/reactos/ntoskrnl/ke/ipi.c +++ b/reactos/ntoskrnl/ke/ipi.c @@ -32,7 +32,7 @@ KiIpiSendRequest(ULONG TargetSet, ULONG IpiRequest) if (TargetSet & (1 << i)) { Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE); - Ke386TestAndSetBit(IpiRequest, &Pcr->PrcbData.IpiFrozen); + Ke386TestAndSetBit(IpiRequest, &Pcr->Prcb->IpiFrozen); HalRequestIpi(i); } } @@ -50,34 +50,34 @@ KiIpiServiceRoutine(IN PKTRAP_FRAME TrapFrame, LARGE_INTEGER StartTime, CurrentTime, Frequency; ULONG Count = 5; #endif - PKPCR Pcr; + PKPRCB Prcb; ASSERT(KeGetCurrentIrql() == IPI_LEVEL); DPRINT("KiIpiServiceRoutine\n"); - Pcr = KeGetCurrentKPCR(); + Prcb = KeGetCurrentPrcb(); - if (Ke386TestAndClearBit(IPI_REQUEST_APC, &Pcr->PrcbData.IpiFrozen)) + if (Ke386TestAndClearBit(IPI_REQUEST_APC, &Prcb->IpiFrozen)) { HalRequestSoftwareInterrupt(APC_LEVEL); } - if (Ke386TestAndClearBit(IPI_REQUEST_DPC, &Pcr->PrcbData.IpiFrozen)) + if (Ke386TestAndClearBit(IPI_REQUEST_DPC, &Prcb->IpiFrozen)) { - Pcr->PrcbData.DpcInterruptRequested = TRUE; + Prcb->DpcInterruptRequested = TRUE; HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } - if (Ke386TestAndClearBit(IPI_REQUEST_FUNCTIONCALL, &Pcr->PrcbData.IpiFrozen)) + if (Ke386TestAndClearBit(IPI_REQUEST_FUNCTIONCALL, &Prcb->IpiFrozen)) { - InterlockedDecrementUL(&Pcr->PrcbData.SignalDone->CurrentPacket[1]); - if (InterlockedCompareExchangeUL(&Pcr->PrcbData.SignalDone->CurrentPacket[2], 0, 0)) + InterlockedDecrementUL(&Prcb->SignalDone->CurrentPacket[1]); + if (InterlockedCompareExchangeUL(&Prcb->SignalDone->CurrentPacket[2], 0, 0)) { #ifdef DBG StartTime = KeQueryPerformanceCounter(&Frequency); #endif - while (0 != InterlockedCompareExchangeUL(&Pcr->PrcbData.SignalDone->CurrentPacket[1], 0, 0)) + while (0 != InterlockedCompareExchangeUL(&Prcb->SignalDone->CurrentPacket[1], 0, 0)) { #ifdef DBG CurrentTime = KeQueryPerformanceCounter(NULL); @@ -89,14 +89,14 @@ KiIpiServiceRoutine(IN PKTRAP_FRAME TrapFrame, #endif } } - ((VOID STDCALL(*)(PVOID))(Pcr->PrcbData.SignalDone->WorkerRoutine))(Pcr->PrcbData.SignalDone->CurrentPacket[0]); - Ke386TestAndClearBit(KeGetCurrentProcessorNumber(), &Pcr->PrcbData.SignalDone->TargetSet); - if (InterlockedCompareExchangeUL(&Pcr->PrcbData.SignalDone->CurrentPacket[2], 0, 0)) + ((VOID STDCALL(*)(PVOID))(Prcb->SignalDone->WorkerRoutine))(Prcb->SignalDone->CurrentPacket[0]); + Ke386TestAndClearBit(KeGetCurrentProcessorNumber(), &Prcb->SignalDone->TargetSet); + if (InterlockedCompareExchangeUL(&Prcb->SignalDone->CurrentPacket[2], 0, 0)) { #ifdef DBG StartTime = KeQueryPerformanceCounter(&Frequency); #endif - while (0 != InterlockedCompareExchangeUL(&Pcr->PrcbData.SignalDone->TargetSet, 0, 0)) + while (0 != InterlockedCompareExchangeUL(&Prcb->SignalDone->TargetSet, 0, 0)) { #ifdef DBG CurrentTime = KeQueryPerformanceCounter(NULL); @@ -108,7 +108,7 @@ KiIpiServiceRoutine(IN PKTRAP_FRAME TrapFrame, #endif } } - InterlockedExchangePointer(&Pcr->PrcbData.SignalDone, NULL); + InterlockedExchangePointer(&Prcb->SignalDone, NULL); } DPRINT("KiIpiServiceRoutine done\n"); return TRUE; @@ -119,18 +119,18 @@ STDCALL KiIpiSendPacket(ULONG TargetSet, VOID STDCALL (*WorkerRoutine)(PVOID), PVOID Argument, ULONG Count, BOOLEAN Synchronize) { ULONG i, Processor, CurrentProcessor; - PKPCR Pcr, CurrentPcr; + PKPRCB Prcb, CurrentPrcb; KIRQL oldIrql; ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL); - CurrentPcr = KeGetCurrentKPCR(); - InterlockedExchangeUL(&CurrentPcr->PrcbData.TargetSet, TargetSet); - InterlockedExchangeUL(&CurrentPcr->PrcbData.WorkerRoutine, (ULONG_PTR)WorkerRoutine); - InterlockedExchangePointer(&CurrentPcr->PrcbData.CurrentPacket[0], Argument); - InterlockedExchangeUL(&CurrentPcr->PrcbData.CurrentPacket[1], Count); - InterlockedExchangeUL(&CurrentPcr->PrcbData.CurrentPacket[2], Synchronize ? 1 : 0); + CurrentPrcb = KeGetCurrentPrcb(); + InterlockedExchangeUL(&CurrentPrcb->TargetSet, TargetSet); + InterlockedExchangeUL(&CurrentPrcb->WorkerRoutine, (ULONG_PTR)WorkerRoutine); + InterlockedExchangePointer(&CurrentPrcb->CurrentPacket[0], Argument); + InterlockedExchangeUL(&CurrentPrcb->CurrentPacket[1], Count); + InterlockedExchangeUL(&CurrentPrcb->CurrentPacket[2], Synchronize ? 1 : 0); CurrentProcessor = 1 << KeGetCurrentProcessorNumber(); @@ -138,9 +138,9 @@ KiIpiSendPacket(ULONG TargetSet, VOID STDCALL (*WorkerRoutine)(PVOID), PVOID Arg { if (TargetSet & Processor) { - Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE); - while(0 != InterlockedCompareExchangeUL(&Pcr->PrcbData.SignalDone, (LONG)&CurrentPcr->PrcbData, 0)); - Ke386TestAndSetBit(IPI_REQUEST_FUNCTIONCALL, &Pcr->PrcbData.IpiFrozen); + Prcb = ((PKPCR)(KPCR_BASE + i * PAGE_SIZE))->Prcb; + while(0 != InterlockedCompareExchangeUL(&Prcb->SignalDone, (LONG)CurrentPrcb, 0)); + Ke386TestAndSetBit(IPI_REQUEST_FUNCTIONCALL, &Prcb->IpiFrozen); if (Processor != CurrentProcessor) { HalRequestIpi(i); diff --git a/reactos/ntoskrnl/ke/kqueue.c b/reactos/ntoskrnl/ke/kqueue.c index b19a3155890..ebd1b743006 100644 --- a/reactos/ntoskrnl/ke/kqueue.c +++ b/reactos/ntoskrnl/ke/kqueue.c @@ -1,11 +1,13 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PURPOSE: ReactOS kernel * FILE: ntoskrnl/ke/kqueue.c * PURPOSE: Implement device queues * - * PROGRAMMERS: David Welch (welch@mcmail.com) + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Several optimizations and implement + * usage of Inserted flag + reformat and + * add debug output. + * David Welch (welch@mcmail.com) */ /* INCLUDES ****************************************************************/ @@ -16,157 +18,33 @@ /* FUNCTIONS *****************************************************************/ - /* * @implemented - */ -BOOLEAN STDCALL -KeInsertByKeyDeviceQueue ( - IN PKDEVICE_QUEUE DeviceQueue, - IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry, - IN ULONG SortKey) -{ - DPRINT("KeInsertByKeyDeviceQueue()\n"); - ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); - - DeviceQueueEntry->SortKey=SortKey; - - KeAcquireSpinLockAtDpcLevel(&DeviceQueue->Lock); - - if (!DeviceQueue->Busy) - { - DeviceQueue->Busy=TRUE; - KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); - return(FALSE); - } - - /* Insert new entry after the last entry with SortKey less or equal to passed-in SortKey */ - InsertAscendingListFIFO(&DeviceQueue->DeviceListHead, - KDEVICE_QUEUE_ENTRY, - DeviceListEntry, - DeviceQueueEntry, - SortKey); - - KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); - return(TRUE); -} - -/* - * @implemented - */ -PKDEVICE_QUEUE_ENTRY -STDCALL -KeRemoveByKeyDeviceQueue ( - IN PKDEVICE_QUEUE DeviceQueue, - IN ULONG SortKey - ) -{ - PLIST_ENTRY current; - PKDEVICE_QUEUE_ENTRY entry; - - ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); - ASSERT(DeviceQueue!=NULL); - ASSERT(DeviceQueue->Busy); - - KeAcquireSpinLockAtDpcLevel(&DeviceQueue->Lock); - - /* an attempt to remove an entry from an empty (and busy) queue sets the queue to idle */ - if (IsListEmpty(&DeviceQueue->DeviceListHead)) - { - DeviceQueue->Busy = FALSE; - KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); - return NULL; - } - - /* find entry with SortKey greater than or equal to the passed-in SortKey */ - current = DeviceQueue->DeviceListHead.Flink; - while (current != &DeviceQueue->DeviceListHead) - { - entry = CONTAINING_RECORD(current,KDEVICE_QUEUE_ENTRY,DeviceListEntry); - if (entry->SortKey >= SortKey) - { - RemoveEntryList(current); - KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); - return entry; - } - current = current->Flink; - } - - /* if we didn't find a match, return the first entry */ - current = RemoveHeadList(&DeviceQueue->DeviceListHead); - KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); - - return CONTAINING_RECORD(current,KDEVICE_QUEUE_ENTRY,DeviceListEntry); -} - -/* - * @implemented - */ -PKDEVICE_QUEUE_ENTRY -STDCALL -KeRemoveDeviceQueue ( - IN PKDEVICE_QUEUE DeviceQueue) -/* - * FUNCTION: Removes an entry from a device queue - * ARGUMENTS: - * DeviceQueue = Queue to remove the entry - * RETURNS: The removed entry - */ -{ - PLIST_ENTRY list_entry; - - DPRINT("KeRemoveDeviceQueue(DeviceQueue %x)\n",DeviceQueue); - - ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); - ASSERT(DeviceQueue!=NULL); - ASSERT(DeviceQueue->Busy); - - KeAcquireSpinLockAtDpcLevel(&DeviceQueue->Lock); - - /* an attempt to remove an entry from an empty (and busy) queue sets the queue idle */ - if (IsListEmpty(&DeviceQueue->DeviceListHead)) - { - DeviceQueue->Busy = FALSE; - KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); - return NULL; - } - - list_entry = RemoveHeadList(&DeviceQueue->DeviceListHead); - KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); - - return CONTAINING_RECORD(list_entry,KDEVICE_QUEUE_ENTRY,DeviceListEntry); -} - -/* - * @implemented - */ -VOID -STDCALL -KeInitializeDeviceQueue ( - IN PKDEVICE_QUEUE DeviceQueue - ) -/* + * * FUNCTION: Intializes a device queue * ARGUMENTS: * DeviceQueue = Device queue to initialize */ +VOID +STDCALL +KeInitializeDeviceQueue(IN PKDEVICE_QUEUE DeviceQueue) { - ASSERT(DeviceQueue!=NULL); - InitializeListHead(&DeviceQueue->DeviceListHead); - DeviceQueue->Busy=FALSE; - KeInitializeSpinLock(&DeviceQueue->Lock); + + /* Initialize the Header */ + DeviceQueue->Type = DeviceQueueObject; + DeviceQueue->Size = sizeof(KDEVICE_QUEUE); + + /* Initialize the Listhead and Spinlock */ + InitializeListHead(&DeviceQueue->DeviceListHead); + KeInitializeSpinLock(&DeviceQueue->Lock); + + /* Set it as busy */ + DeviceQueue->Busy=FALSE; } /* * @implemented - */ -BOOLEAN -STDCALL -KeInsertDeviceQueue ( - IN PKDEVICE_QUEUE DeviceQueue, - IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry - ) -/* + * * FUNCTION: Inserts an entry in a device queue * ARGUMENTS: * DeviceQueue = Queue to insert the entry in @@ -174,58 +52,221 @@ KeInsertDeviceQueue ( * RETURNS: False is the device queue wasn't busy * True otherwise */ +BOOLEAN +STDCALL +KeInsertDeviceQueue(IN PKDEVICE_QUEUE DeviceQueue, + IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry) { - ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + BOOLEAN Inserted; + + DPRINT("KeInsertDeviceQueue(DeviceQueue %x)\n", DeviceQueue); + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* Lock the Queue */ + KeAcquireSpinLockAtDpcLevel(&DeviceQueue->Lock); - KeAcquireSpinLockAtDpcLevel(&DeviceQueue->Lock); + if (!DeviceQueue->Busy) { + + /* Set it as busy */ + Inserted = FALSE; + DeviceQueue->Busy = TRUE; + + } else { + + /* Insert it into the list */ + Inserted = TRUE; + InsertTailList(&DeviceQueue->DeviceListHead, + &DeviceQueueEntry->DeviceListEntry); + + + } - if (!DeviceQueue->Busy) - { - DeviceQueue->Busy=TRUE; + /* Sert the Insert state into the entry */ + DeviceQueueEntry->Inserted = Inserted; + + /* Release lock and return */ KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); - return(FALSE); - } - - DeviceQueueEntry->SortKey=0; - InsertTailList(&DeviceQueue->DeviceListHead, &DeviceQueueEntry->DeviceListEntry); - - KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); - return(TRUE); + return Inserted; } +/* + * @implemented + */ +BOOLEAN +STDCALL +KeInsertByKeyDeviceQueue(IN PKDEVICE_QUEUE DeviceQueue, + IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry, + IN ULONG SortKey) +{ + BOOLEAN Inserted; + + DPRINT("KeInsertByKeyDeviceQueue(DeviceQueue %x)\n", DeviceQueue); + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* Acquire the Lock */ + KeAcquireSpinLockAtDpcLevel(&DeviceQueue->Lock); + + /* Set the Sort Key */ + DeviceQueueEntry->SortKey=SortKey; + + if (!DeviceQueue->Busy) { + + DeviceQueue->Busy=TRUE; + Inserted = FALSE; + + } else { + + /* Insert new entry after the last entry with SortKey less or equal to passed-in SortKey */ + InsertAscendingListFIFO(&DeviceQueue->DeviceListHead, + KDEVICE_QUEUE_ENTRY, + DeviceListEntry, + DeviceQueueEntry, + SortKey); + Inserted = TRUE; + } + + /* Reset the Inserted State */ + DeviceQueueEntry->Inserted = Inserted; + + /* Release Lock and Return */ + KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); + return Inserted; +} + +/* + * @implemented + * + * FUNCTION: Removes an entry from a device queue + * ARGUMENTS: + * DeviceQueue = Queue to remove the entry + * RETURNS: The removed entry + */ +PKDEVICE_QUEUE_ENTRY +STDCALL +KeRemoveDeviceQueue(IN PKDEVICE_QUEUE DeviceQueue) +{ + PLIST_ENTRY ListEntry; + PKDEVICE_QUEUE_ENTRY ReturnEntry; + + DPRINT("KeRemoveDeviceQueue(DeviceQueue %x)\n", DeviceQueue); + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* Acquire the Lock */ + KeAcquireSpinLockAtDpcLevel(&DeviceQueue->Lock); + ASSERT(DeviceQueue->Busy); + + /* An attempt to remove an entry from an empty (and busy) queue sets the queue idle */ + if (IsListEmpty(&DeviceQueue->DeviceListHead)) { + DeviceQueue->Busy = FALSE; + ReturnEntry = NULL; + + } else { + + /* Remove the Entry from the List */ + ListEntry = RemoveHeadList(&DeviceQueue->DeviceListHead); + ReturnEntry = CONTAINING_RECORD(ListEntry, + KDEVICE_QUEUE_ENTRY, + DeviceListEntry); + + /* Set it as non-inserted */ + ReturnEntry->Inserted = FALSE; + } + + /* Release lock and Return */ + KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); + return ReturnEntry; +} + +/* + * @implemented + */ +PKDEVICE_QUEUE_ENTRY +STDCALL +KeRemoveByKeyDeviceQueue (IN PKDEVICE_QUEUE DeviceQueue, + IN ULONG SortKey) +{ + PLIST_ENTRY ListEntry; + PKDEVICE_QUEUE_ENTRY ReturnEntry; + + DPRINT("KeRemoveByKeyDeviceQueue(DeviceQueue %x)\n", DeviceQueue); + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* Acquire the Lock */ + KeAcquireSpinLockAtDpcLevel(&DeviceQueue->Lock); + ASSERT(DeviceQueue->Busy); + + /* An attempt to remove an entry from an empty (and busy) queue sets the queue idle */ + if (IsListEmpty(&DeviceQueue->DeviceListHead)) { + + DeviceQueue->Busy = FALSE; + ReturnEntry = NULL; + + } else { + + /* Find entry with SortKey greater than or equal to the passed-in SortKey */ + ListEntry = DeviceQueue->DeviceListHead.Flink; + while (ListEntry != &DeviceQueue->DeviceListHead) { + + /* Get Entry */ + ReturnEntry = CONTAINING_RECORD(ListEntry, + KDEVICE_QUEUE_ENTRY, + DeviceListEntry); + + /* Check if keys match */ + if (ReturnEntry->SortKey >= SortKey) break; + + /* Move to next item */ + ListEntry = ListEntry->Flink; + } + + /* Check if we found something */ + if (ListEntry == &DeviceQueue->DeviceListHead) { + + /* Not found, return the first entry */ + ListEntry = RemoveHeadList(&DeviceQueue->DeviceListHead); + ReturnEntry = CONTAINING_RECORD(ListEntry, + KDEVICE_QUEUE_ENTRY, + DeviceListEntry); + } else { + + /* We found it, so just remove it */ + RemoveEntryList(&ReturnEntry->DeviceListEntry); + } + + /* Set it as non-inserted */ + ReturnEntry->Inserted = FALSE; + } + + /* Release lock and Return */ + KeReleaseSpinLockFromDpcLevel(&DeviceQueue->Lock); + return ReturnEntry; +} /* * @implemented */ BOOLEAN STDCALL -KeRemoveEntryDeviceQueue( - IN PKDEVICE_QUEUE DeviceQueue, - IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry) +KeRemoveEntryDeviceQueue(IN PKDEVICE_QUEUE DeviceQueue, + IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry) { - PLIST_ENTRY current; - KIRQL oldIrql; - PKDEVICE_QUEUE_ENTRY entry; + KIRQL OldIrql; + BOOLEAN OldState; + + DPRINT("KeRemoveEntryDeviceQueue(DeviceQueue %x)\n", DeviceQueue); + ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - - KeAcquireSpinLock(&DeviceQueue->Lock, &oldIrql); - - current = DeviceQueue->DeviceListHead.Flink; - while (current != &DeviceQueue->DeviceListHead) - { - entry = CONTAINING_RECORD(current, KDEVICE_QUEUE_ENTRY, DeviceListEntry); - if (DeviceQueueEntry == entry) - { - RemoveEntryList(current); - KeReleaseSpinLock(&DeviceQueue->Lock, oldIrql); - /* entry was in the queue (but not anymore) */ - return TRUE; + /* Acquire the Lock */ + KeAcquireSpinLock(&DeviceQueue->Lock, &OldIrql); + + /* Check/Set Old State */ + if ((OldState = DeviceQueueEntry->Inserted)) { + + /* Remove it */ + DeviceQueueEntry->Inserted = FALSE; + RemoveEntryList(&DeviceQueueEntry->DeviceListEntry); } - current = current->Flink; - } - KeReleaseSpinLock(&DeviceQueue->Lock, oldIrql); - - /* entry wasn't in the queue */ - return FALSE; + /* Unlock and return old state */ + KeReleaseSpinLock(&DeviceQueue->Lock, OldIrql); + return OldState; } diff --git a/reactos/ntoskrnl/ke/kthread.c b/reactos/ntoskrnl/ke/kthread.c index 40b352fb11a..dbfd3b50062 100644 --- a/reactos/ntoskrnl/ke/kthread.c +++ b/reactos/ntoskrnl/ke/kthread.c @@ -1,11 +1,11 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/kthread.c * PURPOSE: Microkernel thread support * - * PROGRAMMERS: David Welch (welch@cwcom.net) + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + * David Welch (welch@cwcom.net) */ /* INCLUDES *****************************************************************/ @@ -14,21 +14,565 @@ #define NDEBUG #include +#define THREAD_ALERT_INCREMENT 2 + +extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue]; + +/* + * PURPOSE: List of threads associated with each priority level + */ +LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY]; +static ULONG PriorityListMask = 0; +ULONG IdleProcessorMask = 0; +extern BOOLEAN DoneInitYet; +extern PETHREAD PspReaperList; + /* FUNCTIONS *****************************************************************/ +STATIC VOID -KiServiceCheck (VOID) +KiRequestReschedule(CCHAR Processor) { - PETHREAD Thread; + PKPCR Pcr; - Thread = PsGetCurrentThread(); + Pcr = (PKPCR)(KPCR_BASE + Processor * PAGE_SIZE); + Pcr->Prcb->QuantumEnd = TRUE; + KiIpiSendRequest(1 << Processor, IPI_REQUEST_DPC); +} - if (Thread->Tcb.ServiceTable != KeServiceDescriptorTableShadow) - { - PsInitWin32Thread (Thread); - - Thread->Tcb.ServiceTable = KeServiceDescriptorTableShadow; +STATIC +VOID +KiInsertIntoThreadList(KPRIORITY Priority, + PKTHREAD Thread) +{ + ASSERT(THREAD_STATE_READY == Thread->State); + ASSERT(Thread->Priority == Priority); + + if (Priority >= MAXIMUM_PRIORITY || Priority < LOW_PRIORITY) { + + DPRINT1("Invalid thread priority (%d)\n", Priority); + KEBUGCHECK(0); } + + InsertTailList(&PriorityListHead[Priority], &Thread->QueueListEntry); + PriorityListMask |= (1 << Priority); +} + +STATIC +VOID +KiRemoveFromThreadList(PKTHREAD Thread) +{ + ASSERT(THREAD_STATE_READY == Thread->State); + RemoveEntryList(&Thread->QueueListEntry); + if (IsListEmpty(&PriorityListHead[(ULONG)Thread->Priority])) { + + PriorityListMask &= ~(1 << Thread->Priority); + } +} + +STATIC +PKTHREAD +KiScanThreadList(KPRIORITY Priority, + KAFFINITY Affinity) +{ + PLIST_ENTRY current_entry; + PKTHREAD current; + ULONG Mask; + + Mask = (1 << Priority); + + if (PriorityListMask & Mask) { + + current_entry = PriorityListHead[Priority].Flink; + + while (current_entry != &PriorityListHead[Priority]) { + + current = CONTAINING_RECORD(current_entry, KTHREAD, QueueListEntry); + + if (current->State != THREAD_STATE_READY) { + + DPRINT1("%d/%d\n", ¤t, current->State); + } + + ASSERT(current->State == THREAD_STATE_READY); + + if (current->Affinity & Affinity) { + + KiRemoveFromThreadList(current); + return(current); + } + + current_entry = current_entry->Flink; + } + } + + return(NULL); +} + +VOID +STDCALL +KiDispatchThreadNoLock(ULONG NewThreadStatus) +{ + KPRIORITY CurrentPriority; + PKTHREAD Candidate; + ULONG Affinity; + PKTHREAD CurrentThread = KeGetCurrentThread(); + + DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(), + CurrentThread, NewThreadStatus, CurrentThread->State); + + CurrentThread->State = (UCHAR)NewThreadStatus; + + if (NewThreadStatus == THREAD_STATE_READY) { + + KiInsertIntoThreadList(CurrentThread->Priority, + CurrentThread); + } + + Affinity = 1 << KeGetCurrentProcessorNumber(); + + for (CurrentPriority = HIGH_PRIORITY; CurrentPriority >= LOW_PRIORITY; CurrentPriority--) { + + Candidate = KiScanThreadList(CurrentPriority, Affinity); + + if (Candidate == CurrentThread) { + + Candidate->State = THREAD_STATE_RUNNING; + KeReleaseDispatcherDatabaseLockFromDpcLevel(); + return; + } + + if (Candidate != NULL) { + + PKTHREAD OldThread; + PKTHREAD IdleThread; + + DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority); + + Candidate->State = THREAD_STATE_RUNNING; + + OldThread = CurrentThread; + CurrentThread = Candidate; + IdleThread = KeGetCurrentPrcb()->IdleThread; + + if (OldThread == IdleThread) { + + IdleProcessorMask &= ~Affinity; + + } else if (CurrentThread == IdleThread) { + + IdleProcessorMask |= Affinity; + } + + MmUpdatePageDir(PsGetCurrentProcess(),((PETHREAD)CurrentThread)->ThreadsProcess, sizeof(EPROCESS)); + + /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */ + KiArchContextSwitch(CurrentThread, OldThread); + return; + } + } + + DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber()); + KEBUGCHECK(0); +} + +VOID +STDCALL +KiBlockThread(PNTSTATUS Status, + UCHAR Alertable, + ULONG WaitMode, + UCHAR WaitReason) +{ + PKTHREAD Thread = KeGetCurrentThread(); + PKWAIT_BLOCK WaitBlock; + + if (Thread->ApcState.KernelApcPending) { + + DPRINT("Dispatching Thread as ready (APC!)\n"); + + /* Remove Waits */ + WaitBlock = Thread->WaitBlockList; + while (WaitBlock) { + RemoveEntryList (&WaitBlock->WaitListEntry); + WaitBlock = WaitBlock->NextWaitBlock; + } + Thread->WaitBlockList = NULL; + + /* Dispatch it and return status */ + KiDispatchThreadNoLock (THREAD_STATE_READY); + if (Status != NULL) *Status = STATUS_KERNEL_APC; + + } else { + + /* Set the Thread Data as Requested */ + DPRINT("Dispatching Thread as blocked\n"); + Thread->Alertable = Alertable; + Thread->WaitMode = (UCHAR)WaitMode; + Thread->WaitReason = WaitReason; + + /* Dispatch it and return status */ + KiDispatchThreadNoLock(THREAD_STATE_BLOCKED); + if (Status != NULL) *Status = Thread->WaitStatus; + } + + DPRINT("Releasing Dispatcher Lock\n"); + KfLowerIrql(Thread->WaitIrql); +} + +VOID +STDCALL +KiDispatchThread(ULONG NewThreadStatus) +{ + KIRQL OldIrql; + + if (!DoneInitYet || KeGetCurrentPrcb()->IdleThread == NULL) { + return; + } + + OldIrql = KeAcquireDispatcherDatabaseLock(); + KiDispatchThreadNoLock(NewThreadStatus); + KeLowerIrql(OldIrql); +} + +VOID +STDCALL +KiUnblockThread(PKTHREAD Thread, + PNTSTATUS WaitStatus, + KPRIORITY Increment) +{ + if (THREAD_STATE_TERMINATED_1 == Thread->State || + THREAD_STATE_TERMINATED_2 == Thread->State) { + + DPRINT("Can't unblock thread 0x%x because it's terminating\n", + Thread); + + } else if (THREAD_STATE_READY == Thread->State || + THREAD_STATE_RUNNING == Thread->State) { + + DPRINT("Can't unblock thread 0x%x because it's %s\n", + Thread, (Thread->State == THREAD_STATE_READY ? "ready" : "running")); + + } else { + + ULONG Processor; + KAFFINITY Affinity; + + /* FIXME: This propably isn't the right way to do it... */ + /* No it's not... i'll fix it later-- Alex */ + if (Thread->Priority < LOW_REALTIME_PRIORITY && + Thread->BasePriority < LOW_REALTIME_PRIORITY - 2) { + + if (!Thread->PriorityDecrement && !Thread->DisableBoost) { + + Thread->Priority = Thread->BasePriority + Increment; + Thread->PriorityDecrement = Increment; + } + + } else { + + Thread->Quantum = Thread->ApcState.Process->ThreadQuantum; + } + + if (WaitStatus != NULL) { + + Thread->WaitStatus = *WaitStatus; + } + + Thread->State = THREAD_STATE_READY; + KiInsertIntoThreadList(Thread->Priority, Thread); + Processor = KeGetCurrentProcessorNumber(); + Affinity = Thread->Affinity; + + if (!(IdleProcessorMask & (1 << Processor) & Affinity) && + (IdleProcessorMask & ~(1 << Processor) & Affinity)) { + + ULONG i; + + for (i = 0; i < KeNumberProcessors - 1; i++) { + + Processor++; + + if (Processor >= KeNumberProcessors) { + + Processor = 0; + } + + if (IdleProcessorMask & (1 << Processor) & Affinity) { +#if 0 + /* FIXME: + * Reschedule the threads on an other processor + */ + KeReleaseDispatcherDatabaseLockFromDpcLevel(); + KiRequestReschedule(Processor); + KeAcquireDispatcherDatabaseLockAtDpcLevel(); +#endif + break; + } + } + } + } +} + +VOID +STDCALL +KiSuspendThreadKernelRoutine(PKAPC Apc, + PKNORMAL_ROUTINE* NormalRoutine, + PVOID* NormalContext, + PVOID* SystemArgument1, + PVOID* SystemArguemnt2) +{ +} + +VOID +STDCALL +KiSuspendThreadNormalRoutine(PVOID NormalContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + PKTHREAD CurrentThread = KeGetCurrentThread(); + + /* Non-alertable kernel-mode suspended wait */ + DPRINT("Waiting...\n"); + KeWaitForSingleObject(&CurrentThread->SuspendSemaphore, + Suspended, + KernelMode, + FALSE, + NULL); + DPRINT("Done Waiting\n"); +} + +VOID +STDCALL +KeRundownThread(VOID) +{ + KIRQL OldIrql; + PKTHREAD Thread = KeGetCurrentThread(); + PLIST_ENTRY CurrentEntry; + PKMUTANT Mutant; + + DPRINT("KeRundownThread: %x\n", Thread); + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + while (!IsListEmpty(&Thread->MutantListHead)) { + + /* Get the Mutant */ + CurrentEntry = RemoveHeadList(&Thread->MutantListHead); + Mutant = CONTAINING_RECORD(CurrentEntry, KMUTANT, MutantListEntry); + ASSERT(Mutant->ApcDisable == 0); + + /* Uncondtionally abandon it */ + DPRINT("Abandonning the Mutant\n"); + Mutant->Header.SignalState = 1; + Mutant->Abandoned = TRUE; + Mutant->OwnerThread = NULL; + RemoveEntryList(&Mutant->MutantListEntry); + + /* Check if the Wait List isn't empty */ + DPRINT("Checking whether to wake the Mutant\n"); + if (!IsListEmpty(&Mutant->Header.WaitListHead)) { + + /* Wake the Mutant */ + DPRINT("Waking the Mutant\n"); + KiWaitTest(&Mutant->Header, MUTANT_INCREMENT); + } + } + + /* Release the Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); +} + +ULONG +STDCALL +KeResumeThread(PKTHREAD Thread) +{ + ULONG PreviousCount; + KIRQL OldIrql; + + DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread, Thread->SuspendCount, Thread->FreezeCount); + + /* Lock the Dispatcher */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save the Old Count */ + PreviousCount = Thread->SuspendCount; + + /* Check if it existed */ + if (PreviousCount) { + + Thread->SuspendCount--; + + /* Decrease the current Suspend Count and Check Freeze Count */ + if ((!Thread->SuspendCount) && (!Thread->FreezeCount)) { + + /* Signal the Suspend Semaphore */ + Thread->SuspendSemaphore.Header.SignalState++; + KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT); + } + } + + /* Release Lock and return the Old State */ + KeReleaseDispatcherDatabaseLock(OldIrql); + return PreviousCount; +} + +BOOLEAN +STDCALL +KiInsertQueueApc(PKAPC Apc, + KPRIORITY PriorityBoost); + +NTSTATUS +STDCALL +KeSuspendThread(PKTHREAD Thread) +{ + ULONG PreviousCount; + KIRQL OldIrql; + + DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread, Thread->SuspendCount, Thread->FreezeCount); + + /* Lock the Dispatcher */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save the Old Count */ + PreviousCount = Thread->SuspendCount; + + /* Increment it */ + Thread->SuspendCount++; + + /* Check if we should suspend it */ + if (!PreviousCount && !Thread->FreezeCount) { + + /* Insert the APC */ + if (!KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT)) { + + /* Unsignal the Semaphore, the APC already got inserted */ + Thread->SuspendSemaphore.Header.SignalState--; + } + } + + /* Release Lock and return the Old State */ + KeReleaseDispatcherDatabaseLock(OldIrql); + return PreviousCount; +} + +ULONG +STDCALL +KeForceResumeThread(IN PKTHREAD Thread) +{ + KIRQL OldIrql; + ULONG PreviousCount; + + /* Lock the Dispatcher Database and the APC Queue */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save the old Suspend Count */ + PreviousCount = Thread->SuspendCount + Thread->FreezeCount; + + /* If the thread is suspended, wake it up!!! */ + if (PreviousCount) { + + /* Unwait it completely */ + Thread->SuspendCount = 0; + Thread->FreezeCount = 0; + + /* Signal and satisfy */ + Thread->SuspendSemaphore.Header.SignalState++; + KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT); + } + + /* Release Lock and return the Old State */ + KeReleaseDispatcherDatabaseLock(OldIrql); + return PreviousCount; +} + +ULONG +STDCALL +KeAlertResumeThread(IN PKTHREAD Thread) +{ + ULONG PreviousCount; + KIRQL OldIrql; + + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* Lock the Dispatcher Database and the APC Queue */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + KiAcquireSpinLock(&Thread->ApcQueueLock); + + /* Return if Thread is already alerted. */ + if (Thread->Alerted[KernelMode] == FALSE) { + + /* If it's Blocked, unblock if it we should */ + if (Thread->State == THREAD_STATE_BLOCKED && Thread->Alertable) { + + DPRINT("Aborting Wait\n"); + KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT); + + } else { + + /* If not, simply Alert it */ + Thread->Alerted[KernelMode] = TRUE; + } + } + + /* Save the old Suspend Count */ + PreviousCount = Thread->SuspendCount; + + /* If the thread is suspended, decrease one of the suspend counts */ + if (PreviousCount) { + + /* Decrease count. If we are now zero, unwait it completely */ + if (--Thread->SuspendCount) { + + /* Signal and satisfy */ + Thread->SuspendSemaphore.Header.SignalState++; + KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT); + } + } + + /* Release Locks and return the Old State */ + KiReleaseSpinLock(&Thread->ApcQueueLock); + KeReleaseDispatcherDatabaseLock(OldIrql); + return PreviousCount; +} + +BOOLEAN +STDCALL +KeAlertThread(PKTHREAD Thread, + KPROCESSOR_MODE AlertMode) +{ + KIRQL OldIrql; + BOOLEAN PreviousState; + + /* Acquire the Dispatcher Database Lock */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save the Previous State */ + PreviousState = Thread->Alerted[AlertMode]; + + /* Return if Thread is already alerted. */ + if (PreviousState == FALSE) { + + /* If it's Blocked, unblock if it we should */ + if (Thread->State == THREAD_STATE_BLOCKED && + (AlertMode == KernelMode || Thread->WaitMode == AlertMode) && + Thread->Alertable) { + + DPRINT("Aborting Wait\n"); + KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT); + + } else { + + /* If not, simply Alert it */ + Thread->Alerted[AlertMode] = TRUE; + } + } + + /* Release the Dispatcher Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Return the old state */ + return PreviousState; } /* @@ -36,28 +580,232 @@ KiServiceCheck (VOID) */ VOID STDCALL -KeCapturePersistentThreadState( - IN PVOID CurrentThread, - IN ULONG Setting1, - IN ULONG Setting2, - IN ULONG Setting3, - IN ULONG Setting4, - IN ULONG Setting5, - IN PVOID ThreadState -) +KeCapturePersistentThreadState(IN PVOID CurrentThread, + IN ULONG Setting1, + IN ULONG Setting2, + IN ULONG Setting3, + IN ULONG Setting4, + IN ULONG Setting5, + IN PVOID ThreadState) { - UNIMPLEMENTED; + UNIMPLEMENTED; } +/* + * FUNCTION: Initialize the microkernel state of the thread + */ VOID -KeFreeStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, - PFN_TYPE Page, SWAPENTRY SwapEntry, BOOLEAN Dirty) +STDCALL +KeInitializeThread(PKPROCESS Process, + PKTHREAD Thread, + BOOLEAN First) { - ASSERT(SwapEntry == 0); - if (Page != 0) - { - MmReleasePageMemoryConsumer(MC_NPPOOL, Page); + PVOID KernelStack; + NTSTATUS Status; + extern unsigned int init_stack_top; + extern unsigned int init_stack; + PMEMORY_AREA StackArea; + ULONG i; + PHYSICAL_ADDRESS BoundaryAddressMultiple; + + /* Initialize the Boundary Address */ + BoundaryAddressMultiple.QuadPart = 0; + + /* Initalize the Dispatcher Header */ + KeInitializeDispatcherHeader(&Thread->DispatcherHeader, + ThreadObject, + sizeof(KTHREAD), + FALSE); + InitializeListHead(&Thread->MutantListHead); + + /* If this is isn't the first thread, allocate the Kernel Stack */ + if (!First) { + + PFN_TYPE Page[MM_STACK_SIZE / PAGE_SIZE]; + KernelStack = NULL; + + MmLockAddressSpace(MmGetKernelAddressSpace()); + Status = MmCreateMemoryArea(NULL, + MmGetKernelAddressSpace(), + MEMORY_AREA_KERNEL_STACK, + &KernelStack, + MM_STACK_SIZE, + 0, + &StackArea, + FALSE, + FALSE, + BoundaryAddressMultiple); + MmUnlockAddressSpace(MmGetKernelAddressSpace()); + + /* Check for Success */ + if (!NT_SUCCESS(Status)) { + + DPRINT1("Failed to create thread stack\n"); + KEBUGCHECK(0); + } + + /* Mark the Stack */ + for (i = 0; i < (MM_STACK_SIZE / PAGE_SIZE); i++) { + + Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page[i]); + + /* Check for success */ + if (!NT_SUCCESS(Status)) { + + KEBUGCHECK(0); + } + } + + /* Create a Virtual Mapping for it */ + Status = MmCreateVirtualMapping(NULL, + KernelStack, + PAGE_READWRITE, + Page, + MM_STACK_SIZE / PAGE_SIZE); + + /* Check for success */ + if (!NT_SUCCESS(Status)) { + + KEBUGCHECK(0); + } + + /* Set the Kernel Stack */ + Thread->InitialStack = (PCHAR)KernelStack + MM_STACK_SIZE; + Thread->StackBase = (PCHAR)KernelStack + MM_STACK_SIZE; + Thread->StackLimit = (ULONG_PTR)KernelStack; + Thread->KernelStack = (PCHAR)KernelStack + MM_STACK_SIZE; + + } else { + + /* Use the Initial Stack */ + Thread->InitialStack = (PCHAR)init_stack_top; + Thread->StackBase = (PCHAR)init_stack_top; + Thread->StackLimit = (ULONG_PTR)init_stack; + Thread->KernelStack = (PCHAR)init_stack_top; } + + /* + * Establish the pde's for the new stack and the thread structure within the + * address space of the new process. They are accessed while taskswitching or + * while handling page faults. At this point it isn't possible to call the + * page fault handler for the missing pde's. + */ + MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE); + MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD)); + + /* Set the Thread to initalized */ + Thread->State = THREAD_STATE_INITIALIZED; + + /* The Native API function will initialize the TEB field later */ + Thread->Teb = NULL; + + /* Initialize stuff to zero */ + Thread->TlsArray = NULL; + Thread->DebugActive = 0; + Thread->Alerted[0] = 0; + Thread->Alerted[1] = 0; + Thread->Iopl = 0; + + /* Set up FPU/NPX Stuff */ + Thread->NpxState = NPX_STATE_INVALID; + Thread->NpxIrql = 0; + + /* Setup APC Fields */ + InitializeListHead(&Thread->ApcState.ApcListHead[0]); + InitializeListHead(&Thread->ApcState.ApcListHead[1]); + Thread->ApcState.Process = Process; + Thread->ApcState.KernelApcInProgress = 0; + Thread->ApcState.KernelApcPending = 0; + Thread->ApcState.UserApcPending = 0; + Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState; + Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState; + Thread->ApcStateIndex = OriginalApcEnvironment; + Thread->ApcQueueable = TRUE; + RtlZeroMemory(&Thread->SavedApcState, sizeof(KAPC_STATE)); + KeInitializeSpinLock(&Thread->ApcQueueLock); + + /* Setup Wait Fields */ + Thread->WaitStatus = STATUS_SUCCESS; + Thread->WaitIrql = PASSIVE_LEVEL; + Thread->WaitMode = 0; + Thread->WaitNext = FALSE; + Thread->WaitListEntry.Flink = NULL; + Thread->WaitListEntry.Blink = NULL; + Thread->WaitTime = 0; + Thread->WaitBlockList = NULL; + RtlZeroMemory(Thread->WaitBlock, sizeof(KWAIT_BLOCK) * 4); + RtlZeroMemory(&Thread->Timer, sizeof(KTIMER)); + KeInitializeTimer(&Thread->Timer); + + /* Setup scheduler Fields */ + Thread->BasePriority = Process->BasePriority; + Thread->DecrementCount = 0; + Thread->PriorityDecrement = 0; + Thread->Quantum = Process->ThreadQuantum; + Thread->Saturation = 0; + Thread->Priority = Process->BasePriority; + Thread->UserAffinity = Process->Affinity; + Thread->SystemAffinityActive = 0; + Thread->Affinity = Process->Affinity; + Thread->Preempted = 0; + Thread->ProcessReadyQueue = 0; + Thread->KernelStackResident = 1; + Thread->NextProcessor = 0; + Thread->ContextSwitches = 0; + + /* Setup Queue Fields */ + Thread->Queue = NULL; + Thread->QueueListEntry.Flink = NULL; + Thread->QueueListEntry.Blink = NULL; + + /* Setup Misc Fields */ + Thread->LegoData = 0; + Thread->PowerState = 0; + Thread->ServiceTable = KeServiceDescriptorTable; + Thread->CallbackStack = NULL; + Thread->Win32Thread = NULL; + Thread->TrapFrame = NULL; + Thread->EnableStackSwap = 0; + Thread->LargeStack = 0; + Thread->ResourceIndex = 0; + Thread->PreviousMode = KernelMode; + Thread->KernelTime = 0; + Thread->UserTime = 0; + Thread->AutoAlignment = Process->AutoAlignment; + + /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */ +#if 0 + Thread->WaitBlock[3].Object = (PVOID)&Thread->Timer; + Thread->WaitBlock[3].Thread = Thread; + Thread->WaitBlock[3].WaitKey = STATUS_TIMEOUT; + Thread->WaitBlock[3].WaitType = WaitAny; + Thread->WaitBlock[3].NextWaitBlock = NULL; + InsertTailList(&Thread->Timer.Header.WaitListHead, + &Thread->WaitBlock[3].WaitListEntry); +#endif + + /* Initialize the Suspend APC */ + KeInitializeApc(&Thread->SuspendApc, + Thread, + OriginalApcEnvironment, + KiSuspendThreadKernelRoutine, + NULL, + KiSuspendThreadNormalRoutine, + KernelMode, + NULL); + + /* Initialize the Suspend Semaphore */ + KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 128); + + /* Insert the Thread into the Process's Thread List */ + InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry); + + /* Set up the Suspend Counts */ + Thread->FreezeCount = 0; + Thread->SuspendCount = 0; + ((PETHREAD)Thread)->ReaperLink = NULL; /* Union. Will also clear termination port */ + + /* Do x86 specific part */ } /* @@ -65,11 +813,9 @@ KeFreeStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, */ KPRIORITY STDCALL -KeQueryPriorityThread ( - IN PKTHREAD Thread - ) +KeQueryPriorityThread (IN PKTHREAD Thread) { - return Thread->Priority; + return Thread->Priority; } /* @@ -77,19 +823,29 @@ KeQueryPriorityThread ( */ ULONG STDCALL -KeQueryRuntimeThread( - IN PKTHREAD Thread, - OUT PULONG UserTime - ) +KeQueryRuntimeThread(IN PKTHREAD Thread, + OUT PULONG UserTime) { - /* Return the User Time */ - *UserTime = Thread->UserTime; - - /* Return the Kernel Time */ - return Thread->KernelTime; + /* Return the User Time */ + *UserTime = Thread->UserTime; + + /* Return the Kernel Time */ + return Thread->KernelTime; } -NTSTATUS +VOID +KeFreeStackPage(PVOID Context, + MEMORY_AREA* MemoryArea, + PVOID Address, + PFN_TYPE Page, + SWAPENTRY SwapEntry, + BOOLEAN Dirty) +{ + ASSERT(SwapEntry == 0); + if (Page) MmReleasePageMemoryConsumer(MC_NPPOOL, Page); +} + +NTSTATUS KeReleaseThread(PKTHREAD Thread) /* * FUNCTION: Releases the resource allocated for a thread by @@ -123,198 +879,26 @@ KeReleaseThread(PKTHREAD Thread) */ BOOLEAN STDCALL -KeSetKernelStackSwapEnable( - IN BOOLEAN Enable - ) +KeSetKernelStackSwapEnable(IN BOOLEAN Enable) { - PKTHREAD Thread; - BOOLEAN PreviousState; - - Thread = KeGetCurrentThread(); - - /* Save Old State */ - PreviousState = Thread->EnableStackSwap; - - /* Set New State */ - Thread->EnableStackSwap = Enable; + PKTHREAD Thread = KeGetCurrentThread(); + BOOLEAN PreviousState; + KIRQL OldIrql; + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); - /* Return Old State */ - return PreviousState; -} + /* Save Old State */ + PreviousState = Thread->EnableStackSwap; -VOID -KeInitializeThread(PKPROCESS Process, PKTHREAD Thread, BOOLEAN First) -/* - * FUNCTION: Initialize the microkernel state of the thread - */ -{ - PVOID KernelStack; - NTSTATUS Status; - extern unsigned int init_stack_top; - extern unsigned int init_stack; - PMEMORY_AREA StackArea; - ULONG i; - PHYSICAL_ADDRESS BoundaryAddressMultiple; - - BoundaryAddressMultiple.QuadPart = 0; - - KeInitializeDispatcherHeader(&Thread->DispatcherHeader, - InternalThreadType, - sizeof(ETHREAD), - FALSE); - InitializeListHead(&Thread->MutantListHead); - if (!First) - { - PFN_TYPE Page[MM_STACK_SIZE / PAGE_SIZE]; - KernelStack = NULL; - - MmLockAddressSpace(MmGetKernelAddressSpace()); - Status = MmCreateMemoryArea(NULL, - MmGetKernelAddressSpace(), - MEMORY_AREA_KERNEL_STACK, - &KernelStack, - MM_STACK_SIZE, - 0, - &StackArea, - FALSE, - FALSE, - BoundaryAddressMultiple); - MmUnlockAddressSpace(MmGetKernelAddressSpace()); - - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to create thread stack\n"); - KEBUGCHECK(0); - } - for (i = 0; i < (MM_STACK_SIZE / PAGE_SIZE); i++) - { - Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page[i]); - if (!NT_SUCCESS(Status)) - { - KEBUGCHECK(0); - } - } - Status = MmCreateVirtualMapping(NULL, - KernelStack, - PAGE_READWRITE, - Page, - MM_STACK_SIZE / PAGE_SIZE); - if (!NT_SUCCESS(Status)) - { - KEBUGCHECK(0); - } - Thread->InitialStack = (PCHAR)KernelStack + MM_STACK_SIZE; - Thread->StackBase = (PCHAR)KernelStack + MM_STACK_SIZE; - Thread->StackLimit = (ULONG_PTR)KernelStack; - Thread->KernelStack = (PCHAR)KernelStack + MM_STACK_SIZE; - } - else - { - Thread->InitialStack = (PCHAR)init_stack_top; - Thread->StackBase = (PCHAR)init_stack_top; - Thread->StackLimit = (ULONG_PTR)init_stack; - Thread->KernelStack = (PCHAR)init_stack_top; - } + /* Set New State */ + Thread->EnableStackSwap = Enable; - /* - * Establish the pde's for the new stack and the thread structure within the - * address space of the new process. They are accessed while taskswitching or - * while handling page faults. At this point it isn't possible to call the - * page fault handler for the missing pde's. - */ - - MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE); - MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD)); - - /* - * The Native API function will initialize the TEB field later - */ - Thread->Teb = NULL; - Thread->TlsArray = NULL; - Thread->DebugActive = 0; - Thread->State = THREAD_STATE_INITIALIZED; - Thread->Alerted[0] = 0; - Thread->Alerted[1] = 0; - Thread->Iopl = 0; - /* - * FIXME: Think how this might work - */ - Thread->NpxState = 0; - - Thread->Saturation = 0; - Thread->Priority = Process->BasePriority; - InitializeListHead(&Thread->ApcState.ApcListHead[0]); - InitializeListHead(&Thread->ApcState.ApcListHead[1]); - Thread->ApcState.Process = Process; - Thread->ApcState.KernelApcInProgress = 0; - Thread->ApcState.KernelApcPending = 0; - Thread->ApcState.UserApcPending = 0; - Thread->ContextSwitches = 0; - Thread->WaitStatus = STATUS_SUCCESS; - Thread->WaitIrql = PASSIVE_LEVEL; - Thread->WaitMode = 0; - Thread->WaitNext = FALSE; - Thread->WaitBlockList = NULL; - Thread->WaitListEntry.Flink = NULL; - Thread->WaitListEntry.Blink = NULL; - Thread->WaitTime = 0; - Thread->BasePriority = Process->BasePriority; - Thread->DecrementCount = 0; - Thread->PriorityDecrement = 0; - Thread->Quantum = Process->ThreadQuantum; - memset(Thread->WaitBlock, 0, sizeof(KWAIT_BLOCK)*4); - Thread->LegoData = 0; - Thread->UserAffinity = Process->Affinity; - Thread->SystemAffinityActive = 0; - Thread->PowerState = 0; - Thread->NpxIrql = 0; - Thread->ServiceTable = KeServiceDescriptorTable; - Thread->Queue = NULL; - KeInitializeSpinLock(&Thread->ApcQueueLock); - memset(&Thread->Timer, 0, sizeof(KTIMER)); - KeInitializeTimer(&Thread->Timer); - Thread->QueueListEntry.Flink = NULL; - Thread->QueueListEntry.Blink = NULL; - Thread->Affinity = Process->Affinity; - Thread->Preempted = 0; - Thread->ProcessReadyQueue = 0; - Thread->KernelStackResident = 1; - Thread->NextProcessor = 0; - Thread->CallbackStack = NULL; - Thread->Win32Thread = NULL; - Thread->TrapFrame = NULL; - Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState; - Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState; - Thread->EnableStackSwap = 0; - Thread->LargeStack = 0; - Thread->ResourceIndex = 0; - Thread->PreviousMode = KernelMode; - Thread->KernelTime = 0; - Thread->UserTime = 0; - memset(&Thread->SavedApcState, 0, sizeof(KAPC_STATE)); - - Thread->ApcStateIndex = OriginalApcEnvironment; - Thread->ApcQueueable = TRUE; - Thread->AutoAlignment = Process->AutoAlignment; - - KeInitializeApc(&Thread->SuspendApc, - Thread, - OriginalApcEnvironment, - PiSuspendThreadKernelRoutine, - PiSuspendThreadRundownRoutine, - PiSuspendThreadNormalRoutine, - KernelMode, - NULL); - KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 128); - - InsertTailList(&Process->ThreadListHead, - &Thread->ThreadListEntry); - Thread->FreezeCount = 0; - Thread->SuspendCount = 0; - - /* - * Do x86 specific part - */ + /* No, Release Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Return Old State */ + return PreviousState; } /* @@ -324,33 +908,33 @@ VOID STDCALL KeRevertToUserAffinityThread(VOID) { -#ifdef CONFIG_SMP - PKTHREAD CurrentThread; - KIRQL oldIrql; + PKTHREAD CurrentThread = KeGetCurrentThread(); + KIRQL OldIrql; - oldIrql = KeAcquireDispatcherDatabaseLock(); + ASSERT(CurrentThread->SystemAffinityActive != FALSE); + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); - CurrentThread = KeGetCurrentThread(); + /* Return to User Affinity */ + CurrentThread->Affinity = CurrentThread->UserAffinity; - ASSERT(CurrentThread->SystemAffinityActive != FALSE); - - /* Return to User Affinity */ - CurrentThread->Affinity = CurrentThread->UserAffinity; - - /* Disable System Affinity */ - CurrentThread->SystemAffinityActive = FALSE; - - if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber())) - { - KeReleaseDispatcherDatabaseLock(oldIrql); - } - else - { - CurrentThread->WaitIrql = oldIrql; - PsDispatchThreadNoLock(THREAD_STATE_READY); - KeLowerIrql(oldIrql); - } -#endif + /* Disable System Affinity */ + CurrentThread->SystemAffinityActive = FALSE; + + /* Check if we need to Dispatch a New thread */ + if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber())) { + + /* No, just release */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + } else { + + /* We need to dispatch a new thread */ + CurrentThread->WaitIrql = OldIrql; + KiDispatchThreadNoLock(THREAD_STATE_READY); + KeLowerIrql(OldIrql); + } } /* @@ -358,20 +942,26 @@ KeRevertToUserAffinityThread(VOID) */ CCHAR STDCALL -KeSetIdealProcessorThread ( - IN PKTHREAD Thread, - IN CCHAR Processor) +KeSetIdealProcessorThread(IN PKTHREAD Thread, + IN CCHAR Processor) { - CCHAR PreviousIdealProcessor; + CCHAR PreviousIdealProcessor; + KIRQL OldIrql; + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); - /* Save Old Ideal Processor */ - PreviousIdealProcessor = Thread->IdealProcessor; - - /* Set New Ideal Processor */ - Thread->IdealProcessor = Processor; - - /* Return Old Ideal Processor */ - return PreviousIdealProcessor; + /* Save Old Ideal Processor */ + PreviousIdealProcessor = Thread->IdealProcessor; + + /* Set New Ideal Processor */ + Thread->IdealProcessor = Processor; + + /* Release Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Return Old Ideal Processor */ + return PreviousIdealProcessor; } /* @@ -381,86 +971,461 @@ VOID STDCALL KeSetSystemAffinityThread(IN KAFFINITY Affinity) { -#ifdef CONFIG_SMP - PKTHREAD CurrentThread; - KIRQL oldIrql; + PKTHREAD CurrentThread = KeGetCurrentThread(); + KIRQL OldIrql; - oldIrql = KeAcquireDispatcherDatabaseLock(); + ASSERT(Affinity & ((1 << KeNumberProcessors) - 1)); + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); - CurrentThread = KeGetCurrentThread(); + /* Set the System Affinity Specified */ + CurrentThread->Affinity = Affinity; - ASSERT(Affinity & ((1 << KeNumberProcessors) - 1)); - - /* Set the System Affinity Specified */ - CurrentThread->Affinity = Affinity; - - /* Enable System Affinity */ - CurrentThread->SystemAffinityActive = TRUE; - - if (Affinity & (1 << KeGetCurrentProcessorNumber())) - { - KeReleaseDispatcherDatabaseLock(oldIrql); - } - else - { - CurrentThread->WaitIrql = oldIrql; - PsDispatchThreadNoLock(THREAD_STATE_READY); - KeLowerIrql(oldIrql); - } -#endif + /* Enable System Affinity */ + CurrentThread->SystemAffinityActive = TRUE; + + /* Check if we need to Dispatch a New thread */ + if (Affinity & (1 << KeGetCurrentProcessorNumber())) { + + /* No, just release */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + } else { + + /* We need to dispatch a new thread */ + CurrentThread->WaitIrql = OldIrql; + KiDispatchThreadNoLock(THREAD_STATE_READY); + KeLowerIrql(OldIrql); + } } /* * @implemented */ +LONG STDCALL +KeSetBasePriorityThread (PKTHREAD Thread, + LONG Increment) +/* + * Sets thread's base priority relative to the process' base priority + * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h + */ +{ + KPRIORITY Priority; + if (Increment < -2) + { + Increment = -2; + } + else if (Increment > 2) + { + Increment = 2; + } + Priority = ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment; + if (Priority < LOW_PRIORITY) + { + Priority = LOW_PRIORITY; + } + else if (Priority >= MAXIMUM_PRIORITY) + { + Thread->BasePriority = HIGH_PRIORITY; + } + KeSetPriorityThread(Thread, Priority); + return 1; +} + +/* + * @implemented + */ +KPRIORITY +STDCALL +KeSetPriorityThread(PKTHREAD Thread, + KPRIORITY Priority) +{ + KPRIORITY OldPriority; + KIRQL OldIrql; + PKTHREAD CurrentThread; + ULONG Mask; + int i; + PKPCR Pcr; + + if (Priority < LOW_PRIORITY || Priority >= MAXIMUM_PRIORITY) { + + KEBUGCHECK(0); + } + + OldIrql = KeAcquireDispatcherDatabaseLock(); + + OldPriority = Thread->Priority; + + if (OldPriority != Priority) { + + CurrentThread = KeGetCurrentThread(); + + if (Thread->State == THREAD_STATE_READY) { + + KiRemoveFromThreadList(Thread); + Thread->BasePriority = Thread->Priority = (CHAR)Priority; + KiInsertIntoThreadList(Priority, Thread); + + if (CurrentThread->Priority < Priority) { + + KiDispatchThreadNoLock(THREAD_STATE_READY); + KeLowerIrql(OldIrql); + return (OldPriority); + } + + } else if (Thread->State == THREAD_STATE_RUNNING) { + + Thread->BasePriority = Thread->Priority = (CHAR)Priority; + + if (Priority < OldPriority) { + + /* Check for threads with a higher priority */ + Mask = ~((1 << (Priority + 1)) - 1); + if (PriorityListMask & Mask) { + + if (Thread == CurrentThread) { + + KiDispatchThreadNoLock(THREAD_STATE_READY); + KeLowerIrql(OldIrql); + return (OldPriority); + + } else { + + for (i = 0; i < KeNumberProcessors; i++) { + + Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE); + + if (Pcr->Prcb->CurrentThread == Thread) { + + KeReleaseDispatcherDatabaseLockFromDpcLevel(); + KiRequestReschedule(i); + KeLowerIrql(OldIrql); + return (OldPriority); + } + } + } + } + } + } else { + + Thread->BasePriority = Thread->Priority = (CHAR)Priority; + } + } + + KeReleaseDispatcherDatabaseLock(OldIrql); + return(OldPriority); +} + +/* + * @implemented + * + * Sets thread's affinity + */ +NTSTATUS +STDCALL +KeSetAffinityThread(PKTHREAD Thread, + KAFFINITY Affinity) +{ + KIRQL OldIrql; + ULONG i; + PKPCR Pcr; + KAFFINITY ProcessorMask; + + DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread, Affinity); + + ASSERT(Affinity & ((1 << KeNumberProcessors) - 1)); + + OldIrql = KeAcquireDispatcherDatabaseLock(); + + Thread->UserAffinity = Affinity; + + if (Thread->SystemAffinityActive == FALSE) { + + Thread->Affinity = Affinity; + + if (Thread->State == THREAD_STATE_RUNNING) { + + ProcessorMask = 1 << KeGetCurrentKPCR()->ProcessorNumber; + if (Thread == KeGetCurrentThread()) { + + if (!(Affinity & ProcessorMask)) { + + KiDispatchThreadNoLock(THREAD_STATE_READY); + KeLowerIrql(OldIrql); + return STATUS_SUCCESS; + } + + } else { + + for (i = 0; i < KeNumberProcessors; i++) { + + Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE); + if (Pcr->Prcb->CurrentThread == Thread) { + + if (!(Affinity & ProcessorMask)) { + + KeReleaseDispatcherDatabaseLockFromDpcLevel(); + KiRequestReschedule(i); + KeLowerIrql(OldIrql); + return STATUS_SUCCESS; + } + + break; + } + } + + ASSERT (i < KeNumberProcessors); + } + } + } + + KeReleaseDispatcherDatabaseLock(OldIrql); + return STATUS_SUCCESS; +} + +/* + * @implemented + */ + /* The Increment Argument seems to be ignored by NT and always 0 when called */ VOID STDCALL KeTerminateThread(IN KPRIORITY Increment) { - /* The Increment Argument seems to be ignored by NT and always 0 when called */ - - /* Call our own internal routine */ - PsTerminateCurrentThread(0); + KIRQL OldIrql; + PKTHREAD Thread = KeGetCurrentThread(); + + /* Lock the Dispatcher Database and the APC Queue */ + DPRINT("Terminating\n"); + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Insert into the Reaper List */ + DPRINT("List: %p\n", PspReaperList); + ((PETHREAD)Thread)->ReaperLink = PspReaperList; + PspReaperList = (PETHREAD)Thread; + DPRINT("List: %p\n", PspReaperList); + + /* Check if it's active */ + if (PspReaping == FALSE) { + + /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */ + PspReaping = TRUE; + DPRINT("Terminating\n"); + KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue, + &PspReaperWorkItem.List, + FALSE); + } + + /* Handle Kernel Queues */ + if (Thread->Queue) { + + DPRINT("Waking Queue\n"); + RemoveEntryList(&Thread->QueueListEntry); + KiWakeQueue(Thread->Queue); + } + + /* Signal the thread */ + Thread->DispatcherHeader.SignalState = TRUE; + if (IsListEmpty(&Thread->DispatcherHeader.WaitListHead) != TRUE) { + + /* Satisfy waits */ + KiWaitTest((PVOID)Thread, Increment); + } + + /* Find a new Thread */ + KiDispatchThreadNoLock(THREAD_STATE_TERMINATED_1); } +/* + * FUNCTION: Tests whether there are any pending APCs for the current thread + * and if so the APCs will be delivered on exit from kernel mode + */ +BOOLEAN +STDCALL +KeTestAlertThread(IN KPROCESSOR_MODE AlertMode) +{ + KIRQL OldIrql; + PKTHREAD Thread = KeGetCurrentThread(); + BOOLEAN OldState; + + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* Lock the Dispatcher Database and the APC Queue */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + KiAcquireSpinLock(&Thread->ApcQueueLock); + + /* Save the old State */ + OldState = Thread->Alerted[AlertMode]; + + /* If the Thread is Alerted, Clear it */ + if (OldState) { + + Thread->Alerted[AlertMode] = FALSE; + + } else if ((AlertMode == UserMode) && (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) { + + /* If the mode is User and the Queue isn't empty, set Pending */ + Thread->ApcState.UserApcPending = TRUE; + } + + /* Release Locks and return the Old State */ + KiReleaseSpinLock(&Thread->ApcQueueLock); + KeReleaseDispatcherDatabaseLock(OldIrql); + return OldState; +} + +VOID +KiServiceCheck (VOID) +{ + PKTHREAD Thread = KeGetCurrentThread(); + + /* Check if we need to inialize Win32 for this Thread */ + if (Thread->ServiceTable != KeServiceDescriptorTableShadow) { + + /* We do. Initialize it and save the new table */ + PsInitWin32Thread((PETHREAD)Thread); + Thread->ServiceTable = KeServiceDescriptorTableShadow; + } +} + +/* + * + * NOT EXPORTED + */ +NTSTATUS +STDCALL +NtAlertResumeThread(IN HANDLE ThreadHandle, + OUT PULONG SuspendCount) +{ + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PETHREAD Thread; + NTSTATUS Status; + ULONG PreviousState; + + /* Check if parameters are valid */ + if(PreviousMode != KernelMode) { + + _SEH_TRY { + + ProbeForWrite(SuspendCount, + sizeof(HANDLE), + sizeof(ULONG)); + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + + /* Reference the Object */ + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_SUSPEND_RESUME, + PsThreadType, + PreviousMode, + (PVOID*)&Thread, + NULL); + + /* Check for Success */ + if (NT_SUCCESS(Status)) { + + /* Call the Kernel Function */ + PreviousState = KeAlertResumeThread(&Thread->Tcb); + + /* Dereference Object */ + ObDereferenceObject(Thread); + + if (SuspendCount) { + + _SEH_TRY { + + *SuspendCount = PreviousState; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } + + /* Return status */ + return Status; +} + +/* + * @implemented + * + * EXPORTED + */ +NTSTATUS +STDCALL +NtAlertThread (IN HANDLE ThreadHandle) +{ + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PETHREAD Thread; + NTSTATUS Status; + + /* Reference the Object */ + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_SUSPEND_RESUME, + PsThreadType, + PreviousMode, + (PVOID*)&Thread, + NULL); + + /* Check for Success */ + if (NT_SUCCESS(Status)) { + + /* + * Do an alert depending on the processor mode. If some kmode code wants to + * enforce a umode alert it should call KeAlertThread() directly. If kmode + * code wants to do a kmode alert it's sufficient to call it with Zw or just + * use KeAlertThread() directly + */ + KeAlertThread(&Thread->Tcb, PreviousMode); + + /* Dereference Object */ + ObDereferenceObject(Thread); + } + + /* Return status */ + return Status; +} NTSTATUS STDCALL NtDelayExecution(IN BOOLEAN Alertable, IN PLARGE_INTEGER DelayInterval) { - KPROCESSOR_MODE PreviousMode; - LARGE_INTEGER SafeInterval; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + LARGE_INTEGER SafeInterval; + NTSTATUS Status; - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode != KernelMode) - { - NTSTATUS Status = STATUS_SUCCESS; + /* Check if parameters are valid */ + if(PreviousMode != KernelMode) { - _SEH_TRY - { - ProbeForRead(DelayInterval, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - /* make a copy on the kernel stack and let DelayInterval point to it so - we don't need to wrap KeDelayExecutionThread in SEH! */ - SafeInterval = *DelayInterval; - DelayInterval = &SafeInterval; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } + _SEH_TRY { + + ProbeForRead(DelayInterval, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + + /* make a copy on the kernel stack and let DelayInterval point to it so + we don't need to wrap KeDelayExecutionThread in SEH! */ + SafeInterval = *DelayInterval; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; } - return KeDelayExecutionThread(PreviousMode, - Alertable, - DelayInterval); + /* Call the Kernel Function */ + Status = KeDelayExecutionThread(PreviousMode, + Alertable, + &SafeInterval); + + /* Return Status */ + return Status; } diff --git a/reactos/ntoskrnl/ke/main.c b/reactos/ntoskrnl/ke/main.c index eef694b0bc7..0efcd836158 100644 --- a/reactos/ntoskrnl/ke/main.c +++ b/reactos/ntoskrnl/ke/main.c @@ -1,37 +1,24 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/main.c * PURPOSE: Initalizes the kernel * - * PROGRAMMERS: David Welch (welch@cwcom.net) + * PROGRAMMERS: Alex Ionescu (cleaned up code, moved Executiv stuff to ex/init.c) + * David Welch (welch@cwcom.net) */ /* INCLUDES *****************************************************************/ #include -#include "../dbg/kdb.h" -#include -#include - -#ifdef HALDBG -#include -#else -#if defined(_MSC_VER) && (_MSC_VER <= 1200) -#define ps -#else -#define ps(args...) -#endif /* HALDBG */ - -#endif - #define NDEBUG #include /* GLOBALS *******************************************************************/ #define BUILD_OSCSDVERSION(major, minor) (((major & 0xFF) << 8) | (minor & 0xFF)) + + ULONG NtMajorVersion = 4; ULONG NtMinorVersion = 0; ULONG NtOSCSDVersion = BUILD_OSCSDVERSION(6, 0); @@ -58,19 +45,17 @@ EXPORTED ULONG KiDmaIoCoherency = 0; /* RISC Architectures only */ EXPORTED ULONG InitSafeBootMode = 0; /* KB83764 */ #endif /* __GNUC__ */ -static LOADER_MODULE KeLoaderModules[64]; +LOADER_MODULE KeLoaderModules[64]; static CHAR KeLoaderModuleStrings[64][256]; static CHAR KeLoaderCommandLine[256]; -static ADDRESS_RANGE KeMemoryMap[64]; -static ULONG KeMemoryMapRangeCount; -static ULONG_PTR FirstKrnlPhysAddr; -static ULONG_PTR LastKrnlPhysAddr; -static ULONG_PTR LastKernelAddress; +ADDRESS_RANGE KeMemoryMap[64]; +ULONG KeMemoryMapRangeCount; +ULONG_PTR FirstKrnlPhysAddr; +ULONG_PTR LastKrnlPhysAddr; +ULONG_PTR LastKernelAddress; volatile BOOLEAN Initialized = FALSE; -extern ULONG MmCoreDumpType; -extern CHAR KiTimerSystemAuditing; -extern PVOID Ki386InitialStackArray[MAXIMUM_PROCESSORS]; +ULONG KeLargestCacheLine = 0x40; /* FIXME: Arch-specific */ /* We allocate 4 pages, but we only use 3. The 4th is to guarantee page alignment */ ULONG kernel_stack[4096]; @@ -82,793 +67,60 @@ ULONG init_stack_top; ULONG trap_stack; ULONG trap_stack_top; +/* Cached modules from the loader block */ +PLOADER_MODULE CachedModules[MaximumCachedModuleType]; + /* FUNCTIONS ****************************************************************/ -static VOID INIT_FUNCTION -InitSystemSharedUserPage (PCSZ ParameterLine) -{ - UNICODE_STRING ArcDeviceName; - UNICODE_STRING ArcName; - UNICODE_STRING BootPath; - UNICODE_STRING DriveDeviceName; - UNICODE_STRING DriveName; - WCHAR DriveNameBuffer[20]; - PCHAR ParamBuffer; - PWCHAR ArcNameBuffer; - PCHAR p; - NTSTATUS Status; - ULONG Length; - OBJECT_ATTRIBUTES ObjectAttributes; - HANDLE Handle; - ULONG i; - BOOLEAN BootDriveFound; - - /* - * NOTE: - * The shared user page has been zeroed-out right after creation. - * There is NO need to do this again. - */ - - Ki386SetProcessorFeatures(); - - SharedUserData->NtProductType = NtProductWinNt; - SharedUserData->ProductTypeIsValid = TRUE; - SharedUserData->NtMajorVersion = 5; - SharedUserData->NtMinorVersion = 0; - - BootDriveFound = FALSE; - - /* - * Retrieve the current dos system path - * (e.g.: C:\reactos) from the given arc path - * (e.g.: multi(0)disk(0)rdisk(0)partititon(1)\reactos) - * Format: "\ [options...]" - */ - - /* create local parameter line copy */ - ParamBuffer = ExAllocatePool (PagedPool, 256); - strcpy (ParamBuffer, (char *)ParameterLine); - DPRINT("%s\n", ParamBuffer); - - /* cut options off */ - p = strchr (ParamBuffer, ' '); - if (p) - { - *p = 0; - } - DPRINT("%s\n", ParamBuffer); - - /* extract path */ - p = strchr (ParamBuffer, '\\'); - if (p) - { - DPRINT("Boot path: %s\n", p); - RtlCreateUnicodeStringFromAsciiz (&BootPath, p); - *p = 0; - } - else - { - DPRINT("Boot path: %s\n", "\\"); - RtlCreateUnicodeStringFromAsciiz (&BootPath, "\\"); - } - DPRINT("Arc name: %s\n", ParamBuffer); - - /* Only arc name left - build full arc name */ - ArcNameBuffer = ExAllocatePool (PagedPool, 256 * sizeof(WCHAR)); - swprintf (ArcNameBuffer, L"\\ArcName\\%S", ParamBuffer); - RtlInitUnicodeString (&ArcName, ArcNameBuffer); - DPRINT("Arc name: %wZ\n", &ArcName); - - /* free ParamBuffer */ - ExFreePool (ParamBuffer); - - /* allocate arc device name string */ - ArcDeviceName.Length = 0; - ArcDeviceName.MaximumLength = 256 * sizeof(WCHAR); - ArcDeviceName.Buffer = ExAllocatePool (PagedPool, 256 * sizeof(WCHAR)); - - InitializeObjectAttributes (&ObjectAttributes, - &ArcName, - OBJ_OPENLINK, - NULL, - NULL); - - Status = NtOpenSymbolicLinkObject (&Handle, - SYMBOLIC_LINK_ALL_ACCESS, - &ObjectAttributes); - RtlFreeUnicodeString (&ArcName); - if (!NT_SUCCESS(Status)) - { - RtlFreeUnicodeString (&BootPath); - RtlFreeUnicodeString (&ArcDeviceName); - CPRINT("NtOpenSymbolicLinkObject() failed (Status %x)\n", - Status); - - KEBUGCHECK (0x0); - } - - Status = NtQuerySymbolicLinkObject (Handle, - &ArcDeviceName, - &Length); - NtClose (Handle); - if (!NT_SUCCESS(Status)) - { - RtlFreeUnicodeString (&BootPath); - RtlFreeUnicodeString (&ArcDeviceName); - CPRINT("NtQuerySymbolicObject() failed (Status %x)\n", - Status); - - KEBUGCHECK (0x0); - } - DPRINT("Length: %lu ArcDeviceName: %wZ\n", Length, &ArcDeviceName); - - - /* allocate device name string */ - DriveDeviceName.Length = 0; - DriveDeviceName.MaximumLength = 256 * sizeof(WCHAR); - DriveDeviceName.Buffer = ExAllocatePool (PagedPool, 256 * sizeof(WCHAR)); - - for (i = 0; i < 26; i++) - { - swprintf (DriveNameBuffer, L"\\??\\%C:", 'A' + i); - RtlInitUnicodeString (&DriveName, - DriveNameBuffer); - - InitializeObjectAttributes (&ObjectAttributes, - &DriveName, - OBJ_OPENLINK, - NULL, - NULL); - - Status = NtOpenSymbolicLinkObject (&Handle, - SYMBOLIC_LINK_ALL_ACCESS, - &ObjectAttributes); - if (!NT_SUCCESS(Status)) - { - DPRINT("Failed to open link %wZ\n", - &DriveName); - continue; - } - - Status = NtQuerySymbolicLinkObject (Handle, - &DriveDeviceName, - &Length); - if (!NT_SUCCESS(Status)) - { - DPRINT("Failed query open link %wZ\n", - &DriveName); - continue; - } - DPRINT("Opened link: %wZ ==> %wZ\n", - &DriveName, &DriveDeviceName); - - if (!RtlCompareUnicodeString (&ArcDeviceName, &DriveDeviceName, FALSE)) - { - DPRINT("DOS Boot path: %c:%wZ\n", 'A' + i, &BootPath); - swprintf(SharedUserData->NtSystemRoot, - L"%C:%wZ", 'A' + i, &BootPath); - - BootDriveFound = TRUE; - } - - NtClose (Handle); - } - - RtlFreeUnicodeString (&BootPath); - RtlFreeUnicodeString (&DriveDeviceName); - RtlFreeUnicodeString (&ArcDeviceName); - - if (BootDriveFound == FALSE) - { - DbgPrint("No system drive found!\n"); - KEBUGCHECK (NO_BOOT_DEVICE); - } -} - -VOID INIT_FUNCTION -ExpInitializeExecutive(VOID) -{ - LARGE_INTEGER Timeout; - HANDLE ProcessHandle; - HANDLE ThreadHandle; - ULONG i; - ULONG start; - ULONG length; - PCHAR name; - CHAR str[50]; - NTSTATUS Status; - BOOLEAN SetupBoot; - PCHAR p1, p2; - ULONG MaxMem; - BOOLEAN NoGuiBoot = FALSE; - UNICODE_STRING Name; - HANDLE InitDoneEventHandle; - OBJECT_ATTRIBUTES ObjectAttributes; - - /* - * Fail at runtime if someone has changed various structures without - * updating the offsets used for the assembler code. - */ - ASSERT(FIELD_OFFSET(KTHREAD, InitialStack) == KTHREAD_INITIAL_STACK); - ASSERT(FIELD_OFFSET(KTHREAD, Teb) == KTHREAD_TEB); - ASSERT(FIELD_OFFSET(KTHREAD, KernelStack) == KTHREAD_KERNEL_STACK); - ASSERT(FIELD_OFFSET(KTHREAD, NpxState) == KTHREAD_NPX_STATE); - ASSERT(FIELD_OFFSET(KTHREAD, ServiceTable) == KTHREAD_SERVICE_TABLE); - ASSERT(FIELD_OFFSET(KTHREAD, PreviousMode) == KTHREAD_PREVIOUS_MODE); - ASSERT(FIELD_OFFSET(KTHREAD, TrapFrame) == KTHREAD_TRAP_FRAME); - ASSERT(FIELD_OFFSET(KTHREAD, CallbackStack) == KTHREAD_CALLBACK_STACK); - ASSERT(FIELD_OFFSET(KTHREAD, ApcState.Process) == KTHREAD_APCSTATE_PROCESS); - ASSERT(FIELD_OFFSET(KPROCESS, DirectoryTableBase) == - KPROCESS_DIRECTORY_TABLE_BASE); - ASSERT(FIELD_OFFSET(KPROCESS, IopmOffset) == KPROCESS_IOPM_OFFSET); - ASSERT(FIELD_OFFSET(KPROCESS, LdtDescriptor) == KPROCESS_LDT_DESCRIPTOR0); - ASSERT(FIELD_OFFSET(KTRAP_FRAME, Reserved9) == KTRAP_FRAME_RESERVED9); - ASSERT(FIELD_OFFSET(KV86M_TRAP_FRAME, SavedExceptionStack) == TF_SAVED_EXCEPTION_STACK); - ASSERT(FIELD_OFFSET(KV86M_TRAP_FRAME, regs) == TF_REGS); - ASSERT(FIELD_OFFSET(KV86M_TRAP_FRAME, orig_ebp) == TF_ORIG_EBP); - - ASSERT(FIELD_OFFSET(KPCR, Tib.ExceptionList) == KPCR_EXCEPTION_LIST); - ASSERT(FIELD_OFFSET(KPCR, Self) == KPCR_SELF); - ASSERT(FIELD_OFFSET(KPCR, PrcbData) + FIELD_OFFSET(KPRCB, CurrentThread) == KPCR_CURRENT_THREAD); - ASSERT(FIELD_OFFSET(KPCR, PrcbData) + FIELD_OFFSET(KPRCB, NpxThread) == KPCR_NPX_THREAD); - - ASSERT(FIELD_OFFSET(KTSS, Esp0) == KTSS_ESP0); - ASSERT(FIELD_OFFSET(KTSS, Eflags) == KTSS_EFLAGS); - ASSERT(FIELD_OFFSET(KTSS, IoMapBase) == KTSS_IOMAPBASE); - - ASSERT(sizeof(FX_SAVE_AREA) == SIZEOF_FX_SAVE_AREA); - - LdrInit1(); - - KeLowerIrql(DISPATCH_LEVEL); - - NtEarlyInitVdm(); - - p1 = (PCHAR)KeLoaderBlock.CommandLine; - - MaxMem = 0; - while(*p1 && (p2 = strchr(p1, '/'))) - { - p2++; - if (!_strnicmp(p2, "MAXMEM", 6)) - { - p2 += 6; - while (isspace(*p2)) p2++; - if (*p2 == '=') - { - p2++; - while(isspace(*p2)) p2++; - if (isdigit(*p2)) - { - while (isdigit(*p2)) - { - MaxMem = MaxMem * 10 + *p2 - '0'; - p2++; - } - break; - } - } - } - else if (!_strnicmp(p2, "NOGUIBOOT", 9)) - { - p2 += 9; - NoGuiBoot = TRUE; - } - else if (!_strnicmp(p2, "CRASHDUMP", 9)) - { - p2 += 9; - if (*p2 == ':') - { - p2++; - if (!_strnicmp(p2, "FULL", 4)) - { - MmCoreDumpType = MM_CORE_DUMP_TYPE_FULL; - } - else - { - MmCoreDumpType = MM_CORE_DUMP_TYPE_NONE; - } - } - } - p1 = p2; - } - - MmInit1(FirstKrnlPhysAddr, - LastKrnlPhysAddr, - LastKernelAddress, - (PADDRESS_RANGE)&KeMemoryMap, - KeMemoryMapRangeCount, - MaxMem > 8 ? MaxMem : 4096); - - /* Import ANSI code page table */ - for (i = 1; i < KeLoaderBlock.ModsCount; i++) - { - start = KeLoaderModules[i].ModStart; - length = KeLoaderModules[i].ModEnd - start; - - name = strrchr((PCHAR)KeLoaderModules[i].String, '\\'); - if (name == NULL) - { - name = (PCHAR)KeLoaderModules[i].String; - } - else - { - name++; - } - - if (!_stricmp (name, "ansi.nls")) - { - RtlpImportAnsiCodePage((PUSHORT)start, length); - } - } - - /* Import OEM code page table */ - for (i = 1; i < KeLoaderBlock.ModsCount; i++) - { - start = KeLoaderModules[i].ModStart; - length = KeLoaderModules[i].ModEnd - start; - - name = strrchr((PCHAR)KeLoaderModules[i].String, '\\'); - if (name == NULL) - { - name = (PCHAR)KeLoaderModules[i].String; - } - else - { - name++; - } - - if (!_stricmp (name, "oem.nls")) - { - RtlpImportOemCodePage((PUSHORT)start, length); - } - } - - /* Import Unicode casemap table */ - for (i = 1; i < KeLoaderBlock.ModsCount; i++) - { - start = KeLoaderModules[i].ModStart; - length = KeLoaderModules[i].ModEnd - start; - - name = strrchr((PCHAR)KeLoaderModules[i].String, '\\'); - if (name == NULL) - { - name = (PCHAR)KeLoaderModules[i].String; - } - else - { - name++; - } - - if (!_stricmp (name, "casemap.nls")) - { - RtlpImportUnicodeCasemap((PUSHORT)start, length); - } - } - - /* Create initial NLS tables */ - RtlpCreateInitialNlsTables(); - - /* - * Initialize the kernel debugger - */ - KdInitSystem (1, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); - - KeInit2(); - -#if 0 - if (KeMemoryMapRangeCount > 0) - { - DPRINT1("MemoryMap:\n"); - for (i = 0; i < KeMemoryMapRangeCount; i++) - { - switch(KeMemoryMap[i].Type) - { - case 1: - strcpy(str, "(usable)"); - break; - case 2: - strcpy(str, "(reserved)"); - break; - case 3: - strcpy(str, "(ACPI data)"); - break; - case 4: - strcpy(str, "(ACPI NVS)"); - break; - default: - sprintf(str, "type %lu", KeMemoryMap[i].Type); - } - DPRINT1("%08x - %08x %s\n", KeMemoryMap[i].BaseAddrLow, KeMemoryMap[i].BaseAddrLow + KeMemoryMap[i].LengthLow, str); - } - } -#endif - - KeLowerIrql(PASSIVE_LEVEL); - - if (!SeInit1()) - KEBUGCHECK(SECURITY_INITIALIZATION_FAILED); - - ObInit(); - ExInit2(); - MmInit2(); - - if (!SeInit2()) - KEBUGCHECK(SECURITY1_INITIALIZATION_FAILED); - - KeNumberProcessors = 1; - - PiInitProcessManager(); - - if (KdPollBreakIn ()) - { - DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C); - } - - /* Initialize all processors */ - while (!HalAllProcessorsStarted()) - { - PVOID ProcessorStack; - - KePrepareForApplicationProcessorInit(KeNumberProcessors); - PsPrepareForApplicationProcessorInit(KeNumberProcessors); - - /* Allocate a stack for use when booting the processor */ - ProcessorStack = Ki386InitialStackArray[((int)KeNumberProcessors)] + MM_STACK_SIZE; - - HalStartNextProcessor(0, (ULONG)ProcessorStack - 2*sizeof(FX_SAVE_AREA)); - KeNumberProcessors++; - } - - /* - * Initialize various critical subsystems - */ - HalInitSystem(1, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); - - ExInit3(); - KdInit1(); - IoInit(); - PoInit(); - CmInitializeRegistry(); - MmInit3(); - CcInit(); - KdInit2(); - FsRtlpInitFileLockingImplementation(); - - /* Report all resources used by hal */ - HalReportResourceUsage(); - - /* - * Clear the screen to blue - */ - HalInitSystem(2, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); - - /* - * Display version number and copyright/warranty message - */ - HalDisplayString("Starting ReactOS "KERNEL_VERSION_STR" (Build " - KERNEL_VERSION_BUILD_STR")\n"); - HalDisplayString(RES_STR_LEGAL_COPYRIGHT); - HalDisplayString("\n\nReactOS is free software, covered by the GNU General " - "Public License, and you\n"); - HalDisplayString("are welcome to change it and/or distribute copies of it " - "under certain\n"); - HalDisplayString("conditions. There is absolutely no warranty for " - "ReactOS.\n\n"); - - if (KeNumberProcessors > 1) - { - sprintf(str, - "Found %d system processors. [%lu MB Memory]\n", - KeNumberProcessors, - (KeLoaderBlock.MemHigher + 1088)/ 1024); - } - else - { - sprintf(str, - "Found 1 system processor. [%lu MB Memory]\n", - (KeLoaderBlock.MemHigher + 1088)/ 1024); - } - HalDisplayString(str); - - KdInit3(); - - - /* Create the NLS section */ - RtlpCreateNlsSection(); - - /* - * Initalize services loaded at boot time - */ - DPRINT("%d files loaded\n",KeLoaderBlock.ModsCount); - for (i=0; i < KeLoaderBlock.ModsCount; i++) - { - CPRINT("Module: '%s' at %08lx, length 0x%08lx\n", - KeLoaderModules[i].String, - KeLoaderModules[i].ModStart, - KeLoaderModules[i].ModEnd - KeLoaderModules[i].ModStart); - } - - /* Pass 1: import system hive registry chunk */ - SetupBoot = TRUE; - for (i = 1; i < KeLoaderBlock.ModsCount; i++) - { - start = KeLoaderModules[i].ModStart; - length = KeLoaderModules[i].ModEnd - start; - - DPRINT("Module: '%s'\n", (PCHAR)KeLoaderModules[i].String); - name = strrchr((PCHAR)KeLoaderModules[i].String, '\\'); - if (name == NULL) - { - name = (PCHAR)KeLoaderModules[i].String; - } - else - { - name++; - } - - if (!_stricmp (name, "system") || - !_stricmp (name, "system.hiv")) - { - CPRINT("Process system hive registry chunk at %08lx\n", start); - SetupBoot = FALSE; - CmImportSystemHive((PCHAR)start, length); - } - } - - /* Pass 2: import hardware hive registry chunk */ - for (i = 1; i < KeLoaderBlock.ModsCount; i++) - { - start = KeLoaderModules[i].ModStart; - length = KeLoaderModules[i].ModEnd - start; - name = (PCHAR)KeLoaderModules[i].String; - if (!_stricmp (name, "hardware") || - !_stricmp (name, "hardware.hiv")) - { - CPRINT("Process hardware hive registry chunk at %08lx\n", start); - CmImportHardwareHive((PCHAR)start, length); - } - } - - /* Create dummy keys if no hardware hive was found */ - CmImportHardwareHive (NULL, 0); - - /* Initialize volatile registry settings */ - if (SetupBoot == FALSE) - { - CmInit2((PCHAR)KeLoaderBlock.CommandLine); - } - - /* Initialize the time zone information from the registry */ - ExpInitTimeZoneInfo(); - - /* - * Enter the kernel debugger before starting up the boot drivers - */ -#ifdef KDBG - KdbEnter(); -#endif /* KDBG */ - - IoCreateDriverList(); - - IoInit2(); - - /* Initialize Callbacks before drivers */ - ExpInitializeCallbacks(); - - /* Start boot logging */ - IopInitBootLog(); - p1 = (PCHAR)KeLoaderBlock.CommandLine; - while (*p1 && (p2 = strchr(p1, '/'))) - { - p2++; - if (!_strnicmp(p2, "BOOTLOG", 7)) - { - p2 += 7; - IopStartBootLog(); - } - - p1 = p2; - } - - /* - * Load boot start drivers - */ - IopInitializeBootDrivers(); - - /* Display the boot screen image if not disabled */ - if (!NoGuiBoot) - { - InbvEnableBootDriver(TRUE); - } - - /* Create ARC names for boot devices */ - IoCreateArcNames(); - - /* Create the SystemRoot symbolic link */ - CPRINT("CommandLine: %s\n", (PCHAR)KeLoaderBlock.CommandLine); - DPRINT1("MmSystemRangeStart: 0x%x PageDir: 0x%x\n", MmSystemRangeStart, KeLoaderBlock.PageDirectoryStart); - Status = IoCreateSystemRootLink((PCHAR)KeLoaderBlock.CommandLine); - if (!NT_SUCCESS(Status)) - { - DbgPrint ( "IoCreateSystemRootLink FAILED: (0x%x) - ", Status ); - DbgPrintErrorMessage ( Status ); - KEBUGCHECK(INACCESSIBLE_BOOT_DEVICE); - } - -#if defined(KDBG) || defined(DBG) - KdbInitProfiling2(); -#endif /* KDBG */ - - /* On the assumption that we can now access disks start up the debug - * logger thread */ - if ((KdDebuggerEnabled == TRUE) && (KdDebugState & KD_DEBUG_BOOTLOG)) - { - DebugLogInit2(); - } - - PiInitDefaultLocale(); - - /* - * Load services for devices found by PnP manager - */ - IopInitializePnpServices(IopRootDeviceNode, FALSE); - - /* - * Load system start drivers - */ - IopInitializeSystemDrivers(); - - IoDestroyDriverList(); - - /* Stop boot logging */ - IopStopBootLog(); - - /* - * Assign drive letters - */ - IoAssignDriveLetters ((PLOADER_PARAMETER_BLOCK)&KeLoaderBlock, - NULL, - NULL, - NULL); - - /* - * Initialize shared user page: - * - set dos system path, dos device map, etc. - */ - InitSystemSharedUserPage ((PCHAR)KeLoaderBlock.CommandLine); - - /* Create 'ReactOSInitDone' event */ - RtlInitUnicodeString(&Name, L"\\ReactOSInitDone"); - InitializeObjectAttributes(&ObjectAttributes, - &Name, - 0, - NULL, - NULL); - Status = ZwCreateEvent(&InitDoneEventHandle, - EVENT_ALL_ACCESS, - &ObjectAttributes, - SynchronizationEvent, - FALSE); /* Not signalled */ - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to create 'ReactOSInitDone' event (Status 0x%x)\n", Status); - InitDoneEventHandle = INVALID_HANDLE_VALUE; - } - - /* - * Launch initial process - */ - Status = LdrLoadInitialProcess(&ProcessHandle, - &ThreadHandle); - if (!NT_SUCCESS(Status)) - { - KEBUGCHECKEX(SESSION4_INITIALIZATION_FAILED, Status, 0, 0, 0); - } - - if (InitDoneEventHandle != INVALID_HANDLE_VALUE) - { - HANDLE Handles[2]; /* Init event, Initial process */ - - Handles[0] = InitDoneEventHandle; - Handles[1] = ProcessHandle; - - /* Wait for the system to be initialized */ - Timeout.QuadPart = (LONGLONG)-1200000000; /* 120 second timeout */ - Status = ZwWaitForMultipleObjects(((LONG) sizeof(Handles) / sizeof(HANDLE)), - Handles, - WaitAny, - FALSE, /* Non-alertable */ - &Timeout); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWaitForMultipleObjects failed with status 0x%x!\n", Status); - } - else if (Status == STATUS_TIMEOUT) - { - DPRINT1("WARNING: System not initialized after 120 seconds.\n"); - } - else if (Status == STATUS_WAIT_0 + 1) - { - /* - * Crash the system if the initial process was terminated. - */ - KEBUGCHECKEX(SESSION5_INITIALIZATION_FAILED, Status, 0, 0, 0); - } - - if (!NoGuiBoot) - { - InbvEnableBootDriver(FALSE); - } - - ZwSetEvent(InitDoneEventHandle, NULL); - - ZwClose(InitDoneEventHandle); - } - else - { - /* On failure to create 'ReactOSInitDone' event, go to text mode ASAP */ - if (!NoGuiBoot) - { - InbvEnableBootDriver(FALSE); - } - - /* - * Crash the system if the initial process terminates within 5 seconds. - */ - Timeout.QuadPart = (LONGLONG)-50000000; /* 5 second timeout */ - Status = ZwWaitForSingleObject(ProcessHandle, - FALSE, - &Timeout); - if (Status != STATUS_TIMEOUT) - { - KEBUGCHECKEX(SESSION5_INITIALIZATION_FAILED, Status, 1, 0, 0); - } - } /* - * Tell ke/timer.c it's okay to run. + * @implemented */ - - KiTimerSystemAuditing = 1; - - ZwClose(ThreadHandle); - ZwClose(ProcessHandle); +ULONG +STDCALL +KeGetRecommendedSharedDataAlignment(VOID) +{ + return KeLargestCacheLine; } -VOID __attribute((noinline)) +VOID +__attribute((noinline)) KiSystemStartup(BOOLEAN BootProcessor) { - DPRINT1("KiSystemStartup(%d)\n", BootProcessor); - if (BootProcessor) - { - } - else - { - KeApplicationProcessorInit(); - } + DPRINT("KiSystemStartup(%d)\n", BootProcessor); + + /* Initialize the Application Processor */ + if (!BootProcessor) KeApplicationProcessorInit(); + + /* Initialize the Processor with HAL */ + HalInitializeProcessor(KeNumberProcessors, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); - HalInitializeProcessor(KeNumberProcessors, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); - - if (BootProcessor) - { - ExpInitializeExecutive(); - MiFreeInitMemory(); - /* Never returns */ - PsTerminateSystemThread(STATUS_SUCCESS); - } - else - { - /* Do application processor initialization */ - PsApplicationProcessorInit(); - KeLowerIrql(PASSIVE_LEVEL); - PsIdleThreadMain(NULL); - } - KEBUGCHECK(0); - for(;;); + /* Load the Kernel if this is the Boot CPU, else inialize the App CPU only */ + if (BootProcessor) { + + /* Initialize the Kernel Executive */ + ExpInitializeExecutive(); + + /* Free Initial Memory */ + MiFreeInitMemory(); + + /* Never returns */ + PspExitThread(STATUS_SUCCESS); + + } else { + + /* Do application processor initialization */ + PsApplicationProcessorInit(); + + /* Lower IRQL and go to Idle Thread */ + KeLowerIrql(PASSIVE_LEVEL); + PsIdleThreadMain(NULL); + } + + /* Bug Check and loop forever if anything failed */ + KEBUGCHECK(0); + for(;;); } -VOID INIT_FUNCTION -_main (ULONG MultiBootMagic, PLOADER_PARAMETER_BLOCK _LoaderBlock) /* * FUNCTION: Called by the boot loader to start the kernel * ARGUMENTS: @@ -877,173 +129,158 @@ _main (ULONG MultiBootMagic, PLOADER_PARAMETER_BLOCK _LoaderBlock) * NOTE: The boot parameters are stored in low memory which will become * invalid after the memory managment is initialized so we make a local copy. */ +VOID +INIT_FUNCTION +_main(ULONG MultiBootMagic, + PLOADER_PARAMETER_BLOCK _LoaderBlock) { - ULONG i; - ULONG size; - ULONG HalBase; - ULONG DriverBase; - ULONG DriverSize; + ULONG i; + ULONG size; + ULONG HalBase; + ULONG DriverBase; + ULONG DriverSize; + PIMAGE_NT_HEADERS NtHeader; + PIMAGE_OPTIONAL_HEADER OptHead; + CHAR* s; - /* Set up the Stacks */ - trap_stack = PAGE_ROUND_UP(&double_trap_stack); - trap_stack_top = trap_stack + 3 * PAGE_SIZE; - init_stack = PAGE_ROUND_UP(&kernel_stack); - init_stack_top = init_stack + 3 * PAGE_SIZE; + /* Set up the Stacks (Initial Kernel Stack and Double Trap Stack)*/ + trap_stack = PAGE_ROUND_UP(&double_trap_stack); + trap_stack_top = trap_stack + 3 * PAGE_SIZE; + init_stack = PAGE_ROUND_UP(&kernel_stack); + init_stack_top = init_stack + 3 * PAGE_SIZE; - /* - * Copy the parameters to a local buffer because lowmem will go away - */ - memcpy(&KeLoaderBlock, _LoaderBlock, sizeof(LOADER_PARAMETER_BLOCK)); - memcpy(&KeLoaderModules[1], (PVOID)KeLoaderBlock.ModsAddr, - sizeof(LOADER_MODULE) * KeLoaderBlock.ModsCount); - KeLoaderBlock.ModsCount++; - KeLoaderBlock.ModsAddr = (ULONG)&KeLoaderModules; + /* Copy the Loader Block Data locally since Low-Memory will be wiped */ + memcpy(&KeLoaderBlock, _LoaderBlock, sizeof(LOADER_PARAMETER_BLOCK)); + memcpy(&KeLoaderModules[1], + (PVOID)KeLoaderBlock.ModsAddr, + sizeof(LOADER_MODULE) * KeLoaderBlock.ModsCount); + KeLoaderBlock.ModsCount++; + KeLoaderBlock.ModsAddr = (ULONG)&KeLoaderModules; - /* Save the Base Address */ - MmSystemRangeStart = (PVOID)KeLoaderBlock.KernelBase; + /* Save the Base Address */ + MmSystemRangeStart = (PVOID)KeLoaderBlock.KernelBase; - /* - * Convert a path specification in the grub format to one understood by the - * rest of the kernel. - */ - if (((PUCHAR)_LoaderBlock->CommandLine)[0] == '(') - { - ULONG DiskNumber = 0, PartNumber = 0; - PCH p; - CHAR Temp[256]; - PCH options; - PCH s1; - - if (((PUCHAR)_LoaderBlock->CommandLine)[1] == 'h' && - ((PUCHAR)_LoaderBlock->CommandLine)[2] == 'd') - { - DiskNumber = ((PCHAR)_LoaderBlock->CommandLine)[3] - '0'; - PartNumber = ((PCHAR)_LoaderBlock->CommandLine)[5] - '0'; - } - strcpy(Temp, &((PCHAR)_LoaderBlock->CommandLine)[7]); - if ((options = strchr(Temp, ' ')) != NULL) - { - *options = 0; - options++; - } - else - { - options = ""; - } - if ((s1 = strrchr(Temp, '/')) != NULL) - { - *s1 = 0; - if ((s1 = strrchr(Temp, '/')) != NULL) - { - *s1 = 0; - } - } - sprintf(KeLoaderCommandLine, - "multi(0)disk(0)rdisk(%lu)partition(%lu)%s %s", - DiskNumber, PartNumber + 1, Temp, options); - - p = KeLoaderCommandLine; - while (*p != 0 && *p != ' ') - { - if ((*p) == '/') - { - (*p) = '\\'; - } - p++; - } - DPRINT1("Command Line: %s\n", KeLoaderCommandLine); - } - else - { - strcpy(KeLoaderCommandLine, (PCHAR)_LoaderBlock->CommandLine); - } - KeLoaderBlock.CommandLine = (ULONG)KeLoaderCommandLine; + /* Set the Command Line */ + strcpy(KeLoaderCommandLine, (PCHAR)_LoaderBlock->CommandLine); + KeLoaderBlock.CommandLine = (ULONG)KeLoaderCommandLine; - strcpy(KeLoaderModuleStrings[0], "ntoskrnl.exe"); - KeLoaderModules[0].String = (ULONG)KeLoaderModuleStrings[0]; - KeLoaderModules[0].ModStart = KERNEL_BASE; - /* Take this value from the PE... */ - PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader((PVOID)KeLoaderModules[0].ModStart); - PIMAGE_OPTIONAL_HEADER OptHead = &NtHeader->OptionalHeader; + /* Write the first Module (the Kernel) */ + strcpy(KeLoaderModuleStrings[0], "ntoskrnl.exe"); + KeLoaderModules[0].String = (ULONG)KeLoaderModuleStrings[0]; + KeLoaderModules[0].ModStart = KERNEL_BASE; + + /* Read PE Data */ + NtHeader = RtlImageNtHeader((PVOID)KeLoaderModules[0].ModStart); + OptHead = &NtHeader->OptionalHeader; + + /* Set Kernel Ending */ KeLoaderModules[0].ModEnd = KeLoaderModules[0].ModStart + PAGE_ROUND_UP((ULONG)OptHead->SizeOfImage); - for (i = 1; i < KeLoaderBlock.ModsCount; i++) - { - CHAR* s; - if ((s = strrchr((PCHAR)KeLoaderModules[i].String, '/')) != 0) - { - strcpy(KeLoaderModuleStrings[i], s + 1); - } - else - { - strcpy(KeLoaderModuleStrings[i], (PCHAR)KeLoaderModules[i].String); - } - KeLoaderModules[i].ModStart -= 0x200000; - KeLoaderModules[i].ModStart += KERNEL_BASE; - KeLoaderModules[i].ModEnd -= 0x200000; - KeLoaderModules[i].ModEnd += KERNEL_BASE; - KeLoaderModules[i].String = (ULONG)KeLoaderModuleStrings[i]; - } - - LastKernelAddress = PAGE_ROUND_UP(KeLoaderModules[KeLoaderBlock.ModsCount - 1].ModEnd); - - /* Low level architecture specific initialization */ - KeInit1((PCHAR)KeLoaderBlock.CommandLine, &LastKernelAddress); - - HalBase = KeLoaderModules[1].ModStart; - DriverBase = LastKernelAddress; - LdrHalBase = (ULONG_PTR)DriverBase; - - LdrInitModuleManagement(); - - /* - * Process hal.dll - */ - LdrSafePEProcessModule((PVOID)HalBase, (PVOID)DriverBase, (PVOID)KERNEL_BASE, &DriverSize); - - LastKernelAddress += PAGE_ROUND_UP(DriverSize); - - /* - * Process ntoskrnl.exe - */ - LdrSafePEProcessModule((PVOID)KERNEL_BASE, (PVOID)KERNEL_BASE, (PVOID)DriverBase, &DriverSize); - - /* Now our imports from HAL are fixed. This is the first */ - /* time in the boot process that we can use HAL */ - - FirstKrnlPhysAddr = KeLoaderModules[0].ModStart - KERNEL_BASE + 0x200000; - LastKrnlPhysAddr = LastKernelAddress - KERNEL_BASE + 0x200000; - - KeMemoryMapRangeCount = 0; - if (KeLoaderBlock.Flags & MB_FLAGS_MMAP_INFO) - { - /* We have a memory map from the nice BIOS */ - size = *((PULONG)(KeLoaderBlock.MmapAddr - sizeof(ULONG))); - i = 0; - while (i < KeLoaderBlock.MmapLength) - { - memcpy (&KeMemoryMap[KeMemoryMapRangeCount], - (PVOID)(KeLoaderBlock.MmapAddr + i), - sizeof(ADDRESS_RANGE)); - KeMemoryMapRangeCount++; - i += size; + + /* Create a block for each module */ + for (i = 1; i < KeLoaderBlock.ModsCount; i++) { + + /* Check if we have to copy the path or not */ + if ((s = strrchr((PCHAR)KeLoaderModules[i].String, '/')) != 0) { + + strcpy(KeLoaderModuleStrings[i], s + 1); + + } else { + + strcpy(KeLoaderModuleStrings[i], (PCHAR)KeLoaderModules[i].String); } - KeLoaderBlock.MmapLength = KeMemoryMapRangeCount * sizeof(ADDRESS_RANGE); - KeLoaderBlock.MmapAddr = (ULONG)KeMemoryMap; - } - else - { - KeLoaderBlock.MmapLength = 0; - KeLoaderBlock.MmapAddr = (ULONG)KeMemoryMap; + + /* Substract the base Address in Physical Memory */ + KeLoaderModules[i].ModStart -= 0x200000; + + /* Add the Kernel Base Address in Virtual Memory */ + KeLoaderModules[i].ModStart += KERNEL_BASE; + + /* Substract the base Address in Physical Memory */ + KeLoaderModules[i].ModEnd -= 0x200000; + + /* Add the Kernel Base Address in Virtual Memory */ + KeLoaderModules[i].ModEnd += KERNEL_BASE; + + /* Select the proper String */ + KeLoaderModules[i].String = (ULONG)KeLoaderModuleStrings[i]; } - KdInitSystem (0, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); - HalInitSystem (0, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); + /* Choose last module address as the final kernel address */ + LastKernelAddress = PAGE_ROUND_UP(KeLoaderModules[KeLoaderBlock.ModsCount - 1].ModEnd); - DPRINT1("_main (%x, %x)\n", MultiBootMagic, _LoaderBlock); + /* Low level architecture specific initialization */ + KeInit1((PCHAR)KeLoaderBlock.CommandLine, &LastKernelAddress); + + /* Select the HAL Base */ + HalBase = KeLoaderModules[1].ModStart; + + /* Choose Driver Base */ + DriverBase = LastKernelAddress; + LdrHalBase = (ULONG_PTR)DriverBase; + + /* Initialize Module Management */ + LdrInitModuleManagement(); + + /* Load HAL.DLL with the PE Loader */ + LdrSafePEProcessModule((PVOID)HalBase, + (PVOID)DriverBase, + (PVOID)KERNEL_BASE, + &DriverSize); + + /* Increase the last kernel address with the size of HAL */ + LastKernelAddress += PAGE_ROUND_UP(DriverSize); + /* Load the Kernel with the PE Loader */ + LdrSafePEProcessModule((PVOID)KERNEL_BASE, + (PVOID)KERNEL_BASE, + (PVOID)DriverBase, + &DriverSize); - KiSystemStartup(1); + /* Now select the final beginning and ending Kernel Addresses */ + FirstKrnlPhysAddr = KeLoaderModules[0].ModStart - KERNEL_BASE + 0x200000; + LastKrnlPhysAddr = LastKernelAddress - KERNEL_BASE + 0x200000; + + KeMemoryMapRangeCount = 0; + if (KeLoaderBlock.Flags & MB_FLAGS_MMAP_INFO) { + + /* We have a memory map from the nice BIOS */ + size = *((PULONG)(KeLoaderBlock.MmapAddr - sizeof(ULONG))); + i = 0; + + /* Map it until we run out of size */ + while (i < KeLoaderBlock.MmapLength) { + + /* Copy into the Kernel Memory Map */ + memcpy (&KeMemoryMap[KeMemoryMapRangeCount], + (PVOID)(KeLoaderBlock.MmapAddr + i), + sizeof(ADDRESS_RANGE)); + + /* Increase Memory Map Count */ + KeMemoryMapRangeCount++; + + /* Increase Size */ + i += size; + } + + /* Save data */ + KeLoaderBlock.MmapLength = KeMemoryMapRangeCount * sizeof(ADDRESS_RANGE); + KeLoaderBlock.MmapAddr = (ULONG)KeMemoryMap; + + } else { + + /* Nothing from BIOS */ + KeLoaderBlock.MmapLength = 0; + KeLoaderBlock.MmapAddr = (ULONG)KeMemoryMap; + } + + /* Initialize the Debugger */ + KdInitSystem (0, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); + + /* Initialize HAL */ + HalInitSystem (0, (PLOADER_PARAMETER_BLOCK)&KeLoaderBlock); + + /* Do general System Startup */ + KiSystemStartup(1); } /* EOF */ - diff --git a/reactos/ntoskrnl/ke/mutex.c b/reactos/ntoskrnl/ke/mutex.c index 3532781082c..2ff0507fd5c 100644 --- a/reactos/ntoskrnl/ke/mutex.c +++ b/reactos/ntoskrnl/ke/mutex.c @@ -1,16 +1,20 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/mutex.c - * PURPOSE: Implements mutex + * PURPOSE: Implements Mutexes and Mutants (that silly davec...) * - * PROGRAMMERS: David Welch (welch@mcmail.com) + * PROGRAMMERS: + * Alex Ionescu (alex@relsoft.net) - Reorganized/commented some of the code. + * Simplified some functions, fixed some return values and + * corrected some minor bugs, added debug output. + * David Welch (welch@mcmail.com) */ /* INCLUDES *****************************************************************/ #include +#define NDEBUG #include /* FUNCTIONS *****************************************************************/ @@ -18,176 +22,232 @@ /* * @implemented */ -VOID STDCALL -KeInitializeMutex(IN PKMUTEX Mutex, - IN ULONG Level) -{ - KeInitializeDispatcherHeader(&Mutex->Header, - InternalMutexType, - sizeof(KMUTEX) / sizeof(ULONG), - 1); - Mutex->MutantListEntry.Flink = NULL; - Mutex->MutantListEntry.Blink = NULL; - Mutex->OwnerThread = NULL; - Mutex->Abandoned = FALSE; - Mutex->ApcDisable = 1; -} - -/* - * @implemented - */ -LONG STDCALL -KeReadStateMutex(IN PKMUTEX Mutex) -{ - return(Mutex->Header.SignalState); -} - -/* - * @implemented - */ -LONG STDCALL -KeReleaseMutex(IN PKMUTEX Mutex, - IN BOOLEAN Wait) -{ - KIRQL OldIrql; - - OldIrql = KeAcquireDispatcherDatabaseLock(); - if (Mutex->OwnerThread != KeGetCurrentThread()) - { - DbgPrint("THREAD_NOT_MUTEX_OWNER: Mutex %p\n", Mutex); - KEBUGCHECK(THREAD_NOT_MUTEX_OWNER); - } - Mutex->Header.SignalState++; - ASSERT(Mutex->Header.SignalState <= 1); - if (Mutex->Header.SignalState == 1) - { - Mutex->OwnerThread = NULL; - if (Mutex->MutantListEntry.Flink && Mutex->MutantListEntry.Blink) - RemoveEntryList(&Mutex->MutantListEntry); - KiDispatcherObjectWake(&Mutex->Header, IO_NO_INCREMENT); - } - - if (Wait == FALSE) - { - KeReleaseDispatcherDatabaseLock(OldIrql); - } - else - { - KTHREAD *Thread = KeGetCurrentThread(); - Thread->WaitNext = TRUE; - Thread->WaitIrql = OldIrql; - } - - return(0); -} - -/* - * @implemented - */ -NTSTATUS STDCALL -KeWaitForMutexObject(IN PKMUTEX Mutex, - IN KWAIT_REASON WaitReason, - IN KPROCESSOR_MODE WaitMode, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER Timeout) -{ - return(KeWaitForSingleObject(Mutex,WaitReason,WaitMode,Alertable,Timeout)); -} - - -/* - * @implemented - */ -VOID STDCALL +VOID +STDCALL KeInitializeMutant(IN PKMUTANT Mutant, - IN BOOLEAN InitialOwner) + IN BOOLEAN InitialOwner) { - if (InitialOwner == TRUE) - { - KeInitializeDispatcherHeader(&Mutant->Header, - InternalMutexType, - sizeof(KMUTANT) / sizeof(ULONG), - 0); - InsertTailList(&KeGetCurrentThread()->MutantListHead, - &Mutant->MutantListEntry); - Mutant->OwnerThread = KeGetCurrentThread(); + ULONG Signaled = TRUE; + PKTHREAD CurrentThread = NULL; + KIRQL OldIrql; + + DPRINT("KeInitializeMutant: %x\n", Mutant); + + /* Check if we have an initial owner */ + if (InitialOwner == TRUE) { + + /* In this case, the object is not signaled */ + Signaled = FALSE; + + /* We also need to associate a thread */ + CurrentThread = KeGetCurrentThread(); + + /* We're about to touch the Thread, so lock the Dispatcher */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* And insert it into its list */ + InsertTailList(&CurrentThread->MutantListHead, &Mutant->MutantListEntry); + + /* Release Dispatcher Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); + DPRINT("Mutant with Initial Owner\n"); + + } else { + + /* In this case, we don't have an owner yet */ + Mutant->OwnerThread = NULL; } - else - { - KeInitializeDispatcherHeader(&Mutant->Header, - InternalMutexType, - sizeof(KMUTANT) / sizeof(ULONG), - 1); - Mutant->MutantListEntry.Flink = NULL; - Mutant->MutantListEntry.Blink = NULL; - Mutant->OwnerThread = NULL; - } - Mutant->Abandoned = FALSE; - Mutant->ApcDisable = 0; + + /* Now we set up the Dispatcher Header */ + KeInitializeDispatcherHeader(&Mutant->Header, + MutantObject, + sizeof(KMUTANT) / sizeof(ULONG), + Signaled); + + /* Initialize the default data */ + Mutant->OwnerThread = CurrentThread; + Mutant->Abandoned = FALSE; + Mutant->ApcDisable = 0; } /* * @implemented */ -LONG STDCALL +VOID +STDCALL +KeInitializeMutex(IN PKMUTEX Mutex, + IN ULONG Level) +{ + DPRINT("KeInitializeMutex: %x\n", Mutex); + + + /* Set up the Dispatcher Header */ + KeInitializeDispatcherHeader(&Mutex->Header, + MutantObject, + sizeof(KMUTEX) / sizeof(ULONG), + 1); + + /* Initialize the default data */ + Mutex->OwnerThread = NULL; + Mutex->Abandoned = FALSE; + Mutex->ApcDisable = 1; + InitializeListHead(&Mutex->Header.WaitListHead); +} + +/* + * @implemented + */ +LONG +STDCALL KeReadStateMutant(IN PKMUTANT Mutant) { - return(Mutant->Header.SignalState); + /* Return the Signal State */ + return(Mutant->Header.SignalState); } /* * @implemented */ -LONG STDCALL -KeReleaseMutant(IN PKMUTANT Mutant, - IN KPRIORITY Increment, - IN BOOLEAN Abandon, - IN BOOLEAN Wait) +LONG +STDCALL +KeReadStateMutex(IN PKMUTEX Mutex) { - KIRQL OldIrql; + /* Return the Signal State */ + return(Mutex->Header.SignalState); +} - OldIrql = KeAcquireDispatcherDatabaseLock(); - if (Abandon == FALSE) - { - if (Mutant->OwnerThread != NULL && Mutant->OwnerThread != KeGetCurrentThread()) - { - DbgPrint("THREAD_NOT_MUTEX_OWNER: Mutant->OwnerThread %p CurrentThread %p\n", - Mutant->OwnerThread, - KeGetCurrentThread()); - KEBUGCHECK(THREAD_NOT_MUTEX_OWNER); - } - Mutant->Header.SignalState++; - ASSERT(Mutant->Header.SignalState <= 1); +/* + * @implemented + */ +LONG +STDCALL +KeReleaseMutant(IN PKMUTANT Mutant, + IN KPRIORITY Increment, + IN BOOLEAN Abandon, + IN BOOLEAN Wait) +{ + KIRQL OldIrql; + LONG PreviousState; + PKTHREAD CurrentThread = KeGetCurrentThread(); + + DPRINT("KeReleaseMutant: %x\n", Mutant); + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save the Previous State */ + PreviousState = Mutant->Header.SignalState; + + /* Check if it is to be abandonned */ + if (Abandon == FALSE) { + + /* Make sure that the Owner Thread is the current Thread */ + if (Mutant->OwnerThread != CurrentThread) { + + KeReleaseDispatcherDatabaseLock(OldIrql); + + DPRINT1("Trying to touch a Mutant that the caller doesn't own!\n"); + ExRaiseStatus(STATUS_MUTANT_NOT_OWNED); + } + + /* If the thread owns it, then increase the signal state */ + Mutant->Header.SignalState++; + + } else { + + /* It's going to be abandonned */ + DPRINT("Abandonning the Mutant\n"); + Mutant->Header.SignalState = 1; + Mutant->Abandoned = TRUE; } - else - { - if (Mutant->OwnerThread != NULL) - { - Mutant->Header.SignalState = 1; - Mutant->Abandoned = TRUE; - } + + /* Check if the signal state is only single */ + if (Mutant->Header.SignalState == 1) { + + /* Check if it's below 0 now */ + if (PreviousState <= 0) { + + /* Remove the mutant from the list */ + DPRINT("Removing Mutant\n"); + RemoveEntryList(&Mutant->MutantListEntry); + + /* Reenable APCs */ + DPRINT("Re-enabling APCs\n"); + CurrentThread->KernelApcDisable += Mutant->ApcDisable; + + /* Force an Interrupt if Apcs are pending */ + if (!IsListEmpty(&CurrentThread->ApcState.ApcListHead[KernelMode])) { + + /* Make sure they aren't disabled though */ + if (!CurrentThread->KernelApcDisable) { + + /* Request the Interrupt */ + DPRINT("Requesting APC Interupt\n"); + HalRequestSoftwareInterrupt(APC_LEVEL); + } + } + } + + /* Remove the Owning Thread and wake it */ + Mutant->OwnerThread = NULL; + + /* Check if the Wait List isn't empty */ + DPRINT("Checking whether to wake the Mutant\n"); + if (!IsListEmpty(&Mutant->Header.WaitListHead)) { + + /* Wake the Mutant */ + DPRINT("Waking the Mutant\n"); + KiWaitTest(&Mutant->Header, Increment); + } } - if (Mutant->Header.SignalState == 1) - { - Mutant->OwnerThread = NULL; - if (Mutant->MutantListEntry.Flink && Mutant->MutantListEntry.Blink) - RemoveEntryList(&Mutant->MutantListEntry); - KiDispatcherObjectWake(&Mutant->Header, Increment); + /* If the Wait is true, then return with a Wait and don't unlock the Dispatcher Database */ + if (Wait == FALSE) { + + /* Release the Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + } else { + + /* Set a wait */ + CurrentThread->WaitNext = TRUE; + CurrentThread->WaitIrql = OldIrql; } - if (Wait == FALSE) - { - KeReleaseDispatcherDatabaseLock(OldIrql); - } - else - { - KTHREAD *Thread = KeGetCurrentThread(); - Thread->WaitNext = TRUE; - Thread->WaitIrql = OldIrql; - } + /* Return the previous state */ + return PreviousState; +} - return(0); +/* + * @implemented + */ +LONG +STDCALL +KeReleaseMutex(IN PKMUTEX Mutex, + IN BOOLEAN Wait) +{ + + /* There's no difference at this level between the two */ + return KeReleaseMutant(Mutex, IO_NO_INCREMENT, FALSE, Wait); +} + +/* + * @implemented + */ +NTSTATUS +STDCALL +KeWaitForMutexObject(IN PKMUTEX Mutex, + IN KWAIT_REASON WaitReason, + IN KPROCESSOR_MODE WaitMode, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout) +{ + /* This is a simple macro. Export the function here though */ + return KeWaitForSingleObject(Mutex, + WaitReason, + WaitMode, + Alertable, + Timeout); } /* EOF */ diff --git a/reactos/ntoskrnl/ke/process.c b/reactos/ntoskrnl/ke/process.c index 15486ecbd3a..49b1aca222d 100644 --- a/reactos/ntoskrnl/ke/process.c +++ b/reactos/ntoskrnl/ke/process.c @@ -1,34 +1,85 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/process.c - * PURPOSE: Microkernel process management + * PURPOSE: Attaching/Detaching and System Call Tables * - * PROGRAMMERS: David Welch (welch@cwcom.net) + * PROGRAMMERS: Alex Ionescu (Implemented Attach/Detach and KeRemoveSystemServiceTable) + * Gregor Anich (Bugfixes to Attach Functions) */ /* INCLUDES *****************************************************************/ #include +#include #define NDEBUG #include +/* GLOBALS *****************************************************************/ + +SSDT_ENTRY +__declspec(dllexport) +KeServiceDescriptorTable[SSDT_MAX_ENTRIES] = { + { MainSSDT, NULL, NUMBER_OF_SYSCALLS, MainSSPT }, + { NULL, NULL, 0, NULL }, + { NULL, NULL, 0, NULL }, + { NULL, NULL, 0, NULL } +}; + +SSDT_ENTRY +KeServiceDescriptorTableShadow[SSDT_MAX_ENTRIES] = { + { MainSSDT, NULL, NUMBER_OF_SYSCALLS, MainSSPT }, + { NULL, NULL, 0, NULL }, + { NULL, NULL, 0, NULL }, + { NULL, NULL, 0, NULL } +}; + /* FUNCTIONS *****************************************************************/ static inline void UpdatePageDirs(PKTHREAD Thread, PKPROCESS Process) { - /* The stack and the thread structure of the current process may be - located in a page which is not present in the page directory of - the process we're attaching to. That would lead to a page fault - when this function returns. However, since the processor can't - call the page fault handler 'cause it can't push EIP on the stack, - this will show up as a stack fault which will crash the entire system. - To prevent this, make sure the page directory of the process we're - attaching to is up-to-date. */ - MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE); - MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD)); + /* + * The stack and the thread structure of the current process may be + * located in a page which is not present in the page directory of + * the process we're attaching to. That would lead to a page fault + * when this function returns. However, since the processor can't + * call the page fault handler 'cause it can't push EIP on the stack, + * this will show up as a stack fault which will crash the entire system. + * To prevent this, make sure the page directory of the process we're + * attaching to is up-to-date. + */ + MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE); + MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD)); +} + +ULONG +STDCALL +KeSetProcess(PKPROCESS Process, + KPRIORITY Increment) +{ + KIRQL OldIrql; + ULONG OldState; + + /* Lock Dispatcher */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Get Old State */ + OldState = Process->DispatcherHeader.SignalState; + + /* Signal the Process */ + Process->DispatcherHeader.SignalState = TRUE; + if ((OldState == 0) && IsListEmpty(&Process->DispatcherHeader.WaitListHead) != TRUE) { + + /* Satisfy waits */ + KiWaitTest((PVOID)Process, Increment); + } + + /* Release Dispatcher Database */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Return the previous State */ + return OldState; } /* @@ -38,29 +89,33 @@ VOID STDCALL KeAttachProcess(PKPROCESS Process) { - KIRQL OldIrql; - PKTHREAD Thread = KeGetCurrentThread(); - - DPRINT("KeAttachProcess: %x\n", Process); + KIRQL OldIrql; + PKTHREAD Thread = KeGetCurrentThread(); - UpdatePageDirs(Thread, Process); + DPRINT("KeAttachProcess: %x\n", Process); - /* Lock Dispatcher */ - OldIrql = KeAcquireDispatcherDatabaseLock(); - - /* Crash system if DPC is being executed! */ - if (KeIsExecutingDpc()) { - DPRINT1("Invalid attach (Thread is executing a DPC!)\n"); - KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT); - } - - /* Check if the Target Process is already attached */ - if (Thread->ApcState.Process == Process || Thread->ApcStateIndex != OriginalApcEnvironment) { - DPRINT("Process already Attached. Exitting\n"); - KeReleaseDispatcherDatabaseLock(OldIrql); - } else { - KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState); - } + /* Make sure that we are in the right page directory */ + UpdatePageDirs(Thread, Process); + + /* Lock Dispatcher */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Crash system if DPC is being executed! */ + if (KeIsExecutingDpc()) { + + DPRINT1("Invalid attach (Thread is executing a DPC!)\n"); + KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT); + } + + /* Check if the Target Process is already attached */ + if (Thread->ApcState.Process == Process || Thread->ApcStateIndex != OriginalApcEnvironment) { + + DPRINT("Process already Attached. Exitting\n"); + KeReleaseDispatcherDatabaseLock(OldIrql); + } else { + + KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState); + } } VOID @@ -68,52 +123,54 @@ STDCALL KiAttachProcess(PKTHREAD Thread, PKPROCESS Process, KIRQL ApcLock, PRKAPC_STATE SavedApcState) { - DPRINT("KiAttachProcess(Thread: %x, Process: %x, SavedApcState: %x\n", Thread, Process, SavedApcState); + DPRINT("KiAttachProcess(Thread: %x, Process: %x, SavedApcState: %x\n", Thread, Process, SavedApcState); - /* Increase Stack Count */ - Process->StackCount++; - - /* Swap the APC Environment */ - KiMoveApcState(&Thread->ApcState, SavedApcState); - - /* Reinitialize Apc State */ - InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]); - InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]); - Thread->ApcState.Process = Process; - Thread->ApcState.KernelApcInProgress = FALSE; - Thread->ApcState.KernelApcPending = FALSE; - Thread->ApcState.UserApcPending = FALSE; + /* Increase Stack Count */ + Process->StackCount++; + + /* Swap the APC Environment */ + KiMoveApcState(&Thread->ApcState, SavedApcState); - /* Update Environment Pointers if needed*/ - if (SavedApcState == &Thread->SavedApcState) { - Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState; - Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState; - Thread->ApcStateIndex = AttachedApcEnvironment; - } - - /* Swap the Processes */ - KiSwapProcess(Process, SavedApcState->Process); - - /* Return to old IRQL*/ - KeReleaseDispatcherDatabaseLock(ApcLock); - - DPRINT("KiAttachProcess Completed Sucesfully\n"); + /* Reinitialize Apc State */ + InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]); + InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]); + Thread->ApcState.Process = Process; + Thread->ApcState.KernelApcInProgress = FALSE; + Thread->ApcState.KernelApcPending = FALSE; + Thread->ApcState.UserApcPending = FALSE; + + /* Update Environment Pointers if needed*/ + if (SavedApcState == &Thread->SavedApcState) { + + Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState; + Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState; + Thread->ApcStateIndex = AttachedApcEnvironment; + } + + /* Swap the Processes */ + KiSwapProcess(Process, SavedApcState->Process); + + /* Return to old IRQL*/ + KeReleaseDispatcherDatabaseLock(ApcLock); + + DPRINT("KiAttachProcess Completed Sucesfully\n"); } VOID STDCALL KiSwapProcess(PKPROCESS NewProcess, PKPROCESS OldProcess) { - //PKPCR Pcr = KeGetCurrentKpcr(); + //PKPCR Pcr = KeGetCurrentKpcr(); - /* Do they have an LDT? */ - if ((NewProcess->LdtDescriptor) || (OldProcess->LdtDescriptor)) { - /* FIXME : SWitch GDT/IDT */ - } - DPRINT("Switching CR3 to: %x\n", NewProcess->DirectoryTableBase.u.LowPart); - Ke386SetPageTableDirectory(NewProcess->DirectoryTableBase.u.LowPart); - - /* FIXME: Set IopmOffset in TSS */ + /* Do they have an LDT? */ + if ((NewProcess->LdtDescriptor) || (OldProcess->LdtDescriptor)) { + + /* FIXME : SWitch GDT/IDT */ + } + DPRINT("Switching CR3 to: %x\n", NewProcess->DirectoryTableBase.u.LowPart); + Ke386SetPageTableDirectory(NewProcess->DirectoryTableBase.u.LowPart); + + /* FIXME: Set IopmOffset in TSS */ } /* @@ -121,11 +178,10 @@ KiSwapProcess(PKPROCESS NewProcess, PKPROCESS OldProcess) */ BOOLEAN STDCALL -KeIsAttachedProcess( - VOID - ) +KeIsAttachedProcess(VOID) { - return KeGetCurrentThread()->ApcStateIndex; + /* Return the APC State */ + return KeGetCurrentThread()->ApcStateIndex; } /* @@ -133,36 +189,41 @@ KeIsAttachedProcess( */ VOID STDCALL -KeStackAttachProcess ( - IN PKPROCESS Process, - OUT PRKAPC_STATE ApcState - ) +KeStackAttachProcess(IN PKPROCESS Process, + OUT PRKAPC_STATE ApcState) { - KIRQL OldIrql; - PKTHREAD Thread = KeGetCurrentThread(); + KIRQL OldIrql; + PKTHREAD Thread = KeGetCurrentThread(); - UpdatePageDirs(Thread, Process); + /* Make sure that we are in the right page directory */ + UpdatePageDirs(Thread, Process); - OldIrql = KeAcquireDispatcherDatabaseLock(); - - /* Crash system if DPC is being executed! */ - if (KeIsExecutingDpc()) { - DPRINT1("Invalid attach (Thread is executing a DPC!)\n"); - KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT); - } - - /* Check if the Target Process is already attached */ - if (Thread->ApcState.Process == Process) { - ApcState->Process = (PKPROCESS)1; /* Meaning already attached to the same Process */ - } else { - /* Check if the Current Thread is already attached and call the Internal Function*/ - if (Thread->ApcStateIndex != OriginalApcEnvironment) { - KiAttachProcess(Thread, Process, OldIrql, ApcState); - } else { - KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState); - ApcState->Process = NULL; - } - } + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Crash system if DPC is being executed! */ + if (KeIsExecutingDpc()) { + + DPRINT1("Invalid attach (Thread is executing a DPC!)\n"); + KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT); + } + + /* Check if the Target Process is already attached */ + if (Thread->ApcState.Process == Process) { + + ApcState->Process = (PKPROCESS)1; /* Meaning already attached to the same Process */ + + } else { + + /* Check if the Current Thread is already attached and call the Internal Function*/ + if (Thread->ApcStateIndex != OriginalApcEnvironment) { + + KiAttachProcess(Thread, Process, OldIrql, ApcState); + } else { + + KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState); + ApcState->Process = NULL; + } + } } /* @@ -171,38 +232,39 @@ KeStackAttachProcess ( VOID STDCALL KeDetachProcess (VOID) { - PKTHREAD Thread; - KIRQL OldIrql; + PKTHREAD Thread; + KIRQL OldIrql; - DPRINT("KeDetachProcess()\n"); + DPRINT("KeDetachProcess()\n"); - /* Get Current Thread and Lock */ - Thread = KeGetCurrentThread(); - OldIrql = KeAcquireDispatcherDatabaseLock(); - - /* Check if it's attached */ - DPRINT("Current ApcStateIndex: %x\n", Thread->ApcStateIndex); - - if (Thread->ApcStateIndex == OriginalApcEnvironment) { - DPRINT1("Invalid detach (thread was not attached)\n"); - KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT); - } + /* Get Current Thread and Lock */ + Thread = KeGetCurrentThread(); + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Check if it's attached */ + DPRINT("Current ApcStateIndex: %x\n", Thread->ApcStateIndex); + + if (Thread->ApcStateIndex == OriginalApcEnvironment) { + + DPRINT1("Invalid detach (thread was not attached)\n"); + KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT); + } - /* Decrease Stack Count */ - Thread->ApcState.Process->StackCount--; - - /* Restore the APC State */ - KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState); - Thread->SavedApcState.Process = NULL; - Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState; - Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState; - Thread->ApcStateIndex = OriginalApcEnvironment; - - /* Swap Processes */ - KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process); + /* Decrease Stack Count */ + Thread->ApcState.Process->StackCount--; + + /* Restore the APC State */ + KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState); + Thread->SavedApcState.Process = NULL; + Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState; + Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState; + Thread->ApcStateIndex = OriginalApcEnvironment; + + /* Swap Processes */ + KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process); - /* Unlock Dispatcher */ - KeReleaseDispatcherDatabaseLock(OldIrql); + /* Unlock Dispatcher */ + KeReleaseDispatcherDatabaseLock(OldIrql); } /* @@ -214,55 +276,56 @@ KeUnstackDetachProcess ( IN PRKAPC_STATE ApcState ) { - KIRQL OldIrql; - PKTHREAD Thread; + KIRQL OldIrql; + PKTHREAD Thread; - /* If the special "We tried to attach to the process already being attached to" flag is there, don't do anything */ - if (ApcState->Process == (PKPROCESS)1) return; - - Thread = KeGetCurrentThread(); - OldIrql = KeAcquireDispatcherDatabaseLock(); - - /* Sorry Buddy, can't help you if you've got APCs or just aren't attached */ - if ((Thread->ApcStateIndex == OriginalApcEnvironment) || (Thread->ApcState.KernelApcInProgress)) { - DPRINT1("Invalid detach (Thread not Attached, or Kernel APC in Progress!)\n"); - KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT); - } - - /* Restore the Old APC State if a Process was present */ - if (ApcState->Process) { - KiMoveApcState(ApcState, &Thread->ApcState); - } else { - /* The ApcState parameter is useless, so use the saved data and reset it */ - KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState); - Thread->SavedApcState.Process = NULL; - Thread->ApcStateIndex = OriginalApcEnvironment; - Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState; - Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState; - } + /* + * If the special "We tried to attach to the process already being + * attached to" flag is there, don't do anything + */ + if (ApcState->Process == (PKPROCESS)1) return; + + Thread = KeGetCurrentThread(); + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Sorry Buddy, can't help you if you've got APCs or just aren't attached */ + if ((Thread->ApcStateIndex == OriginalApcEnvironment) || (Thread->ApcState.KernelApcInProgress)) { + + DPRINT1("Invalid detach (Thread not Attached, or Kernel APC in Progress!)\n"); + KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT); + } + + /* Restore the Old APC State if a Process was present */ + if (ApcState->Process) { + + KiMoveApcState(ApcState, &Thread->ApcState); + + } else { + + /* The ApcState parameter is useless, so use the saved data and reset it */ + KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState); + Thread->SavedApcState.Process = NULL; + Thread->ApcStateIndex = OriginalApcEnvironment; + Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState; + Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState; + } - /* Swap Processes */ - KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process); + /* Swap Processes */ + KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process); - /* Return to old IRQL*/ - KeReleaseDispatcherDatabaseLock(OldIrql); + /* Return to old IRQL*/ + KeReleaseDispatcherDatabaseLock(OldIrql); } -/* This function should be used by win32k.sys to add its own user32/gdi32 services - * TableIndex is 0 based - * ServiceCountTable its not used at the moment - */ /* * @implemented */ BOOLEAN STDCALL -KeAddSystemServiceTable ( - PSSDT SSDT, - PULONG ServiceCounterTable, - ULONG NumberOfServices, - PSSPT SSPT, - ULONG TableIndex - ) +KeAddSystemServiceTable(PSSDT SSDT, + PULONG ServiceCounterTable, + ULONG NumberOfServices, + PSSPT SSPT, + ULONG TableIndex) { /* check if descriptor table entry is free */ if ((TableIndex > SSDT_MAX_ENTRIES - 1) || @@ -280,15 +343,37 @@ KeAddSystemServiceTable ( } /* - * @unimplemented + * @implemented */ BOOLEAN STDCALL -KeRemoveSystemServiceTable( - IN PUCHAR Number -) +KeRemoveSystemServiceTable(IN ULONG TableIndex) { - UNIMPLEMENTED; - return FALSE; + /* Make sure the Index is valid */ + if (TableIndex > SSDT_MAX_ENTRIES - 1) return FALSE; + + /* Is there a Normal Descriptor Table? */ + if (!KeServiceDescriptorTable[TableIndex].SSDT) { + + /* Not with the index, is there a shadow at least? */ + if (!KeServiceDescriptorTableShadow[TableIndex].SSDT) return FALSE; + } + + /* Now clear from the Shadow Table. */ + KeServiceDescriptorTableShadow[TableIndex].SSDT = NULL; + KeServiceDescriptorTableShadow[TableIndex].SSPT = NULL; + KeServiceDescriptorTableShadow[TableIndex].NumberOfServices = 0; + KeServiceDescriptorTableShadow[TableIndex].ServiceCounterTable = NULL; + + /* Check if we should clean from the Master one too */ + if (TableIndex == 1) { + + KeServiceDescriptorTable[TableIndex].SSDT = NULL; + KeServiceDescriptorTable[TableIndex].SSPT = NULL; + KeServiceDescriptorTable[TableIndex].NumberOfServices = 0; + KeServiceDescriptorTable[TableIndex].ServiceCounterTable = NULL; + } + + return TRUE; } /* EOF */ diff --git a/reactos/ntoskrnl/ke/profile.c b/reactos/ntoskrnl/ke/profile.c index 454e15c3afc..cb2279bf878 100644 --- a/reactos/ntoskrnl/ke/profile.c +++ b/reactos/ntoskrnl/ke/profile.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/profile.c @@ -8,335 +7,294 @@ * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) */ +/* INCLUDES *****************************************************************/ #include #define NDEBUG #include -extern LIST_ENTRY ProcessProfileListHashTable[PROFILE_HASH_TABLE_SIZE]; -extern LIST_ENTRY SystemProfileList; -extern KSPIN_LOCK ProfileListLock; -extern BOOLEAN ProfileInitDone; +/* GLOBALS *******************************************************************/ + +KIRQL KiProfileIrql = PROFILE_LEVEL; +LIST_ENTRY KiProfileListHead; +LIST_ENTRY KiProfileSourceListHead; +KSPIN_LOCK KiProfileLock; +ULONG KiProfileTimeInterval = 78125; /* Default resolution 7.8ms (sysinternals) */ /* FUNCTIONS *****************************************************************/ +STDCALL VOID -KiAddProfileEventToProcess(PLIST_ENTRY ListHead, PVOID Eip) - /* - * Add a profile event to the profile objects for a particular process - * or the system - */ +KeInitializeProfile(PKPROFILE Profile, + PKPROCESS Process, + PVOID ImageBase, + ULONG ImageSize, + ULONG BucketSize, + KPROFILE_SOURCE ProfileSource, + KAFFINITY Affinity) { - PKPROFILE current; - PLIST_ENTRY current_entry; - - current_entry = ListHead->Flink; - while (current_entry != ListHead) - { - current = CONTAINING_RECORD(current_entry, KPROFILE, ListEntry); - - if (current->Base > Eip) - { - return; - } - - if (current->Base <= Eip && ((char*)current->Base + current->Size) > (char*)Eip && - current->Started) - { - ULONG Bucket; - - Bucket = ((ULONG)((char*)Eip - (char*)current->Base)) >> current->BucketShift; - - if ((Bucket*4) < current->BufferSize) - { - current->Buffer[Bucket]++; - } - } - - current_entry = current_entry->Flink; - } -} - -VOID -KiAddProfileEvent(KPROFILE_SOURCE Source, ULONG Eip) - /* - * Add a profile event - */ -{ - HANDLE Pid; - PKPROCESS_PROFILE current; - PLIST_ENTRY current_entry; - PLIST_ENTRY ListHead; - - if (!ProfileInitDone) - { - return; - } - - Pid = PsGetCurrentProcessId(); - ListHead = - ProcessProfileListHashTable[(ULONG)Pid % PROFILE_HASH_TABLE_SIZE].Flink; - - KeAcquireSpinLockAtDpcLevel(&ProfileListLock); - - current_entry = ListHead; - while (current_entry != ListHead) - { - current = CONTAINING_RECORD(current_entry, KPROCESS_PROFILE, ListEntry); - - if (current->Pid == Pid) - { - KiAddProfileEventToProcess(¤t->ProfileListHead, (PVOID)Eip); - break; - } - - current_entry = current_entry->Flink; - } - - KiAddProfileEventToProcess(&SystemProfileList, (PVOID)Eip); - - KeReleaseSpinLockFromDpcLevel(&ProfileListLock); -} - -VOID -KiInsertProfileIntoProcess(PLIST_ENTRY ListHead, PKPROFILE Profile) - /* - * Insert a profile object into the list for a process or the system - */ -{ - PKPROFILE current; - PLIST_ENTRY current_entry; - - current_entry = ListHead; - while (current_entry != ListHead) - { - current = CONTAINING_RECORD(current_entry, KPROFILE, ListEntry); - - if (current->Base > Profile->Base) - { - Profile->ListEntry.Flink = current_entry; - Profile->ListEntry.Blink = current_entry->Blink; - current_entry->Blink->Flink = &Profile->ListEntry; - current_entry->Blink = &Profile->ListEntry; - return; - } - - current_entry = current_entry->Flink; - } - InsertTailList(ListHead, &Profile->ListEntry); -} - -VOID -KiInsertProfile(PKPROFILE Profile) - /* - * Insert a profile into the relevant data structures - */ -{ - KIRQL oldIrql; - - KeAcquireSpinLock(&ProfileListLock, &oldIrql); - - if (Profile->Process == NULL) - { - KiInsertProfileIntoProcess(&SystemProfileList, Profile); - } - else - { - HANDLE Pid; - PKPROCESS_PROFILE current; - PLIST_ENTRY current_entry; - PLIST_ENTRY ListHead; - - Pid = Profile->Process->UniqueProcessId; - ListHead = &ProcessProfileListHashTable[(ULONG_PTR)Pid % PROFILE_HASH_TABLE_SIZE]; - - current_entry = ListHead; - while(current_entry != ListHead) - { - current = CONTAINING_RECORD(current_entry, KPROCESS_PROFILE, - ListEntry); - - if (current->Pid == Pid) - { - KiInsertProfileIntoProcess(¤t->ProfileListHead, Profile); - KeReleaseSpinLock(&ProfileListLock, oldIrql); - return; - } - - current_entry = current_entry->Flink; - } - - current = ExAllocatePool(NonPagedPool, sizeof(KPROCESS_PROFILE)); - - current->Pid = Pid; - InitializeListHead(¤t->ProfileListHead); - InsertTailList(ListHead, ¤t->ListEntry); - - KiInsertProfileIntoProcess(¤t->ProfileListHead, Profile); - } - - KeReleaseSpinLock(&ProfileListLock, oldIrql); -} - -VOID KiRemoveProfile(PKPROFILE Profile) -{ - KIRQL oldIrql; - - KeAcquireSpinLock(&ProfileListLock, &oldIrql); - - if (Profile->Process == NULL) - { - RemoveEntryList(&Profile->ListEntry); - } - else - { - HANDLE Pid; - PLIST_ENTRY ListHead; - PKPROCESS_PROFILE current; - PLIST_ENTRY current_entry; - - RemoveEntryList(&Profile->ListEntry); - - Pid = Profile->Process->UniqueProcessId; - ListHead = &ProcessProfileListHashTable[(ULONG_PTR)Pid % PROFILE_HASH_TABLE_SIZE]; - - current_entry = ListHead; - while(current_entry != ListHead) - { - current = CONTAINING_RECORD(current_entry, KPROCESS_PROFILE, - ListEntry); - - if (current->Pid == Pid) - { - if (IsListEmpty(¤t->ProfileListHead)) - { - RemoveEntryList(¤t->ListEntry); - ExFreePool(current); - } - KeReleaseSpinLock(&ProfileListLock, oldIrql); - return; - } - - current_entry = current_entry->Flink; - } - KEBUGCHECK(0); - } - - KeReleaseSpinLock(&ProfileListLock, oldIrql); -} - -VOID STDCALL -KiDeleteProfile(PVOID ObjectBody) -{ - PKPROFILE Profile; - - Profile = (PKPROFILE)ObjectBody; - - KiRemoveProfile(Profile); - if (Profile->Process != NULL) - { - ObDereferenceObject(Profile->Process); - Profile->Process = NULL; - } - - if (Profile->BufferMdl->MappedSystemVa != NULL) - { - MmUnmapLockedPages(Profile->BufferMdl->MappedSystemVa, - Profile->BufferMdl); - } - MmUnlockPages(Profile->BufferMdl); - ExFreePool(Profile->BufferMdl); - Profile->BufferMdl = NULL; -} - -/* - * @unimplemented - */ -STDCALL -VOID -KeProfileInterrupt( - PKTRAP_FRAME TrapFrame -) -{ - UNIMPLEMENTED; -} - -/* - * @unimplemented - */ -STDCALL -VOID -KeProfileInterruptWithSource( - IN PKTRAP_FRAME TrapFrame, - IN KPROFILE_SOURCE Source -) -{ - UNIMPLEMENTED; -} - -/* - * @unimplemented - */ -STDCALL -VOID -KeSetProfileIrql( - IN KIRQL ProfileIrql -) -{ - UNIMPLEMENTED; -} - -NTSTATUS STDCALL -NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter, - OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL) -{ - LARGE_INTEGER PerfCounter; - LARGE_INTEGER PerfFrequency; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForWrite(PerformanceCounter, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - if(PerformanceFrequency != NULL) - { - ProbeForWrite(PerformanceFrequency, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* Initialize the Header */ + Profile->Type = ProfileObject; + Profile->Size = sizeof(KPROFILE); - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - PerfCounter = KeQueryPerformanceCounter(&PerfFrequency); - - _SEH_TRY - { - *PerformanceCounter = PerfCounter; - if(PerformanceFrequency != NULL) - { - *PerformanceFrequency = PerfFrequency; - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - return Status; + /* Copy all the settings we were given */ + Profile->Process = Process; + Profile->RegionStart = ImageBase; + Profile->BucketShift = BucketSize - 2; /* See ntinternals.net -- Alex */ + Profile->RegionEnd = (PVOID)(ULONG_PTR)ImageBase + ImageSize; + Profile->Active = FALSE; + Profile->Source = ProfileSource; + Profile->Affinity = Affinity; +} + +STDCALL +VOID +KeStartProfile(PKPROFILE Profile, + PVOID Buffer) +{ + KIRQL OldIrql; + PKPROFILE_SOURCE_OBJECT SourceBuffer; + PKPROFILE_SOURCE_OBJECT Source = NULL; + PKPROFILE_SOURCE_OBJECT CurrentSource; + BOOLEAN FreeBuffer = TRUE; + PKPROCESS ProfileProcess; + PLIST_ENTRY ListEntry; + + /* Allocate a buffer first, before we raise IRQL */ + SourceBuffer = ExAllocatePoolWithTag(NonPagedPool, + sizeof(KPROFILE_SOURCE_OBJECT), + TAG('P', 'r', 'o', 'f')); + RtlZeroMemory(Source, sizeof(KPROFILE_SOURCE_OBJECT)); + + /* Raise to PROFILE_LEVEL */ + KeRaiseIrql(PROFILE_LEVEL, &OldIrql); + KeAcquireSpinLockAtDpcLevel(&KiProfileLock); + + /* Make sure it's not running */ + if (!Profile->Active) { + + /* Set it as active */ + Profile->Buffer = Buffer; + Profile->Active = TRUE; + + /* Get the process, if any */ + ProfileProcess = Profile->Process; + + /* Insert it into the Process List or Global List */ + if (ProfileProcess) { + + InsertTailList(&ProfileProcess->ProfileListHead, &Profile->ListEntry); + + } else { + + InsertTailList(&KiProfileListHead, &Profile->ListEntry); + } + + /* Check if this type of profile (source) is already running */ + for (ListEntry = KiProfileSourceListHead.Flink; + ListEntry != &KiProfileSourceListHead; + ListEntry = ListEntry->Flink) { + + /* Get the Source Object */ + CurrentSource = CONTAINING_RECORD(ListEntry, + KPROFILE_SOURCE_OBJECT, + ListEntry); + + /* Check if it's the same as the one being requested now */ + if (CurrentSource->Source == Profile->Source) { + + Source = CurrentSource; + break; + } + } + + /* See if the loop found something */ + if (!Source) { + + /* Nothing found, use our allocated buffer */ + Source = SourceBuffer; + + /* Set up the Source Object */ + Source->Source = Profile->Source; + InsertHeadList(&KiProfileSourceListHead, &Source->ListEntry); + + /* Don't free the pool later on */ + FreeBuffer = FALSE; + } + } + + /* Lower the IRQL */ + KeReleaseSpinLockFromDpcLevel(&KiProfileLock); + KeLowerIrql(OldIrql); + + /* FIXME: Tell HAL to Start the Profile Interrupt */ + //HalStartProfileInterrupt(Profile->Source); + + /* Free the pool */ + if (!FreeBuffer) ExFreePool(SourceBuffer); +} + +STDCALL +VOID +KeStopProfile(PKPROFILE Profile) +{ + KIRQL OldIrql; + PLIST_ENTRY ListEntry; + PKPROFILE_SOURCE_OBJECT CurrentSource = NULL; + + /* Raise to PROFILE_LEVEL and acquire spinlock */ + KeRaiseIrql(PROFILE_LEVEL, &OldIrql); + KeAcquireSpinLockAtDpcLevel(&KiProfileLock); + + /* Make sure it's running */ + if (Profile->Active) { + + /* Remove it from the list and disable */ + RemoveEntryList(&Profile->ListEntry); + Profile->Active = FALSE; + + /* Find the Source Object */ + for (ListEntry = KiProfileSourceListHead.Flink; + CurrentSource->Source != Profile->Source; + ListEntry = ListEntry->Flink) { + + /* Get the Source Object */ + CurrentSource = CONTAINING_RECORD(ListEntry, + KPROFILE_SOURCE_OBJECT, + ListEntry); + } + + /* Remove it */ + RemoveEntryList(&CurrentSource->ListEntry); + } + + /* Lower IRQL */ + KeReleaseSpinLockFromDpcLevel(&KiProfileLock); + KeLowerIrql(OldIrql); + + /* Stop Profiling. FIXME: Implement in HAL */ + //HalStopProfileInterrupt(Profile->Source); + + /* Free the Source Object */ + if (CurrentSource) ExFreePool(CurrentSource); +} + +STDCALL +ULONG +KeQueryIntervalProfile(KPROFILE_SOURCE ProfileSource) +{ + /* Check if this is the timer profile */ + if (ProfileSource == ProfileTime) { + + /* Return the good old 100ns sampling interval */ + return KiProfileTimeInterval; + + } else { + + /* Request it from HAL. FIXME: What structure is used? */ + HalQuerySystemInformation(HalProfileSourceInformation, + sizeof(NULL), + NULL, + NULL); + + return 0; + } +} + +STDCALL +VOID +KeSetIntervalProfile(KPROFILE_SOURCE ProfileSource, + ULONG Interval) +{ + /* Check if this is the timer profile */ + if (ProfileSource == ProfileTime) { + + /* Set the good old 100ns sampling interval */ + KiProfileTimeInterval = Interval; + + } else { + + /* Set it with HAL. FIXME: What structure is used? */ + HalSetSystemInformation(HalProfileSourceInformation, + sizeof(NULL), + NULL); + + } +} + +/* + * @implemented + */ +STDCALL +VOID +KeProfileInterrupt(PKTRAP_FRAME TrapFrame) +{ + /* Called from HAL for Timer Profiling */ + KeProfileInterruptWithSource(TrapFrame, ProfileTime); +} + +VOID +STDCALL +KiParseProfileList(IN PKTRAP_FRAME TrapFrame, + IN KPROFILE_SOURCE Source, + IN PLIST_ENTRY ListHead) +{ + PULONG BucketValue; + PKPROFILE Profile; + PLIST_ENTRY NextEntry; + + /* Loop the List */ + for (NextEntry = ListHead->Flink; NextEntry != ListHead; NextEntry = NextEntry->Flink) { + + /* Get the Current Profile in the List */ + Profile = CONTAINING_RECORD(NextEntry, KPROFILE, ListEntry); + + /* Check if the source is good, and if it's within the range */ + if ((Profile->Source != Source) || + (TrapFrame->Eip < (ULONG_PTR)Profile->RegionStart) || + (TrapFrame->Eip > (ULONG_PTR)Profile->RegionEnd)) { + + continue; + } + + /* Get the Pointer to the Bucket Value representing this EIP */ + BucketValue = (PULONG)(((ULONG_PTR)(Profile->Buffer + + (TrapFrame->Eip - (ULONG_PTR)Profile->RegionStart)) + >> Profile->BucketShift) &~ 0x3); + + /* Increment the value */ + ++BucketValue; + } +} + +/* + * @implemented + * + * Remarks: + * Called from HAL, this function looks up the process + * entries, finds the proper source object, verifies the + * ranges with the trapframe data, and inserts the information + * from the trap frame into the buffer, while using buckets and + * shifting like we specified. -- Alex + */ +STDCALL +VOID +KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame, + IN KPROFILE_SOURCE Source) +{ + PKPROCESS Process = KeGetCurrentThread()->ApcState.Process; + + /* We have to parse 2 lists. Per-Process and System-Wide */ + KiParseProfileList(TrapFrame, Source, &Process->ProfileListHead); + KiParseProfileList(TrapFrame, Source, &KiProfileListHead); +} + +/* + * @implemented + */ +STDCALL +VOID +KeSetProfileIrql(IN KIRQL ProfileIrql) +{ + /* Set the IRQL at which Profiling will run */ + KiProfileIrql = ProfileIrql; } diff --git a/reactos/ntoskrnl/ke/queue.c b/reactos/ntoskrnl/ke/queue.c index 9c478709de8..5f2983d71b5 100644 --- a/reactos/ntoskrnl/ke/queue.c +++ b/reactos/ntoskrnl/ke/queue.c @@ -1,11 +1,12 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/queue.c * PURPOSE: Implements kernel queues * - * PROGRAMMERS: Eric Kohl (ekohl@rz-online.de) + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + * Gunnar Dalsnes + * Eric Kohl (ekohl@rz-online.de) */ /* INCLUDES *****************************************************************/ @@ -16,245 +17,483 @@ /* FUNCTIONS *****************************************************************/ - +LONG STDCALL KiInsertQueue(IN PKQUEUE Queue, IN PLIST_ENTRY Entry, BOOLEAN Head); + /* * @implemented */ -VOID STDCALL +VOID +STDCALL KeInitializeQueue(IN PKQUEUE Queue, - IN ULONG Count OPTIONAL) + IN ULONG Count OPTIONAL) { - KeInitializeDispatcherHeader(&Queue->Header, - InternalQueueType, - sizeof(KQUEUE)/sizeof(ULONG), - 0); - InitializeListHead(&Queue->EntryListHead); - InitializeListHead(&Queue->ThreadListHead); - Queue->CurrentCount = 0; - Queue->MaximumCount = (Count == 0) ? (ULONG) KeNumberProcessors : Count; + DPRINT("KeInitializeQueue %x\n", Queue); + + /* Initialize the Header */ + KeInitializeDispatcherHeader(&Queue->Header, + QueueObject, + sizeof(KQUEUE)/sizeof(ULONG), + 0); + + /* Initialize the Lists */ + InitializeListHead(&Queue->EntryListHead); + InitializeListHead(&Queue->ThreadListHead); + + /* Set the Current and Maximum Count */ + Queue->CurrentCount = 0; + Queue->MaximumCount = (Count == 0) ? (ULONG) KeNumberProcessors : Count; } - -/* - * @implemented - * - * Returns number of entries in the queue - */ -LONG STDCALL -KeReadStateQueue(IN PKQUEUE Queue) -{ - return(Queue->Header.SignalState); -} - -/* - * Returns the previous number of entries in the queue - */ -LONG STDCALL -KiInsertQueue( - IN PKQUEUE Queue, - IN PLIST_ENTRY Entry, - BOOLEAN Head - ) -{ - ULONG InitialState; - - DPRINT("KiInsertQueue(Queue %x, Entry %x)\n", Queue, Entry); - - InitialState = Queue->Header.SignalState; - - if (Head) - { - InsertHeadList(&Queue->EntryListHead, Entry); - } - else - { - InsertTailList(&Queue->EntryListHead, Entry); - } - - //inc. num entries in queue - Queue->Header.SignalState++; - - /* Why the KeGetCurrentThread()->Queue != Queue? - * KiInsertQueue might be called from an APC for the current thread. - * -Gunnar - */ - if (Queue->CurrentCount < Queue->MaximumCount && - !IsListEmpty(&Queue->Header.WaitListHead) && - KeGetCurrentThread()->Queue != Queue) - { - KiDispatcherObjectWake(&Queue->Header, IO_NO_INCREMENT); - } - - return InitialState; -} - - - /* * @implemented */ -LONG STDCALL +LONG +STDCALL KeInsertHeadQueue(IN PKQUEUE Queue, - IN PLIST_ENTRY Entry) + IN PLIST_ENTRY Entry) { - LONG Result; - KIRQL OldIrql; + LONG PreviousState; + KIRQL OldIrql; + + DPRINT("KeInsertHeadQueue %x\n", Queue); + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Insert the Queue */ + PreviousState = KiInsertQueue(Queue, Entry, TRUE); + + /* Release the Dispatcher Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); - OldIrql = KeAcquireDispatcherDatabaseLock(); - Result = KiInsertQueue(Queue,Entry,TRUE); - KeReleaseDispatcherDatabaseLock(OldIrql); - - return Result; + /* Return previous State */ + return PreviousState; } - /* * @implemented */ LONG STDCALL KeInsertQueue(IN PKQUEUE Queue, - IN PLIST_ENTRY Entry) + IN PLIST_ENTRY Entry) { - LONG Result; - KIRQL OldIrql; + LONG PreviousState; + KIRQL OldIrql; + + DPRINT("KeInsertQueue %x\n", Queue); + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Insert the Queue */ + PreviousState = KiInsertQueue(Queue, Entry, FALSE); + + /* Release the Dispatcher Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); - OldIrql = KeAcquireDispatcherDatabaseLock(); - Result = KiInsertQueue(Queue,Entry,FALSE); - KeReleaseDispatcherDatabaseLock(OldIrql); - - return Result; + /* Return previous State */ + return PreviousState; } +/* + * @implemented + * + * Returns number of entries in the queue + */ +LONG +STDCALL +KeReadStateQueue(IN PKQUEUE Queue) +{ + /* Returns the Signal State */ + return(Queue->Header.SignalState); +} /* * @implemented */ -PLIST_ENTRY STDCALL +PLIST_ENTRY +STDCALL KeRemoveQueue(IN PKQUEUE Queue, - IN KPROCESSOR_MODE WaitMode, - IN PLARGE_INTEGER Timeout OPTIONAL) + IN KPROCESSOR_MODE WaitMode, + IN PLARGE_INTEGER Timeout OPTIONAL) { - PLIST_ENTRY ListEntry; - NTSTATUS Status; - PKTHREAD Thread = KeGetCurrentThread(); - KIRQL OldIrql; + PLIST_ENTRY ListEntry; + NTSTATUS Status; + PKTHREAD Thread = KeGetCurrentThread(); + KIRQL OldIrql; + PKQUEUE PreviousQueue; + PKWAIT_BLOCK WaitBlock; + PKWAIT_BLOCK TimerWaitBlock; + PKTIMER Timer; - OldIrql = KeAcquireDispatcherDatabaseLock (); + DPRINT("KeRemoveQueue %x\n", Queue); + + /* Check if the Lock is already held */ + if (Thread->WaitNext) { + + DPRINT("Lock is already held\n"); + + } else { + + /* Lock the Dispatcher Database */ + DPRINT("Lock not held, acquiring\n"); + OldIrql = KeAcquireDispatcherDatabaseLock(); + Thread->WaitIrql = OldIrql; + } - if (Thread->Queue != Queue) - { - /* - * INVESTIGATE: What is the Thread->QueueListEntry used for? It's linked it into the - * Queue->ThreadListHead when the thread registers with the queue and unlinked when - * the thread registers with a new queue. The Thread->Queue already tells us what - * queue the thread is registered with. - * -Gunnar - */ + /* This is needed so that we can set the new queue right here, before additional processing */ + PreviousQueue = Thread->Queue; + Thread->Queue = Queue; - //unregister thread from previous queue (if any) - if (Thread->Queue) - { - RemoveEntryList(&Thread->QueueListEntry); - Thread->Queue->CurrentCount--; - - if (Thread->Queue->CurrentCount < Thread->Queue->MaximumCount && - !IsListEmpty(&Thread->Queue->EntryListHead)) - { - KiDispatcherObjectWake(&Thread->Queue->Header, 0); - } + /* Check if this is a different queue */ + if (Queue != PreviousQueue) { + + /* + * INVESTIGATE: What is the Thread->QueueListEntry used for? It's linked it into the + * Queue->ThreadListHead when the thread registers with the queue and unlinked when + * the thread registers with a new queue. The Thread->Queue already tells us what + * queue the thread is registered with. + * -Gunnar + */ + DPRINT("Different Queue\n"); + if (PreviousQueue) { + + /* Remove from this list */ + DPRINT("Removing Old Queue\n"); + RemoveEntryList(&Thread->QueueListEntry); + + /* Wake the queue */ + DPRINT("Activating new thread\n"); + KiWakeQueue(PreviousQueue); } - // register thread with this queue - InsertTailList(&Queue->ThreadListHead, &Thread->QueueListEntry); - Thread->Queue = Queue; - } - else /* if (Thread->Queue == Queue) */ - { - //dec. num running threads - Queue->CurrentCount--; - } + /* Insert in this new Queue */ + DPRINT("Inserting new Queue!\n"); + InsertTailList(&Queue->ThreadListHead, &Thread->QueueListEntry); - - - - while (TRUE) - { - if (Queue->CurrentCount < Queue->MaximumCount && !IsListEmpty(&Queue->EntryListHead)) - { - ListEntry = RemoveHeadList(&Queue->EntryListHead); - //dec. num entries in queue - Queue->Header.SignalState--; - //inc. num running threads - Queue->CurrentCount++; - - KeReleaseDispatcherDatabaseLock(OldIrql); - return ListEntry; - } - else - { - //inform KeWaitXxx that we are holding disp. lock - Thread->WaitNext = TRUE; - Thread->WaitIrql = OldIrql; + } else { + + /* Same queue, decrement waiting threads */ + DPRINT("Same Queue!\n"); + Queue->CurrentCount--; + } + + /* Loop until the queue is processed */ + while (TRUE) { + + /* Get the Entry */ + ListEntry = Queue->EntryListHead.Flink; + + /* Check if the counts are valid and if there is still a queued entry */ + if ((Queue->CurrentCount < Queue->MaximumCount) && + (ListEntry != &Queue->EntryListHead)) { + + /* Remove the Entry and Save it */ + DPRINT("Removing Queue Entry. CurrentCount: %d, Maximum Count: %d\n", + Queue->CurrentCount, Queue->MaximumCount); + ListEntry = RemoveHeadList(&Queue->EntryListHead); + + /* Decrease the number of entries */ + Queue->Header.SignalState--; + + /* Increase numbef of running threads */ + Queue->CurrentCount++; + + /* Check if the entry is valid. If not, bugcheck */ + if (!ListEntry->Flink || !ListEntry->Blink) { + + KEBUGCHECK(INVALID_WORK_QUEUE_ITEM); + } + + /* Remove the Entry */ + RemoveEntryList(ListEntry); + ListEntry->Flink = NULL; + + /* Nothing to wait on */ + break; + + } else { + + /* Do the wait */ + DPRINT("Waiting on Queue Entry. CurrentCount: %d, Maximum Count: %d\n", + Queue->CurrentCount, Queue->MaximumCount); + + /* Use the Thread's Wait Block, it's big enough */ + Thread->WaitBlockList = &Thread->WaitBlock[0]; + + /* Fail if there's an APC Pending */ + if (WaitMode == UserMode && Thread->ApcState.UserApcPending) { + + /* Return the status and increase the pending threads */ + ListEntry = (PLIST_ENTRY)STATUS_USER_APC; + Queue->CurrentCount++; + + /* Nothing to wait on */ + break; + } + + /* Build the Wait Block */ + WaitBlock = &Thread->WaitBlock[0]; + WaitBlock->Object = (PVOID)Queue; + WaitBlock->WaitKey = STATUS_SUCCESS; + WaitBlock->WaitType = WaitAny; + WaitBlock->Thread = Thread; + WaitBlock->NextWaitBlock = NULL; + + Thread->WaitStatus = STATUS_SUCCESS; + + /* We need to wait for the object... check if we have a timeout */ + if (Timeout) { + + /* If it's zero, then don't do any waiting */ + if (!Timeout->QuadPart) { + + /* Instant Timeout, return the status and increase the pending threads */ + DPRINT("Queue Wait has timed out\n"); + ListEntry = (PLIST_ENTRY)STATUS_TIMEOUT; + Queue->CurrentCount++; + + /* Nothing to wait on */ + break; + } + + /* + * Set up the Timer. We'll use the internal function so that we can + * hold on to the dispatcher lock. + */ + Timer = &Thread->Timer; + TimerWaitBlock = &Thread->WaitBlock[1]; - Status = KeWaitForSingleObject(Queue, - WrQueue, - WaitMode, - TRUE, //bAlertable - Timeout); - - if (Status == STATUS_TIMEOUT || Status == STATUS_USER_APC) - { - return (PVOID)Status; - } - - OldIrql = KeAcquireDispatcherDatabaseLock (); - } - } + /* Set up the Timer Wait Block */ + TimerWaitBlock->Object = (PVOID)Timer; + TimerWaitBlock->Thread = Thread; + TimerWaitBlock->WaitKey = STATUS_TIMEOUT; + TimerWaitBlock->WaitType = WaitAny; + TimerWaitBlock->NextWaitBlock = NULL; + + /* Link the timer to this Wait Block */ + InitializeListHead(&Timer->Header.WaitListHead); + InsertTailList(&Timer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry); + + /* Create Timer */ + DPRINT("Creating Timer with timeout %I64d\n", *Timeout); + KiInsertTimer(Timer, *Timeout); + } + + /* Insert the wait block into the Queues's wait list */ + WaitBlock = Thread->WaitBlockList; + InsertTailList(&Queue->Header.WaitListHead, &WaitBlock->WaitListEntry); + + /* Block the Thread */ + DPRINT("Blocking the Thread: %x %x!\n", KeGetCurrentThread(), Thread); + KiBlockThread(&Status, + FALSE, + WaitMode, + WrQueue); + + /* Reset the wait reason */ + Thread->WaitReason = 0; + + /* Check if we were executing an APC */ + if (Status != STATUS_KERNEL_APC) { + + /* Done Waiting */ + DPRINT("Done waking queue. Thread: %x %x!\n", KeGetCurrentThread(), Thread); + return (PLIST_ENTRY)Status; + } + + /* Acquire again the lock */ + DPRINT("Looping again\n"); + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save the new IRQL and decrease number of waiting threads */ + Thread->WaitIrql = OldIrql; + Queue->CurrentCount--; + } + } + + /* Unlock Database and return */ + KeReleaseDispatcherDatabaseLock(Thread->WaitIrql); + DPRINT("Returning. CurrentCount: %d, Maximum Count: %d\n", + Queue->CurrentCount, Queue->MaximumCount); + return ListEntry; } - /* * @implemented */ -PLIST_ENTRY STDCALL +PLIST_ENTRY +STDCALL KeRundownQueue(IN PKQUEUE Queue) { - PLIST_ENTRY EnumEntry; - PKTHREAD Thread; - KIRQL OldIrql; + PLIST_ENTRY EnumEntry; + PLIST_ENTRY FirstEntry; + PKTHREAD Thread; + KIRQL OldIrql; - DPRINT("KeRundownQueue(Queue %x)\n", Queue); + DPRINT("KeRundownQueue(Queue %x)\n", Queue); - /* I'm just guessing how this should work:-/ - * -Gunnar - */ - - OldIrql = KeAcquireDispatcherDatabaseLock (); + /* Get the Dispatcher Lock */ + OldIrql = KeAcquireDispatcherDatabaseLock(); - //no thread must wait on queue at rundown - ASSERT(IsListEmpty(&Queue->Header.WaitListHead)); + /* Get the First Empty Entry */ + FirstEntry = Queue->EntryListHead.Flink; + + /* Make sure the list is not empty */ + if (FirstEntry == &Queue->EntryListHead) { + + /* It is, so don't return anything */ + EnumEntry = NULL; - // unlink threads and clear their Thread->Queue - while (!IsListEmpty(&Queue->ThreadListHead)) - { - EnumEntry = RemoveHeadList(&Queue->ThreadListHead); - Thread = CONTAINING_RECORD(EnumEntry, KTHREAD, QueueListEntry); - Thread->Queue = NULL; - } + } else { + + /* Remove it */ + RemoveEntryList(&Queue->EntryListHead); + } + + /* Unlink threads and clear their Thread->Queue */ + while (!IsListEmpty(&Queue->ThreadListHead)) { + + /* Get the Entry and Remove it */ + EnumEntry = RemoveHeadList(&Queue->ThreadListHead); + + /* Get the Entry's Thread */ + Thread = CONTAINING_RECORD(EnumEntry, KTHREAD, QueueListEntry); + + /* Kill its Queue */ + Thread->Queue = NULL; + } - if (IsListEmpty(&Queue->EntryListHead)) - { - EnumEntry = NULL; - } - else - { - EnumEntry = Queue->EntryListHead.Flink; - } + /* Release the lock and return */ + KeReleaseDispatcherDatabaseLock(OldIrql); + return FirstEntry; +} - KeReleaseDispatcherDatabaseLock (OldIrql); +/* + * Called when a thread which has a queue entry is entering a wait state + */ +VOID +FASTCALL +KiWakeQueue(IN PKQUEUE Queue) +{ + PLIST_ENTRY QueueEntry; + PLIST_ENTRY WaitEntry; + PKWAIT_BLOCK WaitBlock; + + /* Decrement the number of active threads */ + DPRINT("KiWakeQueue: %x. Thread: %x\n", Queue, KeGetCurrentThread()); + Queue->CurrentCount--; + + /* Make sure the counts are OK */ + if (Queue->CurrentCount < Queue->MaximumCount) { + + /* Get the Queue Entry */ + QueueEntry = Queue->EntryListHead.Flink; + + /* Get the Wait Entry */ + WaitEntry = Queue->Header.WaitListHead.Blink; + DPRINT("Queue Count is ok, Queue entries: %x, %x\n", QueueEntry, WaitEntry); + + /* Make sure that the Queue List isn't empty and that this entry is valid */ + if (!IsListEmpty(&Queue->Header.WaitListHead) && + (QueueEntry != &Queue->EntryListHead)) { + + /* Remove this entry */ + DPRINT("Queue in List, removing it\n"); + RemoveEntryList(QueueEntry); + QueueEntry->Flink = NULL; + + /* Decrease the Signal State */ + Queue->Header.SignalState--; + + /* Unwait the Thread */ + DPRINT("Unwaiting Thread\n"); + WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); + KiAbortWaitThread(WaitBlock->Thread, (NTSTATUS)QueueEntry, IO_NO_INCREMENT); + } + } +} - return EnumEntry; +/* + * Returns the previous number of entries in the queue + */ +LONG +STDCALL +KiInsertQueue(IN PKQUEUE Queue, + IN PLIST_ENTRY Entry, + BOOLEAN Head) +{ + ULONG InitialState; + PKTHREAD Thread = KeGetCurrentThread(); + PKWAIT_BLOCK WaitBlock; + PLIST_ENTRY WaitEntry; + + DPRINT("KiInsertQueue(Queue %x, Entry %x)\n", Queue, Entry); + + /* Save the old state */ + InitialState = Queue->Header.SignalState; + + /* Get the Entry */ + WaitEntry = Queue->Header.WaitListHead.Blink; + DPRINT("Initial State, WaitEntry: %d, %x\n", InitialState, WaitEntry); + + /* + * Why the KeGetCurrentThread()->Queue != Queue? + * KiInsertQueue might be called from an APC for the current thread. + * -Gunnar + */ + if ((Queue->CurrentCount < Queue->MaximumCount) && + (WaitEntry != &Queue->Header.WaitListHead) && + ((Thread->Queue != Queue) || (Thread->WaitReason != WrQueue))) { + + /* Remove the wait entry */ + DPRINT("Removing Entry\n"); + RemoveEntryList(WaitEntry); + + /* Get the Wait Block and Thread */ + WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); + DPRINT("Got wait block: %x\n", WaitBlock); + Thread = WaitBlock->Thread; + + /* Reset the wait reason */ + Thread->WaitReason = 0; + + /* Increase the waiting threads */ + Queue->CurrentCount++; + + /* Check if there's a Thread Timer */ + if (Thread->Timer.Header.Inserted) { + + /* Cancel the Thread Timer with the no-lock fastpath */ + DPRINT("Removing the Thread's Timer\n"); + Thread->Timer.Header.Inserted = FALSE; + RemoveEntryList(&Thread->Timer.TimerListEntry); + } + + /* Reschedule the Thread */ + DPRINT("Unblocking the Thread\n"); + KiUnblockThread(Thread, (PNTSTATUS)&Entry, 0); + + } else { + + /* Increase the Entries */ + DPRINT("Adding new Queue Entry: %d %d\n", Head, Queue->Header.SignalState); + Queue->Header.SignalState++; + + if (Head) { + + InsertHeadList(&Queue->EntryListHead, Entry); + + } else { + + InsertTailList(&Queue->EntryListHead, Entry); + } + } + + /* Return the previous state */ + DPRINT("Returning\n"); + return InitialState; } /* EOF */ diff --git a/reactos/ntoskrnl/ke/sem.c b/reactos/ntoskrnl/ke/sem.c index 9408e66948c..2d3fe4db6c2 100644 --- a/reactos/ntoskrnl/ke/sem.c +++ b/reactos/ntoskrnl/ke/sem.c @@ -19,36 +19,39 @@ /* * @implemented */ -VOID STDCALL -KeInitializeSemaphore (PKSEMAPHORE Semaphore, - LONG Count, - LONG Limit) +VOID +STDCALL +KeInitializeSemaphore(PKSEMAPHORE Semaphore, + LONG Count, + LONG Limit) { - KeInitializeDispatcherHeader(&Semaphore->Header, - InternalSemaphoreType, - sizeof(KSEMAPHORE)/sizeof(ULONG), - Count); - Semaphore->Limit=Limit; + + DPRINT("KeInitializeSemaphore Sem: %x\n", Semaphore); + + /* Simply Initialize the Header */ + KeInitializeDispatcherHeader(&Semaphore->Header, + SemaphoreObject, + sizeof(KSEMAPHORE)/sizeof(ULONG), + Count); + + /* Set the Limit */ + Semaphore->Limit = Limit; } /* * @implemented */ -LONG STDCALL -KeReadStateSemaphore (PKSEMAPHORE Semaphore) +LONG +STDCALL +KeReadStateSemaphore(PKSEMAPHORE Semaphore) { - return(Semaphore->Header.SignalState); + /* Just return the Signal State */ + return(Semaphore->Header.SignalState); } /* * @implemented - */ -LONG STDCALL -KeReleaseSemaphore (PKSEMAPHORE Semaphore, - KPRIORITY Increment, - LONG Adjustment, - BOOLEAN Wait) -/* + * * FUNCTION: KeReleaseSemaphore releases a given semaphore object. This * routine supplies a runtime priority boost for waiting threads. If this * call sets the semaphore to the Signaled state, the semaphore count is @@ -68,40 +71,65 @@ KeReleaseSemaphore (PKSEMAPHORE Semaphore, * RETURNS: If the return value is zero, the previous state of the semaphore * object is Not-Signaled. */ +LONG +STDCALL +KeReleaseSemaphore(PKSEMAPHORE Semaphore, + KPRIORITY Increment, + LONG Adjustment, + BOOLEAN Wait) + { - ULONG InitialState; - KIRQL OldIrql; + ULONG InitialState; + KIRQL OldIrql; + PKTHREAD CurrentThread; - DPRINT("KeReleaseSemaphore(Semaphore %x, Increment %d, Adjustment %d, " - "Wait %d)\n", Semaphore, Increment, Adjustment, Wait); + DPRINT("KeReleaseSemaphore(Semaphore %x, Increment %d, Adjustment %d, Wait %d)\n", + Semaphore, + Increment, + Adjustment, + Wait); - OldIrql = KeAcquireDispatcherDatabaseLock(); + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); - InitialState = Semaphore->Header.SignalState; - if (Semaphore->Limit < (LONG) InitialState + Adjustment || - InitialState > InitialState + Adjustment) - { - ExRaiseStatus(STATUS_SEMAPHORE_LIMIT_EXCEEDED); + /* Save the Old State */ + InitialState = Semaphore->Header.SignalState; + + /* Check if the Limit was exceeded */ + if (Semaphore->Limit < (LONG) InitialState + Adjustment || + InitialState > InitialState + Adjustment) { + + /* Raise an error if it was exceeded */ + KeReleaseDispatcherDatabaseLock(OldIrql); + ExRaiseStatus(STATUS_SEMAPHORE_LIMIT_EXCEEDED); } - Semaphore->Header.SignalState += Adjustment; - if (InitialState == 0) - { - KiDispatcherObjectWake(&Semaphore->Header, SEMAPHORE_INCREMENT); + /* Now set the new state */ + Semaphore->Header.SignalState += Adjustment; + + /* Check if we should wake it */ + if (InitialState == 0 && !IsListEmpty(&Semaphore->Header.WaitListHead)) { + + /* Wake the Semaphore */ + KiWaitTest(&Semaphore->Header, Increment); } - if (Wait == FALSE) - { - KeReleaseDispatcherDatabaseLock(OldIrql); - } - else - { - KTHREAD *Thread = KeGetCurrentThread(); - Thread->WaitNext = TRUE; - Thread->WaitIrql = OldIrql; + /* If the Wait is true, then return with a Wait and don't unlock the Dispatcher Database */ + if (Wait == FALSE) { + + /* Release the Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + } else { + + /* Set a wait */ + CurrentThread = KeGetCurrentThread(); + CurrentThread->WaitNext = TRUE; + CurrentThread->WaitIrql = OldIrql; } - return(InitialState); + /* Return the previous state */ + return InitialState; } /* EOF */ diff --git a/reactos/ntoskrnl/ke/spinlock.c b/reactos/ntoskrnl/ke/spinlock.c index 15c3b13737c..258a15d9a07 100644 --- a/reactos/ntoskrnl/ke/spinlock.c +++ b/reactos/ntoskrnl/ke/spinlock.c @@ -178,11 +178,7 @@ KiAcquireSpinLock(PKSPIN_LOCK SpinLock) * FIXME: This depends on gcc assembling this test to a single load from * the spinlock's value. */ - if (*SpinLock >= 2) - { - DbgPrint("Lock %x has bad value %x\n", SpinLock, *SpinLock); - KEBUGCHECK(0); - } + ASSERT(*SpinLock < 2); while ((i = InterlockedExchangeUL(SpinLock, 1)) == 1) { @@ -199,7 +195,7 @@ KiAcquireSpinLock(PKSPIN_LOCK SpinLock) #endif #else DbgPrint("Spinning on spinlock %x current value %x\n", SpinLock, i); - KEBUGCHECK(0); + KEBUGCHECKEX(SPIN_LOCK_ALREADY_OWNED, (ULONG)SpinLock, 0, 0, 0); #endif /* CONFIG_SMP */ } } @@ -227,7 +223,7 @@ KiReleaseSpinLock(PKSPIN_LOCK SpinLock) if (*SpinLock != 1) { DbgPrint("Releasing unacquired spinlock %x\n", SpinLock); - KEBUGCHECK(0); + KEBUGCHECKEX(SPIN_LOCK_NOT_OWNED, (ULONG)SpinLock, 0, 0, 0); } (void)InterlockedExchangeUL(SpinLock, 0); } diff --git a/reactos/ntoskrnl/ke/timer.c b/reactos/ntoskrnl/ke/timer.c index c46db9ddcff..49504f9dbaa 100644 --- a/reactos/ntoskrnl/ke/timer.c +++ b/reactos/ntoskrnl/ke/timer.c @@ -107,12 +107,11 @@ KeInitializeTimerEx (PKTIMER Timer, /* Initialize the Dispatch Header */ KeInitializeDispatcherHeader(&Timer->Header, - InternalNotificationTimer + Type, + TimerNotificationObject + Type, sizeof(KTIMER) / sizeof(ULONG), FALSE); - /* Initalize the List Head and other data */ - InitializeListHead(&Timer->Header.WaitListHead); + /* Initalize the Other data */ Timer->DueTime.QuadPart = 0; Timer->Period = 0; } @@ -196,7 +195,6 @@ KeSetTimerEx (PKTIMER Timer, Timer->Dpc = Dpc; Timer->Period = Period; Timer->Header.SignalState = FALSE; - Timer->Header.Absolute = FALSE; /* Insert it */ if (!KiInsertTimer(Timer, DueTime)) { @@ -219,7 +217,6 @@ KiExpireTimers(PKDPC Dpc, PVOID SystemArgument1, PVOID SystemArgument2) { - ULONG Eip = (ULONG)SystemArgument1; PKTIMER Timer; ULONGLONG InterruptTime; LIST_ENTRY ExpiredTimerList; @@ -267,8 +264,6 @@ KiExpireTimers(PKDPC Dpc, } DPRINT("Timers expired\n"); - /* Add a Profile Event */ - KiAddProfileEvent(ProfileTime, Eip); /* Release Dispatcher Lock */ KeReleaseDispatcherDatabaseLock(OldIrql); @@ -296,7 +291,7 @@ KiHandleExpiredTimer(PKTIMER Timer) /* Set it as Signaled */ DPRINT("Setting Timer as Signaled\n"); Timer->Header.SignalState = TRUE; - KiDispatcherObjectWake(&Timer->Header, 0); + KiWaitTest(&Timer->Header, IO_NO_INCREMENT); /* If the Timer is periodic, reinsert the timer with the new due time */ if (Timer->Period) { @@ -306,7 +301,7 @@ KiHandleExpiredTimer(PKTIMER Timer) if (!KiInsertTimer(Timer, DueTime)) { /* FIXME: I will think about how to handle this and fix it ASAP -- Alex */ - DPRINT1("CRITICAL UNHANDLED CASE: TIMER ALREADY EXPIRED!!!\n"); + DPRINT("CRITICAL UNHANDLED CASE: TIMER ALREADY EXPIRED!!!\n"); }; } @@ -338,8 +333,10 @@ KiInsertTimer(PKTIMER Timer, DPRINT("KiInsertTimer(Timer %x DueTime %I64d)\n", Timer, DueTime.QuadPart); - /* Set it as Inserted */ + /* Set default data */ Timer->Header.Inserted = TRUE; + Timer->Header.Absolute = FALSE; + if (!Timer->Period) Timer->Header.SignalState = FALSE; /* Convert to relative time if needed */ if (DueTime.u.HighPart >= 0) { diff --git a/reactos/ntoskrnl/ps/w32call.c b/reactos/ntoskrnl/ke/usercall.c similarity index 63% rename from reactos/ntoskrnl/ps/w32call.c rename to reactos/ntoskrnl/ke/usercall.c index 455fd633b7c..02573bfad44 100644 --- a/reactos/ntoskrnl/ps/w32call.c +++ b/reactos/ntoskrnl/ke/usercall.c @@ -1,37 +1,28 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ps/w32call.c - * PURPOSE: Thread managment + * FILE: ntoskrnl/ke/usercall.c + * PURPOSE: User-Mode callbacks. Portable part. * - * PROGRAMMERS: David Welch (welch@mcmail.com) - * Phillip Susi + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) */ -/* - * NOTE: - * - * All of the routines that manipulate the thread queue synchronize on - * a single spinlock - * - */ - -/* INCLUDES ****************************************************************/ +/* INCLUDES ******************************************************************/ #include #define NDEBUG #include -#if defined(__GNUC__) -/* void * alloca(size_t size); */ -#elif defined(_MSC_VER) -void* _alloca(size_t size); -#else -#error Unknown compiler for alloca intrinsic stack allocation "function" -#endif +/* FUNCTIONS *****************************************************************/ -/* TYPES *******************************************************************/ +#if ALEX_CB_REWRITE + +NTSTATUS +STDCALL +KiSwitchToUserMode(IN PVOID *OutputBuffer, + IN PULONG OutputLength); + +#else typedef struct _NTW32CALL_SAVED_STATE { @@ -55,8 +46,6 @@ typedef struct KSPIN_LOCK CallbackStackListLock; static LIST_ENTRY CallbackStackListHead; -/* FUNCTIONS ***************************************************************/ - VOID INIT_FUNCTION PsInitialiseW32Call(VOID) { @@ -64,90 +53,6 @@ PsInitialiseW32Call(VOID) KeInitializeSpinLock(&CallbackStackListLock); } -NTSTATUS STDCALL -NtCallbackReturn (PVOID Result, - ULONG ResultLength, - NTSTATUS Status) -{ - PULONG OldStack; - PETHREAD Thread; - PNTSTATUS CallbackStatus; - PULONG CallerResultLength; - PVOID* CallerResult; - PVOID InitialStack; - PVOID StackBase; - ULONG_PTR StackLimit; - KIRQL oldIrql; - PNTW32CALL_SAVED_STATE State; - PKTRAP_FRAME SavedTrapFrame; - PVOID SavedCallbackStack; - PVOID SavedExceptionStack; - - PAGED_CODE(); - - Thread = PsGetCurrentThread(); - if (Thread->Tcb.CallbackStack == NULL) - { - return(STATUS_NO_CALLBACK_ACTIVE); - } - - OldStack = (PULONG)Thread->Tcb.CallbackStack; - - /* - * Get the values that NtW32Call left on the inactive stack for us. - */ - State = (PNTW32CALL_SAVED_STATE)OldStack[0]; - CallbackStatus = State->CallbackStatus; - CallerResultLength = State->CallerResultLength; - CallerResult = State->CallerResult; - InitialStack = State->SavedInitialStack; - StackBase = State->SavedStackBase; - StackLimit = State->SavedStackLimit; - SavedTrapFrame = State->SavedTrapFrame; - SavedCallbackStack = State->SavedCallbackStack; - SavedExceptionStack = State->SavedExceptionStack; - - /* - * Copy the callback status and the callback result to NtW32Call - */ - *CallbackStatus = Status; - if (CallerResult != NULL && CallerResultLength != NULL) - { - if (Result == NULL) - { - *CallerResultLength = 0; - } - else - { - *CallerResultLength = min(ResultLength, *CallerResultLength); - memcpy(*CallerResult, Result, *CallerResultLength); - } - } - - /* - * Restore the old stack. - */ - KeRaiseIrql(HIGH_LEVEL, &oldIrql); - if ((Thread->Tcb.NpxState & NPX_STATE_VALID) && - ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentKPCR()->PrcbData.NpxThread) - { - memcpy((char*)InitialStack - sizeof(FX_SAVE_AREA), - (char*)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA), - sizeof(FX_SAVE_AREA)); - } - Thread->Tcb.InitialStack = InitialStack; - Thread->Tcb.StackBase = StackBase; - Thread->Tcb.StackLimit = StackLimit; - Thread->Tcb.TrapFrame = SavedTrapFrame; - Thread->Tcb.CallbackStack = SavedCallbackStack; - KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)SavedExceptionStack; - KeStackSwitchAndRet((PVOID)(OldStack + 1)); - - /* Should never return. */ - KEBUGCHECK(0); - return(STATUS_UNSUCCESSFUL); -} - VOID STATIC PsFreeCallbackStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, PFN_TYPE Page, SWAPENTRY SwapEntry, @@ -244,14 +149,19 @@ PsAllocateCallbackStack(ULONG StackSize) } return(KernelStack); } +#endif -NTSTATUS STDCALL -NtW32Call (IN ULONG RoutineIndex, - IN PVOID Argument, - IN ULONG ArgumentLength, - OUT PVOID* Result OPTIONAL, - OUT PULONG ResultLength OPTIONAL) -{ +/* + * @implemented + */ +NTSTATUS +STDCALL +KeUserModeCallback(IN ULONG RoutineIndex, + IN PVOID Argument, + IN ULONG ArgumentLength, + OUT PVOID *Result, + OUT PULONG ResultLength) +{ PETHREAD Thread; PVOID NewStack; ULONG_PTR StackSize; @@ -264,7 +174,7 @@ NtW32Call (IN ULONG RoutineIndex, PAGED_CODE(); - DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n", + DPRINT("KeUserModeCallback(RoutineIndex %d, Argument %X, ArgumentLength %d)\n", RoutineIndex, Argument, ArgumentLength); Thread = PsGetCurrentThread(); @@ -289,11 +199,11 @@ NtW32Call (IN ULONG RoutineIndex, AssignedStack = CONTAINING_RECORD(StackEntry, NTW32CALL_CALLBACK_STACK, ListEntry); NewStack = AssignedStack->BaseAddress; - memset(NewStack, 0, StackSize); + RtlZeroMemory(NewStack, StackSize); } /* FIXME: Need to check whether we were interrupted from v86 mode. */ - memcpy((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA), - Thread->Tcb.TrapFrame, sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD))); + RtlCopyMemory((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA), + Thread->Tcb.TrapFrame, sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD))); NewFrame = (PKTRAP_FRAME)((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA)); /* We need the stack pointer to remain 4-byte aligned */ NewFrame->Esp -= (((ArgumentLength + 3) & (~ 0x3)) + (4 * sizeof(ULONG))); @@ -303,7 +213,7 @@ NtW32Call (IN ULONG RoutineIndex, UserEsp[1] = RoutineIndex; UserEsp[2] = (ULONG)&UserEsp[4]; UserEsp[3] = ArgumentLength; - memcpy((PVOID)&UserEsp[4], Argument, ArgumentLength); + RtlCopyMemory((PVOID)&UserEsp[4], Argument, ArgumentLength); /* Switch to the new environment and return to user-mode. */ KeRaiseIrql(HIGH_LEVEL, &oldIrql); @@ -317,11 +227,11 @@ NtW32Call (IN ULONG RoutineIndex, SavedState.SavedCallbackStack = Thread->Tcb.CallbackStack; SavedState.SavedExceptionStack = (PVOID)KeGetCurrentKPCR()->TSS->Esp0; if ((Thread->Tcb.NpxState & NPX_STATE_VALID) && - ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentKPCR()->PrcbData.NpxThread) + ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentPrcb()->NpxThread) { - memcpy((char*)NewStack + StackSize - sizeof(FX_SAVE_AREA), - (char*)SavedState.SavedInitialStack - sizeof(FX_SAVE_AREA), - sizeof(FX_SAVE_AREA)); + RtlCopyMemory((char*)NewStack + StackSize - sizeof(FX_SAVE_AREA), + (char*)SavedState.SavedInitialStack - sizeof(FX_SAVE_AREA), + sizeof(FX_SAVE_AREA)); } Thread->Tcb.InitialStack = Thread->Tcb.StackBase = (char*)NewStack + StackSize; Thread->Tcb.StackLimit = (ULONG)NewStack; @@ -338,6 +248,6 @@ NtW32Call (IN ULONG RoutineIndex, InsertTailList(&CallbackStackListHead, &AssignedStack->ListEntry); KeReleaseSpinLock(&CallbackStackListLock, PASSIVE_LEVEL); return(CallbackStatus); -} - +} + /* EOF */ diff --git a/reactos/ntoskrnl/ke/wait.c b/reactos/ntoskrnl/ke/wait.c index a6575e9bb88..a4b51d47cde 100644 --- a/reactos/ntoskrnl/ke/wait.c +++ b/reactos/ntoskrnl/ke/wait.c @@ -1,16 +1,11 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS project * FILE: ntoskrnl/ke/wait.c * PURPOSE: Manages non-busy waiting * - * PROGRAMMERS: David Welch (welch@mcmail.com) - * Phillip Susi - */ - -/* NOTES ******************************************************************** - * + * PROGRAMMERS: Alex Ionescu - Fixes and optimization. + * Gunnar Dalsnes - Implementation */ /* INCLUDES ******************************************************************/ @@ -24,387 +19,51 @@ static KSPIN_LOCK DispatcherDatabaseLock; -#define KeDispatcherObjectWakeOne(hdr, increment) KeDispatcherObjectWakeOneOrAll(hdr, increment, FALSE) -#define KeDispatcherObjectWakeAll(hdr, increment) KeDispatcherObjectWakeOneOrAll(hdr, increment, TRUE) +/* Tells us if the Timer or Event is a Syncronization or Notification Object */ +#define TIMER_OR_EVENT_TYPE 0x7L -extern POBJECT_TYPE EXPORTED ExMutantObjectType; -extern POBJECT_TYPE EXPORTED ExSemaphoreObjectType; -extern POBJECT_TYPE EXPORTED ExTimerType; +/* One of the Reserved Wait Blocks, this one is for the Thread's Timer */ +#define TIMER_WAIT_BLOCK 0x3L /* FUNCTIONS *****************************************************************/ -VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header, - ULONG Type, - ULONG Size, - ULONG SignalState) -{ - Header->Type = (UCHAR)Type; - Header->Absolute = 0; - Header->Inserted = 0; - Header->Size = (UCHAR)Size; - Header->SignalState = SignalState; - InitializeListHead(&(Header->WaitListHead)); -} - - -KIRQL -KeAcquireDispatcherDatabaseLock(VOID) -/* - * PURPOSE: Acquires the dispatcher database lock for the caller - */ -{ - KIRQL OldIrql; - - DPRINT("KeAcquireDispatcherDatabaseLock()\n"); - - KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql); - return OldIrql; -} - - VOID -KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID) -/* - * PURPOSE: Acquires the dispatcher database lock for the caller - */ +inline +FASTCALL +KiCheckAlertability(BOOLEAN Alertable, + PKTHREAD CurrentThread, + KPROCESSOR_MODE WaitMode, + PNTSTATUS Status) { - DPRINT("KeAcquireDispatcherDatabaseLockAtDpcLevel()\n"); - - KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock); -} - - -VOID -KeReleaseDispatcherDatabaseLock(KIRQL OldIrql) -{ - DPRINT("KeReleaseDispatcherDatabaseLock(OldIrql %x)\n",OldIrql); - if (!KeIsExecutingDpc() && - OldIrql < DISPATCH_LEVEL && - KeGetCurrentThread() != NULL && - KeGetCurrentThread() == KeGetCurrentKPCR()->PrcbData.IdleThread) - { - PsDispatchThreadNoLock(THREAD_STATE_READY); - KeLowerIrql(OldIrql); - } - else - { - KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql); - } -} - - -VOID -KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID) -{ - DPRINT("KeReleaseDispatcherDatabaseLock()\n"); - - KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock); -} - - -static BOOLEAN -KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr, - PKTHREAD Thread) -/* - * FUNCTION: Perform side effects on object before a wait for a thread is - * satisfied - */ -{ - BOOLEAN Abandoned = FALSE; - - switch (hdr->Type) - { - case InternalSynchronizationEvent: - hdr->SignalState = 0; - break; - - case InternalQueueType: - break; - - case InternalSemaphoreType: - hdr->SignalState--; - break; - - case InternalProcessType: - break; - - case InternalThreadType: - break; - - case InternalNotificationEvent: - break; - - case InternalSynchronizationTimer: - hdr->SignalState = FALSE; - break; - - case InternalNotificationTimer: - break; - - case InternalMutexType: - { - PKMUTEX Mutex; - - Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header); - hdr->SignalState--; - ASSERT(hdr->SignalState <= 1); - if (hdr->SignalState == 0) - { - if (Thread == NULL) - { - DPRINT("Thread == NULL!\n"); - KEBUGCHECK(0); - } - Abandoned = Mutex->Abandoned; - if (Thread != NULL) - InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry); - Mutex->OwnerThread = Thread; - Mutex->Abandoned = FALSE; - } - } - break; - - default: - DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr); - KEBUGCHECK(0); - } - - return Abandoned; -} - -static BOOLEAN -KiIsObjectSignalled(DISPATCHER_HEADER * hdr, - PKTHREAD Thread) -{ - if (hdr->Type == InternalMutexType) - { - PKMUTEX Mutex; - - Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header); - - ASSERT(hdr->SignalState <= 1); - - if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1) - { - return (TRUE); - } - else - { - return (FALSE); - } - } - - if (hdr->SignalState <= 0) - { - return (FALSE); - } - else - { - return (TRUE); - } -} - -/* Must be called with the dispatcher lock held */ -BOOLEAN KiAbortWaitThread(PKTHREAD Thread, NTSTATUS WaitStatus) -{ - PKWAIT_BLOCK WaitBlock; - BOOLEAN WasWaiting; - - /* if we are blocked, we must be waiting on something also */ - ASSERT((Thread->State == THREAD_STATE_BLOCKED) == (Thread->WaitBlockList != NULL)); - - WaitBlock = (PKWAIT_BLOCK)Thread->WaitBlockList; - WasWaiting = (WaitBlock != NULL); - - while (WaitBlock) - { - RemoveEntryList(&WaitBlock->WaitListEntry); - WaitBlock = WaitBlock->NextWaitBlock; - } - - Thread->WaitBlockList = NULL; - - if (WasWaiting) - { - PsUnblockThread((PETHREAD)Thread, &WaitStatus, 0); - } - return WasWaiting; -} - -static BOOLEAN -KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr, - KPRIORITY increment, - BOOLEAN WakeAll) -{ - PKWAIT_BLOCK Waiter; - PKWAIT_BLOCK WaiterHead; - PLIST_ENTRY EnumEntry; - NTSTATUS Status; - BOOLEAN Abandoned; - BOOLEAN AllSignaled; - BOOLEAN WakedAny = FALSE; - - DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr); - DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n", - hdr->WaitListHead.Flink, hdr->WaitListHead.Blink); - - if (IsListEmpty(&hdr->WaitListHead)) - { - return (FALSE); - } - - //enum waiters for this dispatcher object - EnumEntry = hdr->WaitListHead.Flink; - while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny)) - { - WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry); - DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead); - EnumEntry = EnumEntry->Flink; - ASSERT(WaiterHead->Thread != NULL); - ASSERT(WaiterHead->Thread->WaitBlockList != NULL); - - Abandoned = FALSE; - - if (WaiterHead->WaitType == WaitAny) - { - DPRINT("WaitAny: Remove all wait blocks.\n"); - for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock) - { - RemoveEntryList(&Waiter->WaitListEntry); - } - - WaiterHead->Thread->WaitBlockList = NULL; - - /* - * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times, - * but thats ok since WakeAll objects has no sideeffects. - */ - Abandoned |= KiSideEffectsBeforeWake(hdr, WaiterHead->Thread); - } - else - { - DPRINT("WaitAll: All WaitAll objects must be signaled.\n"); - - AllSignaled = TRUE; - - //all WaitAll obj. for thread need to be signaled to satisfy a wake - for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock) - { - //no need to check hdr since it has to be signaled - if (Waiter->WaitType == WaitAll && Waiter->Object != hdr) - { - if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread)) - { - AllSignaled = FALSE; - break; - } - } - } - - if (AllSignaled) - { - for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock) - { - RemoveEntryList(&Waiter->WaitListEntry); - - if (Waiter->WaitType == WaitAll) - { - Abandoned |= KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread); - } - - //no WaitAny objects can possibly be signaled since we are here - ASSERT(!(Waiter->WaitType == WaitAny - && KiIsObjectSignalled(Waiter->Object, Waiter->Thread))); - } - - WaiterHead->Thread->WaitBlockList = NULL; - } - } - - if (WaiterHead->Thread->WaitBlockList == NULL) - { - Status = WaiterHead->WaitKey; - if (Abandoned) - { - DPRINT("Abandoned mutex among objects"); - Status += STATUS_ABANDONED_WAIT_0; - } - - WakedAny = TRUE; - DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status); - PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb), - &Status, increment); - } - } - - return WakedAny; -} - - -BOOLEAN KiDispatcherObjectWake(DISPATCHER_HEADER* hdr, KPRIORITY increment) -/* - * FUNCTION: Wake threads waiting on a dispatcher object - * NOTE: The exact semantics of waking are dependant on the type of object - */ -{ - BOOL Ret; - - DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr); -// DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n", -// &hdr->WaitListHead,hdr->WaitListHead.Flink); - DPRINT("hdr->Type %x\n",hdr->Type); - switch (hdr->Type) - { - case InternalNotificationEvent: - return(KeDispatcherObjectWakeAll(hdr, increment)); - - case InternalNotificationTimer: - return(KeDispatcherObjectWakeAll(hdr, increment)); - - case InternalSynchronizationEvent: - return(KeDispatcherObjectWakeOne(hdr, increment)); - - case InternalSynchronizationTimer: - return(KeDispatcherObjectWakeOne(hdr, increment)); - - case InternalQueueType: - return(KeDispatcherObjectWakeOne(hdr, increment)); - - case InternalSemaphoreType: - DPRINT("hdr->SignalState %d\n", hdr->SignalState); - if(hdr->SignalState>0) - { - do - { - DPRINT("Waking one semaphore waiter\n"); - Ret = KeDispatcherObjectWakeOne(hdr, increment); - } while(hdr->SignalState > 0 && Ret) ; - return(Ret); - } - else return FALSE; - - case InternalProcessType: - return(KeDispatcherObjectWakeAll(hdr, increment)); - - case InternalThreadType: - return(KeDispatcherObjectWakeAll(hdr, increment)); - - case InternalMutexType: - return(KeDispatcherObjectWakeOne(hdr, increment)); - } - DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type); - KEBUGCHECK(0); - return(FALSE); + /* At this point, we have to do a wait, so make sure we can make the thread Alertable if requested */ + if (Alertable) { + + /* If the Thread is Alerted, set the Wait Status accordingly */ + if (CurrentThread->Alerted[(int)WaitMode]) { + + CurrentThread->Alerted[(int)WaitMode] = FALSE; + DPRINT("Thread was Alerted\n"); + *Status = STATUS_ALERTED; + + /* If there are User APCs Pending, then we can't really be alertable */ + } else if ((!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])) && + (WaitMode == UserMode)) { + + DPRINT("APCs are Pending\n"); + CurrentThread->ApcState.UserApcPending = TRUE; + *Status = STATUS_USER_APC; + } + + /* If there are User APCs Pending and we are waiting in usermode, then we must notify the caller */ + } else if ((CurrentThread->ApcState.UserApcPending) && (WaitMode == UserMode)) { + DPRINT("APCs are Pending\n"); + *Status = STATUS_USER_APC; + } } /* * @implemented - */ -NTSTATUS STDCALL -KeDelayExecutionThread (KPROCESSOR_MODE WaitMode, - BOOLEAN Alertable, - PLARGE_INTEGER Interval) -/* + * * FUNCTION: Puts the current thread into an alertable or nonalertable * wait state for a given internal * ARGUMENTS: @@ -413,27 +72,103 @@ KeDelayExecutionThread (KPROCESSOR_MODE WaitMode, * Interval = Specifies the interval to wait * RETURNS: Status */ +NTSTATUS +STDCALL +KeDelayExecutionThread(KPROCESSOR_MODE WaitMode, + BOOLEAN Alertable, + PLARGE_INTEGER Interval) { - PKTHREAD Thread = KeGetCurrentThread(); + PKWAIT_BLOCK TimerWaitBlock; + PKTIMER ThreadTimer; + PKTHREAD CurrentThread = KeGetCurrentThread(); + NTSTATUS Status; - KeSetTimer(&Thread->Timer, *Interval, NULL); - return (KeWaitForSingleObject(&Thread->Timer, - (WaitMode == KernelMode) ? Executive : UserRequest, /* TMN: Was unconditionally Executive */ - WaitMode, /* TMN: Was UserMode */ - Alertable, - NULL)); + DPRINT("Entering KeDelayExecutionThread\n"); + + /* Check if the lock is already held */ + if (CurrentThread->WaitNext) { + + /* Lock is held, disable Wait Next */ + DPRINT("Lock is held\n"); + CurrentThread->WaitNext = FALSE; + + } else { + + /* Lock not held, acquire it */ + DPRINT("Lock is not held, acquiring\n"); + CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); + } + + /* Use built-in Wait block */ + TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK]; + + /* Start Wait Loop */ + do { + + /* We are going to wait no matter what (that's the point), so test Alertability */ + KiCheckAlertability(Alertable, CurrentThread, KernelMode, &Status); + + /* Set Timer */ + ThreadTimer = &CurrentThread->Timer; + + /* Setup the Wait Block */ + CurrentThread->WaitBlockList = TimerWaitBlock; + TimerWaitBlock->Object = (PVOID)ThreadTimer; + TimerWaitBlock->Thread = CurrentThread; + TimerWaitBlock->WaitKey = (USHORT)STATUS_TIMEOUT; + TimerWaitBlock->WaitType = WaitAny; + TimerWaitBlock->NextWaitBlock = NULL; + + /* Link the timer to this Wait Block */ + InitializeListHead(&ThreadTimer->Header.WaitListHead); + InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry); + + /* Insert the Timer into the Timer Lists and enable it */ + if (!KiInsertTimer(ThreadTimer, *Interval)) { + + /* FIXME: The timer already expired, we should find a new ready thread */ + Status = STATUS_SUCCESS; + break; + } + + /* Handle Kernel Queues */ + if (CurrentThread->Queue) { + + DPRINT("Waking Queue\n"); + KiWakeQueue(CurrentThread->Queue); + } + + /* Block the Thread */ + DPRINT("Blocking the Thread: %d, %d, %x\n", Alertable, WaitMode, KeGetCurrentThread()); + KiBlockThread(&Status, + Alertable, + WaitMode, + DelayExecution); + + /* Check if we were executing an APC or if we timed out */ + if (Status != STATUS_KERNEL_APC) { + + /* This is a good thing */ + if (Status == STATUS_TIMEOUT) Status = STATUS_SUCCESS; + + /* Return Status */ + return Status; + } + + DPRINT("Looping Again\n"); + CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); + + } while (TRUE); + + /* Release the Lock, we are done */ + DPRINT("Returning from KeDelayExecutionThread(), %x. Status: %d\n", KeGetCurrentThread(), Status); + KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); + return Status; } /* * @implemented - */ -NTSTATUS STDCALL -KeWaitForSingleObject(PVOID Object, - KWAIT_REASON WaitReason, - KPROCESSOR_MODE WaitMode, - BOOLEAN Alertable, - PLARGE_INTEGER Timeout) -/* + * * FUNCTION: Puts the current thread into a wait state until the * given dispatcher object is set to signalled * ARGUMENTS: @@ -446,52 +181,174 @@ KeWaitForSingleObject(PVOID Object, * Timeout = Optional timeout value * RETURNS: Status */ +NTSTATUS +STDCALL +KeWaitForSingleObject(PVOID Object, + KWAIT_REASON WaitReason, + KPROCESSOR_MODE WaitMode, + BOOLEAN Alertable, + PLARGE_INTEGER Timeout) { - return KeWaitForMultipleObjects(1, - &Object, - WaitAny, - WaitReason, - WaitMode, - Alertable, - Timeout, - NULL); -} + PDISPATCHER_HEADER CurrentObject; + PKWAIT_BLOCK WaitBlock; + PKWAIT_BLOCK TimerWaitBlock; + PKTIMER ThreadTimer; + PKTHREAD CurrentThread = KeGetCurrentThread(); + NTSTATUS Status; + NTSTATUS WaitStatus; - -inline -PVOID -KiGetWaitableObjectFromObject(PVOID Object) -{ - //special case when waiting on file objects - if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType) - { - return &((PFILE_OBJECT)Object)->Event; - } - - return Object; -} - - -inline BOOL -KiIsObjectWaitable(PVOID Object) -{ - POBJECT_HEADER Header; - Header = BODY_TO_HEADER(Object); - if (Header->ObjectType == ExEventObjectType || - Header->ObjectType == ExIoCompletionType || - Header->ObjectType == ExMutantObjectType || - Header->ObjectType == ExSemaphoreObjectType || - Header->ObjectType == ExTimerType || - Header->ObjectType == PsProcessType || - Header->ObjectType == PsThreadType || - Header->ObjectType == IoFileObjectType) - { - return TRUE; - } - else - { - return FALSE; + DPRINT("Entering KeWaitForSingleObject\n"); + + /* Check if the lock is already held */ + if (CurrentThread->WaitNext) { + + /* Lock is held, disable Wait Next */ + DPRINT("Lock is held\n"); + CurrentThread->WaitNext = FALSE; + + } else { + + /* Lock not held, acquire it */ + DPRINT("Lock is not held, acquiring\n"); + CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); } + + /* Start the actual Loop */ + do { + + /* Get the current Wait Status */ + WaitStatus = CurrentThread->WaitStatus; + + /* Append wait block to the KTHREAD wait block list */ + CurrentThread->WaitBlockList = WaitBlock = &CurrentThread->WaitBlock[0]; + + /* Get the Current Object */ + CurrentObject = (PDISPATCHER_HEADER)Object; + + /* FIXME: + * Temporary hack until my Object Manager re-write. Basically some objects, like + * the File Object, but also LPCs and others, are actually waitable on their event. + * The Object Manager sets this up in The ObjectTypeInformation->DefaultObject member, + * by using pretty much the same kind of hack as us. Normal objects point to themselves + * in that pointer. Then, NtWaitForXXX will populate the WaitList that gets sent to us by + * using ->DefaultObject, so the proper actual objects will be sent to us. Until then however, + * I will keep this hack here, since there's no need to make an interim hack until the rewrite + * -- Alex Ionescu 24/02/05 + */ + if (CurrentObject->Type == IO_TYPE_FILE) { + + DPRINT1("Hack used: %x\n", &((PFILE_OBJECT)CurrentObject)->Event); + CurrentObject = (PDISPATCHER_HEADER)(&((PFILE_OBJECT)CurrentObject)->Event); + } + + /* Check if the Object is Signaled */ + if (KiIsObjectSignaled(CurrentObject, CurrentThread)) { + + /* Just unwait this guy and exit */ + if (CurrentObject->SignalState != MINLONG) { + + /* It has a normal signal state, so unwait it and return */ + KiSatisfyObjectWait(CurrentObject, CurrentThread); + Status = STATUS_WAIT_0; + goto WaitDone; + + } else { + + /* Is this a Mutant? */ + if (CurrentObject->Type == MutantObject) { + + /* According to wasm.ru, we must raise this exception (tested and true) */ + KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); + ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); + } + } + } + + /* Set up the Wait Block */ + WaitBlock->Object = CurrentObject; + WaitBlock->Thread = CurrentThread; + WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0); + WaitBlock->WaitType = WaitAny; + WaitBlock->NextWaitBlock = NULL; + + /* Make sure we can satisfy the Alertable request */ + KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status); + + /* Set the Wait Status */ + CurrentThread->WaitStatus = Status; + + /* Enable the Timeout Timer if there was any specified */ + if (Timeout != NULL) { + + /* However if 0 timeout was specified, then we must fail since we need to peform a wait */ + if (!Timeout->QuadPart) { + + /* Return a timeout */ + Status = STATUS_TIMEOUT; + goto WaitDone; + } + + /* Point to Timer Wait Block and Thread Timer */ + TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK]; + ThreadTimer = &CurrentThread->Timer; + + /* Connect the Timer Wait Block */ + WaitBlock->NextWaitBlock = TimerWaitBlock; + + /* Set up the Timer Wait Block */ + TimerWaitBlock->Object = (PVOID)ThreadTimer; + TimerWaitBlock->Thread = CurrentThread; + TimerWaitBlock->WaitKey = STATUS_TIMEOUT; + TimerWaitBlock->WaitType = WaitAny; + TimerWaitBlock->NextWaitBlock = NULL; + + /* Link the timer to this Wait Block */ + InitializeListHead(&ThreadTimer->Header.WaitListHead); + InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry); + + /* Insert the Timer into the Timer Lists and enable it */ + if (!KiInsertTimer(ThreadTimer, *Timeout)) { + + /* Return a timeout if we couldn't insert the timer for some reason */ + Status = STATUS_TIMEOUT; + goto WaitDone; + } + } + + /* Link the Object to this Wait Block */ + InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry); + + /* Handle Kernel Queues */ + if (CurrentThread->Queue) { + + DPRINT("Waking Queue\n"); + KiWakeQueue(CurrentThread->Queue); + } + + /* Block the Thread */ + DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode, WaitReason, KeGetCurrentThread()); + KiBlockThread(&Status, + Alertable, + WaitMode, + (UCHAR)WaitReason); + + /* Check if we were executing an APC */ + if (Status != STATUS_KERNEL_APC) { + + /* Return Status */ + return Status; + } + + DPRINT("Looping Again\n"); + CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); + + } while (TRUE); + +WaitDone: + /* Release the Lock, we are done */ + DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status); + KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); + return Status; } /* @@ -507,558 +364,543 @@ KeWaitForMultipleObjects(ULONG Count, PLARGE_INTEGER Timeout, PKWAIT_BLOCK WaitBlockArray) { - DISPATCHER_HEADER *hdr; - PKWAIT_BLOCK blk; - PKTHREAD CurrentThread; - ULONG CountSignaled; - ULONG i; - NTSTATUS Status; - KIRQL OldIrql; - BOOLEAN Abandoned; - NTSTATUS WaitStatus; + PDISPATCHER_HEADER CurrentObject; + PKWAIT_BLOCK WaitBlock; + PKWAIT_BLOCK TimerWaitBlock; + PKTIMER ThreadTimer; + PKTHREAD CurrentThread = KeGetCurrentThread(); + ULONG AllObjectsSignaled; + ULONG WaitIndex; + NTSTATUS Status; + NTSTATUS WaitStatus; - DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) " - "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread()); + DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) " + "PsGetCurrentThread() %x, Timeout %x\n", Count, Object, PsGetCurrentThread(), Timeout); - ASSERT(0 < Count && Count <= MAXIMUM_WAIT_OBJECTS); - - CurrentThread = KeGetCurrentThread(); - - /* - * Work out where we are going to put the wait blocks - */ - if (WaitBlockArray == NULL) - { - if (Count > THREAD_WAIT_OBJECTS) - { - DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__); - return (STATUS_UNSUCCESSFUL); - } - WaitBlockArray = &CurrentThread->WaitBlock[0]; - } - else - { - if (Count > MAXIMUM_WAIT_OBJECTS) - { - DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__); - return (STATUS_UNSUCCESSFUL); - } - } - - - - /* - * Set up the timeout if required - */ - if (Timeout != NULL && Timeout->QuadPart != 0) - { - KeSetTimer(&CurrentThread->Timer, *Timeout, NULL); - } - - do - { - if (CurrentThread->WaitNext) - { - CurrentThread->WaitNext = FALSE; - OldIrql = CurrentThread->WaitIrql; - } - else - { - OldIrql = KeAcquireDispatcherDatabaseLock (); - } - - /* Get the current Wait Status */ - WaitStatus = CurrentThread->WaitStatus; - - if (Alertable) { + /* Set the Current Thread */ + CurrentThread = KeGetCurrentThread(); - /* If the Thread is Alerted, set the Wait Status accordingly */ - if (CurrentThread->Alerted[(int)WaitMode]) { - - CurrentThread->Alerted[(int)WaitMode] = FALSE; - DPRINT("Thread was Alerted\n"); - WaitStatus = STATUS_ALERTED; - - /* If there are User APCs Pending, then we can't really be alertable */ - } else if ((!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])) && - (WaitMode == UserMode)) { - - DPRINT1("APCs are Pending\n"); - CurrentThread->ApcState.UserApcPending = TRUE; - WaitStatus = STATUS_USER_APC; - } + /* Check if the lock is already held */ + if (CurrentThread->WaitNext) { - /* If there are User APCs Pending and we are waiting in usermode, then we must notify the caller */ - } else if ((CurrentThread->ApcState.UserApcPending) && (WaitMode == UserMode)) { - DPRINT1("APCs are Pending\n"); - WaitStatus = STATUS_USER_APC; + /* Lock is held, disable Wait Next */ + DPRINT("Lock is held\n"); + CurrentThread->WaitNext = FALSE; + + } else { + + /* Lock not held, acquire it */ + DPRINT("Lock is not held, acquiring\n"); + CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); } - /* - * Check if the wait is (already) satisfied - */ - CountSignaled = 0; - Abandoned = FALSE; - for (i = 0; i < Count; i++) - { - hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]); - - if (KiIsObjectSignalled(hdr, CurrentThread)) - { - CountSignaled++; - - if (WaitType == WaitAny) - { - Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned; - - KeReleaseDispatcherDatabaseLock(OldIrql); - - if (Timeout != NULL && Timeout->QuadPart != 0) - { - KeCancelTimer(&CurrentThread->Timer); - } - - DPRINT("One object is (already) signaled!\n"); - if (Abandoned == TRUE) - { - return (STATUS_ABANDONED_WAIT_0 + i); - } - - return (STATUS_WAIT_0 + i); - } - } - } - - Abandoned = FALSE; - if ((WaitType == WaitAll) && (CountSignaled == Count)) - { - for (i = 0; i < Count; i++) - { - hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]); - Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned; - } - - KeReleaseDispatcherDatabaseLock(OldIrql); - - if (Timeout != NULL && Timeout->QuadPart != 0) - { - KeCancelTimer(&CurrentThread->Timer); - } - - DPRINT("All objects are (already) signaled!\n"); - - if (Abandoned == TRUE) - { - return (STATUS_ABANDONED_WAIT_0); - } - - return (STATUS_WAIT_0); - } - - //zero timeout is used for testing if the object(s) can be immediately acquired - if (Timeout != NULL && Timeout->QuadPart == 0) - { - KeReleaseDispatcherDatabaseLock(OldIrql); - return STATUS_TIMEOUT; - } - - /* - * Check if we have already timed out - */ - if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread)) - { - KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread); - KeReleaseDispatcherDatabaseLock(OldIrql); - KeCancelTimer(&CurrentThread->Timer); - return (STATUS_TIMEOUT); - } - - /* Append wait block to the KTHREAD wait block list */ - CurrentThread->WaitBlockList = blk = WaitBlockArray; - - /* - * Set up the wait - */ - CurrentThread->WaitStatus = WaitStatus;; - - for (i = 0; i < Count; i++) - { - hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]); - - blk->Object = KiGetWaitableObjectFromObject(Object[i]); - blk->Thread = CurrentThread; - blk->WaitKey = (USHORT)(STATUS_WAIT_0 + i); - blk->WaitType = (USHORT)WaitType; - - if (i == (Count - 1)) - { - if (Timeout != NULL) - { - blk->NextWaitBlock = &CurrentThread->WaitBlock[3]; - } - else - { - blk->NextWaitBlock = NULL; - } - } - else - { - blk->NextWaitBlock = blk + 1; - } - - /* - * add wait block to disp. obj. wait list - * Use FIFO for all waits except for queues which use LIFO - */ - if (WaitReason == WrQueue) - { - InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry); - } - else - { - InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry); - } - - blk = blk->NextWaitBlock; - } - - if (Timeout != NULL) - { - CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer; - CurrentThread->WaitBlock[3].Thread = CurrentThread; - CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT; - CurrentThread->WaitBlock[3].WaitType = WaitAny; - CurrentThread->WaitBlock[3].NextWaitBlock = NULL; - - InsertTailList(&CurrentThread->Timer.Header.WaitListHead, - &CurrentThread->WaitBlock[3].WaitListEntry); - } - - //kernel queues - if (CurrentThread->Queue && WaitReason != WrQueue) - { - DPRINT("queue: sleep on something else\n"); - CurrentThread->Queue->CurrentCount--; - - //wake another thread - if (CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount && - !IsListEmpty(&CurrentThread->Queue->EntryListHead)) - { - KiDispatcherObjectWake(&CurrentThread->Queue->Header, IO_NO_INCREMENT); - } - } - - PsBlockThread(&Status, Alertable, WaitMode, TRUE, OldIrql, (UCHAR)WaitReason); - - //kernel queues - OldIrql = KeAcquireDispatcherDatabaseLock (); - if (CurrentThread->Queue && WaitReason != WrQueue) - { - DPRINT("queue: wake from something else\n"); - CurrentThread->Queue->CurrentCount++; - } - if (Status == STATUS_KERNEL_APC) - { - CurrentThread->WaitNext = TRUE; - CurrentThread->WaitIrql = OldIrql; - } - else - { - KeReleaseDispatcherDatabaseLock(OldIrql); - } - - } while (Status == STATUS_KERNEL_APC); - - - if (Timeout != NULL) - { - KeCancelTimer(&CurrentThread->Timer); - } - - DPRINT("Returning from KeWaitForMultipleObjects()\n"); - return (Status); -} - -VOID KeInitializeDispatcher(VOID) -{ - KeInitializeSpinLock(&DispatcherDatabaseLock); -} - -NTSTATUS STDCALL -NtWaitForMultipleObjects(IN ULONG ObjectCount, - IN PHANDLE ObjectsArray, - IN WAIT_TYPE WaitType, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER TimeOut OPTIONAL) -{ - KWAIT_BLOCK WaitBlockArray[MAXIMUM_WAIT_OBJECTS]; - HANDLE SafeObjectsArray[MAXIMUM_WAIT_OBJECTS]; - PVOID ObjectPtrArray[MAXIMUM_WAIT_OBJECTS]; - ULONG i, j; - KPROCESSOR_MODE PreviousMode; - LARGE_INTEGER SafeTimeOut; - NTSTATUS Status = STATUS_SUCCESS; - - DPRINT("NtWaitForMultipleObjects(ObjectCount %lu ObjectsArray[] %x, Alertable %d, " - "TimeOut %x)\n", ObjectCount,ObjectsArray,Alertable,TimeOut); - - PreviousMode = ExGetPreviousMode(); - - if (ObjectCount > MAXIMUM_WAIT_OBJECTS) - return STATUS_UNSUCCESSFUL; - if (0 == ObjectCount) - return STATUS_INVALID_PARAMETER; - - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForRead(ObjectsArray, - ObjectCount * sizeof(ObjectsArray[0]), - sizeof(ULONG)); - /* make a copy so we don't have to guard with SEH later and keep track of - what objects we referenced in case dereferencing pointers suddenly fails */ - RtlCopyMemory(SafeObjectsArray, ObjectsArray, ObjectCount * sizeof(ObjectsArray[0])); - ObjectsArray = SafeObjectsArray; + /* Make sure the Wait Count is valid for the Thread and Maximum Wait Objects */ + if (!WaitBlockArray) { - if(TimeOut != NULL) - { - ProbeForRead(TimeOut, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - /* make a local copy of the timeout on the stack */ - SafeTimeOut = *TimeOut; - TimeOut = &SafeTimeOut; - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } + /* Check in regards to the Thread Object Limit */ + if (Count > THREAD_WAIT_OBJECTS) { + + DPRINT1("(%s:%d) Too many objects!\n", __FILE__, __LINE__); + KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED); + } + + /* Use the Thread's Wait Block */ + WaitBlockArray = &CurrentThread->WaitBlock[0]; + + } else { + + /* Using our own Block Array. Check in regards to System Object Limit */ + if (Count > MAXIMUM_WAIT_OBJECTS) { + + DPRINT1("(%s:%d) Too many objects!\n", __FILE__, __LINE__); + KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED); + } + } + + /* Start the actual Loop */ + do { + + /* Get the current Wait Status */ + WaitStatus = CurrentThread->WaitStatus; + + /* Append wait block to the KTHREAD wait block list */ + CurrentThread->WaitBlockList = WaitBlock = WaitBlockArray; + + /* Check if the wait is (already) satisfied */ + AllObjectsSignaled = TRUE; + + /* First, we'll try to satisfy the wait directly */ + for (WaitIndex = 0; WaitIndex < Count; WaitIndex++) { + + /* Get the Current Object */ + CurrentObject = (PDISPATCHER_HEADER)Object[WaitIndex]; + + /* FIXME: + * Temporary hack until my Object Manager re-write. Basically some objects, like + * the File Object, but also LPCs and others, are actually waitable on their event. + * The Object Manager sets this up in The ObjectTypeInformation->DefaultObject member, + * by using pretty much the same kind of hack as us. Normal objects point to themselves + * in that pointer. Then, NtWaitForXXX will populate the WaitList that gets sent to us by + * using ->DefaultObject, so the proper actual objects will be sent to us. Until then however, + * I will keep this hack here, since there's no need to make an interim hack until the rewrite + * -- Alex Ionescu 24/02/05 + */ + if (CurrentObject->Type == IO_TYPE_FILE) { + + DPRINT1("Hack used: %x\n", &((PFILE_OBJECT)CurrentObject)->Event); + CurrentObject = (PDISPATCHER_HEADER)(&((PFILE_OBJECT)CurrentObject)->Event); + } - /* reference all objects */ - for (i = 0; i < ObjectCount; i++) - { - Status = ObReferenceObjectByHandle(ObjectsArray[i], - SYNCHRONIZE, - NULL, - PreviousMode, - &ObjectPtrArray[i], - NULL); - if (!NT_SUCCESS(Status) || !KiIsObjectWaitable(ObjectPtrArray[i])) - { - if (NT_SUCCESS(Status)) - { - DPRINT1("Waiting for object type '%wZ' is not supported\n", - &BODY_TO_HEADER(ObjectPtrArray[i])->ObjectType->TypeName); - Status = STATUS_HANDLE_NOT_WAITABLE; - i++; - } - /* dereference all referenced objects */ - for (j = 0; j < i; j++) - { - ObDereferenceObject(ObjectPtrArray[j]); - } + /* Check if the Object is Signaled */ + if (KiIsObjectSignaled(CurrentObject, CurrentThread)) { + + /* Check what kind of wait this is */ + if (WaitType == WaitAny) { + + /* This is a Wait Any, so just unwait this guy and exit */ + if (CurrentObject->SignalState != MINLONG) { + + /* It has a normal signal state, so unwait it and return */ + KiSatisfyObjectWait(CurrentObject, CurrentThread); + Status = STATUS_WAIT_0 | WaitIndex; + goto WaitDone; + + } else { + + /* Is this a Mutant? */ + if (CurrentObject->Type == MutantObject) { + + /* According to wasm.ru, we must raise this exception (tested and true) */ + KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); + ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); + } + } + } + + } else { + + /* One of the objects isn't signaled... if this is a WaitAll, we will fail later */ + AllObjectsSignaled = FALSE; + } - return(Status); - } - } + /* Set up a Wait Block for this Object */ + WaitBlock->Object = CurrentObject; + WaitBlock->Thread = CurrentThread; + WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0 + WaitIndex); + WaitBlock->WaitType = (USHORT)WaitType; + WaitBlock->NextWaitBlock = WaitBlock + 1; + + /* Move to the next Wait Block */ + WaitBlock = WaitBlock->NextWaitBlock; + } + + /* Return to the Root Wait Block */ + WaitBlock--; + WaitBlock->NextWaitBlock = NULL; + + /* Check if this is a Wait All and all the objects are signaled */ + if ((WaitType == WaitAll) && (AllObjectsSignaled)) { + + /* Return to the Root Wait Block */ + WaitBlock = CurrentThread->WaitBlockList; + + /* Satisfy their Waits and return to the caller */ + KiSatisifyMultipleObjectWaits(WaitBlock); + Status = STATUS_WAIT_0; + goto WaitDone; + } + + /* Make sure we can satisfy the Alertable request */ + KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status); + + /* Set the Wait Status */ + CurrentThread->WaitStatus = Status; + + /* Enable the Timeout Timer if there was any specified */ + if (Timeout != NULL) { + + /* However if 0 timeout was specified, then we must fail since we need to peform a wait */ + if (!Timeout->QuadPart) { + + /* Return a timeout */ + Status = STATUS_TIMEOUT; + goto WaitDone; + } + + /* Point to Timer Wait Block and Thread Timer */ + TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK]; + ThreadTimer = &CurrentThread->Timer; - Status = KeWaitForMultipleObjects(ObjectCount, - ObjectPtrArray, - WaitType, - UserRequest, - PreviousMode, - Alertable, - TimeOut, - WaitBlockArray); + /* Connect the Timer Wait Block */ + WaitBlock->NextWaitBlock = TimerWaitBlock; + + /* Set up the Timer Wait Block */ + TimerWaitBlock->Object = (PVOID)ThreadTimer; + TimerWaitBlock->Thread = CurrentThread; + TimerWaitBlock->WaitKey = STATUS_TIMEOUT; + TimerWaitBlock->WaitType = WaitAny; + TimerWaitBlock->NextWaitBlock = NULL; + + /* Link the timer to this Wait Block */ + InitializeListHead(&ThreadTimer->Header.WaitListHead); - /* dereference all objects */ - for (i = 0; i < ObjectCount; i++) - { - ObDereferenceObject(ObjectPtrArray[i]); - } + /* Insert the Timer into the Timer Lists and enable it */ + if (!KiInsertTimer(ThreadTimer, *Timeout)) { - return(Status); + /* Return a timeout if we couldn't insert the timer for some reason */ + Status = STATUS_TIMEOUT; + goto WaitDone; + } + } + + /* Insert into Object's Wait List*/ + WaitBlock = CurrentThread->WaitBlockList; + while (WaitBlock) { + + /* Get the Current Object */ + CurrentObject = WaitBlock->Object; + + /* Link the Object to this Wait Block */ + InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry); + + /* Move to the next Wait Block */ + WaitBlock = WaitBlock->NextWaitBlock; + } + + /* Handle Kernel Queues */ + if (CurrentThread->Queue) { + + DPRINT("Waking Queue\n"); + KiWakeQueue(CurrentThread->Queue); + } + + /* Block the Thread */ + DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode, WaitReason, KeGetCurrentThread()); + KiBlockThread(&Status, + Alertable, + WaitMode, + (UCHAR)WaitReason); + + /* Check if we were executing an APC */ + if (Status != STATUS_KERNEL_APC) { + + /* Return Status */ + return Status; + } + + DPRINT("Looping Again\n"); + CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock(); + + } while (TRUE); + +WaitDone: + /* Release the Lock, we are done */ + DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status); + KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql); + return Status; } +VOID +FASTCALL +KiSatisfyObjectWait(PDISPATCHER_HEADER Object, + PKTHREAD Thread) -/* - * @implemented - */ -NTSTATUS STDCALL -NtWaitForSingleObject(IN HANDLE ObjectHandle, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER TimeOut OPTIONAL) { - PVOID ObjectPtr; - KPROCESSOR_MODE PreviousMode; - LARGE_INTEGER SafeTimeOut; - NTSTATUS Status = STATUS_SUCCESS; - - DPRINT("NtWaitForSingleObject(ObjectHandle %x, Alertable %d, TimeOut %x)\n", - ObjectHandle,Alertable,TimeOut); - - PreviousMode = ExGetPreviousMode(); - - if(TimeOut != NULL && PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForRead(TimeOut, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - /* make a copy on the stack */ - SafeTimeOut = *TimeOut; - TimeOut = &SafeTimeOut; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* Special case for Mutants */ + if (Object->Type == MutantObject) { - if(!NT_SUCCESS(Status)) - { - return Status; - } - } + /* Decrease the Signal State */ + Object->SignalState--; + + /* Check if it's now non-signaled */ + if (Object->SignalState == 0) { + + /* Set the Owner Thread */ + ((PKMUTANT)Object)->OwnerThread = Thread; + + /* Disable APCs if needed */ + Thread->KernelApcDisable -= ((PKMUTANT)Object)->ApcDisable; + + /* Check if it's abandoned */ + if (((PKMUTANT)Object)->Abandoned) { + + /* Unabandon it */ + ((PKMUTANT)Object)->Abandoned = FALSE; + + /* Return Status */ + Thread->WaitStatus = STATUS_ABANDONED; + } + + /* Insert it into the Mutant List */ + InsertHeadList(&Thread->MutantListHead, &((PKMUTANT)Object)->MutantListEntry); + } + + } else if ((Object->Type & TIMER_OR_EVENT_TYPE) == EventSynchronizationObject) { + + /* These guys (Syncronization Timers and Events) just get un-signaled */ + Object->SignalState = 0; + + } else if (Object->Type == SemaphoreObject) { - Status = ObReferenceObjectByHandle(ObjectHandle, - SYNCHRONIZE, - NULL, - PreviousMode, - &ObjectPtr, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - if (!KiIsObjectWaitable(ObjectPtr)) - { - DPRINT1("Waiting for object type '%wZ' is not supported\n", - &BODY_TO_HEADER(ObjectPtr)->ObjectType->TypeName); - Status = STATUS_HANDLE_NOT_WAITABLE; - } - else - { - Status = KeWaitForSingleObject(ObjectPtr, - UserRequest, - PreviousMode, - Alertable, - TimeOut); - } - - ObDereferenceObject(ObjectPtr); - - return(Status); + /* These ones can have multiple signalings, so we only decrease it */ + Object->SignalState--; + } } - -NTSTATUS STDCALL -NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal, - IN HANDLE WaitableObjectHandle, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER TimeOut OPTIONAL) +VOID +FASTCALL +KiWaitTest(PDISPATCHER_HEADER Object, + KPRIORITY Increment) { - KPROCESSOR_MODE PreviousMode; - DISPATCHER_HEADER* hdr; - PVOID SignalObj; - PVOID WaitObj; - LARGE_INTEGER SafeTimeOut; - NTSTATUS Status = STATUS_SUCCESS; + PLIST_ENTRY WaitEntry; + PLIST_ENTRY WaitList; + PKWAIT_BLOCK CurrentWaitBlock; + PKWAIT_BLOCK NextWaitBlock; + + /* Loop the Wait Entries */ + DPRINT("KiWaitTest for Object: %x\n", Object); + WaitList = &Object->WaitListHead; + WaitEntry = WaitList->Flink; + while ((WaitEntry != WaitList) && (Object->SignalState > 0)) { + + /* Get the current wait block */ + CurrentWaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); + + /* Check the current Wait Mode */ + if (CurrentWaitBlock->WaitType == WaitAny) { + + /* Easy case, satisfy only this wait */ + DPRINT("Satisfiying a Wait any\n"); + WaitEntry = WaitEntry->Blink; + KiSatisfyObjectWait(Object, CurrentWaitBlock->Thread); + + } else { + + /* Everything must be satisfied */ + DPRINT("Checking for a Wait All\n"); + NextWaitBlock = CurrentWaitBlock->NextWaitBlock; + + /* Loop first to make sure they are valid */ + while (NextWaitBlock) { + + /* Check if the object is signaled */ + if (!KiIsObjectSignaled(Object, CurrentWaitBlock->Thread)) { + + /* It's not, move to the next one */ + DPRINT1("One of the object is non-signaled, sorry.\n"); + goto SkipUnwait; + } + + /* Go to the next Wait block */ + NextWaitBlock = NextWaitBlock->NextWaitBlock; + } + + /* All the objects are signaled, we can satisfy */ + DPRINT("Satisfiying a Wait All\n"); + WaitEntry = WaitEntry->Blink; + KiSatisifyMultipleObjectWaits(CurrentWaitBlock); + } + + /* All waits satisfied, unwait the thread */ + DPRINT("Unwaiting the Thread\n"); + KiAbortWaitThread(CurrentWaitBlock->Thread, CurrentWaitBlock->WaitKey, Increment); - PreviousMode = ExGetPreviousMode(); - - if(TimeOut != NULL && PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForRead(TimeOut, - sizeof(LARGE_INTEGER), - sizeof(ULONG)); - /* make a copy on the stack */ - SafeTimeOut = *TimeOut; - TimeOut = &SafeTimeOut; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObReferenceObjectByHandle(ObjectHandleToSignal, - 0, - NULL, - PreviousMode, - &SignalObj, - NULL); - if (!NT_SUCCESS(Status)) - { - return Status; - } +SkipUnwait: + /* Next entry */ + WaitEntry = WaitEntry->Flink; + } + + DPRINT("Done\n"); +} - Status = ObReferenceObjectByHandle(WaitableObjectHandle, - SYNCHRONIZE, - NULL, - PreviousMode, - &WaitObj, - NULL); - if (!NT_SUCCESS(Status)) - { - ObDereferenceObject(SignalObj); - return Status; - } +/* Must be called with the dispatcher lock held */ +VOID +FASTCALL +KiAbortWaitThread(PKTHREAD Thread, + NTSTATUS WaitStatus, + KPRIORITY Increment) +{ + PKWAIT_BLOCK WaitBlock; - hdr = (DISPATCHER_HEADER *)SignalObj; - switch (hdr->Type) - { - case InternalNotificationEvent: - case InternalSynchronizationEvent: - KeSetEvent(SignalObj, - EVENT_INCREMENT, - TRUE); - break; + /* If we are blocked, we must be waiting on something also */ + DPRINT("KiAbortWaitThread: %x, Status: %x, %x \n", Thread, WaitStatus, Thread->WaitBlockList); + ASSERT((Thread->State == THREAD_STATE_BLOCKED) == (Thread->WaitBlockList != NULL)); - case InternalMutexType: - KeReleaseMutex(SignalObj, - TRUE); - break; + /* Remove the Wait Blocks from the list */ + DPRINT("Removing waits\n"); + WaitBlock = Thread->WaitBlockList; + while (WaitBlock) { + + /* Remove it */ + DPRINT("Removing Waitblock: %x, %x\n", WaitBlock, WaitBlock->NextWaitBlock); + RemoveEntryList(&WaitBlock->WaitListEntry); + + /* Go to the next one */ + WaitBlock = WaitBlock->NextWaitBlock; + }; + + /* Check if there's a Thread Timer */ + if (Thread->Timer.Header.Inserted) { + + /* Cancel the Thread Timer with the no-lock fastpath */ + DPRINT("Removing the Thread's Timer\n"); + Thread->Timer.Header.Inserted = FALSE; + RemoveEntryList(&Thread->Timer.TimerListEntry); + } + + /* Increment the Queue's active threads */ + if (Thread->Queue) { + + DPRINT("Incrementing Queue's active threads\n"); + Thread->Queue->CurrentCount++; + } - case InternalSemaphoreType: - KeReleaseSemaphore(SignalObj, - SEMAPHORE_INCREMENT, - 1, - TRUE); - break; - - default: - ObDereferenceObject(SignalObj); - ObDereferenceObject(WaitObj); - return STATUS_OBJECT_TYPE_MISMATCH; - } - - Status = KeWaitForSingleObject(WaitObj, - UserRequest, - PreviousMode, - Alertable, - TimeOut); - - ObDereferenceObject(SignalObj); - ObDereferenceObject(WaitObj); - - return Status; + /* Reschedule the Thread */ + DPRINT("Unblocking the Thread\n"); + KiUnblockThread(Thread, &WaitStatus, 0); } + +BOOLEAN +inline +FASTCALL +KiIsObjectSignaled(PDISPATCHER_HEADER Object, + PKTHREAD Thread) +{ + /* Mutants are...well...mutants! */ + if (Object->Type == MutantObject) { + + /* + * Because Cutler hates mutants, they are actually signaled if the Signal State is <= 0 + * Well, only if they are recursivly acquired (i.e if we own it right now). + * Of course, they are also signaled if their signal state is 1. + */ + if ((Object->SignalState <= 0 && ((PKMUTANT)Object)->OwnerThread == Thread) || + (Object->SignalState == 1)) { + + /* Signaled Mutant */ + return (TRUE); + + } else { + + /* Unsignaled Mutant */ + return (FALSE); + } + } + + /* Any other object is not a mutated freak, so let's use logic */ + return (!Object->SignalState <= 0); +} + +BOOL +inline +FASTCALL +KiIsObjectWaitable(PVOID Object) +{ + POBJECT_HEADER Header; + Header = BODY_TO_HEADER(Object); + + if (Header->ObjectType == ExEventObjectType || + Header->ObjectType == ExIoCompletionType || + Header->ObjectType == ExMutantObjectType || + Header->ObjectType == ExSemaphoreObjectType || + Header->ObjectType == ExTimerType || + Header->ObjectType == PsProcessType || + Header->ObjectType == PsThreadType || + Header->ObjectType == IoFileObjectType) { + + return TRUE; + + } else { + + return FALSE; + } +} + +VOID +inline +FASTCALL +KiSatisifyMultipleObjectWaits(PKWAIT_BLOCK WaitBlock) +{ + PKTHREAD WaitThread = WaitBlock->Thread; + + /* Loop through all the Wait Blocks, and wake each Object */ + while (WaitBlock) { + + /* Wake the Object */ + KiSatisfyObjectWait(WaitBlock->Object, WaitThread); + WaitBlock = WaitBlock->NextWaitBlock; + } +} + +VOID +inline +FASTCALL +KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header, + ULONG Type, + ULONG Size, + ULONG SignalState) +{ + Header->Type = (UCHAR)Type; + Header->Absolute = 0; + Header->Inserted = 0; + Header->Size = (UCHAR)Size; + Header->SignalState = SignalState; + InitializeListHead(&(Header->WaitListHead)); +} + +KIRQL +inline +FASTCALL +KeAcquireDispatcherDatabaseLock(VOID) +{ + KIRQL OldIrql; + + KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql); + return OldIrql; +} + +VOID +inline +FASTCALL +KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID) +{ + KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock); +} + +VOID +inline +FASTCALL +KeInitializeDispatcher(VOID) +{ + /* Initialize the Dispatcher Lock */ + KeInitializeSpinLock(&DispatcherDatabaseLock); +} + +VOID +inline +FASTCALL +KeReleaseDispatcherDatabaseLock(KIRQL OldIrql) +{ + /* If it's the idle thread, dispatch */ + if (!KeIsExecutingDpc() && OldIrql < DISPATCH_LEVEL && KeGetCurrentThread() != NULL && + KeGetCurrentThread() == KeGetCurrentPrcb()->IdleThread) { + + KiDispatchThreadNoLock(THREAD_STATE_READY); + KeLowerIrql(OldIrql); + + } else { + + /* Just release the spin lock */ + KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql); + } +} + +VOID +inline +FASTCALL +KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID) +{ + KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock); +} + +/* EOF */ diff --git a/reactos/ntoskrnl/ldr/init.c b/reactos/ntoskrnl/ldr/init.c index 7b0d5352fb7..aaddd443b46 100644 --- a/reactos/ntoskrnl/ldr/init.c +++ b/reactos/ntoskrnl/ldr/init.c @@ -11,540 +11,152 @@ /* INCLUDES *****************************************************************/ + #include + #define NDEBUG #include -/* MACROS ******************************************************************/ +/* + * HACK! No matter what i did, i couldnt get it working when i put these into ntos\rtl.h + * (got redefinition problems, since ntdll\rtl.h somehow include ntos\rtl.h). + * We need to merge ntos\rtl.h and ntdll\rtl.h to get this working. -Gunnar + */ +typedef struct _RTL_PROCESS_INFO +{ + ULONG Size; + HANDLE ProcessHandle; + HANDLE ThreadHandle; + CLIENT_ID ClientId; + SECTION_IMAGE_INFORMATION ImageInfo; +} RTL_PROCESS_INFO, *PRTL_PROCESS_INFO; + +NTSTATUS +STDCALL +RtlCreateUserProcess ( + IN PUNICODE_STRING ImageFileName, + IN ULONG Attributes, + IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters, + IN PSECURITY_DESCRIPTOR ProcessSecutityDescriptor OPTIONAL, + IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL, + IN HANDLE ParentProcess OPTIONAL, + IN BOOLEAN CurrentDirectory, + IN HANDLE DebugPort OPTIONAL, + IN HANDLE ExceptionPort OPTIONAL, + OUT PRTL_PROCESS_INFO ProcessInfo + ); + +NTSTATUS +STDCALL +RtlCreateProcessParameters ( + OUT PRTL_USER_PROCESS_PARAMETERS *ProcessParameters, + IN PUNICODE_STRING ImagePathName OPTIONAL, + IN PUNICODE_STRING DllPath OPTIONAL, + IN PUNICODE_STRING CurrentDirectory OPTIONAL, + IN PUNICODE_STRING CommandLine OPTIONAL, + IN PWSTR Environment OPTIONAL, + IN PUNICODE_STRING WindowTitle OPTIONAL, + IN PUNICODE_STRING DesktopInfo OPTIONAL, + IN PUNICODE_STRING ShellInfo OPTIONAL, + IN PUNICODE_STRING RuntimeInfo OPTIONAL + ); +NTSTATUS +STDCALL +RtlDestroyProcessParameters ( + IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters + ); -#define DENORMALIZE(x,addr) {if(x) x=(VOID*)((ULONG)(x)-(ULONG)(addr));} -#define ALIGN(x,align) (((ULONG)(x)+(align)-1UL)&(~((align)-1UL))) /* FUNCTIONS *****************************************************************/ -static NTSTATUS -LdrpMapProcessImage(PHANDLE SectionHandle, - PUNICODE_STRING ImagePath) -{ - OBJECT_ATTRIBUTES ObjectAttributes; - IO_STATUS_BLOCK IoStatusBlock; - HANDLE FileHandle; - NTSTATUS Status; - - /* Open image file */ - InitializeObjectAttributes(&ObjectAttributes, - ImagePath, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); - - DPRINT("Opening image file %S\n", ObjectAttributes.ObjectName->Buffer); - Status = NtOpenFile(&FileHandle, - FILE_ALL_ACCESS, - &ObjectAttributes, - &IoStatusBlock, - 0, - FILE_SYNCHRONOUS_IO_ALERT); - if (!NT_SUCCESS(Status)) - { - DPRINT("NtOpenFile() failed (Status %lx)\n", Status); - return(Status); - } - - /* Create a section for the image */ - DPRINT("Creating section\n"); - Status = NtCreateSection(SectionHandle, - SECTION_ALL_ACCESS, - NULL, - NULL, - PAGE_READWRITE, - SEC_COMMIT | SEC_IMAGE, - FileHandle); - NtClose(FileHandle); - if (!NT_SUCCESS(Status)) - { - DPRINT("NtCreateSection() failed (Status %lx)\n", Status); - } - - return(Status); -} -static NTSTATUS -LdrpCreateProcessEnvironment(HANDLE ProcessHandle, - PUNICODE_STRING ImagePath, - PVOID* ImageBaseAddress) -{ - PRTL_USER_PROCESS_PARAMETERS LocalPpb; - PRTL_USER_PROCESS_PARAMETERS ProcessPpb; - ULONG BytesWritten; - ULONG Offset; - ULONG Size; - ULONG RegionSize; - NTSTATUS Status; - - /* Calculate the PPB size */ - Size = sizeof(RTL_USER_PROCESS_PARAMETERS); - Size += ALIGN(ImagePath->Length + sizeof(WCHAR), sizeof(ULONG)); - RegionSize = ROUND_UP(Size, PAGE_SIZE); - DPRINT("Size %lu RegionSize %lu\n", Size, RegionSize); - - /* Allocate the local PPB */ - LocalPpb = NULL; - Status = NtAllocateVirtualMemory(NtCurrentProcess(), - (PVOID*)&LocalPpb, - 0, - &RegionSize, - MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE); - if (!NT_SUCCESS(Status)) - { - DPRINT("NtAllocateVirtualMemory() failed (Status %lx)\n", Status); - return(Status); - } - - DPRINT("LocalPpb %p AllocationSize %lu\n", LocalPpb, RegionSize); - - /* Initialize the local PPB */ - RtlZeroMemory(LocalPpb, - RegionSize); - LocalPpb->AllocationSize = RegionSize; - LocalPpb->Size = Size; - LocalPpb->ImagePathName.Length = ImagePath->Length; - LocalPpb->ImagePathName.MaximumLength = ImagePath->Length + sizeof(WCHAR); - LocalPpb->ImagePathName.Buffer = (PWCHAR)(LocalPpb + 1); - - /* Copy image path */ - RtlCopyMemory(LocalPpb->ImagePathName.Buffer, - ImagePath->Buffer, - ImagePath->Length); - LocalPpb->ImagePathName.Buffer[ImagePath->Length / sizeof(WCHAR)] = L'\0'; - - /* Denormalize the process parameter block */ - DENORMALIZE(LocalPpb->ImagePathName.Buffer, LocalPpb); - LocalPpb->Flags &= ~PPF_NORMALIZED; - - /* Create the process PPB */ - ProcessPpb = NULL; - Status = NtAllocateVirtualMemory(ProcessHandle, - (PVOID*)&ProcessPpb, - 0, - &RegionSize, - MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE); - if (!NT_SUCCESS(Status)) - { - DPRINT("NtAllocateVirtualMemory() failed (Status %lx)\n", Status); - - /* Release the local PPB */ - RegionSize = 0; - NtFreeVirtualMemory(NtCurrentProcess(), - (PVOID*)&LocalPpb, - &RegionSize, - MEM_RELEASE); - return(Status); - } - - /* Copy local PPB into the process PPB */ - NtWriteVirtualMemory(ProcessHandle, - ProcessPpb, - LocalPpb, - LocalPpb->AllocationSize, - &BytesWritten); - - /* Update pointer to process PPB in the process PEB */ - Offset = FIELD_OFFSET(PEB, ProcessParameters); - NtWriteVirtualMemory(ProcessHandle, - (PVOID)(PEB_BASE + Offset), - &ProcessPpb, - sizeof(ProcessPpb), - &BytesWritten); - - /* Release local PPB */ - RegionSize = 0; - NtFreeVirtualMemory(NtCurrentProcess(), - (PVOID*)&LocalPpb, - &RegionSize, - MEM_RELEASE); - - /* Read image base address. */ - Offset = FIELD_OFFSET(PEB, ImageBaseAddress); - NtReadVirtualMemory(ProcessHandle, - (PVOID)(PEB_BASE + Offset), - ImageBaseAddress, - sizeof(PVOID), - &BytesWritten); - - return(STATUS_SUCCESS); -} - -/* - FIXME: this sucks. Sucks sucks sucks. This code was duplicated, if you can - believe it, in four different places - excluding this, and twice in the two - DLLs that contained it (kernel32.dll and ntdll.dll). As much as I'd like to - rip the whole RTL out of ntdll.dll and ntoskrnl.exe and into its own static - library, ntoskrnl.exe is built separatedly from the rest of ReactOS, coming - with its own linker scripts and specifications, and, save for changes and fixes - to make it at least compile, I'm not going to touch any of it. If you feel - brave enough, you're welcome [KJK::Hyperion] -*/ -static NTSTATUS LdrpCreateStack -( - HANDLE ProcessHandle, - PINITIAL_TEB InitialTeb, - PULONG_PTR StackReserve, - PULONG_PTR StackCommit -) -{ - PVOID pStackLowest = NULL; - ULONG_PTR nSize = 0; - NTSTATUS nErrCode; - - if(StackReserve == NULL || StackCommit == NULL) - return STATUS_INVALID_PARAMETER; - - /* FIXME: no SEH, no guard pages */ - *StackCommit = *StackReserve; - - InitialTeb->StackBase = NULL; - InitialTeb->StackLimit = NULL; - InitialTeb->StackCommit = NULL; - InitialTeb->StackCommitMax = NULL; - InitialTeb->StackReserved = NULL; - - /* FIXME: this code assumes a stack growing downwards */ - /* fixed stack */ - if(*StackCommit == *StackReserve) - { - DPRINT("Fixed stack\n"); - - InitialTeb->StackLimit = NULL; - - /* allocate the stack */ - nErrCode = NtAllocateVirtualMemory - ( - ProcessHandle, - &(InitialTeb->StackLimit), - 0, - StackReserve, - MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE - ); - - /* failure */ - if(!NT_SUCCESS(nErrCode)) return nErrCode; - - /* store the highest (first) address of the stack */ - InitialTeb->StackBase = - (PUCHAR)(InitialTeb->StackLimit) + *StackReserve; - } - /* expandable stack */ - else - { - ULONG_PTR nGuardSize = PAGE_SIZE; - PVOID pGuardBase; - - DPRINT("Expandable stack\n"); - - InitialTeb->StackLimit = NULL; - InitialTeb->StackBase = NULL; - InitialTeb->StackReserved = NULL; - - /* reserve the stack */ - nErrCode = NtAllocateVirtualMemory - ( - ProcessHandle, - &(InitialTeb->StackReserved), - 0, - StackReserve, - MEM_RESERVE, - PAGE_READWRITE - ); - - /* failure */ - if(!NT_SUCCESS(nErrCode)) return nErrCode; - - DPRINT("Reserved %08X bytes\n", *StackReserve); - - /* expandable stack base - the highest address of the stack */ - InitialTeb->StackCommit = - (PUCHAR)(InitialTeb->StackReserved) + *StackReserve; - - /* expandable stack limit - the lowest committed address of the stack */ - InitialTeb->StackCommitMax = - (PUCHAR)(InitialTeb->StackCommit) - *StackCommit; - - DPRINT("Stack commit %p\n", InitialTeb->StackCommit); - DPRINT("Stack commit max %p\n", InitialTeb->StackCommitMax); - - /* commit as much stack as requested */ - nErrCode = NtAllocateVirtualMemory - ( - ProcessHandle, - &(InitialTeb->StackCommitMax), - 0, - StackCommit, - MEM_COMMIT, - PAGE_READWRITE - ); - - /* failure */ - if(!NT_SUCCESS(nErrCode)) goto l_Cleanup; - - DPRINT("Stack commit max %p\n", InitialTeb->StackCommitMax); - - pGuardBase = (PUCHAR)(InitialTeb->StackCommitMax) - PAGE_SIZE; - - DPRINT("Guard base %p\n", InitialTeb->StackCommit); - - /* set up the guard page */ - nErrCode = NtAllocateVirtualMemory - ( - ProcessHandle, - &pGuardBase, - 0, - &nGuardSize, - MEM_COMMIT, - PAGE_READWRITE | PAGE_GUARD - ); - - /* failure */ - if(!NT_SUCCESS(nErrCode)) goto l_Cleanup; - - DPRINT("Guard base %p\n", InitialTeb->StackCommit); - } - - return STATUS_SUCCESS; - - /* cleanup in case of failure */ -l_Cleanup: - if(InitialTeb->StackLimit) - pStackLowest = InitialTeb->StackLimit; - else if(InitialTeb->StackReserved) - pStackLowest = InitialTeb->StackReserved; - - /* free the stack, if it was allocated */ - if(pStackLowest != NULL) - NtFreeVirtualMemory(ProcessHandle, &pStackLowest, &nSize, MEM_RELEASE); - - return nErrCode; -} - - -NTSTATUS INIT_FUNCTION +INIT_FUNCTION +NTSTATUS LdrLoadInitialProcess(PHANDLE ProcessHandle, PHANDLE ThreadHandle) { - SECTION_IMAGE_INFORMATION Sii; - UNICODE_STRING ImagePath; - HANDLE SectionHandle; - CONTEXT Context; - INITIAL_TEB InitialTeb; - ULONG_PTR nStackReserve = 0; - ULONG_PTR nStackCommit = 0; - PVOID pStackLowest; - PVOID pStackBase; - ULONG ResultLength; - PVOID ImageBaseAddress; - ULONG InitialStack[5]; - HANDLE SystemProcessHandle; - NTSTATUS Status; + UNICODE_STRING ImagePath; + HANDLE SystemProcessHandle; + NTSTATUS Status; + PRTL_USER_PROCESS_PARAMETERS Params=NULL; + RTL_PROCESS_INFO Info; - /* Get the absolute path to smss.exe. */ - RtlRosInitUnicodeStringFromLiteral(&ImagePath, - L"\\SystemRoot\\system32\\smss.exe"); + /* Get the absolute path to smss.exe. */ + RtlRosInitUnicodeStringFromLiteral(&ImagePath, + L"\\SystemRoot\\system32\\smss.exe"); - /* Map process image */ - Status = LdrpMapProcessImage(&SectionHandle, - &ImagePath); - if (!NT_SUCCESS(Status)) - { - DPRINT("LdrpMapImage() failed (Status %lx)\n", Status); - return(Status); - } - /* Get information about the process image. */ - Status = NtQuerySection(SectionHandle, - SectionImageInformation, - &Sii, - sizeof(Sii), - &ResultLength); - if (!NT_SUCCESS(Status) || ResultLength != sizeof(Sii)) - { - DPRINT("ZwQuerySection failed (Status %X)\n", Status); - NtClose(ProcessHandle); - NtClose(SectionHandle); - return(Status); - } + Status = ObCreateHandle( + PsGetCurrentProcess(), + PsInitialSystemProcess, + PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION, + FALSE, + &SystemProcessHandle + ); - Status = ObCreateHandle(PsGetCurrentProcess(), - PsInitialSystemProcess, - PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION, - FALSE, - &SystemProcessHandle); + if(!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create a handle for the system process!\n"); + return Status; + } + + + Status = RtlCreateProcessParameters( + &Params, + &ImagePath, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + ); + if(!NT_SUCCESS(Status)) { - DPRINT1("Failed to create a handle for the system process!\n"); + DPRINT1("Failed to create ppb!\n"); + ZwClose(SystemProcessHandle); return Status; } + - DPRINT("Creating process\n"); - Status = NtCreateProcess(ProcessHandle, - PROCESS_ALL_ACCESS, - NULL, - SystemProcessHandle, - FALSE, - SectionHandle, - NULL, - NULL); - NtClose(SectionHandle); - NtClose(SystemProcessHandle); - if (!NT_SUCCESS(Status)) - { - DPRINT("NtCreateProcess() failed (Status %lx)\n", Status); + DPRINT("Creating process\n"); + + Status = RtlCreateUserProcess( + &ImagePath, + OBJ_CASE_INSENSITIVE, //Valid are OBJ_INHERIT and OBJ_CASE_INSENSITIVE. + Params, + NULL, + NULL, + SystemProcessHandle, + FALSE, + NULL, + NULL, + &Info + ); + + ZwClose(SystemProcessHandle); + RtlDestroyProcessParameters(Params); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtCreateProcess() failed (Status %lx)\n", Status); return(Status); - } + } - /* Create process environment */ - DPRINT("Creating the process environment\n"); - Status = LdrpCreateProcessEnvironment(*ProcessHandle, - &ImagePath, - &ImageBaseAddress); - if (!NT_SUCCESS(Status)) - { - DPRINT("LdrpCreateProcessEnvironment() failed (Status %lx)\n", Status); - NtClose(*ProcessHandle); - return(Status); - } - DPRINT("ImageBaseAddress: %p\n", ImageBaseAddress); + ZwResumeThread(Info.ThreadHandle, NULL); + *ProcessHandle = Info.ProcessHandle; + *ThreadHandle= Info.ThreadHandle; - /* Calculate initial stack sizes */ - if (Sii.StackReserve > 0x100000) - nStackReserve = Sii.StackReserve; - else - nStackReserve = 0x100000; /* 1MByte */ + DPRINT("Process created successfully\n"); - /* FIXME */ -#if 0 - if (Sii.StackCommit > PAGE_SIZE) - nStackCommit = Sii.StackCommit; - else - nStackCommit = PAGE_SIZE; -#endif - nStackCommit = nStackReserve - PAGE_SIZE; - - DPRINT("StackReserve 0x%lX StackCommit 0x%lX\n", - nStackReserve, nStackCommit); - - - /* Create the process stack */ - Status = LdrpCreateStack - ( - *ProcessHandle, - &InitialTeb, - &nStackReserve, - &nStackCommit - ); - - if (!NT_SUCCESS(Status)) - { - DPRINT("Failed to write initial stack.\n"); - NtClose(ProcessHandle); - return(Status); - } - - if(InitialTeb.StackBase && InitialTeb.StackLimit) - { - pStackBase = InitialTeb.StackBase; - pStackLowest = InitialTeb.StackLimit; - } - else - { - pStackBase = InitialTeb.StackCommit; - pStackLowest = InitialTeb.StackReserved; - } - - DPRINT("pStackBase = %p\n", pStackBase); - DPRINT("pStackLowest = %p\n", pStackLowest); - - /* - * Initialize context to point to LdrStartup - */ -#if defined(_M_IX86) - memset(&Context,0,sizeof(CONTEXT)); - Context.ContextFlags = CONTEXT_FULL; - Context.FloatSave.ControlWord = 0xffff037f; - Context.FloatSave.StatusWord = 0xffff0000; - Context.FloatSave.TagWord = 0xffffffff; - Context.FloatSave.DataSelector = 0xffff0000; - Context.Eip = (ULONG_PTR)((char*)ImageBaseAddress + (ULONG_PTR)Sii.EntryPoint); - Context.SegCs = USER_CS; - Context.SegDs = USER_DS; - Context.SegEs = USER_DS; - Context.SegFs = TEB_SELECTOR; - Context.SegGs = USER_DS; - Context.SegSs = USER_DS; - Context.EFlags = 0x202; - Context.Esp = (ULONG_PTR)pStackBase - 20; -#else -#error Unsupported architecture -#endif - - /* - * Write in the initial stack. - */ - InitialStack[0] = 0; - InitialStack[1] = PEB_BASE; - Status = NtWriteVirtualMemory(*ProcessHandle, - (PVOID)Context.Esp, - InitialStack, - sizeof(InitialStack), - &ResultLength); - if (!NT_SUCCESS(Status)) - { - ULONG_PTR nSize = 0; - - DPRINT("Failed to write initial stack.\n"); - - NtFreeVirtualMemory(*ProcessHandle, - pStackLowest, - &nSize, - MEM_RELEASE); - NtClose(*ProcessHandle); - return(Status); - } - - /* Create initial thread */ - DPRINT("Creating thread for initial process\n"); - Status = NtCreateThread(ThreadHandle, - THREAD_ALL_ACCESS, - NULL, - *ProcessHandle, - NULL, - &Context, - &InitialTeb, - FALSE); - if (!NT_SUCCESS(Status)) - { - ULONG_PTR nSize = 0; - - DPRINT("NtCreateThread() failed (Status %lx)\n", Status); - - NtFreeVirtualMemory(*ProcessHandle, - pStackLowest, - &nSize, - MEM_RELEASE); - - NtClose(*ProcessHandle); - return(Status); - } - - DPRINT("Process created successfully\n"); - - return(STATUS_SUCCESS); + return(STATUS_SUCCESS); } /* EOF */ diff --git a/reactos/ntoskrnl/ldr/loader.c b/reactos/ntoskrnl/ldr/loader.c index c1f7a8ab78a..61ce65a03ed 100644 --- a/reactos/ntoskrnl/ldr/loader.c +++ b/reactos/ntoskrnl/ldr/loader.c @@ -1,5 +1,5 @@ /* $Id$ - * + * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ldr/loader.c @@ -41,10 +41,13 @@ STATIC MODULE_TEXT_SECTION NtoskrnlTextSection; STATIC MODULE_TEXT_SECTION LdrHalTextSection; ULONG_PTR LdrHalBase; -#define TAG_DRIVER_MEM TAG('D', 'R', 'V', 'M') +#define TAG_DRIVER_MEM TAG('D', 'R', 'V', 'M') /* drvm */ +#define TAG_MODULE_OBJECT TAG('k', 'l', 'm', 'o') /* klmo - kernel ldr module object */ +#define TAG_LDR_WSTR TAG('k', 'l', 'w', 's') /* klws - kernel ldr wide string */ +#define TAG_MODULE_TEXT_SECTION TAG('k', 'l', 'm', 't') /* klmt - kernel ldr module text */ #ifndef HIWORD -#define HIWORD(X) ((WORD) (((DWORD) (X) >> 16) & 0xFFFF)) +#define HIWORD(X) ((WORD) (((DWORD) (X) >> 16) & 0xFFFF)) #endif #ifndef LOWORD #define LOWORD(X) ((WORD) (X)) @@ -53,1554 +56,1583 @@ ULONG_PTR LdrHalBase; /* FORWARD DECLARATIONS ******************************************************/ NTSTATUS -LdrProcessModule(PVOID ModuleLoadBase, - PUNICODE_STRING ModuleName, - PMODULE_OBJECT *ModuleObject); +LdrProcessModule ( + PVOID ModuleLoadBase, + PUNICODE_STRING ModuleName, + PMODULE_OBJECT *ModuleObject ); static VOID -LdrpBuildModuleBaseName(PUNICODE_STRING BaseName, - PUNICODE_STRING FullName); +LdrpBuildModuleBaseName ( + PUNICODE_STRING BaseName, + PUNICODE_STRING FullName ); static LONG -LdrpCompareModuleNames(IN PUNICODE_STRING String1, - IN PUNICODE_STRING String2); +LdrpCompareModuleNames ( + IN PUNICODE_STRING String1, + IN PUNICODE_STRING String2 ); /* PE Driver load support */ -static NTSTATUS LdrPEProcessModule(PVOID ModuleLoadBase, - PUNICODE_STRING FileName, - PMODULE_OBJECT *ModuleObject); -static PVOID -LdrPEGetExportByName(PVOID BaseAddress, - PUCHAR SymbolName, - WORD Hint); +static NTSTATUS +LdrPEProcessModule ( + PVOID ModuleLoadBase, + PUNICODE_STRING FileName, + PMODULE_OBJECT *ModuleObject ); static PVOID -LdrPEFixupForward(PCHAR ForwardName); +LdrPEGetExportByName ( + PVOID BaseAddress, + PUCHAR SymbolName, + WORD Hint ); + +static PVOID +LdrPEFixupForward ( PCHAR ForwardName ); static NTSTATUS -LdrPEPerformRelocations(PVOID DriverBase, - ULONG DriverSize); +LdrPEPerformRelocations ( + PVOID DriverBase, + ULONG DriverSize ); static NTSTATUS -LdrPEFixupImports(PMODULE_OBJECT Module); +LdrPEFixupImports ( PMODULE_OBJECT Module ); /* FUNCTIONS *****************************************************************/ VOID -LdrInitDebug(PLOADER_MODULE Module, PWCH Name) +LdrInitDebug ( PLOADER_MODULE Module, PWCH Name ) { - PLIST_ENTRY current_entry; - MODULE_TEXT_SECTION* current; + PLIST_ENTRY current_entry; + MODULE_TEXT_SECTION* current; - current_entry = ModuleTextListHead.Flink; - while (current_entry != &ModuleTextListHead) + current_entry = ModuleTextListHead.Flink; + while (current_entry != &ModuleTextListHead) { - current = - CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry); - if (wcscmp(current->Name, Name) == 0) - { - break; - } - current_entry = current_entry->Flink; + current = + CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry); + if (wcscmp(current->Name, Name) == 0) + { + break; + } + current_entry = current_entry->Flink; } - if (current_entry == &ModuleTextListHead) + if (current_entry == &ModuleTextListHead) { - return; + return; } } VOID INIT_FUNCTION -LdrInit1(VOID) +LdrInit1 ( VOID ) { - PIMAGE_NT_HEADERS NtHeader; - PIMAGE_SECTION_HEADER SectionList; + PIMAGE_NT_HEADERS NtHeader; + PIMAGE_SECTION_HEADER SectionList; - InitializeListHead(&ModuleTextListHead); + InitializeListHead(&ModuleTextListHead); - /* Setup ntoskrnl.exe text section */ - /* - * This isn't the base of the text segment, but the start of the - * full image (in memory) - * Also, the Length field isn't set to the length of the segment, - * but is more like the offset, from the image base, to the end - * of the segment. - */ - NtHeader = RtlImageNtHeader((PVOID)KERNEL_BASE); - SectionList = IMAGE_FIRST_SECTION(NtHeader); - NtoskrnlTextSection.Base = KERNEL_BASE; - NtoskrnlTextSection.Length = SectionList[0].Misc.VirtualSize + - SectionList[0].VirtualAddress; - NtoskrnlTextSection.Name = KERNEL_MODULE_NAME; - NtoskrnlTextSection.OptionalHeader = OPTHDROFFSET(KERNEL_BASE); - InsertTailList(&ModuleTextListHead, &NtoskrnlTextSection.ListEntry); + /* Setup ntoskrnl.exe text section */ + /* + * This isn't the base of the text segment, but the start of the + * full image (in memory) + * Also, the Length field isn't set to the length of the segment, + * but is more like the offset, from the image base, to the end + * of the segment. + */ + NtHeader = RtlImageNtHeader((PVOID)KERNEL_BASE); + SectionList = IMAGE_FIRST_SECTION(NtHeader); + NtoskrnlTextSection.Base = KERNEL_BASE; + NtoskrnlTextSection.Length = SectionList[0].Misc.VirtualSize + + SectionList[0].VirtualAddress; + NtoskrnlTextSection.Name = KERNEL_MODULE_NAME; + NtoskrnlTextSection.OptionalHeader = OPTHDROFFSET(KERNEL_BASE); + InsertTailList(&ModuleTextListHead, &NtoskrnlTextSection.ListEntry); - /* Setup hal.dll text section */ - /* Same comment as above applies */ - NtHeader = RtlImageNtHeader((PVOID)LdrHalBase); - SectionList = IMAGE_FIRST_SECTION(NtHeader); - LdrHalTextSection.Base = LdrHalBase; - LdrHalTextSection.Length = SectionList[0].Misc.VirtualSize + - SectionList[0].VirtualAddress; - LdrHalTextSection.Name = HAL_MODULE_NAME; - LdrHalTextSection.OptionalHeader = OPTHDROFFSET(LdrHalBase); - InsertTailList(&ModuleTextListHead, &LdrHalTextSection.ListEntry); + /* Setup hal.dll text section */ + /* Same comment as above applies */ + NtHeader = RtlImageNtHeader((PVOID)LdrHalBase); + SectionList = IMAGE_FIRST_SECTION(NtHeader); + LdrHalTextSection.Base = LdrHalBase; + LdrHalTextSection.Length = SectionList[0].Misc.VirtualSize + + SectionList[0].VirtualAddress; + LdrHalTextSection.Name = HAL_MODULE_NAME; + LdrHalTextSection.OptionalHeader = OPTHDROFFSET(LdrHalBase); + InsertTailList(&ModuleTextListHead, &LdrHalTextSection.ListEntry); - /* Hook for KDB on initialization of the loader. */ - KDB_LOADERINIT_HOOK(&NtoskrnlTextSection, &LdrHalTextSection); + /* Hook for KDB on initialization of the loader. */ + KDB_LOADERINIT_HOOK(&NtoskrnlTextSection, &LdrHalTextSection); } VOID INIT_FUNCTION -LdrInitModuleManagement(VOID) +LdrInitModuleManagement ( VOID ) { - PIMAGE_NT_HEADERS NtHeader; + PIMAGE_NT_HEADERS NtHeader; - /* Initialize the module list and spinlock */ - InitializeListHead(&ModuleListHead); - KeInitializeSpinLock(&ModuleListLock); + /* Initialize the module list and spinlock */ + InitializeListHead(&ModuleListHead); + KeInitializeSpinLock(&ModuleListLock); - /* Initialize ModuleObject for NTOSKRNL */ - RtlZeroMemory(&NtoskrnlModuleObject, sizeof(MODULE_OBJECT)); - NtoskrnlModuleObject.Base = (PVOID) KERNEL_BASE; - NtoskrnlModuleObject.Flags = MODULE_FLAG_PE; - RtlInitUnicodeString(&NtoskrnlModuleObject.FullName, KERNEL_MODULE_NAME); - LdrpBuildModuleBaseName(&NtoskrnlModuleObject.BaseName, &NtoskrnlModuleObject.FullName); + /* Initialize ModuleObject for NTOSKRNL */ + RtlZeroMemory(&NtoskrnlModuleObject, sizeof(MODULE_OBJECT)); + NtoskrnlModuleObject.Base = (PVOID) KERNEL_BASE; + NtoskrnlModuleObject.Flags = MODULE_FLAG_PE; + RtlInitUnicodeString(&NtoskrnlModuleObject.FullName, KERNEL_MODULE_NAME); + LdrpBuildModuleBaseName(&NtoskrnlModuleObject.BaseName, &NtoskrnlModuleObject.FullName); - NtHeader = RtlImageNtHeader((PVOID)KERNEL_BASE); - NtoskrnlModuleObject.Image.PE.FileHeader = &NtHeader->FileHeader; - NtoskrnlModuleObject.Image.PE.OptionalHeader = &NtHeader->OptionalHeader; - NtoskrnlModuleObject.Image.PE.SectionList = IMAGE_FIRST_SECTION(NtHeader); - NtoskrnlModuleObject.EntryPoint = (PVOID) ((ULONG_PTR) NtoskrnlModuleObject.Base + NtHeader->OptionalHeader.AddressOfEntryPoint); - DPRINT("ModuleObject:%08x entrypoint at %x\n", &NtoskrnlModuleObject, NtoskrnlModuleObject.EntryPoint); - NtoskrnlModuleObject.Length = NtoskrnlModuleObject.Image.PE.OptionalHeader->SizeOfImage; - NtoskrnlModuleObject.TextSection = &NtoskrnlTextSection; + NtHeader = RtlImageNtHeader((PVOID)KERNEL_BASE); + NtoskrnlModuleObject.Image.PE.FileHeader = &NtHeader->FileHeader; + NtoskrnlModuleObject.Image.PE.OptionalHeader = &NtHeader->OptionalHeader; + NtoskrnlModuleObject.Image.PE.SectionList = IMAGE_FIRST_SECTION(NtHeader); + NtoskrnlModuleObject.EntryPoint = (PVOID) ((ULONG_PTR) NtoskrnlModuleObject.Base + NtHeader->OptionalHeader.AddressOfEntryPoint); + DPRINT("ModuleObject:%08x entrypoint at %x\n", &NtoskrnlModuleObject, NtoskrnlModuleObject.EntryPoint); + NtoskrnlModuleObject.Length = NtoskrnlModuleObject.Image.PE.OptionalHeader->SizeOfImage; + NtoskrnlModuleObject.TextSection = &NtoskrnlTextSection; - InsertTailList(&ModuleListHead, - &NtoskrnlModuleObject.ListEntry); + InsertTailList(&ModuleListHead, + &NtoskrnlModuleObject.ListEntry); - /* Initialize ModuleObject for HAL */ - RtlZeroMemory(&HalModuleObject, sizeof(MODULE_OBJECT)); - HalModuleObject.Base = (PVOID) LdrHalBase; - HalModuleObject.Flags = MODULE_FLAG_PE; + /* Initialize ModuleObject for HAL */ + RtlZeroMemory(&HalModuleObject, sizeof(MODULE_OBJECT)); + HalModuleObject.Base = (PVOID) LdrHalBase; + HalModuleObject.Flags = MODULE_FLAG_PE; - RtlInitUnicodeString(&HalModuleObject.FullName, HAL_MODULE_NAME); - LdrpBuildModuleBaseName(&HalModuleObject.BaseName, &HalModuleObject.FullName); + RtlInitUnicodeString(&HalModuleObject.FullName, HAL_MODULE_NAME); + LdrpBuildModuleBaseName(&HalModuleObject.BaseName, &HalModuleObject.FullName); - NtHeader = RtlImageNtHeader((PVOID)LdrHalBase); - HalModuleObject.Image.PE.FileHeader = &NtHeader->FileHeader; - HalModuleObject.Image.PE.OptionalHeader = &NtHeader->OptionalHeader; - HalModuleObject.Image.PE.SectionList = IMAGE_FIRST_SECTION(NtHeader); - HalModuleObject.EntryPoint = (PVOID) ((ULONG_PTR) HalModuleObject.Base + NtHeader->OptionalHeader.AddressOfEntryPoint); - DPRINT("ModuleObject:%08x entrypoint at %x\n", &HalModuleObject, HalModuleObject.EntryPoint); - HalModuleObject.Length = HalModuleObject.Image.PE.OptionalHeader->SizeOfImage; - HalModuleObject.TextSection = &LdrHalTextSection; + NtHeader = RtlImageNtHeader((PVOID)LdrHalBase); + HalModuleObject.Image.PE.FileHeader = &NtHeader->FileHeader; + HalModuleObject.Image.PE.OptionalHeader = &NtHeader->OptionalHeader; + HalModuleObject.Image.PE.SectionList = IMAGE_FIRST_SECTION(NtHeader); + HalModuleObject.EntryPoint = (PVOID) ((ULONG_PTR) HalModuleObject.Base + NtHeader->OptionalHeader.AddressOfEntryPoint); + DPRINT("ModuleObject:%08x entrypoint at %x\n", &HalModuleObject, HalModuleObject.EntryPoint); + HalModuleObject.Length = HalModuleObject.Image.PE.OptionalHeader->SizeOfImage; + HalModuleObject.TextSection = &LdrHalTextSection; - InsertTailList(&ModuleListHead, - &HalModuleObject.ListEntry); + InsertTailList(&ModuleListHead, + &HalModuleObject.ListEntry); } NTSTATUS -LdrpLoadImage(PUNICODE_STRING DriverName, - PVOID *ModuleBase, - PVOID *SectionPointer, - PVOID *EntryPoint, - PVOID *ExportSectionPointer) +LdrpLoadImage ( + PUNICODE_STRING DriverName, + PVOID *ModuleBase, + PVOID *SectionPointer, + PVOID *EntryPoint, + PVOID *ExportSectionPointer ) { - PMODULE_OBJECT ModuleObject; - NTSTATUS Status; + PMODULE_OBJECT ModuleObject; + NTSTATUS Status; - ModuleObject = LdrGetModuleObject(DriverName); - if (ModuleObject == NULL) + ModuleObject = LdrGetModuleObject(DriverName); + if (ModuleObject == NULL) { - Status = LdrLoadModule(DriverName, &ModuleObject); - if (!NT_SUCCESS(Status)) - { - return(Status); - } + Status = LdrLoadModule(DriverName, &ModuleObject); + if (!NT_SUCCESS(Status)) + { + return(Status); + } } - if (ModuleBase) - *ModuleBase = ModuleObject->Base; + if (ModuleBase) + *ModuleBase = ModuleObject->Base; -// if (SectionPointer) -// *SectionPointer = ModuleObject-> + //if (SectionPointer) + // *SectionPointer = ModuleObject-> - if (EntryPoint) - *EntryPoint = ModuleObject->EntryPoint; + if (EntryPoint) + *EntryPoint = ModuleObject->EntryPoint; -// if (ExportSectionPointer) -// *ExportSectionPointer = ModuleObject-> + //if (ExportSectionPointer) + // *ExportSectionPointer = ModuleObject-> - return(STATUS_SUCCESS); + return(STATUS_SUCCESS); } NTSTATUS -LdrpUnloadImage(PVOID ModuleBase) +LdrpUnloadImage ( PVOID ModuleBase ) { - return(STATUS_NOT_IMPLEMENTED); + return(STATUS_NOT_IMPLEMENTED); } NTSTATUS -LdrpLoadAndCallImage(PUNICODE_STRING ModuleName) +LdrpLoadAndCallImage ( PUNICODE_STRING ModuleName ) { - PDRIVER_INITIALIZE DriverEntry; - PMODULE_OBJECT ModuleObject; - NTSTATUS Status; + PDRIVER_INITIALIZE DriverEntry; + PMODULE_OBJECT ModuleObject; + NTSTATUS Status; - ModuleObject = LdrGetModuleObject(ModuleName); - if (ModuleObject != NULL) + ModuleObject = LdrGetModuleObject(ModuleName); + if (ModuleObject != NULL) { - return(STATUS_IMAGE_ALREADY_LOADED); + return(STATUS_IMAGE_ALREADY_LOADED); } - Status = LdrLoadModule(ModuleName, &ModuleObject); - if (!NT_SUCCESS(Status)) + Status = LdrLoadModule(ModuleName, &ModuleObject); + if (!NT_SUCCESS(Status)) { - return(Status); + return(Status); } - DriverEntry = (PDRIVER_INITIALIZE)ModuleObject->EntryPoint; + DriverEntry = (PDRIVER_INITIALIZE)ModuleObject->EntryPoint; - Status = DriverEntry(NULL, NULL); - if (!NT_SUCCESS(Status)) + Status = DriverEntry(NULL, NULL); + if (!NT_SUCCESS(Status)) { - LdrUnloadModule(ModuleObject); + LdrUnloadModule(ModuleObject); } - return(Status); + return(Status); } NTSTATUS -LdrLoadModule(PUNICODE_STRING Filename, - PMODULE_OBJECT *ModuleObject) +LdrLoadModule( + PUNICODE_STRING Filename, + PMODULE_OBJECT *ModuleObject ) { - PVOID ModuleLoadBase; - NTSTATUS Status; - HANDLE FileHandle; - OBJECT_ATTRIBUTES ObjectAttributes; - PMODULE_OBJECT Module; - FILE_STANDARD_INFORMATION FileStdInfo; - IO_STATUS_BLOCK IoStatusBlock; + PVOID ModuleLoadBase; + NTSTATUS Status; + HANDLE FileHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + PMODULE_OBJECT Module; + FILE_STANDARD_INFORMATION FileStdInfo; + IO_STATUS_BLOCK IoStatusBlock; - *ModuleObject = NULL; + *ModuleObject = NULL; - DPRINT("Loading Module %wZ...\n", Filename); + DPRINT("Loading Module %wZ...\n", Filename); - /* Open the Module */ - InitializeObjectAttributes(&ObjectAttributes, - Filename, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); - CHECKPOINT; - Status = ZwOpenFile(&FileHandle, - FILE_ALL_ACCESS, - &ObjectAttributes, - &IoStatusBlock, - 0, - FILE_SYNCHRONOUS_IO_NONALERT); - CHECKPOINT; - if (!NT_SUCCESS(Status)) + /* Open the Module */ + InitializeObjectAttributes(&ObjectAttributes, + Filename, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + CHECKPOINT; + Status = ZwOpenFile(&FileHandle, + FILE_ALL_ACCESS, + &ObjectAttributes, + &IoStatusBlock, + 0, + FILE_SYNCHRONOUS_IO_NONALERT); + CHECKPOINT; + if (!NT_SUCCESS(Status)) { - CPRINT("Could not open module file: %wZ\n", Filename); - return(Status); + CPRINT("Could not open module file: %wZ\n", Filename); + return(Status); } - CHECKPOINT; + CHECKPOINT; - /* Get the size of the file */ - Status = ZwQueryInformationFile(FileHandle, - &IoStatusBlock, - &FileStdInfo, - sizeof(FileStdInfo), - FileStandardInformation); - if (!NT_SUCCESS(Status)) + /* Get the size of the file */ + Status = ZwQueryInformationFile(FileHandle, + &IoStatusBlock, + &FileStdInfo, + sizeof(FileStdInfo), + FileStandardInformation); + if (!NT_SUCCESS(Status)) { - CPRINT("Could not get file size\n"); - NtClose(FileHandle); - return(Status); + CPRINT("Could not get file size\n"); + NtClose(FileHandle); + return(Status); } - CHECKPOINT; + CHECKPOINT; - /* Allocate nonpageable memory for driver */ - ModuleLoadBase = ExAllocatePoolWithTag(NonPagedPool, - FileStdInfo.EndOfFile.u.LowPart, - TAG_DRIVER_MEM); - if (ModuleLoadBase == NULL) + /* Allocate nonpageable memory for driver */ + ModuleLoadBase = ExAllocatePoolWithTag(NonPagedPool, + FileStdInfo.EndOfFile.u.LowPart, + TAG_DRIVER_MEM); + if (ModuleLoadBase == NULL) { - CPRINT("Could not allocate memory for module"); - NtClose(FileHandle); - return(STATUS_INSUFFICIENT_RESOURCES); + CPRINT("Could not allocate memory for module"); + NtClose(FileHandle); + return(STATUS_INSUFFICIENT_RESOURCES); } - CHECKPOINT; + CHECKPOINT; - /* Load driver into memory chunk */ - Status = ZwReadFile(FileHandle, - 0, 0, 0, - &IoStatusBlock, - ModuleLoadBase, - FileStdInfo.EndOfFile.u.LowPart, - 0, 0); - if (!NT_SUCCESS(Status)) + /* Load driver into memory chunk */ + Status = ZwReadFile(FileHandle, + 0, 0, 0, + &IoStatusBlock, + ModuleLoadBase, + FileStdInfo.EndOfFile.u.LowPart, + 0, 0); + if (!NT_SUCCESS(Status)) { - CPRINT("Could not read module file into memory"); - ExFreePool(ModuleLoadBase); - NtClose(FileHandle); - return(Status); + CPRINT("Could not read module file into memory"); + ExFreePool(ModuleLoadBase); + NtClose(FileHandle); + return(Status); } - CHECKPOINT; + CHECKPOINT; - ZwClose(FileHandle); + ZwClose(FileHandle); - Status = LdrProcessModule(ModuleLoadBase, - Filename, - &Module); - if (!NT_SUCCESS(Status)) + Status = LdrProcessModule(ModuleLoadBase, + Filename, + &Module); + if (!NT_SUCCESS(Status)) { - CPRINT("Could not process module\n"); - ExFreePool(ModuleLoadBase); - return(Status); + CPRINT("Could not process module\n"); + ExFreePool(ModuleLoadBase); + return(Status); } - /* Cleanup */ - ExFreePool(ModuleLoadBase); + /* Cleanup */ + ExFreePool(ModuleLoadBase); - *ModuleObject = Module; + *ModuleObject = Module; - /* Hook for KDB on loading a driver. */ - KDB_LOADDRIVER_HOOK(Filename, Module); + /* Hook for KDB on loading a driver. */ + KDB_LOADDRIVER_HOOK(Filename, Module); - return(STATUS_SUCCESS); + return(STATUS_SUCCESS); } NTSTATUS -LdrUnloadModule(PMODULE_OBJECT ModuleObject) +LdrUnloadModule ( PMODULE_OBJECT ModuleObject ) { - KIRQL Irql; + KIRQL Irql; - /* Remove the module from the module list */ - KeAcquireSpinLock(&ModuleListLock,&Irql); - RemoveEntryList(&ModuleObject->ListEntry); - KeReleaseSpinLock(&ModuleListLock, Irql); + /* Remove the module from the module list */ + KeAcquireSpinLock(&ModuleListLock,&Irql); + RemoveEntryList(&ModuleObject->ListEntry); + KeReleaseSpinLock(&ModuleListLock, Irql); - /* Hook for KDB on unloading a driver. */ - KDB_UNLOADDRIVER_HOOK(ModuleObject); + /* Hook for KDB on unloading a driver. */ + KDB_UNLOADDRIVER_HOOK(ModuleObject); - /* Free text section */ - if (ModuleObject->TextSection != NULL) + /* Free text section */ + if (ModuleObject->TextSection != NULL) { - ExFreePool(ModuleObject->TextSection->Name); - RemoveEntryList(&ModuleObject->TextSection->ListEntry); - ExFreePool(ModuleObject->TextSection); - ModuleObject->TextSection = NULL; + ExFreePool(ModuleObject->TextSection->Name); + RemoveEntryList(&ModuleObject->TextSection->ListEntry); + ExFreePool(ModuleObject->TextSection); + ModuleObject->TextSection = NULL; } - /* Free module section */ -// MmFreeSection(ModuleObject->Base); + /* Free module section */ + // MmFreeSection(ModuleObject->Base); - ExFreePool(ModuleObject->FullName.Buffer); - ExFreePool(ModuleObject); + ExFreePool(ModuleObject->FullName.Buffer); + ExFreePool(ModuleObject); - return(STATUS_SUCCESS); + return(STATUS_SUCCESS); } NTSTATUS -LdrProcessModule(PVOID ModuleLoadBase, - PUNICODE_STRING ModuleName, - PMODULE_OBJECT *ModuleObject) +LdrProcessModule( + PVOID ModuleLoadBase, + PUNICODE_STRING ModuleName, + PMODULE_OBJECT *ModuleObject ) { - PIMAGE_DOS_HEADER PEDosHeader; + PIMAGE_DOS_HEADER PEDosHeader; - /* If MZ header exists */ - PEDosHeader = (PIMAGE_DOS_HEADER) ModuleLoadBase; - if (PEDosHeader->e_magic == IMAGE_DOS_SIGNATURE && PEDosHeader->e_lfanew != 0L) + /* If MZ header exists */ + PEDosHeader = (PIMAGE_DOS_HEADER) ModuleLoadBase; + if (PEDosHeader->e_magic == IMAGE_DOS_SIGNATURE && PEDosHeader->e_lfanew != 0L) { - return LdrPEProcessModule(ModuleLoadBase, - ModuleName, - ModuleObject); + return LdrPEProcessModule(ModuleLoadBase, + ModuleName, + ModuleObject); } - CPRINT("Module wasn't PE\n"); - return STATUS_UNSUCCESSFUL; + CPRINT("Module wasn't PE\n"); + return STATUS_UNSUCCESSFUL; } NTSTATUS -LdrpQueryModuleInformation(PVOID Buffer, - ULONG Size, - PULONG ReqSize) +LdrpQueryModuleInformation ( + PVOID Buffer, + ULONG Size, + PULONG ReqSize ) { - PLIST_ENTRY current_entry; - PMODULE_OBJECT current; - ULONG ModuleCount = 0; - PSYSTEM_MODULE_INFORMATION Smi; - ANSI_STRING AnsiName; - PCHAR p; - KIRQL Irql; + PLIST_ENTRY current_entry; + PMODULE_OBJECT current; + ULONG ModuleCount = 0; + PSYSTEM_MODULE_INFORMATION Smi; + ANSI_STRING AnsiName; + PCHAR p; + KIRQL Irql; - KeAcquireSpinLock(&ModuleListLock,&Irql); + KeAcquireSpinLock(&ModuleListLock,&Irql); - /* calculate required size */ - current_entry = ModuleListHead.Flink; - while (current_entry != (&ModuleListHead)) + /* calculate required size */ + current_entry = ModuleListHead.Flink; + while (current_entry != (&ModuleListHead)) { - ModuleCount++; - current_entry = current_entry->Flink; + ModuleCount++; + current_entry = current_entry->Flink; } - *ReqSize = sizeof(SYSTEM_MODULE_INFORMATION)+ - (ModuleCount - 1) * sizeof(SYSTEM_MODULE_INFORMATION_ENTRY); + *ReqSize = sizeof(SYSTEM_MODULE_INFORMATION)+ + (ModuleCount - 1) * sizeof(SYSTEM_MODULE_INFORMATION_ENTRY); - if (Size < *ReqSize) + if (Size < *ReqSize) { - KeReleaseSpinLock(&ModuleListLock, Irql); - return(STATUS_INFO_LENGTH_MISMATCH); + KeReleaseSpinLock(&ModuleListLock, Irql); + return(STATUS_INFO_LENGTH_MISMATCH); } - /* fill the buffer */ - memset(Buffer, '=', Size); + /* fill the buffer */ + memset(Buffer, '=', Size); - Smi = (PSYSTEM_MODULE_INFORMATION)Buffer; - Smi->Count = ModuleCount; + Smi = (PSYSTEM_MODULE_INFORMATION)Buffer; + Smi->Count = ModuleCount; - ModuleCount = 0; - current_entry = ModuleListHead.Flink; - while (current_entry != (&ModuleListHead)) + ModuleCount = 0; + current_entry = ModuleListHead.Flink; + while (current_entry != (&ModuleListHead)) { - current = CONTAINING_RECORD(current_entry,MODULE_OBJECT,ListEntry); + current = CONTAINING_RECORD(current_entry,MODULE_OBJECT,ListEntry); - Smi->Module[ModuleCount].Unknown1 = 0; /* Always 0 */ - Smi->Module[ModuleCount].Unknown2 = 0; /* Always 0 */ - Smi->Module[ModuleCount].Base = current->Base; - Smi->Module[ModuleCount].Size = current->Length; - Smi->Module[ModuleCount].Flags = 0; /* Flags ??? (GN) */ - Smi->Module[ModuleCount].Index = (USHORT)ModuleCount; - Smi->Module[ModuleCount].NameLength = 0; - Smi->Module[ModuleCount].LoadCount = 0; /* FIXME */ + Smi->Module[ModuleCount].Unknown1 = 0; /* Always 0 */ + Smi->Module[ModuleCount].Unknown2 = 0; /* Always 0 */ + Smi->Module[ModuleCount].Base = current->Base; + Smi->Module[ModuleCount].Size = current->Length; + Smi->Module[ModuleCount].Flags = 0; /* Flags ??? (GN) */ + Smi->Module[ModuleCount].Index = (USHORT)ModuleCount; + Smi->Module[ModuleCount].NameLength = 0; + Smi->Module[ModuleCount].LoadCount = 0; /* FIXME */ - AnsiName.Length = 0; - AnsiName.MaximumLength = 256; - AnsiName.Buffer = Smi->Module[ModuleCount].ImageName; - RtlUnicodeStringToAnsiString(&AnsiName, - ¤t->FullName, - FALSE); + AnsiName.Length = 0; + AnsiName.MaximumLength = 256; + AnsiName.Buffer = Smi->Module[ModuleCount].ImageName; + RtlUnicodeStringToAnsiString(&AnsiName, + ¤t->FullName, + FALSE); - p = strrchr(AnsiName.Buffer, '\\'); - if (p == NULL) - { - Smi->Module[ModuleCount].PathLength = 0; - } - else - { - p++; - Smi->Module[ModuleCount].PathLength = p - AnsiName.Buffer; - } + p = strrchr(AnsiName.Buffer, '\\'); + if (p == NULL) + { + Smi->Module[ModuleCount].PathLength = 0; + } + else + { + p++; + Smi->Module[ModuleCount].PathLength = p - AnsiName.Buffer; + } - ModuleCount++; - current_entry = current_entry->Flink; + ModuleCount++; + current_entry = current_entry->Flink; } - KeReleaseSpinLock(&ModuleListLock, Irql); + KeReleaseSpinLock(&ModuleListLock, Irql); - return(STATUS_SUCCESS); + return(STATUS_SUCCESS); } static VOID -LdrpBuildModuleBaseName(PUNICODE_STRING BaseName, - PUNICODE_STRING FullName) +LdrpBuildModuleBaseName ( + PUNICODE_STRING BaseName, + PUNICODE_STRING FullName ) { - PWCHAR p; + PWCHAR p; - DPRINT("LdrpBuildModuleBaseName()\n"); - DPRINT("FullName %wZ\n", FullName); + DPRINT("LdrpBuildModuleBaseName()\n"); + DPRINT("FullName %wZ\n", FullName); - p = wcsrchr(FullName->Buffer, L'\\'); - if (p == NULL) - { - p = FullName->Buffer; - } - else - { - p++; - } + p = wcsrchr(FullName->Buffer, L'\\'); + if (p == NULL) + { + p = FullName->Buffer; + } + else + { + p++; + } - DPRINT("p %S\n", p); + DPRINT("p %S\n", p); - RtlInitUnicodeString(BaseName, p); + RtlInitUnicodeString(BaseName, p); } static LONG -LdrpCompareModuleNames(IN PUNICODE_STRING String1, - IN PUNICODE_STRING String2) +LdrpCompareModuleNames ( + IN PUNICODE_STRING String1, + IN PUNICODE_STRING String2 ) { - ULONG len1, len2, i; - PWCHAR s1, s2, p; - WCHAR c1, c2; + ULONG len1, len2, i; + PWCHAR s1, s2, p; + WCHAR c1, c2; - if (String1 && String2) + if (String1 && String2) { - /* Search String1 for last path component */ - len1 = String1->Length / sizeof(WCHAR); - s1 = String1->Buffer; - for (i = 0, p = String1->Buffer; i < String1->Length; i = i + sizeof(WCHAR), p++) - { - if (*p == L'\\') - { - if (i == String1->Length - sizeof(WCHAR)) - { - s1 = NULL; - len1 = 0; - } - else - { - s1 = p + 1; - len1 = (String1->Length - i) / sizeof(WCHAR); - } - } - } + /* Search String1 for last path component */ + len1 = String1->Length / sizeof(WCHAR); + s1 = String1->Buffer; + for (i = 0, p = String1->Buffer; i < String1->Length; i = i + sizeof(WCHAR), p++) + { + if (*p == L'\\') + { + if (i == String1->Length - sizeof(WCHAR)) + { + s1 = NULL; + len1 = 0; + } + else + { + s1 = p + 1; + len1 = (String1->Length - i) / sizeof(WCHAR); + } + } + } - /* Search String2 for last path component */ - len2 = String2->Length / sizeof(WCHAR); - s2 = String2->Buffer; - for (i = 0, p = String2->Buffer; i < String2->Length; i = i + sizeof(WCHAR), p++) - { - if (*p == L'\\') - { - if (i == String2->Length - sizeof(WCHAR)) - { - s2 = NULL; - len2 = 0; - } - else - { - s2 = p + 1; - len2 = (String2->Length - i) / sizeof(WCHAR); - } - } - } + /* Search String2 for last path component */ + len2 = String2->Length / sizeof(WCHAR); + s2 = String2->Buffer; + for (i = 0, p = String2->Buffer; i < String2->Length; i = i + sizeof(WCHAR), p++) + { + if (*p == L'\\') + { + if (i == String2->Length - sizeof(WCHAR)) + { + s2 = NULL; + len2 = 0; + } + else + { + s2 = p + 1; + len2 = (String2->Length - i) / sizeof(WCHAR); + } + } + } - /* Compare last path components */ - if (s1 && s2) - { - while (1) - { - c1 = len1-- ? RtlUpcaseUnicodeChar (*s1++) : 0; - c2 = len2-- ? RtlUpcaseUnicodeChar (*s2++) : 0; - if ((c1 == 0 && c2 == L'.') || (c1 == L'.' && c2 == 0)) - return(0); - if (!c1 || !c2 || c1 != c2) - return(c1 - c2); - } - } + /* Compare last path components */ + if (s1 && s2) + { + while (1) + { + c1 = len1-- ? RtlUpcaseUnicodeChar (*s1++) : 0; + c2 = len2-- ? RtlUpcaseUnicodeChar (*s2++) : 0; + if ((c1 == 0 && c2 == L'.') || (c1 == L'.' && c2 == 0)) + return(0); + if (!c1 || !c2 || c1 != c2) + return(c1 - c2); + } + } } - return(0); + return(0); } PMODULE_OBJECT -LdrGetModuleObject(PUNICODE_STRING ModuleName) +LdrGetModuleObject ( PUNICODE_STRING ModuleName ) { - PMODULE_OBJECT Module; - PLIST_ENTRY Entry; - KIRQL Irql; + PMODULE_OBJECT Module; + PLIST_ENTRY Entry; + KIRQL Irql; - DPRINT("LdrGetModuleObject(%wZ) called\n", ModuleName); + DPRINT("LdrGetModuleObject(%wZ) called\n", ModuleName); - KeAcquireSpinLock(&ModuleListLock,&Irql); + KeAcquireSpinLock(&ModuleListLock,&Irql); - Entry = ModuleListHead.Flink; - while (Entry != &ModuleListHead) + Entry = ModuleListHead.Flink; + while (Entry != &ModuleListHead) { - Module = CONTAINING_RECORD(Entry, MODULE_OBJECT, ListEntry); + Module = CONTAINING_RECORD(Entry, MODULE_OBJECT, ListEntry); - DPRINT("Comparing %wZ and %wZ\n", - &Module->BaseName, - ModuleName); + DPRINT("Comparing %wZ and %wZ\n", + &Module->BaseName, + ModuleName); - if (!LdrpCompareModuleNames(&Module->BaseName, ModuleName)) - { - DPRINT("Module %wZ\n", &Module->BaseName); - KeReleaseSpinLock(&ModuleListLock, Irql); - return(Module); - } + if (!LdrpCompareModuleNames(&Module->BaseName, ModuleName)) + { + DPRINT("Module %wZ\n", &Module->BaseName); + KeReleaseSpinLock(&ModuleListLock, Irql); + return(Module); + } - Entry = Entry->Flink; + Entry = Entry->Flink; } - KeReleaseSpinLock(&ModuleListLock, Irql); + KeReleaseSpinLock(&ModuleListLock, Irql); - DPRINT("Could not find module '%wZ'\n", ModuleName); + DPRINT("Could not find module '%wZ'\n", ModuleName); - return(NULL); + return(NULL); } /* ---------------------------------------------- PE Module support */ static ULONG -LdrLookupPageProtection(PVOID PageStart, - PVOID DriverBase, - PIMAGE_FILE_HEADER PEFileHeader, - PIMAGE_SECTION_HEADER PESectionHeaders) +LdrLookupPageProtection ( + PVOID PageStart, + PVOID DriverBase, + PIMAGE_FILE_HEADER PEFileHeader, + PIMAGE_SECTION_HEADER PESectionHeaders ) { - BOOLEAN Write = FALSE; - BOOLEAN Execute = FALSE; - ULONG Characteristics; - ULONG Idx; - ULONG Length; - PVOID BaseAddress; - - for (Idx = 0; Idx < PEFileHeader->NumberOfSections && (!Write || !Execute); Idx++) - { - Characteristics = PESectionHeaders[Idx].Characteristics; - if (!(Characteristics & IMAGE_SCN_TYPE_NOLOAD)) - { - Length = max(PESectionHeaders[Idx].Misc.VirtualSize, PESectionHeaders[Idx].SizeOfRawData); - BaseAddress = PESectionHeaders[Idx].VirtualAddress + (char*)DriverBase; - if (BaseAddress < (PVOID)((ULONG_PTR)PageStart + PAGE_SIZE) && - PageStart < (PVOID)((ULONG_PTR)BaseAddress + Length)) - { - if (Characteristics & IMAGE_SCN_CNT_CODE) - { - Execute = TRUE; - } - if (Characteristics & (IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_UNINITIALIZED_DATA)) - { - Write = TRUE; - } - } - } - } - if (Write && Execute) - { - return PAGE_EXECUTE_READWRITE; - } - else if (Execute) - { - return PAGE_EXECUTE_READ; - } - else if (Write) - { - return PAGE_READWRITE; - } - else - { - return PAGE_READONLY; - } + BOOLEAN Write = FALSE; + BOOLEAN Execute = FALSE; + ULONG Characteristics; + ULONG Idx; + ULONG Length; + PVOID BaseAddress; + + for (Idx = 0; Idx < PEFileHeader->NumberOfSections && (!Write || !Execute); Idx++) + { + Characteristics = PESectionHeaders[Idx].Characteristics; + if (!(Characteristics & IMAGE_SCN_TYPE_NOLOAD)) + { + Length = max(PESectionHeaders[Idx].Misc.VirtualSize, PESectionHeaders[Idx].SizeOfRawData); + BaseAddress = PESectionHeaders[Idx].VirtualAddress + (char*)DriverBase; + if (BaseAddress < (PVOID)((ULONG_PTR)PageStart + PAGE_SIZE) && + PageStart < (PVOID)((ULONG_PTR)BaseAddress + Length)) + { + if (Characteristics & IMAGE_SCN_CNT_CODE) + { + Execute = TRUE; + } + if (Characteristics & (IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_UNINITIALIZED_DATA)) + { + Write = TRUE; + } + } + } + } + if (Write && Execute) + { + return PAGE_EXECUTE_READWRITE; + } + else if (Execute) + { + return PAGE_EXECUTE_READ; + } + else if (Write) + { + return PAGE_READWRITE; + } + else + { + return PAGE_READONLY; + } } static NTSTATUS -LdrPEProcessModule(PVOID ModuleLoadBase, - PUNICODE_STRING FileName, - PMODULE_OBJECT *ModuleObject) +LdrPEProcessModule( + PVOID ModuleLoadBase, + PUNICODE_STRING FileName, + PMODULE_OBJECT *ModuleObject ) { - unsigned int DriverSize, Idx; - DWORD CurrentSize; - PVOID DriverBase; - PIMAGE_DOS_HEADER PEDosHeader; - PIMAGE_NT_HEADERS PENtHeaders; - PIMAGE_SECTION_HEADER PESectionHeaders; - PMODULE_OBJECT CreatedModuleObject; - MODULE_TEXT_SECTION* ModuleTextSection; - NTSTATUS Status; - KIRQL Irql; + unsigned int DriverSize, Idx; + DWORD CurrentSize; + PVOID DriverBase; + PIMAGE_DOS_HEADER PEDosHeader; + PIMAGE_NT_HEADERS PENtHeaders; + PIMAGE_SECTION_HEADER PESectionHeaders; + PMODULE_OBJECT CreatedModuleObject; + MODULE_TEXT_SECTION* ModuleTextSection; + NTSTATUS Status; + KIRQL Irql; - DPRINT("Processing PE Module at module base:%08lx\n", ModuleLoadBase); + DPRINT("Processing PE Module at module base:%08lx\n", ModuleLoadBase); - /* Get header pointers */ - PEDosHeader = (PIMAGE_DOS_HEADER) ModuleLoadBase; - PENtHeaders = RtlImageNtHeader(ModuleLoadBase); - PESectionHeaders = IMAGE_FIRST_SECTION(PENtHeaders); - CHECKPOINT; + /* Get header pointers */ + PEDosHeader = (PIMAGE_DOS_HEADER) ModuleLoadBase; + PENtHeaders = RtlImageNtHeader(ModuleLoadBase); + PESectionHeaders = IMAGE_FIRST_SECTION(PENtHeaders); + CHECKPOINT; - /* Check file magic numbers */ - if (PEDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + /* Check file magic numbers */ + if (PEDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { - CPRINT("Incorrect MZ magic: %04x\n", PEDosHeader->e_magic); - return STATUS_UNSUCCESSFUL; + CPRINT("Incorrect MZ magic: %04x\n", PEDosHeader->e_magic); + return STATUS_UNSUCCESSFUL; } - if (PEDosHeader->e_lfanew == 0) + if (PEDosHeader->e_lfanew == 0) { - CPRINT("Invalid lfanew offset: %08x\n", PEDosHeader->e_lfanew); - return STATUS_UNSUCCESSFUL; + CPRINT("Invalid lfanew offset: %08x\n", PEDosHeader->e_lfanew); + return STATUS_UNSUCCESSFUL; } - if (PENtHeaders->Signature != IMAGE_NT_SIGNATURE) + if (PENtHeaders->Signature != IMAGE_NT_SIGNATURE) { - CPRINT("Incorrect PE magic: %08x\n", PENtHeaders->Signature); - return STATUS_UNSUCCESSFUL; + CPRINT("Incorrect PE magic: %08x\n", PENtHeaders->Signature); + return STATUS_UNSUCCESSFUL; } - if (PENtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) + if (PENtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) { - CPRINT("Incorrect Architechture: %04x\n", PENtHeaders->FileHeader.Machine); - return STATUS_UNSUCCESSFUL; + CPRINT("Incorrect Architechture: %04x\n", PENtHeaders->FileHeader.Machine); + return STATUS_UNSUCCESSFUL; } - CHECKPOINT; + CHECKPOINT; - /* FIXME: if image is fixed-address load, then fail */ + /* FIXME: if image is fixed-address load, then fail */ - /* FIXME: check/verify OS version number */ + /* FIXME: check/verify OS version number */ - DPRINT("OptionalHdrMagic:%04x LinkVersion:%d.%d\n", - PENtHeaders->OptionalHeader.Magic, - PENtHeaders->OptionalHeader.MajorLinkerVersion, - PENtHeaders->OptionalHeader.MinorLinkerVersion); - DPRINT("Entry Point:%08lx\n", PENtHeaders->OptionalHeader.AddressOfEntryPoint); + DPRINT("OptionalHdrMagic:%04x LinkVersion:%d.%d\n", + PENtHeaders->OptionalHeader.Magic, + PENtHeaders->OptionalHeader.MajorLinkerVersion, + PENtHeaders->OptionalHeader.MinorLinkerVersion); + DPRINT("Entry Point:%08lx\n", PENtHeaders->OptionalHeader.AddressOfEntryPoint); - /* Determine the size of the module */ - DriverSize = 0; - for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) - { - if (!(PESectionHeaders[Idx].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) - { + /* Determine the size of the module */ + DriverSize = 0; + for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) + { + if (!(PESectionHeaders[Idx].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) + { + CurrentSize = PESectionHeaders[Idx].VirtualAddress + PESectionHeaders[Idx].Misc.VirtualSize; + DriverSize = max(DriverSize, CurrentSize); + } + } + DriverSize = ROUND_UP(DriverSize, PENtHeaders->OptionalHeader.SectionAlignment); + DPRINT("DriverSize %x, SizeOfImage %x\n",DriverSize, PENtHeaders->OptionalHeader.SizeOfImage); + + /* Allocate a virtual section for the module */ + DriverBase = NULL; + DriverBase = MmAllocateSection(DriverSize, DriverBase); + if (DriverBase == 0) + { + CPRINT("Failed to allocate a virtual section for driver\n"); + return STATUS_UNSUCCESSFUL; + } + DbgPrint("DriverBase for %wZ: %x\n", FileName, DriverBase); + + /* Copy headers over */ + memcpy(DriverBase, ModuleLoadBase, PENtHeaders->OptionalHeader.SizeOfHeaders); + + /* Copy image sections into virtual section */ + for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) + { CurrentSize = PESectionHeaders[Idx].VirtualAddress + PESectionHeaders[Idx].Misc.VirtualSize; - DriverSize = max(DriverSize, CurrentSize); - } - } - DriverSize = ROUND_UP(DriverSize, PENtHeaders->OptionalHeader.SectionAlignment); - DPRINT("DriverSize %x, SizeOfImage %x\n",DriverSize, PENtHeaders->OptionalHeader.SizeOfImage); - - /* Allocate a virtual section for the module */ - DriverBase = NULL; - DriverBase = MmAllocateSection(DriverSize, DriverBase); - if (DriverBase == 0) - { - CPRINT("Failed to allocate a virtual section for driver\n"); - return STATUS_UNSUCCESSFUL; + /* Copy current section into current offset of virtual section */ + if (CurrentSize <= DriverSize && + PESectionHeaders[Idx].SizeOfRawData) + { + DPRINT("PESectionHeaders[Idx].VirtualAddress + DriverBase %x\n", + PESectionHeaders[Idx].VirtualAddress + (ULONG_PTR)DriverBase); + memcpy((PVOID)((ULONG_PTR)DriverBase + PESectionHeaders[Idx].VirtualAddress), + (PVOID)((ULONG_PTR)ModuleLoadBase + PESectionHeaders[Idx].PointerToRawData), + PESectionHeaders[Idx].Misc.VirtualSize > PESectionHeaders[Idx].SizeOfRawData + ? PESectionHeaders[Idx].SizeOfRawData : PESectionHeaders[Idx].Misc.VirtualSize ); + } } - DbgPrint("DriverBase for %wZ: %x\n", FileName, DriverBase); - - /* Copy headers over */ - memcpy(DriverBase, ModuleLoadBase, PENtHeaders->OptionalHeader.SizeOfHeaders); - /* Copy image sections into virtual section */ - for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) - { - CurrentSize = PESectionHeaders[Idx].VirtualAddress + PESectionHeaders[Idx].Misc.VirtualSize; - /* Copy current section into current offset of virtual section */ - if (CurrentSize <= DriverSize && - PESectionHeaders[Idx].SizeOfRawData) - { - DPRINT("PESectionHeaders[Idx].VirtualAddress + DriverBase %x\n", - PESectionHeaders[Idx].VirtualAddress + (ULONG_PTR)DriverBase); - memcpy((PVOID)((ULONG_PTR)DriverBase + PESectionHeaders[Idx].VirtualAddress), - (PVOID)((ULONG_PTR)ModuleLoadBase + PESectionHeaders[Idx].PointerToRawData), - PESectionHeaders[Idx].Misc.VirtualSize > PESectionHeaders[Idx].SizeOfRawData - ? PESectionHeaders[Idx].SizeOfRawData : PESectionHeaders[Idx].Misc.VirtualSize ); - } - } + /* Perform relocation fixups */ + Status = LdrPEPerformRelocations(DriverBase, DriverSize); + if (!NT_SUCCESS(Status)) + { + // MmFreeSection(DriverBase); + return Status; + } - /* Perform relocation fixups */ - Status = LdrPEPerformRelocations(DriverBase, DriverSize); - if (!NT_SUCCESS(Status)) - { -// MmFreeSection(DriverBase); - return Status; - } + /* Create the module */ + CreatedModuleObject = ExAllocatePoolWithTag ( + NonPagedPool, sizeof(MODULE_OBJECT), TAG_MODULE_OBJECT ); + if (CreatedModuleObject == NULL) + { + // MmFreeSection(DriverBase); + return STATUS_INSUFFICIENT_RESOURCES; + } - /* Create the module */ - CreatedModuleObject = ExAllocatePool(NonPagedPool, sizeof(MODULE_OBJECT)); - if (CreatedModuleObject == NULL) - { -// MmFreeSection(DriverBase); - return STATUS_INSUFFICIENT_RESOURCES; - } + RtlZeroMemory(CreatedModuleObject, sizeof(MODULE_OBJECT)); - RtlZeroMemory(CreatedModuleObject, sizeof(MODULE_OBJECT)); + /* Initialize ModuleObject data */ + CreatedModuleObject->Base = DriverBase; + CreatedModuleObject->Flags = MODULE_FLAG_PE; - /* Initialize ModuleObject data */ - CreatedModuleObject->Base = DriverBase; - CreatedModuleObject->Flags = MODULE_FLAG_PE; - - CreatedModuleObject->FullName.Length = 0; - CreatedModuleObject->FullName.MaximumLength = FileName->Length + sizeof(UNICODE_NULL); - CreatedModuleObject->FullName.Buffer = ExAllocatePool(PagedPool, CreatedModuleObject->FullName.MaximumLength); - if (CreatedModuleObject->FullName.Buffer == NULL) - { - ExFreePool(CreatedModuleObject); -// MmFreeSection(DriverBase); - return STATUS_INSUFFICIENT_RESOURCES; - } + CreatedModuleObject->FullName.Length = 0; + CreatedModuleObject->FullName.MaximumLength = FileName->Length + sizeof(UNICODE_NULL); + CreatedModuleObject->FullName.Buffer = + ExAllocatePoolWithTag(PagedPool, CreatedModuleObject->FullName.MaximumLength, TAG_LDR_WSTR); + if (CreatedModuleObject->FullName.Buffer == NULL) + { + ExFreePool(CreatedModuleObject); + // MmFreeSection(DriverBase); + return STATUS_INSUFFICIENT_RESOURCES; + } - RtlCopyUnicodeString(&CreatedModuleObject->FullName, FileName); - LdrpBuildModuleBaseName(&CreatedModuleObject->BaseName, - &CreatedModuleObject->FullName); - - CreatedModuleObject->EntryPoint = - (PVOID)((ULONG_PTR)DriverBase + - PENtHeaders->OptionalHeader.AddressOfEntryPoint); - CreatedModuleObject->Length = DriverSize; - DPRINT("EntryPoint at %x\n", CreatedModuleObject->EntryPoint); - - CreatedModuleObject->Image.PE.FileHeader = - (PIMAGE_FILE_HEADER) ((unsigned int) DriverBase + PEDosHeader->e_lfanew + sizeof(ULONG)); + RtlCopyUnicodeString(&CreatedModuleObject->FullName, FileName); + LdrpBuildModuleBaseName(&CreatedModuleObject->BaseName, + &CreatedModuleObject->FullName); - DPRINT("FileHeader at %x\n", CreatedModuleObject->Image.PE.FileHeader); - CreatedModuleObject->Image.PE.OptionalHeader = - (PIMAGE_OPTIONAL_HEADER) ((unsigned int) DriverBase + PEDosHeader->e_lfanew + sizeof(ULONG) + - sizeof(IMAGE_FILE_HEADER)); - DPRINT("OptionalHeader at %x\n", CreatedModuleObject->Image.PE.OptionalHeader); - CreatedModuleObject->Image.PE.SectionList = - (PIMAGE_SECTION_HEADER) ((unsigned int) DriverBase + PEDosHeader->e_lfanew + sizeof(ULONG) + - sizeof(IMAGE_FILE_HEADER) + CreatedModuleObject->Image.PE.FileHeader->SizeOfOptionalHeader); - DPRINT("SectionList at %x\n", CreatedModuleObject->Image.PE.SectionList); + CreatedModuleObject->EntryPoint = + (PVOID)((ULONG_PTR)DriverBase + + PENtHeaders->OptionalHeader.AddressOfEntryPoint); + CreatedModuleObject->Length = DriverSize; + DPRINT("EntryPoint at %x\n", CreatedModuleObject->EntryPoint); - /* Perform import fixups */ - Status = LdrPEFixupImports(CreatedModuleObject); - if (!NT_SUCCESS(Status)) - { -// MmFreeSection(DriverBase); - ExFreePool(CreatedModuleObject->FullName.Buffer); - ExFreePool(CreatedModuleObject); - return Status; - } + CreatedModuleObject->Image.PE.FileHeader = + (PIMAGE_FILE_HEADER) ((unsigned int) DriverBase + PEDosHeader->e_lfanew + sizeof(ULONG)); - MmSetPageProtect(NULL, DriverBase, PAGE_READONLY); - /* Set the protections for the various parts of the driver */ - for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) - { - ULONG Characteristics = PESectionHeaders[Idx].Characteristics; - ULONG Length; - PVOID BaseAddress; - PVOID PageAddress; - ULONG Protect; - Length = PESectionHeaders[Idx].Misc.VirtualSize; - BaseAddress = PESectionHeaders[Idx].VirtualAddress + (char*)DriverBase; - PageAddress = (PVOID)PAGE_ROUND_DOWN(BaseAddress); + DPRINT("FileHeader at %x\n", CreatedModuleObject->Image.PE.FileHeader); + CreatedModuleObject->Image.PE.OptionalHeader = + (PIMAGE_OPTIONAL_HEADER) ((unsigned int) DriverBase + PEDosHeader->e_lfanew + sizeof(ULONG) + + sizeof(IMAGE_FILE_HEADER)); + DPRINT("OptionalHeader at %x\n", CreatedModuleObject->Image.PE.OptionalHeader); + CreatedModuleObject->Image.PE.SectionList = + (PIMAGE_SECTION_HEADER) ((unsigned int) DriverBase + PEDosHeader->e_lfanew + sizeof(ULONG) + + sizeof(IMAGE_FILE_HEADER) + CreatedModuleObject->Image.PE.FileHeader->SizeOfOptionalHeader); + DPRINT("SectionList at %x\n", CreatedModuleObject->Image.PE.SectionList); + + /* Perform import fixups */ + Status = LdrPEFixupImports(CreatedModuleObject); + if (!NT_SUCCESS(Status)) + { + // MmFreeSection(DriverBase); + ExFreePool(CreatedModuleObject->FullName.Buffer); + ExFreePool(CreatedModuleObject); + return Status; + } + + MmSetPageProtect(NULL, DriverBase, PAGE_READONLY); + /* Set the protections for the various parts of the driver */ + for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) + { + ULONG Characteristics = PESectionHeaders[Idx].Characteristics; + ULONG Length; + PVOID BaseAddress; + PVOID PageAddress; + ULONG Protect; + Length = PESectionHeaders[Idx].Misc.VirtualSize; + BaseAddress = PESectionHeaders[Idx].VirtualAddress + (char*)DriverBase; + PageAddress = (PVOID)PAGE_ROUND_DOWN(BaseAddress); - Protect = LdrLookupPageProtection(PageAddress, DriverBase, &PENtHeaders->FileHeader, PESectionHeaders); -#if 1 - /* - * FIXME: - * This driver modifies a string in the first page of the text section while initialising. - */ - if (0 == _wcsicmp(L"fireport.sys", FileName->Buffer)) - { - Protect = PAGE_EXECUTE_READWRITE; - } -#endif - if (PageAddress < DriverBase + DriverSize) - { - MmSetPageProtect(NULL, PageAddress, Protect); - } - - if (Characteristics & IMAGE_SCN_CNT_CODE) - { - if (Characteristics & IMAGE_SCN_MEM_WRITE) - { - Protect = PAGE_EXECUTE_READWRITE; - } - else - { - Protect = PAGE_EXECUTE_READ; - } - } - else if (Characteristics & (IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_UNINITIALIZED_DATA)) - { - Protect = PAGE_READWRITE; - } - else - { - Protect = PAGE_READONLY; - } - PageAddress = (PVOID)((ULONG_PTR)PageAddress + PAGE_SIZE); - while ((ULONG_PTR)PageAddress + PAGE_SIZE < (ULONG_PTR)BaseAddress + Length) - { - if (PageAddress < DriverBase + DriverSize) - { - MmSetPageProtect(NULL, PageAddress, Protect); - } - PageAddress = (PVOID)((ULONG_PTR)PageAddress + PAGE_SIZE); - } - if (PageAddress < (PVOID)((ULONG_PTR)BaseAddress + Length) && - PageAddress < DriverBase + DriverSize) - { Protect = LdrLookupPageProtection(PageAddress, DriverBase, &PENtHeaders->FileHeader, PESectionHeaders); - MmSetPageProtect(NULL, PageAddress, Protect); - } - } +#if 1 + /* + * FIXME: + * This driver modifies a string in the first page of the text section while initialising. + */ + if (0 == _wcsicmp(L"fireport.sys", FileName->Buffer)) + { + Protect = PAGE_EXECUTE_READWRITE; + } +#endif + if (PageAddress < DriverBase + DriverSize) + { + MmSetPageProtect(NULL, PageAddress, Protect); + } - /* Insert module */ - KeAcquireSpinLock(&ModuleListLock, &Irql); - InsertTailList(&ModuleListHead, - &CreatedModuleObject->ListEntry); - KeReleaseSpinLock(&ModuleListLock, Irql); - - - ModuleTextSection = ExAllocatePool(NonPagedPool, - sizeof(MODULE_TEXT_SECTION)); - ASSERT(ModuleTextSection); - RtlZeroMemory(ModuleTextSection, sizeof(MODULE_TEXT_SECTION)); - ModuleTextSection->Base = (ULONG)DriverBase; - ModuleTextSection->Length = DriverSize; - ModuleTextSection->Name = ExAllocatePool(NonPagedPool, - (CreatedModuleObject->BaseName.Length + 1) * sizeof(WCHAR)); - RtlCopyMemory(ModuleTextSection->Name, - CreatedModuleObject->BaseName.Buffer, - CreatedModuleObject->BaseName.Length); - ModuleTextSection->Name[CreatedModuleObject->BaseName.Length / sizeof(WCHAR)] = 0; - ModuleTextSection->OptionalHeader = - CreatedModuleObject->Image.PE.OptionalHeader; - InsertTailList(&ModuleTextListHead, &ModuleTextSection->ListEntry); - - CreatedModuleObject->TextSection = ModuleTextSection; - - *ModuleObject = CreatedModuleObject; - - DPRINT("Loading Module %wZ...\n", FileName); - - if (KdDebuggerEnabled && (KdDebugState & KD_DEBUG_GDB)) - { - DPRINT("Module %wZ loaded at 0x%.08x.\n", - FileName, CreatedModuleObject->Base); + if (Characteristics & IMAGE_SCN_CNT_CODE) + { + if (Characteristics & IMAGE_SCN_MEM_WRITE) + { + Protect = PAGE_EXECUTE_READWRITE; + } + else + { + Protect = PAGE_EXECUTE_READ; + } + } + else if (Characteristics & (IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_UNINITIALIZED_DATA)) + { + Protect = PAGE_READWRITE; + } + else + { + Protect = PAGE_READONLY; + } + PageAddress = (PVOID)((ULONG_PTR)PageAddress + PAGE_SIZE); + while ((ULONG_PTR)PageAddress + PAGE_SIZE < (ULONG_PTR)BaseAddress + Length) + { + if (PageAddress < DriverBase + DriverSize) + { + MmSetPageProtect(NULL, PageAddress, Protect); + } + PageAddress = (PVOID)((ULONG_PTR)PageAddress + PAGE_SIZE); + } + if (PageAddress < (PVOID)((ULONG_PTR)BaseAddress + Length) && + PageAddress < DriverBase + DriverSize) + { + Protect = LdrLookupPageProtection(PageAddress, DriverBase, &PENtHeaders->FileHeader, PESectionHeaders); + MmSetPageProtect(NULL, PageAddress, Protect); + } } - return STATUS_SUCCESS; + /* Insert module */ + KeAcquireSpinLock(&ModuleListLock, &Irql); + InsertTailList(&ModuleListHead, + &CreatedModuleObject->ListEntry); + KeReleaseSpinLock(&ModuleListLock, Irql); + + + ModuleTextSection = ExAllocatePoolWithTag ( + NonPagedPool, + sizeof(MODULE_TEXT_SECTION), + TAG_MODULE_TEXT_SECTION ); + ASSERT(ModuleTextSection); + RtlZeroMemory(ModuleTextSection, sizeof(MODULE_TEXT_SECTION)); + ModuleTextSection->Base = (ULONG)DriverBase; + ModuleTextSection->Length = DriverSize; + ModuleTextSection->Name = ExAllocatePoolWithTag ( + NonPagedPool, + (CreatedModuleObject->BaseName.Length + 1) * sizeof(WCHAR), + TAG_LDR_WSTR ); + RtlCopyMemory(ModuleTextSection->Name, + CreatedModuleObject->BaseName.Buffer, + CreatedModuleObject->BaseName.Length); + ModuleTextSection->Name[CreatedModuleObject->BaseName.Length / sizeof(WCHAR)] = 0; + ModuleTextSection->OptionalHeader = + CreatedModuleObject->Image.PE.OptionalHeader; + InsertTailList(&ModuleTextListHead, &ModuleTextSection->ListEntry); + + CreatedModuleObject->TextSection = ModuleTextSection; + + *ModuleObject = CreatedModuleObject; + + DPRINT("Loading Module %wZ...\n", FileName); + + if (KdDebuggerEnabled && (KdDebugState & KD_DEBUG_GDB)) + { + DPRINT("Module %wZ loaded at 0x%.08x.\n", + FileName, CreatedModuleObject->Base); + } + + return STATUS_SUCCESS; } PVOID INIT_FUNCTION -LdrSafePEProcessModule(PVOID ModuleLoadBase, - PVOID DriverBase, - PVOID ImportModuleBase, - PULONG DriverSize) +LdrSafePEProcessModule ( + PVOID ModuleLoadBase, + PVOID DriverBase, + PVOID ImportModuleBase, + PULONG DriverSize) { - unsigned int Idx; - ULONG CurrentSize; - PIMAGE_DOS_HEADER PEDosHeader; - PIMAGE_NT_HEADERS PENtHeaders; - PIMAGE_SECTION_HEADER PESectionHeaders; - NTSTATUS Status; + unsigned int Idx; + ULONG CurrentSize; + PIMAGE_DOS_HEADER PEDosHeader; + PIMAGE_NT_HEADERS PENtHeaders; + PIMAGE_SECTION_HEADER PESectionHeaders; + NTSTATUS Status; - ps("Processing PE Module at module base:%08lx\n", ModuleLoadBase); + ps("Processing PE Module at module base:%08lx\n", ModuleLoadBase); - /* Get header pointers */ - PEDosHeader = (PIMAGE_DOS_HEADER) ModuleLoadBase; - PENtHeaders = RtlImageNtHeader(ModuleLoadBase); - PESectionHeaders = IMAGE_FIRST_SECTION(PENtHeaders); - CHECKPOINT; + /* Get header pointers */ + PEDosHeader = (PIMAGE_DOS_HEADER) ModuleLoadBase; + PENtHeaders = RtlImageNtHeader(ModuleLoadBase); + PESectionHeaders = IMAGE_FIRST_SECTION(PENtHeaders); + CHECKPOINT; - /* Check file magic numbers */ - if (PEDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + /* Check file magic numbers */ + if (PEDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { - return NULL; + return NULL; } - if (PEDosHeader->e_lfanew == 0) + if (PEDosHeader->e_lfanew == 0) { - return NULL; + return NULL; } - if (PENtHeaders->Signature != IMAGE_NT_SIGNATURE) + if (PENtHeaders->Signature != IMAGE_NT_SIGNATURE) { - return NULL; + return NULL; } - if (PENtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) + if (PENtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) { - return NULL; + return NULL; } - ps("OptionalHdrMagic:%04x LinkVersion:%d.%d\n", - PENtHeaders->OptionalHeader.Magic, - PENtHeaders->OptionalHeader.MajorLinkerVersion, - PENtHeaders->OptionalHeader.MinorLinkerVersion); - ps("Entry Point:%08lx\n", PENtHeaders->OptionalHeader.AddressOfEntryPoint); + ps("OptionalHdrMagic:%04x LinkVersion:%d.%d\n", + PENtHeaders->OptionalHeader.Magic, + PENtHeaders->OptionalHeader.MajorLinkerVersion, + PENtHeaders->OptionalHeader.MinorLinkerVersion); + ps("Entry Point:%08lx\n", PENtHeaders->OptionalHeader.AddressOfEntryPoint); - /* Determine the size of the module */ - *DriverSize = PENtHeaders->OptionalHeader.SizeOfImage; - ps("DriverSize %x\n",*DriverSize); + /* Determine the size of the module */ + *DriverSize = PENtHeaders->OptionalHeader.SizeOfImage; + ps("DriverSize %x\n",*DriverSize); - /* Copy headers over */ - if (DriverBase != ModuleLoadBase) + /* Copy headers over */ + if (DriverBase != ModuleLoadBase) { - memcpy(DriverBase, ModuleLoadBase, PENtHeaders->OptionalHeader.SizeOfHeaders); + memcpy(DriverBase, ModuleLoadBase, PENtHeaders->OptionalHeader.SizeOfHeaders); } - ps("Hdr: 0x%X\n", PENtHeaders); - ps("Hdr->SizeOfHeaders: 0x%X\n", PENtHeaders->OptionalHeader.SizeOfHeaders); - ps("FileHdr->NumberOfSections: 0x%X\n", PENtHeaders->FileHeader.NumberOfSections); + ps("Hdr: 0x%X\n", PENtHeaders); + ps("Hdr->SizeOfHeaders: 0x%X\n", PENtHeaders->OptionalHeader.SizeOfHeaders); + ps("FileHdr->NumberOfSections: 0x%X\n", PENtHeaders->FileHeader.NumberOfSections); - /* Ntoskrnl.exe need no relocation fixups since it is linked to run at the same - address as it is mapped */ - if (DriverBase != ModuleLoadBase) + /* Ntoskrnl.exe need no relocation fixups since it is linked to run at the same + address as it is mapped */ + if (DriverBase != ModuleLoadBase) { - CurrentSize = 0; + CurrentSize = 0; - /* Copy image sections into virtual section */ - for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) + /* Copy image sections into virtual section */ + for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) { - PIMAGE_SECTION_HEADER Section = &PESectionHeaders[Idx]; - // Copy current section into current offset of virtual section - if (Section->SizeOfRawData) + PIMAGE_SECTION_HEADER Section = &PESectionHeaders[Idx]; + // Copy current section into current offset of virtual section + if (Section->SizeOfRawData) { -// ps("PESectionHeaders[Idx].VirtualAddress (%X) + DriverBase %x\n", -// PESectionHeaders[Idx].VirtualAddress, PESectionHeaders[Idx].VirtualAddress + DriverBase); - memcpy(Section->VirtualAddress + (char*)DriverBase, - Section->PointerToRawData + (char*)ModuleLoadBase, - Section->Misc.VirtualSize > Section->SizeOfRawData ? Section->SizeOfRawData : Section->Misc.VirtualSize); + // ps("PESectionHeaders[Idx].VirtualAddress (%X) + DriverBase %x\n", + // PESectionHeaders[Idx].VirtualAddress, PESectionHeaders[Idx].VirtualAddress + DriverBase); + memcpy(Section->VirtualAddress + (char*)DriverBase, + Section->PointerToRawData + (char*)ModuleLoadBase, + Section->Misc.VirtualSize > Section->SizeOfRawData ? Section->SizeOfRawData : Section->Misc.VirtualSize); } - if (Section->SizeOfRawData < Section->Misc.VirtualSize) + if (Section->SizeOfRawData < Section->Misc.VirtualSize) { - memset(Section->VirtualAddress + Section->SizeOfRawData + (char*)DriverBase, - 0, - Section->Misc.VirtualSize - Section->SizeOfRawData); + memset(Section->VirtualAddress + Section->SizeOfRawData + (char*)DriverBase, + 0, + Section->Misc.VirtualSize - Section->SizeOfRawData); } - CurrentSize += ROUND_UP(Section->Misc.VirtualSize, - PENtHeaders->OptionalHeader.SectionAlignment); + CurrentSize += ROUND_UP(Section->Misc.VirtualSize, + PENtHeaders->OptionalHeader.SectionAlignment); } - /* Perform relocation fixups */ - Status = LdrPEPerformRelocations(DriverBase, *DriverSize); - if (!NT_SUCCESS(Status)) - { - return NULL; - } - } - - /* Perform import fixups */ - Status = LdrPEFixupImports(DriverBase == ModuleLoadBase ? &NtoskrnlModuleObject : &HalModuleObject); - if (!NT_SUCCESS(Status)) - { - return NULL; - } - - /* Set the page protection for the virtual sections */ - MmSetPageProtect(NULL, DriverBase, PAGE_READONLY); - for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) - { - ULONG Characteristics = PESectionHeaders[Idx].Characteristics; - ULONG Length; - PVOID BaseAddress; - PVOID PageAddress; - ULONG Protect; - Length = PESectionHeaders[Idx].Misc.VirtualSize; - BaseAddress = PESectionHeaders[Idx].VirtualAddress + (char*)DriverBase; - PageAddress = (PVOID)PAGE_ROUND_DOWN(BaseAddress); - - if (Characteristics & IMAGE_SCN_MEM_EXECUTE) - { - if (Characteristics & IMAGE_SCN_MEM_WRITE) - { - Protect = PAGE_EXECUTE_READWRITE; - } - else - { - Protect = PAGE_EXECUTE_READ; - } - } - else if (Characteristics & IMAGE_SCN_MEM_WRITE) - { - Protect = PAGE_READWRITE; - } - else - { - Protect = PAGE_READONLY; - } - while ((ULONG_PTR)PageAddress < (ULONG_PTR)BaseAddress + Length) - { - MmSetPageProtect(NULL, PageAddress, Protect); - PageAddress = (PVOID)((ULONG_PTR)PageAddress + PAGE_SIZE); - } - if (DriverBase == ModuleLoadBase && - Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) - { - /* For ntoskrnl, we must stop after the bss section */ - break; - } - - } - - return DriverBase; -} - -static PVOID -LdrPEFixupForward(PCHAR ForwardName) -{ - CHAR NameBuffer[128]; - UNICODE_STRING ModuleName; - PCHAR p; - PMODULE_OBJECT ModuleObject; - - DPRINT("LdrPEFixupForward (%s)\n", ForwardName); - - strcpy(NameBuffer, ForwardName); - p = strchr(NameBuffer, '.'); - if (p == NULL) - { - return NULL; - } - - *p = 0; - - DPRINT("Driver: %s Function: %s\n", NameBuffer, p+1); - - RtlCreateUnicodeStringFromAsciiz(&ModuleName, - NameBuffer); - ModuleObject = LdrGetModuleObject(&ModuleName); - RtlFreeUnicodeString(&ModuleName); - - DPRINT("ModuleObject: %p\n", ModuleObject); - - if (ModuleObject == NULL) - { - CPRINT("LdrPEFixupForward: failed to find module %s\n", NameBuffer); - return NULL; - } - return LdrPEGetExportByName(ModuleObject->Base, (PUCHAR)(p+1), 0xffff); -} - -static NTSTATUS -LdrPEPerformRelocations(PVOID DriverBase, - ULONG DriverSize) -{ - PIMAGE_NT_HEADERS NtHeaders; - PIMAGE_DATA_DIRECTORY RelocationDDir; - PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd; - ULONG Count, i; - PVOID Address, MaxAddress; - PUSHORT TypeOffset; - ULONG_PTR Delta; - SHORT Offset; - USHORT Type; - PUSHORT ShortPtr; - PULONG LongPtr; - - NtHeaders = RtlImageNtHeader(DriverBase); - - if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) - { - return STATUS_UNSUCCESSFUL; - } - - RelocationDDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; - - if (RelocationDDir->VirtualAddress == 0 || RelocationDDir->Size == 0) - { - return STATUS_SUCCESS; - } - - Delta = (ULONG_PTR)DriverBase - NtHeaders->OptionalHeader.ImageBase; - RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)DriverBase + RelocationDDir->VirtualAddress); - RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + RelocationDDir->Size); - MaxAddress = DriverBase + DriverSize; - - while (RelocationDir < RelocationEnd && - RelocationDir->SizeOfBlock > 0) - { - Count = (RelocationDir->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); - Address = DriverBase + RelocationDir->VirtualAddress; - TypeOffset = (PUSHORT)(RelocationDir + 1); - - for (i = 0; i < Count; i++) - { - Offset = *TypeOffset & 0xFFF; - Type = *TypeOffset >> 12; - ShortPtr = (PUSHORT)(Address + Offset); - - /* Don't relocate after the end of the loaded driver */ - if ((PVOID)ShortPtr >= MaxAddress) - { - break; - } - - /* - * Don't relocate within the relocation section itself. - * GCC/LD generates sometimes relocation records for the relecotion section. - * This is a bug in GCC/LD. - */ - if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir || - (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd) - { - switch (Type) - { - case IMAGE_REL_BASED_ABSOLUTE: - break; - - case IMAGE_REL_BASED_HIGH: - *ShortPtr += HIWORD(Delta); - break; - - case IMAGE_REL_BASED_LOW: - *ShortPtr += LOWORD(Delta); - break; - - case IMAGE_REL_BASED_HIGHLOW: - LongPtr = (PULONG)ShortPtr; - *LongPtr += Delta; - break; - - case IMAGE_REL_BASED_HIGHADJ: - case IMAGE_REL_BASED_MIPS_JMPADDR: - default: - DPRINT1("Unknown/unsupported fixup type %hu.\n", Type); - DPRINT1("Address %x, Current %d, Count %d, *TypeOffset %x\n", Address, i, Count, *TypeOffset); - return STATUS_UNSUCCESSFUL; - } - } - TypeOffset++; - } - RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + RelocationDir->SizeOfBlock); - } - - return STATUS_SUCCESS; -} - -static NTSTATUS -LdrPEGetOrLoadModule(PMODULE_OBJECT Module, - PCHAR ImportedName, - PMODULE_OBJECT* ImportedModule) -{ - UNICODE_STRING DriverName; - UNICODE_STRING NameString; - WCHAR NameBuffer[PATH_MAX]; - NTSTATUS Status = STATUS_SUCCESS; - - if (0 == _stricmp(ImportedName, "ntoskrnl") || - 0 == _stricmp(ImportedName, "ntoskrnl.exe")) - { - *ImportedModule = &NtoskrnlModuleObject; - return STATUS_SUCCESS; - } - - if (0 == _stricmp(ImportedName, "hal") || - 0 == _stricmp(ImportedName, "hal.dll")) - { - *ImportedModule = &HalModuleObject; - return STATUS_SUCCESS; - } - - RtlCreateUnicodeStringFromAsciiz (&DriverName, ImportedName); - DPRINT("Import module: %wZ\n", &DriverName); - - *ImportedModule = LdrGetModuleObject(&DriverName); - if (*ImportedModule == NULL) - { - PWCHAR PathEnd; - ULONG PathLength; - - PathEnd = wcsrchr(Module->FullName.Buffer, L'\\'); - if (NULL != PathEnd) - { - PathLength = (PathEnd - Module->FullName.Buffer + 1) * sizeof(WCHAR); - RtlCopyMemory(NameBuffer, Module->FullName.Buffer, PathLength); - RtlCopyMemory(NameBuffer + (PathLength / sizeof(WCHAR)), DriverName.Buffer, DriverName.Length); - NameString.Buffer = NameBuffer; - NameString.MaximumLength = NameString.Length = PathLength + DriverName.Length; - - /* NULL-terminate */ - NameString.MaximumLength++; - NameBuffer[NameString.Length / sizeof(WCHAR)] = 0; - - Status = LdrLoadModule(&NameString, ImportedModule); - } - else - { - DPRINT("Module '%wZ' not loaded yet\n", &DriverName); - wcscpy(NameBuffer, L"\\SystemRoot\\system32\\drivers\\"); - wcsncat(NameBuffer, DriverName.Buffer, DriverName.Length / sizeof(WCHAR)); - RtlInitUnicodeString(&NameString, NameBuffer); - Status = LdrLoadModule(&NameString, ImportedModule); - } - if (!NT_SUCCESS(Status)) - { - wcscpy(NameBuffer, L"\\SystemRoot\\system32\\"); - wcsncat(NameBuffer, DriverName.Buffer, DriverName.Length / sizeof(WCHAR)); - RtlInitUnicodeString(&NameString, NameBuffer); - Status = LdrLoadModule(&NameString, ImportedModule); + /* Perform relocation fixups */ + Status = LdrPEPerformRelocations(DriverBase, *DriverSize); if (!NT_SUCCESS(Status)) { - DPRINT1("Unknown import module: %wZ (Status %lx)\n", &DriverName, Status); + return NULL; } - } - } - RtlFreeUnicodeString(&DriverName); - return Status; + } + + /* Perform import fixups */ + Status = LdrPEFixupImports(DriverBase == ModuleLoadBase ? &NtoskrnlModuleObject : &HalModuleObject); + if (!NT_SUCCESS(Status)) + { + return NULL; + } + + /* Set the page protection for the virtual sections */ + MmSetPageProtect(NULL, DriverBase, PAGE_READONLY); + for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++) + { + ULONG Characteristics = PESectionHeaders[Idx].Characteristics; + ULONG Length; + PVOID BaseAddress; + PVOID PageAddress; + ULONG Protect; + Length = PESectionHeaders[Idx].Misc.VirtualSize; + BaseAddress = PESectionHeaders[Idx].VirtualAddress + (char*)DriverBase; + PageAddress = (PVOID)PAGE_ROUND_DOWN(BaseAddress); + + if (Characteristics & IMAGE_SCN_MEM_EXECUTE) + { + if (Characteristics & IMAGE_SCN_MEM_WRITE) + { + Protect = PAGE_EXECUTE_READWRITE; + } + else + { + Protect = PAGE_EXECUTE_READ; + } + } + else if (Characteristics & IMAGE_SCN_MEM_WRITE) + { + Protect = PAGE_READWRITE; + } + else + { + Protect = PAGE_READONLY; + } + while ((ULONG_PTR)PageAddress < (ULONG_PTR)BaseAddress + Length) + { + MmSetPageProtect(NULL, PageAddress, Protect); + PageAddress = (PVOID)((ULONG_PTR)PageAddress + PAGE_SIZE); + } + if (DriverBase == ModuleLoadBase && + Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) + { + /* For ntoskrnl, we must stop after the bss section */ + break; + } + + } + + return DriverBase; } static PVOID -LdrPEGetExportByName(PVOID BaseAddress, - PUCHAR SymbolName, - WORD Hint) +LdrPEFixupForward ( PCHAR ForwardName ) { - PIMAGE_EXPORT_DIRECTORY ExportDir; - PDWORD * ExFunctions; - PDWORD * ExNames; - USHORT * ExOrdinals; - ULONG i; - PVOID ExName; - ULONG Ordinal; - PVOID Function; - LONG minn, maxn; - ULONG ExportDirSize; + CHAR NameBuffer[128]; + UNICODE_STRING ModuleName; + PCHAR p; + PMODULE_OBJECT ModuleObject; - DPRINT("LdrPEGetExportByName %x %s %hu\n", BaseAddress, SymbolName, Hint); + DPRINT("LdrPEFixupForward (%s)\n", ForwardName); - ExportDir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(BaseAddress, - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &ExportDirSize); - if (ExportDir == NULL) - { - DPRINT1("LdrPEGetExportByName(): no export directory!\n"); - return NULL; - } + strcpy(NameBuffer, ForwardName); + p = strchr(NameBuffer, '.'); + if (p == NULL) + { + return NULL; + } + + *p = 0; + + DPRINT("Driver: %s Function: %s\n", NameBuffer, p+1); + + RtlCreateUnicodeStringFromAsciiz(&ModuleName, + NameBuffer); + ModuleObject = LdrGetModuleObject(&ModuleName); + RtlFreeUnicodeString(&ModuleName); + + DPRINT("ModuleObject: %p\n", ModuleObject); + + if (ModuleObject == NULL) + { + CPRINT("LdrPEFixupForward: failed to find module %s\n", NameBuffer); + return NULL; + } + return LdrPEGetExportByName(ModuleObject->Base, (PUCHAR)(p+1), 0xffff); +} + +static NTSTATUS +LdrPEPerformRelocations ( + PVOID DriverBase, + ULONG DriverSize) +{ + PIMAGE_NT_HEADERS NtHeaders; + PIMAGE_DATA_DIRECTORY RelocationDDir; + PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd; + ULONG Count, i; + PVOID Address, MaxAddress; + PUSHORT TypeOffset; + ULONG_PTR Delta; + SHORT Offset; + USHORT Type; + PUSHORT ShortPtr; + PULONG LongPtr; + + NtHeaders = RtlImageNtHeader(DriverBase); + + if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) + { + return STATUS_UNSUCCESSFUL; + } + + RelocationDDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + + if (RelocationDDir->VirtualAddress == 0 || RelocationDDir->Size == 0) + { + return STATUS_SUCCESS; + } + + Delta = (ULONG_PTR)DriverBase - NtHeaders->OptionalHeader.ImageBase; + RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)DriverBase + RelocationDDir->VirtualAddress); + RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + RelocationDDir->Size); + MaxAddress = DriverBase + DriverSize; + + while (RelocationDir < RelocationEnd && + RelocationDir->SizeOfBlock > 0) + { + Count = (RelocationDir->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); + Address = DriverBase + RelocationDir->VirtualAddress; + TypeOffset = (PUSHORT)(RelocationDir + 1); + + for (i = 0; i < Count; i++) + { + Offset = *TypeOffset & 0xFFF; + Type = *TypeOffset >> 12; + ShortPtr = (PUSHORT)(Address + Offset); + + /* Don't relocate after the end of the loaded driver */ + if ((PVOID)ShortPtr >= MaxAddress) + { + break; + } + + /* + * Don't relocate within the relocation section itself. + * GCC/LD generates sometimes relocation records for the relecotion section. + * This is a bug in GCC/LD. + */ + if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir || + (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd) + { + switch (Type) + { + case IMAGE_REL_BASED_ABSOLUTE: + break; + + case IMAGE_REL_BASED_HIGH: + *ShortPtr += HIWORD(Delta); + break; + + case IMAGE_REL_BASED_LOW: + *ShortPtr += LOWORD(Delta); + break; + + case IMAGE_REL_BASED_HIGHLOW: + LongPtr = (PULONG)ShortPtr; + *LongPtr += Delta; + break; + + case IMAGE_REL_BASED_HIGHADJ: + case IMAGE_REL_BASED_MIPS_JMPADDR: + default: + DPRINT1("Unknown/unsupported fixup type %hu.\n", Type); + DPRINT1("Address %x, Current %d, Count %d, *TypeOffset %x\n", Address, i, Count, *TypeOffset); + return STATUS_UNSUCCESSFUL; + } + } + TypeOffset++; + } + RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + RelocationDir->SizeOfBlock); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS +LdrPEGetOrLoadModule ( + PMODULE_OBJECT Module, + PCHAR ImportedName, + PMODULE_OBJECT* ImportedModule) +{ + UNICODE_STRING DriverName; + UNICODE_STRING NameString; + WCHAR NameBuffer[PATH_MAX]; + NTSTATUS Status = STATUS_SUCCESS; + + if (0 == _stricmp(ImportedName, "ntoskrnl") || + 0 == _stricmp(ImportedName, "ntoskrnl.exe")) + { + *ImportedModule = &NtoskrnlModuleObject; + return STATUS_SUCCESS; + } + + if (0 == _stricmp(ImportedName, "hal") || + 0 == _stricmp(ImportedName, "hal.dll")) + { + *ImportedModule = &HalModuleObject; + return STATUS_SUCCESS; + } + + RtlCreateUnicodeStringFromAsciiz (&DriverName, ImportedName); + DPRINT("Import module: %wZ\n", &DriverName); + + *ImportedModule = LdrGetModuleObject(&DriverName); + if (*ImportedModule == NULL) + { + PWCHAR PathEnd; + ULONG PathLength; + + PathEnd = wcsrchr(Module->FullName.Buffer, L'\\'); + if (NULL != PathEnd) + { + PathLength = (PathEnd - Module->FullName.Buffer + 1) * sizeof(WCHAR); + RtlCopyMemory(NameBuffer, Module->FullName.Buffer, PathLength); + RtlCopyMemory(NameBuffer + (PathLength / sizeof(WCHAR)), DriverName.Buffer, DriverName.Length); + NameString.Buffer = NameBuffer; + NameString.MaximumLength = NameString.Length = PathLength + DriverName.Length; + + /* NULL-terminate */ + NameString.MaximumLength++; + NameBuffer[NameString.Length / sizeof(WCHAR)] = 0; + + Status = LdrLoadModule(&NameString, ImportedModule); + } + else + { + DPRINT("Module '%wZ' not loaded yet\n", &DriverName); + wcscpy(NameBuffer, L"\\SystemRoot\\system32\\drivers\\"); + wcsncat(NameBuffer, DriverName.Buffer, DriverName.Length / sizeof(WCHAR)); + RtlInitUnicodeString(&NameString, NameBuffer); + Status = LdrLoadModule(&NameString, ImportedModule); + } + if (!NT_SUCCESS(Status)) + { + wcscpy(NameBuffer, L"\\SystemRoot\\system32\\"); + wcsncat(NameBuffer, DriverName.Buffer, DriverName.Length / sizeof(WCHAR)); + RtlInitUnicodeString(&NameString, NameBuffer); + Status = LdrLoadModule(&NameString, ImportedModule); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Unknown import module: %wZ (Status %lx)\n", &DriverName, Status); + } + } + } + RtlFreeUnicodeString(&DriverName); + return Status; +} + +static PVOID +LdrPEGetExportByName ( + PVOID BaseAddress, + PUCHAR SymbolName, + WORD Hint ) +{ + PIMAGE_EXPORT_DIRECTORY ExportDir; + PDWORD * ExFunctions; + PDWORD * ExNames; + USHORT * ExOrdinals; + ULONG i; + PVOID ExName; + ULONG Ordinal; + PVOID Function; + LONG minn, maxn; + ULONG ExportDirSize; + + DPRINT("LdrPEGetExportByName %x %s %hu\n", BaseAddress, SymbolName, Hint); + + ExportDir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(BaseAddress, + TRUE, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &ExportDirSize); + if (ExportDir == NULL) + { + DPRINT1("LdrPEGetExportByName(): no export directory!\n"); + return NULL; + } - /* The symbol names may be missing entirely */ - if (ExportDir->AddressOfNames == 0) - { - DPRINT("LdrPEGetExportByName(): symbol names missing entirely\n"); - return NULL; - } + /* The symbol names may be missing entirely */ + if (ExportDir->AddressOfNames == 0) + { + DPRINT("LdrPEGetExportByName(): symbol names missing entirely\n"); + return NULL; + } - /* + /* * Get header pointers */ - ExNames = (PDWORD *)RVA(BaseAddress, ExportDir->AddressOfNames); - ExOrdinals = (USHORT *)RVA(BaseAddress, ExportDir->AddressOfNameOrdinals); - ExFunctions = (PDWORD *)RVA(BaseAddress, ExportDir->AddressOfFunctions); + ExNames = (PDWORD *)RVA(BaseAddress, ExportDir->AddressOfNames); + ExOrdinals = (USHORT *)RVA(BaseAddress, ExportDir->AddressOfNameOrdinals); + ExFunctions = (PDWORD *)RVA(BaseAddress, ExportDir->AddressOfFunctions); - /* + /* * Check the hint first */ - if (Hint < ExportDir->NumberOfNames) - { - ExName = RVA(BaseAddress, ExNames[Hint]); - if (strcmp(ExName, (PCHAR)SymbolName) == 0) - { - Ordinal = ExOrdinals[Hint]; - Function = RVA(BaseAddress, ExFunctions[Ordinal]); - if ((ULONG_PTR)Function >= (ULONG_PTR)ExportDir && - (ULONG_PTR)Function < (ULONG_PTR)ExportDir + ExportDirSize) - { - DPRINT("Forward: %s\n", (PCHAR)Function); - Function = LdrPEFixupForward((PCHAR)Function); - if (Function == NULL) + if (Hint < ExportDir->NumberOfNames) + { + ExName = RVA(BaseAddress, ExNames[Hint]); + if (strcmp(ExName, (PCHAR)SymbolName) == 0) + { + Ordinal = ExOrdinals[Hint]; + Function = RVA(BaseAddress, ExFunctions[Ordinal]); + if ((ULONG_PTR)Function >= (ULONG_PTR)ExportDir && + (ULONG_PTR)Function < (ULONG_PTR)ExportDir + ExportDirSize) { - DPRINT1("LdrPEGetExportByName(): failed to find %s\n",SymbolName); + DPRINT("Forward: %s\n", (PCHAR)Function); + Function = LdrPEFixupForward((PCHAR)Function); + if (Function == NULL) + { + DPRINT1("LdrPEGetExportByName(): failed to find %s\n",SymbolName); + } + return Function; } - return Function; - } - if (Function != NULL) - { - return Function; - } - } - } + if (Function != NULL) + { + return Function; + } + } + } - /* + /* * Try a binary search first */ - minn = 0; - maxn = ExportDir->NumberOfNames - 1; - while (minn <= maxn) - { - LONG mid; - LONG res; + minn = 0; + maxn = ExportDir->NumberOfNames - 1; + while (minn <= maxn) + { + LONG mid; + LONG res; - mid = (minn + maxn) / 2; + mid = (minn + maxn) / 2; - ExName = RVA(BaseAddress, ExNames[mid]); - res = strcmp(ExName, (PCHAR)SymbolName); - if (res == 0) - { - Ordinal = ExOrdinals[mid]; - Function = RVA(BaseAddress, ExFunctions[Ordinal]); - if ((ULONG_PTR)Function >= (ULONG_PTR)ExportDir && - (ULONG_PTR)Function < (ULONG_PTR)ExportDir + ExportDirSize) - { - DPRINT("Forward: %s\n", (PCHAR)Function); - Function = LdrPEFixupForward((PCHAR)Function); - if (Function == NULL) + ExName = RVA(BaseAddress, ExNames[mid]); + res = strcmp(ExName, (PCHAR)SymbolName); + if (res == 0) + { + Ordinal = ExOrdinals[mid]; + Function = RVA(BaseAddress, ExFunctions[Ordinal]); + if ((ULONG_PTR)Function >= (ULONG_PTR)ExportDir && + (ULONG_PTR)Function < (ULONG_PTR)ExportDir + ExportDirSize) { - DPRINT1("LdrPEGetExportByName(): failed to find %s\n",SymbolName); + DPRINT("Forward: %s\n", (PCHAR)Function); + Function = LdrPEFixupForward((PCHAR)Function); + if (Function == NULL) + { + DPRINT1("LdrPEGetExportByName(): failed to find %s\n",SymbolName); + } + return Function; } - return Function; - } - if (Function != NULL) - { - return Function; - } - } - else if (minn == maxn) - { - DPRINT("LdrPEGetExportByName(): binary search failed\n"); - break; - } - else if (res > 0) - { - maxn = mid - 1; - } - else - { - minn = mid + 1; - } - } + if (Function != NULL) + { + return Function; + } + } + else if (minn == maxn) + { + DPRINT("LdrPEGetExportByName(): binary search failed\n"); + break; + } + else if (res > 0) + { + maxn = mid - 1; + } + else + { + minn = mid + 1; + } + } - /* + /* * Fall back on a linear search */ - DPRINT("LdrPEGetExportByName(): Falling back on a linear search of export table\n"); - for (i = 0; i < ExportDir->NumberOfNames; i++) - { - ExName = RVA(BaseAddress, ExNames[i]); - if (strcmp(ExName, (PCHAR)SymbolName) == 0) - { - Ordinal = ExOrdinals[i]; - Function = RVA(BaseAddress, ExFunctions[Ordinal]); - DPRINT("%x %x %x\n", Function, ExportDir, ExportDir + ExportDirSize); - if ((ULONG_PTR)Function >= (ULONG_PTR)ExportDir && - (ULONG_PTR)Function < (ULONG_PTR)ExportDir + ExportDirSize) - { - DPRINT("Forward: %s\n", (PCHAR)Function); - Function = LdrPEFixupForward((PCHAR)Function); - } - if (Function == NULL) - { - break; - } - return Function; - } - } - DPRINT1("LdrPEGetExportByName(): failed to find %s\n",SymbolName); - return (PVOID)NULL; + DPRINT("LdrPEGetExportByName(): Falling back on a linear search of export table\n"); + for (i = 0; i < ExportDir->NumberOfNames; i++) + { + ExName = RVA(BaseAddress, ExNames[i]); + if (strcmp(ExName, (PCHAR)SymbolName) == 0) + { + Ordinal = ExOrdinals[i]; + Function = RVA(BaseAddress, ExFunctions[Ordinal]); + DPRINT("%x %x %x\n", Function, ExportDir, ExportDir + ExportDirSize); + if ((ULONG_PTR)Function >= (ULONG_PTR)ExportDir && + (ULONG_PTR)Function < (ULONG_PTR)ExportDir + ExportDirSize) + { + DPRINT("Forward: %s\n", (PCHAR)Function); + Function = LdrPEFixupForward((PCHAR)Function); + } + if (Function == NULL) + { + break; + } + return Function; + } + } + DPRINT1("LdrPEGetExportByName(): failed to find %s\n",SymbolName); + return (PVOID)NULL; } static PVOID -LdrPEGetExportByOrdinal (PVOID BaseAddress, - ULONG Ordinal) +LdrPEGetExportByOrdinal ( + PVOID BaseAddress, + ULONG Ordinal ) { - PIMAGE_EXPORT_DIRECTORY ExportDir; - ULONG ExportDirSize; - PDWORD * ExFunctions; - PVOID Function; + PIMAGE_EXPORT_DIRECTORY ExportDir; + ULONG ExportDirSize; + PDWORD * ExFunctions; + PVOID Function; - ExportDir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData (BaseAddress, - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &ExportDirSize); + ExportDir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData ( + BaseAddress, + TRUE, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &ExportDirSize); - ExFunctions = (PDWORD *)RVA(BaseAddress, - ExportDir->AddressOfFunctions); - DPRINT("LdrPEGetExportByOrdinal(Ordinal %d) = %x\n", - Ordinal, - RVA(BaseAddress, ExFunctions[Ordinal - ExportDir->Base])); + ExFunctions = (PDWORD *)RVA(BaseAddress, + ExportDir->AddressOfFunctions); + DPRINT("LdrPEGetExportByOrdinal(Ordinal %d) = %x\n", + Ordinal, + RVA(BaseAddress, ExFunctions[Ordinal - ExportDir->Base])); - Function = 0 != ExFunctions[Ordinal - ExportDir->Base] - ? RVA(BaseAddress, ExFunctions[Ordinal - ExportDir->Base] ) - : NULL; + Function = 0 != ExFunctions[Ordinal - ExportDir->Base] + ? RVA(BaseAddress, ExFunctions[Ordinal - ExportDir->Base] ) + : NULL; - if (((ULONG)Function >= (ULONG)ExportDir) && - ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize)) - { - DPRINT("Forward: %s\n", (PCHAR)Function); - Function = LdrPEFixupForward((PCHAR)Function); - } + if (((ULONG)Function >= (ULONG)ExportDir) && + ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize)) + { + DPRINT("Forward: %s\n", (PCHAR)Function); + Function = LdrPEFixupForward((PCHAR)Function); + } - return Function; + return Function; } static NTSTATUS -LdrPEProcessImportDirectoryEntry(PVOID DriverBase, - PMODULE_OBJECT ImportedModule, - PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory) +LdrPEProcessImportDirectoryEntry( + PVOID DriverBase, + PMODULE_OBJECT ImportedModule, + PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory ) { - PVOID* ImportAddressList; - PULONG FunctionNameList; - ULONG Ordinal; + PVOID* ImportAddressList; + PULONG FunctionNameList; + ULONG Ordinal; - if (ImportModuleDirectory == NULL || ImportModuleDirectory->Name == 0) - { - return STATUS_UNSUCCESSFUL; - } + if (ImportModuleDirectory == NULL || ImportModuleDirectory->Name == 0) + { + return STATUS_UNSUCCESSFUL; + } - /* Get the import address list. */ - ImportAddressList = (PVOID*)(DriverBase + (ULONG_PTR)ImportModuleDirectory->FirstThunk); + /* Get the import address list. */ + ImportAddressList = (PVOID*)(DriverBase + (ULONG_PTR)ImportModuleDirectory->FirstThunk); - /* Get the list of functions to import. */ - if (ImportModuleDirectory->OriginalFirstThunk != 0) - { - FunctionNameList = (PULONG) (DriverBase + (ULONG_PTR)ImportModuleDirectory->OriginalFirstThunk); - } - else - { - FunctionNameList = (PULONG)(DriverBase + (ULONG_PTR)ImportModuleDirectory->FirstThunk); - } + /* Get the list of functions to import. */ + if (ImportModuleDirectory->OriginalFirstThunk != 0) + { + FunctionNameList = (PULONG) (DriverBase + (ULONG_PTR)ImportModuleDirectory->OriginalFirstThunk); + } + else + { + FunctionNameList = (PULONG)(DriverBase + (ULONG_PTR)ImportModuleDirectory->FirstThunk); + } - /* Walk through function list and fixup addresses. */ - while (*FunctionNameList != 0L) - { - if ((*FunctionNameList) & 0x80000000) - { - Ordinal = (*FunctionNameList) & 0x7fffffff; - *ImportAddressList = LdrPEGetExportByOrdinal(ImportedModule->Base, Ordinal); - if ((*ImportAddressList) == NULL) - { - DPRINT1("Failed to import #%ld from %wZ\n", Ordinal, &ImportedModule->FullName); - return STATUS_UNSUCCESSFUL; - } - } - else - { - IMAGE_IMPORT_BY_NAME *pe_name; - pe_name = RVA(DriverBase, *FunctionNameList); - *ImportAddressList = LdrPEGetExportByName(ImportedModule->Base, pe_name->Name, pe_name->Hint); - if ((*ImportAddressList) == NULL) - { - DPRINT1("Failed to import %s from %wZ\n", pe_name->Name, &ImportedModule->FullName); - return STATUS_UNSUCCESSFUL; - } - } - ImportAddressList++; - FunctionNameList++; - } - return STATUS_SUCCESS; + /* Walk through function list and fixup addresses. */ + while (*FunctionNameList != 0L) + { + if ((*FunctionNameList) & 0x80000000) + { + Ordinal = (*FunctionNameList) & 0x7fffffff; + *ImportAddressList = LdrPEGetExportByOrdinal(ImportedModule->Base, Ordinal); + if ((*ImportAddressList) == NULL) + { + DPRINT1("Failed to import #%ld from %wZ\n", Ordinal, &ImportedModule->FullName); + return STATUS_UNSUCCESSFUL; + } + } + else + { + IMAGE_IMPORT_BY_NAME *pe_name; + pe_name = RVA(DriverBase, *FunctionNameList); + *ImportAddressList = LdrPEGetExportByName(ImportedModule->Base, pe_name->Name, pe_name->Hint); + if ((*ImportAddressList) == NULL) + { + DPRINT1("Failed to import %s from %wZ\n", pe_name->Name, &ImportedModule->FullName); + return STATUS_UNSUCCESSFUL; + } + } + ImportAddressList++; + FunctionNameList++; + } + return STATUS_SUCCESS; } static NTSTATUS -LdrPEFixupImports(PMODULE_OBJECT Module) +LdrPEFixupImports ( PMODULE_OBJECT Module ) { - PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory; - PCHAR ImportedName; - PMODULE_OBJECT ImportedModule; - NTSTATUS Status; + PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory; + PCHAR ImportedName; + PMODULE_OBJECT ImportedModule; + NTSTATUS Status; - /* Process each import module */ - ImportModuleDirectory = (PIMAGE_IMPORT_DESCRIPTOR) - RtlImageDirectoryEntryToData(Module->Base, - TRUE, - IMAGE_DIRECTORY_ENTRY_IMPORT, - NULL); - DPRINT("Processeing import directory at %p\n", ImportModuleDirectory); - while (ImportModuleDirectory->Name) - { - if (Module->Length <= ImportModuleDirectory->Name) - { - DPRINT1("Invalid import directory in %wZ\n", &Module->FullName); - return STATUS_SECTION_NOT_IMAGE; - } + /* Process each import module */ + ImportModuleDirectory = (PIMAGE_IMPORT_DESCRIPTOR) + RtlImageDirectoryEntryToData(Module->Base, + TRUE, + IMAGE_DIRECTORY_ENTRY_IMPORT, + NULL); + DPRINT("Processeing import directory at %p\n", ImportModuleDirectory); + while (ImportModuleDirectory->Name) + { + if (Module->Length <= ImportModuleDirectory->Name) + { + DPRINT1("Invalid import directory in %wZ\n", &Module->FullName); + return STATUS_SECTION_NOT_IMAGE; + } - /* Check to make sure that import lib is kernel */ - ImportedName = (PCHAR) Module->Base + ImportModuleDirectory->Name; + /* Check to make sure that import lib is kernel */ + ImportedName = (PCHAR) Module->Base + ImportModuleDirectory->Name; - Status = LdrPEGetOrLoadModule(Module, ImportedName, &ImportedModule); - if (!NT_SUCCESS(Status)) - { - return Status; - } + Status = LdrPEGetOrLoadModule(Module, ImportedName, &ImportedModule); + if (!NT_SUCCESS(Status)) + { + return Status; + } - Status = LdrPEProcessImportDirectoryEntry(Module->Base, ImportedModule, ImportModuleDirectory); - if (!NT_SUCCESS(Status)) - { - return Status; - } + Status = LdrPEProcessImportDirectoryEntry(Module->Base, ImportedModule, ImportModuleDirectory); + if (!NT_SUCCESS(Status)) + { + return Status; + } - ImportModuleDirectory++; - } - return STATUS_SUCCESS; + ImportModuleDirectory++; + } + return STATUS_SUCCESS; } /* EOF */ diff --git a/reactos/ntoskrnl/ldr/sysdll.c b/reactos/ntoskrnl/ldr/sysdll.c index 9af2d009eef..7e20b6b892b 100644 --- a/reactos/ntoskrnl/ldr/sysdll.c +++ b/reactos/ntoskrnl/ldr/sysdll.c @@ -18,11 +18,11 @@ /* GLOBALS *******************************************************************/ -static PVOID SystemDllEntryPoint = NULL; -static PVOID SystemDllApcDispatcher = NULL; -static PVOID SystemDllCallbackDispatcher = NULL; -static PVOID SystemDllExceptionDispatcher = NULL; -static PVOID SystemDllRaiseExceptionDispatcher = NULL; +PVOID SystemDllEntryPoint = NULL; +PVOID SystemDllApcDispatcher = NULL; +PVOID SystemDllCallbackDispatcher = NULL; +PVOID SystemDllExceptionDispatcher = NULL; +PVOID SystemDllRaiseExceptionDispatcher = NULL; /* FUNCTIONS *****************************************************************/ @@ -144,7 +144,7 @@ NTSTATUS LdrpMapSystemDll(HANDLE ProcessHandle, SECTION_ALL_ACCESS, NULL, NULL, - PAGE_READWRITE, + PAGE_READONLY, SEC_IMAGE | SEC_COMMIT, FileHandle); if (!NT_SUCCESS(Status)) diff --git a/reactos/ntoskrnl/lpc/close.c b/reactos/ntoskrnl/lpc/close.c index 4f61c713263..c73a8dfe367 100644 --- a/reactos/ntoskrnl/lpc/close.c +++ b/reactos/ntoskrnl/lpc/close.c @@ -41,8 +41,7 @@ NiClosePort (PVOID ObjectBody, ULONG HandleCount) * If the client has just closed its handle then tell the server what * happened and disconnect this port. */ - if (HandleCount == 0 && Port->State == EPORT_CONNECTED_CLIENT && - ObGetObjectPointerCount(Port) == 2) + if (HandleCount == 1 && Port->State == EPORT_CONNECTED_CLIENT) { DPRINT("Informing server\n"); Message.MessageSize = sizeof(LPC_MESSAGE); @@ -64,8 +63,7 @@ NiClosePort (PVOID ObjectBody, ULONG HandleCount) * If the server has closed all of its handles then disconnect the port, * don't actually notify the client until it attempts an operation. */ - if (HandleCount == 0 && Port->State == EPORT_CONNECTED_SERVER && - ObGetObjectPointerCount(Port) == 1) + if (HandleCount == 1 && Port->State == EPORT_CONNECTED_SERVER) { DPRINT("Cleaning up server\n"); Port->OtherPort->OtherPort = NULL; diff --git a/reactos/ntoskrnl/mm/RPoolMgr.h b/reactos/ntoskrnl/mm/RPoolMgr.h index dae429f3574..6d3a90535a1 100644 --- a/reactos/ntoskrnl/mm/RPoolMgr.h +++ b/reactos/ntoskrnl/mm/RPoolMgr.h @@ -805,8 +805,9 @@ try_again: que_reclaimed = 1; goto try_again; } - /*DPRINT1("Trying to allocate %lu bytes from paged pool - nothing suitable found, returning NULL\n", - queBytes );*/ + DPRINT1("Trying to allocate %lu bytes from paged pool - nothing suitable found, returning NULL\n", + queBytes ); + R_RELEASE_MUTEX(pool); return NULL; } /* diff --git a/reactos/ntoskrnl/mm/freelist.c b/reactos/ntoskrnl/mm/freelist.c index 0e6038467a1..02bcd736b07 100644 --- a/reactos/ntoskrnl/mm/freelist.c +++ b/reactos/ntoskrnl/mm/freelist.c @@ -623,15 +623,15 @@ MmGetSavedSwapEntryPage(PFN_TYPE Pfn) } VOID -MmReferencePage(PFN_TYPE Pfn) +MmReferencePageUnsafe(PFN_TYPE Pfn) { KIRQL oldIrql; - DPRINT("MmReferencePage(PysicalAddress %x)\n", Pfn << PAGE_SHIFT); + DPRINT("MmReferencePageUnsafe(PysicalAddress %x)\n", Pfn << PAGE_SHIFT); if (Pfn == 0 || Pfn >= MmPageArraySize) { - KEBUGCHECK(0); + return; } KeAcquireSpinLock(&PageListLock, &oldIrql); @@ -646,6 +646,19 @@ MmReferencePage(PFN_TYPE Pfn) KeReleaseSpinLock(&PageListLock, oldIrql); } +VOID +MmReferencePage(PFN_TYPE Pfn) +{ + DPRINT("MmReferencePage(PysicalAddress %x)\n", Pfn << PAGE_SHIFT); + + if (Pfn == 0 || Pfn >= MmPageArraySize) + { + KEBUGCHECK(0); + } + + MmReferencePageUnsafe(Pfn); +} + ULONG MmGetReferenceCountPage(PFN_TYPE Pfn) { @@ -792,15 +805,15 @@ MmGetLockCountPage(PFN_TYPE Pfn) } VOID -MmLockPage(PFN_TYPE Pfn) +MmLockPageUnsafe(PFN_TYPE Pfn) { KIRQL oldIrql; - DPRINT("MmLockPage(PhysicalAddress %x)\n", Pfn << PAGE_SHIFT); + DPRINT("MmLockPageUnsafe(PhysicalAddress %x)\n", Pfn << PAGE_SHIFT); if (Pfn == 0 || Pfn >= MmPageArraySize) { - KEBUGCHECK(0); + return; } KeAcquireSpinLock(&PageListLock, &oldIrql); @@ -815,6 +828,19 @@ MmLockPage(PFN_TYPE Pfn) KeReleaseSpinLock(&PageListLock, oldIrql); } +VOID +MmLockPage(PFN_TYPE Pfn) +{ + DPRINT("MmLockPage(PhysicalAddress %x)\n", Pfn << PAGE_SHIFT); + + if (Pfn == 0 || Pfn >= MmPageArraySize) + { + KEBUGCHECK(0); + } + + MmLockPageUnsafe(Pfn); +} + VOID MmUnlockPage(PFN_TYPE Pfn) { diff --git a/reactos/ntoskrnl/mm/marea.c b/reactos/ntoskrnl/mm/marea.c index 6830f995a38..65b3c9a3e26 100644 --- a/reactos/ntoskrnl/mm/marea.c +++ b/reactos/ntoskrnl/mm/marea.c @@ -491,7 +491,8 @@ MmFindGapBottomUp( /* Check if there is enough space after the last memory area. */ AlignedAddress = MM_ROUND_UP(PreviousNode->EndingAddress, Granularity); - if ((ULONG_PTR)HighestAddress - (ULONG_PTR)AlignedAddress >= Length) + if ((ULONG_PTR)HighestAddress > (ULONG_PTR)AlignedAddress && + (ULONG_PTR)HighestAddress - (ULONG_PTR)AlignedAddress >= Length) { DPRINT("MmFindGapBottomUp: %p\n", AlignedAddress); return AlignedAddress; diff --git a/reactos/ntoskrnl/mm/mdl.c b/reactos/ntoskrnl/mm/mdl.c index e3d7c2a44de..7f24286de1c 100644 --- a/reactos/ntoskrnl/mm/mdl.c +++ b/reactos/ntoskrnl/mm/mdl.c @@ -29,7 +29,7 @@ extern ULONG MmPageArraySize; /* MDL Flags desc. -MDL_PAGES_LOCKED MmProbelAndLockPages has been called for this mdl +MDL_PAGES_LOCKED MmProbeAndLockPages has been called for this mdl MDL_SOURCE_IS_NONPAGED_POOL mdl has been build by MmBuildMdlForNonPagedPool MDL_PARTIAL mdl has been built by IoBuildPartialMdl MDL_MAPPING_CAN_FAIL in case of an error, MmMapLockedPages will return NULL instead of to bugcheck @@ -233,7 +233,7 @@ MmUnmapLockedPages(PVOID BaseAddress, PMDL Mdl) /* Unmap all the pages. */ for (i = 0; i < PageCount; i++) { - MmDeleteVirtualMapping(NULL, + MmDeleteVirtualMapping(Mdl->Process, (char*)BaseAddress + (i * PAGE_SIZE), FALSE, NULL, @@ -300,7 +300,7 @@ MmBuildMdlFromPages(PMDL Mdl, PPFN_TYPE Pages) { memcpy(Mdl + 1, Pages, sizeof(PFN_TYPE) * (PAGE_ROUND_UP(Mdl->ByteOffset+Mdl->ByteCount)/PAGE_SIZE)); - //FIXME: this flag should be set by the caller perhaps? + /* FIXME: this flag should be set by the caller perhaps? */ Mdl->MdlFlags |= MDL_IO_PAGE_READ; } @@ -361,6 +361,7 @@ VOID STDCALL MmProbeAndLockPages (PMDL Mdl, KPROCESSOR_MODE Mode; PFN_TYPE Page; PEPROCESS CurrentProcess = PsGetCurrentProcess(); + PMADDRESS_SPACE AddressSpace; DPRINT("MmProbeAndLockPages(Mdl %x)\n", Mdl); @@ -374,8 +375,8 @@ VOID STDCALL MmProbeAndLockPages (PMDL Mdl, ASSERT(NrPages <= (Mdl->Size - sizeof(MDL))/sizeof(PFN_TYPE)); - if (Mdl->StartVa >= (PVOID)KERNEL_BASE && - MmGetPfnForProcess(NULL, Mdl->StartVa) > MmPageArraySize) + if (Mdl->StartVa >= (PVOID)KERNEL_BASE && + MmGetPfnForProcess(NULL, Mdl->StartVa) >= MmPageArraySize) { /* phys addr is not phys memory so this must be io memory */ @@ -391,22 +392,24 @@ VOID STDCALL MmProbeAndLockPages (PMDL Mdl, if (Mdl->StartVa >= (PVOID)KERNEL_BASE) { - //FIXME: why isn't AccessMode used? + /* FIXME: why isn't AccessMode used? */ Mode = KernelMode; Mdl->Process = NULL; + AddressSpace = MmGetKernelAddressSpace(); } else { - //FIXME: why isn't AccessMode used? + /* FIXME: why isn't AccessMode used? */ Mode = UserMode; Mdl->Process = CurrentProcess; + AddressSpace = &CurrentProcess->AddressSpace; } /* * Lock the pages */ - MmLockAddressSpace(&CurrentProcess->AddressSpace); + MmLockAddressSpace(AddressSpace); for (i = 0; i < NrPages; i++) { @@ -427,10 +430,14 @@ VOID STDCALL MmProbeAndLockPages (PMDL Mdl, for (j = 0; j < i; j++) { Page = MdlPages[j]; - MmUnlockPage(Page); - MmDereferencePage(Page); + if (Page < MmPageArraySize) + { + MmUnlockPage(Page); + MmDereferencePage(Page); + } } - ExRaiseStatus(Status); + MmUnlockAddressSpace(AddressSpace); + ExRaiseStatus(STATUS_ACCESS_VIOLATION); } } else @@ -447,18 +454,25 @@ VOID STDCALL MmProbeAndLockPages (PMDL Mdl, for (j = 0; j < i; j++) { Page = MdlPages[j]; - MmUnlockPage(Page); - MmDereferencePage(Page); + if (Page < MmPageArraySize) + { + MmUnlockPage(Page); + MmDereferencePage(Page); + } } - ExRaiseStatus(Status); + MmUnlockAddressSpace(AddressSpace); + ExRaiseStatus(STATUS_ACCESS_VIOLATION); } } Page = MmGetPfnForProcess(NULL, Address); MdlPages[i] = Page; - MmReferencePage(Page); + if (Page >= MmPageArraySize) + Mdl->MdlFlags |= MDL_IO_SPACE; + else + MmReferencePage(Page); } - MmUnlockAddressSpace(&CurrentProcess->AddressSpace); + MmUnlockAddressSpace(AddressSpace); Mdl->MdlFlags |= MDL_PAGES_LOCKED; } @@ -838,6 +852,8 @@ MmMapLockedPagesSpecifyCache ( IN PMDL Mdl, } KeReleaseSpinLock(&MiMdlMappingRegionLock, oldIrql); + + Mdl->Process = NULL; } /* Set the virtual mappings for the MDL pages. */ @@ -848,11 +864,18 @@ MmMapLockedPagesSpecifyCache ( IN PMDL Mdl, Protect |= PAGE_NOCACHE; else if (CacheType == MmWriteCombined) DPRINT("CacheType MmWriteCombined not supported!\n"); - Status = MmCreateVirtualMapping(CurrentProcess, - Base, - Protect, - MdlPages, - PageCount); + if (Mdl->MdlFlags & MDL_IO_SPACE) + Status = MmCreateVirtualMappingUnsafe(CurrentProcess, + Base, + Protect, + MdlPages, + PageCount); + else + Status = MmCreateVirtualMapping(CurrentProcess, + Base, + Protect, + MdlPages, + PageCount); if (!NT_SUCCESS(Status)) { DbgPrint("Unable to create virtual mapping\n"); diff --git a/reactos/ntoskrnl/mm/mminit.c b/reactos/ntoskrnl/mm/mminit.c index 7c7973921b0..f3ebeae7c66 100644 --- a/reactos/ntoskrnl/mm/mminit.c +++ b/reactos/ntoskrnl/mm/mminit.c @@ -386,10 +386,22 @@ MmInit1(ULONG_PTR FirstKrnlPhysAddr, /* * Unmap low memory */ -#ifndef CONFIG_SMP - /* In SMP mode we unmap the low memory in MmInit3. +#ifdef CONFIG_SMP + /* In SMP mode we unmap the low memory pagetable in MmInit3. The APIC needs the mapping of the first pages - while the processors are starting up. */ + while the processors are starting up. + We unmap all pages except page 2 and 3. */ + for (MappingAddress = 0; + MappingAddress < 1024 * PAGE_SIZE; + MappingAddress += PAGE_SIZE) + { + if (MappingAddress != 2 * PAGE_SIZE && + MappingAddress != 3 * PAGE_SIZE) + { + MmRawDeleteVirtualMapping((PVOID)MappingAddress); + } + } +#else MmDeletePageTable(NULL, 0); #endif diff --git a/reactos/ntoskrnl/mm/section.c b/reactos/ntoskrnl/mm/section.c index 3587bff37a4..2a6a35fd882 100644 --- a/reactos/ntoskrnl/mm/section.c +++ b/reactos/ntoskrnl/mm/section.c @@ -727,6 +727,9 @@ MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace, MmSharePageEntrySectionSegment(Segment, Offset); + /* FIXME: Should we call MmCreateVirtualMappingUnsafe if + * (Section->AllocationAttributes & SEC_PHYSICALMEMORY) is true? + */ Status = MmCreateVirtualMapping(MemoryArea->Process, Address, Attributes, @@ -829,14 +832,14 @@ MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace, * Just map the desired physical page */ Page = (Offset + MemoryArea->Data.SectionData.ViewOffset) >> PAGE_SHIFT; - Status = MmCreateVirtualMapping(AddressSpace->Process, - Address, - Region->Protect, - &Page, - 1); + Status = MmCreateVirtualMappingUnsafe(AddressSpace->Process, + Address, + Region->Protect, + &Page, + 1); if (!NT_SUCCESS(Status)) { - DPRINT("MmCreateVirtualMapping failed, not out of memory\n"); + DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n"); KEBUGCHECK(0); return(Status); } @@ -846,7 +849,7 @@ MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace, */ if (Locked) { - MmLockPage(Page); + MmLockPageUnsafe(Page); } /* diff --git a/reactos/ntoskrnl/mm/virtual.c b/reactos/ntoskrnl/mm/virtual.c index aadecb396c9..7398a8f599f 100644 --- a/reactos/ntoskrnl/mm/virtual.c +++ b/reactos/ntoskrnl/mm/virtual.c @@ -463,11 +463,41 @@ NtReadVirtualMemory(IN HANDLE ProcessHandle, IN ULONG NumberOfBytesToRead, OUT PULONG NumberOfBytesRead OPTIONAL) { - NTSTATUS Status; PMDL Mdl; PVOID SystemAddress; + KPROCESSOR_MODE PreviousMode; PEPROCESS Process, CurrentProcess; - + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForWrite(Buffer, + NumberOfBytesToRead, + 1); + if(NumberOfBytesRead != NULL) + { + ProbeForWrite(NumberOfBytesRead, + sizeof(ULONG), + sizeof(ULONG)); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, " "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress, @@ -476,7 +506,7 @@ NtReadVirtualMemory(IN HANDLE ProcessHandle, Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_VM_WRITE, NULL, - UserMode, + PreviousMode, (PVOID*)(&Process), NULL); if (!NT_SUCCESS(Status)) @@ -488,7 +518,15 @@ NtReadVirtualMemory(IN HANDLE ProcessHandle, if (Process == CurrentProcess) { - memcpy(Buffer, BaseAddress, NumberOfBytesToRead); + _SEH_TRY + { + RtlCopyMemory(Buffer, BaseAddress, NumberOfBytesToRead); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; } else { @@ -500,39 +538,62 @@ NtReadVirtualMemory(IN HANDLE ProcessHandle, ObDereferenceObject(Process); return(STATUS_NO_MEMORY); } - MmProbeAndLockPages(Mdl, - UserMode, - IoWriteAccess); - - KeAttachProcess(&Process->Pcb); - - SystemAddress = MmGetSystemAddressForMdl(Mdl); - - Status = STATUS_SUCCESS; - _SEH_TRY { - ProbeForRead(BaseAddress, NumberOfBytesToRead, 1); - Status = STATUS_PARTIAL_COPY; - memcpy(SystemAddress, BaseAddress, NumberOfBytesToRead); - Status = STATUS_SUCCESS; - } _SEH_HANDLE { - if(Status != STATUS_PARTIAL_COPY) - Status = _SEH_GetExceptionCode(); - } _SEH_END; - - KeDetachProcess(); - - if (Mdl->MappedSystemVa != NULL) + _SEH_TRY { - MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl); + MmProbeAndLockPages(Mdl, + PreviousMode, + IoWriteAccess); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + KeAttachProcess(&Process->Pcb); + + SystemAddress = MmGetSystemAddressForMdl(Mdl); + + Status = STATUS_SUCCESS; + _SEH_TRY { + ProbeForRead(BaseAddress, NumberOfBytesToRead, 1); + Status = STATUS_PARTIAL_COPY; + RtlCopyMemory(SystemAddress, BaseAddress, NumberOfBytesToRead); + Status = STATUS_SUCCESS; + } _SEH_HANDLE { + if(Status != STATUS_PARTIAL_COPY) + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + KeDetachProcess(); + + if (Mdl->MappedSystemVa != NULL) + { + MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl); + } + MmUnlockPages(Mdl); } - MmUnlockPages(Mdl); ExFreePool(Mdl); } ObDereferenceObject(Process); - if (NumberOfBytesRead) - *NumberOfBytesRead = NumberOfBytesToRead; + if((NT_SUCCESS(Status) || Status == STATUS_PARTIAL_COPY) && + NumberOfBytesRead != NULL) + { + _SEH_TRY + { + *NumberOfBytesRead = NumberOfBytesToRead; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + return(Status); } diff --git a/reactos/ntoskrnl/ntoskrnl.def b/reactos/ntoskrnl/ntoskrnl.def index 461ebe6aa04..f7159dee818 100644 --- a/reactos/ntoskrnl/ntoskrnl.def +++ b/reactos/ntoskrnl/ntoskrnl.def @@ -122,7 +122,6 @@ ExIsResourceAcquiredExclusiveLite@4 ExIsResourceAcquiredSharedLite@4 ExLocalTimeToSystemTime@8 ExNotifyCallback@12 -ExPostSystemEvent@12 ExQueryPoolBlockSize@8 ExQueueWorkItem@8 ExRaiseAccessViolation@0 @@ -153,24 +152,6 @@ ExUuidCreate@4 ExVerifySuite@4 @ExWaitForRundownProtectionRelease@4 ExWindowStationObjectType DATA -ExInitializeBinaryTree@12 -ExDeleteBinaryTree@4 -ExInsertBinaryTree@12 -ExSearchBinaryTree@12 -ExRemoveBinaryTree@12 -ExTraverseBinaryTree@16 -ExInitializeSplayTree@16 -ExDeleteSplayTree@4 -ExInsertSplayTree@12 -ExSearchSplayTree@12 -ExRemoveSplayTree@12 -ExWeightOfSplayTree@8 -ExTraverseSplayTree@16 -ExInitializeHashTable@16 -ExDeleteHashTable@4 -ExInsertHashTable@16 -ExSearchHashTable@16 -ExRemoveHashTable@16 @ExfAcquirePushLockExclusive@4 @ExfAcquirePushLockShared@4 @ExfReleasePushLock@4 @@ -530,7 +511,7 @@ Ke386IoSetAccessProcess@8 Ke386QueryIoAccessMap@8 Ke386SetIoAccessMap@8 KeAcquireSpinLockAtDpcLevel@4 -KeAcquireDispatcherDatabaseLockAtDpcLevel +@KeAcquireDispatcherDatabaseLockAtDpcLevel@0 @KeAcquireInStackQueuedSpinLockAtDpcLevel@8 KeAcquireInterruptSpinLock@4 KeAddSystemServiceTable@20 @@ -611,7 +592,7 @@ KeReadStateSemaphore@4 KeReadStateTimer@4 KeRegisterBugCheckCallback@20 KeRegisterBugCheckReasonCallback@16 -KeReleaseDispatcherDatabaseLockFromDpcLevel +@KeReleaseDispatcherDatabaseLockFromDpcLevel@0 @KeReleaseInStackQueuedSpinLockFromDpcLevel@4 KeReleaseInterruptSpinLock@8 KeReleaseMutant@16 @@ -865,6 +846,7 @@ ObDereferenceObject@4 ;PfxInsertPrefix ;PfxRemovePrefix PoCallDriver@8 +PoQueueShutdownWorkItem@4 PoRegisterDeviceForIdleDetection@16 PoRegisterSystemState@8 PoRequestPowerIrp@24 @@ -1231,7 +1213,7 @@ RtlUpperString@8 RtlValidRelativeSecurityDescriptor@12 RtlValidSecurityDescriptor@4 RtlValidSid@4 -;RtlVerifyVersionInfo@16 +RtlVerifyVersionInfo@16 RtlVolumeDeviceToDosName@8 RtlWalkFrameChain@12 RtlWriteRegistryValue@24 @@ -1419,6 +1401,7 @@ ZwYieldExecution@0 _abnormal_termination _alldiv _allmul +_alloca_probe _allrem _allshl _allshr diff --git a/reactos/ntoskrnl/ntoskrnl.mc b/reactos/ntoskrnl/ntoskrnl.mc index 1458a0eca28..4fcbae9dda8 100644 --- a/reactos/ntoskrnl/ntoskrnl.mc +++ b/reactos/ntoskrnl/ntoskrnl.mc @@ -987,6 +987,30 @@ Language=English INVALID_WORK_QUEUE_ITEM . +MessageId=0xE1 +Severity=Success +Facility=System +SymbolicName=WORKER_THREAD_RETURNED_AT_BAD_IRQL +Language=English +WORKER_THREAD_RETURNED_AT_BAD_IRQL +. + +MessageId=0xE2 +Severity=Success +Facility=System +SymbolicName=MANUALLY_INITIATED_CRASH +Language=English +MANUALLY_INITIATED_CRASH +. + +MessageId=0xFA +Severity=Success +Facility=System +SymbolicName=IMPERSONATING_WORKER_THREAD +Language=English +IMPERSONATING_WORKER_THREAD +. + MessageId=0x9A Severity=Informational Facility=System diff --git a/reactos/ntoskrnl/ntoskrnl.xml b/reactos/ntoskrnl/ntoskrnl.xml index 7665b576202..d9aa91760bb 100644 --- a/reactos/ntoskrnl/ntoskrnl.xml +++ b/reactos/ntoskrnl/ntoskrnl.xml @@ -11,8 +11,8 @@ hal kjs pseh - rosrtl rtl + rosrtl string rossym @@ -41,22 +41,19 @@ trap.s tskswitch.S tss.c - usercall.c + usercall.S usertrap.c v86m.c v86m_sup.S vdm.c - alert.c apc.c bug.c catch.c clock.c - critical.c device.c dpc.c - error.c event.c gmutex.c ipi.c @@ -70,6 +67,7 @@ sem.c spinlock.c timer.c + usercall.c wait.c @@ -119,24 +117,22 @@ interlck.c - btree.c callback.c + error.c event.c evtpair.c fmutex.c - hashtab.c + handle.c init.c interlck.c list.c lookas.c mutant.c - napi.c power.c profile.c resource.c rundown.c sem.c - stree.c synch.c sysinfo.c time.c @@ -296,6 +292,7 @@ sdcache.c security.c symlink.c + wait.c power.c @@ -315,10 +312,10 @@ locale.c process.c psmgr.c + query.c + security.c suspend.c thread.c - tinfo.c - w32call.c win32.c diff --git a/reactos/ntoskrnl/ob/handle.c b/reactos/ntoskrnl/ob/handle.c index dcc90efd882..bf59c501975 100644 --- a/reactos/ntoskrnl/ob/handle.c +++ b/reactos/ntoskrnl/ob/handle.c @@ -33,89 +33,42 @@ #define NDEBUG #include -/* TYPES *******************************************************************/ +#define EX_OBJ_TO_HDR(eob) ((POBJECT_HEADER)((ULONG_PTR)(eob) & \ + ~(EX_HANDLE_ENTRY_PROTECTFROMCLOSE | EX_HANDLE_ENTRY_INHERITABLE | \ + EX_HANDLE_ENTRY_AUDITONCLOSE))) +#define EX_HTE_TO_HDR(hte) ((POBJECT_HEADER)((ULONG_PTR)((hte)->u1.Object) & \ + ~(EX_HANDLE_ENTRY_PROTECTFROMCLOSE | EX_HANDLE_ENTRY_INHERITABLE | \ + EX_HANDLE_ENTRY_AUDITONCLOSE))) -/* - * PURPOSE: Defines a handle - */ -typedef struct _HANDLE_ENTRY -{ - PVOID ObjectBody; - ACCESS_MASK GrantedAccess; -} HANDLE_ENTRY, *PHANDLE_ENTRY; - -#define HANDLE_BLOCK_ENTRIES \ - (((4 * PAGE_SIZE) - \ - (sizeof(LIST_ENTRY) + sizeof(ULONG))) / sizeof(HANDLE_ENTRY)) - -#define OB_HANDLE_FLAG_MASK 0x00000007 -#define OB_HANDLE_FLAG_AUDIT 0x00000004 -#define OB_HANDLE_FLAG_PROTECT 0x00000002 -#define OB_HANDLE_FLAG_INHERIT 0x00000001 - -#define OB_POINTER_TO_ENTRY(Pointer) \ - (PVOID)((ULONG_PTR)(Pointer) & ~OB_HANDLE_FLAG_MASK) - -#define OB_ENTRY_TO_POINTER(Entry) \ - (PVOID)((ULONG_PTR)(Entry) & ~OB_HANDLE_FLAG_MASK) - -/* - * PURPOSE: Defines a 4 page's worth of handles - */ -typedef struct -{ - LIST_ENTRY entry; - ULONG allocation_hint; - ULONG allocation_count; - HANDLE_ENTRY handles[HANDLE_BLOCK_ENTRIES]; -} HANDLE_BLOCK, *PHANDLE_BLOCK; - - -/* GLOBALS *******************************************************************/ - -#define TAG_HANDLE_TABLE TAG('H', 'T', 'B', 'L') +#define GENERIC_ANY (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL) /* FUNCTIONS ***************************************************************/ - -/* - * FUNCTION: Get the data structure for a handle - * ARGUMENTS: - * Process = Process to get the handle for - * h = Handle - * ARGUMENTS: A pointer to the information about the handle on success, - * NULL on failure - */ -static PHANDLE_ENTRY -ObpGetObjectByHandle(PHANDLE_TABLE HandleTable, - HANDLE h, - HANDLE_BLOCK **Block) +VOID +STDCALL +ObKillProcess(PEPROCESS Process) { - PLIST_ENTRY current; - unsigned int handle = (((unsigned int)h) >> 2) - 1; - unsigned int count = handle / HANDLE_BLOCK_ENTRIES; - HANDLE_BLOCK* blk = NULL; - unsigned int i; - - DPRINT("ObpGetObjectByHandle(HandleTable %x, h %x)\n",HandleTable,h); - - current = HandleTable->ListHead.Flink; - DPRINT("current %x\n",current); - - for (i = 0; i < count; i++) - { - current = current->Flink; - if (current == (&(HandleTable->ListHead))) - { - return(NULL); - } - } - - blk = CONTAINING_RECORD(current,HANDLE_BLOCK,entry); - if (Block) - *Block = blk; - DPRINT("object: %p\n",&(blk->handles[handle%HANDLE_BLOCK_ENTRIES])); - return(&(blk->handles[handle%HANDLE_BLOCK_ENTRIES])); + ObDeleteHandleTable(Process); +} + +VOID +ObpDecrementHandleCount(PVOID ObjectBody) +{ + POBJECT_HEADER ObjectHeader = BODY_TO_HEADER(ObjectBody); + LONG NewHandleCount = InterlockedDecrement(&ObjectHeader->HandleCount); + + if ((ObjectHeader->ObjectType != NULL) && + (ObjectHeader->ObjectType->Close != NULL)) + { + /* the handle count should be decremented but we pass the previous value + to the callback */ + ObjectHeader->ObjectType->Close(ObjectBody, NewHandleCount + 1); + } + + if(NewHandleCount == 0) + { + ObDereferenceObject(ObjectBody); + } } @@ -124,29 +77,32 @@ ObpQueryHandleAttributes(HANDLE Handle, POBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo) { PEPROCESS Process; - KIRQL oldIrql; - PHANDLE_ENTRY HandleEntry; + PHANDLE_TABLE_ENTRY HandleTableEntry; + LONG ExHandle = HANDLE_TO_EX_HANDLE(Handle); + + PAGED_CODE(); DPRINT("ObpQueryHandleAttributes(Handle %x)\n", Handle); + KeEnterCriticalRegion(); + Process = PsGetCurrentProcess(); - KeAcquireSpinLock(&Process->HandleTable.ListLock, &oldIrql); - HandleEntry = ObpGetObjectByHandle(&Process->HandleTable, - Handle, - NULL); - if (HandleEntry == NULL) + HandleTableEntry = ExMapHandleToPointer(Process->ObjectTable, + ExHandle); + if (HandleTableEntry == NULL) { - KeReleaseSpinLock(&Process->HandleTable.ListLock, oldIrql); + KeLeaveCriticalRegion(); return STATUS_INVALID_HANDLE; } - HandleInfo->Inherit = - ((ULONG_PTR)HandleEntry->ObjectBody & OB_HANDLE_FLAG_INHERIT); - HandleInfo->ProtectFromClose = - ((ULONG_PTR)HandleEntry->ObjectBody & OB_HANDLE_FLAG_PROTECT); + HandleInfo->Inherit = (HandleTableEntry->u1.ObAttributes & EX_HANDLE_ENTRY_INHERITABLE) != 0; + HandleInfo->ProtectFromClose = (HandleTableEntry->u1.ObAttributes & EX_HANDLE_ENTRY_PROTECTFROMCLOSE) != 0; - KeReleaseSpinLock(&Process->HandleTable.ListLock, oldIrql); + ExUnlockHandleTableEntry(Process->ObjectTable, + HandleTableEntry); + + KeLeaveCriticalRegion(); return STATUS_SUCCESS; } @@ -156,37 +112,42 @@ NTSTATUS ObpSetHandleAttributes(HANDLE Handle, POBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo) { - PHANDLE_ENTRY HandleEntry; PEPROCESS Process; - KIRQL oldIrql; + PHANDLE_TABLE_ENTRY HandleTableEntry; + LONG ExHandle = HANDLE_TO_EX_HANDLE(Handle); + + PAGED_CODE(); - DPRINT("ObpQueryHandleAttributes(Handle %x)\n", Handle); + DPRINT("ObpSetHandleAttributes(Handle %x)\n", Handle); Process = PsGetCurrentProcess(); + + KeEnterCriticalRegion(); - KeAcquireSpinLock(&Process->HandleTable.ListLock, &oldIrql); - HandleEntry = ObpGetObjectByHandle(&Process->HandleTable, - Handle, - NULL); - if (HandleEntry == NULL) + HandleTableEntry = ExMapHandleToPointer(Process->ObjectTable, + ExHandle); + if (HandleTableEntry == NULL) { - KeReleaseSpinLock(&Process->HandleTable.ListLock, oldIrql); + KeLeaveCriticalRegion(); return STATUS_INVALID_HANDLE; } if (HandleInfo->Inherit) - HandleEntry->ObjectBody = (PVOID)((ULONG_PTR)HandleEntry->ObjectBody | OB_HANDLE_FLAG_INHERIT); + HandleTableEntry->u1.ObAttributes |= EX_HANDLE_ENTRY_INHERITABLE; else - HandleEntry->ObjectBody = (PVOID)((ULONG_PTR)HandleEntry->ObjectBody & ~OB_HANDLE_FLAG_INHERIT); + HandleTableEntry->u1.ObAttributes &= ~EX_HANDLE_ENTRY_INHERITABLE; if (HandleInfo->ProtectFromClose) - HandleEntry->ObjectBody = (PVOID)((ULONG_PTR)HandleEntry->ObjectBody | OB_HANDLE_FLAG_PROTECT); + HandleTableEntry->u1.ObAttributes |= EX_HANDLE_ENTRY_PROTECTFROMCLOSE; else - HandleEntry->ObjectBody = (PVOID)((ULONG_PTR)HandleEntry->ObjectBody & ~OB_HANDLE_FLAG_PROTECT); + HandleTableEntry->u1.ObAttributes &= ~EX_HANDLE_ENTRY_PROTECTFROMCLOSE; /* FIXME: Do we need to set anything in the object header??? */ - KeReleaseSpinLock(&Process->HandleTable.ListLock, oldIrql); + ExUnlockHandleTableEntry(Process->ObjectTable, + HandleTableEntry); + + KeLeaveCriticalRegion(); return STATUS_SUCCESS; } @@ -201,46 +162,97 @@ ObDuplicateObject(PEPROCESS SourceProcess, BOOLEAN InheritHandle, ULONG Options) { - KIRQL oldIrql; - PHANDLE_ENTRY SourceHandleEntry; + PHANDLE_TABLE_ENTRY SourceHandleEntry; + HANDLE_TABLE_ENTRY NewHandleEntry; PVOID ObjectBody; - - KeAcquireSpinLock(&SourceProcess->HandleTable.ListLock, &oldIrql); - SourceHandleEntry = ObpGetObjectByHandle(&SourceProcess->HandleTable, - SourceHandle, - NULL); + POBJECT_HEADER ObjectHeader; + LONG ExTargetHandle; + LONG ExSourceHandle = HANDLE_TO_EX_HANDLE(SourceHandle); + ULONG NewHandleCount; + + PAGED_CODE(); + + KeEnterCriticalRegion(); + + SourceHandleEntry = ExMapHandleToPointer(SourceProcess->ObjectTable, + ExSourceHandle); if (SourceHandleEntry == NULL) { - KeReleaseSpinLock(&SourceProcess->HandleTable.ListLock, oldIrql); + KeLeaveCriticalRegion(); return STATUS_INVALID_HANDLE; } - ObjectBody = OB_ENTRY_TO_POINTER(SourceHandleEntry->ObjectBody); - ObReferenceObjectByPointer(ObjectBody, - 0, - NULL, - UserMode); - + ObjectHeader = EX_HTE_TO_HDR(SourceHandleEntry); + ObjectBody = HEADER_TO_BODY(ObjectHeader); + + NewHandleEntry.u1.Object = SourceHandleEntry->u1.Object; + if(InheritHandle) + NewHandleEntry.u1.ObAttributes |= EX_HANDLE_ENTRY_INHERITABLE; + else + NewHandleEntry.u1.ObAttributes &= ~EX_HANDLE_ENTRY_INHERITABLE; + NewHandleEntry.u2.GrantedAccess = ((Options & DUPLICATE_SAME_ACCESS) ? + SourceHandleEntry->u2.GrantedAccess : + DesiredAccess); if (Options & DUPLICATE_SAME_ACCESS) + { + NewHandleEntry.u2.GrantedAccess = SourceHandleEntry->u2.GrantedAccess; + } + else + { + if (DesiredAccess & GENERIC_ANY) { - DesiredAccess = SourceHandleEntry->GrantedAccess; + RtlMapGenericMask(&DesiredAccess, + ObjectHeader->ObjectType->Mapping); } - - KeReleaseSpinLock(&SourceProcess->HandleTable.ListLock, oldIrql); - ObCreateHandle(TargetProcess, - ObjectBody, - DesiredAccess, - InheritHandle, - TargetHandle); + NewHandleEntry.u2.GrantedAccess = DesiredAccess; + } - if (Options & DUPLICATE_CLOSE_SOURCE) + /* reference the object so it doesn't get deleted after releasing the lock + and before creating a new handle for it */ + ObReferenceObject(ObjectBody); + + /* increment the handle count of the object, it should always be >= 2 because + we're holding a handle lock to this object! if the new handle count was + 1 here, we're in big trouble... it would've been safe to increment and + check the handle count without using interlocked functions because the + entry is locked, which means the handle count can't change. */ + NewHandleCount = InterlockedIncrement(&ObjectHeader->HandleCount); + ASSERT(NewHandleCount >= 2); + + ExUnlockHandleTableEntry(SourceProcess->ObjectTable, + SourceHandleEntry); + + KeLeaveCriticalRegion(); + + /* attempt to create the new handle */ + ExTargetHandle = ExCreateHandle(TargetProcess->ObjectTable, + &NewHandleEntry); + if (ExTargetHandle != EX_INVALID_HANDLE) + { + if (Options & DUPLICATE_CLOSE_SOURCE) { - ZwClose(SourceHandle); + ObDeleteHandle(SourceProcess, + SourceHandle); } + + ObDereferenceObject(ObjectBody); - ObDereferenceObject(ObjectBody); - - return STATUS_SUCCESS; + *TargetHandle = EX_HANDLE_TO_HANDLE(ExTargetHandle); + + return STATUS_SUCCESS; + } + else + { + /* decrement the handle count we previously incremented, but don't call the + closing procedure because we're not closing a handle! */ + if(InterlockedDecrement(&ObjectHeader->HandleCount) == 0) + { + ObDereferenceObject(ObjectBody); + } + + ObDereferenceObject(ObjectBody); + return STATUS_UNSUCCESSFUL; + } } /* @@ -250,7 +262,7 @@ NTSTATUS STDCALL NtDuplicateObject (IN HANDLE SourceProcessHandle, IN HANDLE SourceHandle, IN HANDLE TargetProcessHandle, - OUT PHANDLE UnsafeTargetHandle, + OUT PHANDLE TargetHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN InheritHandle, ULONG Options) @@ -281,18 +293,38 @@ NtDuplicateObject (IN HANDLE SourceProcessHandle, { PEPROCESS SourceProcess; PEPROCESS TargetProcess; - PHANDLE_ENTRY SourceHandleEntry; - KIRQL oldIrql; - PVOID ObjectBody; - HANDLE TargetHandle; - NTSTATUS Status; + HANDLE hTarget; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); + PreviousMode = ExGetPreviousMode(); + + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForWrite(TargetHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + Status = ObReferenceObjectByHandle(SourceProcessHandle, PROCESS_DUP_HANDLE, NULL, - UserMode, + PreviousMode, (PVOID*)&SourceProcess, NULL); if (!NT_SUCCESS(Status)) @@ -303,7 +335,7 @@ NtDuplicateObject (IN HANDLE SourceProcessHandle, Status = ObReferenceObjectByHandle(TargetProcessHandle, PROCESS_DUP_HANDLE, NULL, - UserMode, + PreviousMode, (PVOID*)&TargetProcess, NULL); if (!NT_SUCCESS(Status)) @@ -313,136 +345,95 @@ NtDuplicateObject (IN HANDLE SourceProcessHandle, } /* Check for magic handle first */ - if (SourceHandle == NtCurrentThread()) + if (SourceHandle == NtCurrentThread() || + SourceHandle == NtCurrentProcess()) { - ObReferenceObjectByHandle(SourceHandle, - PROCESS_DUP_HANDLE, - NULL, - UserMode, - &ObjectBody, - NULL); + PVOID ObjectBody; + POBJECT_TYPE ObjectType; + + ObjectType = (SourceHandle == NtCurrentThread()) ? PsThreadType : PsProcessType; - ObCreateHandle(TargetProcess, - ObjectBody, - THREAD_ALL_ACCESS, - InheritHandle, - &TargetHandle); + Status = ObReferenceObjectByHandle(SourceHandle, + 0, + ObjectType, + PreviousMode, + &ObjectBody, + NULL); + if(NT_SUCCESS(Status)) + { + if (Options & DUPLICATE_SAME_ACCESS) + { + /* grant all access rights */ + DesiredAccess = ((ObjectType == PsThreadType) ? THREAD_ALL_ACCESS : PROCESS_ALL_ACCESS); + } + else + { + if (DesiredAccess & GENERIC_ANY) + { + RtlMapGenericMask(&DesiredAccess, + ObjectType->Mapping); + } + } + Status = ObCreateHandle(TargetProcess, + ObjectBody, + DesiredAccess, + InheritHandle, + &hTarget); + + ObDereferenceObject(ObjectBody); + + if (Options & DUPLICATE_CLOSE_SOURCE) + { + ObDeleteHandle(SourceProcess, + SourceHandle); + } + } } else { - KeAcquireSpinLock(&SourceProcess->HandleTable.ListLock, &oldIrql); - SourceHandleEntry = ObpGetObjectByHandle(&SourceProcess->HandleTable, - SourceHandle, - NULL); - if (SourceHandleEntry == NULL) - { - KeReleaseSpinLock(&SourceProcess->HandleTable.ListLock, oldIrql); - ObDereferenceObject(SourceProcess); - ObDereferenceObject(TargetProcess); - return(STATUS_INVALID_HANDLE); - } - ObjectBody = OB_ENTRY_TO_POINTER(SourceHandleEntry->ObjectBody); - ObReferenceObjectByPointer(ObjectBody, - 0, - NULL, - UserMode); - - if (Options & DUPLICATE_SAME_ACCESS) - { - DesiredAccess = SourceHandleEntry->GrantedAccess; - } - - KeReleaseSpinLock(&SourceProcess->HandleTable.ListLock, oldIrql); - - ObCreateHandle(TargetProcess, - ObjectBody, - DesiredAccess, - InheritHandle, - &TargetHandle); + Status = ObDuplicateObject(SourceProcess, + TargetProcess, + SourceHandle, + &hTarget, + DesiredAccess, + InheritHandle, + Options); } - - if (Options & DUPLICATE_CLOSE_SOURCE) - { - ZwClose(SourceHandle); - } - + ObDereferenceObject(TargetProcess); ObDereferenceObject(SourceProcess); - ObDereferenceObject(ObjectBody); - - Status = MmCopyToCaller(UnsafeTargetHandle, &TargetHandle, sizeof(HANDLE)); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - return(STATUS_SUCCESS); + if(NT_SUCCESS(Status)) + { + _SEH_TRY + { + *TargetHandle = hTarget; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + + return Status; } -VOID ObCloseAllHandles(PEPROCESS Process) +static VOID STDCALL +DeleteHandleCallback(PHANDLE_TABLE HandleTable, + PVOID Object, + ULONG GrantedAccess, + PVOID Context) { - KIRQL oldIrql; - PHANDLE_TABLE HandleTable; - PLIST_ENTRY current_entry; - PHANDLE_BLOCK current; - ULONG i; - PVOID ObjectBody; - - DPRINT("ObCloseAllHandles(Process %x)\n", Process); - - HandleTable = &Process->HandleTable; - - KeAcquireSpinLock(&HandleTable->ListLock, &oldIrql); - - current_entry = HandleTable->ListHead.Flink; - - while (current_entry != &HandleTable->ListHead) - { - current = CONTAINING_RECORD(current_entry, HANDLE_BLOCK, entry); - - for (i = 0; i < HANDLE_BLOCK_ENTRIES; i++) - { - ObjectBody = OB_ENTRY_TO_POINTER(current->handles[i].ObjectBody); - - if (ObjectBody != NULL) - { - POBJECT_HEADER Header = BODY_TO_HEADER(ObjectBody); - -#if 0 - if (Header->ObjectType == PsProcessType || - Header->ObjectType == PsThreadType) - { - DPRINT("Deleting handle to %x\n", ObjectBody); - } -#endif - - ObReferenceObjectByPointer(ObjectBody, - 0, - NULL, - UserMode); - InterlockedDecrement(&Header->HandleCount); - current->handles[i].ObjectBody = NULL; - - KeReleaseSpinLock(&HandleTable->ListLock, oldIrql); - if ((Header->ObjectType != NULL) && - (Header->ObjectType->Close != NULL)) - { - Header->ObjectType->Close(ObjectBody, - Header->HandleCount); - } - - ObDereferenceObject(ObjectBody); - KeAcquireSpinLock(&HandleTable->ListLock, &oldIrql); - current_entry = &HandleTable->ListHead; - break; - } - } - - current_entry = current_entry->Flink; - } - KeReleaseSpinLock(&HandleTable->ListLock, oldIrql); - DPRINT("ObCloseAllHandles() finished\n"); - DPRINT("Type %x\n", BODY_TO_HEADER(Process)->ObjectType); + POBJECT_HEADER ObjectHeader; + PVOID ObjectBody; + + PAGED_CODE(); + + ObjectHeader = EX_OBJ_TO_HDR(Object); + ObjectBody = HEADER_TO_BODY(ObjectHeader); + + ObpDecrementHandleCount(ObjectBody); } VOID ObDeleteHandleTable(PEPROCESS Process) @@ -450,26 +441,38 @@ VOID ObDeleteHandleTable(PEPROCESS Process) * FUNCTION: Deletes the handle table associated with a process */ { - PLIST_ENTRY current = NULL; - PHANDLE_TABLE HandleTable = NULL; - - ObCloseAllHandles(Process); - - HandleTable = &Process->HandleTable; - current = RemoveHeadList(&HandleTable->ListHead); - - while (current != &HandleTable->ListHead) - { - HANDLE_BLOCK* HandleBlock = CONTAINING_RECORD(current, - HANDLE_BLOCK, - entry); - DPRINT("Freeing %x\n", HandleBlock); - ExFreePool(HandleBlock); - - current = RemoveHeadList(&HandleTable->ListHead); - } + PAGED_CODE(); + + ExDestroyHandleTable(Process->ObjectTable, + DeleteHandleCallback, + Process); } +static BOOLEAN STDCALL +DuplicateHandleCallback(PHANDLE_TABLE HandleTable, + PHANDLE_TABLE_ENTRY HandleTableEntry, + PVOID Context) +{ + POBJECT_HEADER ObjectHeader; + BOOLEAN Ret = FALSE; + + PAGED_CODE(); + + Ret = (HandleTableEntry->u1.ObAttributes & EX_HANDLE_ENTRY_INHERITABLE) != 0; + if(Ret) + { + ObjectHeader = EX_HTE_TO_HDR(HandleTableEntry); + if(InterlockedIncrement(&ObjectHeader->HandleCount) == 1) + { + ObReferenceObjectByPointer(HEADER_TO_BODY(ObjectHeader), + 0, + NULL, + UserMode); + } + } + + return Ret; +} VOID ObCreateHandleTable(PEPROCESS Parent, BOOLEAN Inherit, @@ -482,133 +485,69 @@ VOID ObCreateHandleTable(PEPROCESS Parent, * Process = Process whose handle table is to be created */ { - PHANDLE_TABLE ParentHandleTable, HandleTable; - KIRQL oldIrql; - PLIST_ENTRY parent_current; - ULONG i; - PHANDLE_BLOCK current_block, new_block; - + PAGED_CODE(); + DPRINT("ObCreateHandleTable(Parent %x, Inherit %d, Process %x)\n", Parent,Inherit,Process); - - InitializeListHead(&(Process->HandleTable.ListHead)); - KeInitializeSpinLock(&(Process->HandleTable.ListLock)); - - if (Parent != NULL) - { - ParentHandleTable = &Parent->HandleTable; - HandleTable = &Process->HandleTable; - - KeAcquireSpinLock(&Parent->HandleTable.ListLock, &oldIrql); - KeAcquireSpinLockAtDpcLevel(&Process->HandleTable.ListLock); - - parent_current = ParentHandleTable->ListHead.Flink; - - while (parent_current != &ParentHandleTable->ListHead) - { - current_block = CONTAINING_RECORD(parent_current, - HANDLE_BLOCK, - entry); - new_block = ExAllocatePoolWithTag(NonPagedPool, - sizeof(HANDLE_BLOCK), - TAG_HANDLE_TABLE); - if (new_block == NULL) - { - break; - } - RtlZeroMemory(new_block, sizeof(HANDLE_BLOCK)); - - for (i=0; ihandles[i].ObjectBody) - { - if ((ULONG_PTR)current_block->handles[i].ObjectBody & OB_HANDLE_FLAG_INHERIT) - { - new_block->handles[i].ObjectBody = - current_block->handles[i].ObjectBody; - new_block->handles[i].GrantedAccess = - current_block->handles[i].GrantedAccess; - InterlockedIncrement(&(BODY_TO_HEADER(OB_ENTRY_TO_POINTER(current_block->handles[i].ObjectBody))->HandleCount)); - } - } - } - InsertTailList(&Process->HandleTable.ListHead, &new_block->entry); - parent_current = parent_current->Flink; - } - KeReleaseSpinLockFromDpcLevel(&Process->HandleTable.ListLock); - KeReleaseSpinLock(&Parent->HandleTable.ListLock, oldIrql); - } + if(Parent != NULL) + { + Process->ObjectTable = ExDupHandleTable(Process, + DuplicateHandleCallback, + NULL, + Parent->ObjectTable); + } + else + { + Process->ObjectTable = ExCreateHandleTable(Process); + } } NTSTATUS ObDeleteHandle(PEPROCESS Process, - HANDLE Handle, - PVOID *ObjectBody) + HANDLE Handle) { - PHANDLE_ENTRY HandleEntry; + PHANDLE_TABLE_ENTRY HandleEntry; PVOID Body; - KIRQL oldIrql; - PHANDLE_TABLE HandleTable; - POBJECT_HEADER Header; - HANDLE_BLOCK *Block; + POBJECT_HEADER ObjectHeader; + LONG ExHandle = HANDLE_TO_EX_HANDLE(Handle); PAGED_CODE(); DPRINT("ObDeleteHandle(Handle %x)\n",Handle); - - HandleTable = &Process->HandleTable; - - KeAcquireSpinLock(&HandleTable->ListLock, &oldIrql); - - HandleEntry = ObpGetObjectByHandle(HandleTable, Handle, &Block); - if (HandleEntry == NULL) + + KeEnterCriticalRegion(); + + HandleEntry = ExMapHandleToPointer(Process->ObjectTable, + ExHandle); + if(HandleEntry != NULL) + { + if(HandleEntry->u1.ObAttributes & EX_HANDLE_ENTRY_PROTECTFROMCLOSE) { - KeReleaseSpinLock(&HandleTable->ListLock, oldIrql); - *ObjectBody = NULL; - return STATUS_INVALID_HANDLE; + ExUnlockHandleTableEntry(Process->ObjectTable, + HandleEntry); + + KeLeaveCriticalRegion(); + + return STATUS_HANDLE_NOT_CLOSABLE; } - if ((ULONG_PTR)HandleEntry->ObjectBody & OB_HANDLE_FLAG_PROTECT) - { - KeReleaseSpinLock(&HandleTable->ListLock, oldIrql); - *ObjectBody = NULL; - return STATUS_HANDLE_NOT_CLOSABLE; - } - - Body = OB_ENTRY_TO_POINTER(HandleEntry->ObjectBody); - DPRINT("ObjectBody %x\n", Body); - if (Body == NULL) - { - KeReleaseSpinLock(&HandleTable->ListLock, oldIrql); - *ObjectBody = NULL; - return STATUS_UNSUCCESSFUL; - } - - Header = BODY_TO_HEADER(Body); - ObReferenceObjectByPointer(Body, - 0, - NULL, - UserMode); - InterlockedDecrement(&Header->HandleCount); - HandleEntry->ObjectBody = NULL; - - Block->allocation_count--; - Block->allocation_hint = (ULONG_PTR)Handle % HANDLE_BLOCK_ENTRIES; - - KeReleaseSpinLock(&HandleTable->ListLock, oldIrql); - - if ((Header->ObjectType != NULL) && - (Header->ObjectType->Close != NULL)) - { - Header->ObjectType->Close(Body, Header->HandleCount); - } - - *ObjectBody = Body; - - DPRINT("Finished ObDeleteHandle()\n"); - - return STATUS_SUCCESS; + ObjectHeader = EX_HTE_TO_HDR(HandleEntry); + Body = HEADER_TO_BODY(ObjectHeader); + + ObpDecrementHandleCount(Body); + + /* destroy and unlock the handle entry */ + ExDestroyHandleByEntry(Process->ObjectTable, + HandleEntry, + ExHandle); + + KeLeaveCriticalRegion(); + + return STATUS_SUCCESS; + } + KeLeaveCriticalRegion(); + return STATUS_INVALID_HANDLE; } @@ -626,94 +565,59 @@ ObCreateHandle(PEPROCESS Process, * NOTE: The handle is valid only in the context of the current process */ { - LIST_ENTRY* current; - unsigned int handle=1; - unsigned int Loop, Index, MaxIndex; - HANDLE_BLOCK* new_blk = NULL; - PHANDLE_TABLE HandleTable; - KIRQL oldlvl; + HANDLE_TABLE_ENTRY NewEntry; + POBJECT_HEADER ObjectHeader; + LONG ExHandle; PAGED_CODE(); DPRINT("ObCreateHandle(Process %x, obj %x)\n",Process,ObjectBody); ASSERT(Process); + ASSERT(ObjectBody); - if (ObjectBody != NULL) + ObjectHeader = BODY_TO_HEADER(ObjectBody); + + ASSERT((ULONG_PTR)ObjectHeader & EX_HANDLE_ENTRY_LOCKED); + + if (GrantedAccess & MAXIMUM_ALLOWED) { - InterlockedIncrement(&(BODY_TO_HEADER(ObjectBody)->HandleCount)); - } - HandleTable = &Process->HandleTable; - KeAcquireSpinLock(&HandleTable->ListLock, &oldlvl); - current = HandleTable->ListHead.Flink; - /* - * Scan through the currently allocated handle blocks looking for a free - * slot - */ - while (current != (&HandleTable->ListHead)) - { - HANDLE_BLOCK* blk = CONTAINING_RECORD(current,HANDLE_BLOCK,entry); - - DPRINT("Current %x\n",current); - - if (blk->allocation_count == HANDLE_BLOCK_ENTRIES) - { - handle = handle + HANDLE_BLOCK_ENTRIES; - current = current->Flink; - continue; - } - - Index = blk->allocation_hint; - MaxIndex = HANDLE_BLOCK_ENTRIES; - for (Loop = 0; Loop < 2; Loop++) - { - for (Index = 0; Index < MaxIndex; Index++) - { - DPRINT("Considering slot %d containing %x\n", Index, blk->handles[Index]); - if (blk->handles[Index].ObjectBody == NULL) - { - blk->handles[Index].ObjectBody = OB_POINTER_TO_ENTRY(ObjectBody); - if (Inherit) - blk->handles[Index].ObjectBody = (PVOID)((ULONG_PTR)blk->handles[Index].ObjectBody | OB_HANDLE_FLAG_INHERIT); - blk->handles[Index].GrantedAccess = GrantedAccess; - blk->allocation_hint = Index + 1; - blk->allocation_count++; - KeReleaseSpinLock(&HandleTable->ListLock, oldlvl); - *HandleReturn = (HANDLE)((handle + Index) << 2); - return(STATUS_SUCCESS); - } - } - Index = 0; - MaxIndex = blk->allocation_hint; - } - - handle = handle + HANDLE_BLOCK_ENTRIES; - current = current->Flink; + GrantedAccess &= ~MAXIMUM_ALLOWED; + GrantedAccess |= GENERIC_ALL; } - /* - * Add a new handle block to the end of the list - */ - new_blk = - (HANDLE_BLOCK *)ExAllocatePoolWithTag(NonPagedPool,sizeof(HANDLE_BLOCK), - TAG_HANDLE_TABLE); - if (!new_blk) - { - *HandleReturn = (PHANDLE)NULL; - return(STATUS_INSUFFICIENT_RESOURCES); - } - RtlZeroMemory(new_blk,sizeof(HANDLE_BLOCK)); - InsertTailList(&(Process->HandleTable.ListHead), - &new_blk->entry); - new_blk->handles[0].ObjectBody = OB_POINTER_TO_ENTRY(ObjectBody); - if (Inherit) - new_blk->handles[0].ObjectBody = (PVOID)((ULONG_PTR)new_blk->handles[0].ObjectBody | OB_HANDLE_FLAG_INHERIT); - new_blk->handles[0].GrantedAccess = GrantedAccess; - new_blk->allocation_hint = 1; - new_blk->allocation_count++; - KeReleaseSpinLock(&HandleTable->ListLock, oldlvl); - *HandleReturn = (HANDLE)(handle << 2); - return(STATUS_SUCCESS); + if (GrantedAccess & GENERIC_ANY) + { + RtlMapGenericMask(&GrantedAccess, + ObjectHeader->ObjectType->Mapping); + } + + NewEntry.u1.Object = ObjectHeader; + if(Inherit) + NewEntry.u1.ObAttributes |= EX_HANDLE_ENTRY_INHERITABLE; + else + NewEntry.u1.ObAttributes &= ~EX_HANDLE_ENTRY_INHERITABLE; + NewEntry.u2.GrantedAccess = GrantedAccess; + + ExHandle = ExCreateHandle(Process->ObjectTable, + &NewEntry); + DPRINT("ObCreateHandle(0x%x)==0x%x [HT:0x%x]\n", ObjectHeader, *HandleReturn, Process->ObjectTable); + if(ExHandle != EX_INVALID_HANDLE) + { + if(InterlockedIncrement(&ObjectHeader->HandleCount) == 1) + { + ObReferenceObjectByPointer(ObjectBody, + 0, + NULL, + UserMode); + } + + *HandleReturn = EX_HANDLE_TO_HANDLE(ExHandle); + + return STATUS_SUCCESS; + } + + return STATUS_UNSUCCESSFUL; } @@ -724,31 +628,35 @@ NTSTATUS STDCALL ObQueryObjectAuditingByHandle(IN HANDLE Handle, OUT PBOOLEAN GenerateOnClose) { + PHANDLE_TABLE_ENTRY HandleEntry; PEPROCESS Process; - KIRQL oldIrql; - PHANDLE_ENTRY HandleEntry; + LONG ExHandle = HANDLE_TO_EX_HANDLE(Handle); PAGED_CODE(); DPRINT("ObQueryObjectAuditingByHandle(Handle %x)\n", Handle); Process = PsGetCurrentProcess(); + + KeEnterCriticalRegion(); - KeAcquireSpinLock(&Process->HandleTable.ListLock, &oldIrql); - HandleEntry = ObpGetObjectByHandle(&Process->HandleTable, - Handle, - NULL); - if (HandleEntry == NULL) - { - KeReleaseSpinLock(&Process->HandleTable.ListLock, oldIrql); - return STATUS_INVALID_HANDLE; - } + HandleEntry = ExMapHandleToPointer(Process->ObjectTable, + ExHandle); + if(HandleEntry != NULL) + { + *GenerateOnClose = (HandleEntry->u1.ObAttributes & EX_HANDLE_ENTRY_AUDITONCLOSE) != 0; + + ExUnlockHandleTableEntry(Process->ObjectTable, + HandleEntry); - *GenerateOnClose = (BOOLEAN)((ULONG_PTR)HandleEntry->ObjectBody | OB_HANDLE_FLAG_AUDIT); - - KeReleaseSpinLock(&Process->HandleTable.ListLock, oldIrql); - - return STATUS_SUCCESS; + KeLeaveCriticalRegion(); + + return STATUS_SUCCESS; + } + + KeLeaveCriticalRegion(); + + return STATUS_INVALID_HANDLE; } @@ -775,13 +683,13 @@ ObReferenceObjectByHandle(HANDLE Handle, PVOID* Object, POBJECT_HANDLE_INFORMATION HandleInformation) { - PHANDLE_ENTRY HandleEntry; + PHANDLE_TABLE_ENTRY HandleEntry; POBJECT_HEADER ObjectHeader; - KIRQL oldIrql; PVOID ObjectBody; ACCESS_MASK GrantedAccess; ULONG Attributes; NTSTATUS Status; + LONG ExHandle = HANDLE_TO_EX_HANDLE(Handle); PAGED_CODE(); @@ -795,8 +703,6 @@ ObReferenceObjectByHandle(HANDLE Handle, if (Handle == NtCurrentProcess() && (ObjectType == PsProcessType || ObjectType == NULL)) { - DPRINT("Reference from %x\n", ((PULONG)&Handle)[-1]); - Status = ObReferenceObjectByPointer(PsGetCurrentProcess(), PROCESS_ALL_ACCESS, PsProcessType, @@ -808,7 +714,7 @@ ObReferenceObjectByHandle(HANDLE Handle, if (HandleInformation != NULL) { - HandleInformation->HandleAttributes = 0; /* FIXME? */ + HandleInformation->HandleAttributes = 0; HandleInformation->GrantedAccess = PROCESS_ALL_ACCESS; } @@ -836,7 +742,7 @@ ObReferenceObjectByHandle(HANDLE Handle, if (HandleInformation != NULL) { - HandleInformation->HandleAttributes = 0; /* FIXME? */ + HandleInformation->HandleAttributes = 0; HandleInformation->GrantedAccess = THREAD_ALL_ACCESS; } @@ -850,55 +756,76 @@ ObReferenceObjectByHandle(HANDLE Handle, return(STATUS_OBJECT_TYPE_MISMATCH); } - KeAcquireSpinLock(&PsGetCurrentProcess()->HandleTable.ListLock, - &oldIrql); - HandleEntry = ObpGetObjectByHandle(&PsGetCurrentProcess()->HandleTable, - Handle, - NULL); - if (HandleEntry == NULL || HandleEntry->ObjectBody == 0) + /* desire as much access rights as possible */ + if (DesiredAccess & MAXIMUM_ALLOWED) { - KeReleaseSpinLock(&PsGetCurrentProcess()->HandleTable.ListLock, - oldIrql); - return(STATUS_INVALID_HANDLE); + DesiredAccess &= ~MAXIMUM_ALLOWED; + DesiredAccess |= GENERIC_ALL; } - ObjectBody = OB_ENTRY_TO_POINTER(HandleEntry->ObjectBody); - DPRINT("ObjectBody %p\n",ObjectBody); - ObjectHeader = BODY_TO_HEADER(ObjectBody); - DPRINT("ObjectHeader->RefCount %lu\n",ObjectHeader->RefCount); + + KeEnterCriticalRegion(); + + HandleEntry = ExMapHandleToPointer(PsGetCurrentProcess()->ObjectTable, + ExHandle); + if (HandleEntry == NULL) + { + KeLeaveCriticalRegion(); + DPRINT("ExMapHandleToPointer() failed for handle 0x%x\n", Handle); + return(STATUS_INVALID_HANDLE); + } + + ObjectHeader = EX_HTE_TO_HDR(HandleEntry); + ObjectBody = HEADER_TO_BODY(ObjectHeader); + + DPRINT("locked1: ObjectHeader: 0x%x [HT:0x%x]\n", ObjectHeader, PsGetCurrentProcess()->ObjectTable); + + if (ObjectType != NULL && ObjectType != ObjectHeader->ObjectType) + { + DPRINT("ObjectType mismatch: %wZ vs %wZ (handle 0x%x)\n", &ObjectType->TypeName, ObjectHeader->ObjectType ? &ObjectHeader->ObjectType->TypeName : NULL, Handle); + + ExUnlockHandleTableEntry(PsGetCurrentProcess()->ObjectTable, + HandleEntry); + + KeLeaveCriticalRegion(); + + return(STATUS_OBJECT_TYPE_MISMATCH); + } + + /* map the generic access masks if the caller asks for generic access */ + if (DesiredAccess & GENERIC_ANY) + { + RtlMapGenericMask(&DesiredAccess, + BODY_TO_HEADER(ObjectBody)->ObjectType->Mapping); + } + + GrantedAccess = HandleEntry->u2.GrantedAccess; + + /* Unless running as KernelMode, deny access if caller desires more access + rights than the handle can grant */ + if(AccessMode != KernelMode && (~GrantedAccess & DesiredAccess)) + { + ExUnlockHandleTableEntry(PsGetCurrentProcess()->ObjectTable, + HandleEntry); + + KeLeaveCriticalRegion(); + + DPRINT1("GrantedAccess: 0x%x, ~GrantedAccess: 0x%x, DesiredAccess: 0x%x, denied: 0x%x\n", GrantedAccess, ~GrantedAccess, DesiredAccess, ~GrantedAccess & DesiredAccess); + + return(STATUS_ACCESS_DENIED); + } + ObReferenceObjectByPointer(ObjectBody, 0, NULL, UserMode); - Attributes = (ULONG_PTR)HandleEntry->ObjectBody & OB_HANDLE_FLAG_MASK; - GrantedAccess = HandleEntry->GrantedAccess; - KeReleaseSpinLock(&PsGetCurrentProcess()->HandleTable.ListLock, - oldIrql); - - ObjectHeader = BODY_TO_HEADER(ObjectBody); - DPRINT("ObjectHeader->RefCount %lu\n",ObjectHeader->RefCount); + Attributes = HandleEntry->u1.ObAttributes & (EX_HANDLE_ENTRY_PROTECTFROMCLOSE | + EX_HANDLE_ENTRY_INHERITABLE | + EX_HANDLE_ENTRY_AUDITONCLOSE); - if (ObjectType != NULL && ObjectType != ObjectHeader->ObjectType) - { - CHECKPOINT; - return(STATUS_OBJECT_TYPE_MISMATCH); - } + ExUnlockHandleTableEntry(PsGetCurrentProcess()->ObjectTable, + HandleEntry); - if (ObjectHeader->ObjectType == PsProcessType) - { - DPRINT("Reference from %x\n", ((PULONG)&Handle)[-1]); - } - - if (DesiredAccess && AccessMode == UserMode) - { - RtlMapGenericMask(&DesiredAccess, ObjectHeader->ObjectType->Mapping); - - if (!(GrantedAccess & DesiredAccess) && - !((~GrantedAccess) & DesiredAccess)) - { - CHECKPOINT; - return(STATUS_ACCESS_DENIED); - } - } + KeLeaveCriticalRegion(); if (HandleInformation != NULL) { @@ -908,7 +835,6 @@ ObReferenceObjectByHandle(HANDLE Handle, *Object = ObjectBody; - CHECKPOINT; return(STATUS_SUCCESS); } @@ -932,17 +858,12 @@ ObReferenceObjectByHandle(HANDLE Handle, NTSTATUS STDCALL NtClose(IN HANDLE Handle) { - PVOID ObjectBody; - POBJECT_HEADER Header; NTSTATUS Status; PAGED_CODE(); - DPRINT("NtClose(Handle %x)\n",Handle); - Status = ObDeleteHandle(PsGetCurrentProcess(), - Handle, - &ObjectBody); + Handle); if (!NT_SUCCESS(Status)) { if(((PEPROCESS)(KeGetCurrentThread()->ApcState.Process))->ExceptionPort) @@ -950,11 +871,6 @@ NtClose(IN HANDLE Handle) return Status; } - Header = BODY_TO_HEADER(ObjectBody); - - DPRINT("Dereferencing %x\n", ObjectBody); - ObDereferenceObject(ObjectBody); - return(STATUS_SUCCESS); } @@ -978,9 +894,6 @@ ObInsertObject(IN PVOID Object, Access = DesiredAccess; ObjectHeader = BODY_TO_HEADER(Object); - RtlMapGenericMask(&Access, - ObjectHeader->ObjectType->Mapping); - return(ObCreateHandle(PsGetCurrentProcess(), Object, Access, @@ -992,42 +905,7 @@ ObInsertObject(IN PVOID Object, ULONG ObpGetHandleCountByHandleTable(PHANDLE_TABLE HandleTable) { - PHANDLE_BLOCK blk; - POBJECT_HEADER Header; - PVOID ObjectBody; - KIRQL OldIrql; - PLIST_ENTRY current; - ULONG i; - ULONG Count=0; - - KeAcquireSpinLock(&HandleTable->ListLock, &OldIrql); - - current = HandleTable->ListHead.Flink; - while (current != &HandleTable->ListHead) - { - blk = CONTAINING_RECORD(current, HANDLE_BLOCK, entry); - - for (i = 0; i < HANDLE_BLOCK_ENTRIES; i++) - { - ObjectBody = OB_ENTRY_TO_POINTER(blk->handles[i].ObjectBody); - if (ObjectBody != NULL) - { - Header = BODY_TO_HEADER(ObjectBody); - - /* Make sure this is real. Okay! For real!*/ - if ((Header->ObjectType != NULL) && - (Header->ObjectType->Close != NULL)) - Count++; - } - } - - current = current->Flink; - } - - KeReleaseSpinLock(&HandleTable->ListLock, - OldIrql); - - return Count; + return HandleTable->HandleCount; } /* @@ -1085,7 +963,7 @@ ObpGetNextHandleByProcessCount(PSYSTEM_HANDLE_TABLE_ENTRY_INFO pshi, int Count) { ULONG P; - KIRQL oldIrql; +// KIRQL oldIrql; // pshi->HandleValue; @@ -1097,14 +975,14 @@ ObpGetNextHandleByProcessCount(PSYSTEM_HANDLE_TABLE_ENTRY_INFO pshi, P = (ULONG) Process->UniqueProcessId; pshi->UniqueProcessId = (USHORT) P; - KeAcquireSpinLock( &Process->HandleTable.ListLock, &oldIrql ); +// KeAcquireSpinLock( &Process->HandleTable.ListLock, &oldIrql ); // pshi->GrantedAccess; // pshi->Object; // pshi->ObjectTypeIndex; // pshi->HandleAttributes; - KeReleaseSpinLock( &Process->HandleTable.ListLock, oldIrql ); +// KeReleaseSpinLock( &Process->HandleTable.ListLock, oldIrql ); return; } diff --git a/reactos/ntoskrnl/ob/object.c b/reactos/ntoskrnl/ob/object.c index f54fbf62674..44a69c141e9 100644 --- a/reactos/ntoskrnl/ob/object.c +++ b/reactos/ntoskrnl/ob/object.c @@ -25,18 +25,6 @@ typedef struct _RETENTION_CHECK_PARAMS /* FUNCTIONS ************************************************************/ -PVOID HEADER_TO_BODY(POBJECT_HEADER obj) -{ - return(((char*)obj)+sizeof(OBJECT_HEADER)-sizeof(COMMON_BODY_HEADER)); -} - - -POBJECT_HEADER BODY_TO_HEADER(PVOID body) -{ - PCOMMON_BODY_HEADER chdr = (PCOMMON_BODY_HEADER)body; - return(CONTAINING_RECORD((&(chdr->Type)),OBJECT_HEADER,Type)); -} - NTSTATUS ObpCaptureObjectAttributes(IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN KPROCESSOR_MODE AccessMode, @@ -102,6 +90,7 @@ ObpCaptureObjectAttributes(IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, CapturedObjectAttributes->RootDirectory = ObjectAttributes->RootDirectory; CapturedObjectAttributes->Attributes = ObjectAttributes->Attributes; CapturedObjectAttributes->SecurityDescriptor = ObjectAttributes->SecurityDescriptor; + CapturedObjectAttributes->SecurityQualityOfService = ObjectAttributes->SecurityQualityOfService; } return STATUS_SUCCESS; @@ -147,6 +136,53 @@ ObpCaptureObjectAttributes(IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, { CapturedObjectAttributes->SecurityDescriptor = NULL; } + + if(AttributesCopy.SecurityQualityOfService != NULL) + { + SECURITY_QUALITY_OF_SERVICE SafeQoS; + + _SEH_TRY + { + ProbeForRead(AttributesCopy.SecurityQualityOfService, + sizeof(SECURITY_QUALITY_OF_SERVICE), + sizeof(ULONG)); + SafeQoS = *(PSECURITY_QUALITY_OF_SERVICE)AttributesCopy.SecurityQualityOfService; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + DPRINT1("Unable to capture QoS!!!\n"); + goto failcleanupsdescriptor; + } + + if(SafeQoS.Length != sizeof(SECURITY_QUALITY_OF_SERVICE)) + { + DPRINT1("Unable to capture QoS, wrong size!!!\n"); + Status = STATUS_INVALID_PARAMETER; + goto failcleanupsdescriptor; + } + + CapturedObjectAttributes->SecurityQualityOfService = ExAllocatePool(PoolType, + sizeof(SECURITY_QUALITY_OF_SERVICE)); + if(CapturedObjectAttributes->SecurityQualityOfService != NULL) + { + *CapturedObjectAttributes->SecurityQualityOfService = SafeQoS; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto failcleanupsdescriptor; + } + } + else + { + CapturedObjectAttributes->SecurityQualityOfService = NULL; + } } if(ObjectName != NULL) @@ -259,6 +295,8 @@ ObpCaptureObjectAttributes(IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, { ExFreePool(ObjectName->Buffer); } + +failcleanupsdescriptor: if(CapturedObjectAttributes != NULL) { /* cleanup allocated resources */ @@ -293,11 +331,18 @@ ObpReleaseObjectAttributes(IN PCAPTURED_OBJECT_ATTRIBUTES CapturedObjectAttribut to ObpCaptureObjectAttributes() to avoid memory leaks */ if(AccessMode != KernelMode || CaptureIfKernel) { - if(CapturedObjectAttributes != NULL && - CapturedObjectAttributes->SecurityDescriptor != NULL) + if(CapturedObjectAttributes != NULL) { - ExFreePool(CapturedObjectAttributes->SecurityDescriptor); - CapturedObjectAttributes->SecurityDescriptor = NULL; + if(CapturedObjectAttributes->SecurityDescriptor != NULL) + { + ExFreePool(CapturedObjectAttributes->SecurityDescriptor); + CapturedObjectAttributes->SecurityDescriptor = NULL; + } + if(CapturedObjectAttributes->SecurityQualityOfService != NULL) + { + ExFreePool(CapturedObjectAttributes->SecurityQualityOfService); + CapturedObjectAttributes->SecurityQualityOfService = NULL; + } } if(ObjectName != NULL && ObjectName->Length > 0) @@ -367,7 +412,7 @@ ObFindObject(POBJECT_ATTRIBUTES ObjectAttributes, else { Status = ObReferenceObjectByHandle(ObjectAttributes->RootDirectory, - DIRECTORY_TRAVERSE, + 0, NULL, UserMode, &CurrentObject, @@ -677,7 +722,7 @@ ObCreateObject (IN KPROCESSOR_MODE ObjectAttributesAccessMode OPTIONAL, RtlZeroMemory(Header, OBJECT_ALLOC_SIZE(ObjectSize)); /* Initialize the object header */ - DPRINT("Initalizing header\n"); + DPRINT("Initalizing header 0x%x (%wZ)\n", Header, &Type->TypeName); Header->HandleCount = 0; Header->RefCount = 1; Header->ObjectType = Type; @@ -849,7 +894,7 @@ ObReferenceObjectByPointer(IN PVOID Object, DPRINT("eip %x\n", ((PULONG)&Object)[-1]); } - if (Header->CloseInProcess) + if (Header->RefCount == 0 && !Header->Permanent) { if (Header->ObjectType == PsProcessType) { @@ -862,7 +907,10 @@ ObReferenceObjectByPointer(IN PVOID Object, return(STATUS_UNSUCCESSFUL); } - InterlockedIncrement(&Header->RefCount); + if (1 == InterlockedIncrement(&Header->RefCount) && !Header->Permanent) + { + KEBUGCHECK(0); + } return(STATUS_SUCCESS); } @@ -978,12 +1026,6 @@ ObpDeleteObjectDpcLevel(IN POBJECT_HEADER ObjectHeader, KEBUGCHECK(0); } - if (ObjectHeader->CloseInProcess) - { - KEBUGCHECK(0); - return STATUS_UNSUCCESSFUL; - } - ObjectHeader->CloseInProcess = TRUE; switch (KeGetCurrentIrql ()) { @@ -1049,12 +1091,11 @@ ObfReferenceObject(IN PVOID Object) Header = BODY_TO_HEADER(Object); /* No one should be referencing an object once we are deleting it. */ - if (Header->CloseInProcess) - { - KEBUGCHECK(0); - } + if (InterlockedIncrement(&Header->RefCount) == 1 && !Header->Permanent) + { + KEBUGCHECK(0); + } - (VOID)InterlockedIncrement(&Header->RefCount); } @@ -1080,25 +1121,23 @@ ObfDereferenceObject(IN PVOID Object) POBJECT_HEADER Header; LONG NewRefCount; BOOL Permanent; - ULONG HandleCount; ASSERT(Object); /* Extract the object header. */ Header = BODY_TO_HEADER(Object); Permanent = Header->Permanent; - HandleCount = Header->HandleCount; /* Drop our reference and get the new count so we can tell if this was the last reference. */ NewRefCount = InterlockedDecrement(&Header->RefCount); + DPRINT("ObfDereferenceObject(0x%x)==%d (%wZ)\n", Object, NewRefCount, &Header->ObjectType->TypeName); ASSERT(NewRefCount >= 0); /* Check whether the object can now be deleted. */ if (NewRefCount == 0 && - HandleCount == 0 && !Permanent) { ObpDeleteObjectDpcLevel(Header, NewRefCount); diff --git a/reactos/ntoskrnl/ob/wait.c b/reactos/ntoskrnl/ob/wait.c new file mode 100644 index 00000000000..1b693c6ba6b --- /dev/null +++ b/reactos/ntoskrnl/ob/wait.c @@ -0,0 +1,300 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/ob/wait.c + * PURPOSE: Handles Waiting on Objects + * + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Created file + * David Welch (welch@mcmail.com) + */ + +/* INCLUDES ******************************************************************/ + +#include +#define NDEBUG +#include + +/* FUNCTIONS *****************************************************************/ + +BOOL inline FASTCALL KiIsObjectWaitable(PVOID Object); + +NTSTATUS STDCALL +NtWaitForMultipleObjects(IN ULONG ObjectCount, + IN PHANDLE ObjectsArray, + IN WAIT_TYPE WaitType, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER TimeOut OPTIONAL) +{ + KWAIT_BLOCK WaitBlockArray[MAXIMUM_WAIT_OBJECTS]; + HANDLE SafeObjectsArray[MAXIMUM_WAIT_OBJECTS]; + PVOID ObjectPtrArray[MAXIMUM_WAIT_OBJECTS]; + ULONG i, j; + KPROCESSOR_MODE PreviousMode; + LARGE_INTEGER SafeTimeOut; + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("NtWaitForMultipleObjects(ObjectCount %lu ObjectsArray[] %x, Alertable %d, " + "TimeOut %x)\n", ObjectCount,ObjectsArray,Alertable,TimeOut); + + PreviousMode = ExGetPreviousMode(); + + if (ObjectCount > MAXIMUM_WAIT_OBJECTS) + return STATUS_UNSUCCESSFUL; + if (0 == ObjectCount) + return STATUS_INVALID_PARAMETER; + + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForRead(ObjectsArray, + ObjectCount * sizeof(ObjectsArray[0]), + sizeof(ULONG)); + /* make a copy so we don't have to guard with SEH later and keep track of + what objects we referenced in case dereferencing pointers suddenly fails */ + RtlCopyMemory(SafeObjectsArray, ObjectsArray, ObjectCount * sizeof(ObjectsArray[0])); + ObjectsArray = SafeObjectsArray; + + if(TimeOut != NULL) + { + ProbeForRead(TimeOut, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + /* make a local copy of the timeout on the stack */ + SafeTimeOut = *TimeOut; + TimeOut = &SafeTimeOut; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + + /* reference all objects */ + for (i = 0; i < ObjectCount; i++) + { + Status = ObReferenceObjectByHandle(ObjectsArray[i], + SYNCHRONIZE, + NULL, + PreviousMode, + &ObjectPtrArray[i], + NULL); + if (!NT_SUCCESS(Status) || !KiIsObjectWaitable(ObjectPtrArray[i])) + { + if (NT_SUCCESS(Status)) + { + DPRINT1("Waiting for object type '%wZ' is not supported\n", + &BODY_TO_HEADER(ObjectPtrArray[i])->ObjectType->TypeName); + Status = STATUS_HANDLE_NOT_WAITABLE; + i++; + } + /* dereference all referenced objects */ + for (j = 0; j < i; j++) + { + ObDereferenceObject(ObjectPtrArray[j]); + } + + return(Status); + } + } + + Status = KeWaitForMultipleObjects(ObjectCount, + ObjectPtrArray, + WaitType, + UserRequest, + PreviousMode, + Alertable, + TimeOut, + WaitBlockArray); + + /* dereference all objects */ + for (i = 0; i < ObjectCount; i++) + { + ObDereferenceObject(ObjectPtrArray[i]); + } + + return(Status); +} + + +/* + * @implemented + */ +NTSTATUS STDCALL +NtWaitForSingleObject(IN HANDLE ObjectHandle, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER TimeOut OPTIONAL) +{ + PVOID ObjectPtr; + KPROCESSOR_MODE PreviousMode; + LARGE_INTEGER SafeTimeOut; + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("NtWaitForSingleObject(ObjectHandle %x, Alertable %d, TimeOut %x)\n", + ObjectHandle,Alertable,TimeOut); + + PreviousMode = ExGetPreviousMode(); + + if(TimeOut != NULL && PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForRead(TimeOut, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + /* make a copy on the stack */ + SafeTimeOut = *TimeOut; + TimeOut = &SafeTimeOut; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + + Status = ObReferenceObjectByHandle(ObjectHandle, + SYNCHRONIZE, + NULL, + PreviousMode, + &ObjectPtr, + NULL); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + if (!KiIsObjectWaitable(ObjectPtr)) + { + DPRINT1("Waiting for object type '%wZ' is not supported\n", + &BODY_TO_HEADER(ObjectPtr)->ObjectType->TypeName); + Status = STATUS_HANDLE_NOT_WAITABLE; + } + else + { + Status = KeWaitForSingleObject(ObjectPtr, + UserRequest, + PreviousMode, + Alertable, + TimeOut); + } + + ObDereferenceObject(ObjectPtr); + + return(Status); +} + + +NTSTATUS STDCALL +NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal, + IN HANDLE WaitableObjectHandle, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER TimeOut OPTIONAL) +{ + KPROCESSOR_MODE PreviousMode; + DISPATCHER_HEADER* hdr; + PVOID SignalObj; + PVOID WaitObj; + LARGE_INTEGER SafeTimeOut; + NTSTATUS Status = STATUS_SUCCESS; + + PreviousMode = ExGetPreviousMode(); + + if(TimeOut != NULL && PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForRead(TimeOut, + sizeof(LARGE_INTEGER), + sizeof(ULONG)); + /* make a copy on the stack */ + SafeTimeOut = *TimeOut; + TimeOut = &SafeTimeOut; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + + Status = ObReferenceObjectByHandle(ObjectHandleToSignal, + 0, + NULL, + PreviousMode, + &SignalObj, + NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + Status = ObReferenceObjectByHandle(WaitableObjectHandle, + SYNCHRONIZE, + NULL, + PreviousMode, + &WaitObj, + NULL); + if (!NT_SUCCESS(Status)) + { + ObDereferenceObject(SignalObj); + return Status; + } + + hdr = (DISPATCHER_HEADER *)SignalObj; + switch (hdr->Type) + { + case EventNotificationObject: + case EventSynchronizationObject: + KeSetEvent(SignalObj, + EVENT_INCREMENT, + TRUE); + break; + + case MutantObject: + KeReleaseMutex(SignalObj, + TRUE); + break; + + case SemaphoreObject: + KeReleaseSemaphore(SignalObj, + SEMAPHORE_INCREMENT, + 1, + TRUE); + break; + + default: + ObDereferenceObject(SignalObj); + ObDereferenceObject(WaitObj); + return STATUS_OBJECT_TYPE_MISMATCH; + } + + Status = KeWaitForSingleObject(WaitObj, + UserRequest, + PreviousMode, + Alertable, + TimeOut); + + ObDereferenceObject(SignalObj); + ObDereferenceObject(WaitObj); + + return Status; +} + +/* EOF */ diff --git a/reactos/ntoskrnl/po/power.c b/reactos/ntoskrnl/po/power.c index b478b9cc3c8..2dc711b7071 100644 --- a/reactos/ntoskrnl/po/power.c +++ b/reactos/ntoskrnl/po/power.c @@ -12,8 +12,8 @@ #define NDEBUG #include - PDEVICE_NODE PopSystemPowerDeviceNode = NULL; +BOOLEAN PopAcpiPresent = FALSE; /* * @implemented @@ -133,10 +133,7 @@ PoUnregisterSystemState( NTSTATUS PopSetSystemPowerState( SYSTEM_POWER_STATE PowerState) -{ - -#ifdef ACPI - +{ IO_STATUS_BLOCK IoStatusBlock; PDEVICE_OBJECT DeviceObject; PIO_STACK_LOCATION IrpSp; @@ -144,6 +141,8 @@ PopSetSystemPowerState( NTSTATUS Status; KEVENT Event; PIRP Irp; + + if (!PopAcpiPresent) return STATUS_NOT_IMPLEMENTED; Status = IopGetSystemPowerDeviceObject(&DeviceObject); if (!NT_SUCCESS(Status)) { @@ -190,15 +189,23 @@ PopSetSystemPowerState( ObDereferenceObject(Fdo); return Status; - -#endif /* ACPI */ - - return STATUS_NOT_IMPLEMENTED; } -VOID INIT_FUNCTION -PoInit(VOID) +VOID +INIT_FUNCTION +PoInit(PLOADER_PARAMETER_BLOCK LoaderBlock, + BOOLEAN ForceAcpiDisable) { + if (ForceAcpiDisable) + { + /* Set the ACPI State to False if it's been forced that way */ + PopAcpiPresent = FALSE; + } + else + { + /* Otherwise check the LoaderBlock's Flag */ + PopAcpiPresent = (LoaderBlock->Flags & MB_FLAGS_ACPI_TABLE) ? TRUE : FALSE; + } } /* @@ -268,4 +275,19 @@ NtPowerInformation( return Status; } + +NTSTATUS +STDCALL +PoQueueShutdownWorkItem( + IN PWORK_QUEUE_ITEM WorkItem + ) +{ + PAGED_CODE(); + + DPRINT1("PoQueueShutdownWorkItem(%p)\n", WorkItem); + + return STATUS_NOT_IMPLEMENTED; +} + + /* EOF */ diff --git a/reactos/ntoskrnl/ps/cid.c b/reactos/ntoskrnl/ps/cid.c index e38842500f5..eece7f4a4cd 100644 --- a/reactos/ntoskrnl/ps/cid.c +++ b/reactos/ntoskrnl/ps/cid.c @@ -16,147 +16,188 @@ /* GLOBALS ******************************************************************/ -/* - * FIXME - use a global handle table instead! - */ - -KSPIN_LOCK CidLock; -LIST_ENTRY CidHead; -KEVENT CidReleaseEvent; -LONG CidCounter = 0; -LARGE_INTEGER ShortDelay, LongDelay; +PHANDLE_TABLE PspCidTable = NULL; #define TAG_CIDOBJECT TAG('C', 'I', 'D', 'O') +#define CID_FLAG_PROCESS 0x1 +#define CID_FLAG_THREAD 0x2 +#define CID_FLAGS_MASK (CID_FLAG_PROCESS | CID_FLAG_THREAD) + /* FUNCTIONS *****************************************************************/ VOID INIT_FUNCTION PsInitClientIDManagment(VOID) { - InitializeListHead(&CidHead); - KeInitializeSpinLock(&CidLock); - KeInitializeEvent(&CidReleaseEvent, SynchronizationEvent, FALSE); - ShortDelay.QuadPart = -100LL; - LongDelay.QuadPart = -100000LL; -} - -VOID -PspReferenceCidObject(PCID_OBJECT Object) -{ - InterlockedIncrement(&Object->ref); -} - -VOID -PspDereferenceCidObject(PCID_OBJECT Object) -{ - if(InterlockedDecrement(&Object->ref) == 0) - { - ExFreePool(Object); - } + PspCidTable = ExCreateHandleTable(NULL); + ASSERT(PspCidTable); } NTSTATUS PsCreateCidHandle(PVOID Object, POBJECT_TYPE ObjectType, PHANDLE Handle) { - KIRQL oldIrql; - PCID_OBJECT cido = ExAllocatePoolWithTag(NonPagedPool, - sizeof(CID_OBJECT), - TAG_CIDOBJECT); - if(cido != NULL) + HANDLE_TABLE_ENTRY NewEntry; + LONG ExHandle; + + PAGED_CODE(); + + NewEntry.u1.Object = Object; + if(ObjectType == PsThreadType) + NewEntry.u2.GrantedAccess = CID_FLAG_THREAD; + else if(ObjectType == PsProcessType) + NewEntry.u2.GrantedAccess = CID_FLAG_PROCESS; + else { - cido->ref = 1; - ExInitializeFastMutex(&cido->Lock); - cido->Obj.Object = Object; - - KeAcquireSpinLock(&CidLock, &oldIrql); - cido->Handle = (HANDLE)((ULONG_PTR)(++CidCounter) << 2); - InsertTailList(&CidHead, &cido->Entry); - KeReleaseSpinLock(&CidLock, oldIrql); - - *Handle = cido->Handle; + DPRINT1("Can't create CID handles for %wZ objects\n", &ObjectType->TypeName); + KEBUGCHECK(0); + } + + ExHandle = ExCreateHandle(PspCidTable, + &NewEntry); + if(ExHandle != EX_INVALID_HANDLE) + { + *Handle = EX_HANDLE_TO_HANDLE(ExHandle); return STATUS_SUCCESS; } - return STATUS_INSUFFICIENT_RESOURCES; + return STATUS_UNSUCCESSFUL; } NTSTATUS PsDeleteCidHandle(HANDLE CidHandle, POBJECT_TYPE ObjectType) { - PCID_OBJECT cido, Found = NULL; - PLIST_ENTRY Current; - KIRQL oldIrql; + PHANDLE_TABLE_ENTRY Entry; + LONG ExHandle = HANDLE_TO_EX_HANDLE(CidHandle); - if(CidHandle == NULL) - { - return STATUS_INVALID_PARAMETER; - } - - KeAcquireSpinLock(&CidLock, &oldIrql); - Current = CidHead.Flink; - while(Current != &CidHead) - { - cido = CONTAINING_RECORD(Current, CID_OBJECT, Entry); - if(cido->Handle == CidHandle) - { - RemoveEntryList(&cido->Entry); - cido->Handle = NULL; - Found = cido; - break; - } - Current = Current->Flink; - } - KeReleaseSpinLock(&CidLock, oldIrql); + PAGED_CODE(); - if(Found != NULL) + Entry = ExMapHandleToPointer(PspCidTable, + ExHandle); + if(Entry != NULL) { - PspDereferenceCidObject(Found); + if((ObjectType == PsThreadType && ((Entry->u2.GrantedAccess & CID_FLAGS_MASK) == CID_FLAG_THREAD)) || + (ObjectType == PsProcessType && ((Entry->u2.GrantedAccess & CID_FLAGS_MASK) == CID_FLAG_PROCESS))) + { + ExDestroyHandleByEntry(PspCidTable, + Entry, + ExHandle); + return STATUS_SUCCESS; + } + else + { + ExUnlockHandleTableEntry(PspCidTable, + Entry); + return STATUS_OBJECT_TYPE_MISMATCH; + } + } + + return STATUS_INVALID_HANDLE; +} + +PHANDLE_TABLE_ENTRY +PsLookupCidHandle(HANDLE CidHandle, POBJECT_TYPE ObjectType, PVOID *Object) +{ + PHANDLE_TABLE_ENTRY Entry; + + PAGED_CODE(); + + KeEnterCriticalRegion(); + + Entry = ExMapHandleToPointer(PspCidTable, + HANDLE_TO_EX_HANDLE(CidHandle)); + if(Entry != NULL) + { + if((ObjectType == PsProcessType && ((Entry->u2.GrantedAccess & CID_FLAGS_MASK) == CID_FLAG_PROCESS)) || + (ObjectType == PsThreadType && ((Entry->u2.GrantedAccess & CID_FLAGS_MASK) == CID_FLAG_THREAD))) + { + *Object = Entry->u1.Object; + return Entry; + } + else + { + DPRINT1("CID Obj type mismatch handle 0x%x %wZ vs 0x%x\n", CidHandle, + &ObjectType->TypeName, Entry->u2.GrantedAccess); + ExUnlockHandleTableEntry(PspCidTable, + Entry); + } + } + + KeLeaveCriticalRegion(); + + return NULL; +} + +/* + * @implemented + */ +NTSTATUS STDCALL +PsLookupProcessThreadByCid(IN PCLIENT_ID Cid, + OUT PEPROCESS *Process OPTIONAL, + OUT PETHREAD *Thread) +{ + PHANDLE_TABLE_ENTRY CidEntry; + PETHREAD FoundThread; + + PAGED_CODE(); + + ASSERT(Thread); + ASSERT(Cid); + + CidEntry = PsLookupCidHandle(Cid->UniqueThread, PsThreadType, (PVOID*)&FoundThread); + if(CidEntry != NULL) + { + ObReferenceObject(FoundThread); + + PsUnlockCidHandle(CidEntry); + + if(Process != NULL) + { + *Process = FoundThread->ThreadsProcess; + } + *Thread = FoundThread; return STATUS_SUCCESS; } - return STATUS_UNSUCCESSFUL; + return STATUS_INVALID_PARAMETER; } -PCID_OBJECT -PsLockCidHandle(HANDLE CidHandle, POBJECT_TYPE ObjectType) + +/* + * @implemented + */ +NTSTATUS STDCALL +PsLookupThreadByThreadId(IN HANDLE ThreadId, + OUT PETHREAD *Thread) { - PCID_OBJECT cido, Found = NULL; - PLIST_ENTRY Current; - KIRQL oldIrql; - - if(CidHandle == NULL) + PHANDLE_TABLE_ENTRY CidEntry; + PETHREAD FoundThread; + + PAGED_CODE(); + + ASSERT(Thread); + + CidEntry = PsLookupCidHandle(ThreadId, PsThreadType, (PVOID*)&FoundThread); + if(CidEntry != NULL) { - return NULL; - } - - KeAcquireSpinLock(&CidLock, &oldIrql); - Current = CidHead.Flink; - while(Current != &CidHead) - { - cido = CONTAINING_RECORD(Current, CID_OBJECT, Entry); - if(cido->Handle == CidHandle) - { - Found = cido; - PspReferenceCidObject(Found); - break; - } - Current = Current->Flink; - } - KeReleaseSpinLock(&CidLock, oldIrql); - - if(Found != NULL) - { - ExAcquireFastMutex(&Found->Lock); + ObReferenceObject(FoundThread); + + PsUnlockCidHandle(CidEntry); + + *Thread = FoundThread; + return STATUS_SUCCESS; } - return Found; + return STATUS_INVALID_PARAMETER; } VOID -PsUnlockCidObject(PCID_OBJECT CidObject) +PsUnlockCidHandle(PHANDLE_TABLE_ENTRY CidEntry) { - ExReleaseFastMutex(&CidObject->Lock); - PspDereferenceCidObject(CidObject); + PAGED_CODE(); + + ExUnlockHandleTableEntry(PspCidTable, + CidEntry); + KeLeaveCriticalRegion(); } /* EOF */ diff --git a/reactos/ntoskrnl/ps/create.c b/reactos/ntoskrnl/ps/create.c index 8c85ab4e3ef..957f6a4bd87 100644 --- a/reactos/ntoskrnl/ps/create.c +++ b/reactos/ntoskrnl/ps/create.c @@ -4,7 +4,7 @@ * PROJECT: ReactOS kernel * FILE: ntoskrnl/ps/create.c * PURPOSE: Thread managment - * + * * PROGRAMMERS: David Welch (welch@mcmail.com) * Phillip Susi * Skywing @@ -12,10 +12,10 @@ /* * NOTE: - * + * * All of the routines that manipulate the thread queue synchronize on * a single spinlock - * + * */ /* INCLUDES ****************************************************************/ @@ -27,802 +27,489 @@ /* GLOBAL *******************************************************************/ #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8 +#define TAG_KAPC TAG('k','p','a','p') /* kpap - kernel ps apc */ static ULONG PiThreadNotifyRoutineCount = 0; static PCREATE_THREAD_NOTIFY_ROUTINE PiThreadNotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT]; +ULONG +STDCALL +KeSuspendThread(PKTHREAD Thread); /* FUNCTIONS ***************************************************************/ -/* - * @implemented - */ -NTSTATUS STDCALL -PsAssignImpersonationToken(PETHREAD Thread, - HANDLE TokenHandle) -{ - PACCESS_TOKEN Token; - SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; - NTSTATUS Status; - - if (TokenHandle != NULL) - { - Status = ObReferenceObjectByHandle(TokenHandle, - TOKEN_IMPERSONATE, - SepTokenObjectType, - KeGetPreviousMode(), - (PVOID*)&Token, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - ImpersonationLevel = SeTokenImpersonationLevel(Token); - } - else - { - Token = NULL; - ImpersonationLevel = 0; - } - - PsImpersonateClient(Thread, - Token, - FALSE, - FALSE, - ImpersonationLevel); - if (Token != NULL) - { - ObDereferenceObject(Token); - } - - return(STATUS_SUCCESS); -} - - -/* - * @implemented - */ -VOID STDCALL -PsRevertToSelf (VOID) -{ - PsRevertThreadToSelf(PsGetCurrentThread()); -} - -/* - * @implemented - */ -VOID -STDCALL -PsRevertThreadToSelf( - IN PETHREAD Thread - ) -{ - if (Thread->ActiveImpersonationInfo == TRUE) - { - ObDereferenceObject (Thread->ImpersonationInfo->Token); - Thread->ActiveImpersonationInfo = FALSE; - } -} - -/* - * @implemented - */ -VOID STDCALL -PsImpersonateClient (IN PETHREAD Thread, - IN PACCESS_TOKEN Token, - IN BOOLEAN CopyOnOpen, - IN BOOLEAN EffectiveOnly, - IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) -{ - if (Token == NULL) - { - if (Thread->ActiveImpersonationInfo == TRUE) - { - Thread->ActiveImpersonationInfo = FALSE; - if (Thread->ImpersonationInfo->Token != NULL) - { - ObDereferenceObject (Thread->ImpersonationInfo->Token); - } - } - return; - } - - if (Thread->ImpersonationInfo == NULL) - { - Thread->ImpersonationInfo = ExAllocatePool (NonPagedPool, - sizeof(PS_IMPERSONATION_INFORMATION)); - } - - Thread->ImpersonationInfo->ImpersonationLevel = ImpersonationLevel; - Thread->ImpersonationInfo->CopyOnOpen = CopyOnOpen; - Thread->ImpersonationInfo->EffectiveOnly = EffectiveOnly; - Thread->ImpersonationInfo->Token = Token; - ObReferenceObjectByPointer (Token, - 0, - SepTokenObjectType, - KernelMode); - Thread->ActiveImpersonationInfo = TRUE; -} - - -PACCESS_TOKEN -PsReferenceEffectiveToken(PETHREAD Thread, - PTOKEN_TYPE TokenType, - PBOOLEAN EffectiveOnly, - PSECURITY_IMPERSONATION_LEVEL Level) -{ - PEPROCESS Process; - PACCESS_TOKEN Token; - - if (Thread->ActiveImpersonationInfo == FALSE) - { - Process = Thread->ThreadsProcess; - *TokenType = TokenPrimary; - *EffectiveOnly = FALSE; - Token = Process->Token; - } - else - { - Token = Thread->ImpersonationInfo->Token; - *TokenType = TokenImpersonation; - *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly; - *Level = Thread->ImpersonationInfo->ImpersonationLevel; - } - return(Token); -} - - -NTSTATUS STDCALL -NtImpersonateThread(IN HANDLE ThreadHandle, - IN HANDLE ThreadToImpersonateHandle, - IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService) -{ - SECURITY_QUALITY_OF_SERVICE SafeServiceQoS; - SECURITY_CLIENT_CONTEXT ClientContext; - PETHREAD Thread; - PETHREAD ThreadToImpersonate; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForRead(SecurityQualityOfService, - sizeof(SECURITY_QUALITY_OF_SERVICE), - sizeof(ULONG)); - SafeServiceQoS = *SecurityQualityOfService; - SecurityQualityOfService = &SafeServiceQoS; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObReferenceObjectByHandle(ThreadHandle, - THREAD_IMPERSONATE, - PsThreadType, - PreviousMode, - (PVOID*)&Thread, - NULL); - if(NT_SUCCESS(Status)) - { - Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle, - THREAD_DIRECT_IMPERSONATION, - PsThreadType, - PreviousMode, - (PVOID*)&ThreadToImpersonate, - NULL); - if(NT_SUCCESS(Status)) - { - Status = SeCreateClientSecurity(ThreadToImpersonate, - SecurityQualityOfService, - 0, - &ClientContext); - if(NT_SUCCESS(Status)) - { - SeImpersonateClient(&ClientContext, - Thread); - if(ClientContext.ClientToken != NULL) - { - ObDereferenceObject (ClientContext.ClientToken); - } - } - - ObDereferenceObject(ThreadToImpersonate); - } - ObDereferenceObject(Thread); - } - - return Status; -} - -/* - * @implemented - */ -PACCESS_TOKEN STDCALL -PsReferenceImpersonationToken(IN PETHREAD Thread, - OUT PBOOLEAN CopyOnOpen, - OUT PBOOLEAN EffectiveOnly, - OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel) -{ - if (Thread->ActiveImpersonationInfo == FALSE) - { - return NULL; - } - - *ImpersonationLevel = Thread->ImpersonationInfo->ImpersonationLevel; - *CopyOnOpen = Thread->ImpersonationInfo->CopyOnOpen; - *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly; - ObReferenceObjectByPointer (Thread->ImpersonationInfo->Token, - TOKEN_ALL_ACCESS, - SepTokenObjectType, - KernelMode); - - return Thread->ImpersonationInfo->Token; -} - -#ifdef PsDereferencePrimaryToken -#undef PsDereferenceImpersonationToken -#endif -/* - * @implemented - */ -VOID -STDCALL -PsDereferenceImpersonationToken( - IN PACCESS_TOKEN ImpersonationToken - ) -{ - if (ImpersonationToken) { - ObDereferenceObject(ImpersonationToken); - } -} - -#ifdef PsDereferencePrimaryToken -#undef PsDereferencePrimaryToken -#endif -/* - * @implemented - */ -VOID -STDCALL -PsDereferencePrimaryToken( - IN PACCESS_TOKEN PrimaryToken - ) -{ - ObDereferenceObject(PrimaryToken); -} - -/* - * @implemented - */ -BOOLEAN -STDCALL -PsDisableImpersonation( - IN PETHREAD Thread, - IN PSE_IMPERSONATION_STATE ImpersonationState - ) -{ - if (Thread->ActiveImpersonationInfo == FALSE) - { - ImpersonationState->Token = NULL; - ImpersonationState->CopyOnOpen = FALSE; - ImpersonationState->EffectiveOnly = FALSE; - ImpersonationState->Level = 0; - return TRUE; - } - -/* FIXME */ -/* ExfAcquirePushLockExclusive(&Thread->ThreadLock); */ - - Thread->ActiveImpersonationInfo = FALSE; - ImpersonationState->Token = Thread->ImpersonationInfo->Token; - ImpersonationState->CopyOnOpen = Thread->ImpersonationInfo->CopyOnOpen; - ImpersonationState->EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly; - ImpersonationState->Level = Thread->ImpersonationInfo->ImpersonationLevel; - -/* FIXME */ -/* ExfReleasePushLock(&Thread->ThreadLock); */ - - return TRUE; -} - -/* - * @implemented - */ -VOID -STDCALL -PsRestoreImpersonation( - IN PETHREAD Thread, - IN PSE_IMPERSONATION_STATE ImpersonationState - ) -{ - PsImpersonateClient(Thread, ImpersonationState->Token, - ImpersonationState->CopyOnOpen, - ImpersonationState->EffectiveOnly, - ImpersonationState->Level); - ObfDereferenceObject(ImpersonationState->Token); -} - VOID PiBeforeBeginThread(CONTEXT c) { - KeLowerIrql(PASSIVE_LEVEL); + KeLowerIrql(PASSIVE_LEVEL); } - -VOID STDCALL -PiDeleteThread(PVOID ObjectBody) -{ - PETHREAD Thread; - PEPROCESS Process; - - Thread = (PETHREAD)ObjectBody; - - DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody); - - Process = Thread->ThreadsProcess; - Thread->ThreadsProcess = NULL; - - PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType); - - if(Thread->Tcb.Win32Thread != NULL) - { - /* Free the W32THREAD structure if present */ - ExFreePool (Thread->Tcb.Win32Thread); - } - - KeReleaseThread(ETHREAD_TO_KTHREAD(Thread)); - - ObDereferenceObject(Process); - - DPRINT("PiDeleteThread() finished\n"); -} - - NTSTATUS -PsInitializeThread(PEPROCESS Process, - PETHREAD* ThreadPtr, - PHANDLE ThreadHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ThreadAttributes, - BOOLEAN First) +PsInitializeThread ( + PEPROCESS Process, + PETHREAD* ThreadPtr, + POBJECT_ATTRIBUTES ObjectAttributes, + KPROCESSOR_MODE AccessMode, + BOOLEAN First ) { - PETHREAD Thread; - NTSTATUS Status; - KIRQL oldIrql; + PETHREAD Thread; + NTSTATUS Status; + KIRQL oldIrql; - if (Process == NULL) - { - Process = PsInitialSystemProcess; - } + PAGED_CODE(); - /* - * Reference process - */ - ObReferenceObjectByPointer(Process, - PROCESS_CREATE_THREAD, - PsProcessType, - KernelMode); - - /* + if (Process == NULL) + { + Process = PsInitialSystemProcess; + } + + /* * Create and initialize thread */ - Status = ObCreateObject(UserMode, - PsThreadType, - ThreadAttributes, - KernelMode, - NULL, - sizeof(ETHREAD), - 0, - 0, - (PVOID*)&Thread); - if (!NT_SUCCESS(Status)) - { - ObDereferenceObject (Process); - return(Status); - } - - /* create a client id handle */ - Status = PsCreateCidHandle(Thread, PsThreadType, &Thread->Cid.UniqueThread); - if (!NT_SUCCESS(Status)) + Status = ObCreateObject(AccessMode, + PsThreadType, + ObjectAttributes, + KernelMode, + NULL, + sizeof(ETHREAD), + 0, + 0, + (PVOID*)&Thread); + if (!NT_SUCCESS(Status)) { - ObDereferenceObject (Thread); - ObDereferenceObject (Process); - return Status; + return(Status); } - Thread->ThreadsProcess = Process; - Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId; - DPRINT("Thread = %x\n",Thread); + /* + * Reference process + */ + ObReferenceObjectByPointer(Process, + PROCESS_CREATE_THREAD, + PsProcessType, + KernelMode); - KeInitializeThread(&Process->Pcb, &Thread->Tcb, First); - InitializeListHead(&Thread->TerminationPortList); - KeInitializeSpinLock(&Thread->ActiveTimerListLock); - InitializeListHead(&Thread->IrpList); - Thread->DeadThread = FALSE; - Thread->HasTerminated = FALSE; - Thread->Tcb.Win32Thread = NULL; - DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread); - + Thread->ThreadsProcess = Process; + Thread->Cid.UniqueThread = NULL; + Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId; - Thread->Tcb.BasePriority = (CHAR)Process->Pcb.BasePriority; - Thread->Tcb.Priority = Thread->Tcb.BasePriority; + DPRINT("Thread = %x\n",Thread); - /* + KeInitializeThread(&Process->Pcb, &Thread->Tcb, First); + InitializeListHead(&Thread->ActiveTimerListHead); + KeInitializeSpinLock(&Thread->ActiveTimerListLock); + InitializeListHead(&Thread->IrpList); + Thread->DeadThread = FALSE; + Thread->HasTerminated = FALSE; + Thread->Tcb.Win32Thread = NULL; + DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread); + + + Thread->Tcb.BasePriority = (CHAR)Process->Pcb.BasePriority; + Thread->Tcb.Priority = Thread->Tcb.BasePriority; + + /* * Local Procedure Call facility (LPC) */ - KeInitializeSemaphore (& Thread->LpcReplySemaphore, 0, LONG_MAX); - Thread->LpcReplyMessage = NULL; - Thread->LpcReplyMessageId = 0; /* not valid */ - /* Thread->LpcReceiveMessageId = 0; */ - Thread->LpcExitThreadCalled = FALSE; - Thread->LpcReceivedMsgIdValid = FALSE; + KeInitializeSemaphore (& Thread->LpcReplySemaphore, 0, LONG_MAX); + Thread->LpcReplyMessage = NULL; + Thread->LpcReplyMessageId = 0; /* not valid */ + /* Thread->LpcReceiveMessageId = 0; */ + Thread->LpcExitThreadCalled = FALSE; + Thread->LpcReceivedMsgIdValid = FALSE; - oldIrql = KeAcquireDispatcherDatabaseLock(); - InsertTailList(&Process->ThreadListHead, - &Thread->ThreadListEntry); - KeReleaseDispatcherDatabaseLock(oldIrql); + oldIrql = KeAcquireDispatcherDatabaseLock(); + InsertTailList(&Process->ThreadListHead, + &Thread->ThreadListEntry); + KeReleaseDispatcherDatabaseLock(oldIrql); - *ThreadPtr = Thread; - - Status = ObInsertObject((PVOID)Thread, - NULL, - DesiredAccess, - 0, - NULL, - ThreadHandle); - return(Status); + *ThreadPtr = Thread; + + return STATUS_SUCCESS; } static NTSTATUS -PsCreateTeb(HANDLE ProcessHandle, - PTEB *TebPtr, - PETHREAD Thread, - PINITIAL_TEB InitialTeb) +PsCreateTeb ( + HANDLE ProcessHandle, + PTEB *TebPtr, + PETHREAD Thread, + PINITIAL_TEB InitialTeb ) { - PEPROCESS Process; - NTSTATUS Status; - ULONG ByteCount; - ULONG RegionSize; - ULONG TebSize; - PVOID TebBase; - TEB Teb; - - PAGED_CODE(); + PEPROCESS Process; + NTSTATUS Status; + ULONG ByteCount; + ULONG RegionSize; + ULONG TebSize; + PVOID TebBase; + TEB Teb; - TebSize = PAGE_SIZE; + PAGED_CODE(); - if (NULL == Thread->ThreadsProcess) - { - /* We'll be allocating a 64k block here and only use 4k of it, but this - path should almost never be taken. Actually, I never saw it was taken, - so maybe we should just ASSERT(NULL != Thread->ThreadsProcess) and - move on */ - TebBase = NULL; - Status = ZwAllocateVirtualMemory(ProcessHandle, - &TebBase, - 0, - &TebSize, - MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, - PAGE_READWRITE); - if (! NT_SUCCESS(Status)) - { - DPRINT1("Failed to allocate virtual memory for TEB\n"); - return Status; - } - } - else - { - Process = Thread->ThreadsProcess; - ExAcquireFastMutex(&Process->TebLock); - if (NULL == Process->TebBlock || - Process->TebBlock == Process->TebLastAllocated) - { - Process->TebBlock = NULL; - RegionSize = MM_VIRTMEM_GRANULARITY; - Status = ZwAllocateVirtualMemory(ProcessHandle, - &Process->TebBlock, - 0, - &RegionSize, - MEM_RESERVE | MEM_TOP_DOWN, - PAGE_READWRITE); - if (! NT_SUCCESS(Status)) - { - ExReleaseFastMutex(&Process->TebLock); - DPRINT1("Failed to reserve virtual memory for TEB\n"); - return Status; - } - Process->TebLastAllocated = (PVOID) ((char *) Process->TebBlock + RegionSize); - } - TebBase = (PVOID) ((char *) Process->TebLastAllocated - PAGE_SIZE); - Status = ZwAllocateVirtualMemory(ProcessHandle, - &TebBase, - 0, - &TebSize, - MEM_COMMIT, - PAGE_READWRITE); - if (! NT_SUCCESS(Status)) - { - DPRINT1("Failed to commit virtual memory for TEB\n"); - return Status; - } - Process->TebLastAllocated = TebBase; - ExReleaseFastMutex(&Process->TebLock); - } + TebSize = PAGE_SIZE; - DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize); - ASSERT(NULL != TebBase && PAGE_SIZE <= TebSize); - - RtlZeroMemory(&Teb, sizeof(TEB)); - /* set all pointers to and from the TEB */ - Teb.Tib.Self = TebBase; - if (Thread->ThreadsProcess) - { - Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */ - } - DPRINT("Teb.Peb %x\n", Teb.Peb); - - /* store stack information from InitialTeb */ - if(InitialTeb != NULL) - { - /* fixed-size stack */ - if(InitialTeb->StackBase && InitialTeb->StackLimit) + if (NULL == Thread->ThreadsProcess) { - Teb.Tib.StackBase = InitialTeb->StackBase; - Teb.Tib.StackLimit = InitialTeb->StackLimit; - Teb.DeallocationStack = InitialTeb->StackLimit; + /* We'll be allocating a 64k block here and only use 4k of it, but this + path should almost never be taken. Actually, I never saw it was taken, + so maybe we should just ASSERT(NULL != Thread->ThreadsProcess) and + move on */ + TebBase = NULL; + Status = ZwAllocateVirtualMemory(ProcessHandle, + &TebBase, + 0, + &TebSize, + MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + if (! NT_SUCCESS(Status)) + { + DPRINT1("Failed to allocate virtual memory for TEB\n"); + return Status; + } } - /* expandable stack */ else { - Teb.Tib.StackBase = InitialTeb->StackCommit; - Teb.Tib.StackLimit = InitialTeb->StackCommitMax; - Teb.DeallocationStack = InitialTeb->StackReserved; + Process = Thread->ThreadsProcess; + PsLockProcess(Process, FALSE); + if (NULL == Process->TebBlock || + Process->TebBlock == Process->TebLastAllocated) + { + Process->TebBlock = NULL; + RegionSize = MM_VIRTMEM_GRANULARITY; + Status = ZwAllocateVirtualMemory(ProcessHandle, + &Process->TebBlock, + 0, + &RegionSize, + MEM_RESERVE | MEM_TOP_DOWN, + PAGE_READWRITE); + if (! NT_SUCCESS(Status)) + { + PsUnlockProcess(Process); + DPRINT1("Failed to reserve virtual memory for TEB\n"); + return Status; + } + Process->TebLastAllocated = (PVOID) ((char *) Process->TebBlock + RegionSize); + } + TebBase = (PVOID) ((char *) Process->TebLastAllocated - PAGE_SIZE); + Status = ZwAllocateVirtualMemory(ProcessHandle, + &TebBase, + 0, + &TebSize, + MEM_COMMIT, + PAGE_READWRITE); + if (! NT_SUCCESS(Status)) + { + DPRINT1("Failed to commit virtual memory for TEB\n"); + return Status; + } + Process->TebLastAllocated = TebBase; + PsUnlockProcess(Process); } - } - /* more initialization */ - Teb.Cid.UniqueThread = Thread->Cid.UniqueThread; - Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess; - Teb.CurrentLocale = PsDefaultThreadLocaleId; + DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize); + ASSERT(NULL != TebBase && PAGE_SIZE <= TebSize); - /* Terminate the exception handler list */ - Teb.Tib.ExceptionList = (PVOID)-1; - - DPRINT("sizeof(TEB) %x\n", sizeof(TEB)); - - /* write TEB data into teb page */ - Status = NtWriteVirtualMemory(ProcessHandle, - TebBase, - &Teb, - sizeof(TEB), - &ByteCount); + RtlZeroMemory(&Teb, sizeof(TEB)); + /* set all pointers to and from the TEB */ + Teb.Tib.Self = TebBase; + if (Thread->ThreadsProcess) + { + Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */ + } + DPRINT("Teb.Peb %x\n", Teb.Peb); - if (!NT_SUCCESS(Status)) - { + /* store stack information from InitialTeb */ + if(InitialTeb != NULL) + { + /* fixed-size stack */ + if(InitialTeb->StackBase && InitialTeb->StackLimit) + { + Teb.Tib.StackBase = InitialTeb->StackBase; + Teb.Tib.StackLimit = InitialTeb->StackLimit; + Teb.DeallocationStack = InitialTeb->StackLimit; + } + /* expandable stack */ + else + { + Teb.Tib.StackBase = InitialTeb->StackCommit; + Teb.Tib.StackLimit = InitialTeb->StackCommitMax; + Teb.DeallocationStack = InitialTeb->StackReserved; + } + } + + /* more initialization */ + Teb.Cid.UniqueThread = Thread->Cid.UniqueThread; + Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess; + Teb.CurrentLocale = PsDefaultThreadLocaleId; + + /* Terminate the exception handler list */ + Teb.Tib.ExceptionList = (PVOID)-1; + + DPRINT("sizeof(TEB) %x\n", sizeof(TEB)); + + /* write TEB data into teb page */ + Status = NtWriteVirtualMemory(ProcessHandle, + TebBase, + &Teb, + sizeof(TEB), + &ByteCount); + + if (!NT_SUCCESS(Status)) + { /* free TEB */ DPRINT1 ("Writing TEB failed!\n"); RegionSize = 0; NtFreeVirtualMemory(ProcessHandle, - TebBase, - &RegionSize, - MEM_RELEASE); + TebBase, + &RegionSize, + MEM_RELEASE); return Status; - } + } - if (TebPtr != NULL) - { + if (TebPtr != NULL) + { *TebPtr = (PTEB)TebBase; - } + } - DPRINT("TEB allocated at %p\n", TebBase); + DPRINT("TEB allocated at %p\n", TebBase); - return Status; + return Status; } VOID STDCALL -LdrInitApcRundownRoutine(PKAPC Apc) +LdrInitApcRundownRoutine ( PKAPC Apc ) { - ExFreePool(Apc); + ExFreePool(Apc); } VOID STDCALL -LdrInitApcKernelRoutine(PKAPC Apc, - PKNORMAL_ROUTINE* NormalRoutine, - PVOID* NormalContext, - PVOID* SystemArgument1, - PVOID* SystemArgument2) +LdrInitApcKernelRoutine ( + PKAPC Apc, + PKNORMAL_ROUTINE* NormalRoutine, + PVOID* NormalContext, + PVOID* SystemArgument1, + PVOID* SystemArgument2) { - ExFreePool(Apc); + ExFreePool(Apc); } NTSTATUS STDCALL -NtCreateThread(OUT PHANDLE ThreadHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN HANDLE ProcessHandle, - OUT PCLIENT_ID ClientId, - IN PCONTEXT ThreadContext, - IN PINITIAL_TEB InitialTeb, - IN BOOLEAN CreateSuspended) +NtCreateThread ( + OUT PHANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN HANDLE ProcessHandle, + OUT PCLIENT_ID ClientId, + IN PCONTEXT ThreadContext, + IN PINITIAL_TEB InitialTeb, + IN BOOLEAN CreateSuspended ) { - HANDLE hThread; - CONTEXT SafeContext; - INITIAL_TEB SafeInitialTeb; - PEPROCESS Process; - PETHREAD Thread; - PTEB TebBase; - PKAPC LdrInitApc; - KIRQL oldIrql; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - if(ThreadContext == NULL) - { - return STATUS_INVALID_PARAMETER; - } - - PreviousMode = ExGetPreviousMode(); + HANDLE hThread; + CONTEXT SafeContext; + INITIAL_TEB SafeInitialTeb; + PEPROCESS Process; + PETHREAD Thread; + PTEB TebBase; + PKAPC LdrInitApc; + KIRQL oldIrql; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; - if(PreviousMode != KernelMode) - { - _SEH_TRY + PAGED_CODE(); + + if(ThreadContext == NULL) { - ProbeForWrite(ThreadHandle, - sizeof(HANDLE), + return STATUS_INVALID_PARAMETER; + } + + PreviousMode = ExGetPreviousMode(); + + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForWrite(ThreadHandle, + sizeof(HANDLE), + sizeof(ULONG)); + if(ClientId != NULL) + { + ProbeForWrite(ClientId, + sizeof(CLIENT_ID), sizeof(ULONG)); - if(ClientId != NULL) - { - ProbeForWrite(ClientId, - sizeof(CLIENT_ID), - sizeof(ULONG)); - } - ProbeForRead(ThreadContext, - sizeof(CONTEXT), - sizeof(ULONG)); - SafeContext = *ThreadContext; - ThreadContext = &SafeContext; - ProbeForRead(InitialTeb, - sizeof(INITIAL_TEB), - sizeof(ULONG)); - SafeInitialTeb = *InitialTeb; - InitialTeb = &SafeInitialTeb; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + } + ProbeForRead(ThreadContext, + sizeof(CONTEXT), + sizeof(ULONG)); + SafeContext = *ThreadContext; + ThreadContext = &SafeContext; + ProbeForRead(InitialTeb, + sizeof(INITIAL_TEB), + sizeof(ULONG)); + SafeInitialTeb = *InitialTeb; + InitialTeb = &SafeInitialTeb; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + + DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n", + ThreadHandle,ThreadContext); + + Status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_CREATE_THREAD, + PsProcessType, + PreviousMode, + (PVOID*)&Process, + NULL); if(!NT_SUCCESS(Status)) { - return Status; + return(Status); } - } - DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n", - ThreadHandle,ThreadContext); - - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_CREATE_THREAD, - PsProcessType, - PreviousMode, - (PVOID*)&Process, - NULL); - if(!NT_SUCCESS(Status)) - { - return(Status); - } - - Status = PsInitializeThread(Process, - &Thread, - &hThread, - DesiredAccess, - ObjectAttributes, - FALSE); - - ObDereferenceObject(Process); - - if (!NT_SUCCESS(Status)) + Status = PsLockProcess(Process, FALSE); + if (!NT_SUCCESS(Status)) { - return(Status); + ObDereferenceObject(Process); + return(Status); } - Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext); - if (!NT_SUCCESS(Status)) + if(Process->ExitTime.QuadPart != 0) { - return(Status); + PsUnlockProcess(Process); + return STATUS_PROCESS_IS_TERMINATING; } - Status = PsCreateTeb(ProcessHandle, - &TebBase, - Thread, - InitialTeb); - if (!NT_SUCCESS(Status)) + PsUnlockProcess(Process); + + Status = PsInitializeThread(Process, + &Thread, + ObjectAttributes, + PreviousMode, + FALSE); + + ObDereferenceObject(Process); + + if (!NT_SUCCESS(Status)) { - return(Status); + return(Status); } - Thread->Tcb.Teb = TebBase; - Thread->StartAddress = NULL; - - /* - * Maybe send a message to the process's debugger - */ - DbgkCreateThread((PVOID)ThreadContext->Eip); - - /* - * First, force the thread to be non-alertable for user-mode alerts. - */ - Thread->Tcb.Alertable = FALSE; - - /* - * If the thread is to be created suspended then queue an APC to - * do the suspend before we run any userspace code. - */ - if (CreateSuspended) + /* create a client id handle */ + Status = PsCreateCidHandle ( + Thread, PsThreadType, &Thread->Cid.UniqueThread); + if (!NT_SUCCESS(Status)) { - PsSuspendThread(Thread, NULL); + ObDereferenceObject(Thread); + return Status; } - /* - * Queue an APC to the thread that will execute the ntdll startup - * routine. - */ - LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC)); - KeInitializeApc(LdrInitApc, &Thread->Tcb, OriginalApcEnvironment, LdrInitApcKernelRoutine, - LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(), - UserMode, NULL); - KeInsertQueueApc(LdrInitApc, NULL, NULL, IO_NO_INCREMENT); - - /* - * The thread is non-alertable, so the APC we added did not set UserApcPending to TRUE. - * We must do this manually. Do NOT attempt to set the Thread to Alertable before the call, - * doing so is a blatant and erronous hack. - */ - Thread->Tcb.ApcState.UserApcPending = TRUE; - Thread->Tcb.Alerted[KernelMode] = TRUE; - - oldIrql = KeAcquireDispatcherDatabaseLock (); - PsUnblockThread(Thread, NULL, 0); - KeReleaseDispatcherDatabaseLock(oldIrql); - - _SEH_TRY - { - if(ClientId != NULL) + Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext); + if (!NT_SUCCESS(Status)) { - *ClientId = Thread->Cid; + PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType); + ObDereferenceObject(Thread); + return(Status); } - *ThreadHandle = hThread; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - return Status; + Status = PsCreateTeb(ProcessHandle, + &TebBase, + Thread, + InitialTeb); + if (!NT_SUCCESS(Status)) + { + PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType); + ObDereferenceObject(Thread); + return(Status); + } + Thread->Tcb.Teb = TebBase; + + Thread->StartAddress = NULL; + + /* + * Maybe send a message to the process's debugger + */ + DbgkCreateThread((PVOID)ThreadContext->Eip); + + /* + * First, force the thread to be non-alertable for user-mode alerts. + */ + Thread->Tcb.Alertable = FALSE; + + /* + * If the thread is to be created suspended then queue an APC to + * do the suspend before we run any userspace code. + */ + if (CreateSuspended) + { + KeSuspendThread(&Thread->Tcb); + } + + /* + * Queue an APC to the thread that will execute the ntdll startup + * routine. + */ + LdrInitApc = ExAllocatePoolWithTag ( + NonPagedPool, sizeof(KAPC), TAG_KAPC ); + KeInitializeApc ( + LdrInitApc, + &Thread->Tcb, + OriginalApcEnvironment, + LdrInitApcKernelRoutine, + LdrInitApcRundownRoutine, + LdrpGetSystemDllEntryPoint(), + UserMode, + NULL ); + KeInsertQueueApc(LdrInitApc, NULL, NULL, IO_NO_INCREMENT); + + /* + * The thread is non-alertable, so the APC we added did not set UserApcPending to TRUE. + * We must do this manually. Do NOT attempt to set the Thread to Alertable before the call, + * doing so is a blatant and erronous hack. + */ + Thread->Tcb.ApcState.UserApcPending = TRUE; + Thread->Tcb.Alerted[KernelMode] = TRUE; + + oldIrql = KeAcquireDispatcherDatabaseLock (); + KiUnblockThread(&Thread->Tcb, NULL, 0); + KeReleaseDispatcherDatabaseLock(oldIrql); + + Status = ObInsertObject((PVOID)Thread, + NULL, + DesiredAccess, + 0, + NULL, + &hThread); + if(NT_SUCCESS(Status)) + { + _SEH_TRY + { + if(ClientId != NULL) + { + *ClientId = Thread->Cid; + } + *ThreadHandle = hThread; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + + return Status; } @@ -830,17 +517,18 @@ NtCreateThread(OUT PHANDLE ThreadHandle, * @implemented */ NTSTATUS STDCALL -PsCreateSystemThread(PHANDLE ThreadHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes, - HANDLE ProcessHandle, - PCLIENT_ID ClientId, - PKSTART_ROUTINE StartRoutine, - PVOID StartContext) +PsCreateSystemThread ( + PHANDLE ThreadHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + HANDLE ProcessHandle, + PCLIENT_ID ClientId, + PKSTART_ROUTINE StartRoutine, + PVOID StartContext ) /* * FUNCTION: Creates a thread which executes in kernel mode * ARGUMENTS: - * ThreadHandle (OUT) = Caller supplied storage for the returned thread + * ThreadHandle (OUT) = Caller supplied storage for the returned thread * handle * DesiredAccess = Requested access to the thread * ObjectAttributes = Object attributes (optional) @@ -854,56 +542,82 @@ PsCreateSystemThread(PHANDLE ThreadHandle, * RETURNS: Success or failure status */ { - PETHREAD Thread; - NTSTATUS Status; - KIRQL oldIrql; - - PAGED_CODE(); - - DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n", - ThreadHandle,ProcessHandle); - - Status = PsInitializeThread(NULL, - &Thread, - ThreadHandle, - DesiredAccess, - ObjectAttributes, - FALSE); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - - Thread->StartAddress = StartRoutine; - Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext); - if (!NT_SUCCESS(Status)) - { - return(Status); - } + PETHREAD Thread; + NTSTATUS Status; + KIRQL oldIrql; - if (ClientId != NULL) - { - *ClientId=Thread->Cid; - } + PAGED_CODE(); - oldIrql = KeAcquireDispatcherDatabaseLock (); - PsUnblockThread(Thread, NULL, 0); - KeReleaseDispatcherDatabaseLock(oldIrql); - - return(STATUS_SUCCESS); + DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n", + ThreadHandle,ProcessHandle); + + Status = PsInitializeThread( + NULL, + &Thread, + ObjectAttributes, + KernelMode, + FALSE); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + /* Set the thread as a system thread */ + Thread->SystemThread = TRUE; + + Status = PsCreateCidHandle(Thread, + PsThreadType, + &Thread->Cid.UniqueThread); + if(!NT_SUCCESS(Status)) + { + ObDereferenceObject(Thread); + return Status; + } + + Thread->StartAddress = StartRoutine; + Status = KiArchInitThread ( + &Thread->Tcb, StartRoutine, StartContext); + if (!NT_SUCCESS(Status)) + { + ObDereferenceObject(Thread); + return(Status); + } + + if (ClientId != NULL) + { + *ClientId=Thread->Cid; + } + + oldIrql = KeAcquireDispatcherDatabaseLock (); + KiUnblockThread(&Thread->Tcb, NULL, 0); + KeReleaseDispatcherDatabaseLock(oldIrql); + + Status = ObInsertObject( + (PVOID)Thread, + NULL, + DesiredAccess, + 0, + NULL, + ThreadHandle); + + /* don't dereference the thread, the initial reference serves as the keep-alive + reference which will be removed by the thread reaper */ + + return Status; } VOID STDCALL -PspRunCreateThreadNotifyRoutines(PETHREAD CurrentThread, - BOOLEAN Create) +PspRunCreateThreadNotifyRoutines ( + PETHREAD CurrentThread, + BOOLEAN Create ) { - ULONG i; - CLIENT_ID Cid = CurrentThread->Cid; + ULONG i; + CLIENT_ID Cid = CurrentThread->Cid; - for (i = 0; i < PiThreadNotifyRoutineCount; i++) + for (i = 0; i < PiThreadNotifyRoutineCount; i++) { - PiThreadNotifyRoutine[i](Cid.UniqueProcess, Cid.UniqueThread, Create); + PiThreadNotifyRoutine[i](Cid.UniqueProcess, Cid.UniqueThread, Create); } } @@ -912,17 +626,18 @@ PspRunCreateThreadNotifyRoutines(PETHREAD CurrentThread, * @implemented */ NTSTATUS STDCALL -PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine) +PsSetCreateThreadNotifyRoutine ( + IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine ) { - if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT) + if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT) { - return(STATUS_INSUFFICIENT_RESOURCES); + return(STATUS_INSUFFICIENT_RESOURCES); } - PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine; - PiThreadNotifyRoutineCount++; + PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine; + PiThreadNotifyRoutineCount++; - return(STATUS_SUCCESS); + return(STATUS_SUCCESS); } /* EOF */ diff --git a/reactos/ntoskrnl/ps/debug.c b/reactos/ntoskrnl/ps/debug.c index a028cdb9a32..a9015623aa8 100644 --- a/reactos/ntoskrnl/ps/debug.c +++ b/reactos/ntoskrnl/ps/debug.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ps/debug.c @@ -9,13 +8,6 @@ * Phillip Susi */ -/* - * NOTE: - * - * All of the routines that manipulate the thread queue synchronize on - * a single spinlock - * - */ /* INCLUDES ****************************************************************/ @@ -23,378 +15,256 @@ #define NDEBUG #include +/* GLOBALS *****************************************************************/ + +/* Thread "Set/Get Context" Context Structure */ +typedef struct _GET_SET_CTX_CONTEXT { + KAPC Apc; + KEVENT Event; + CONTEXT Context; +} GET_SET_CTX_CONTEXT, *PGET_SET_CTX_CONTEXT; + + /* FUNCTIONS ***************************************************************/ -VOID -KeContextToTrapFrame(PCONTEXT Context, - PKTRAP_FRAME TrapFrame) -{ - if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) - { - TrapFrame->Esp = Context->Esp; - TrapFrame->Ss = Context->SegSs; - TrapFrame->Cs = Context->SegCs; - TrapFrame->Eip = Context->Eip; - TrapFrame->Eflags = Context->EFlags; - TrapFrame->Ebp = Context->Ebp; - } - if ((Context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) - { - TrapFrame->Eax = Context->Eax; - TrapFrame->Ebx = Context->Ebx; - TrapFrame->Ecx = Context->Ecx; - TrapFrame->Edx = Context->Edx; - TrapFrame->Esi = Context->Esi; - TrapFrame->Edi = Context->Edi; - } - if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) - { - TrapFrame->Ds = Context->SegDs; - TrapFrame->Es = Context->SegEs; - TrapFrame->Fs = Context->SegFs; - TrapFrame->Gs = Context->SegGs; - } - if ((Context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) - { - /* - * Not handled - * - * This should be handled separately I think. - * - blight - */ - } - if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS) - { - /* - * Not handled - */ - } -} - -VOID -KeTrapFrameToContext(PKTRAP_FRAME TrapFrame, - PCONTEXT Context) -{ - if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) - { - Context->SegSs = TrapFrame->Ss; - Context->Esp = TrapFrame->Esp; - Context->SegCs = TrapFrame->Cs; - Context->Eip = TrapFrame->Eip; - Context->EFlags = TrapFrame->Eflags; - Context->Ebp = TrapFrame->Ebp; - } - if ((Context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) - { - Context->Eax = TrapFrame->Eax; - Context->Ebx = TrapFrame->Ebx; - Context->Ecx = TrapFrame->Ecx; - /* - * NOTE: In the trap frame which is built on entry to a system - * call TrapFrame->Edx will actually hold the address of the - * previous TrapFrame. I don't believe leaking this information - * has security implications. Also EDX holds the address of the - * arguments to the system call in progress so it isn't of much - * interest to the debugger. - */ - Context->Edx = TrapFrame->Edx; - Context->Esi = TrapFrame->Esi; - Context->Edi = TrapFrame->Edi; - } - if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) - { - Context->SegDs = TrapFrame->Ds; - Context->SegEs = TrapFrame->Es; - Context->SegFs = TrapFrame->Fs; - Context->SegGs = TrapFrame->Gs; - } - if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS) - { - /* - * FIXME: Implement this case - */ - Context->ContextFlags &= (~CONTEXT_DEBUG_REGISTERS) | CONTEXT_i386; - } - if ((Context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) - { - /* - * FIXME: Implement this case - * - * I think this should only be filled for FPU exceptions, otherwise I - * would not know where to get it from as it can be the current state - * of the FPU or already saved in the thread's FPU save area. - * -blight - */ - Context->ContextFlags &= (~CONTEXT_FLOATING_POINT) | CONTEXT_i386; - } -#if 0 - if ((Context->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) - { - /* - * FIXME: Investigate this - * - * This is the XMM state (first 512 bytes of FXSAVE_FORMAT/FX_SAVE_AREA) - * This should only be filled in case of a SIMD exception I think, so - * this is not the right place (like for FPU the state could already be - * saved in the thread's FX_SAVE_AREA or still be in the CPU) - * -blight - */ - Context->ContextFlags &= ~CONTEXT_EXTENDED_REGISTERS; - } -#endif -} - -VOID STDCALL -KeGetSetContextRundownRoutine(PKAPC Apc) -{ - PKEVENT Event; - PNTSTATUS Status; - - Event = (PKEVENT)Apc->SystemArgument1; - Status = (PNTSTATUS)Apc->SystemArgument2; - (*Status) = STATUS_THREAD_IS_TERMINATING; - KeSetEvent(Event, IO_NO_INCREMENT, FALSE); -} - -VOID STDCALL -KeGetContextKernelRoutine(PKAPC Apc, - PKNORMAL_ROUTINE* NormalRoutine, - PVOID* NormalContext, - PVOID* SystemArgument1, - PVOID* SystemArgument2) /* * FUNCTION: This routine is called by an APC sent by NtGetContextThread to * copy the context of a thread into a buffer. */ +VOID +STDCALL +PspGetOrSetContextKernelRoutine(PKAPC Apc, + PKNORMAL_ROUTINE* NormalRoutine, + PVOID* NormalContext, + PVOID* SystemArgument1, + PVOID* SystemArgument2) { - PKEVENT Event; - PCONTEXT Context; - PNTSTATUS Status; + PGET_SET_CTX_CONTEXT GetSetContext; + PKEVENT Event; + PCONTEXT Context; - Context = (PCONTEXT)(*NormalContext); - Event = (PKEVENT)(*SystemArgument1); - Status = (PNTSTATUS)(*SystemArgument2); + /* Get the Context Structure */ + GetSetContext = CONTAINING_RECORD(Apc, GET_SET_CTX_CONTEXT, Apc); + Context = &GetSetContext->Context; + Event = &GetSetContext->Event; + + /* Check if it's a set or get */ + if (SystemArgument1) { + + /* Get the Context */ + KeTrapFrameToContext(KeGetCurrentThread()->TrapFrame, Context); + + } else { + + /* Set the Context */ + KeContextToTrapFrame(Context, KeGetCurrentThread()->TrapFrame); + } - KeTrapFrameToContext(KeGetCurrentThread()->TrapFrame, Context); - - *Status = STATUS_SUCCESS; - KeSetEvent(Event, IO_NO_INCREMENT, FALSE); + /* Notify the Native API that we are done */ + KeSetEvent(Event, IO_NO_INCREMENT, FALSE); } -NTSTATUS STDCALL +NTSTATUS +STDCALL NtGetContextThread(IN HANDLE ThreadHandle, - OUT PCONTEXT ThreadContext) + OUT PCONTEXT ThreadContext) { - PETHREAD Thread; - CONTEXT Context; - KAPC Apc; - KEVENT Event; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + PETHREAD Thread; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + GET_SET_CTX_CONTEXT GetSetContext; + NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); + PAGED_CODE(); - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForWrite(ThreadContext, - sizeof(CONTEXT), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = ObReferenceObjectByHandle(ThreadHandle, - THREAD_GET_CONTEXT, - PsThreadType, - PreviousMode, - (PVOID*)&Thread, - NULL); - if(NT_SUCCESS(Status)) - { - if(Thread == PsGetCurrentThread()) - { - /* - * I don't know if trying to get your own context makes much - * sense but we can handle it more efficently. - */ - - KeTrapFrameToContext(Thread->Tcb.TrapFrame, &Context); - } - else - { - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); - - KeInitializeApc(&Apc, - &Thread->Tcb, - OriginalApcEnvironment, - KeGetContextKernelRoutine, - KeGetSetContextRundownRoutine, - NULL, - KernelMode, - (PVOID)&Context); - if (!KeInsertQueueApc(&Apc, - (PVOID)&Event, - (PVOID)&Status, - IO_NO_INCREMENT)) - { - Status = STATUS_THREAD_IS_TERMINATING; - } - else - { - Status = KeWaitForSingleObject(&Event, - 0, - KernelMode, - FALSE, - NULL); - } - } - ObDereferenceObject(Thread); + /* Check the buffer to be OK */ + if(PreviousMode != KernelMode) { - if(NT_SUCCESS(Status)) - { - _SEH_TRY - { - *ThreadContext = Context; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + _SEH_TRY { + + ProbeForWrite(ThreadContext, + sizeof(CONTEXT), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; } - } - return Status; + /* Get the Thread Object */ + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_GET_CONTEXT, + PsThreadType, + PreviousMode, + (PVOID*)&Thread, + NULL); + + /* Check success */ + if(NT_SUCCESS(Status)) { + + /* Check if we're running in the same thread */ + if(Thread == PsGetCurrentThread()) { + + /* + * I don't know if trying to get your own context makes much + * sense but we can handle it more efficently. + */ + KeTrapFrameToContext(Thread->Tcb.TrapFrame, &GetSetContext.Context); + + } else { + + /* Use an APC... Initialize the Event */ + KeInitializeEvent(&GetSetContext.Event, + NotificationEvent, + FALSE); + + /* Initialize the APC */ + KeInitializeApc(&GetSetContext.Apc, + &Thread->Tcb, + OriginalApcEnvironment, + PspGetOrSetContextKernelRoutine, + NULL, + NULL, + KernelMode, + NULL); + + /* Queue it as a Get APC */ + if (!KeInsertQueueApc(&GetSetContext.Apc, + (PVOID)1, + NULL, + IO_NO_INCREMENT)) { + + Status = STATUS_THREAD_IS_TERMINATING; + + } else { + + /* Wait for the APC to complete */ + Status = KeWaitForSingleObject(&GetSetContext.Event, + 0, + KernelMode, + FALSE, + NULL); + } + } + + /* Dereference the thread */ + ObDereferenceObject(Thread); + + /* Check for success and return the Context */ + if(NT_SUCCESS(Status)) { + + _SEH_TRY { + + *ThreadContext = GetSetContext.Context; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; + } + } + + /* Return status */ + return Status; } -VOID STDCALL -KeSetContextKernelRoutine(PKAPC Apc, - PKNORMAL_ROUTINE* NormalRoutine, - PVOID* NormalContext, - PVOID* SystemArgument1, - PVOID* SystemArgument2) -/* - * FUNCTION: This routine is called by an APC sent by NtSetContextThread to - * set the context of a thread from a buffer. - */ -{ - PKEVENT Event; - PCONTEXT Context; - PNTSTATUS Status; - - Context = (PCONTEXT)(*NormalContext); - Event = (PKEVENT)(*SystemArgument1); - Status = (PNTSTATUS)(*SystemArgument2); - - KeContextToTrapFrame(Context, KeGetCurrentThread()->TrapFrame); - - *Status = STATUS_SUCCESS; - KeSetEvent(Event, IO_NO_INCREMENT, FALSE); -} - -NTSTATUS STDCALL +NTSTATUS +STDCALL NtSetContextThread(IN HANDLE ThreadHandle, - IN PCONTEXT ThreadContext) + IN PCONTEXT ThreadContext) { - PETHREAD Thread; - KAPC Apc; - KEVENT Event; - CONTEXT Context; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; + PETHREAD Thread; + GET_SET_CTX_CONTEXT GetSetContext; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); + PAGED_CODE(); - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode != KernelMode) - { - _SEH_TRY - { - ProbeForRead(ThreadContext, - sizeof(CONTEXT), - sizeof(ULONG)); - Context = *ThreadContext; - ThreadContext = &Context; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* Check the buffer to be OK */ + if(PreviousMode != KernelMode) { - if(!NT_SUCCESS(Status)) - { - return Status; + _SEH_TRY { + + ProbeForRead(ThreadContext, + sizeof(CONTEXT), + sizeof(ULONG)); + + GetSetContext.Context = *ThreadContext; + ThreadContext = &GetSetContext.Context; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + } _SEH_END; + + if(!NT_SUCCESS(Status)) return Status; } - } - Status = ObReferenceObjectByHandle(ThreadHandle, - THREAD_SET_CONTEXT, - PsThreadType, - PreviousMode, - (PVOID*)&Thread, - NULL); - if(NT_SUCCESS(Status)) - { - if (Thread == PsGetCurrentThread()) - { - /* - * I don't know if trying to set your own context makes much - * sense but we can handle it more efficently. - */ - - KeContextToTrapFrame(&Context, Thread->Tcb.TrapFrame); + /* Get the Thread Object */ + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_SET_CONTEXT, + PsThreadType, + PreviousMode, + (PVOID*)&Thread, + NULL); + + /* Check success */ + if(NT_SUCCESS(Status)) { + + /* Check if we're running in the same thread */ + if(Thread == PsGetCurrentThread()) { + + /* + * I don't know if trying to get your own context makes much + * sense but we can handle it more efficently. + */ + KeContextToTrapFrame(&GetSetContext.Context, Thread->Tcb.TrapFrame); + + } else { + + /* Use an APC... Initialize the Event */ + KeInitializeEvent(&GetSetContext.Event, + NotificationEvent, + FALSE); + + /* Initialize the APC */ + KeInitializeApc(&GetSetContext.Apc, + &Thread->Tcb, + OriginalApcEnvironment, + PspGetOrSetContextKernelRoutine, + NULL, + NULL, + KernelMode, + NULL); + + /* Queue it as a Get APC */ + if (!KeInsertQueueApc(&GetSetContext.Apc, + NULL, + NULL, + IO_NO_INCREMENT)) { + + Status = STATUS_THREAD_IS_TERMINATING; + + } else { + + /* Wait for the APC to complete */ + Status = KeWaitForSingleObject(&GetSetContext.Event, + 0, + KernelMode, + FALSE, + NULL); + } + } + + /* Dereference the thread */ + ObDereferenceObject(Thread); } - else - { - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); - - KeInitializeApc(&Apc, - &Thread->Tcb, - OriginalApcEnvironment, - KeSetContextKernelRoutine, - KeGetSetContextRundownRoutine, - NULL, - KernelMode, - (PVOID)&Context); - if (!KeInsertQueueApc(&Apc, - (PVOID)&Event, - (PVOID)&Status, - IO_NO_INCREMENT)) - { - Status = STATUS_THREAD_IS_TERMINATING; - } - else - { - Status = KeWaitForSingleObject(&Event, - 0, - KernelMode, - FALSE, - NULL); - } - } - ObDereferenceObject(Thread); - } - return Status; + /* Return status */ + return Status; } /* EOF */ diff --git a/reactos/ntoskrnl/ps/i386/continue.c b/reactos/ntoskrnl/ps/i386/continue.c index 10d2c60650b..39064fd139a 100644 --- a/reactos/ntoskrnl/ps/i386/continue.c +++ b/reactos/ntoskrnl/ps/i386/continue.c @@ -67,9 +67,9 @@ NtContinue ( { Thread->NpxState = NPX_STATE_VALID; KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); - if (KeGetCurrentKPCR()->PrcbData.NpxThread == Thread) + if (KeGetCurrentPrcb()->NpxThread == Thread) { - KeGetCurrentKPCR()->PrcbData.NpxThread = NULL; + KeGetCurrentPrcb()->NpxThread = NULL; Ke386SetCr0(Ke386GetCr0() | X86_CR0_TS); } else diff --git a/reactos/ntoskrnl/ps/idle.c b/reactos/ntoskrnl/ps/idle.c index c1d0b09e77a..b8d2598c9b1 100644 --- a/reactos/ntoskrnl/ps/idle.c +++ b/reactos/ntoskrnl/ps/idle.c @@ -16,6 +16,8 @@ /* GLOBALS *******************************************************************/ +extern PEPROCESS PsIdleProcess; + /* FUNCTIONS *****************************************************************/ /** System idle thread procedure @@ -26,11 +28,11 @@ PsIdleThreadMain(PVOID Context) { KIRQL oldlvl; - PKPCR Pcr = KeGetCurrentKPCR(); + PKPRCB Prcb = KeGetCurrentPrcb(); for(;;) { - if (Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0) + if (Prcb->DpcData[0].DpcQueueDepth > 0) { KeRaiseIrql(DISPATCH_LEVEL,&oldlvl); KiDispatchInterrupt(); @@ -52,35 +54,35 @@ PsInitIdleThread(VOID) { NTSTATUS Status; PETHREAD IdleThread; - HANDLE IdleThreadHandle; + KIRQL oldIrql; + + Status = PsInitializeThread(PsIdleProcess, + &IdleThread, + NULL, + KernelMode, + FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Couldn't create idle system thread! Status: 0x%x\n", Status); + KEBUGCHECK(0); + return; + } - Status = PsCreateSystemThread(&IdleThreadHandle, - THREAD_ALL_ACCESS, - NULL, - NULL, - NULL, - PsIdleThreadMain, - NULL); - if(!NT_SUCCESS(Status)) - { - DPRINT("Couldn't create Idle System Thread!"); - KEBUGCHECK(0); - return; - } - Status = ObReferenceObjectByHandle(IdleThreadHandle, - THREAD_ALL_ACCESS, - PsThreadType, - KernelMode, - (PVOID*)&IdleThread, - NULL); - if(!NT_SUCCESS(Status)) - { - DPRINT("Couldn't get pointer to Idle System Thread!"); - KEBUGCHECK(0); - return; - } - NtClose(IdleThreadHandle); - KeGetCurrentKPCR()->PrcbData.IdleThread = &IdleThread->Tcb; + IdleThread->StartAddress = PsIdleThreadMain; + Status = KiArchInitThread(&IdleThread->Tcb, PsIdleThreadMain, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Couldn't initialize system idle thread! Status: 0x%x\n", Status); + ObDereferenceObject(IdleThread); + KEBUGCHECK(0); + return; + } + + oldIrql = KeAcquireDispatcherDatabaseLock (); + KiUnblockThread(&IdleThread->Tcb, NULL, 0); + KeReleaseDispatcherDatabaseLock(oldIrql); + + KeGetCurrentPrcb()->IdleThread = &IdleThread->Tcb; KeSetPriorityThread(&IdleThread->Tcb, LOW_PRIORITY); KeSetAffinityThread(&IdleThread->Tcb, 1 << 0); diff --git a/reactos/ntoskrnl/ps/job.c b/reactos/ntoskrnl/ps/job.c index 4e764b32cb5..b28740c9841 100644 --- a/reactos/ntoskrnl/ps/job.c +++ b/reactos/ntoskrnl/ps/job.c @@ -1,10 +1,10 @@ /* $Id$ - * + * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ps/job.c * PURPOSE: Job Native Functions - * + * * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) (stubs) * Thomas Weidenmueller */ @@ -17,281 +17,293 @@ /* GLOBALS *******************************************************************/ +#define TAG_EJOB TAG('E', 'J', 'O', 'B') /* EJOB */ + POBJECT_TYPE EXPORTED PsJobType = NULL; LIST_ENTRY PsJobListHead; static FAST_MUTEX PsJobListLock; -static GENERIC_MAPPING PiJobMapping = {STANDARD_RIGHTS_READ | JOB_OBJECT_QUERY, - STANDARD_RIGHTS_WRITE | JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_TERMINATE | JOB_OBJECT_SET_SECURITY_ATTRIBUTES, - STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, - STANDARD_RIGHTS_ALL | JOB_OBJECT_ALL_ACCESS}; +static GENERIC_MAPPING PiJobMapping = +{ + STANDARD_RIGHTS_READ | JOB_OBJECT_QUERY, + STANDARD_RIGHTS_WRITE | JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_TERMINATE | JOB_OBJECT_SET_SECURITY_ATTRIBUTES, + STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, + STANDARD_RIGHTS_ALL | JOB_OBJECT_ALL_ACCESS +}; /* FUNCTIONS *****************************************************************/ VOID STDCALL -PiDeleteJob(PVOID ObjectBody) +PiDeleteJob ( PVOID ObjectBody ) { - PEJOB Job = (PEJOB)ObjectBody; - - /* remove the reference to the completion port if associated */ - if(Job->CompletionPort != NULL) - { - ObDereferenceObject(Job->CompletionPort); - } + PEJOB Job = (PEJOB)ObjectBody; - /* unlink the job object */ - if(Job->JobLinks.Flink != NULL) - { - ExAcquireFastMutex(&PsJobListLock); - RemoveEntryList(&Job->JobLinks); - ExReleaseFastMutex(&PsJobListLock); - } - - ExDeleteResource(&Job->JobLock); + /* remove the reference to the completion port if associated */ + if(Job->CompletionPort != NULL) + { + ObDereferenceObject(Job->CompletionPort); + } + + /* unlink the job object */ + if(Job->JobLinks.Flink != NULL) + { + ExAcquireFastMutex(&PsJobListLock); + RemoveEntryList(&Job->JobLinks); + ExReleaseFastMutex(&PsJobListLock); + } + + ExDeleteResource(&Job->JobLock); } VOID INIT_FUNCTION -PsInitJobManagment(VOID) +PsInitJobManagment ( VOID ) { - PsJobType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); + PsJobType = ExAllocatePoolWithTag ( + NonPagedPool, sizeof(OBJECT_TYPE), TAG_EJOB ); - PsJobType->Tag = TAG('E', 'J', 'O', 'B'); - PsJobType->TotalObjects = 0; - PsJobType->TotalHandles = 0; - PsJobType->PeakObjects = 0; - PsJobType->PeakHandles = 0; - PsJobType->PagedPoolCharge = 0; - PsJobType->NonpagedPoolCharge = sizeof(EJOB); - PsJobType->Mapping = &PiJobMapping; - PsJobType->Dump = NULL; - PsJobType->Open = NULL; - PsJobType->Close = NULL; - PsJobType->Delete = PiDeleteJob; - PsJobType->Parse = NULL; - PsJobType->Security = NULL; - PsJobType->QueryName = NULL; - PsJobType->OkayToClose = NULL; - PsJobType->Create = NULL; - PsJobType->DuplicationNotify = NULL; - - RtlInitUnicodeString(&PsJobType->TypeName, L"Job"); - - ObpCreateTypeObject(PsJobType); - - InitializeListHead(&PsJobListHead); - ExInitializeFastMutex(&PsJobListLock); + PsJobType->Tag = TAG_EJOB; + PsJobType->TotalObjects = 0; + PsJobType->TotalHandles = 0; + PsJobType->PeakObjects = 0; + PsJobType->PeakHandles = 0; + PsJobType->PagedPoolCharge = 0; + PsJobType->NonpagedPoolCharge = sizeof(EJOB); + PsJobType->Mapping = &PiJobMapping; + PsJobType->Dump = NULL; + PsJobType->Open = NULL; + PsJobType->Close = NULL; + PsJobType->Delete = PiDeleteJob; + PsJobType->Parse = NULL; + PsJobType->Security = NULL; + PsJobType->QueryName = NULL; + PsJobType->OkayToClose = NULL; + PsJobType->Create = NULL; + PsJobType->DuplicationNotify = NULL; + + RtlInitUnicodeString(&PsJobType->TypeName, L"Job"); + + ObpCreateTypeObject(PsJobType); + + InitializeListHead(&PsJobListHead); + ExInitializeFastMutex(&PsJobListLock); } NTSTATUS -PspAssignProcessToJob(PEPROCESS Process, - PEJOB Job) +PspAssignProcessToJob ( + PEPROCESS Process, + PEJOB Job) { - DPRINT("PspAssignProcessToJob() is unimplemented!\n"); - return STATUS_NOT_IMPLEMENTED; + DPRINT("PspAssignProcessToJob() is unimplemented!\n"); + return STATUS_NOT_IMPLEMENTED; } NTSTATUS -PspTerminateJobObject(PEJOB Job, - KPROCESSOR_MODE AccessMode, - NTSTATUS ExitStatus) +PspTerminateJobObject ( + PEJOB Job, + KPROCESSOR_MODE AccessMode, + NTSTATUS ExitStatus ) { - DPRINT("PspTerminateJobObject() is unimplemented!\n"); - return STATUS_NOT_IMPLEMENTED; + DPRINT("PspTerminateJobObject() is unimplemented!\n"); + return STATUS_NOT_IMPLEMENTED; } /* * @unimplemented */ -NTSTATUS -STDCALL -NtAssignProcessToJobObject(HANDLE JobHandle, - HANDLE ProcessHandle) +NTSTATUS +STDCALL +NtAssignProcessToJobObject ( + HANDLE JobHandle, + HANDLE ProcessHandle) { - PEPROCESS Process; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - /* make sure we're having a handle with enough rights, especially the to - terminate the process. otherwise one could abuse the job objects to - terminate processes without having rights granted to do so! The reason - I open the process handle before the job handle is that a simple test showed - that it first complains about a invalid process handle! The other way around - would be simpler though... */ - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_TERMINATE, - PsProcessType, - PreviousMode, - (PVOID*)&Process, - NULL); - if(NT_SUCCESS(Status)) - { - if(Process->Job == NULL) - { - PEJOB Job; - - Status = ObReferenceObjectByHandle(JobHandle, - JOB_OBJECT_ASSIGN_PROCESS, - PsJobType, - PreviousMode, - (PVOID*)&Job, - NULL); - if(NT_SUCCESS(Status)) - { - /* lock the process so we can safely assign the process. Note that in the - meanwhile another thread could have assigned this process to a job! */ + PEPROCESS Process; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status; - Status = PsLockProcess(Process, FALSE); - if(NT_SUCCESS(Status)) + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + /* make sure we're having a handle with enough rights, especially the to + terminate the process. otherwise one could abuse the job objects to + terminate processes without having rights granted to do so! The reason + I open the process handle before the job handle is that a simple test showed + that it first complains about a invalid process handle! The other way around + would be simpler though... */ + Status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_TERMINATE, + PsProcessType, + PreviousMode, + (PVOID*)&Process, + NULL); + if(NT_SUCCESS(Status)) + { + if(Process->Job == NULL) + { + PEJOB Job; + + Status = ObReferenceObjectByHandle( + JobHandle, + JOB_OBJECT_ASSIGN_PROCESS, + PsJobType, + PreviousMode, + (PVOID*)&Job, + NULL); + if(NT_SUCCESS(Status)) + { + /* lock the process so we can safely assign the process. Note that in the + meanwhile another thread could have assigned this process to a job! */ + + Status = PsLockProcess(Process, FALSE); + if(NT_SUCCESS(Status)) + { + if(Process->Job == NULL && Process->SessionId == Job->SessionId) + { + /* Just store the pointer to the job object in the process, we'll + assign it later. The reason we can't do this here is that locking + the job object might require it to wait, which is a bad thing + while holding the process lock! */ + Process->Job = Job; + } + else + { + /* process is already assigned to a job or session id differs! */ + Status = STATUS_ACCESS_DENIED; + } + PsUnlockProcess(Process); + + if(NT_SUCCESS(Status)) + { + /* let's actually assign the process to the job as we're not holding + the process lock anymore! */ + Status = PspAssignProcessToJob(Process, Job); + } + } + + ObDereferenceObject(Job); + } + } + else { - if(Process->Job == NULL && Process->SessionId == Job->SessionId) - { - /* Just store the pointer to the job object in the process, we'll - assign it later. The reason we can't do this here is that locking - the job object might require it to wait, which is a bad thing - while holding the process lock! */ - Process->Job = Job; - } - else - { /* process is already assigned to a job or session id differs! */ Status = STATUS_ACCESS_DENIED; - } - PsUnlockProcess(Process); - - if(NT_SUCCESS(Status)) - { - /* let's actually assign the process to the job as we're not holding - the process lock anymore! */ - Status = PspAssignProcessToJob(Process, Job); - } } - ObDereferenceObject(Job); - } + ObDereferenceObject(Process); } - else - { - /* process is already assigned to a job or session id differs! */ - Status = STATUS_ACCESS_DENIED; - } - - ObDereferenceObject(Process); - } - return Status; + return Status; } /* * @unimplemented */ -NTSTATUS -STDCALL -NtCreateJobObject(PHANDLE JobHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes) +NTSTATUS +STDCALL +NtCreateJobObject ( + PHANDLE JobHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes ) { - HANDLE hJob; - PEJOB Job; - KPROCESSOR_MODE PreviousMode; - PEPROCESS CurrentProcess; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); + HANDLE hJob; + PEJOB Job; + KPROCESSOR_MODE PreviousMode; + PEPROCESS CurrentProcess; + NTSTATUS Status = STATUS_SUCCESS; - PreviousMode = ExGetPreviousMode(); - CurrentProcess = PsGetCurrentProcess(); + PAGED_CODE(); - /* check for valid buffers */ - if(PreviousMode == UserMode) - { - _SEH_TRY - { - /* probe with 32bit alignment */ - ProbeForWrite(JobHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + PreviousMode = ExGetPreviousMode(); + CurrentProcess = PsGetCurrentProcess(); - if(!NT_SUCCESS(Status)) + /* check for valid buffers */ + if(PreviousMode == UserMode) { - return Status; + _SEH_TRY + { + /* probe with 32bit alignment */ + ProbeForWrite(JobHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } } - } - - Status = ObCreateObject(PreviousMode, - PsJobType, - ObjectAttributes, - PreviousMode, - NULL, - sizeof(EJOB), - 0, - 0, - (PVOID*)&Job); - - if(NT_SUCCESS(Status)) - { - /* FIXME - Zero all fields as we don't yet implement all of them */ - RtlZeroMemory(Job, sizeof(EJOB)); - - /* make sure that early destruction doesn't attempt to remove the object from - the list before it even gets added! */ - Job->JobLinks.Flink = NULL; - - /* setup the job object */ - InitializeListHead(&Job->ProcessListHead); - Job->SessionId = CurrentProcess->SessionId; /* inherit the session id from the caller */ - - Status = ExInitializeResource(&Job->JobLock); - if(!NT_SUCCESS(Status)) - { - DPRINT1("Failed to initialize job lock!!!\n"); - ObDereferenceObject(Job); - return Status; - } - KeInitializeEvent(&Job->Event, NotificationEvent, FALSE); - - /* link the object into the global job list */ - ExAcquireFastMutex(&PsJobListLock); - InsertTailList(&PsJobListHead, &Job->JobLinks); - ExReleaseFastMutex(&PsJobListLock); - - Status = ObInsertObject(Job, - NULL, - DesiredAccess, - 0, - NULL, - &hJob); + + Status = ObCreateObject(PreviousMode, + PsJobType, + ObjectAttributes, + PreviousMode, + NULL, + sizeof(EJOB), + 0, + 0, + (PVOID*)&Job); + if(NT_SUCCESS(Status)) { - /* pass the handle back to the caller */ - _SEH_TRY - { - /* NOTE: if the caller passed invalid buffers to receive the handle it's his - own fault! the object will still be created and live... It's possible - to find the handle using ObFindHandleForObject()! */ - *JobHandle = hJob; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* FIXME - Zero all fields as we don't yet implement all of them */ + RtlZeroMemory(Job, sizeof(EJOB)); + + /* make sure that early destruction doesn't attempt to remove the object from + the list before it even gets added! */ + Job->JobLinks.Flink = NULL; + + /* setup the job object */ + InitializeListHead(&Job->ProcessListHead); + Job->SessionId = CurrentProcess->SessionId; /* inherit the session id from the caller */ + + Status = ExInitializeResource(&Job->JobLock); + if(!NT_SUCCESS(Status)) + { + DPRINT1("Failed to initialize job lock!!!\n"); + ObDereferenceObject(Job); + return Status; + } + KeInitializeEvent(&Job->Event, NotificationEvent, FALSE); + + /* link the object into the global job list */ + ExAcquireFastMutex(&PsJobListLock); + InsertTailList(&PsJobListHead, &Job->JobLinks); + ExReleaseFastMutex(&PsJobListLock); + + Status = ObInsertObject(Job, + NULL, + DesiredAccess, + 0, + NULL, + &hJob); + if(NT_SUCCESS(Status)) + { + /* pass the handle back to the caller */ + _SEH_TRY + { + /* NOTE: if the caller passed invalid buffers to receive the handle it's his + own fault! the object will still be created and live... It's possible + to find the handle using ObFindHandleForObject()! */ + *JobHandle = hJob; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } } - } - - return Status; + + return Status; } @@ -300,207 +312,203 @@ NtCreateJobObject(PHANDLE JobHandle, */ NTSTATUS STDCALL -NtIsProcessInJob(IN HANDLE ProcessHandle, - IN HANDLE JobHandle OPTIONAL) +NtIsProcessInJob ( + IN HANDLE ProcessHandle, + IN HANDLE JobHandle OPTIONAL ) { - KPROCESSOR_MODE PreviousMode; - PEPROCESS Process; - NTSTATUS Status; - - PreviousMode = ExGetPreviousMode(); - - PAGED_CODE(); - - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_QUERY_INFORMATION, - PsProcessType, - PreviousMode, - (PVOID*)&Process, - NULL); - if(NT_SUCCESS(Status)) - { - /* FIXME - make sure the job object doesn't get exchanged or deleted while trying to - reference it, e.g. by locking it somehow until it is referenced... */ + KPROCESSOR_MODE PreviousMode; + PEPROCESS Process; + NTSTATUS Status; - PEJOB ProcessJob = Process->Job; - - if(ProcessJob != NULL) - { - if(JobHandle == NULL) - { - /* the process is assigned to a job */ - Status = STATUS_PROCESS_IN_JOB; - } - else /* JobHandle != NULL */ - { - PEJOB JobObject; + PreviousMode = ExGetPreviousMode(); - /* get the job object and compare the object pointer with the one assigned to the process */ - Status = ObReferenceObjectByHandle(JobHandle, - JOB_OBJECT_QUERY, - PsJobType, - PreviousMode, - (PVOID*)&JobObject, - NULL); - if(NT_SUCCESS(Status)) - { - Status = ((ProcessJob == JobObject) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB); - ObDereferenceObject(JobObject); - } - } - } - else - { - /* the process is not assigned to any job */ - Status = STATUS_PROCESS_NOT_IN_JOB; - } - ObDereferenceObject(Process); - } + PAGED_CODE(); - return Status; -} - - -/* - * @implemented - */ -NTSTATUS -STDCALL -NtOpenJobObject(PHANDLE JobHandle, - ACCESS_MASK DesiredAccess, - POBJECT_ATTRIBUTES ObjectAttributes) -{ - KPROCESSOR_MODE PreviousMode; - HANDLE hJob; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - /* check for valid buffers */ - if(PreviousMode == UserMode) - { - _SEH_TRY - { - /* probe with 32bit alignment */ - ProbeForWrite(JobHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - if(NT_SUCCESS(Status)) - { - Status = ObOpenObjectByName(ObjectAttributes, - PsJobType, - NULL, - PreviousMode, - DesiredAccess, - NULL, - &hJob); + Status = ObReferenceObjectByHandle( + ProcessHandle, + PROCESS_QUERY_INFORMATION, + PsProcessType, + PreviousMode, + (PVOID*)&Process, + NULL); if(NT_SUCCESS(Status)) { - _SEH_TRY - { - *JobHandle = hJob; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; + /* FIXME - make sure the job object doesn't get exchanged or deleted while trying to + reference it, e.g. by locking it somehow until it is referenced... */ + + PEJOB ProcessJob = Process->Job; + + if(ProcessJob != NULL) + { + if(JobHandle == NULL) + { + /* the process is assigned to a job */ + Status = STATUS_PROCESS_IN_JOB; + } + else /* JobHandle != NULL */ + { + PEJOB JobObject; + + /* get the job object and compare the object pointer with the one assigned to the process */ + Status = ObReferenceObjectByHandle(JobHandle, + JOB_OBJECT_QUERY, + PsJobType, + PreviousMode, + (PVOID*)&JobObject, + NULL); + if(NT_SUCCESS(Status)) + { + Status = ((ProcessJob == JobObject) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB); + ObDereferenceObject(JobObject); + } + } + } + else + { + /* the process is not assigned to any job */ + Status = STATUS_PROCESS_NOT_IN_JOB; + } + ObDereferenceObject(Process); } - } - - return Status; -} - -/* - * @unimplemented - */ -NTSTATUS -STDCALL -NtQueryInformationJobObject(HANDLE JobHandle, - JOBOBJECTINFOCLASS JobInformationClass, - PVOID JobInformation, - ULONG JobInformationLength, - PULONG ReturnLength) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - - -/* - * @unimplemented - */ -NTSTATUS -STDCALL -NtSetInformationJobObject(HANDLE JobHandle, - JOBOBJECTINFOCLASS JobInformationClass, - PVOID JobInformation, - ULONG JobInformationLength) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - - -/* - * @unimplemented - */ -NTSTATUS -STDCALL -NtTerminateJobObject(HANDLE JobHandle, - NTSTATUS ExitStatus) -{ - KPROCESSOR_MODE PreviousMode; - PEJOB Job; - NTSTATUS Status; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - Status = ObReferenceObjectByHandle(JobHandle, - JOB_OBJECT_TERMINATE, - PsJobType, - PreviousMode, - (PVOID*)&Job, - NULL); - if(NT_SUCCESS(Status)) - { - Status = PspTerminateJobObject(Job, - PreviousMode, - ExitStatus); - ObDereferenceObject(Job); - } - - return Status; + return Status; } /* * @implemented */ -PVOID -STDCALL -PsGetJobLock(PEJOB Job) +NTSTATUS +STDCALL +NtOpenJobObject ( + PHANDLE JobHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes) { - ASSERT(Job); - return (PVOID)&Job->JobLock; + KPROCESSOR_MODE PreviousMode; + HANDLE hJob; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + /* check for valid buffers */ + if(PreviousMode == UserMode) + { + _SEH_TRY + { + /* probe with 32bit alignment */ + ProbeForWrite(JobHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + + if(NT_SUCCESS(Status)) + { + Status = ObOpenObjectByName(ObjectAttributes, + PsJobType, + NULL, + PreviousMode, + DesiredAccess, + NULL, + &hJob); + if(NT_SUCCESS(Status)) + { + _SEH_TRY + { + *JobHandle = hJob; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + } + + return Status; +} + + +/* + * @unimplemented + */ +NTSTATUS +STDCALL +NtQueryInformationJobObject ( + HANDLE JobHandle, + JOBOBJECTINFOCLASS JobInformationClass, + PVOID JobInformation, + ULONG JobInformationLength, + PULONG ReturnLength ) +{ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + + +/* + * @unimplemented + */ +NTSTATUS +STDCALL +NtSetInformationJobObject ( + HANDLE JobHandle, + JOBOBJECTINFOCLASS JobInformationClass, + PVOID JobInformation, + ULONG JobInformationLength) +{ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + + +/* + * @unimplemented + */ +NTSTATUS +STDCALL +NtTerminateJobObject ( + HANDLE JobHandle, + NTSTATUS ExitStatus ) +{ + KPROCESSOR_MODE PreviousMode; + PEJOB Job; + NTSTATUS Status; + + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + Status = ObReferenceObjectByHandle( + JobHandle, + JOB_OBJECT_TERMINATE, + PsJobType, + PreviousMode, + (PVOID*)&Job, + NULL); + if(NT_SUCCESS(Status)) + { + Status = PspTerminateJobObject( + Job, + PreviousMode, + ExitStatus); + ObDereferenceObject(Job); + } + + return Status; } @@ -509,10 +517,22 @@ PsGetJobLock(PEJOB Job) */ PVOID STDCALL -PsGetJobSessionId(PEJOB Job) +PsGetJobLock ( PEJOB Job ) { - ASSERT(Job); - return (PVOID)Job->SessionId; + ASSERT(Job); + return (PVOID)&Job->JobLock; +} + + +/* + * @implemented + */ +PVOID +STDCALL +PsGetJobSessionId ( PEJOB Job ) +{ + ASSERT(Job); + return (PVOID)Job->SessionId; } @@ -521,10 +541,10 @@ PsGetJobSessionId(PEJOB Job) */ ULONG STDCALL -PsGetJobUIRestrictionsClass(PEJOB Job) +PsGetJobUIRestrictionsClass ( PEJOB Job ) { - ASSERT(Job); - return Job->UIRestrictionsClass; + ASSERT(Job); + return Job->UIRestrictionsClass; } @@ -533,12 +553,13 @@ PsGetJobUIRestrictionsClass(PEJOB Job) */ VOID STDCALL -PsSetJobUIRestrictionsClass(PEJOB Job, - ULONG UIRestrictionsClass) +PsSetJobUIRestrictionsClass ( + PEJOB Job, + ULONG UIRestrictionsClass) { - ASSERT(Job); - InterlockedExchangeUL(&Job->UIRestrictionsClass, UIRestrictionsClass); - /* FIXME - walk through the job process list and update the restrictions? */ + ASSERT(Job); + InterlockedExchangeUL(&Job->UIRestrictionsClass, UIRestrictionsClass); + /* FIXME - walk through the job process list and update the restrictions? */ } /* EOF */ diff --git a/reactos/ntoskrnl/ps/kill.c b/reactos/ntoskrnl/ps/kill.c index acd9271c3e5..cda7b57c0ce 100644 --- a/reactos/ntoskrnl/ps/kill.c +++ b/reactos/ntoskrnl/ps/kill.c @@ -1,11 +1,11 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ps/kill.c - * PURPOSE: Terminating a thread + * PURPOSE: Thread Termination and Reaping * - * PROGRAMMERS: David Welch (welch@cwcom.net) + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + * David Welch (welch@cwcom.net) */ /* INCLUDES *****************************************************************/ @@ -15,485 +15,627 @@ #include /* GLOBALS *******************************************************************/ - -VOID PsTerminateCurrentThread(NTSTATUS ExitStatus); -NTSTATUS STDCALL NtCallTerminatePorts(PETHREAD Thread); - + #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C') -LIST_ENTRY ThreadsToReapHead; - -#define TERMINATE_PROC 0x1 -#define TERMINATE_APC 0x2 +PETHREAD PspReaperList = NULL; +WORK_QUEUE_ITEM PspReaperWorkItem; +BOOLEAN PspReaping = FALSE; +extern LIST_ENTRY PsActiveProcessHead; +extern FAST_MUTEX PspActiveProcessMutex; /* FUNCTIONS *****************************************************************/ +STDCALL VOID -PsInitializeThreadReaper(VOID) +PspReapRoutine(PVOID Context) { - InitializeListHead(&ThreadsToReapHead); + KIRQL OldIrql; + PETHREAD Thread, NewThread; + + /* Acquire lock */ + DPRINT("Evil reaper running!!\n"); + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Get the first Thread Entry */ + Thread = PspReaperList; + PspReaperList = NULL; + DPRINT("PspReaperList: %x\n", Thread); + + /* Check to see if the list is empty */ + do { + + /* Unlock the Dispatcher */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Is there a thread on the list? */ + while (Thread) { + + /* Get the next Thread */ + DPRINT("Thread: %x\n", Thread); + DPRINT("Thread: %x\n", Thread->ReaperLink); + NewThread = Thread->ReaperLink; + + /* Remove reference to current thread */ + ObDereferenceObject(Thread); + + /* Move to next Thread */ + Thread = NewThread; + } + + /* No more linked threads... Reacquire the Lock */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Now try to get a new thread from the list */ + Thread = PspReaperList; + PspReaperList = NULL; + DPRINT("PspReaperList: %x\n", Thread); + + /* Loop again if there is a new thread */ + } while (Thread); + + PspReaping = FALSE; + DPRINT("Done reaping\n"); + KeReleaseDispatcherDatabaseLock(OldIrql); } VOID -PsReapThreads(VOID) +STDCALL +PspTerminateProcessThreads(PEPROCESS Process, + NTSTATUS ExitStatus) { - KIRQL oldlvl; - PETHREAD Thread; - PLIST_ENTRY ListEntry; - - oldlvl = KeAcquireDispatcherDatabaseLock(); - while((ListEntry = RemoveHeadList(&ThreadsToReapHead)) != &ThreadsToReapHead) - { - PiNrThreadsAwaitingReaping--; - KeReleaseDispatcherDatabaseLock(oldlvl); - Thread = CONTAINING_RECORD(ListEntry, ETHREAD, TerminationPortList); - - ObDereferenceObject(Thread); - oldlvl = KeAcquireDispatcherDatabaseLock(); - } - KeReleaseDispatcherDatabaseLock(oldlvl); -} - -VOID -PsQueueThreadReap(PETHREAD Thread) -{ - InsertTailList(&ThreadsToReapHead, &Thread->TerminationPortList); - PiNrThreadsAwaitingReaping++; -} - -VOID -PiTerminateProcessThreads(PEPROCESS Process, - NTSTATUS ExitStatus) -{ - KIRQL oldlvl; - PLIST_ENTRY current_entry; - PETHREAD current, CurrentThread = PsGetCurrentThread(); + PLIST_ENTRY CurrentEntry; + PETHREAD Thread, CurrentThread = PsGetCurrentThread(); - DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n", - Process, ExitStatus); - - oldlvl = KeAcquireDispatcherDatabaseLock(); - - current_entry = Process->ThreadListHead.Flink; - while (current_entry != &Process->ThreadListHead) - { - current = CONTAINING_RECORD(current_entry, ETHREAD, - ThreadListEntry); - if (current != CurrentThread && current->HasTerminated == 0) - { - DPRINT("Terminating %x, current thread: %x, " - "thread's process: %x\n", current, PsGetCurrentThread(), - current->ThreadsProcess); - KeReleaseDispatcherDatabaseLock(oldlvl); - PsTerminateOtherThread(current, ExitStatus); - oldlvl = KeAcquireDispatcherDatabaseLock(); - current_entry = Process->ThreadListHead.Flink; - } - else - { - current_entry = current_entry->Flink; - } - } - KeReleaseDispatcherDatabaseLock(oldlvl); - DPRINT("Finished PiTerminateProcessThreads()\n"); + CurrentEntry = Process->ThreadListHead.Flink; + while (CurrentEntry != &Process->ThreadListHead) { + + /* Get the Current Thread */ + Thread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry); + + /* Move to the Next Thread */ + CurrentEntry = CurrentEntry->Flink; + + /* Make sure it's not the one we're in */ + if (Thread != CurrentThread) { + + /* Make sure it didn't already terminate */ + if (!Thread->HasTerminated) { + + Thread->HasTerminated = TRUE; + + /* Terminate it by APC */ + PspTerminateThreadByPointer(Thread, ExitStatus); + } + } + } } -VOID -PsTerminateCurrentThread(NTSTATUS ExitStatus) +VOID +STDCALL +PspDeleteProcess(PVOID ObjectBody) +{ + PEPROCESS Process = (PEPROCESS)ObjectBody; + + DPRINT("PiDeleteProcess(ObjectBody %x)\n",Process); + + /* Delete the CID Handle */ + if(Process->UniqueProcessId != NULL) { + + PsDeleteCidHandle(Process->UniqueProcessId, PsProcessType); + } + + /* KDB hook */ + KDB_DELETEPROCESS_HOOK(Process); + + /* Dereference the Token and release Memory Information */ + ObDereferenceObject(Process->Token); + MmReleaseMmInfo(Process); + + /* Delete the W32PROCESS structure if there's one associated */ + if(Process->Win32Process != NULL) ExFreePool(Process->Win32Process); +} + +VOID +STDCALL +PspDeleteThread(PVOID ObjectBody) +{ + PETHREAD Thread = (PETHREAD)ObjectBody; + PEPROCESS Process = Thread->ThreadsProcess; + + DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody, Thread->ThreadsProcess); + + /* Deassociate the Process */ + Thread->ThreadsProcess = NULL; + + /* Delete the CID Handle */ + if(Thread->Cid.UniqueThread != NULL) { + + PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType); + } + + /* Free the W32THREAD structure if present */ + if(Thread->Tcb.Win32Thread != NULL) ExFreePool (Thread->Tcb.Win32Thread); + + /* Release the Thread */ + KeReleaseThread(ETHREAD_TO_KTHREAD(Thread)); + + /* Dereference the Process */ + ObDereferenceObject(Process); +} + /* * FUNCTION: Terminates the current thread + * See "Windows Internals" - Chapter 13, Page 50-53 */ +VOID +STDCALL +PspExitThread(NTSTATUS ExitStatus) { - KIRQL oldIrql; - PETHREAD CurrentThread; - PLIST_ENTRY current_entry; - PKMUTANT Mutant; - BOOLEAN Last; - PEPROCESS CurrentProcess; - SIZE_T Length = PAGE_SIZE; - PVOID TebBlock; + PETHREAD CurrentThread; + BOOLEAN Last; + PEPROCESS CurrentProcess; + SIZE_T Length = PAGE_SIZE; + PVOID TebBlock; + PTERMINATION_PORT TerminationPort; - DPRINT("PsTerminateCurrentThread(ExitStatus %x)\n", ExitStatus); + DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread()); - CurrentThread = PsGetCurrentThread(); + /* Get the Current Thread and Process */ + CurrentThread = PsGetCurrentThread(); + CurrentProcess = CurrentThread->ThreadsProcess; + + /* Set the Exit Status and Exit Time */ + CurrentThread->ExitStatus = ExitStatus; + KeQuerySystemTime(&CurrentThread->ExitTime); - oldIrql = KeAcquireDispatcherDatabaseLock(); - if (CurrentThread->HasTerminated & TERMINATE_PROC) - { - KeReleaseDispatcherDatabaseLock(oldIrql); - return; - } - CurrentThread->HasTerminated |= TERMINATE_PROC; - KeReleaseDispatcherDatabaseLock(oldIrql); - - KeLowerIrql(PASSIVE_LEVEL); - - CurrentProcess = CurrentThread->ThreadsProcess; - - /* Can't terminate a thread if it attached another process */ - if (AttachedApcEnvironment == CurrentThread->Tcb.ApcStateIndex) - { + /* Can't terminate a thread if it attached another process */ + if (KeIsAttachedProcess()) { + KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG) CurrentProcess, (ULONG) CurrentThread->Tcb.ApcState.Process, (ULONG) CurrentThread->Tcb.ApcStateIndex, (ULONG) CurrentThread); - } + } + + /* Lower to Passive Level */ + KeLowerIrql(PASSIVE_LEVEL); - KeCancelTimer(&CurrentThread->Tcb.Timer); + /* Lock the Process before we modify its thread entries */ + PsLockProcess(CurrentProcess, FALSE); + + /* wake up the thread so we don't deadlock on PsLockProcess */ + KeForceResumeThread(&CurrentThread->Tcb); + + /* Run Thread Notify Routines before we desintegrate the thread */ + PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE); - oldIrql = KeAcquireDispatcherDatabaseLock(); - - DPRINT("terminating %x\n",CurrentThread); - - CurrentThread->ExitStatus = ExitStatus; - KeQuerySystemTime((PLARGE_INTEGER)&CurrentThread->ExitTime); - - /* If the ProcessoR Control Block's NpxThread points to the current thread - * unset it. - */ - InterlockedCompareExchangePointer(&KeGetCurrentKPCR()->PrcbData.NpxThread, - NULL, ETHREAD_TO_KTHREAD(CurrentThread)); - - KeReleaseDispatcherDatabaseLock(oldIrql); - - PsLockProcess(CurrentProcess, FALSE); - - /* Cancel I/O for the thread. */ - IoCancelThreadIo(CurrentThread); - - /* Remove the thread from the thread list of its process */ - RemoveEntryList(&CurrentThread->ThreadListEntry); - Last = IsListEmpty(&CurrentProcess->ThreadListHead); - PsUnlockProcess(CurrentProcess); - - /* Notify subsystems of the thread termination */ - PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE); - PsTerminateWin32Thread(CurrentThread); - - /* Free the TEB */ - if(CurrentThread->Tcb.Teb) - { - DPRINT("Decommit teb at %p\n", CurrentThread->Tcb.Teb); - ExAcquireFastMutex(&CurrentProcess->TebLock); - TebBlock = MM_ROUND_DOWN(CurrentThread->Tcb.Teb, MM_VIRTMEM_GRANULARITY); - ZwFreeVirtualMemory(NtCurrentProcess(), - (PVOID *)&CurrentThread->Tcb.Teb, - &Length, - MEM_DECOMMIT); - DPRINT("teb %p, TebBlock %p\n", CurrentThread->Tcb.Teb, TebBlock); - if (TebBlock != CurrentProcess->TebBlock || - CurrentProcess->TebBlock == CurrentProcess->TebLastAllocated) - { - MmLockAddressSpace(&CurrentProcess->AddressSpace); - MmReleaseMemoryAreaIfDecommitted(CurrentProcess, &CurrentProcess->AddressSpace, TebBlock); - MmUnlockAddressSpace(&CurrentProcess->AddressSpace); - } - CurrentThread->Tcb.Teb = NULL; - ExReleaseFastMutex(&CurrentProcess->TebLock); - } - - /* abandon all owned mutants */ - current_entry = CurrentThread->Tcb.MutantListHead.Flink; - while (current_entry != &CurrentThread->Tcb.MutantListHead) - { - Mutant = CONTAINING_RECORD(current_entry, KMUTANT, - MutantListEntry); - KeReleaseMutant(Mutant, - MUTANT_INCREMENT, - TRUE, - FALSE); - current_entry = CurrentThread->Tcb.MutantListHead.Flink; - } - - oldIrql = KeAcquireDispatcherDatabaseLock(); - CurrentThread->Tcb.DispatcherHeader.SignalState = TRUE; - KiDispatcherObjectWake(&CurrentThread->Tcb.DispatcherHeader, IO_NO_INCREMENT); - KeReleaseDispatcherDatabaseLock (oldIrql); - - /* The last thread shall close the door on exit */ - if(Last) - { - /* save the last thread exit status */ + /* Remove the thread from the thread list of its process */ + RemoveEntryList(&CurrentThread->ThreadListEntry); + Last = IsListEmpty(&CurrentProcess->ThreadListHead); + + /* Set the last Thread Exit Status */ CurrentProcess->LastThreadExitStatus = ExitStatus; - PspRunCreateProcessNotifyRoutines(CurrentProcess, FALSE); - PsTerminateWin32Process(CurrentProcess); - PiTerminateProcess(CurrentProcess, ExitStatus); - } + if (Last) { - oldIrql = KeAcquireDispatcherDatabaseLock(); - -#ifdef _ENABLE_THRDEVTPAIR - ExpSwapThreadEventPair(CurrentThread, NULL); /* Release the associated eventpair object, if there was one */ -#endif /* _ENABLE_THRDEVTPAIR */ - - ASSERT(CurrentThread->Tcb.WaitBlockList == NULL); - - PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1); - DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread()); - KEBUGCHECK(0); -} - -VOID STDCALL -PiTerminateThreadRundownRoutine(PKAPC Apc) -{ - ExFreePool(Apc); -} - -VOID STDCALL -PiTerminateThreadKernelRoutine(PKAPC Apc, - PKNORMAL_ROUTINE* NormalRoutine, - PVOID* NormalContext, - PVOID* SystemArgument1, - PVOID* SystemArguemnt2) -{ - ExFreePool(Apc); -} - -VOID STDCALL -PiTerminateThreadNormalRoutine(PVOID NormalContext, - PVOID SystemArgument1, - PVOID SystemArgument2) -{ - PsTerminateCurrentThread((NTSTATUS)SystemArgument1); -} - -VOID -PsTerminateOtherThread(PETHREAD Thread, - NTSTATUS ExitStatus) -/* - * FUNCTION: Terminate a thread when calling from another thread's context - * NOTES: This function must be called with PiThreadLock held - */ -{ - PKAPC Apc; - KIRQL OldIrql; - - DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n", - Thread, ExitStatus); - - OldIrql = KeAcquireDispatcherDatabaseLock(); - if (Thread->HasTerminated & TERMINATE_APC) - { - KeReleaseDispatcherDatabaseLock (OldIrql); - return; - } - Thread->HasTerminated |= TERMINATE_APC; - KeReleaseDispatcherDatabaseLock (OldIrql); - Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC); - KeInitializeApc(Apc, - &Thread->Tcb, - OriginalApcEnvironment, - PiTerminateThreadKernelRoutine, - PiTerminateThreadRundownRoutine, - PiTerminateThreadNormalRoutine, - KernelMode, - NULL); - KeInsertQueueApc(Apc, - (PVOID)ExitStatus, - NULL, - IO_NO_INCREMENT); - - OldIrql = KeAcquireDispatcherDatabaseLock(); - if (THREAD_STATE_BLOCKED == Thread->Tcb.State && UserMode == Thread->Tcb.WaitMode) - { - DPRINT("Unblocking thread\n"); - KiAbortWaitThread((PKTHREAD)Thread, STATUS_THREAD_IS_TERMINATING); + /* Save the Exit Time if not already done by NtTerminateProcess. This + happens when the last thread just terminates without explicitly + terminating the process. */ + CurrentProcess->ExitTime = CurrentThread->ExitTime; } - KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Check if the process has a debug port */ + if (CurrentProcess->DebugPort) { + + /* Notify the Debug API. TODO */ + //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus); + } + + /* Process the Termination Ports */ + TerminationPort = CurrentThread->TerminationPort; + DPRINT("TerminationPort: %p\n", TerminationPort); + while (TerminationPort) { + + /* Send the LPC Message */ + LpcSendTerminationPort(TerminationPort->Port, CurrentThread->CreateTime); + + /* Free the Port */ + ExFreePool(TerminationPort); + + /* Get the next one */ + TerminationPort = TerminationPort->Next; + DPRINT("TerminationPort: %p\n", TerminationPort); + } + + /* Rundown Win32 Structures */ + PsTerminateWin32Thread(CurrentThread); + if (Last) PsTerminateWin32Process(CurrentProcess); + + /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */ + //CmNotifyRunDown(CurrentThread); + + /* Free the TEB */ + if(CurrentThread->Tcb.Teb) { + + DPRINT("Decommit teb at %p\n", CurrentThread->Tcb.Teb); + TebBlock = MM_ROUND_DOWN(CurrentThread->Tcb.Teb, MM_VIRTMEM_GRANULARITY); + + ZwFreeVirtualMemory(NtCurrentProcess(), + (PVOID *)&CurrentThread->Tcb.Teb, + &Length, + MEM_DECOMMIT); + + DPRINT("teb %p, TebBlock %p\n", CurrentThread->Tcb.Teb, TebBlock); + + if (TebBlock != CurrentProcess->TebBlock || + CurrentProcess->TebBlock == CurrentProcess->TebLastAllocated) { + + MmLockAddressSpace(&CurrentProcess->AddressSpace); + MmReleaseMemoryAreaIfDecommitted(CurrentProcess, &CurrentProcess->AddressSpace, TebBlock); + MmUnlockAddressSpace(&CurrentProcess->AddressSpace); + } + + CurrentThread->Tcb.Teb = NULL; + } + + /* The last Thread shuts down the Process */ + if (Last) PspExitProcess(CurrentProcess); + + /* Unlock the Process */ + PsUnlockProcess(CurrentProcess); + + /* Cancel I/O for the thread. */ + IoCancelThreadIo(CurrentThread); + + /* Rundown Timers */ + ExTimerRundown(); + KeCancelTimer(&CurrentThread->Tcb.Timer); + + /* If the Processor Control Block's NpxThread points to the current thread + * unset it. + */ + InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread, + NULL, + (PKPROCESS)CurrentThread); + + /* Rundown Mutexes */ + KeRundownThread(); + + /* Terminate the Thread from the Scheduler */ + KeTerminateThread(0); + DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread()); + KEBUGCHECK(0); } -NTSTATUS STDCALL -PiTerminateProcess(PEPROCESS Process, - NTSTATUS ExitStatus) +VOID +STDCALL +PsExitSpecialApc(PKAPC Apc, + PKNORMAL_ROUTINE* NormalRoutine, + PVOID* NormalContext, + PVOID* SystemArgument1, + PVOID* SystemArguemnt2) { - KIRQL OldIrql; - PEPROCESS CurrentProcess; - - DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n", - Process, ExitStatus, ObGetObjectPointerCount(Process), - ObGetObjectHandleCount(Process)); - - ObReferenceObject(Process); - if (InterlockedExchangeUL(&Process->Pcb.State, - PROCESS_STATE_TERMINATED) == - PROCESS_STATE_TERMINATED) - { - ObDereferenceObject(Process); - return(STATUS_SUCCESS); - } - CurrentProcess = PsGetCurrentProcess(); - if (Process != CurrentProcess) - { - KeAttachProcess(&Process->Pcb); - } - ObCloseAllHandles(Process); - if (Process != CurrentProcess) - { - KeDetachProcess(); - } - OldIrql = KeAcquireDispatcherDatabaseLock (); - Process->Pcb.DispatcherHeader.SignalState = TRUE; - KiDispatcherObjectWake(&Process->Pcb.DispatcherHeader, IO_NO_INCREMENT); - KeReleaseDispatcherDatabaseLock (OldIrql); - ObDereferenceObject(Process); - return(STATUS_SUCCESS); + NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext; + + DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x)\n", PsGetCurrentThread(), PsGetCurrentProcess()); + + /* Free the APC */ + ExFreePool(Apc); + + /* Terminate the Thread */ + PspExitThread(ExitStatus); + + /* we should never reach this point! */ + KEBUGCHECK(0); } -NTSTATUS STDCALL -NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL, - IN NTSTATUS ExitStatus) +VOID +STDCALL +PspExitNormalApc(PVOID NormalContext, + PVOID SystemArgument1, + PVOID SystemArgument2) { - NTSTATUS Status; - PEPROCESS Process; - - PAGED_CODE(); - - DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n", - ProcessHandle, ExitStatus); - - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_TERMINATE, - PsProcessType, - KeGetCurrentThread()->PreviousMode, - (PVOID*)&Process, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - Process->ExitStatus = ExitStatus; - PiTerminateProcessThreads(Process, ExitStatus); - if (PsGetCurrentThread()->ThreadsProcess == Process) - { - ObDereferenceObject(Process); - PsTerminateCurrentThread(ExitStatus); - /* - * We should never get here! - */ - return(STATUS_SUCCESS); - } - ObDereferenceObject(Process); - return(STATUS_SUCCESS); + /* Not fully supported yet... must work out some issues that + * I don't understand yet -- Alex + */ + DPRINT1("APC2\n"); + PspExitThread((NTSTATUS)NormalContext); + + /* we should never reach this point! */ + KEBUGCHECK(0); } - -NTSTATUS STDCALL -NtTerminateThread(IN HANDLE ThreadHandle, - IN NTSTATUS ExitStatus) +/* + * See "Windows Internals" - Chapter 13, Page 49 + */ +VOID +STDCALL +PspTerminateThreadByPointer(PETHREAD Thread, + NTSTATUS ExitStatus) { - PETHREAD Thread; - NTSTATUS Status; + PKAPC Apc; + + DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n", + Thread, ExitStatus); + + /* Check if we are already in the right context */ + if (PsGetCurrentThread() == Thread) { + + /* Directly terminate the thread */ + PspExitThread(ExitStatus); + + /* we should never reach this point! */ + KEBUGCHECK(0); + } + + /* Allocate the APC */ + Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC); + + /* Initialize a Kernel Mode APC to Kill the Thread */ + KeInitializeApc(Apc, + &Thread->Tcb, + OriginalApcEnvironment, + PsExitSpecialApc, + NULL, + PspExitNormalApc, + KernelMode, + (PVOID)ExitStatus); + + /* Insert it into the APC Queue */ + KeInsertQueueApc(Apc, + Apc, + NULL, + 2); + + /* Forcefully resume the thread */ + KeForceResumeThread(&Thread->Tcb); +} - PAGED_CODE(); +NTSTATUS +STDCALL +PspExitProcess(PEPROCESS Process) +{ + DPRINT("PspExitProcess 0x%x\n", Process); + + PspRunCreateProcessNotifyRoutines(Process, FALSE); + + /* Remove it from the Active List */ + ExAcquireFastMutex(&PspActiveProcessMutex); + RemoveEntryList(&Process->ProcessListEntry); + ExReleaseFastMutex(&PspActiveProcessMutex); + + /* close all handles associated with our process, this needs to be done + when the last thread still runs */ + ObKillProcess(Process); + + KeSetProcess(&Process->Pcb, IO_NO_INCREMENT); + + return(STATUS_SUCCESS); +} + +NTSTATUS +STDCALL +NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL, + IN NTSTATUS ExitStatus) +{ + NTSTATUS Status; + PEPROCESS Process; + PETHREAD CurrentThread; + BOOLEAN KillByHandle; - Status = ObReferenceObjectByHandle(ThreadHandle, - THREAD_TERMINATE, - PsThreadType, - KeGetCurrentThread()->PreviousMode, - (PVOID*)&Thread, - NULL); - if (Status != STATUS_SUCCESS) - { - return(Status); - } + PAGED_CODE(); - if (Thread == PsGetCurrentThread()) - { - /* dereference the thread object before we kill our thread */ + DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n", + ProcessHandle, ExitStatus); + + KillByHandle = (ProcessHandle != NULL); + + /* Get the Process Object */ + Status = ObReferenceObjectByHandle((KillByHandle ? ProcessHandle : NtCurrentProcess()), + PROCESS_TERMINATE, + PsProcessType, + KeGetPreviousMode(), + (PVOID*)&Process, + NULL); + if (!NT_SUCCESS(Status)) { + + DPRINT1("Invalid handle to Process\n"); + return(Status); + } + + CurrentThread = PsGetCurrentThread(); + + PsLockProcess(Process, FALSE); + + if(Process->ExitTime.QuadPart != 0) + { + PsUnlockProcess(Process); + return STATUS_PROCESS_IS_TERMINATING; + } + + /* Terminate all the Process's Threads */ + PspTerminateProcessThreads(Process, ExitStatus); + + /* only kill the calling thread if it either passed a process handle or + NtCurrentProcess() */ + if (KillByHandle) { + + /* set the exit time as we're about to release the process lock before + we kill ourselves to prevent threads outside of our process trying + to kill us */ + KeQuerySystemTime(&Process->ExitTime); + + /* Only master thread remains... kill it off */ + if (CurrentThread->ThreadsProcess == Process) { + + /* mark our thread as terminating so attempts to terminate it, when + unlocking the process, fail */ + CurrentThread->HasTerminated = TRUE; + + PsUnlockProcess(Process); + + /* we can safely dereference the process because the current thread + holds a reference to it until it gets reaped */ + ObDereferenceObject(Process); + + /* now the other threads get a chance to terminate, we don't wait but + just kill ourselves right now. The process will be run down when the + last thread terminates */ + + PspExitThread(ExitStatus); + + /* we should never reach this point! */ + KEBUGCHECK(0); + } + } + + /* unlock and dereference the process so the threads can kill themselves */ + PsUnlockProcess(Process); + ObDereferenceObject(Process); + + return(STATUS_SUCCESS); +} + +NTSTATUS +STDCALL +NtTerminateThread(IN HANDLE ThreadHandle, + IN NTSTATUS ExitStatus) +{ + PETHREAD Thread; + NTSTATUS Status; + + PAGED_CODE(); + + /* Get the Thread Object */ + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_TERMINATE, + PsThreadType, + KeGetPreviousMode(), + (PVOID*)&Thread, + NULL); + if (!NT_SUCCESS(Status)) { + + DPRINT1("Could not reference thread object\n"); + return(Status); + } + + /* Make sure this is not a system thread */ + if (PsIsSystemThread(Thread)) { + + DPRINT1("Trying to Terminate a system thread!\n"); ObDereferenceObject(Thread); - PsTerminateCurrentThread(ExitStatus); - /* - * We should never get here! - */ - } - else - { - PsTerminateOtherThread(Thread, ExitStatus); - ObDereferenceObject(Thread); - } - return(STATUS_SUCCESS); -} + return STATUS_INVALID_PARAMETER; + } + + /* Check to see if we're running in the same thread */ + if (Thread != PsGetCurrentThread()) { + + /* we need to lock the process to make sure it's not already terminating */ + PsLockProcess(Thread->ThreadsProcess, FALSE); + + /* This isn't our thread, terminate it if not already done */ + if (!Thread->HasTerminated) { + + Thread->HasTerminated = TRUE; + + /* Terminate it */ + PspTerminateThreadByPointer(Thread, ExitStatus); + } + + PsUnlockProcess(Thread->ThreadsProcess); + + /* Dereference the Thread and return */ + ObDereferenceObject(Thread); + + } else { + Thread->HasTerminated = TRUE; + + /* it's safe to dereference thread, there's at least the keep-alive + reference which will be removed by the thread reaper causing the + thread to be finally destroyed */ + ObDereferenceObject(Thread); + + /* Terminate him, he's ours */ + PspExitThread(ExitStatus); + + /* We do never reach this point */ + KEBUGCHECK(0); + } + + return(STATUS_SUCCESS); +} /* * @implemented */ -NTSTATUS STDCALL +NTSTATUS +STDCALL PsTerminateSystemThread(NTSTATUS ExitStatus) -/* - * FUNCTION: Terminates the current thread - * ARGUMENTS: - * ExitStatus = Status to pass to the creater - * RETURNS: Doesn't - */ { - PsTerminateCurrentThread(ExitStatus); - return(STATUS_SUCCESS); + PETHREAD Thread = PsGetCurrentThread(); + + /* Make sure this is a system thread */ + if (!PsIsSystemThread(Thread)) { + + DPRINT1("Trying to Terminate a non-system thread!\n"); + return STATUS_INVALID_PARAMETER; + } + + /* Terminate it for real */ + PspExitThread(ExitStatus); + + /* we should never reach this point! */ + KEBUGCHECK(0); + + return(STATUS_SUCCESS); } -NTSTATUS STDCALL -NtCallTerminatePorts(PETHREAD Thread) -{ - KIRQL oldIrql; - PLIST_ENTRY current_entry; - PEPORT_TERMINATION_REQUEST current; - - PAGED_CODE(); - - KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql); - while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) != - &Thread->TerminationPortList); - { - current = CONTAINING_RECORD(current_entry, - EPORT_TERMINATION_REQUEST, - ThreadListEntry); - KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql); - LpcSendTerminationPort(current->Port, - Thread->CreateTime); - ExFreePool(current); - KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql); - } - KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql); - return(STATUS_SUCCESS); -} - -NTSTATUS STDCALL +NTSTATUS +STDCALL NtRegisterThreadTerminatePort(HANDLE PortHandle) { - NTSTATUS Status; - PEPORT_TERMINATION_REQUEST Request; - PEPORT TerminationPort; - KIRQL oldIrql; - PETHREAD Thread; + NTSTATUS Status; + PTERMINATION_PORT TerminationPort; + PVOID TerminationLpcPort; + PETHREAD Thread; - PAGED_CODE(); + PAGED_CODE(); + + /* Get the Port */ + Status = ObReferenceObjectByHandle(PortHandle, + PORT_ALL_ACCESS, + LpcPortObjectType, + KeGetPreviousMode(), + &TerminationLpcPort, + NULL); + if (!NT_SUCCESS(Status)) { + + DPRINT1("Failed to reference Port\n"); + return(Status); + } - Status = ObReferenceObjectByHandle(PortHandle, - PORT_ALL_ACCESS, - LpcPortObjectType, - KeGetCurrentThread()->PreviousMode, - (PVOID*)&TerminationPort, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - - Request = ExAllocatePool(NonPagedPool, sizeof(EPORT_TERMINATION_REQUEST)); - if(Request != NULL) - { - Request->Port = TerminationPort; - Thread = PsGetCurrentThread(); - KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql); - InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry); - KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql); + /* Allocate the Port and make sure it suceeded */ + if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool, + sizeof(PTERMINATION_PORT), + TAG('P', 's', 'T', '=')))) { + + /* Associate the Port */ + Thread = PsGetCurrentThread(); + TerminationPort->Port = TerminationLpcPort; + DPRINT("TerminationPort: %p\n", TerminationPort); + TerminationPort->Next = Thread->TerminationPort; + Thread->TerminationPort = TerminationPort; + DPRINT("TerminationPort: %p\n", Thread->TerminationPort); - return(STATUS_SUCCESS); - } - else - { - ObDereferenceObject(TerminationPort); - return(STATUS_INSUFFICIENT_RESOURCES); - } + /* Return success */ + return(STATUS_SUCCESS); + + } else { + + /* Dereference and Fail */ + ObDereferenceObject(TerminationPort); + return(STATUS_INSUFFICIENT_RESOURCES); + } } diff --git a/reactos/ntoskrnl/ps/process.c b/reactos/ntoskrnl/ps/process.c index ac2f018c615..6211a6a2afc 100644 --- a/reactos/ntoskrnl/ps/process.c +++ b/reactos/ntoskrnl/ps/process.c @@ -16,7 +16,10 @@ /* GLOBALS ******************************************************************/ +VOID INIT_FUNCTION PsInitClientIDManagment(VOID); + PEPROCESS EXPORTED PsInitialSystemProcess = NULL; +PEPROCESS PsIdleProcess = NULL; POBJECT_TYPE EXPORTED PsProcessType = NULL; @@ -31,44 +34,6 @@ static GENERIC_MAPPING PiProcessMapping = {STANDARD_RIGHTS_READ | PROCESS_QUERY_ STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, PROCESS_ALL_ACCESS}; -static const INFORMATION_CLASS_INFO PsProcessInfoClass[] = -{ - ICI_SQ_SAME( sizeof(PROCESS_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* ProcessBasicInformation */ - ICI_SQ_SAME( sizeof(QUOTA_LIMITS), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessQuotaLimits */ - ICI_SQ_SAME( sizeof(IO_COUNTERS), sizeof(ULONG), ICIF_QUERY ), /* ProcessIoCounters */ - ICI_SQ_SAME( sizeof(VM_COUNTERS), sizeof(ULONG), ICIF_QUERY ), /* ProcessVmCounters */ - ICI_SQ_SAME( sizeof(KERNEL_USER_TIMES), sizeof(ULONG), ICIF_QUERY ), /* ProcessTimes */ - ICI_SQ_SAME( sizeof(KPRIORITY), sizeof(ULONG), ICIF_SET ), /* ProcessBasePriority */ - ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_SET ), /* ProcessRaisePriority */ - ICI_SQ_SAME( sizeof(HANDLE), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessDebugPort */ - ICI_SQ_SAME( sizeof(HANDLE), sizeof(ULONG), ICIF_SET ), /* ProcessExceptionPort */ - ICI_SQ_SAME( sizeof(PROCESS_ACCESS_TOKEN), sizeof(ULONG), ICIF_SET ), /* ProcessAccessToken */ - ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessLdtInformation */ - ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_SET ), /* ProcessLdtSize */ - ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessDefaultHardErrorMode */ - ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_SET ), /* ProcessIoPortHandlers */ - ICI_SQ_SAME( sizeof(POOLED_USAGE_AND_LIMITS), sizeof(ULONG), ICIF_QUERY ), /* ProcessPooledUsageAndLimits */ - ICI_SQ_SAME( sizeof(PROCESS_WS_WATCH_INFORMATION), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessWorkingSetWatch */ - ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_SET ), /* ProcessUserModeIOPL */ - ICI_SQ_SAME( sizeof(BOOLEAN), sizeof(ULONG), ICIF_SET ), /* ProcessEnableAlignmentFaultFixup */ - ICI_SQ_SAME( sizeof(PROCESS_PRIORITY_CLASS), sizeof(USHORT), ICIF_QUERY | ICIF_SET ), /* ProcessPriorityClass */ - ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY ), /* ProcessWx86Information */ - ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY ), /* ProcessHandleCount */ - ICI_SQ_SAME( sizeof(KAFFINITY), sizeof(ULONG), ICIF_SET ), /* ProcessAffinityMask */ - ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessPriorityBoost */ - - ICI_SQ(/*Q*/ sizeof(((PPROCESS_DEVICEMAP_INFORMATION)0x0)->Query), /* ProcessDeviceMap */ - /*S*/ sizeof(((PPROCESS_DEVICEMAP_INFORMATION)0x0)->Set), - /*Q*/ sizeof(ULONG), - /*S*/ sizeof(ULONG), - ICIF_QUERY | ICIF_SET ), - - ICI_SQ_SAME( sizeof(PROCESS_SESSION_INFORMATION), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessSessionInformation */ - ICI_SQ_SAME( sizeof(BOOLEAN), sizeof(ULONG), ICIF_SET ), /* ProcessForegroundInformation */ - ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY ), /* ProcessWow64Information */ - ICI_SQ_SAME( sizeof(UNICODE_STRING), sizeof(ULONG), ICIF_QUERY | ICIF_SIZE_VARIABLE), /* ProcessImageFileName */ -}; - #define MAX_PROCESS_NOTIFY_ROUTINE_COUNT 8 #define MAX_LOAD_IMAGE_NOTIFY_ROUTINE_COUNT 8 @@ -78,26 +43,8 @@ static PLOAD_IMAGE_NOTIFY_ROUTINE PiLoadImageNotifyRoutine[MAX_LOAD_IMAGE_NOTIFY_ROUTINE_COUNT]; -typedef struct -{ - WORK_QUEUE_ITEM WorkQueueItem; - KEVENT Event; - PEPROCESS Process; - BOOLEAN IsWorkerQueue; -} DEL_CONTEXT, *PDEL_CONTEXT; - /* FUNCTIONS *****************************************************************/ -VOID -STDCALL -PsExitSpecialApc(PKAPC Apc, - PKNORMAL_ROUTINE *NormalRoutine, - PVOID *NormalContext, - PVOID *SystemArgument1, - PVOID *SystemArgument2) -{ -} - PEPROCESS PsGetNextProcess(PEPROCESS OldProcess) { @@ -106,34 +53,36 @@ PsGetNextProcess(PEPROCESS OldProcess) if (OldProcess == NULL) { - Status = ObReferenceObjectByPointer(PsInitialSystemProcess, + Status = ObReferenceObjectByPointer(PsIdleProcess, PROCESS_ALL_ACCESS, PsProcessType, KernelMode); if (!NT_SUCCESS(Status)) { - CPRINT("PsGetNextProcess(): ObReferenceObjectByPointer failed for PsInitialSystemProcess\n"); + CPRINT("PsGetNextProcess(): ObReferenceObjectByPointer failed for PsIdleProcess\n"); KEBUGCHECK(0); } - return PsInitialSystemProcess; + return PsIdleProcess; } ExAcquireFastMutex(&PspActiveProcessMutex); NextProcess = OldProcess; while (1) { - if (NextProcess->ProcessListEntry.Blink == &PsActiveProcessHead) + PLIST_ENTRY Flink = (NextProcess == PsIdleProcess ? PsActiveProcessHead.Flink : + NextProcess->ProcessListEntry.Flink); + if (Flink != &PsActiveProcessHead) { - NextProcess = CONTAINING_RECORD(PsActiveProcessHead.Blink, - EPROCESS, - ProcessListEntry); + NextProcess = CONTAINING_RECORD(Flink, + EPROCESS, + ProcessListEntry); } else { - NextProcess = CONTAINING_RECORD(NextProcess->ProcessListEntry.Blink, - EPROCESS, - ProcessListEntry); + NextProcess = NULL; + break; } + Status = ObReferenceObjectByPointer(NextProcess, PROCESS_ALL_ACCESS, PsProcessType, @@ -148,8 +97,7 @@ PsGetNextProcess(PEPROCESS OldProcess) } else if (!NT_SUCCESS(Status)) { - CPRINT("PsGetNextProcess(): ObReferenceObjectByPointer failed\n"); - KEBUGCHECK(0); + continue; } } @@ -159,126 +107,6 @@ PsGetNextProcess(PEPROCESS OldProcess) return(NextProcess); } - -/* - * @implemented - */ -NTSTATUS STDCALL -NtOpenProcessToken(IN HANDLE ProcessHandle, - IN ACCESS_MASK DesiredAccess, - OUT PHANDLE TokenHandle) -{ - return NtOpenProcessTokenEx(ProcessHandle, - DesiredAccess, - 0, - TokenHandle); -} - - -/* - * @unimplemented - */ -NTSTATUS -STDCALL -NtOpenProcessTokenEx( - IN HANDLE ProcessHandle, - IN ACCESS_MASK DesiredAccess, - IN ULONG HandleAttributes, - OUT PHANDLE TokenHandle - ) -{ - PACCESS_TOKEN Token; - HANDLE hToken; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - if(PreviousMode == UserMode) - { - _SEH_TRY - { - ProbeForWrite(TokenHandle, - sizeof(HANDLE), - sizeof(ULONG)); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(!NT_SUCCESS(Status)) - { - return Status; - } - } - - Status = PsOpenTokenOfProcess(ProcessHandle, - &Token); - if(NT_SUCCESS(Status)) - { - Status = ObCreateHandle(PsGetCurrentProcess(), - Token, - DesiredAccess, - FALSE, - &hToken); - ObDereferenceObject(Token); - - _SEH_TRY - { - *TokenHandle = hToken; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - - return Status; -} - - -/* - * @implemented - */ -PACCESS_TOKEN STDCALL -PsReferencePrimaryToken(PEPROCESS Process) -{ - ObReferenceObjectByPointer(Process->Token, - TOKEN_ALL_ACCESS, - SepTokenObjectType, - KernelMode); - return(Process->Token); -} - - -NTSTATUS -PsOpenTokenOfProcess(HANDLE ProcessHandle, - PACCESS_TOKEN* Token) -{ - PEPROCESS Process; - NTSTATUS Status; - - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_QUERY_INFORMATION, - PsProcessType, - ExGetPreviousMode(), - (PVOID*)&Process, - NULL); - if(NT_SUCCESS(Status)) - { - *Token = PsReferencePrimaryToken(Process); - ObDereferenceObject(Process); - } - - return Status; -} - - VOID PiKillMostProcesses(VOID) { @@ -297,14 +125,13 @@ PiKillMostProcesses(VOID) if (current->UniqueProcessId != PsInitialSystemProcess->UniqueProcessId && current->UniqueProcessId != PsGetCurrentProcessId()) { - PiTerminateProcessThreads(current, STATUS_SUCCESS); + PspTerminateProcessThreads(current, STATUS_SUCCESS); } } ExReleaseFastMutex(&PspActiveProcessMutex); } - VOID INIT_FUNCTION PsInitProcessManagment(VOID) { @@ -330,7 +157,7 @@ PsInitProcessManagment(VOID) PsProcessType->Dump = NULL; PsProcessType->Open = NULL; PsProcessType->Close = NULL; - PsProcessType->Delete = PiDeleteProcess; + PsProcessType->Delete = PspDeleteProcess; PsProcessType->Parse = NULL; PsProcessType->Security = NULL; PsProcessType->QueryName = NULL; @@ -348,6 +175,44 @@ PsInitProcessManagment(VOID) RtlZeroMemory(PiProcessNotifyRoutine, sizeof(PiProcessNotifyRoutine)); RtlZeroMemory(PiLoadImageNotifyRoutine, sizeof(PiLoadImageNotifyRoutine)); + /* + * Initialize the idle process + */ + Status = ObCreateObject(KernelMode, + PsProcessType, + NULL, + KernelMode, + NULL, + sizeof(EPROCESS), + 0, + 0, + (PVOID*)&PsIdleProcess); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create the idle process object, Status: 0x%x\n", Status); + KEBUGCHECK(0); + return; + } + + RtlZeroMemory(PsIdleProcess, sizeof(EPROCESS)); + + PsIdleProcess->Pcb.Affinity = 0xFFFFFFFF; + PsIdleProcess->Pcb.IopmOffset = 0xffff; + PsIdleProcess->Pcb.LdtDescriptor[0] = 0; + PsIdleProcess->Pcb.LdtDescriptor[1] = 0; + PsIdleProcess->Pcb.BasePriority = PROCESS_PRIO_IDLE; + PsIdleProcess->Pcb.ThreadQuantum = 6; + InitializeListHead(&PsIdleProcess->Pcb.ThreadListHead); + InitializeListHead(&PsIdleProcess->ThreadListHead); + InitializeListHead(&PsIdleProcess->ProcessListEntry); + KeInitializeDispatcherHeader(&PsIdleProcess->Pcb.DispatcherHeader, + ProcessObject, + sizeof(EPROCESS), + FALSE); + PsIdleProcess->Pcb.DirectoryTableBase = + (LARGE_INTEGER)(LONGLONG)(ULONG)MmGetPageDirectory(); + strcpy(PsIdleProcess->ImageFileName, "Idle"); + /* * Initialize the system process */ @@ -362,7 +227,9 @@ PsInitProcessManagment(VOID) (PVOID*)&PsInitialSystemProcess); if (!NT_SUCCESS(Status)) { - return; + DPRINT1("Failed to create the system process object, Status: 0x%x\n", Status); + KEBUGCHECK(0); + return; } /* System threads may run on any processor. */ @@ -374,14 +241,13 @@ PsInitProcessManagment(VOID) PsInitialSystemProcess->Pcb.ThreadQuantum = 6; InitializeListHead(&PsInitialSystemProcess->Pcb.ThreadListHead); KeInitializeDispatcherHeader(&PsInitialSystemProcess->Pcb.DispatcherHeader, - InternalProcessType, + ProcessObject, sizeof(EPROCESS), FALSE); KProcess = &PsInitialSystemProcess->Pcb; MmInitializeAddressSpace(PsInitialSystemProcess, &PsInitialSystemProcess->AddressSpace); - ObCreateHandleTable(NULL,FALSE,PsInitialSystemProcess); KeInitializeEvent(&PsInitialSystemProcess->LockEvent, SynchronizationEvent, FALSE); PsInitialSystemProcess->LockCount = 0; @@ -400,15 +266,6 @@ PsInitProcessManagment(VOID) strcpy(PsInitialSystemProcess->ImageFileName, "System"); - Status = PsCreateCidHandle(PsInitialSystemProcess, - PsProcessType, - &PsInitialSystemProcess->UniqueProcessId); - if(!NT_SUCCESS(Status)) - { - DPRINT1("Failed to create CID handle (unique process id) for the system process!\n"); - return; - } - PsInitialSystemProcess->Win32WindowStation = (HANDLE)0; InsertHeadList(&PsActiveProcessHead, @@ -418,80 +275,25 @@ PsInitProcessManagment(VOID) SepCreateSystemProcessToken(PsInitialSystemProcess); } -VOID STDCALL -PiDeleteProcessWorker(PVOID pContext) +VOID +PspPostInitSystemProcess(VOID) { - PDEL_CONTEXT Context; - PEPROCESS CurrentProcess; - PEPROCESS Process; - - Context = (PDEL_CONTEXT)pContext; - Process = Context->Process; - CurrentProcess = PsGetCurrentProcess(); - - DPRINT("PiDeleteProcess(ObjectBody %x)\n",Process); - - if (CurrentProcess != Process) - { - KeAttachProcess(&Process->Pcb); - } - - ExAcquireFastMutex(&PspActiveProcessMutex); - RemoveEntryList(&Process->ProcessListEntry); - ExReleaseFastMutex(&PspActiveProcessMutex); - - /* KDB hook */ - KDB_DELETEPROCESS_HOOK(Process); - - ObDereferenceObject(Process->Token); - ObDeleteHandleTable(Process); - - if (CurrentProcess != Process) - { - KeDetachProcess(); - } - - MmReleaseMmInfo(Process); - if (Context->IsWorkerQueue) - { - KeSetEvent(&Context->Event, IO_NO_INCREMENT, FALSE); - } -} - -VOID STDCALL -PiDeleteProcess(PVOID ObjectBody) -{ - DEL_CONTEXT Context; - - Context.Process = (PEPROCESS)ObjectBody; - - if (PsGetCurrentProcess() == Context.Process || - PsGetCurrentThread()->ThreadsProcess == Context.Process) - { - KEBUGCHECK(0); - } - - if (PsGetCurrentThread()->ThreadsProcess == PsGetCurrentProcess()) - { - Context.IsWorkerQueue = FALSE; - PiDeleteProcessWorker(&Context); - } - else - { - Context.IsWorkerQueue = TRUE; - KeInitializeEvent(&Context.Event, NotificationEvent, FALSE); - ExInitializeWorkItem (&Context.WorkQueueItem, PiDeleteProcessWorker, &Context); - ExQueueWorkItem(&Context.WorkQueueItem, HyperCriticalWorkQueue); - if (KeReadStateEvent(&Context.Event) == 0) - { - KeWaitForSingleObject(&Context.Event, Executive, KernelMode, FALSE, NULL); - } - } - - if(((PEPROCESS)ObjectBody)->Win32Process != NULL) + NTSTATUS Status; + + /* this routine is called directly after the exectuive handle tables were + initialized. We'll set up the Client ID handle table and assign the system + process a PID */ + PsInitClientIDManagment(); + + ObCreateHandleTable(NULL, FALSE, PsInitialSystemProcess); + + Status = PsCreateCidHandle(PsInitialSystemProcess, + PsProcessType, + &PsInitialSystemProcess->UniqueProcessId); + if(!NT_SUCCESS(Status)) { - /* delete the W32PROCESS structure if there's one associated */ - ExFreePool (((PEPROCESS)ObjectBody)->Win32Process); + DPRINT1("Failed to create CID handle (unique process id) for the system process!\n"); + KEBUGCHECK(0); } } @@ -823,7 +625,7 @@ exitdereferenceobjects: } KeInitializeDispatcherHeader(&KProcess->DispatcherHeader, - InternalProcessType, + ProcessObject, sizeof(EPROCESS), FALSE); @@ -861,11 +663,10 @@ exitdereferenceobjects: Process->Win32WindowStation = (HANDLE)0; ExAcquireFastMutex(&PspActiveProcessMutex); - InsertHeadList(&PsActiveProcessHead, &Process->ProcessListEntry); + InsertTailList(&PsActiveProcessHead, &Process->ProcessListEntry); InitializeListHead(&Process->ThreadListHead); ExReleaseFastMutex(&PspActiveProcessMutex); - ExInitializeFastMutex(&Process->TebLock); Process->Pcb.State = PROCESS_STATE_ACTIVE; /* @@ -1304,1050 +1105,6 @@ NtOpenProcess(OUT PHANDLE ProcessHandle, DPRINT("NtOpenProcess() = STATUS_UNSUCCESSFUL\n"); return(STATUS_UNSUCCESSFUL); } - return(STATUS_UNSUCCESSFUL); -} - - -/* - * @unimplemented - */ -NTSTATUS STDCALL -NtQueryInformationProcess(IN HANDLE ProcessHandle, - IN PROCESSINFOCLASS ProcessInformationClass, - OUT PVOID ProcessInformation, - IN ULONG ProcessInformationLength, - OUT PULONG ReturnLength OPTIONAL) -{ - PEPROCESS Process; - KPROCESSOR_MODE PreviousMode; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - DefaultQueryInfoBufferCheck(ProcessInformationClass, - PsProcessInfoClass, - ProcessInformation, - ProcessInformationLength, - ReturnLength, - PreviousMode, - &Status); - if(!NT_SUCCESS(Status)) - { - DPRINT1("NtQueryInformationProcess() failed, Status: 0x%x\n", Status); - return Status; - } - - /* - * TODO: Here we should probably check that ProcessInformationLength - * bytes indeed are writable at address ProcessInformation. - */ - - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_QUERY_INFORMATION, - PsProcessType, - PreviousMode, - (PVOID*)&Process, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - - switch (ProcessInformationClass) - { - case ProcessBasicInformation: - { - PPROCESS_BASIC_INFORMATION ProcessBasicInformationP = - (PPROCESS_BASIC_INFORMATION)ProcessInformation; - - _SEH_TRY - { - ProcessBasicInformationP->ExitStatus = Process->ExitStatus; - ProcessBasicInformationP->PebBaseAddress = Process->Peb; - ProcessBasicInformationP->AffinityMask = Process->Pcb.Affinity; - ProcessBasicInformationP->UniqueProcessId = - Process->UniqueProcessId; - ProcessBasicInformationP->InheritedFromUniqueProcessId = - Process->InheritedFromUniqueProcessId; - ProcessBasicInformationP->BasePriority = - Process->Pcb.BasePriority; - - if (ReturnLength) - { - *ReturnLength = sizeof(PROCESS_BASIC_INFORMATION); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessQuotaLimits: - case ProcessIoCounters: - Status = STATUS_NOT_IMPLEMENTED; - break; - - case ProcessTimes: - { - PKERNEL_USER_TIMES ProcessTimeP = (PKERNEL_USER_TIMES)ProcessInformation; - _SEH_TRY - { - ProcessTimeP->CreateTime = Process->CreateTime; - ProcessTimeP->UserTime.QuadPart = Process->Pcb.UserTime * 100000LL; - ProcessTimeP->KernelTime.QuadPart = Process->Pcb.KernelTime * 100000LL; - ProcessTimeP->ExitTime = Process->ExitTime; - - if (ReturnLength) - { - *ReturnLength = sizeof(KERNEL_USER_TIMES); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessDebugPort: - { - _SEH_TRY - { - *(PHANDLE)ProcessInformation = (Process->DebugPort != NULL ? (HANDLE)-1 : NULL); - if (ReturnLength) - { - *ReturnLength = sizeof(HANDLE); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessLdtInformation: - case ProcessWorkingSetWatch: - case ProcessWx86Information: - Status = STATUS_NOT_IMPLEMENTED; - break; - - case ProcessHandleCount: - { - ULONG HandleCount = ObpGetHandleCountByHandleTable(&Process->HandleTable); - - _SEH_TRY - { - *(PULONG)ProcessInformation = HandleCount; - if (ReturnLength) - { - *ReturnLength = sizeof(ULONG); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessSessionInformation: - { - PPROCESS_SESSION_INFORMATION SessionInfo = (PPROCESS_SESSION_INFORMATION)ProcessInformation; - - _SEH_TRY - { - SessionInfo->SessionId = Process->SessionId; - if (ReturnLength) - { - *ReturnLength = sizeof(PROCESS_SESSION_INFORMATION); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessWow64Information: - DPRINT1("We currently don't support the ProcessWow64Information information class!\n"); - Status = STATUS_NOT_IMPLEMENTED; - break; - - case ProcessVmCounters: - { - PVM_COUNTERS pOut = (PVM_COUNTERS)ProcessInformation; - - _SEH_TRY - { - pOut->PeakVirtualSize = Process->PeakVirtualSize; - /* - * Here we should probably use VirtualSize.LowPart, but due to - * incompatibilities in current headers (no unnamed union), - * I opted for cast. - */ - pOut->VirtualSize = (ULONG)Process->VirtualSize.QuadPart; - pOut->PageFaultCount = Process->Vm.PageFaultCount; - pOut->PeakWorkingSetSize = Process->Vm.PeakWorkingSetSize; - pOut->WorkingSetSize = Process->Vm.WorkingSetSize; - pOut->QuotaPeakPagedPoolUsage = Process->QuotaPeakPoolUsage[0]; // TODO: Verify! - pOut->QuotaPagedPoolUsage = Process->QuotaPoolUsage[0]; // TODO: Verify! - pOut->QuotaPeakNonPagedPoolUsage = Process->QuotaPeakPoolUsage[1]; // TODO: Verify! - pOut->QuotaNonPagedPoolUsage = Process->QuotaPoolUsage[1]; // TODO: Verify! - pOut->PagefileUsage = Process->PagefileUsage; - pOut->PeakPagefileUsage = Process->PeakPagefileUsage; - - if (ReturnLength) - { - *ReturnLength = sizeof(VM_COUNTERS); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessDefaultHardErrorMode: - { - PULONG HardErrMode = (PULONG)ProcessInformation; - _SEH_TRY - { - *HardErrMode = Process->DefaultHardErrorProcessing; - if (ReturnLength) - { - *ReturnLength = sizeof(ULONG); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessPriorityBoost: - { - PULONG BoostEnabled = (PULONG)ProcessInformation; - - _SEH_TRY - { - *BoostEnabled = Process->Pcb.DisableBoost ? FALSE : TRUE; - - if (ReturnLength) - { - *ReturnLength = sizeof(ULONG); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessDeviceMap: - { - PROCESS_DEVICEMAP_INFORMATION DeviceMap; - - ObQueryDeviceMapInformation(Process, &DeviceMap); - - _SEH_TRY - { - *(PPROCESS_DEVICEMAP_INFORMATION)ProcessInformation = DeviceMap; - if (ReturnLength) - { - *ReturnLength = sizeof(PROCESS_DEVICEMAP_INFORMATION); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessPriorityClass: - { - PUSHORT Priority = (PUSHORT)ProcessInformation; - - _SEH_TRY - { - *Priority = Process->PriorityClass; - - if (ReturnLength) - { - *ReturnLength = sizeof(USHORT); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessImageFileName: - { - /* - * We DO NOT return the file name stored in the EPROCESS structure. - * Propably if we can't find a PEB or ProcessParameters structure for the - * process! - */ - if(Process->Peb != NULL) - { - PRTL_USER_PROCESS_PARAMETERS ProcParams = NULL; - UNICODE_STRING LocalDest; - BOOLEAN Attached; - ULONG ImagePathLen = 0; - PUNICODE_STRING DstPath = (PUNICODE_STRING)ProcessInformation; - - /* we need to attach to the process to make sure we're in the right context! */ - Attached = Process != PsGetCurrentProcess(); - - if(Attached) - KeAttachProcess(&Process->Pcb); - - _SEH_TRY - { - ProcParams = Process->Peb->ProcessParameters; - ImagePathLen = ProcParams->ImagePathName.Length; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(NT_SUCCESS(Status)) - { - if(ProcessInformationLength < sizeof(UNICODE_STRING) + ImagePathLen + sizeof(WCHAR)) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - } - else - { - PWSTR StrSource = NULL; - - /* create a DstPath structure on the stack */ - _SEH_TRY - { - LocalDest.Length = ImagePathLen; - LocalDest.MaximumLength = ImagePathLen + sizeof(WCHAR); - LocalDest.Buffer = (PWSTR)(DstPath + 1); - - /* save a copy of the pointer to the source buffer */ - StrSource = ProcParams->ImagePathName.Buffer; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(NT_SUCCESS(Status)) - { - /* now, let's allocate some anonymous memory to copy the string to. - we can't just copy it to the buffer the caller pointed as it might - be user memory in another context */ - PWSTR PathCopy = ExAllocatePool(PagedPool, LocalDest.Length + sizeof(WCHAR)); - if(PathCopy != NULL) - { - /* make a copy of the buffer to the temporary buffer */ - _SEH_TRY - { - RtlCopyMemory(PathCopy, StrSource, LocalDest.Length); - PathCopy[LocalDest.Length / sizeof(WCHAR)] = L'\0'; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - /* detach from the process */ - if(Attached) - KeDetachProcess(); - - /* only copy the string back to the caller if we were able to - copy it into the temporary buffer! */ - if(NT_SUCCESS(Status)) - { - /* now let's copy the buffer back to the caller */ - _SEH_TRY - { - *DstPath = LocalDest; - RtlCopyMemory(LocalDest.Buffer, PathCopy, LocalDest.Length + sizeof(WCHAR)); - if (ReturnLength) - { - *ReturnLength = sizeof(UNICODE_STRING) + LocalDest.Length + sizeof(WCHAR); - } - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - } - - /* we're done with the copy operation, free the temporary kernel buffer */ - ExFreePool(PathCopy); - - /* we need to bail because we're already detached from the process */ - break; - } - else - { - Status = STATUS_INSUFFICIENT_RESOURCES; - } - } - } - } - - /* don't forget to detach from the process!!! */ - if(Attached) - KeDetachProcess(); - } - else - { - /* FIXME - what to do here? */ - Status = STATUS_UNSUCCESSFUL; - } - break; - } - - /* - * Note: The following 10 information classes are verified to not be - * implemented on NT, and do indeed return STATUS_INVALID_INFO_CLASS; - */ - case ProcessBasePriority: - case ProcessRaisePriority: - case ProcessExceptionPort: - case ProcessAccessToken: - case ProcessLdtSize: - case ProcessIoPortHandlers: - case ProcessUserModeIOPL: - case ProcessEnableAlignmentFaultFixup: - case ProcessAffinityMask: - case ProcessForegroundInformation: - default: - Status = STATUS_INVALID_INFO_CLASS; - } - - ObDereferenceObject(Process); - return Status; -} - - -NTSTATUS -PspAssignPrimaryToken(PEPROCESS Process, - HANDLE TokenHandle) -{ - PACCESS_TOKEN Token; - PACCESS_TOKEN OldToken; - NTSTATUS Status; - - Status = ObReferenceObjectByHandle(TokenHandle, - 0, - SepTokenObjectType, - UserMode, - (PVOID*)&Token, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - Status = SeExchangePrimaryToken(Process, Token, &OldToken); - if (NT_SUCCESS(Status)) - { - ObDereferenceObject(OldToken); - } - ObDereferenceObject(Token); - return(Status); -} - -/* - * @unimplemented - */ -NTSTATUS STDCALL -NtSetInformationProcess(IN HANDLE ProcessHandle, - IN PROCESSINFOCLASS ProcessInformationClass, - IN PVOID ProcessInformation, - IN ULONG ProcessInformationLength) -{ - PEPROCESS Process; - KPROCESSOR_MODE PreviousMode; - ACCESS_MASK Access; - NTSTATUS Status = STATUS_SUCCESS; - - PAGED_CODE(); - - PreviousMode = ExGetPreviousMode(); - - DefaultSetInfoBufferCheck(ProcessInformationClass, - PsProcessInfoClass, - ProcessInformation, - ProcessInformationLength, - PreviousMode, - &Status); - if(!NT_SUCCESS(Status)) - { - DPRINT1("NtSetInformationProcess() %d %x %x called\n", ProcessInformationClass, ProcessInformation, ProcessInformationLength); - DPRINT1("NtSetInformationProcess() %x failed, Status: 0x%x\n", Status); - return Status; - } - - switch(ProcessInformationClass) - { - case ProcessSessionInformation: - Access = PROCESS_SET_INFORMATION | PROCESS_SET_SESSIONID; - break; - case ProcessExceptionPort: - case ProcessDebugPort: - Access = PROCESS_SET_INFORMATION | PROCESS_SET_PORT; - break; - - default: - Access = PROCESS_SET_INFORMATION; - break; - } - - Status = ObReferenceObjectByHandle(ProcessHandle, - Access, - PsProcessType, - PreviousMode, - (PVOID*)&Process, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - - switch (ProcessInformationClass) - { - case ProcessQuotaLimits: - case ProcessBasePriority: - case ProcessRaisePriority: - Status = STATUS_NOT_IMPLEMENTED; - break; - - case ProcessDebugPort: - { - HANDLE PortHandle = NULL; - - /* make a safe copy of the buffer on the stack */ - _SEH_TRY - { - PortHandle = *(PHANDLE)ProcessInformation; - Status = (PortHandle != NULL ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER); - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(NT_SUCCESS(Status)) - { - PEPORT DebugPort; - - /* in case we had success reading from the buffer, verify the provided - * LPC port handle - */ - Status = ObReferenceObjectByHandle(PortHandle, - 0, - LpcPortObjectType, - PreviousMode, - (PVOID)&DebugPort, - NULL); - if(NT_SUCCESS(Status)) - { - /* lock the process to be thread-safe! */ - - Status = PsLockProcess(Process, FALSE); - if(NT_SUCCESS(Status)) - { - /* - * according to "NT Native API" documentation, setting the debug - * port is only permitted once! - */ - if(Process->DebugPort == NULL) - { - /* keep the reference to the handle! */ - Process->DebugPort = DebugPort; - - if(Process->Peb) - { - /* we're now debugging the process, so set the flag in the PEB - structure. However, to access it we need to attach to the - process so we're sure we're in the right context! */ - - KeAttachProcess(&Process->Pcb); - _SEH_TRY - { - Process->Peb->BeingDebugged = TRUE; - } - _SEH_HANDLE - { - DPRINT1("Trying to set the Peb->BeingDebugged field of process 0x%x failed, exception: 0x%x\n", Process, _SEH_GetExceptionCode()); - } - _SEH_END; - KeDetachProcess(); - } - Status = STATUS_SUCCESS; - } - else - { - ObDereferenceObject(DebugPort); - Status = STATUS_PORT_ALREADY_SET; - } - PsUnlockProcess(Process); - } - else - { - ObDereferenceObject(DebugPort); - } - } - } - break; - } - - case ProcessExceptionPort: - { - HANDLE PortHandle = NULL; - - /* make a safe copy of the buffer on the stack */ - _SEH_TRY - { - PortHandle = *(PHANDLE)ProcessInformation; - Status = STATUS_SUCCESS; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(NT_SUCCESS(Status)) - { - PEPORT ExceptionPort; - - /* in case we had success reading from the buffer, verify the provided - * LPC port handle - */ - Status = ObReferenceObjectByHandle(PortHandle, - 0, - LpcPortObjectType, - PreviousMode, - (PVOID)&ExceptionPort, - NULL); - if(NT_SUCCESS(Status)) - { - /* lock the process to be thread-safe! */ - - Status = PsLockProcess(Process, FALSE); - if(NT_SUCCESS(Status)) - { - /* - * according to "NT Native API" documentation, setting the exception - * port is only permitted once! - */ - if(Process->ExceptionPort == NULL) - { - /* keep the reference to the handle! */ - Process->ExceptionPort = ExceptionPort; - Status = STATUS_SUCCESS; - } - else - { - ObDereferenceObject(ExceptionPort); - Status = STATUS_PORT_ALREADY_SET; - } - PsUnlockProcess(Process); - } - else - { - ObDereferenceObject(ExceptionPort); - } - } - } - break; - } - - case ProcessAccessToken: - { - HANDLE TokenHandle = NULL; - - /* make a safe copy of the buffer on the stack */ - _SEH_TRY - { - TokenHandle = ((PPROCESS_ACCESS_TOKEN)ProcessInformation)->Token; - Status = STATUS_SUCCESS; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(NT_SUCCESS(Status)) - { - /* in case we had success reading from the buffer, perform the actual task */ - Status = PspAssignPrimaryToken(Process, TokenHandle); - } - break; - } - - case ProcessDefaultHardErrorMode: - { - _SEH_TRY - { - InterlockedExchange((LONG*)&Process->DefaultHardErrorProcessing, - *(PLONG)ProcessInformation); - Status = STATUS_SUCCESS; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - break; - } - - case ProcessSessionInformation: - { - PROCESS_SESSION_INFORMATION SessionInfo; - Status = STATUS_SUCCESS; - - _SEH_TRY - { - /* copy the structure to the stack */ - SessionInfo = *(PPROCESS_SESSION_INFORMATION)ProcessInformation; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(NT_SUCCESS(Status)) - { - /* we successfully copied the structure to the stack, continue processing */ - - /* - * setting the session id requires the SeTcbPrivilege! - */ - if(!SeSinglePrivilegeCheck(SeTcbPrivilege, - PreviousMode)) - { - DPRINT1("NtSetInformationProcess: Caller requires the SeTcbPrivilege privilege for setting ProcessSessionInformation!\n"); - /* can't set the session id, bail! */ - Status = STATUS_PRIVILEGE_NOT_HELD; - break; - } - - /* FIXME - update the session id for the process token */ - - Status = PsLockProcess(Process, FALSE); - if(NT_SUCCESS(Status)) - { - Process->SessionId = SessionInfo.SessionId; - - /* Update the session id in the PEB structure */ - if(Process->Peb != NULL) - { - /* we need to attach to the process to make sure we're in the right - context to access the PEB structure */ - KeAttachProcess(&Process->Pcb); - - _SEH_TRY - { - /* FIXME: Process->Peb->SessionId = SessionInfo.SessionId; */ - - Status = STATUS_SUCCESS; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - KeDetachProcess(); - } - - PsUnlockProcess(Process); - } - } - break; - } - - case ProcessPriorityClass: - { - PROCESS_PRIORITY_CLASS ppc; - - _SEH_TRY - { - ppc = *(PPROCESS_PRIORITY_CLASS)ProcessInformation; - } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; - - if(NT_SUCCESS(Status)) - { - } - - break; - } - - case ProcessLdtInformation: - case ProcessLdtSize: - case ProcessIoPortHandlers: - case ProcessWorkingSetWatch: - case ProcessUserModeIOPL: - case ProcessEnableAlignmentFaultFixup: - case ProcessAffinityMask: - Status = STATUS_NOT_IMPLEMENTED; - break; - - case ProcessBasicInformation: - case ProcessIoCounters: - case ProcessTimes: - case ProcessPooledUsageAndLimits: - case ProcessWx86Information: - case ProcessHandleCount: - case ProcessWow64Information: - default: - Status = STATUS_INVALID_INFO_CLASS; - } - ObDereferenceObject(Process); - return(Status); -} - - -/********************************************************************** - * NAME INTERNAL - * PiQuerySystemProcessInformation - * - * DESCRIPTION - * Compute the size of a process+thread snapshot as - * expected by NtQuerySystemInformation. - * - * RETURN VALUE - * 0 on error; otherwise the size, in bytes of the buffer - * required to write a full snapshot. - * - * NOTE - * We assume (sizeof (PVOID) == sizeof (ULONG)) holds. - */ -NTSTATUS -PiQuerySystemProcessInformation(PVOID Buffer, - ULONG Size, - PULONG ReqSize) -{ - return STATUS_NOT_IMPLEMENTED; - -#if 0 - PLIST_ENTRY CurrentEntryP; - PEPROCESS CurrentP; - PLIST_ENTRY CurrentEntryT; - PETHREAD CurrentT; - - ULONG RequiredSize = 0L; - BOOLEAN SizeOnly = FALSE; - - ULONG SpiSize = 0L; - - PSYSTEM_PROCESS_INFORMATION pInfoP = (PSYSTEM_PROCESS_INFORMATION) SnapshotBuffer; - PSYSTEM_PROCESS_INFORMATION pInfoPLast = NULL; - PSYSTEM_THREAD_INFO pInfoT = NULL; - - - /* Lock the process list. */ - ExAcquireFastMutex(&PspActiveProcessMutex); - - /* - * Scan the process list. Since the - * list is circular, the guard is false - * after the last process. - */ - for ( CurrentEntryP = PsActiveProcessHead.Flink; - (CurrentEntryP != & PsActiveProcessHead); - CurrentEntryP = CurrentEntryP->Flink - ) - { - /* - * Compute how much space is - * occupied in the snapshot - * by adding this process info. - * (at least one thread). - */ - SpiSizeCurrent = sizeof (SYSTEM_PROCESS_INFORMATION); - RequiredSize += SpiSizeCurrent; - /* - * Do not write process data in the - * buffer if it is too small. - */ - if (TRUE == SizeOnly) continue; - /* - * Check if the buffer can contain - * the full snapshot. - */ - if (Size < RequiredSize) - { - SizeOnly = TRUE; - continue; - } - /* - * Get a reference to the - * process descriptor we are - * handling. - */ - CurrentP = CONTAINING_RECORD( - CurrentEntryP, - EPROCESS, - ProcessListEntry - ); - /* - * Write process data in the buffer. - */ - RtlZeroMemory (pInfoP, sizeof (SYSTEM_PROCESS_INFORMATION)); - /* PROCESS */ - pInfoP->ThreadCount = 0L; - pInfoP->ProcessId = CurrentP->UniqueProcessId; - RtlInitUnicodeString ( - & pInfoP->Name, - CurrentP->ImageFileName - ); - /* THREAD */ - for ( pInfoT = & CurrentP->ThreadSysInfo [0], - CurrentEntryT = CurrentP->ThreadListHead.Flink; - - (CurrentEntryT != & CurrentP->ThreadListHead); - - pInfoT = & CurrentP->ThreadSysInfo [pInfoP->ThreadCount], - CurrentEntryT = CurrentEntryT->Flink - ) - { - /* - * Recalculate the size of the - * information block. - */ - if (0 < pInfoP->ThreadCount) - { - RequiredSize += sizeof (SYSTEM_THREAD_INFORMATION); - } - /* - * Do not write thread data in the - * buffer if it is too small. - */ - if (TRUE == SizeOnly) continue; - /* - * Check if the buffer can contain - * the full snapshot. - */ - if (Size < RequiredSize) - { - SizeOnly = TRUE; - continue; - } - /* - * Get a reference to the - * thread descriptor we are - * handling. - */ - CurrentT = CONTAINING_RECORD( - CurrentEntryT, - KTHREAD, - ThreadListEntry - ); - /* - * Write thread data. - */ - RtlZeroMemory ( - pInfoT, - sizeof (SYSTEM_THREAD_INFORMATION) - ); - pInfoT->KernelTime = CurrentT-> ; /* TIME */ - pInfoT->UserTime = CurrentT-> ; /* TIME */ - pInfoT->CreateTime = CurrentT-> ; /* TIME */ - pInfoT->TickCount = CurrentT-> ; /* ULONG */ - pInfoT->StartEIP = CurrentT-> ; /* ULONG */ - pInfoT->ClientId = CurrentT-> ; /* CLIENT_ID */ - pInfoT->ClientId = CurrentT-> ; /* CLIENT_ID */ - pInfoT->DynamicPriority = CurrentT-> ; /* ULONG */ - pInfoT->BasePriority = CurrentT-> ; /* ULONG */ - pInfoT->nSwitches = CurrentT-> ; /* ULONG */ - pInfoT->State = CurrentT-> ; /* DWORD */ - pInfoT->WaitReason = CurrentT-> ; /* KWAIT_REASON */ - /* - * Count the number of threads - * this process has. - */ - ++ pInfoP->ThreadCount; - } - /* - * Save the size of information - * stored in the buffer for the - * current process. - */ - pInfoP->RelativeOffset = SpiSize; - /* - * Save a reference to the last - * valid information block. - */ - pInfoPLast = pInfoP; - /* - * Compute the offset of the - * SYSTEM_PROCESS_INFORMATION - * descriptor in the snapshot - * buffer for the next process. - */ - (ULONG) pInfoP += SpiSize; - } - /* - * Unlock the process list. - */ - ExReleaseFastMutex ( - & PspActiveProcessMutex - ); - /* - * Return the proper error status code, - * if the buffer was too small. - */ - if (TRUE == SizeOnly) - { - if (NULL != RequiredSize) - { - *pRequiredSize = RequiredSize; - } - return STATUS_INFO_LENGTH_MISMATCH; - } - /* - * Mark the end of the snapshot. - */ - pInfoP->RelativeOffset = 0L; - /* OK */ - return STATUS_SUCCESS; -#endif } /* @@ -2561,30 +1318,25 @@ NTSTATUS STDCALL PsLookupProcessByProcessId(IN HANDLE ProcessId, OUT PEPROCESS *Process) { - PLIST_ENTRY current_entry; - PEPROCESS current; + PHANDLE_TABLE_ENTRY CidEntry; + PEPROCESS FoundProcess; - ExAcquireFastMutex(&PspActiveProcessMutex); + PAGED_CODE(); - current_entry = PsActiveProcessHead.Flink; - while (current_entry != &PsActiveProcessHead) - { - current = CONTAINING_RECORD(current_entry, - EPROCESS, - ProcessListEntry); - if (current->UniqueProcessId == ProcessId) - { - *Process = current; - ObReferenceObject(current); - ExReleaseFastMutex(&PspActiveProcessMutex); - return(STATUS_SUCCESS); - } - current_entry = current_entry->Flink; - } + ASSERT(Process); - ExReleaseFastMutex(&PspActiveProcessMutex); + CidEntry = PsLookupCidHandle(ProcessId, PsProcessType, (PVOID*)&FoundProcess); + if(CidEntry != NULL) + { + ObReferenceObject(FoundProcess); - return(STATUS_INVALID_PARAMETER); + PsUnlockCidHandle(CidEntry); + + *Process = FoundProcess; + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; } VOID @@ -2892,7 +1644,7 @@ PsReturnProcessPagedPoolQuota( } NTSTATUS -PsLockProcess(PEPROCESS Process, BOOL Timeout) +PsLockProcess(PEPROCESS Process, BOOLEAN Timeout) { ULONG Attempts = 0; PKTHREAD PrevLockOwner; @@ -2906,12 +1658,6 @@ PsLockProcess(PEPROCESS Process, BOOL Timeout) for(;;) { - if(Process->Pcb.State == PROCESS_STATE_TERMINATED) - { - KeLeaveCriticalRegion(); - return STATUS_PROCESS_IS_TERMINATING; - } - PrevLockOwner = (PKTHREAD)InterlockedCompareExchangePointer( &Process->LockOwner, CallingThread, NULL); if(PrevLockOwner == NULL || PrevLockOwner == CallingThread) @@ -2942,9 +1688,8 @@ PsLockProcess(PEPROCESS Process, BOOL Timeout) } #endif KeLeaveCriticalRegion(); - + break; } - break; } else { diff --git a/reactos/ntoskrnl/ps/psmgr.c b/reactos/ntoskrnl/ps/psmgr.c index 051d6ae9989..d8070ab7f56 100644 --- a/reactos/ntoskrnl/ps/psmgr.c +++ b/reactos/ntoskrnl/ps/psmgr.c @@ -14,8 +14,6 @@ #define NDEBUG #include -VOID INIT_FUNCTION PsInitClientIDManagment(VOID); - /* FUNCTIONS ***************************************************************/ VOID PiShutdownProcessManager(VOID) @@ -28,12 +26,10 @@ VOID PiShutdownProcessManager(VOID) VOID INIT_FUNCTION PiInitProcessManager(VOID) { - PsInitClientIDManagment(); PsInitJobManagment(); PsInitProcessManagment(); PsInitThreadManagment(); PsInitIdleThread(); - PsInitialiseSuspendImplementation(); PsInitialiseW32Call(); } diff --git a/reactos/ntoskrnl/ps/query.c b/reactos/ntoskrnl/ps/query.c new file mode 100644 index 00000000000..b6dbac108a7 --- /dev/null +++ b/reactos/ntoskrnl/ps/query.c @@ -0,0 +1,1414 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/ps/query.c + * PURPOSE: Set/Query Process/Thread Information APIs + * + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Created File + * David Welch + */ + +/* INCLUDES ******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ******************************************************************/ + + +static const INFORMATION_CLASS_INFO PsProcessInfoClass[] = +{ + ICI_SQ_SAME( sizeof(PROCESS_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* ProcessBasicInformation */ + ICI_SQ_SAME( sizeof(QUOTA_LIMITS), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessQuotaLimits */ + ICI_SQ_SAME( sizeof(IO_COUNTERS), sizeof(ULONG), ICIF_QUERY ), /* ProcessIoCounters */ + ICI_SQ_SAME( sizeof(VM_COUNTERS), sizeof(ULONG), ICIF_QUERY ), /* ProcessVmCounters */ + ICI_SQ_SAME( sizeof(KERNEL_USER_TIMES), sizeof(ULONG), ICIF_QUERY ), /* ProcessTimes */ + ICI_SQ_SAME( sizeof(KPRIORITY), sizeof(ULONG), ICIF_SET ), /* ProcessBasePriority */ + ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_SET ), /* ProcessRaisePriority */ + ICI_SQ_SAME( sizeof(HANDLE), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessDebugPort */ + ICI_SQ_SAME( sizeof(HANDLE), sizeof(ULONG), ICIF_SET ), /* ProcessExceptionPort */ + ICI_SQ_SAME( sizeof(PROCESS_ACCESS_TOKEN), sizeof(ULONG), ICIF_SET ), /* ProcessAccessToken */ + ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessLdtInformation */ + ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_SET ), /* ProcessLdtSize */ + ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessDefaultHardErrorMode */ + ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_SET ), /* ProcessIoPortHandlers */ + ICI_SQ_SAME( sizeof(POOLED_USAGE_AND_LIMITS), sizeof(ULONG), ICIF_QUERY ), /* ProcessPooledUsageAndLimits */ + ICI_SQ_SAME( sizeof(PROCESS_WS_WATCH_INFORMATION), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessWorkingSetWatch */ + ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_SET ), /* ProcessUserModeIOPL */ + ICI_SQ_SAME( sizeof(BOOLEAN), sizeof(ULONG), ICIF_SET ), /* ProcessEnableAlignmentFaultFixup */ + ICI_SQ_SAME( sizeof(PROCESS_PRIORITY_CLASS), sizeof(USHORT), ICIF_QUERY | ICIF_SET ), /* ProcessPriorityClass */ + ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY ), /* ProcessWx86Information */ + ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY ), /* ProcessHandleCount */ + ICI_SQ_SAME( sizeof(KAFFINITY), sizeof(ULONG), ICIF_SET ), /* ProcessAffinityMask */ + ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessPriorityBoost */ + + ICI_SQ(/*Q*/ sizeof(((PPROCESS_DEVICEMAP_INFORMATION)0x0)->Query), /* ProcessDeviceMap */ + /*S*/ sizeof(((PPROCESS_DEVICEMAP_INFORMATION)0x0)->Set), + /*Q*/ sizeof(ULONG), + /*S*/ sizeof(ULONG), + ICIF_QUERY | ICIF_SET ), + + ICI_SQ_SAME( sizeof(PROCESS_SESSION_INFORMATION), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessSessionInformation */ + ICI_SQ_SAME( sizeof(BOOLEAN), sizeof(ULONG), ICIF_SET ), /* ProcessForegroundInformation */ + ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY ), /* ProcessWow64Information */ + ICI_SQ_SAME( sizeof(UNICODE_STRING), sizeof(ULONG), ICIF_QUERY | ICIF_SIZE_VARIABLE), /* ProcessImageFileName */ + + /* FIXME */ + ICI_SQ_SAME( 0, 1, 0 ), /* ProcessLUIDDeviceMapsEnabled */ + ICI_SQ_SAME( 0, 1, 0 ), /* ProcessBreakOnTermination */ + ICI_SQ_SAME( 0, 1, 0 ), /* ProcessDebugObjectHandle */ + ICI_SQ_SAME( 0, 1, 0 ), /* ProcessDebugFlags */ + ICI_SQ_SAME( 0, 1, 0 ), /* ProcessHandleTracing */ + ICI_SQ_SAME( 0, 1, 0 ), /* ProcessUnknown33 */ + ICI_SQ_SAME( 0, 1, 0 ), /* ProcessUnknown34 */ + ICI_SQ_SAME( 0, 1, 0 ), /* ProcessUnknown35 */ + + ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY), /* ProcessCookie */ +}; + +/* + * FIXME: + * Remove the Implemented value if all functions are implemented. + */ + +static const struct +{ + BOOLEAN Implemented; + ULONG Size; +} QueryInformationData[MaxThreadInfoClass + 1] = +{ + {TRUE, sizeof(THREAD_BASIC_INFORMATION)}, // ThreadBasicInformation + {TRUE, sizeof(KERNEL_USER_TIMES)}, // ThreadTimes + {TRUE, 0}, // ThreadPriority + {TRUE, 0}, // ThreadBasePriority + {TRUE, 0}, // ThreadAffinityMask + {TRUE, 0}, // ThreadImpersonationToken + {FALSE, 0}, // ThreadDescriptorTableEntry + {TRUE, 0}, // ThreadEnableAlignmentFaultFixup + {TRUE, 0}, // ThreadEventPair + {TRUE, sizeof(PVOID)}, // ThreadQuerySetWin32StartAddress + {TRUE, 0}, // ThreadZeroTlsCell + {TRUE, sizeof(LARGE_INTEGER)}, // ThreadPerformanceCount + {TRUE, sizeof(BOOLEAN)}, // ThreadAmILastThread + {TRUE, 0}, // ThreadIdealProcessor + {FALSE, 0}, // ThreadPriorityBoost + {TRUE, 0}, // ThreadSetTlsArrayAddress + {FALSE, 0}, // ThreadIsIoPending + {TRUE, 0} // ThreadHideFromDebugger +}; + +static const struct +{ + BOOLEAN Implemented; + ULONG Size; +} SetInformationData[MaxThreadInfoClass + 1] = +{ + {TRUE, 0}, // ThreadBasicInformation + {TRUE, 0}, // ThreadTimes + {TRUE, sizeof(KPRIORITY)}, // ThreadPriority + {TRUE, sizeof(LONG)}, // ThreadBasePriority + {TRUE, sizeof(KAFFINITY)}, // ThreadAffinityMask + {TRUE, sizeof(HANDLE)}, // ThreadImpersonationToken + {TRUE, 0}, // ThreadDescriptorTableEntry + {FALSE, 0}, // ThreadEnableAlignmentFaultFixup + {FALSE, 0}, // ThreadEventPair + {TRUE, sizeof(PVOID)}, // ThreadQuerySetWin32StartAddress + {FALSE, 0}, // ThreadZeroTlsCell + {TRUE, 0}, // ThreadPerformanceCount + {TRUE, 0}, // ThreadAmILastThread + {FALSE, 0}, // ThreadIdealProcessor + {FALSE, 0}, // ThreadPriorityBoost + {FALSE, 0}, // ThreadSetTlsArrayAddress + {TRUE, 0}, // ThreadIsIoPending + {FALSE, 0} // ThreadHideFromDebugger +}; + +/* FUNCTIONS *****************************************************************/ + +/* + * @unimplemented + */ +NTSTATUS STDCALL +NtQueryInformationProcess(IN HANDLE ProcessHandle, + IN PROCESSINFOCLASS ProcessInformationClass, + OUT PVOID ProcessInformation, + IN ULONG ProcessInformationLength, + OUT PULONG ReturnLength OPTIONAL) +{ + PEPROCESS Process; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + DefaultQueryInfoBufferCheck(ProcessInformationClass, + PsProcessInfoClass, + ProcessInformation, + ProcessInformationLength, + ReturnLength, + PreviousMode, + &Status); + if(!NT_SUCCESS(Status)) + { + DPRINT1("NtQueryInformationProcess() failed, Status: 0x%x\n", Status); + return Status; + } + + if(ProcessInformationClass != ProcessCookie) + { + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_QUERY_INFORMATION, + PsProcessType, + PreviousMode, + (PVOID*)&Process, + NULL); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + } + else if(ProcessHandle != NtCurrentProcess()) + { + /* retreiving the process cookie is only allowed for the calling process + itself! XP only allowes NtCurrentProcess() as process handles even if a + real handle actually represents the current process. */ + return STATUS_INVALID_PARAMETER; + } + + switch (ProcessInformationClass) + { + case ProcessBasicInformation: + { + PPROCESS_BASIC_INFORMATION ProcessBasicInformationP = + (PPROCESS_BASIC_INFORMATION)ProcessInformation; + + _SEH_TRY + { + ProcessBasicInformationP->ExitStatus = Process->ExitStatus; + ProcessBasicInformationP->PebBaseAddress = Process->Peb; + ProcessBasicInformationP->AffinityMask = Process->Pcb.Affinity; + ProcessBasicInformationP->UniqueProcessId = + Process->UniqueProcessId; + ProcessBasicInformationP->InheritedFromUniqueProcessId = + Process->InheritedFromUniqueProcessId; + ProcessBasicInformationP->BasePriority = + Process->Pcb.BasePriority; + + if (ReturnLength) + { + *ReturnLength = sizeof(PROCESS_BASIC_INFORMATION); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessQuotaLimits: + case ProcessIoCounters: + Status = STATUS_NOT_IMPLEMENTED; + break; + + case ProcessTimes: + { + PKERNEL_USER_TIMES ProcessTimeP = (PKERNEL_USER_TIMES)ProcessInformation; + _SEH_TRY + { + ProcessTimeP->CreateTime = Process->CreateTime; + ProcessTimeP->UserTime.QuadPart = Process->Pcb.UserTime * 100000LL; + ProcessTimeP->KernelTime.QuadPart = Process->Pcb.KernelTime * 100000LL; + ProcessTimeP->ExitTime = Process->ExitTime; + + if (ReturnLength) + { + *ReturnLength = sizeof(KERNEL_USER_TIMES); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessDebugPort: + { + _SEH_TRY + { + *(PHANDLE)ProcessInformation = (Process->DebugPort != NULL ? (HANDLE)-1 : NULL); + if (ReturnLength) + { + *ReturnLength = sizeof(HANDLE); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessLdtInformation: + case ProcessWorkingSetWatch: + case ProcessWx86Information: + Status = STATUS_NOT_IMPLEMENTED; + break; + + case ProcessHandleCount: + { + ULONG HandleCount = ObpGetHandleCountByHandleTable(Process->ObjectTable); + + _SEH_TRY + { + *(PULONG)ProcessInformation = HandleCount; + if (ReturnLength) + { + *ReturnLength = sizeof(ULONG); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessSessionInformation: + { + PPROCESS_SESSION_INFORMATION SessionInfo = (PPROCESS_SESSION_INFORMATION)ProcessInformation; + + _SEH_TRY + { + SessionInfo->SessionId = Process->SessionId; + if (ReturnLength) + { + *ReturnLength = sizeof(PROCESS_SESSION_INFORMATION); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessWow64Information: + DPRINT1("We currently don't support the ProcessWow64Information information class!\n"); + Status = STATUS_NOT_IMPLEMENTED; + break; + + case ProcessVmCounters: + { + PVM_COUNTERS pOut = (PVM_COUNTERS)ProcessInformation; + + _SEH_TRY + { + pOut->PeakVirtualSize = Process->PeakVirtualSize; + /* + * Here we should probably use VirtualSize.LowPart, but due to + * incompatibilities in current headers (no unnamed union), + * I opted for cast. + */ + pOut->VirtualSize = (ULONG)Process->VirtualSize.QuadPart; + pOut->PageFaultCount = Process->Vm.PageFaultCount; + pOut->PeakWorkingSetSize = Process->Vm.PeakWorkingSetSize; + pOut->WorkingSetSize = Process->Vm.WorkingSetSize; + pOut->QuotaPeakPagedPoolUsage = Process->QuotaPeakPoolUsage[0]; // TODO: Verify! + pOut->QuotaPagedPoolUsage = Process->QuotaPoolUsage[0]; // TODO: Verify! + pOut->QuotaPeakNonPagedPoolUsage = Process->QuotaPeakPoolUsage[1]; // TODO: Verify! + pOut->QuotaNonPagedPoolUsage = Process->QuotaPoolUsage[1]; // TODO: Verify! + pOut->PagefileUsage = Process->PagefileUsage; + pOut->PeakPagefileUsage = Process->PeakPagefileUsage; + + if (ReturnLength) + { + *ReturnLength = sizeof(VM_COUNTERS); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessDefaultHardErrorMode: + { + PULONG HardErrMode = (PULONG)ProcessInformation; + _SEH_TRY + { + *HardErrMode = Process->DefaultHardErrorProcessing; + if (ReturnLength) + { + *ReturnLength = sizeof(ULONG); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessPriorityBoost: + { + PULONG BoostEnabled = (PULONG)ProcessInformation; + + _SEH_TRY + { + *BoostEnabled = Process->Pcb.DisableBoost ? FALSE : TRUE; + + if (ReturnLength) + { + *ReturnLength = sizeof(ULONG); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessDeviceMap: + { + PROCESS_DEVICEMAP_INFORMATION DeviceMap; + + ObQueryDeviceMapInformation(Process, &DeviceMap); + + _SEH_TRY + { + *(PPROCESS_DEVICEMAP_INFORMATION)ProcessInformation = DeviceMap; + if (ReturnLength) + { + *ReturnLength = sizeof(PROCESS_DEVICEMAP_INFORMATION); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessPriorityClass: + { + PUSHORT Priority = (PUSHORT)ProcessInformation; + + _SEH_TRY + { + *Priority = Process->PriorityClass; + + if (ReturnLength) + { + *ReturnLength = sizeof(USHORT); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessImageFileName: + { + /* + * We DO NOT return the file name stored in the EPROCESS structure. + * Propably if we can't find a PEB or ProcessParameters structure for the + * process! + */ + if(Process->Peb != NULL) + { + PRTL_USER_PROCESS_PARAMETERS ProcParams = NULL; + UNICODE_STRING LocalDest; + BOOLEAN Attached; + ULONG ImagePathLen = 0; + PUNICODE_STRING DstPath = (PUNICODE_STRING)ProcessInformation; + + /* we need to attach to the process to make sure we're in the right context! */ + Attached = Process != PsGetCurrentProcess(); + + if(Attached) + KeAttachProcess(&Process->Pcb); + + _SEH_TRY + { + ProcParams = Process->Peb->ProcessParameters; + ImagePathLen = ProcParams->ImagePathName.Length; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + if(ProcessInformationLength < sizeof(UNICODE_STRING) + ImagePathLen + sizeof(WCHAR)) + { + Status = STATUS_INFO_LENGTH_MISMATCH; + } + else + { + PWSTR StrSource = NULL; + + /* create a DstPath structure on the stack */ + _SEH_TRY + { + LocalDest.Length = ImagePathLen; + LocalDest.MaximumLength = ImagePathLen + sizeof(WCHAR); + LocalDest.Buffer = (PWSTR)(DstPath + 1); + + /* save a copy of the pointer to the source buffer */ + StrSource = ProcParams->ImagePathName.Buffer; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + /* now, let's allocate some anonymous memory to copy the string to. + we can't just copy it to the buffer the caller pointed as it might + be user memory in another context */ + PWSTR PathCopy = ExAllocatePool(PagedPool, LocalDest.Length + sizeof(WCHAR)); + if(PathCopy != NULL) + { + /* make a copy of the buffer to the temporary buffer */ + _SEH_TRY + { + RtlCopyMemory(PathCopy, StrSource, LocalDest.Length); + PathCopy[LocalDest.Length / sizeof(WCHAR)] = L'\0'; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + /* detach from the process */ + if(Attached) + KeDetachProcess(); + + /* only copy the string back to the caller if we were able to + copy it into the temporary buffer! */ + if(NT_SUCCESS(Status)) + { + /* now let's copy the buffer back to the caller */ + _SEH_TRY + { + *DstPath = LocalDest; + RtlCopyMemory(LocalDest.Buffer, PathCopy, LocalDest.Length + sizeof(WCHAR)); + if (ReturnLength) + { + *ReturnLength = sizeof(UNICODE_STRING) + LocalDest.Length + sizeof(WCHAR); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + + /* we're done with the copy operation, free the temporary kernel buffer */ + ExFreePool(PathCopy); + + /* we need to bail because we're already detached from the process */ + break; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + } + + /* don't forget to detach from the process!!! */ + if(Attached) + KeDetachProcess(); + } + else + { + /* FIXME - what to do here? */ + Status = STATUS_UNSUCCESSFUL; + } + break; + } + + case ProcessCookie: + { + ULONG Cookie; + + /* receive the process cookie, this is only allowed for the current + process! */ + + Process = PsGetCurrentProcess(); + + Cookie = Process->Cookie; + if(Cookie == 0) + { + LARGE_INTEGER SystemTime; + ULONG NewCookie; + PKPRCB Prcb; + + /* generate a new cookie */ + + KeQuerySystemTime(&SystemTime); + + Prcb = KeGetCurrentPrcb(); + + NewCookie = Prcb->KeSystemCalls ^ Prcb->InterruptTime ^ + SystemTime.u.LowPart ^ SystemTime.u.HighPart; + + /* try to set the new cookie, return the current one if another thread + set it in the meanwhile */ + Cookie = InterlockedCompareExchange((LONG*)&Process->Cookie, + NewCookie, + Cookie); + if(Cookie == 0) + { + /* successfully set the cookie */ + Cookie = NewCookie; + } + } + + _SEH_TRY + { + *(PULONG)ProcessInformation = Cookie; + if (ReturnLength) + { + *ReturnLength = sizeof(ULONG); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + + /* + * Note: The following 10 information classes are verified to not be + * implemented on NT, and do indeed return STATUS_INVALID_INFO_CLASS; + */ + case ProcessBasePriority: + case ProcessRaisePriority: + case ProcessExceptionPort: + case ProcessAccessToken: + case ProcessLdtSize: + case ProcessIoPortHandlers: + case ProcessUserModeIOPL: + case ProcessEnableAlignmentFaultFixup: + case ProcessAffinityMask: + case ProcessForegroundInformation: + default: + Status = STATUS_INVALID_INFO_CLASS; + } + + if(ProcessInformationClass != ProcessCookie) + { + ObDereferenceObject(Process); + } + + return Status; +} + +/* + * @unimplemented + */ +NTSTATUS STDCALL +NtSetInformationProcess(IN HANDLE ProcessHandle, + IN PROCESSINFOCLASS ProcessInformationClass, + IN PVOID ProcessInformation, + IN ULONG ProcessInformationLength) +{ + PEPROCESS Process; + KPROCESSOR_MODE PreviousMode; + ACCESS_MASK Access; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + DefaultSetInfoBufferCheck(ProcessInformationClass, + PsProcessInfoClass, + ProcessInformation, + ProcessInformationLength, + PreviousMode, + &Status); + if(!NT_SUCCESS(Status)) + { + DPRINT1("NtSetInformationProcess() %d %x %x called\n", ProcessInformationClass, ProcessInformation, ProcessInformationLength); + DPRINT1("NtSetInformationProcess() %x failed, Status: 0x%x\n", Status); + return Status; + } + + switch(ProcessInformationClass) + { + case ProcessSessionInformation: + Access = PROCESS_SET_INFORMATION | PROCESS_SET_SESSIONID; + break; + case ProcessExceptionPort: + case ProcessDebugPort: + Access = PROCESS_SET_INFORMATION | PROCESS_SET_PORT; + break; + + default: + Access = PROCESS_SET_INFORMATION; + break; + } + + Status = ObReferenceObjectByHandle(ProcessHandle, + Access, + PsProcessType, + PreviousMode, + (PVOID*)&Process, + NULL); + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + switch (ProcessInformationClass) + { + case ProcessQuotaLimits: + case ProcessBasePriority: + case ProcessRaisePriority: + Status = STATUS_NOT_IMPLEMENTED; + break; + + case ProcessDebugPort: + { + HANDLE PortHandle = NULL; + + /* make a safe copy of the buffer on the stack */ + _SEH_TRY + { + PortHandle = *(PHANDLE)ProcessInformation; + Status = (PortHandle != NULL ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + PEPORT DebugPort; + + /* in case we had success reading from the buffer, verify the provided + * LPC port handle + */ + Status = ObReferenceObjectByHandle(PortHandle, + 0, + LpcPortObjectType, + PreviousMode, + (PVOID)&DebugPort, + NULL); + if(NT_SUCCESS(Status)) + { + /* lock the process to be thread-safe! */ + + Status = PsLockProcess(Process, FALSE); + if(NT_SUCCESS(Status)) + { + /* + * according to "NT Native API" documentation, setting the debug + * port is only permitted once! + */ + if(Process->DebugPort == NULL) + { + /* keep the reference to the handle! */ + Process->DebugPort = DebugPort; + + if(Process->Peb) + { + /* we're now debugging the process, so set the flag in the PEB + structure. However, to access it we need to attach to the + process so we're sure we're in the right context! */ + + KeAttachProcess(&Process->Pcb); + _SEH_TRY + { + Process->Peb->BeingDebugged = TRUE; + } + _SEH_HANDLE + { + DPRINT1("Trying to set the Peb->BeingDebugged field of process 0x%x failed, exception: 0x%x\n", Process, _SEH_GetExceptionCode()); + } + _SEH_END; + KeDetachProcess(); + } + Status = STATUS_SUCCESS; + } + else + { + ObDereferenceObject(DebugPort); + Status = STATUS_PORT_ALREADY_SET; + } + PsUnlockProcess(Process); + } + else + { + ObDereferenceObject(DebugPort); + } + } + } + break; + } + + case ProcessExceptionPort: + { + HANDLE PortHandle = NULL; + + /* make a safe copy of the buffer on the stack */ + _SEH_TRY + { + PortHandle = *(PHANDLE)ProcessInformation; + Status = STATUS_SUCCESS; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + PEPORT ExceptionPort; + + /* in case we had success reading from the buffer, verify the provided + * LPC port handle + */ + Status = ObReferenceObjectByHandle(PortHandle, + 0, + LpcPortObjectType, + PreviousMode, + (PVOID)&ExceptionPort, + NULL); + if(NT_SUCCESS(Status)) + { + /* lock the process to be thread-safe! */ + + Status = PsLockProcess(Process, FALSE); + if(NT_SUCCESS(Status)) + { + /* + * according to "NT Native API" documentation, setting the exception + * port is only permitted once! + */ + if(Process->ExceptionPort == NULL) + { + /* keep the reference to the handle! */ + Process->ExceptionPort = ExceptionPort; + Status = STATUS_SUCCESS; + } + else + { + ObDereferenceObject(ExceptionPort); + Status = STATUS_PORT_ALREADY_SET; + } + PsUnlockProcess(Process); + } + else + { + ObDereferenceObject(ExceptionPort); + } + } + } + break; + } + + case ProcessAccessToken: + { + HANDLE TokenHandle = NULL; + + /* make a safe copy of the buffer on the stack */ + _SEH_TRY + { + TokenHandle = ((PPROCESS_ACCESS_TOKEN)ProcessInformation)->Token; + Status = STATUS_SUCCESS; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + /* in case we had success reading from the buffer, perform the actual task */ + Status = PspAssignPrimaryToken(Process, TokenHandle); + } + break; + } + + case ProcessDefaultHardErrorMode: + { + _SEH_TRY + { + InterlockedExchange((LONG*)&Process->DefaultHardErrorProcessing, + *(PLONG)ProcessInformation); + Status = STATUS_SUCCESS; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + break; + } + + case ProcessSessionInformation: + { + PROCESS_SESSION_INFORMATION SessionInfo; + Status = STATUS_SUCCESS; + + _SEH_TRY + { + /* copy the structure to the stack */ + SessionInfo = *(PPROCESS_SESSION_INFORMATION)ProcessInformation; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + /* we successfully copied the structure to the stack, continue processing */ + + /* + * setting the session id requires the SeTcbPrivilege! + */ + if(!SeSinglePrivilegeCheck(SeTcbPrivilege, + PreviousMode)) + { + DPRINT1("NtSetInformationProcess: Caller requires the SeTcbPrivilege privilege for setting ProcessSessionInformation!\n"); + /* can't set the session id, bail! */ + Status = STATUS_PRIVILEGE_NOT_HELD; + break; + } + + /* FIXME - update the session id for the process token */ + + Status = PsLockProcess(Process, FALSE); + if(NT_SUCCESS(Status)) + { + Process->SessionId = SessionInfo.SessionId; + + /* Update the session id in the PEB structure */ + if(Process->Peb != NULL) + { + /* we need to attach to the process to make sure we're in the right + context to access the PEB structure */ + KeAttachProcess(&Process->Pcb); + + _SEH_TRY + { + /* FIXME: Process->Peb->SessionId = SessionInfo.SessionId; */ + + Status = STATUS_SUCCESS; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + KeDetachProcess(); + } + + PsUnlockProcess(Process); + } + } + break; + } + + case ProcessPriorityClass: + { + PROCESS_PRIORITY_CLASS ppc; + + _SEH_TRY + { + ppc = *(PPROCESS_PRIORITY_CLASS)ProcessInformation; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + } + + break; + } + + case ProcessLdtInformation: + case ProcessLdtSize: + case ProcessIoPortHandlers: + case ProcessWorkingSetWatch: + case ProcessUserModeIOPL: + case ProcessEnableAlignmentFaultFixup: + case ProcessAffinityMask: + Status = STATUS_NOT_IMPLEMENTED; + break; + + case ProcessBasicInformation: + case ProcessIoCounters: + case ProcessTimes: + case ProcessPooledUsageAndLimits: + case ProcessWx86Information: + case ProcessHandleCount: + case ProcessWow64Information: + default: + Status = STATUS_INVALID_INFO_CLASS; + } + ObDereferenceObject(Process); + return(Status); +} + + +/********************************************************************** + * NAME INTERNAL + * PiQuerySystemProcessInformation + * + * DESCRIPTION + * Compute the size of a process+thread snapshot as + * expected by NtQuerySystemInformation. + * + * RETURN VALUE + * 0 on error; otherwise the size, in bytes of the buffer + * required to write a full snapshot. + * + * NOTE + * We assume (sizeof (PVOID) == sizeof (ULONG)) holds. + */ +NTSTATUS +PiQuerySystemProcessInformation(PVOID Buffer, + ULONG Size, + PULONG ReqSize) +{ + return STATUS_NOT_IMPLEMENTED; + +#if 0 + PLIST_ENTRY CurrentEntryP; + PEPROCESS CurrentP; + PLIST_ENTRY CurrentEntryT; + PETHREAD CurrentT; + + ULONG RequiredSize = 0L; + BOOLEAN SizeOnly = FALSE; + + ULONG SpiSize = 0L; + + PSYSTEM_PROCESS_INFORMATION pInfoP = (PSYSTEM_PROCESS_INFORMATION) SnapshotBuffer; + PSYSTEM_PROCESS_INFORMATION pInfoPLast = NULL; + PSYSTEM_THREAD_INFO pInfoT = NULL; + + + /* Lock the process list. */ + ExAcquireFastMutex(&PspActiveProcessMutex); + + /* + * Scan the process list. Since the + * list is circular, the guard is false + * after the last process. + */ + for ( CurrentEntryP = PsActiveProcessHead.Flink; + (CurrentEntryP != & PsActiveProcessHead); + CurrentEntryP = CurrentEntryP->Flink + ) + { + /* + * Compute how much space is + * occupied in the snapshot + * by adding this process info. + * (at least one thread). + */ + SpiSizeCurrent = sizeof (SYSTEM_PROCESS_INFORMATION); + RequiredSize += SpiSizeCurrent; + /* + * Do not write process data in the + * buffer if it is too small. + */ + if (TRUE == SizeOnly) continue; + /* + * Check if the buffer can contain + * the full snapshot. + */ + if (Size < RequiredSize) + { + SizeOnly = TRUE; + continue; + } + /* + * Get a reference to the + * process descriptor we are + * handling. + */ + CurrentP = CONTAINING_RECORD( + CurrentEntryP, + EPROCESS, + ProcessListEntry + ); + /* + * Write process data in the buffer. + */ + RtlZeroMemory (pInfoP, sizeof (SYSTEM_PROCESS_INFORMATION)); + /* PROCESS */ + pInfoP->ThreadCount = 0L; + pInfoP->ProcessId = CurrentP->UniqueProcessId; + RtlInitUnicodeString ( + & pInfoP->Name, + CurrentP->ImageFileName + ); + /* THREAD */ + for ( pInfoT = & CurrentP->ThreadSysInfo [0], + CurrentEntryT = CurrentP->ThreadListHead.Flink; + + (CurrentEntryT != & CurrentP->ThreadListHead); + + pInfoT = & CurrentP->ThreadSysInfo [pInfoP->ThreadCount], + CurrentEntryT = CurrentEntryT->Flink + ) + { + /* + * Recalculate the size of the + * information block. + */ + if (0 < pInfoP->ThreadCount) + { + RequiredSize += sizeof (SYSTEM_THREAD_INFORMATION); + } + /* + * Do not write thread data in the + * buffer if it is too small. + */ + if (TRUE == SizeOnly) continue; + /* + * Check if the buffer can contain + * the full snapshot. + */ + if (Size < RequiredSize) + { + SizeOnly = TRUE; + continue; + } + /* + * Get a reference to the + * thread descriptor we are + * handling. + */ + CurrentT = CONTAINING_RECORD( + CurrentEntryT, + KTHREAD, + ThreadListEntry + ); + /* + * Write thread data. + */ + RtlZeroMemory ( + pInfoT, + sizeof (SYSTEM_THREAD_INFORMATION) + ); + pInfoT->KernelTime = CurrentT-> ; /* TIME */ + pInfoT->UserTime = CurrentT-> ; /* TIME */ + pInfoT->CreateTime = CurrentT-> ; /* TIME */ + pInfoT->TickCount = CurrentT-> ; /* ULONG */ + pInfoT->StartEIP = CurrentT-> ; /* ULONG */ + pInfoT->ClientId = CurrentT-> ; /* CLIENT_ID */ + pInfoT->ClientId = CurrentT-> ; /* CLIENT_ID */ + pInfoT->DynamicPriority = CurrentT-> ; /* ULONG */ + pInfoT->BasePriority = CurrentT-> ; /* ULONG */ + pInfoT->nSwitches = CurrentT-> ; /* ULONG */ + pInfoT->State = CurrentT-> ; /* DWORD */ + pInfoT->WaitReason = CurrentT-> ; /* KWAIT_REASON */ + /* + * Count the number of threads + * this process has. + */ + ++ pInfoP->ThreadCount; + } + /* + * Save the size of information + * stored in the buffer for the + * current process. + */ + pInfoP->RelativeOffset = SpiSize; + /* + * Save a reference to the last + * valid information block. + */ + pInfoPLast = pInfoP; + /* + * Compute the offset of the + * SYSTEM_PROCESS_INFORMATION + * descriptor in the snapshot + * buffer for the next process. + */ + (ULONG) pInfoP += SpiSize; + } + /* + * Unlock the process list. + */ + ExReleaseFastMutex ( + & PspActiveProcessMutex + ); + /* + * Return the proper error status code, + * if the buffer was too small. + */ + if (TRUE == SizeOnly) + { + if (NULL != RequiredSize) + { + *pRequiredSize = RequiredSize; + } + return STATUS_INFO_LENGTH_MISMATCH; + } + /* + * Mark the end of the snapshot. + */ + pInfoP->RelativeOffset = 0L; + /* OK */ + return STATUS_SUCCESS; +#endif +} + +/* + * @unimplemented + */ +NTSTATUS STDCALL +NtSetInformationThread (IN HANDLE ThreadHandle, + IN THREADINFOCLASS ThreadInformationClass, + IN PVOID ThreadInformation, + IN ULONG ThreadInformationLength) +{ + PETHREAD Thread; + NTSTATUS Status; + union + { + KPRIORITY Priority; + LONG Increment; + KAFFINITY Affinity; + HANDLE Handle; + PVOID Address; + }u; + + PAGED_CODE(); + + if (ThreadInformationClass <= MaxThreadInfoClass && + !SetInformationData[ThreadInformationClass].Implemented) + { + return STATUS_NOT_IMPLEMENTED; + } + if (ThreadInformationClass > MaxThreadInfoClass || + SetInformationData[ThreadInformationClass].Size == 0) + { + return STATUS_INVALID_INFO_CLASS; + } + if (ThreadInformationLength != SetInformationData[ThreadInformationClass].Size) + { + return STATUS_INFO_LENGTH_MISMATCH; + } + + Status = ObReferenceObjectByHandle (ThreadHandle, + THREAD_SET_INFORMATION, + PsThreadType, + ExGetPreviousMode (), + (PVOID*)&Thread, + NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + Status = MmCopyFromCaller(&u.Priority, + ThreadInformation, + SetInformationData[ThreadInformationClass].Size); + if (NT_SUCCESS(Status)) + { + switch (ThreadInformationClass) + { + case ThreadPriority: + if (u.Priority < LOW_PRIORITY || u.Priority >= MAXIMUM_PRIORITY) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + KeSetPriorityThread(&Thread->Tcb, u.Priority); + break; + + case ThreadBasePriority: + KeSetBasePriorityThread (&Thread->Tcb, u.Increment); + break; + + case ThreadAffinityMask: + Status = KeSetAffinityThread(&Thread->Tcb, u.Affinity); + break; + + case ThreadImpersonationToken: + Status = PsAssignImpersonationToken (Thread, u.Handle); + break; + + case ThreadQuerySetWin32StartAddress: + Thread->Win32StartAddress = u.Address; + break; + + default: + /* Shoult never occure if the data table is correct */ + KEBUGCHECK(0); + } + } + ObDereferenceObject (Thread); + + return Status; +} + +/* + * @implemented + */ +NTSTATUS STDCALL +NtQueryInformationThread (IN HANDLE ThreadHandle, + IN THREADINFOCLASS ThreadInformationClass, + OUT PVOID ThreadInformation, + IN ULONG ThreadInformationLength, + OUT PULONG ReturnLength OPTIONAL) +{ + PETHREAD Thread; + NTSTATUS Status; + union + { + THREAD_BASIC_INFORMATION TBI; + KERNEL_USER_TIMES TTI; + PVOID Address; + LARGE_INTEGER Count; + BOOLEAN Last; + }u; + + PAGED_CODE(); + + if (ThreadInformationClass <= MaxThreadInfoClass && + !QueryInformationData[ThreadInformationClass].Implemented) + { + return STATUS_NOT_IMPLEMENTED; + } + if (ThreadInformationClass > MaxThreadInfoClass || + QueryInformationData[ThreadInformationClass].Size == 0) + { + return STATUS_INVALID_INFO_CLASS; + } + if (ThreadInformationLength != QueryInformationData[ThreadInformationClass].Size) + { + return STATUS_INFO_LENGTH_MISMATCH; + } + + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_QUERY_INFORMATION, + PsThreadType, + ExGetPreviousMode(), + (PVOID*)&Thread, + NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + switch (ThreadInformationClass) + { + case ThreadBasicInformation: + /* A test on W2K agains ntdll shows NtQueryInformationThread return STATUS_PENDING + * as ExitStatus for current/running thread, while KETHREAD's ExitStatus is + * 0. So do the conversion here: + * -Gunnar */ + u.TBI.ExitStatus = (Thread->ExitStatus == 0) ? STATUS_PENDING : Thread->ExitStatus; + u.TBI.TebBaseAddress = Thread->Tcb.Teb; + u.TBI.ClientId = Thread->Cid; + u.TBI.AffinityMask = Thread->Tcb.Affinity; + u.TBI.Priority = Thread->Tcb.Priority; + u.TBI.BasePriority = Thread->Tcb.BasePriority; + break; + + case ThreadTimes: + u.TTI.KernelTime.QuadPart = Thread->Tcb.KernelTime * 100000LL; + u.TTI.UserTime.QuadPart = Thread->Tcb.UserTime * 100000LL; + u.TTI.CreateTime = Thread->CreateTime; + /*This works*/ + u.TTI.ExitTime = Thread->ExitTime; + break; + + case ThreadQuerySetWin32StartAddress: + u.Address = Thread->Win32StartAddress; + break; + + case ThreadPerformanceCount: + /* Nebbett says this class is always zero */ + u.Count.QuadPart = 0; + break; + + case ThreadAmILastThread: + if (Thread->ThreadsProcess->ThreadListHead.Flink->Flink == + &Thread->ThreadsProcess->ThreadListHead) + { + u.Last = TRUE; + } + else + { + u.Last = FALSE; + } + break; + default: + /* Shoult never occure if the data table is correct */ + KEBUGCHECK(0); + } + if (QueryInformationData[ThreadInformationClass].Size) + { + Status = MmCopyToCaller(ThreadInformation, + &u.TBI, + QueryInformationData[ThreadInformationClass].Size); + } + if (ReturnLength) + { + NTSTATUS Status2; + static ULONG Null = 0; + Status2 = MmCopyToCaller(ReturnLength, + NT_SUCCESS(Status) ? &QueryInformationData[ThreadInformationClass].Size : &Null, + sizeof(ULONG)); + if (NT_SUCCESS(Status)) + { + Status = Status2; + } + } + + ObDereferenceObject(Thread); + return(Status); +} +/* EOF */ diff --git a/reactos/ntoskrnl/ps/security.c b/reactos/ntoskrnl/ps/security.c new file mode 100644 index 00000000000..ef076b3427f --- /dev/null +++ b/reactos/ntoskrnl/ps/security.c @@ -0,0 +1,502 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/ps/security.c + * PURPOSE: Process Manager Security (Tokens, Impersionation) + * + * PROGRAMMERS: David Welch (welch@cwcom.net) + */ + +/* INCLUDES ******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ******************************************************************/ + +/* FUNCTIONS *****************************************************************/ + +/* + * @implemented + */ +NTSTATUS +STDCALL +NtOpenProcessToken(IN HANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + OUT PHANDLE TokenHandle) +{ + return NtOpenProcessTokenEx(ProcessHandle, + DesiredAccess, + 0, + TokenHandle); +} + +/* + * @implemented + */ +NTSTATUS +STDCALL +NtOpenProcessTokenEx(IN HANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG HandleAttributes, + OUT PHANDLE TokenHandle) +{ + PACCESS_TOKEN Token; + HANDLE hToken; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + if(PreviousMode == UserMode) + { + _SEH_TRY + { + ProbeForWrite(TokenHandle, + sizeof(HANDLE), + sizeof(ULONG)); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + + Status = PsOpenTokenOfProcess(ProcessHandle, + &Token); + if(NT_SUCCESS(Status)) + { + Status = ObCreateHandle(PsGetCurrentProcess(), + Token, + DesiredAccess, + FALSE, + &hToken); + ObDereferenceObject(Token); + + if(NT_SUCCESS(Status)) + { + _SEH_TRY + { + *TokenHandle = hToken; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + } + + return Status; +} + +/* + * @implemented + */ +PACCESS_TOKEN +STDCALL +PsReferencePrimaryToken(PEPROCESS Process) +{ + /* Reference and return the Token */ + ObReferenceObjectByPointer(Process->Token, + TOKEN_ALL_ACCESS, + SepTokenObjectType, + KernelMode); + return(Process->Token); +} + +/* + * @implemented + */ +NTSTATUS +STDCALL +PsOpenTokenOfProcess(HANDLE ProcessHandle, + PACCESS_TOKEN* Token) +{ + PEPROCESS Process; + NTSTATUS Status; + + /* Get the Token */ + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_QUERY_INFORMATION, + PsProcessType, + ExGetPreviousMode(), + (PVOID*)&Process, + NULL); + + /* Reference it */ + if(NT_SUCCESS(Status)) { + + *Token = PsReferencePrimaryToken(Process); + ObDereferenceObject(Process); + } + + /* Return */ + return Status; +} + +NTSTATUS +STDCALL +PspAssignPrimaryToken(PEPROCESS Process, + HANDLE TokenHandle) +{ + PACCESS_TOKEN Token; + PACCESS_TOKEN OldToken; + NTSTATUS Status; + + /* Reference the Token */ + Status = ObReferenceObjectByHandle(TokenHandle, + 0, + SepTokenObjectType, + KeGetPreviousMode(), + (PVOID*)&Token, + NULL); + if (!NT_SUCCESS(Status)) { + + return(Status); + } + + /* Exchange them */ + Status = SeExchangePrimaryToken(Process, Token, &OldToken); + + /* Derefernece Tokens and Return */ + if (NT_SUCCESS(Status)) ObDereferenceObject(OldToken); + ObDereferenceObject(Token); + return(Status); +} + +/* + * @implemented + */ +NTSTATUS +STDCALL +PsAssignImpersonationToken(PETHREAD Thread, + HANDLE TokenHandle) +{ + PACCESS_TOKEN Token; + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; + NTSTATUS Status; + + if (TokenHandle != NULL) { + + Status = ObReferenceObjectByHandle(TokenHandle, + TOKEN_IMPERSONATE, + SepTokenObjectType, + KeGetPreviousMode(), + (PVOID*)&Token, + NULL); + + if (!NT_SUCCESS(Status)) { + + return(Status); + } + + ImpersonationLevel = SeTokenImpersonationLevel(Token); + + } else { + + Token = NULL; + ImpersonationLevel = 0; + } + + PsImpersonateClient(Thread, + Token, + FALSE, + FALSE, + ImpersonationLevel); + + if (Token != NULL) ObDereferenceObject(Token); + return(STATUS_SUCCESS); +} + +/* + * @implemented + */ +VOID STDCALL +PsRevertToSelf (VOID) +{ + PsRevertThreadToSelf(PsGetCurrentThread()); +} + +/* + * @implemented + */ +VOID +STDCALL +PsRevertThreadToSelf(IN PETHREAD Thread) +{ + if (Thread->ActiveImpersonationInfo == TRUE) { + + ObDereferenceObject (Thread->ImpersonationInfo->Token); + Thread->ActiveImpersonationInfo = FALSE; + } +} + +/* + * @implemented + */ +VOID +STDCALL +PsImpersonateClient(IN PETHREAD Thread, + IN PACCESS_TOKEN Token, + IN BOOLEAN CopyOnOpen, + IN BOOLEAN EffectiveOnly, + IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) +{ + + if (Token == NULL) { + + if (Thread->ActiveImpersonationInfo == TRUE) { + + Thread->ActiveImpersonationInfo = FALSE; + + if (Thread->ImpersonationInfo->Token != NULL) { + + ObDereferenceObject (Thread->ImpersonationInfo->Token); + } + } + + return; + } + + if (Thread->ImpersonationInfo == NULL) { + + Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool, + sizeof(PS_IMPERSONATION_INFORMATION)); + } + + Thread->ImpersonationInfo->ImpersonationLevel = ImpersonationLevel; + Thread->ImpersonationInfo->CopyOnOpen = CopyOnOpen; + Thread->ImpersonationInfo->EffectiveOnly = EffectiveOnly; + Thread->ImpersonationInfo->Token = Token; + + ObReferenceObjectByPointer(Token, + 0, + SepTokenObjectType, + KernelMode); + + Thread->ActiveImpersonationInfo = TRUE; +} + + +PACCESS_TOKEN +STDCALL +PsReferenceEffectiveToken(PETHREAD Thread, + PTOKEN_TYPE TokenType, + PBOOLEAN EffectiveOnly, + PSECURITY_IMPERSONATION_LEVEL Level) +{ + PEPROCESS Process; + PACCESS_TOKEN Token; + + if (Thread->ActiveImpersonationInfo == FALSE) { + + Process = Thread->ThreadsProcess; + *TokenType = TokenPrimary; + *EffectiveOnly = FALSE; + Token = Process->Token; + + } else { + + Token = Thread->ImpersonationInfo->Token; + *TokenType = TokenImpersonation; + *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly; + *Level = Thread->ImpersonationInfo->ImpersonationLevel; + } + + return(Token); +} + +NTSTATUS +STDCALL +NtImpersonateThread(IN HANDLE ThreadHandle, + IN HANDLE ThreadToImpersonateHandle, + IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService) +{ + SECURITY_QUALITY_OF_SERVICE SafeServiceQoS; + SECURITY_CLIENT_CONTEXT ClientContext; + PETHREAD Thread; + PETHREAD ThreadToImpersonate; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForRead(SecurityQualityOfService, + sizeof(SECURITY_QUALITY_OF_SERVICE), + sizeof(ULONG)); + SafeServiceQoS = *SecurityQualityOfService; + SecurityQualityOfService = &SafeServiceQoS; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } + } + + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_IMPERSONATE, + PsThreadType, + PreviousMode, + (PVOID*)&Thread, + NULL); + if(NT_SUCCESS(Status)) + { + Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle, + THREAD_DIRECT_IMPERSONATION, + PsThreadType, + PreviousMode, + (PVOID*)&ThreadToImpersonate, + NULL); + if(NT_SUCCESS(Status)) + { + Status = SeCreateClientSecurity(ThreadToImpersonate, + SecurityQualityOfService, + 0, + &ClientContext); + if(NT_SUCCESS(Status)) + { + SeImpersonateClient(&ClientContext, + Thread); + if(ClientContext.ClientToken != NULL) + { + ObDereferenceObject (ClientContext.ClientToken); + } + } + + ObDereferenceObject(ThreadToImpersonate); + } + ObDereferenceObject(Thread); + } + + return Status; +} + +/* + * @implemented + */ +PACCESS_TOKEN +STDCALL +PsReferenceImpersonationToken(IN PETHREAD Thread, + OUT PBOOLEAN CopyOnOpen, + OUT PBOOLEAN EffectiveOnly, + OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel) +{ + + if (Thread->ActiveImpersonationInfo == FALSE) { + + return NULL; + } + + *ImpersonationLevel = Thread->ImpersonationInfo->ImpersonationLevel; + *CopyOnOpen = Thread->ImpersonationInfo->CopyOnOpen; + *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly; + + ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token, + TOKEN_ALL_ACCESS, + SepTokenObjectType, + KernelMode); + + return Thread->ImpersonationInfo->Token; +} + +#ifdef PsDereferencePrimaryToken +#undef PsDereferenceImpersonationToken +#endif +/* + * @implemented + */ +VOID +STDCALL +PsDereferenceImpersonationToken(IN PACCESS_TOKEN ImpersonationToken) +{ + if (ImpersonationToken) { + + ObDereferenceObject(ImpersonationToken); + } +} + +#ifdef PsDereferencePrimaryToken +#undef PsDereferencePrimaryToken +#endif +/* + * @implemented + */ +VOID +STDCALL +PsDereferencePrimaryToken(IN PACCESS_TOKEN PrimaryToken) +{ + ObDereferenceObject(PrimaryToken); +} + +/* + * @implemented + */ +BOOLEAN +STDCALL +PsDisableImpersonation(IN PETHREAD Thread, + IN PSE_IMPERSONATION_STATE ImpersonationState) +{ + if (Thread->ActiveImpersonationInfo == FALSE) { + ImpersonationState->Token = NULL; + ImpersonationState->CopyOnOpen = FALSE; + ImpersonationState->EffectiveOnly = FALSE; + ImpersonationState->Level = 0; + return TRUE; + } + +/* FIXME */ +/* ExfAcquirePushLockExclusive(&Thread->ThreadLock); */ + + Thread->ActiveImpersonationInfo = FALSE; + ImpersonationState->Token = Thread->ImpersonationInfo->Token; + ImpersonationState->CopyOnOpen = Thread->ImpersonationInfo->CopyOnOpen; + ImpersonationState->EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly; + ImpersonationState->Level = Thread->ImpersonationInfo->ImpersonationLevel; + +/* FIXME */ +/* ExfReleasePushLock(&Thread->ThreadLock); */ + + return TRUE; +} + +/* + * @implemented + */ +VOID +STDCALL +PsRestoreImpersonation(IN PETHREAD Thread, + IN PSE_IMPERSONATION_STATE ImpersonationState) +{ + + PsImpersonateClient(Thread, + ImpersonationState->Token, + ImpersonationState->CopyOnOpen, + ImpersonationState->EffectiveOnly, + ImpersonationState->Level); + + ObfDereferenceObject(ImpersonationState->Token); +} + +/* EOF */ diff --git a/reactos/ntoskrnl/ps/suspend.c b/reactos/ntoskrnl/ps/suspend.c index 7e1d56ec5a5..579a5c2e410 100644 --- a/reactos/ntoskrnl/ps/suspend.c +++ b/reactos/ntoskrnl/ps/suspend.c @@ -14,112 +14,15 @@ #define NDEBUG #include -/* NOTES ********************************************************************** - * - */ - -/* GLOBALS *******************************************************************/ - -static FAST_MUTEX SuspendMutex; +ULONG +STDCALL +KeResumeThread(PKTHREAD Thread); +ULONG +STDCALL +KeSuspendThread(PKTHREAD Thread); /* FUNCTIONS *****************************************************************/ -VOID STDCALL -PiSuspendThreadRundownRoutine(PKAPC Apc) -{ -} - - -VOID STDCALL -PiSuspendThreadKernelRoutine(PKAPC Apc, - PKNORMAL_ROUTINE* NormalRoutine, - PVOID* NormalContext, - PVOID* SystemArgument1, - PVOID* SystemArguemnt2) -{ -} - - -VOID STDCALL -PiSuspendThreadNormalRoutine(PVOID NormalContext, - PVOID SystemArgument1, - PVOID SystemArgument2) -{ - PETHREAD CurrentThread = PsGetCurrentThread(); - while (CurrentThread->Tcb.SuspendCount > 0) - { - KeWaitForSingleObject(&CurrentThread->Tcb.SuspendSemaphore, - 0, - UserMode, - TRUE, - NULL); - } -} - - -NTSTATUS -PsResumeThread (PETHREAD Thread, - PULONG SuspendCount) -{ - DPRINT("PsResumeThread (Thread %p SuspendCount %p) called\n"); - - ExAcquireFastMutex (&SuspendMutex); - - if (SuspendCount != NULL) - { - *SuspendCount = Thread->Tcb.SuspendCount; - } - - if (Thread->Tcb.SuspendCount > 0) - { - Thread->Tcb.SuspendCount--; - if (Thread->Tcb.SuspendCount == 0) - { - KeReleaseSemaphore (&Thread->Tcb.SuspendSemaphore, - IO_NO_INCREMENT, - 1, - FALSE); - } - } - - ExReleaseFastMutex (&SuspendMutex); - - return STATUS_SUCCESS; -} - - -NTSTATUS -PsSuspendThread(PETHREAD Thread, PULONG PreviousSuspendCount) -{ - ULONG OldValue; - - ExAcquireFastMutex(&SuspendMutex); - OldValue = Thread->Tcb.SuspendCount; - Thread->Tcb.SuspendCount++; - if (!Thread->Tcb.SuspendApc.Inserted) - { - if (!KeInsertQueueApc(&Thread->Tcb.SuspendApc, - NULL, - NULL, - IO_NO_INCREMENT)) - { - Thread->Tcb.SuspendCount--; - ExReleaseFastMutex(&SuspendMutex); - return(STATUS_THREAD_IS_TERMINATING); - } - } - ExReleaseFastMutex(&SuspendMutex); - if (PreviousSuspendCount != NULL) - { - *PreviousSuspendCount = OldValue; - } - return(STATUS_SUCCESS); -} - - -NTSTATUS STDCALL -NtResumeThread(IN HANDLE ThreadHandle, - IN PULONG SuspendCount OPTIONAL) /* * FUNCTION: Decrements a thread's resume count * ARGUMENTS: @@ -127,48 +30,74 @@ NtResumeThread(IN HANDLE ThreadHandle, * ResumeCount = The resulting resume count. * RETURNS: Status */ +NTSTATUS +STDCALL +NtResumeThread(IN HANDLE ThreadHandle, + IN PULONG SuspendCount OPTIONAL) { - PETHREAD Thread; - NTSTATUS Status; - ULONG Count; + PETHREAD Thread; + ULONG Prev; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); - DPRINT("NtResumeThead(ThreadHandle %lx SuspendCount %p)\n", - ThreadHandle, SuspendCount); + DPRINT("NtResumeThead(ThreadHandle %lx SuspendCount %p)\n", + ThreadHandle, SuspendCount); + + /* Check buffer validity */ + if(SuspendCount && PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(SuspendCount, + sizeof(ULONG), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; - Status = ObReferenceObjectByHandle (ThreadHandle, - THREAD_SUSPEND_RESUME, - PsThreadType, - UserMode, - (PVOID*)&Thread, - NULL); - if (!NT_SUCCESS(Status)) - { - return Status; + if(!NT_SUCCESS(Status)) return Status; } - Status = PsResumeThread (Thread, &Count); - if (!NT_SUCCESS(Status)) - { - ObDereferenceObject ((PVOID)Thread); - return Status; + /* Get the Thread Object */ + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_SUSPEND_RESUME, + PsThreadType, + PreviousMode, + (PVOID*)&Thread, + NULL); + if (!NT_SUCCESS(Status)) { + + return Status; + } + + /* Call the Kernel Function */ + Prev = KeResumeThread(&Thread->Tcb); + + /* Return it */ + if(SuspendCount) { + + _SEH_TRY { + + *SuspendCount = Prev; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; } - if (SuspendCount != NULL) - { - *SuspendCount = Count; - } - - ObDereferenceObject ((PVOID)Thread); - - return STATUS_SUCCESS; + /* Dereference and Return */ + ObDereferenceObject ((PVOID)Thread); + return Status; } - -NTSTATUS STDCALL -NtSuspendThread(IN HANDLE ThreadHandle, - IN PULONG PreviousSuspendCount OPTIONAL) /* * FUNCTION: Increments a thread's suspend count * ARGUMENTS: @@ -182,45 +111,69 @@ NtSuspendThread(IN HANDLE ThreadHandle, * MAXIMUM_SUSPEND_COUNT. * RETURNS: Status */ +NTSTATUS +STDCALL +NtSuspendThread(IN HANDLE ThreadHandle, + IN PULONG PreviousSuspendCount OPTIONAL) { - PETHREAD Thread; - NTSTATUS Status; - ULONG Count; + PETHREAD Thread; + ULONG Prev; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; - PAGED_CODE(); + PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + /* Check buffer validity */ + if(PreviousSuspendCount && PreviousMode == UserMode) { + + _SEH_TRY { + + ProbeForWrite(PreviousSuspendCount, + sizeof(ULONG), + sizeof(ULONG)); + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; - Status = ObReferenceObjectByHandle(ThreadHandle, - THREAD_SUSPEND_RESUME, - PsThreadType, - UserMode, - (PVOID*)&Thread, - NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); + if(!NT_SUCCESS(Status)) return Status; } - Status = PsSuspendThread(Thread, &Count); - if (!NT_SUCCESS(Status)) - { - ObDereferenceObject ((PVOID)Thread); - return Status; + /* Get the Thread Object */ + Status = ObReferenceObjectByHandle(ThreadHandle, + THREAD_SUSPEND_RESUME, + PsThreadType, + PreviousMode, + (PVOID*)&Thread, + NULL); + if (!NT_SUCCESS(Status)) { + + return Status; + } + + /* Call the Kernel Function */ + Prev = KeSuspendThread(&Thread->Tcb); + + /* Return it */ + if(PreviousSuspendCount) { + + _SEH_TRY { + + *PreviousSuspendCount = Prev; + + } _SEH_HANDLE { + + Status = _SEH_GetExceptionCode(); + + } _SEH_END; } - if (PreviousSuspendCount != NULL) - { - *PreviousSuspendCount = Count; - } - - ObDereferenceObject ((PVOID)Thread); - - return STATUS_SUCCESS; -} - -VOID INIT_FUNCTION -PsInitialiseSuspendImplementation(VOID) -{ - ExInitializeFastMutex(&SuspendMutex); + /* Dereference and Return */ + ObDereferenceObject((PVOID)Thread); + return Status; } /* EOF */ diff --git a/reactos/ntoskrnl/ps/thread.c b/reactos/ntoskrnl/ps/thread.c index ee9ad920917..952ff730402 100644 --- a/reactos/ntoskrnl/ps/thread.c +++ b/reactos/ntoskrnl/ps/thread.c @@ -23,28 +23,19 @@ #define NDEBUG #include -/* TYPES *******************************************************************/ - /* GLOBALS ******************************************************************/ extern LIST_ENTRY PsActiveProcessHead; +extern PEPROCESS PsIdleProcess; POBJECT_TYPE EXPORTED PsThreadType = NULL; -LONG PiNrThreadsAwaitingReaping = 0; - extern PVOID Ki386InitialStackArray[MAXIMUM_PROCESSORS]; +extern ULONG IdleProcessorMask; +extern LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY]; -/* - * PURPOSE: List of threads associated with each priority level - */ -static LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY]; -static ULONG PriorityListMask = 0; -static ULONG IdleProcessorMask = 0; -static BOOLEAN DoneInitYet = FALSE; -static KEVENT PiReaperThreadEvent; -static BOOLEAN PiReaperThreadShouldTerminate = FALSE; +BOOLEAN DoneInitYet = FALSE; static GENERIC_MAPPING PiThreadMapping = {STANDARD_RIGHTS_READ | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, STANDARD_RIGHTS_WRITE | THREAD_TERMINATE | THREAD_SUSPEND_RESUME | THREAD_ALERT | THREAD_SET_INFORMATION | THREAD_SET_CONTEXT, @@ -63,11 +54,11 @@ PKTHREAD STDCALL KeGetCurrentThread(VOID) PKTHREAD Thread; Ke386SaveFlags(Flags); Ke386DisableInterrupts(); - Thread = KeGetCurrentKPCR()->PrcbData.CurrentThread; + Thread = KeGetCurrentPrcb()->CurrentThread; Ke386RestoreFlags(Flags); return Thread; #else - return(KeGetCurrentKPCR()->PrcbData.CurrentThread); + return(KeGetCurrentPrcb()->CurrentThread); #endif } @@ -221,21 +212,18 @@ PsIsThreadTerminating(IN PETHREAD Thread) } /* - * @unimplemented - */ + * @implemented + */ BOOLEAN STDCALL -PsIsSystemThread( - PETHREAD Thread - ) +PsIsSystemThread(PETHREAD Thread) { - UNIMPLEMENTED; - return FALSE; + return (Thread->SystemThread ? TRUE: FALSE); } /* * @implemented - */ + */ BOOLEAN STDCALL PsIsThreadImpersonating( @@ -245,41 +233,6 @@ PsIsThreadImpersonating( return Thread->ActiveImpersonationInfo; } -static VOID -KiRequestReschedule(CCHAR Processor) -{ - PKPCR Pcr; - - Pcr = (PKPCR)(KPCR_BASE + Processor * PAGE_SIZE); - Pcr->PrcbData.QuantumEnd = TRUE; - KiIpiSendRequest(1 << Processor, IPI_REQUEST_DPC); -} - -static VOID -PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread) -{ - ASSERT(THREAD_STATE_READY == Thread->Tcb.State); - ASSERT(Thread->Tcb.Priority == Priority); - if (Priority >= MAXIMUM_PRIORITY || Priority < LOW_PRIORITY) - { - DPRINT1("Invalid thread priority (%d)\n", Priority); - KEBUGCHECK(0); - } - InsertTailList(&PriorityListHead[Priority], &Thread->Tcb.QueueListEntry); - PriorityListMask |= (1 << Priority); -} - -static VOID PsRemoveFromThreadList(PETHREAD Thread) -{ - ASSERT(THREAD_STATE_READY == Thread->Tcb.State); - RemoveEntryList(&Thread->Tcb.QueueListEntry); - if (IsListEmpty(&PriorityListHead[(ULONG)Thread->Tcb.Priority])) - { - PriorityListMask &= ~(1 << Thread->Tcb.Priority); - } -} - - VOID PsDumpThreads(BOOLEAN IncludeSystem) { PLIST_ENTRY AThread, AProcess; @@ -334,262 +287,6 @@ VOID PsDumpThreads(BOOLEAN IncludeSystem) } } -static PETHREAD PsScanThreadList(KPRIORITY Priority, ULONG Affinity) -{ - PLIST_ENTRY current_entry; - PETHREAD current; - ULONG Mask; - - Mask = (1 << Priority); - if (PriorityListMask & Mask) - { - current_entry = PriorityListHead[Priority].Flink; - while (current_entry != &PriorityListHead[Priority]) - { - current = CONTAINING_RECORD(current_entry, ETHREAD, - Tcb.QueueListEntry); - if (current->Tcb.State != THREAD_STATE_READY) - { - DPRINT1("%d/%d\n", current->Cid.UniqueThread, current->Tcb.State); - } - ASSERT(current->Tcb.State == THREAD_STATE_READY); - DPRINT("current->Tcb.Affinity %x Affinity %x PID %d %d\n", - current->Tcb.Affinity, Affinity, current->Cid.UniqueThread, - Priority); - if (current->Tcb.Affinity & Affinity) - { - PsRemoveFromThreadList(current); - return(current); - } - current_entry = current_entry->Flink; - } - } - return(NULL); -} - -VOID STDCALL -PiWakeupReaperThread(VOID) -{ - KeSetEvent(&PiReaperThreadEvent, 0, FALSE); -} - -VOID STDCALL -PiReaperThreadMain(PVOID Ignored) -{ - for(;;) - { - KeWaitForSingleObject(&PiReaperThreadEvent, - Executive, - KernelMode, - FALSE, - NULL); - if (PiReaperThreadShouldTerminate) - { - PsTerminateSystemThread(0); - } - PsReapThreads(); - } -} - -VOID PsDispatchThreadNoLock (ULONG NewThreadStatus) -{ - KPRIORITY CurrentPriority; - PETHREAD Candidate; - ULONG Affinity; - PKTHREAD KCurrentThread = KeGetCurrentThread(); - PETHREAD CurrentThread = CONTAINING_RECORD(KCurrentThread, ETHREAD, Tcb); - - DPRINT("PsDispatchThread() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(), - CurrentThread->Cid.UniqueThread, NewThreadStatus, CurrentThread->Tcb.State); - - CurrentThread->Tcb.State = (UCHAR)NewThreadStatus; - switch(NewThreadStatus) - { - case THREAD_STATE_READY: - PsInsertIntoThreadList(CurrentThread->Tcb.Priority, - CurrentThread); - break; - case THREAD_STATE_TERMINATED_1: - PsQueueThreadReap(CurrentThread); - break; - } - - Affinity = 1 << KeGetCurrentProcessorNumber(); - for (CurrentPriority = HIGH_PRIORITY; - CurrentPriority >= LOW_PRIORITY; - CurrentPriority--) - { - Candidate = PsScanThreadList(CurrentPriority, Affinity); - if (Candidate == CurrentThread) - { - Candidate->Tcb.State = THREAD_STATE_RUNNING; - KeReleaseDispatcherDatabaseLockFromDpcLevel(); - return; - } - if (Candidate != NULL) - { - PETHREAD OldThread; - PKTHREAD IdleThread; - - DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority); - - Candidate->Tcb.State = THREAD_STATE_RUNNING; - - OldThread = CurrentThread; - CurrentThread = Candidate; - IdleThread = KeGetCurrentKPCR()->PrcbData.IdleThread; - - if (&OldThread->Tcb == IdleThread) - { - IdleProcessorMask &= ~Affinity; - } - else if (&CurrentThread->Tcb == IdleThread) - { - IdleProcessorMask |= Affinity; - } - - MmUpdatePageDir(PsGetCurrentProcess(),(PVOID)CurrentThread->ThreadsProcess, sizeof(EPROCESS)); - - KiArchContextSwitch(&CurrentThread->Tcb, &OldThread->Tcb); - return; - } - } - CPRINT("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber()); - PsDumpThreads(TRUE); - KEBUGCHECK(0); -} - -VOID STDCALL -PsDispatchThread(ULONG NewThreadStatus) -{ - KIRQL oldIrql; - - if (!DoneInitYet || KeGetCurrentKPCR()->PrcbData.IdleThread == NULL) - { - return; - } - oldIrql = KeAcquireDispatcherDatabaseLock(); - PsDispatchThreadNoLock(NewThreadStatus); - KeLowerIrql(oldIrql); -} - -VOID -PsUnblockThread(PETHREAD Thread, PNTSTATUS WaitStatus, KPRIORITY Increment) -{ - if (THREAD_STATE_TERMINATED_1 == Thread->Tcb.State || - THREAD_STATE_TERMINATED_2 == Thread->Tcb.State) - { - DPRINT("Can't unblock thread %d because it's terminating\n", - Thread->Cid.UniqueThread); - } - else if (THREAD_STATE_READY == Thread->Tcb.State || - THREAD_STATE_RUNNING == Thread->Tcb.State) - { - DPRINT("Can't unblock thread %d because it's ready or running\n", - Thread->Cid.UniqueThread); - } - else - { - ULONG Processor; - KAFFINITY Affinity; - - /* FIXME: This propably isn't the right way to do it... */ - if (Thread->Tcb.Priority < LOW_REALTIME_PRIORITY && - Thread->Tcb.BasePriority < LOW_REALTIME_PRIORITY - 2) - { - if (!Thread->Tcb.PriorityDecrement && !Thread->Tcb.DisableBoost) - { - Thread->Tcb.Priority = Thread->Tcb.BasePriority + Increment; - Thread->Tcb.PriorityDecrement = Increment; - } - } - else - { - Thread->Tcb.Quantum = Thread->Tcb.ApcState.Process->ThreadQuantum; - } - - if (WaitStatus != NULL) - { - Thread->Tcb.WaitStatus = *WaitStatus; - } - Thread->Tcb.State = THREAD_STATE_READY; - PsInsertIntoThreadList(Thread->Tcb.Priority, Thread); - Processor = KeGetCurrentProcessorNumber(); - Affinity = Thread->Tcb.Affinity; - if (!(IdleProcessorMask & (1 << Processor) & Affinity) && - (IdleProcessorMask & ~(1 << Processor) & Affinity)) - { - ULONG i; - for (i = 0; i < KeNumberProcessors - 1; i++) - { - Processor++; - if (Processor >= KeNumberProcessors) - { - Processor = 0; - } - if (IdleProcessorMask & (1 << Processor) & Affinity) - { -#if 0 - /* FIXME: - * Reschedule the threads on an other processor - */ - KeReleaseDispatcherDatabaseLockFromDpcLevel(); - KiRequestReschedule(Processor); - KeAcquireDispatcherDatabaseLockAtDpcLevel(); -#endif - break; - } - } - } - } -} - -VOID -PsBlockThread(PNTSTATUS Status, UCHAR Alertable, ULONG WaitMode, - BOOLEAN DispatcherLock, KIRQL WaitIrql, UCHAR WaitReason) -{ - KIRQL oldIrql; - PKTHREAD KThread; - PETHREAD Thread; - PKWAIT_BLOCK WaitBlock; - - if (!DispatcherLock) - { - oldIrql = KeAcquireDispatcherDatabaseLock(); - } - - KThread = KeGetCurrentThread(); - Thread = CONTAINING_RECORD (KThread, ETHREAD, Tcb); - if (KThread->ApcState.KernelApcPending) - { - WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList; - while (WaitBlock) - { - RemoveEntryList (&WaitBlock->WaitListEntry); - WaitBlock = WaitBlock->NextWaitBlock; - } - Thread->Tcb.WaitBlockList = NULL; - PsDispatchThreadNoLock (THREAD_STATE_READY); - if (Status != NULL) - { - *Status = STATUS_KERNEL_APC; - } - } - else - { - Thread->Tcb.Alertable = Alertable; - Thread->Tcb.WaitMode = (UCHAR)WaitMode; - Thread->Tcb.WaitReason = WaitReason; - PsDispatchThreadNoLock(THREAD_STATE_BLOCKED); - - if (Status != NULL) - { - *Status = Thread->Tcb.WaitStatus; - } - } - KeLowerIrql(WaitIrql); -} - VOID PsFreezeAllThreads(PEPROCESS Process) /* @@ -641,7 +338,7 @@ PsEnumThreadsByProcess(PEPROCESS Process) /* * @unimplemented - */ + */ NTSTATUS STDCALL PsRemoveCreateThreadNotifyRoutine ( @@ -704,14 +401,12 @@ VOID INIT_FUNCTION PsPrepareForApplicationProcessorInit(ULONG Id) { PETHREAD IdleThread; - HANDLE IdleThreadHandle; - PKPCR Pcr = (PKPCR)((ULONG_PTR)KPCR_BASE + Id * PAGE_SIZE); + PKPRCB Prcb = ((PKPCR)((ULONG_PTR)KPCR_BASE + Id * PAGE_SIZE))->Prcb; - PsInitializeThread(NULL, + PsInitializeThread(PsIdleProcess, &IdleThread, - &IdleThreadHandle, - THREAD_ALL_ACCESS, NULL, + KernelMode, FALSE); IdleThread->Tcb.State = THREAD_STATE_RUNNING; IdleThread->Tcb.FreezeCount = 0; @@ -719,12 +414,11 @@ PsPrepareForApplicationProcessorInit(ULONG Id) IdleThread->Tcb.UserAffinity = 1 << Id; IdleThread->Tcb.Priority = LOW_PRIORITY; IdleThread->Tcb.BasePriority = LOW_PRIORITY; - Pcr->PrcbData.IdleThread = &IdleThread->Tcb; - Pcr->PrcbData.CurrentThread = &IdleThread->Tcb; + Prcb->IdleThread = &IdleThread->Tcb; + Prcb->CurrentThread = &IdleThread->Tcb; Ki386InitialStackArray[Id] = (PVOID)IdleThread->Tcb.StackLimit; - NtClose(IdleThreadHandle); DPRINT("IdleThread for Processor %d has PID %d\n", Id, IdleThread->Cid.UniqueThread); } @@ -735,11 +429,8 @@ PsInitThreadManagment(VOID) * FUNCTION: Initialize thread managment */ { - HANDLE PiReaperThreadHandle; PETHREAD FirstThread; ULONG i; - HANDLE FirstThreadHandle; - NTSTATUS Status; for (i=0; i < MAXIMUM_PRIORITY; i++) { @@ -759,7 +450,7 @@ PsInitThreadManagment(VOID) PsThreadType->Dump = NULL; PsThreadType->Open = NULL; PsThreadType->Close = NULL; - PsThreadType->Delete = PiDeleteThread; + PsThreadType->Delete = PspDeleteThread; PsThreadType->Parse = NULL; PsThreadType->Security = NULL; PsThreadType->QueryName = NULL; @@ -771,215 +462,20 @@ PsInitThreadManagment(VOID) ObpCreateTypeObject(PsThreadType); - PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle, - THREAD_ALL_ACCESS,NULL, TRUE); + PsInitializeThread(NULL, &FirstThread, NULL, KernelMode, TRUE); FirstThread->Tcb.State = THREAD_STATE_RUNNING; FirstThread->Tcb.FreezeCount = 0; FirstThread->Tcb.UserAffinity = (1 << 0); /* Set the affinity of the first thread to the boot processor */ FirstThread->Tcb.Affinity = (1 << 0); - KeGetCurrentKPCR()->PrcbData.CurrentThread = (PVOID)FirstThread; - NtClose(FirstThreadHandle); + KeGetCurrentPrcb()->CurrentThread = (PVOID)FirstThread; DPRINT("FirstThread %x\n",FirstThread); DoneInitYet = TRUE; - - /* - * Create the reaper thread - */ - PsInitializeThreadReaper(); - KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE); - Status = PsCreateSystemThread(&PiReaperThreadHandle, - THREAD_ALL_ACCESS, - NULL, - NULL, - NULL, - PiReaperThreadMain, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("PS: Failed to create reaper thread.\n"); - KEBUGCHECK(0); - } - - NtClose(PiReaperThreadHandle); + + ExInitializeWorkItem(&PspReaperWorkItem, PspReapRoutine, NULL); } -/* - * @implemented - */ -LONG STDCALL -KeSetBasePriorityThread (PKTHREAD Thread, - LONG Increment) -/* - * Sets thread's base priority relative to the process' base priority - * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h - */ -{ - KPRIORITY Priority; - if (Increment < -2) - { - Increment = -2; - } - else if (Increment > 2) - { - Increment = 2; - } - Priority = ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment; - if (Priority < LOW_PRIORITY) - { - Priority = LOW_PRIORITY; - } - else if (Priority >= MAXIMUM_PRIORITY) - { - Thread->BasePriority = HIGH_PRIORITY; - } - KeSetPriorityThread(Thread, Priority); - return 1; -} - - -/* - * @implemented - */ -KPRIORITY STDCALL -KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority) -{ - KPRIORITY OldPriority; - KIRQL oldIrql; - PKTHREAD CurrentThread; - ULONG Mask; - int i; - PKPCR Pcr; - - if (Priority < LOW_PRIORITY || Priority >= MAXIMUM_PRIORITY) - { - KEBUGCHECK(0); - } - - oldIrql = KeAcquireDispatcherDatabaseLock(); - - OldPriority = Thread->Priority; - - if (OldPriority != Priority) - { - CurrentThread = KeGetCurrentThread(); - if (Thread->State == THREAD_STATE_READY) - { - PsRemoveFromThreadList((PETHREAD)Thread); - Thread->BasePriority = Thread->Priority = (CHAR)Priority; - PsInsertIntoThreadList(Priority, (PETHREAD)Thread); - if (CurrentThread->Priority < Priority) - { - PsDispatchThreadNoLock(THREAD_STATE_READY); - KeLowerIrql(oldIrql); - return (OldPriority); - } - } - else if (Thread->State == THREAD_STATE_RUNNING) - { - Thread->BasePriority = Thread->Priority = (CHAR)Priority; - if (Priority < OldPriority) - { - /* Check for threads with a higher priority */ - Mask = ~((1 << (Priority + 1)) - 1); - if (PriorityListMask & Mask) - { - if (Thread == CurrentThread) - { - PsDispatchThreadNoLock(THREAD_STATE_READY); - KeLowerIrql(oldIrql); - return (OldPriority); - } - else - { - for (i = 0; i < KeNumberProcessors; i++) - { - Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE); - if (Pcr->PrcbData.CurrentThread == Thread) - { - KeReleaseDispatcherDatabaseLockFromDpcLevel(); - KiRequestReschedule(i); - KeLowerIrql(oldIrql); - return (OldPriority); - } - } - } - } - } - } - else - { - Thread->BasePriority = Thread->Priority = (CHAR)Priority; - } - } - KeReleaseDispatcherDatabaseLock(oldIrql); - return(OldPriority); -} - -/* - * @unimplemented - */ -NTSTATUS STDCALL -KeSetAffinityThread(PKTHREAD Thread, - KAFFINITY Affinity) -/* - * Sets thread's affinity - */ -{ - KIRQL oldIrql; - ULONG i; - PKPCR Pcr; - KAFFINITY ProcessorMask; - - DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread, Affinity); - - ASSERT(Affinity & ((1 << KeNumberProcessors) - 1)); - - oldIrql = KeAcquireDispatcherDatabaseLock(); - - Thread->UserAffinity = Affinity; - if (Thread->SystemAffinityActive == FALSE) - { - Thread->Affinity = Affinity; - if (Thread->State == THREAD_STATE_RUNNING) - { - ProcessorMask = 1 << KeGetCurrentKPCR()->ProcessorNumber; - if (Thread == KeGetCurrentThread()) - { - if (!(Affinity & ProcessorMask)) - { - PsDispatchThreadNoLock(THREAD_STATE_READY); - KeLowerIrql(oldIrql); - return STATUS_SUCCESS; - } - } - else - { - for (i = 0; i < KeNumberProcessors; i++) - { - Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE); - if (Pcr->PrcbData.CurrentThread == Thread) - { - if (!(Affinity & ProcessorMask)) - { - KeReleaseDispatcherDatabaseLockFromDpcLevel(); - KiRequestReschedule(i); - KeLowerIrql(oldIrql); - return STATUS_SUCCESS; - } - break; - } - } - ASSERT (i < KeNumberProcessors); - } - } - } - KeReleaseDispatcherDatabaseLock(oldIrql); - return STATUS_SUCCESS; -} - - /********************************************************************** * NtOpenThread/4 * @@ -988,124 +484,139 @@ KeSetAffinityThread(PKTHREAD Thread, NTSTATUS STDCALL NtOpenThread(OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN PCLIENT_ID ClientId) + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN PCLIENT_ID ClientId OPTIONAL) { - NTSTATUS Status = STATUS_INVALID_PARAMETER; - + KPROCESSOR_MODE PreviousMode; + CLIENT_ID SafeClientId; + HANDLE hThread; + NTSTATUS Status = STATUS_SUCCESS; + PAGED_CODE(); - if((NULL != ThreadHandle)&&(NULL != ObjectAttributes)) - { - PETHREAD EThread = NULL; + PreviousMode = ExGetPreviousMode(); - if((ClientId) - && (ClientId->UniqueThread)) - { - // It is an error to specify both - // ObjectAttributes.ObjectName - // and ClientId. - if((ObjectAttributes) - && (ObjectAttributes->ObjectName) - && (0 < ObjectAttributes->ObjectName->Length)) - { - return(STATUS_INVALID_PARAMETER_MIX); - } - // Parameters mix OK - Status = PsLookupThreadByThreadId(ClientId->UniqueThread, - & EThread); - } - else if((ObjectAttributes) - && (ObjectAttributes->ObjectName) - && (0 < ObjectAttributes->ObjectName->Length)) - { - // Three Ob attributes are forbidden - if(!(ObjectAttributes->Attributes & - (OBJ_PERMANENT | OBJ_EXCLUSIVE | OBJ_OPENIF))) - { - Status = ObReferenceObjectByName(ObjectAttributes->ObjectName, - ObjectAttributes->Attributes, - NULL, - DesiredAccess, - PsThreadType, - UserMode, - NULL, - (PVOID*) & EThread); - } - } - // EThread may be OK... - if(STATUS_SUCCESS == Status) - { - Status = ObCreateHandle(PsGetCurrentProcess(), - EThread, - DesiredAccess, - FALSE, - ThreadHandle); - ObDereferenceObject(EThread); - } + if(PreviousMode != KernelMode) + { + _SEH_TRY + { + ProbeForWrite(ThreadHandle, + sizeof(HANDLE), + sizeof(ULONG)); + if(ClientId != NULL) + { + ProbeForRead(ClientId, + sizeof(CLIENT_ID), + sizeof(ULONG)); + SafeClientId = *ClientId; + ClientId = &SafeClientId; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(!NT_SUCCESS(Status)) + { + return Status; + } } - return(Status); + + if(!((ObjectAttributes == NULL) ^ (ClientId == NULL))) + { + DPRINT("NtOpenThread should be called with either ObjectAttributes or ClientId!\n"); + return STATUS_INVALID_PARAMETER; + } + + if(ClientId != NULL) + { + PETHREAD Thread; + + Status = PsLookupThreadByThreadId(ClientId->UniqueThread, + &Thread); + if(NT_SUCCESS(Status)) + { + Status = ObInsertObject(Thread, + NULL, + DesiredAccess, + 0, + NULL, + &hThread); + + ObDereferenceObject(Thread); + } + } + else + { + Status = ObOpenObjectByName(ObjectAttributes, + PsThreadType, + NULL, + PreviousMode, + DesiredAccess, + NULL, + &hThread); + } + + if(NT_SUCCESS(Status)) + { + _SEH_TRY + { + *ThreadHandle = hThread; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + + return Status; } NTSTATUS STDCALL NtYieldExecution(VOID) { - PsDispatchThread(THREAD_STATE_READY); + KiDispatchThread(THREAD_STATE_READY); return(STATUS_SUCCESS); } /* - * @implemented + * NOT EXPORTED */ NTSTATUS STDCALL -PsLookupProcessThreadByCid(IN PCLIENT_ID Cid, - OUT PEPROCESS *Process OPTIONAL, - OUT PETHREAD *Thread) +NtTestAlert(VOID) { - PCID_OBJECT CidObject; - PETHREAD FoundThread; + /* Check and Alert Thread if needed */ + return KeTestAlertThread(ExGetPreviousMode()) ? STATUS_ALERTED : STATUS_SUCCESS; +} - CidObject = PsLockCidHandle((HANDLE)Cid->UniqueThread, PsThreadType); - if(CidObject != NULL) - { - FoundThread = CidObject->Obj.Thread; - ObReferenceObject(FoundThread); - - if(Process != NULL) - { - *Process = FoundThread->ThreadsProcess; - ObReferenceObject(FoundThread->ThreadsProcess); - } - - PsUnlockCidObject(CidObject); - return STATUS_SUCCESS; - } - - return STATUS_INVALID_PARAMETER; +VOID +KeSetPreviousMode (ULONG Mode) +{ + PsGetCurrentThread()->Tcb.PreviousMode = (UCHAR)Mode; } /* * @implemented */ -NTSTATUS STDCALL -PsLookupThreadByThreadId(IN HANDLE ThreadId, - OUT PETHREAD *Thread) +KPROCESSOR_MODE STDCALL +KeGetPreviousMode (VOID) { - PCID_OBJECT CidObject; - - CidObject = PsLockCidHandle(ThreadId, PsThreadType); - if(CidObject != NULL) - { - *Thread = CidObject->Obj.Thread; - ObReferenceObject(*Thread); - - PsUnlockCidObject(CidObject); - return STATUS_SUCCESS; - } + return (ULONG)PsGetCurrentThread()->Tcb.PreviousMode; +} - return STATUS_INVALID_PARAMETER; + +/* + * @implemented + */ +KPROCESSOR_MODE STDCALL +ExGetPreviousMode (VOID) +{ + return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode; } /* EOF */ diff --git a/reactos/ntoskrnl/ps/tinfo.c b/reactos/ntoskrnl/ps/tinfo.c deleted file mode 100644 index c9ca051d48f..00000000000 --- a/reactos/ntoskrnl/ps/tinfo.c +++ /dev/null @@ -1,337 +0,0 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ps/tinfo.c - * PURPOSE: Getting/setting thread information - * - * PROGRAMMERS: David Welch (welch@mcmail.com) - * Skywing (skywing@valhallalegends.com) - */ - -/* INCLUDES *****************************************************************/ - -#include -#include - -/* GLOBALS *****************************************************************/ - -/* - * FIXME: - * Remove the Implemented value if all functions are implemented. - */ - -static const struct -{ - BOOLEAN Implemented; - ULONG Size; -} QueryInformationData[MaxThreadInfoClass + 1] = -{ - {TRUE, sizeof(THREAD_BASIC_INFORMATION)}, // ThreadBasicInformation - {TRUE, sizeof(KERNEL_USER_TIMES)}, // ThreadTimes - {TRUE, 0}, // ThreadPriority - {TRUE, 0}, // ThreadBasePriority - {TRUE, 0}, // ThreadAffinityMask - {TRUE, 0}, // ThreadImpersonationToken - {FALSE, 0}, // ThreadDescriptorTableEntry - {TRUE, 0}, // ThreadEnableAlignmentFaultFixup - {TRUE, 0}, // ThreadEventPair - {TRUE, sizeof(PVOID)}, // ThreadQuerySetWin32StartAddress - {TRUE, 0}, // ThreadZeroTlsCell - {TRUE, sizeof(LARGE_INTEGER)}, // ThreadPerformanceCount - {TRUE, sizeof(BOOLEAN)}, // ThreadAmILastThread - {TRUE, 0}, // ThreadIdealProcessor - {FALSE, 0}, // ThreadPriorityBoost - {TRUE, 0}, // ThreadSetTlsArrayAddress - {FALSE, 0}, // ThreadIsIoPending - {TRUE, 0} // ThreadHideFromDebugger -}; - -static const struct -{ - BOOLEAN Implemented; - ULONG Size; -} SetInformationData[MaxThreadInfoClass + 1] = -{ - {TRUE, 0}, // ThreadBasicInformation - {TRUE, 0}, // ThreadTimes - {TRUE, sizeof(KPRIORITY)}, // ThreadPriority - {TRUE, sizeof(LONG)}, // ThreadBasePriority - {TRUE, sizeof(KAFFINITY)}, // ThreadAffinityMask - {TRUE, sizeof(HANDLE)}, // ThreadImpersonationToken - {TRUE, 0}, // ThreadDescriptorTableEntry - {FALSE, 0}, // ThreadEnableAlignmentFaultFixup -#ifdef _ENABLE_THRDEVTPAIR - {TRUE, sizeof(HANDLE)}, // ThreadEventPair -#else - {FALSE, 0}, // ThreadEventPair -#endif - {TRUE, sizeof(PVOID)}, // ThreadQuerySetWin32StartAddress - {FALSE, 0}, // ThreadZeroTlsCell - {TRUE, 0}, // ThreadPerformanceCount - {TRUE, 0}, // ThreadAmILastThread - {FALSE, 0}, // ThreadIdealProcessor - {FALSE, 0}, // ThreadPriorityBoost - {FALSE, 0}, // ThreadSetTlsArrayAddress - {TRUE, 0}, // ThreadIsIoPending - {FALSE, 0} // ThreadHideFromDebugger -}; - -/* FUNCTIONS *****************************************************************/ - -/* - * @unimplemented - */ -NTSTATUS STDCALL -NtSetInformationThread (IN HANDLE ThreadHandle, - IN THREADINFOCLASS ThreadInformationClass, - IN PVOID ThreadInformation, - IN ULONG ThreadInformationLength) -{ - PETHREAD Thread; - NTSTATUS Status; - union - { - KPRIORITY Priority; - LONG Increment; - KAFFINITY Affinity; - HANDLE Handle; - PVOID Address; - }u; - - PAGED_CODE(); - - if (ThreadInformationClass <= MaxThreadInfoClass && - !SetInformationData[ThreadInformationClass].Implemented) - { - return STATUS_NOT_IMPLEMENTED; - } - if (ThreadInformationClass > MaxThreadInfoClass || - SetInformationData[ThreadInformationClass].Size == 0) - { - return STATUS_INVALID_INFO_CLASS; - } - if (ThreadInformationLength != SetInformationData[ThreadInformationClass].Size) - { - return STATUS_INFO_LENGTH_MISMATCH; - } - - Status = ObReferenceObjectByHandle (ThreadHandle, - THREAD_SET_INFORMATION, - PsThreadType, - ExGetPreviousMode (), - (PVOID*)&Thread, - NULL); - if (!NT_SUCCESS(Status)) - { - return Status; - } - - Status = MmCopyFromCaller(&u.Priority, - ThreadInformation, - SetInformationData[ThreadInformationClass].Size); - if (NT_SUCCESS(Status)) - { - switch (ThreadInformationClass) - { - case ThreadPriority: - if (u.Priority < LOW_PRIORITY || u.Priority >= MAXIMUM_PRIORITY) - { - Status = STATUS_INVALID_PARAMETER; - break; - } - KeSetPriorityThread(&Thread->Tcb, u.Priority); - break; - - case ThreadBasePriority: - KeSetBasePriorityThread (&Thread->Tcb, u.Increment); - break; - - case ThreadAffinityMask: - Status = KeSetAffinityThread(&Thread->Tcb, u.Affinity); - break; - - case ThreadImpersonationToken: - Status = PsAssignImpersonationToken (Thread, u.Handle); - break; - -#ifdef _ENABLE_THRDEVTPAIR - case ThreadEventPair: - { - PKEVENT_PAIR EventPair; - - Status = ObReferenceObjectByHandle(u.Handle, - STANDARD_RIGHTS_ALL, - ExEventPairObjectType, - ExGetPreviousMode(), - (PVOID*)&EventPair, - NULL); - if (NT_SUCCESS(Status)) - { - ExpSwapThreadEventPair(Thread, EventPair); /* Note that the extra reference is kept intentionally */ - } - break; - } -#endif /* _ENABLE_THRDEVTPAIR */ - - case ThreadQuerySetWin32StartAddress: - Thread->Win32StartAddress = u.Address; - break; - - default: - /* Shoult never occure if the data table is correct */ - KEBUGCHECK(0); - } - } - ObDereferenceObject (Thread); - - return Status; -} - -/* - * @implemented - */ -NTSTATUS STDCALL -NtQueryInformationThread (IN HANDLE ThreadHandle, - IN THREADINFOCLASS ThreadInformationClass, - OUT PVOID ThreadInformation, - IN ULONG ThreadInformationLength, - OUT PULONG ReturnLength OPTIONAL) -{ - PETHREAD Thread; - NTSTATUS Status; - union - { - THREAD_BASIC_INFORMATION TBI; - KERNEL_USER_TIMES TTI; - PVOID Address; - LARGE_INTEGER Count; - BOOLEAN Last; - }u; - - PAGED_CODE(); - - if (ThreadInformationClass <= MaxThreadInfoClass && - !QueryInformationData[ThreadInformationClass].Implemented) - { - return STATUS_NOT_IMPLEMENTED; - } - if (ThreadInformationClass > MaxThreadInfoClass || - QueryInformationData[ThreadInformationClass].Size == 0) - { - return STATUS_INVALID_INFO_CLASS; - } - if (ThreadInformationLength != QueryInformationData[ThreadInformationClass].Size) - { - return STATUS_INFO_LENGTH_MISMATCH; - } - - Status = ObReferenceObjectByHandle(ThreadHandle, - THREAD_QUERY_INFORMATION, - PsThreadType, - ExGetPreviousMode(), - (PVOID*)&Thread, - NULL); - if (!NT_SUCCESS(Status)) - { - return Status; - } - - switch (ThreadInformationClass) - { - case ThreadBasicInformation: - /* A test on W2K agains ntdll shows NtQueryInformationThread return STATUS_PENDING - * as ExitStatus for current/running thread, while KETHREAD's ExitStatus is - * 0. So do the conversion here: - * -Gunnar */ - u.TBI.ExitStatus = (Thread->ExitStatus == 0) ? STATUS_PENDING : Thread->ExitStatus; - u.TBI.TebBaseAddress = Thread->Tcb.Teb; - u.TBI.ClientId = Thread->Cid; - u.TBI.AffinityMask = Thread->Tcb.Affinity; - u.TBI.Priority = Thread->Tcb.Priority; - u.TBI.BasePriority = Thread->Tcb.BasePriority; - break; - - case ThreadTimes: - u.TTI.KernelTime.QuadPart = Thread->Tcb.KernelTime * 100000LL; - u.TTI.UserTime.QuadPart = Thread->Tcb.UserTime * 100000LL; - u.TTI.CreateTime = Thread->CreateTime; - /*This works*/ - u.TTI.ExitTime = Thread->ExitTime; - break; - - case ThreadQuerySetWin32StartAddress: - u.Address = Thread->Win32StartAddress; - break; - - case ThreadPerformanceCount: - /* Nebbett says this class is always zero */ - u.Count.QuadPart = 0; - break; - - case ThreadAmILastThread: - if (Thread->ThreadsProcess->ThreadListHead.Flink->Flink == - &Thread->ThreadsProcess->ThreadListHead) - { - u.Last = TRUE; - } - else - { - u.Last = FALSE; - } - break; - default: - /* Shoult never occure if the data table is correct */ - KEBUGCHECK(0); - } - if (QueryInformationData[ThreadInformationClass].Size) - { - Status = MmCopyToCaller(ThreadInformation, - &u.TBI, - QueryInformationData[ThreadInformationClass].Size); - } - if (ReturnLength) - { - NTSTATUS Status2; - static ULONG Null = 0; - Status2 = MmCopyToCaller(ReturnLength, - NT_SUCCESS(Status) ? &QueryInformationData[ThreadInformationClass].Size : &Null, - sizeof(ULONG)); - if (NT_SUCCESS(Status)) - { - Status = Status2; - } - } - - ObDereferenceObject(Thread); - return(Status); -} - - -VOID -KeSetPreviousMode (ULONG Mode) -{ - PsGetCurrentThread()->Tcb.PreviousMode = (UCHAR)Mode; -} - - -/* - * @implemented - */ -KPROCESSOR_MODE STDCALL -KeGetPreviousMode (VOID) -{ - return (ULONG)PsGetCurrentThread()->Tcb.PreviousMode; -} - - -/* - * @implemented - */ -KPROCESSOR_MODE STDCALL -ExGetPreviousMode (VOID) -{ - return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode; -} - -/* EOF */ diff --git a/reactos/ntoskrnl/ps/win32.c b/reactos/ntoskrnl/ps/win32.c index 6e9ef4d07ba..29b31923060 100644 --- a/reactos/ntoskrnl/ps/win32.c +++ b/reactos/ntoskrnl/ps/win32.c @@ -11,8 +11,8 @@ /* INCLUDES ****************************************************************/ #include - -/* TYPES *******************************************************************/ +#define NDEBUG +#include /* GLOBALS ******************************************************************/ @@ -21,6 +21,28 @@ static PW32_THREAD_CALLBACK PspWin32ThreadCallback = NULL; static ULONG PspWin32ProcessSize = 0; static ULONG PspWin32ThreadSize = 0; +extern OBJECT_CREATE_ROUTINE ExpWindowStationObjectCreate; +extern OBJECT_PARSE_ROUTINE ExpWindowStationObjectParse; +extern OBJECT_DELETE_ROUTINE ExpWindowStationObjectDelete; +extern OBJECT_FIND_ROUTINE ExpWindowStationObjectFind; +extern OBJECT_CREATE_ROUTINE ExpDesktopObjectCreate; +extern OBJECT_DELETE_ROUTINE ExpDesktopObjectDelete; + +#ifndef ALEX_CB_REWRITE +typedef struct _NTW32CALL_SAVED_STATE +{ + ULONG_PTR SavedStackLimit; + PVOID SavedStackBase; + PVOID SavedInitialStack; + PVOID CallerResult; + PULONG CallerResultLength; + PNTSTATUS CallbackStatus; + PKTRAP_FRAME SavedTrapFrame; + PVOID SavedCallbackStack; + PVOID SavedExceptionStack; +} NTW32CALL_SAVED_STATE, *PNTW32CALL_SAVED_STATE; +#endif + /* FUNCTIONS ***************************************************************/ PW32THREAD STDCALL @@ -59,7 +81,7 @@ PsCreateWin32Process(PEPROCESS Process) VOID STDCALL PsEstablishWin32Callouts (PW32_PROCESS_CALLBACK W32ProcessCallback, PW32_THREAD_CALLBACK W32ThreadCallback, - PVOID Param3, + PW32_OBJECT_CALLBACK W32ObjectCallback, PVOID Param4, ULONG W32ThreadSize, ULONG W32ProcessSize) @@ -69,9 +91,15 @@ PsEstablishWin32Callouts (PW32_PROCESS_CALLBACK W32ProcessCallback, PspWin32ProcessSize = W32ProcessSize; PspWin32ThreadSize = W32ThreadSize; + + ExpWindowStationObjectCreate = W32ObjectCallback->WinStaCreate; + ExpWindowStationObjectParse = W32ObjectCallback->WinStaParse; + ExpWindowStationObjectDelete = W32ObjectCallback->WinStaDelete; + ExpWindowStationObjectFind = W32ObjectCallback->WinStaFind; + ExpDesktopObjectCreate = W32ObjectCallback->DesktopCreate; + ExpDesktopObjectDelete = W32ObjectCallback->DesktopDelete; } - NTSTATUS PsInitWin32Thread (PETHREAD Thread) { @@ -149,4 +177,182 @@ PsTerminateWin32Thread (PETHREAD Thread) } } +VOID +STDCALL +DumpEspData(ULONG Esp, ULONG ThLimit, ULONG ThStack, ULONG PcrLimit, ULONG PcrStack, ULONG Esp0) +{ + DPRINT1("Current Esp: %p\n Thread Stack Limit: %p\n Thread Stack: %p\n Pcr Limit: %p, Pcr Stack: %p\n Esp0 :%p\n",Esp, ThLimit, ThStack, PcrLimit, PcrStack, Esp0) ; +} + + PVOID +STDCALL + PsAllocateCallbackStack(ULONG StackSize) + { + PVOID KernelStack = NULL; + NTSTATUS Status; + PMEMORY_AREA StackArea; + ULONG i, j; + PHYSICAL_ADDRESS BoundaryAddressMultiple; + PPFN_TYPE Pages = alloca(sizeof(PFN_TYPE) * (StackSize /PAGE_SIZE)); + + DPRINT1("PsAllocateCallbackStack\n"); + BoundaryAddressMultiple.QuadPart = 0; + StackSize = PAGE_ROUND_UP(StackSize); + MmLockAddressSpace(MmGetKernelAddressSpace()); + Status = MmCreateMemoryArea(NULL, + MmGetKernelAddressSpace(), + MEMORY_AREA_KERNEL_STACK, + &KernelStack, + StackSize, + 0, + &StackArea, + FALSE, + FALSE, + BoundaryAddressMultiple); + MmUnlockAddressSpace(MmGetKernelAddressSpace()); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create thread stack\n"); + return(NULL); + } + for (i = 0; i < (StackSize / PAGE_SIZE); i++) + { + Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Pages[i]); + if (!NT_SUCCESS(Status)) + { + for (j = 0; j < i; j++) + { + MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[j]); + } + return(NULL); + } + } + Status = MmCreateVirtualMapping(NULL, + KernelStack, + PAGE_READWRITE, + Pages, + StackSize / PAGE_SIZE); + if (!NT_SUCCESS(Status)) + { + for (i = 0; i < (StackSize / PAGE_SIZE); i++) + { + MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[i]); + } + return(NULL); + } + DPRINT1("PsAllocateCallbackStack %x\n", KernelStack); + return(KernelStack); +} + +NTSTATUS +STDCALL +NtW32Call(IN ULONG RoutineIndex, + IN PVOID Argument, + IN ULONG ArgumentLength, + OUT PVOID* Result OPTIONAL, + OUT PULONG ResultLength OPTIONAL) +{ + NTSTATUS CallbackStatus; + + DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n", + RoutineIndex, Argument, ArgumentLength); + + /* FIXME: SEH!!! */ + + /* Call kernel function */ + CallbackStatus = KeUserModeCallback(RoutineIndex, + Argument, + ArgumentLength, + Result, + ResultLength); + + /* Return the result */ + return(CallbackStatus); +} + +#ifndef ALEX_CB_REWRITE +NTSTATUS STDCALL +NtCallbackReturn (PVOID Result, + ULONG ResultLength, + NTSTATUS Status) +{ + PULONG OldStack; + PETHREAD Thread; + PNTSTATUS CallbackStatus; + PULONG CallerResultLength; + PVOID* CallerResult; + PVOID InitialStack; + PVOID StackBase; + ULONG_PTR StackLimit; + KIRQL oldIrql; + PNTW32CALL_SAVED_STATE State; + PKTRAP_FRAME SavedTrapFrame; + PVOID SavedCallbackStack; + PVOID SavedExceptionStack; + + PAGED_CODE(); + + Thread = PsGetCurrentThread(); + if (Thread->Tcb.CallbackStack == NULL) + { + return(STATUS_NO_CALLBACK_ACTIVE); + } + + OldStack = (PULONG)Thread->Tcb.CallbackStack; + + /* + * Get the values that NtW32Call left on the inactive stack for us. + */ + State = (PNTW32CALL_SAVED_STATE)OldStack[0]; + CallbackStatus = State->CallbackStatus; + CallerResultLength = State->CallerResultLength; + CallerResult = State->CallerResult; + InitialStack = State->SavedInitialStack; + StackBase = State->SavedStackBase; + StackLimit = State->SavedStackLimit; + SavedTrapFrame = State->SavedTrapFrame; + SavedCallbackStack = State->SavedCallbackStack; + SavedExceptionStack = State->SavedExceptionStack; + + /* + * Copy the callback status and the callback result to NtW32Call + */ + *CallbackStatus = Status; + if (CallerResult != NULL && CallerResultLength != NULL) + { + if (Result == NULL) + { + *CallerResultLength = 0; + } + else + { + *CallerResultLength = min(ResultLength, *CallerResultLength); + RtlCopyMemory(*CallerResult, Result, *CallerResultLength); + } + } + + /* + * Restore the old stack. + */ + KeRaiseIrql(HIGH_LEVEL, &oldIrql); + if ((Thread->Tcb.NpxState & NPX_STATE_VALID) && + ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentPrcb()->NpxThread) + { + RtlCopyMemory((char*)InitialStack - sizeof(FX_SAVE_AREA), + (char*)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA), + sizeof(FX_SAVE_AREA)); + } + Thread->Tcb.InitialStack = InitialStack; + Thread->Tcb.StackBase = StackBase; + Thread->Tcb.StackLimit = StackLimit; + Thread->Tcb.TrapFrame = SavedTrapFrame; + Thread->Tcb.CallbackStack = SavedCallbackStack; + KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)SavedExceptionStack; + KeStackSwitchAndRet((PVOID)(OldStack + 1)); + + /* Should never return. */ + KEBUGCHECK(0); + return(STATUS_UNSUCCESSFUL); +#endif +} /* EOF */ diff --git a/reactos/ntoskrnl/rtl/libsupp.c b/reactos/ntoskrnl/rtl/libsupp.c index bfc592a491b..900ba741139 100644 --- a/reactos/ntoskrnl/rtl/libsupp.c +++ b/reactos/ntoskrnl/rtl/libsupp.c @@ -20,6 +20,13 @@ /* FUNCTIONS *****************************************************************/ + +KPROCESSOR_MODE +RtlpGetMode() +{ + return KernelMode; +} + /* * @implemented */ @@ -38,11 +45,12 @@ RtlReleasePebLock(VOID) } + PPEB STDCALL RtlpCurrentPeb(VOID) { - return ((PEPROCESS)(KeGetCurrentThread()->ApcState.Process))->Peb; + return ((PEPROCESS)(KeGetCurrentThread()->ApcState.Process))->Peb; } NTSTATUS diff --git a/reactos/ntoskrnl/rtl/nls.c b/reactos/ntoskrnl/rtl/nls.c index c56345c0f61..82e6729f645 100644 --- a/reactos/ntoskrnl/rtl/nls.c +++ b/reactos/ntoskrnl/rtl/nls.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/rtl/nls.c @@ -36,7 +35,32 @@ ULONG NlsUnicodeTableOffset = 0; /* FUNCTIONS *****************************************************************/ - +VOID +INIT_FUNCTION +STDCALL +RtlpInitNls(VOID) +{ + ULONG_PTR BaseAddress; + + /* Import NLS Data */ + BaseAddress = CachedModules[AnsiCodepage]->ModStart; + RtlpImportAnsiCodePage((PUSHORT)BaseAddress, + CachedModules[AnsiCodepage]->ModEnd - BaseAddress); + + BaseAddress = CachedModules[OemCodepage]->ModStart; + RtlpImportOemCodePage((PUSHORT)BaseAddress, + CachedModules[OemCodepage]->ModEnd - BaseAddress); + + BaseAddress = CachedModules[UnicodeCasemap]->ModStart; + RtlpImportUnicodeCasemap((PUSHORT)BaseAddress, + CachedModules[UnicodeCasemap]->ModEnd - BaseAddress); + + /* Create initial NLS tables */ + RtlpCreateInitialNlsTables(); + + /* Create the NLS section */ + RtlpCreateNlsSection(); +} VOID INIT_FUNCTION RtlpImportAnsiCodePage(PUSHORT TableBase, diff --git a/reactos/ntoskrnl/se/access.c b/reactos/ntoskrnl/se/access.c index 1e49fce5040..62687dec6f3 100644 --- a/reactos/ntoskrnl/se/access.c +++ b/reactos/ntoskrnl/se/access.c @@ -72,7 +72,7 @@ RtlMapGenericMask ( if (*AccessMask & GENERIC_ALL) *AccessMask |= GenericMapping->GenericAll; - *AccessMask &= 0x0FFFFFFF; + *AccessMask &= ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); } /* diff --git a/reactos/ntoskrnl/se/acl.c b/reactos/ntoskrnl/se/acl.c index 8f1bbd5b2e0..2d2e50feb46 100644 --- a/reactos/ntoskrnl/se/acl.c +++ b/reactos/ntoskrnl/se/acl.c @@ -263,4 +263,105 @@ SepCreateImpersonationTokenDacl(PTOKEN Token, return STATUS_SUCCESS; } +NTSTATUS +SepCaptureAcl(IN PACL InputAcl, + IN KPROCESSOR_MODE AccessMode, + IN POOL_TYPE PoolType, + IN BOOLEAN CaptureIfKernel, + OUT PACL *CapturedAcl) +{ + PACL NewAcl; + ULONG AclSize = 0; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + if(AccessMode != KernelMode) + { + _SEH_TRY + { + ProbeForRead(InputAcl, + sizeof(ACL), + sizeof(ULONG)); + AclSize = InputAcl->AclSize; + ProbeForRead(InputAcl, + AclSize, + sizeof(ULONG)); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + NewAcl = ExAllocatePool(PoolType, + AclSize); + if(NewAcl != NULL) + { + _SEH_TRY + { + RtlCopyMemory(NewAcl, + InputAcl, + AclSize); + + *CapturedAcl = NewAcl; + } + _SEH_HANDLE + { + ExFreePool(NewAcl); + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + else if(!CaptureIfKernel) + { + *CapturedAcl = InputAcl; + } + else + { + AclSize = InputAcl->AclSize; + + NewAcl = ExAllocatePool(PoolType, + AclSize); + + if(NewAcl != NULL) + { + RtlCopyMemory(NewAcl, + InputAcl, + AclSize); + + *CapturedAcl = NewAcl; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + return Status; +} + +VOID +SepReleaseAcl(IN PACL CapturedAcl, + IN KPROCESSOR_MODE AccessMode, + IN BOOLEAN CaptureIfKernel) +{ + PAGED_CODE(); + + if(CapturedAcl != NULL && + (AccessMode == UserMode || + (AccessMode == KernelMode && CaptureIfKernel))) + { + ExFreePool(CapturedAcl); + } +} + /* EOF */ diff --git a/reactos/ntoskrnl/se/luid.c b/reactos/ntoskrnl/se/luid.c index 1b5d653caff..a7c516e6bf7 100644 --- a/reactos/ntoskrnl/se/luid.c +++ b/reactos/ntoskrnl/se/luid.c @@ -102,32 +102,4 @@ NtAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId) return Status; } - -/* - * @implemented - */ -VOID STDCALL -RtlCopyLuid(IN PLUID LuidDest, - IN PLUID LuidSrc) -{ - PAGED_CODE_RTL(); - - LuidDest->LowPart = LuidSrc->LowPart; - LuidDest->HighPart = LuidSrc->HighPart; -} - - -/* - * @implemented - */ -BOOLEAN STDCALL -RtlEqualLuid(IN PLUID Luid1, - IN PLUID Luid2) -{ - PAGED_CODE_RTL(); - - return (Luid1->LowPart == Luid2->LowPart && - Luid1->HighPart == Luid2->HighPart); -} - /* EOF */ diff --git a/reactos/ntoskrnl/se/sd.c b/reactos/ntoskrnl/se/sd.c index 47ec5ba23fa..3e12f8062ec 100644 --- a/reactos/ntoskrnl/se/sd.c +++ b/reactos/ntoskrnl/se/sd.c @@ -108,6 +108,174 @@ SepInitSDs(VOID) return TRUE; } + +NTSTATUS +SepCaptureSecurityQualityOfService(IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN KPROCESSOR_MODE AccessMode, + IN POOL_TYPE PoolType, + IN BOOLEAN CaptureIfKernel, + OUT PSECURITY_QUALITY_OF_SERVICE *CapturedSecurityQualityOfService, + OUT PBOOLEAN Present) +{ + PSECURITY_QUALITY_OF_SERVICE CapturedQos; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + ASSERT(CapturedSecurityQualityOfService); + ASSERT(Present); + + if(ObjectAttributes != NULL) + { + if(AccessMode != KernelMode) + { + SECURITY_QUALITY_OF_SERVICE SafeQos; + + _SEH_TRY + { + ProbeForRead(ObjectAttributes, + sizeof(ObjectAttributes), + sizeof(ULONG)); + if(ObjectAttributes->Length == sizeof(OBJECT_ATTRIBUTES)) + { + if(ObjectAttributes->SecurityQualityOfService != NULL) + { + ProbeForRead(ObjectAttributes->SecurityQualityOfService, + sizeof(SECURITY_QUALITY_OF_SERVICE), + sizeof(ULONG)); + + if(((PSECURITY_QUALITY_OF_SERVICE)ObjectAttributes->SecurityQualityOfService)->Length == + sizeof(SECURITY_QUALITY_OF_SERVICE)) + { + /* don't allocate memory here because ExAllocate should bugcheck + the system if it's buggy, SEH would catch that! So make a local + copy of the qos structure.*/ + RtlCopyMemory(&SafeQos, + ObjectAttributes->SecurityQualityOfService, + sizeof(SECURITY_QUALITY_OF_SERVICE)); + *Present = TRUE; + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + else + { + *CapturedSecurityQualityOfService = NULL; + *Present = FALSE; + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + if(*Present) + { + CapturedQos = ExAllocatePool(PoolType, + sizeof(SECURITY_QUALITY_OF_SERVICE)); + if(CapturedQos != NULL) + { + RtlCopyMemory(CapturedQos, + &SafeQos, + sizeof(SECURITY_QUALITY_OF_SERVICE)); + *CapturedSecurityQualityOfService = CapturedQos; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + *CapturedSecurityQualityOfService = NULL; + } + } + } + else + { + if(ObjectAttributes->Length == sizeof(OBJECT_ATTRIBUTES)) + { + if(CaptureIfKernel) + { + if(ObjectAttributes->SecurityQualityOfService != NULL) + { + if(((PSECURITY_QUALITY_OF_SERVICE)ObjectAttributes->SecurityQualityOfService)->Length == + sizeof(SECURITY_QUALITY_OF_SERVICE)) + { + CapturedQos = ExAllocatePool(PoolType, + sizeof(SECURITY_QUALITY_OF_SERVICE)); + if(CapturedQos != NULL) + { + RtlCopyMemory(CapturedQos, + ObjectAttributes->SecurityQualityOfService, + sizeof(SECURITY_QUALITY_OF_SERVICE)); + *CapturedSecurityQualityOfService = CapturedQos; + *Present = TRUE; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + else + { + *CapturedSecurityQualityOfService = NULL; + *Present = FALSE; + } + } + else + { + *CapturedSecurityQualityOfService = (PSECURITY_QUALITY_OF_SERVICE)ObjectAttributes->SecurityQualityOfService; + *Present = (ObjectAttributes->SecurityQualityOfService != NULL); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + else + { + *CapturedSecurityQualityOfService = NULL; + *Present = FALSE; + } + + return Status; +} + + +VOID +SepReleaseSecurityQualityOfService(IN PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService OPTIONAL, + IN KPROCESSOR_MODE AccessMode, + IN BOOLEAN CaptureIfKernel) +{ + PAGED_CODE(); + + if(CapturedSecurityQualityOfService != NULL && + (AccessMode == UserMode || + (AccessMode == KernelMode && CaptureIfKernel))) + { + ExFreePool(CapturedSecurityQualityOfService); + } +} + + /* * @implemented */ @@ -572,6 +740,8 @@ SeReleaseSecurityDescriptor( IN BOOLEAN CaptureIfKernelMode ) { + PAGED_CODE(); + /* WARNING! You need to call this function with the same value for CurrentMode and CaptureIfKernelMode that you previously passed to SeCaptureSecurityDescriptor() in order to avoid memory leaks! */ diff --git a/reactos/ntoskrnl/se/sid.c b/reactos/ntoskrnl/se/sid.c index d925ee3df63..019072a4d15 100644 --- a/reactos/ntoskrnl/se/sid.c +++ b/reactos/ntoskrnl/se/sid.c @@ -466,4 +466,107 @@ SepInitSecurityIDs(VOID) return(TRUE); } +NTSTATUS +SepCaptureSid(IN PSID InputSid, + IN KPROCESSOR_MODE AccessMode, + IN POOL_TYPE PoolType, + IN BOOLEAN CaptureIfKernel, + OUT PSID *CapturedSid) +{ + ULONG SidSize = 0; + PISID NewSid, Sid = (PISID)InputSid; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + if(AccessMode != KernelMode) + { + _SEH_TRY + { + ProbeForRead(Sid, + sizeof(*Sid) - sizeof(Sid->SubAuthority), + sizeof(UCHAR)); + SidSize = RtlLengthRequiredSid(Sid->SubAuthorityCount); + ProbeForRead(Sid, + SidSize, + sizeof(UCHAR)); + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + /* allocate a SID and copy it */ + NewSid = ExAllocatePool(PoolType, + SidSize); + if(NewSid != NULL) + { + _SEH_TRY + { + RtlCopyMemory(NewSid, + Sid, + SidSize); + + *CapturedSid = NewSid; + } + _SEH_HANDLE + { + ExFreePool(NewSid); + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + else if(!CaptureIfKernel) + { + *CapturedSid = InputSid; + return STATUS_SUCCESS; + } + else + { + SidSize = RtlLengthRequiredSid(Sid->SubAuthorityCount); + + /* allocate a SID and copy it */ + NewSid = ExAllocatePool(PoolType, + SidSize); + if(NewSid != NULL) + { + RtlCopyMemory(NewSid, + Sid, + SidSize); + + *CapturedSid = NewSid; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + return Status; +} + +VOID +SepReleaseSid(IN PSID CapturedSid, + IN KPROCESSOR_MODE AccessMode, + IN BOOLEAN CaptureIfKernel) +{ + PAGED_CODE(); + + if(CapturedSid != NULL && + (AccessMode == UserMode || + (AccessMode == KernelMode && CaptureIfKernel))) + { + ExFreePool(CapturedSid); + } +} + /* EOF */ diff --git a/reactos/ntoskrnl/se/token.c b/reactos/ntoskrnl/se/token.c index 28605d6ff23..7ff0e25a75a 100644 --- a/reactos/ntoskrnl/se/token.c +++ b/reactos/ntoskrnl/se/token.c @@ -18,12 +18,54 @@ /* GLOBALS *******************************************************************/ POBJECT_TYPE SepTokenObjectType = NULL; +ERESOURCE SepTokenLock; static GENERIC_MAPPING SepTokenMapping = {TOKEN_READ, TOKEN_WRITE, TOKEN_EXECUTE, TOKEN_ALL_ACCESS}; +static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = { + + /* Class 0 not used, blame M$! */ + ICI_SQ_SAME( 0, 0, 0), + + /* TokenUser */ + ICI_SQ_SAME( sizeof(TOKEN_USER), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), + /* TokenGroups */ + ICI_SQ_SAME( sizeof(TOKEN_GROUPS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), + /* TokenPrivileges */ + ICI_SQ_SAME( sizeof(TOKEN_PRIVILEGES), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), + /* TokenOwner */ + ICI_SQ_SAME( sizeof(TOKEN_OWNER), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), + /* TokenPrimaryGroup */ + ICI_SQ_SAME( sizeof(TOKEN_PRIMARY_GROUP), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), + /* TokenDefaultDacl */ + ICI_SQ_SAME( sizeof(TOKEN_DEFAULT_DACL), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), + /* TokenSource */ + ICI_SQ_SAME( sizeof(TOKEN_SOURCE), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), + /* TokenType */ + ICI_SQ_SAME( sizeof(TOKEN_TYPE), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), + /* TokenImpersonationLevel */ + ICI_SQ_SAME( sizeof(SECURITY_IMPERSONATION_LEVEL), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), + /* TokenStatistics */ + ICI_SQ_SAME( sizeof(TOKEN_STATISTICS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), + /* TokenRestrictedSids */ + ICI_SQ_SAME( sizeof(TOKEN_GROUPS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), + /* TokenSessionId */ + ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), + /* TokenGroupsAndPrivileges */ + ICI_SQ_SAME( sizeof(TOKEN_GROUPS_AND_PRIVILEGES), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), + /* TokenSessionReference */ + ICI_SQ_SAME( /* FIXME */0, sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), + /* TokenSandBoxInert */ + ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), + /* TokenAuditPolicy */ + ICI_SQ_SAME( /* FIXME */0, sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), + /* TokenOrigin */ + ICI_SQ_SAME( sizeof(TOKEN_ORIGIN), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), +}; + /* FUNCTIONS *****************************************************************/ VOID SepFreeProxyData(PVOID ProxyData) @@ -140,6 +182,8 @@ SepDuplicateToken(PTOKEN Token, PVOID EndMem; PTOKEN AccessToken; NTSTATUS Status; + + PAGED_CODE(); Status = ObCreateObject(PreviousMode, SepTokenObjectType, @@ -170,6 +214,8 @@ SepDuplicateToken(PTOKEN Token, return(Status); } + AccessToken->TokenLock = &SepTokenLock; + AccessToken->TokenInUse = 0; AccessToken->TokenType = TokenType; AccessToken->ImpersonationLevel = Level; @@ -189,7 +235,7 @@ SepDuplicateToken(PTOKEN Token, uLength += RtlLengthSid(Token->UserAndGroups[i].Sid); AccessToken->UserAndGroups = - (PSID_AND_ATTRIBUTES)ExAllocatePoolWithTag(NonPagedPool, + (PSID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PagedPool, uLength, TAG('T', 'O', 'K', 'u')); @@ -216,7 +262,7 @@ SepDuplicateToken(PTOKEN Token, uLength = AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); AccessToken->Privileges = - (PLUID_AND_ATTRIBUTES)ExAllocatePoolWithTag(NonPagedPool, + (PLUID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PagedPool, uLength, TAG('T', 'O', 'K', 'p')); @@ -231,7 +277,7 @@ SepDuplicateToken(PTOKEN Token, if ( Token->DefaultDacl ) { AccessToken->DefaultDacl = - (PACL) ExAllocatePoolWithTag(NonPagedPool, + (PACL) ExAllocatePoolWithTag(PagedPool, Token->DefaultDacl->AclSize, TAG('T', 'O', 'K', 'd')); memcpy(AccessToken->DefaultDacl, @@ -534,6 +580,8 @@ SepDeleteToken(PVOID ObjectBody) VOID INIT_FUNCTION SepInitializeTokenImplementation(VOID) { + ExInitializeResource(&SepTokenLock); + SepTokenObjectType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE)); SepTokenObjectType->Tag = TAG('T', 'O', 'K', 'T'); @@ -555,8 +603,7 @@ SepInitializeTokenImplementation(VOID) SepTokenObjectType->Create = NULL; SepTokenObjectType->DuplicationNotify = NULL; - RtlpCreateUnicodeString(&SepTokenObjectType->TypeName, - L"Token", NonPagedPool); + RtlInitUnicodeString(&SepTokenObjectType->TypeName, L"Token"); ObpCreateTypeObject (SepTokenObjectType); } @@ -571,265 +618,455 @@ NtQueryInformationToken(IN HANDLE TokenHandle, IN ULONG TokenInformationLength, OUT PULONG ReturnLength) { - NTSTATUS Status, LengthStatus; - PVOID UnusedInfo; - PVOID EndMem; + union + { + PVOID Ptr; + ULONG Ulong; + } Unused; PTOKEN Token; - ULONG Length; - PTOKEN_GROUPS PtrTokenGroups; - PTOKEN_DEFAULT_DACL PtrDefaultDacl; - PTOKEN_STATISTICS PtrTokenStatistics; + ULONG RequiredLength; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + /* Check buffers and class validity */ + DefaultQueryInfoBufferCheck(TokenInformationClass, + SeTokenInformationClass, + TokenInformation, + TokenInformationLength, + ReturnLength, + PreviousMode, + &Status); + + if(!NT_SUCCESS(Status)) + { + DPRINT("NtQueryInformationToken() failed, Status: 0x%x\n", Status); + return Status; + } Status = ObReferenceObjectByHandle(TokenHandle, (TokenInformationClass == TokenSource) ? TOKEN_QUERY_SOURCE : TOKEN_QUERY, SepTokenObjectType, - UserMode, + PreviousMode, (PVOID*)&Token, NULL); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - - switch (TokenInformationClass) + if (NT_SUCCESS(Status)) + { + switch (TokenInformationClass) { case TokenUser: - DPRINT("NtQueryInformationToken(TokenUser)\n"); - Length = RtlLengthSidAndAttributes(1, Token->UserAndGroups); - if (TokenInformationLength < Length) - { - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - Status = RtlCopySidAndAttributesArray(1, - Token->UserAndGroups, - TokenInformationLength, - TokenInformation, - (char*)TokenInformation + 8, - &UnusedInfo, - &Length); - if (NT_SUCCESS(Status)) - { - Length = TokenInformationLength - Length; - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - } - } - break; + { + PTOKEN_USER tu = (PTOKEN_USER)TokenInformation; + + DPRINT("NtQueryInformationToken(TokenUser)\n"); + RequiredLength = sizeof(TOKEN_USER) + + RtlLengthSid(Token->UserAndGroups[0].Sid); + + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + Status = RtlCopySidAndAttributesArray(1, + &Token->UserAndGroups[0], + RequiredLength - sizeof(TOKEN_USER), + &tu->User, + (PSID)(tu + 1), + &Unused.Ptr, + &Unused.Ulong); + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } case TokenGroups: - DPRINT("NtQueryInformationToken(TokenGroups)\n"); - Length = RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]) + sizeof(ULONG); - if (TokenInformationLength < Length) - { - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - EndMem = (char*)TokenInformation + Token->UserAndGroupCount * sizeof(SID_AND_ATTRIBUTES); - PtrTokenGroups = (PTOKEN_GROUPS)TokenInformation; - PtrTokenGroups->GroupCount = Token->UserAndGroupCount - 1; - Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1, - &Token->UserAndGroups[1], - TokenInformationLength, - PtrTokenGroups->Groups, - EndMem, - &UnusedInfo, - &Length); - if (NT_SUCCESS(Status)) - { - Length = TokenInformationLength - Length; - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - } - } - break; + { + PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation; + + DPRINT("NtQueryInformationToken(TokenGroups)\n"); + RequiredLength = sizeof(tg->GroupCount) + + RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]); + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) - + ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)); + PSID_AND_ATTRIBUTES Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)TokenInformation + sizeof(tg->GroupCount) + + ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES))); + + tg->GroupCount = Token->UserAndGroupCount - 1; + Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1, + &Token->UserAndGroups[1], + SidLen, + &tg->Groups[0], + (PSID)Sid, + &Unused.Ptr, + &Unused.Ulong); + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + case TokenPrivileges: - DPRINT("NtQueryInformationToken(TokenPrivileges)\n"); - Length = sizeof(ULONG) + Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); - if (TokenInformationLength < Length) - { - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - ULONG i; - TOKEN_PRIVILEGES* pPriv = (TOKEN_PRIVILEGES*)TokenInformation; - - pPriv->PrivilegeCount = Token->PrivilegeCount; - for (i = 0; i < Token->PrivilegeCount; i++) - { - RtlCopyLuid(&pPriv->Privileges[i].Luid, &Token->Privileges[i].Luid); - pPriv->Privileges[i].Attributes = Token->Privileges[i].Attributes; - } - Status = STATUS_SUCCESS; - } - break; + { + PTOKEN_PRIVILEGES tp = (PTOKEN_PRIVILEGES)TokenInformation; + + DPRINT("NtQueryInformationToken(TokenPrivileges)\n"); + RequiredLength = sizeof(tp->PrivilegeCount) + + (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + tp->PrivilegeCount = Token->PrivilegeCount; + RtlCopyLuidAndAttributesArray(Token->PrivilegeCount, + Token->Privileges, + &tp->Privileges[0]); + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + case TokenOwner: - DPRINT("NtQueryInformationToken(TokenOwner)\n"); - Length = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid) + sizeof(TOKEN_OWNER); - if (TokenInformationLength < Length) - { - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - ((PTOKEN_OWNER)TokenInformation)->Owner = - (PSID)(((PTOKEN_OWNER)TokenInformation) + 1); - RtlCopySid(TokenInformationLength - sizeof(TOKEN_OWNER), - ((PTOKEN_OWNER)TokenInformation)->Owner, - Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); - Status = STATUS_SUCCESS; - } - break; + { + ULONG SidLen; + PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation; + + DPRINT("NtQueryInformationToken(TokenOwner)\n"); + SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); + RequiredLength = sizeof(TOKEN_OWNER) + SidLen; + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + to->Owner = (PSID)(to + 1); + Status = RtlCopySid(SidLen, + to->Owner, + Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + case TokenPrimaryGroup: - DPRINT("NtQueryInformationToken(TokenPrimaryGroup)," - "Token->PrimaryGroup = 0x%08x\n", Token->PrimaryGroup); - Length = RtlLengthSid(Token->PrimaryGroup) + sizeof(TOKEN_PRIMARY_GROUP); - if (TokenInformationLength < Length) - { - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - ((PTOKEN_PRIMARY_GROUP)TokenInformation)->PrimaryGroup = - (PSID)(((PTOKEN_PRIMARY_GROUP)TokenInformation) + 1); - RtlCopySid(TokenInformationLength - sizeof(TOKEN_PRIMARY_GROUP), - ((PTOKEN_PRIMARY_GROUP)TokenInformation)->PrimaryGroup, - Token->PrimaryGroup); - Status = STATUS_SUCCESS; - } - break; + { + ULONG SidLen; + PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation; + DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n"); + SidLen = RtlLengthSid(Token->PrimaryGroup); + RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen; + + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + tpg->PrimaryGroup = (PSID)(tpg + 1); + Status = RtlCopySid(SidLen, + tpg->PrimaryGroup, + Token->PrimaryGroup); + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + case TokenDefaultDacl: - DPRINT("NtQueryInformationToken(TokenDefaultDacl)\n"); - PtrDefaultDacl = (PTOKEN_DEFAULT_DACL) TokenInformation; - Length = (Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0) + sizeof(TOKEN_DEFAULT_DACL); - if (TokenInformationLength < Length) - { - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else if (!Token->DefaultDacl) - { - PtrDefaultDacl->DefaultDacl = 0; - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - } - else - { - PtrDefaultDacl->DefaultDacl = (PACL) (PtrDefaultDacl + 1); - memmove(PtrDefaultDacl->DefaultDacl, - Token->DefaultDacl, - Token->DefaultDacl->AclSize); - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - } - break; + { + PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation; + DPRINT("NtQueryInformationToken(TokenDefaultDacl)\n"); + RequiredLength = sizeof(TOKEN_DEFAULT_DACL); + + if(Token->DefaultDacl != NULL) + { + RequiredLength += Token->DefaultDacl->AclSize; + } + + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + if(Token->DefaultDacl != NULL) + { + tdd->DefaultDacl = (PACL)(tdd + 1); + RtlCopyMemory(tdd->DefaultDacl, + Token->DefaultDacl, + Token->DefaultDacl->AclSize); + } + else + { + tdd->DefaultDacl = NULL; + } + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + case TokenSource: - DPRINT("NtQueryInformationToken(TokenSource)\n"); - if (TokenInformationLength < sizeof(TOKEN_SOURCE)) - { - Length = sizeof(TOKEN_SOURCE); - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - Status = MmCopyToCaller(TokenInformation, &Token->TokenSource, sizeof(TOKEN_SOURCE)); - } - break; + { + PTOKEN_SOURCE ts = (PTOKEN_SOURCE)TokenInformation; + DPRINT("NtQueryInformationToken(TokenSource)\n"); + RequiredLength = sizeof(TOKEN_SOURCE); + + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + *ts = Token->TokenSource; + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + case TokenType: - DPRINT("NtQueryInformationToken(TokenType)\n"); - if (TokenInformationLength < sizeof(TOKEN_TYPE)) - { - Length = sizeof(TOKEN_TYPE); - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - Status = MmCopyToCaller(TokenInformation, &Token->TokenType, sizeof(TOKEN_TYPE)); - } - break; + { + PTOKEN_TYPE tt = (PTOKEN_TYPE)TokenInformation; + DPRINT("NtQueryInformationToken(TokenType)\n"); + RequiredLength = sizeof(TOKEN_TYPE); + + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + *tt = Token->TokenType; + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + case TokenImpersonationLevel: - DPRINT("NtQueryInformationToken(TokenImpersonationLevel)\n"); - if (TokenInformationLength < sizeof(SECURITY_IMPERSONATION_LEVEL)) - { - Length = sizeof(SECURITY_IMPERSONATION_LEVEL); - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - Status = MmCopyToCaller(TokenInformation, &Token->ImpersonationLevel, sizeof(SECURITY_IMPERSONATION_LEVEL)); - } - break; + { + PSECURITY_IMPERSONATION_LEVEL sil = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation; + DPRINT("NtQueryInformationToken(TokenImpersonationLevel)\n"); + RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL); + + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + *sil = Token->ImpersonationLevel; + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + case TokenStatistics: - DPRINT("NtQueryInformationToken(TokenStatistics)\n"); - if (TokenInformationLength < sizeof(TOKEN_STATISTICS)) - { - Length = sizeof(TOKEN_STATISTICS); - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - PtrTokenStatistics = (PTOKEN_STATISTICS)TokenInformation; - PtrTokenStatistics->TokenId = Token->TokenId; - PtrTokenStatistics->AuthenticationId = Token->AuthenticationId; - PtrTokenStatistics->ExpirationTime = Token->ExpirationTime; - PtrTokenStatistics->TokenType = Token->TokenType; - PtrTokenStatistics->ImpersonationLevel = Token->ImpersonationLevel; - PtrTokenStatistics->DynamicCharged = Token->DynamicCharged; - PtrTokenStatistics->DynamicAvailable = Token->DynamicAvailable; - PtrTokenStatistics->GroupCount = Token->UserAndGroupCount - 1; - PtrTokenStatistics->PrivilegeCount = Token->PrivilegeCount; - PtrTokenStatistics->ModifiedId = Token->ModifiedId; + { + PTOKEN_STATISTICS ts = (PTOKEN_STATISTICS)TokenInformation; - Status = STATUS_SUCCESS; - } - break; + DPRINT("NtQueryInformationToken(TokenStatistics)\n"); + RequiredLength = sizeof(TOKEN_STATISTICS); + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + ts->TokenId = Token->TokenId; + ts->AuthenticationId = Token->AuthenticationId; + ts->ExpirationTime = Token->ExpirationTime; + ts->TokenType = Token->TokenType; + ts->ImpersonationLevel = Token->ImpersonationLevel; + ts->DynamicCharged = Token->DynamicCharged; + ts->DynamicAvailable = Token->DynamicAvailable; + ts->GroupCount = Token->UserAndGroupCount - 1; + ts->PrivilegeCount = Token->PrivilegeCount; + ts->ModifiedId = Token->ModifiedId; + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } + case TokenOrigin: - DPRINT("NtQueryInformationToken(TokenOrigin)\n"); - if (TokenInformationLength < sizeof(TOKEN_ORIGIN)) - { - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - Status = MmCopyToCaller(&((PTOKEN_ORIGIN)TokenInformation)->OriginatingLogonSession, - &Token->AuthenticationId, sizeof(LUID)); - } - Length = sizeof(TOKEN_ORIGIN); - LengthStatus = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - { - Status = LengthStatus; - } - break; + { + PTOKEN_ORIGIN to = (PTOKEN_ORIGIN)TokenInformation; + + DPRINT("NtQueryInformationToken(TokenOrigin)\n"); + RequiredLength = sizeof(TOKEN_ORIGIN); + + _SEH_TRY + { + if(TokenInformationLength >= RequiredLength) + { + RtlCopyLuid(&to->OriginatingLogonSession, + &Token->AuthenticationId); + } + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + if(ReturnLength != NULL) + { + *ReturnLength = RequiredLength; + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + break; + } case TokenGroupsAndPrivileges: DPRINT1("NtQueryInformationToken(TokenGroupsAndPrivileges) not implemented\n"); @@ -847,27 +1084,44 @@ NtQueryInformationToken(IN HANDLE TokenHandle, break; case TokenSessionId: - DPRINT("NtQueryInformationToken(TokenSessionId)\n"); - if (TokenInformationLength < sizeof(ULONG)) - { - Length = sizeof(ULONG); - Status = MmCopyToCaller(ReturnLength, &Length, sizeof(ULONG)); - if (NT_SUCCESS(Status)) - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - Status = MmCopyToCaller(TokenInformation, &Token->SessionId, sizeof(ULONG)); - } - break; + { + ULONG SessionId = 0; + + DPRINT("NtQueryInformationToken(TokenSessionId)\n"); + + Status = SeQuerySessionIdToken(Token, + &SessionId); + + if(NT_SUCCESS(Status)) + { + _SEH_TRY + { + /* buffer size was already verified, no need to check here again */ + *(PULONG)TokenInformation = SessionId; + + if(ReturnLength != NULL) + { + *ReturnLength = sizeof(ULONG); + } + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + } + + break; + } default: - DPRINT1("NtQueryInformationToken(%d) invalid parameter\n"); - Status = STATUS_INVALID_PARAMETER; + DPRINT1("NtQueryInformationToken(%d) invalid information class\n", TokenInformationClass); + Status = STATUS_INVALID_INFO_CLASS; break; } - ObDereferenceObject(Token); + ObDereferenceObject(Token); + } return(Status); } @@ -888,7 +1142,7 @@ SeQueryInformationToken( } /* - * @unimplemented + * @implemented */ NTSTATUS STDCALL @@ -897,14 +1151,14 @@ SeQuerySessionIdToken( IN PULONG pSessionId ) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + *pSessionId = ((PTOKEN)Token)->SessionId; + return STATUS_SUCCESS; } /* * NtSetTokenInformation: Partly implemented. * Unimplemented: - * TokenOrigin, TokenDefaultDacl, TokenSessionId + * TokenOrigin, TokenDefaultDacl */ NTSTATUS STDCALL @@ -913,123 +1167,229 @@ NtSetInformationToken(IN HANDLE TokenHandle, OUT PVOID TokenInformation, IN ULONG TokenInformationLength) { - NTSTATUS Status; PTOKEN Token; - TOKEN_OWNER TokenOwnerSet = { 0 }; - TOKEN_PRIMARY_GROUP TokenPrimaryGroupSet = { 0 }; - DWORD NeededAccess = 0; + KPROCESSOR_MODE PreviousMode; + ULONG NeededAccess = TOKEN_ADJUST_DEFAULT; + NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); + + PreviousMode = ExGetPreviousMode(); + + DefaultSetInfoBufferCheck(TokenInformationClass, + SeTokenInformationClass, + TokenInformation, + TokenInformationLength, + PreviousMode, + &Status); - switch (TokenInformationClass) - { - case TokenOwner: - case TokenPrimaryGroup: - NeededAccess = TOKEN_ADJUST_DEFAULT; - break; - - case TokenDefaultDacl: - if (TokenInformationLength < sizeof(TOKEN_DEFAULT_DACL)) - return STATUS_BUFFER_TOO_SMALL; - NeededAccess = TOKEN_ADJUST_DEFAULT; - break; - - default: - DPRINT1("NtSetInformationToken: lying about success (stub) - %x\n", TokenInformationClass); - return STATUS_SUCCESS; - - } + if(!NT_SUCCESS(Status)) + { + /* Invalid buffers */ + DPRINT("NtSetInformationToken() failed, Status: 0x%x\n", Status); + return Status; + } + + if(TokenInformationClass == TokenSessionId) + { + NeededAccess |= TOKEN_ADJUST_SESSIONID; + } Status = ObReferenceObjectByHandle(TokenHandle, NeededAccess, SepTokenObjectType, - UserMode, + PreviousMode, (PVOID*)&Token, NULL); - if (!NT_SUCCESS(Status)) + if (NT_SUCCESS(Status)) + { + switch (TokenInformationClass) { - return(Status); - } - - switch (TokenInformationClass) - { - case TokenOwner: - MmCopyFromCaller( &TokenOwnerSet, TokenInformation, - min(sizeof(TokenOwnerSet),TokenInformationLength) ); - RtlCopySid(TokenInformationLength - sizeof(TOKEN_OWNER), - Token->UserAndGroups[Token->DefaultOwnerIndex].Sid, - TokenOwnerSet.Owner); - Status = STATUS_SUCCESS; - DPRINT("NtSetInformationToken(TokenOwner)\n"); - break; - - case TokenPrimaryGroup: - MmCopyFromCaller( &TokenPrimaryGroupSet, TokenInformation, - min(sizeof(TokenPrimaryGroupSet), - TokenInformationLength) ); - RtlCopySid(TokenInformationLength - sizeof(TOKEN_PRIMARY_GROUP), - Token->PrimaryGroup, - TokenPrimaryGroupSet.PrimaryGroup); - Status = STATUS_SUCCESS; - DPRINT("NtSetInformationToken(TokenPrimaryGroup)," - "Token->PrimaryGroup = 0x%08x\n", Token->PrimaryGroup); - break; - - case TokenDefaultDacl: + case TokenOwner: { - TOKEN_DEFAULT_DACL TokenDefaultDacl = { 0 }; - ACL OldAcl; - PACL NewAcl; - - Status = MmCopyFromCaller( &TokenDefaultDacl, TokenInformation, - sizeof(TOKEN_DEFAULT_DACL) ); - if (!NT_SUCCESS(Status)) + if(TokenInformationLength >= sizeof(TOKEN_OWNER)) + { + PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation; + PSID InputSid = NULL; + + _SEH_TRY { - Status = STATUS_INVALID_PARAMETER; + InputSid = to->Owner; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + PSID CapturedSid; + + Status = SepCaptureSid(InputSid, + PreviousMode, + PagedPool, + FALSE, + &CapturedSid); + if(NT_SUCCESS(Status)) + { + RtlCopySid(RtlLengthSid(CapturedSid), + Token->UserAndGroups[Token->DefaultOwnerIndex].Sid, + CapturedSid); + SepReleaseSid(CapturedSid, + PreviousMode, + FALSE); + } + } + } + else + { + Status = STATUS_INFO_LENGTH_MISMATCH; + } + break; + } + + case TokenPrimaryGroup: + { + if(TokenInformationLength >= sizeof(TOKEN_PRIMARY_GROUP)) + { + PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation; + PSID InputSid = NULL; + + _SEH_TRY + { + InputSid = tpg->PrimaryGroup; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + PSID CapturedSid; + + Status = SepCaptureSid(InputSid, + PreviousMode, + PagedPool, + FALSE, + &CapturedSid); + if(NT_SUCCESS(Status)) + { + RtlCopySid(RtlLengthSid(CapturedSid), + Token->PrimaryGroup, + CapturedSid); + SepReleaseSid(CapturedSid, + PreviousMode, + FALSE); + } + } + } + else + { + Status = STATUS_INFO_LENGTH_MISMATCH; + } + break; + } + + case TokenDefaultDacl: + { + if(TokenInformationLength >= sizeof(TOKEN_DEFAULT_DACL)) + { + PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation; + PACL InputAcl = NULL; + + _SEH_TRY + { + InputAcl = tdd->DefaultDacl; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + if(InputAcl != NULL) + { + PACL CapturedAcl; + + /* capture and copy the dacl */ + Status = SepCaptureAcl(InputAcl, + PreviousMode, + PagedPool, + TRUE, + &CapturedAcl); + if(NT_SUCCESS(Status)) + { + /* free the previous dacl if present */ + if(Token->DefaultDacl != NULL) + { + ExFreePool(Token->DefaultDacl); + } + + /* set the new dacl */ + Token->DefaultDacl = CapturedAcl; + } + } + else + { + /* clear and free the default dacl if present */ + if(Token->DefaultDacl != NULL) + { + ExFreePool(Token->DefaultDacl); + Token->DefaultDacl = NULL; + } + } + } + } + else + { + Status = STATUS_INFO_LENGTH_MISMATCH; + } + break; + } + + case TokenSessionId: + { + ULONG SessionId = 0; + + _SEH_TRY + { + /* buffer size was already verified, no need to check here again */ + SessionId = *(PULONG)TokenInformation; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; + + if(NT_SUCCESS(Status)) + { + if(!SeSinglePrivilegeCheck(SeTcbPrivilege, + PreviousMode)) + { + Status = STATUS_PRIVILEGE_NOT_HELD; break; } - Status = MmCopyFromCaller( &OldAcl, TokenDefaultDacl.DefaultDacl, - sizeof(ACL) ); - if (!NT_SUCCESS(Status)) - { - Status = STATUS_INVALID_PARAMETER; - break; - } - - NewAcl = ExAllocatePool(NonPagedPool, sizeof(ACL)); - if (NewAcl == NULL) - { - Status = STATUS_INSUFFICIENT_RESOURCES; - break; - } - - Status = MmCopyFromCaller( NewAcl, TokenDefaultDacl.DefaultDacl, - OldAcl.AclSize ); - if (!NT_SUCCESS(Status)) - { - Status = STATUS_INVALID_PARAMETER; - ExFreePool(NewAcl); - break; - } - - if (Token->DefaultDacl) - { - ExFreePool(Token->DefaultDacl); - } - - Token->DefaultDacl = NewAcl; - - Status = STATUS_SUCCESS; + Token->SessionId = SessionId; + } break; } - default: - Status = STATUS_NOT_IMPLEMENTED; - break; + default: + { + Status = STATUS_NOT_IMPLEMENTED; + break; + } } - ObDereferenceObject(Token); + ObDereferenceObject(Token); + } return(Status); } @@ -1045,16 +1405,18 @@ NtSetInformationToken(IN HANDLE TokenHandle, */ NTSTATUS STDCALL NtDuplicateToken(IN HANDLE ExistingTokenHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL /*is it really optional?*/, - IN BOOLEAN EffectiveOnly, - IN TOKEN_TYPE TokenType, - OUT PHANDLE NewTokenHandle) + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN BOOLEAN EffectiveOnly, + IN TOKEN_TYPE TokenType, + OUT PHANDLE NewTokenHandle) { KPROCESSOR_MODE PreviousMode; HANDLE hToken; PTOKEN Token; PTOKEN NewToken; + PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService; + BOOLEAN QoSPresent; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); @@ -1081,57 +1443,66 @@ NtDuplicateToken(IN HANDLE ExistingTokenHandle, } } + Status = SepCaptureSecurityQualityOfService(ObjectAttributes, + PreviousMode, + PagedPool, + FALSE, + &CapturedSecurityQualityOfService, + &QoSPresent); + if(!NT_SUCCESS(Status)) + { + DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status); + return Status; + } + Status = ObReferenceObjectByHandle(ExistingTokenHandle, TOKEN_DUPLICATE, SepTokenObjectType, PreviousMode, (PVOID*)&Token, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to reference token (Status %lx)\n", Status); - return Status; - } - - Status = SepDuplicateToken(Token, - ObjectAttributes, - EffectiveOnly, - TokenType, - ObjectAttributes->SecurityQualityOfService ? - ((PSECURITY_QUALITY_OF_SERVICE)(ObjectAttributes->SecurityQualityOfService))->ImpersonationLevel : - 0 /*SecurityAnonymous*/, - PreviousMode, - &NewToken); - - ObDereferenceObject(Token); - - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to duplicate token (Status %lx)\n", Status); - return Status; - } - - Status = ObInsertObject((PVOID)NewToken, - NULL, - DesiredAccess, - 0, - NULL, - &hToken); - - ObDereferenceObject(NewToken); - if (NT_SUCCESS(Status)) + { + Status = SepDuplicateToken(Token, + ObjectAttributes, + EffectiveOnly, + TokenType, + (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous), + PreviousMode, + &NewToken); + + ObDereferenceObject(Token); + + if (NT_SUCCESS(Status)) { - _SEH_TRY + Status = ObInsertObject((PVOID)NewToken, + NULL, + DesiredAccess, + 0, + NULL, + &hToken); + + ObDereferenceObject(NewToken); + + if (NT_SUCCESS(Status)) { - *NewTokenHandle = hToken; + _SEH_TRY + { + *NewTokenHandle = hToken; + } + _SEH_HANDLE + { + Status = _SEH_GetExceptionCode(); + } + _SEH_END; } - _SEH_HANDLE - { - Status = _SEH_GetExceptionCode(); - } - _SEH_END; } + } + + /* free the captured structure */ + SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, + PreviousMode, + FALSE); return Status; } @@ -1261,7 +1632,7 @@ NtAdjustPrivilegesToken (IN HANDLE TokenHandle, { // PLUID_AND_ATTRIBUTES Privileges; KPROCESSOR_MODE PreviousMode; -// ULONG PrivilegeCount; + ULONG PrivilegeCount; PTOKEN Token; // ULONG Length; ULONG i; @@ -1292,7 +1663,7 @@ NtAdjustPrivilegesToken (IN HANDLE TokenHandle, // &Length); Status = ObReferenceObjectByHandle (TokenHandle, - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + TOKEN_ADJUST_PRIVILEGES | (PreviousState != NULL ? TOKEN_QUERY : 0), SepTokenObjectType, PreviousMode, (PVOID*)&Token, @@ -1319,6 +1690,12 @@ NtAdjustPrivilegesToken (IN HANDLE TokenHandle, &c); #endif + PrivilegeCount = (BufferLength - FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges)) / + sizeof(LUID_AND_ATTRIBUTES); + + if (PreviousState != NULL) + PreviousState->PrivilegeCount = 0; + k = 0; if (DisableAllPrivileges == TRUE) { @@ -1329,11 +1706,22 @@ NtAdjustPrivilegesToken (IN HANDLE TokenHandle, DPRINT ("Attributes differ\n"); /* Save current privilege */ - if (PreviousState != NULL && k < PreviousState->PrivilegeCount) + if (PreviousState != NULL) { - PreviousState->Privileges[k].Luid = Token->Privileges[i].Luid; - PreviousState->Privileges[k].Attributes = Token->Privileges[i].Attributes; - k++; + if (k < PrivilegeCount) + { + PreviousState->PrivilegeCount++; + PreviousState->Privileges[k].Luid = Token->Privileges[i].Luid; + PreviousState->Privileges[k].Attributes = Token->Privileges[i].Attributes; + } + else + { + /* FIXME: Should revert all the changes, calculate how + * much space would be needed, set ResultLength + * accordingly and fail. + */ + } + k++; } /* Update current privlege */ @@ -1363,11 +1751,22 @@ NtAdjustPrivilegesToken (IN HANDLE TokenHandle, NewState->Privileges[j].Attributes); /* Save current privilege */ - if (PreviousState != NULL && k < PreviousState->PrivilegeCount) + if (PreviousState != NULL) { - PreviousState->Privileges[k].Luid = Token->Privileges[i].Luid; - PreviousState->Privileges[k].Attributes = Token->Privileges[i].Attributes; - k++; + if (k < PrivilegeCount) + { + PreviousState->PrivilegeCount++; + PreviousState->Privileges[k].Luid = Token->Privileges[i].Luid; + PreviousState->Privileges[k].Attributes = Token->Privileges[i].Attributes; + } + else + { + /* FIXME: Should revert all the changes, calculate how + * much space would be needed, set ResultLength + * accordingly and fail. + */ + } + k++; } /* Update current privlege */ @@ -1408,6 +1807,8 @@ SepCreateSystemProcessToken(struct _EPROCESS* Process) NTSTATUS Status; ULONG uSize; ULONG i; + + PAGED_CODE(); ULONG uLocalSystemLength = RtlLengthSid(SeLocalSystemSid); ULONG uWorldLength = RtlLengthSid(SeWorldSid); @@ -1456,6 +1857,8 @@ SepCreateSystemProcessToken(struct _EPROCESS* Process) return Status; } + AccessToken->TokenLock = &SepTokenLock; + AccessToken->TokenType = TokenPrimary; AccessToken->ImpersonationLevel = SecurityDelegation; AccessToken->TokenSource.SourceIdentifier.LowPart = 0; @@ -1706,6 +2109,8 @@ NtCreateToken(OUT PHANDLE TokenHandle, return(Status); } + AccessToken->TokenLock = &SepTokenLock; + RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier, &TokenSource->SourceIdentifier); memcpy(AccessToken->TokenSource.SourceName, @@ -1738,7 +2143,7 @@ NtCreateToken(OUT PHANDLE TokenHandle, uLength += RtlLengthSid(TokenGroups->Groups[i].Sid); AccessToken->UserAndGroups = - (PSID_AND_ATTRIBUTES)ExAllocatePoolWithTag(NonPagedPool, + (PSID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PagedPool, uLength, TAG('T', 'O', 'K', 'u')); @@ -1774,7 +2179,7 @@ NtCreateToken(OUT PHANDLE TokenHandle, { uLength = TokenPrivileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); AccessToken->Privileges = - (PLUID_AND_ATTRIBUTES)ExAllocatePoolWithTag(NonPagedPool, + (PLUID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PagedPool, uLength, TAG('T', 'O', 'K', 'p')); @@ -1791,7 +2196,7 @@ NtCreateToken(OUT PHANDLE TokenHandle, if (NT_SUCCESS(Status)) { AccessToken->DefaultDacl = - (PACL) ExAllocatePoolWithTag(NonPagedPool, + (PACL) ExAllocatePoolWithTag(PagedPool, TokenDefaultDacl->DefaultDacl->AclSize, TAG('T', 'O', 'K', 'd')); memcpy(AccessToken->DefaultDacl, diff --git a/reactos/regtests/regtests/Makefile b/reactos/regtests/regtests/Makefile index 8ea5ef9ee68..49f51ccf958 100755 --- a/reactos/regtests/regtests/Makefile +++ b/reactos/regtests/regtests/Makefile @@ -6,7 +6,7 @@ TARGET_TYPE = dynlink TARGET_NAME = regtests -TARGET_CFLAGS = -Wall -Werror +TARGET_CFLAGS = -D__USE_W32API -Wall -Werror TARGET_OBJECTS = \ regtests.o diff --git a/reactos/regtests/regtests/regtests.xml b/reactos/regtests/regtests/regtests.xml index 6a0cbc44374..aee66c56167 100644 --- a/reactos/regtests/regtests/regtests.xml +++ b/reactos/regtests/regtests/regtests.xml @@ -1,6 +1,7 @@ . + kernel32 regtests.c diff --git a/reactos/services/rpcss/makefile b/reactos/services/rpcss/makefile index ec3b8778d09..93147ebe1e1 100644 --- a/reactos/services/rpcss/makefile +++ b/reactos/services/rpcss/makefile @@ -14,7 +14,7 @@ TARGET_SDKLIBS = kernel32.a advapi32.a TARGET_OBJECTS = $(TARGET_NAME).o endpoint.o -TARGET_CFLAGS = -Wall -Werror +TARGET_CFLAGS = -D__USE_W32API -Wall -Werror include $(PATH_TO_TOP)/rules.mak diff --git a/reactos/services/rpcss/rpcss.xml b/reactos/services/rpcss/rpcss.xml index 28474b0343b..2775a2b9f28 100644 --- a/reactos/services/rpcss/rpcss.xml +++ b/reactos/services/rpcss/rpcss.xml @@ -1,5 +1,6 @@ . + kernel32 advapi32 rpcss.c diff --git a/reactos/services/umpnpmgr/makefile b/reactos/services/umpnpmgr/makefile index f96bb8bd878..d935174a769 100644 --- a/reactos/services/umpnpmgr/makefile +++ b/reactos/services/umpnpmgr/makefile @@ -12,12 +12,25 @@ TARGET_INSTALLDIR = system32 TARGET_SDKLIBS = ntdll.a kernel32.a advapi32.a rpcrt4.a -TARGET_OBJECTS = umpnpmgr.o +TARGET_OBJECTS = pnp_s.o umpnpmgr.o -TARGET_CFLAGS = -Wall -Werror -D__USE_W32API +TARGET_CFLAGS = -Wall -Werror -D__USE_W32API -DUNICODE -D_UNICODE + +TARGET_CLEAN = pnp_s.c pnp.h + +DEP_OBJECTS = $(TARGET_OBJECTS) include $(PATH_TO_TOP)/rules.mak include $(TOOLS_PATH)/helper.mk +include $(TOOLS_PATH)/depend.mk + +WIDL_FLAGS = \ + -D _X86_ -D MIDL_PASS \ + -I $(PATH_TO_TOP)/w32api/include + +pnp_s.c pnp.h: $(PATH_TO_TOP)/include/idl/pnp.idl + $(WIDL) $(WIDL_FLAGS) -h -H pnp.h -s -S pnp_s.c $(PATH_TO_TOP)/include/idl/pnp.idl + # EOF diff --git a/reactos/services/umpnpmgr/umpnpmgr.c b/reactos/services/umpnpmgr/umpnpmgr.c index b9b5659091b..a4b0b156595 100644 --- a/reactos/services/umpnpmgr/umpnpmgr.c +++ b/reactos/services/umpnpmgr/umpnpmgr.c @@ -27,9 +27,6 @@ /* INCLUDES *****************************************************************/ -#define UNICODE -#define _UNICODE - #define NTOS_MODE_USER #include #include @@ -41,6 +38,8 @@ #include #include +#include "pnp_s.h" + #define DBG #define NDEBUG #include @@ -61,6 +60,66 @@ static SERVICE_TABLE_ENTRY ServiceTable[2] = /* FUNCTIONS *****************************************************************/ +static DWORD WINAPI +RpcServerThread(LPVOID lpParameter) +{ + RPC_STATUS Status; + + DPRINT("RpcServerThread() called\n"); + + Status = RpcServerUseProtseqEpW(L"ncacn_np", + 20, + L"\\pipe\\umpnpmgr", + NULL); // Security descriptor + if (Status != RPC_S_OK) + { + DPRINT1("RpcServerUseProtseqEpW() failed (Status %lx)\n", Status); + return 0; + } + + Status = RpcServerRegisterIf(pnp_v1_0_s_ifspec, + NULL, + NULL); + if (Status != RPC_S_OK) + { + DPRINT1("RpcServerRegisterIf() failed (Status %lx)\n", Status); + return 0; + } + + Status = RpcServerListen(1, + 20, + FALSE); + if (Status != RPC_S_OK) + { + DPRINT1("RpcServerListen() failed (Status %lx)\n", Status); + return 0; + } + + DPRINT("RpcServerThread() done\n"); + + return 0; +} + + +void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len) +{ + return GlobalAlloc(GPTR, len); +} + + +void __RPC_USER midl_user_free(void __RPC_FAR * ptr) +{ + GlobalFree(ptr); +} + + +//WORD PNP_GetVersion(RPC_BINDING_HANDLE BindingHandle) +WORD PNP_GetVersion(handle_t BindingHandle) +{ + return 0x0400; +} + + static DWORD WINAPI PnpEventThread(LPVOID lpParameter) { @@ -134,6 +193,15 @@ ServiceMain(DWORD argc, LPTSTR *argv) if (hThread != NULL) CloseHandle(hThread); + hThread = CreateThread(NULL, + 0, + RpcServerThread, + NULL, + 0, + &dwThreadId); + if (hThread != NULL) + CloseHandle(hThread); + DPRINT("ServiceMain() done\n"); } diff --git a/reactos/services/umpnpmgr/umpnpmgr.xml b/reactos/services/umpnpmgr/umpnpmgr.xml index 20c61306842..84ff6f93c73 100644 --- a/reactos/services/umpnpmgr/umpnpmgr.xml +++ b/reactos/services/umpnpmgr/umpnpmgr.xml @@ -1,6 +1,10 @@ . + . + + + pnp_server ntdll kernel32 advapi32 diff --git a/reactos/subsys/csrss/api/process.c b/reactos/subsys/csrss/api/process.c index 7a5bd60d846..24dacee8f9d 100644 --- a/reactos/subsys/csrss/api/process.c +++ b/reactos/subsys/csrss/api/process.c @@ -59,6 +59,9 @@ PCSRSS_PROCESS_DATA STDCALL CsrCreateProcessData(HANDLE ProcessId) { ULONG hash; PCSRSS_PROCESS_DATA pProcessData; + OBJECT_ATTRIBUTES ObjectAttributes; + CLIENT_ID ClientId; + NTSTATUS Status; hash = (ULONG_PTR)ProcessId % (sizeof(ProcessData) / sizeof(*ProcessData)); @@ -80,6 +83,27 @@ PCSRSS_PROCESS_DATA STDCALL CsrCreateProcessData(HANDLE ProcessId) pProcessData->ProcessId = ProcessId; pProcessData->next = ProcessData[hash]; ProcessData[hash] = pProcessData; + + ClientId.UniqueThread = NULL; + ClientId.UniqueProcess = pProcessData->ProcessId; + InitializeObjectAttributes(&ObjectAttributes, + NULL, + 0, + NULL, + NULL); + + /* using OpenProcess is not optimal due to HANDLE vs. DWORD PIDs... */ + Status = NtOpenProcess(&pProcessData->Process, + PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | + PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | SYNCHRONIZE, + &ObjectAttributes, + &ClientId); + if (!NT_SUCCESS(Status)) + { + ProcessData[hash] = pProcessData->next; + RtlFreeHeap(CsrssApiHeap, 0, pProcessData); + pProcessData = NULL; + } } } else @@ -115,6 +139,10 @@ NTSTATUS STDCALL CsrFreeProcessData(HANDLE Pid) if (pProcessData) { DPRINT("CsrFreeProcessData pid: %d\n", Pid); + if (pProcessData->Process) + { + NtClose(pProcessData->Process); + } if (pProcessData->Console) { RtlEnterCriticalSection(&ProcessDataLock); @@ -167,7 +195,6 @@ CSR_API(CsrCreateProcess) { PCSRSS_PROCESS_DATA NewProcessData; NTSTATUS Status; - HANDLE Process; CSRSS_API_REQUEST ApiRequest; CSRSS_API_REPLY ApiReply; @@ -212,8 +239,6 @@ CSR_API(CsrCreateProcess) } else { - CLIENT_ID ClientId; - NewProcessData->Console = ProcessData->Console; InterlockedIncrement( &(ProcessData->Console->Header.ReferenceCount) ); CsrInsertObject(NewProcessData, @@ -225,27 +250,15 @@ CSR_API(CsrCreateProcess) &(NewProcessData->Console->ActiveBuffer->Header) ); RtlLeaveCriticalSection(&ProcessDataLock); - ClientId.UniqueProcess = (HANDLE)NewProcessData->ProcessId; - Status = NtOpenProcess( &Process, PROCESS_DUP_HANDLE, 0, &ClientId ); - if( !NT_SUCCESS( Status ) ) - { - DbgPrint( "CSR: NtOpenProcess() failed for handle duplication\n" ); - InterlockedDecrement( &(NewProcessData->Console->Header.ReferenceCount) ); - CsrFreeProcessData( NewProcessData->ProcessId ); - Reply->Status = Status; - return Status; - } - Status = NtDuplicateObject( NtCurrentProcess(), NewProcessData->Console->ActiveEvent, Process, &NewProcessData->ConsoleEvent, SYNCHRONIZE, FALSE, 0 ); + Status = NtDuplicateObject( NtCurrentProcess(), NewProcessData->Console->ActiveEvent, NewProcessData->Process, &NewProcessData->ConsoleEvent, SYNCHRONIZE, FALSE, 0 ); if( !NT_SUCCESS( Status ) ) { DbgPrint( "CSR: NtDuplicateObject() failed: %x\n", Status ); - NtClose( Process ); InterlockedDecrement( &(NewProcessData->Console->Header.ReferenceCount) ); CsrFreeProcessData( NewProcessData->ProcessId ); Reply->Status = Status; return Status; } - NtClose( Process ); NewProcessData->CtrlDispatcher = Request->Data.CreateProcessRequest.CtrlDispatcher; RtlEnterCriticalSection(&ProcessDataLock ); InsertHeadList(&NewProcessData->Console->ProcessList, &NewProcessData->ProcessEntry); diff --git a/reactos/subsys/csrss/api/wapi.c b/reactos/subsys/csrss/api/wapi.c index 0852c0af2e4..406f5ea90d4 100644 --- a/reactos/subsys/csrss/api/wapi.c +++ b/reactos/subsys/csrss/api/wapi.c @@ -10,16 +10,21 @@ /* INCLUDES ******************************************************************/ +#define NTOS_MODE_USER +#include #include #include -#include + + + +#define NDEBUG #include #include "api.h" /* GLOBALS *******************************************************************/ -HANDLE CsrssApiHeap; +HANDLE CsrssApiHeap = (HANDLE) 0; static unsigned ApiDefinitionsCount = 0; static PCSRSS_API_DEFINITION ApiDefinitions = NULL; @@ -33,6 +38,8 @@ CsrApiRegisterDefinitions(PCSRSS_API_DEFINITION NewDefinitions) PCSRSS_API_DEFINITION Scan; PCSRSS_API_DEFINITION New; + DPRINT("CSR: %s called", __FUNCTION__); + NewCount = 0; for (Scan = NewDefinitions; 0 != Scan->Handler; Scan++) { @@ -107,6 +114,8 @@ ClientConnectionThread(HANDLE ServerPort) PCSRSS_PROCESS_DATA ProcessData; PCSRSS_API_REPLY Reply; + DPRINT("CSR: %s called", __FUNCTION__); + Reply = NULL; for (;;) @@ -153,34 +162,37 @@ ClientConnectionThread(HANDLE ServerPort) * Handle connection requests from clients to the port * "\Windows\ApiPort". */ -void STDCALL +DWORD STDCALL ServerApiPortThread (PVOID PortHandle) { - NTSTATUS Status; + NTSTATUS Status = STATUS_SUCCESS; LPC_MAX_MESSAGE Request; - HANDLE ServerPort; - HANDLE ServerThread; - PCSRSS_PROCESS_DATA ProcessData; + HANDLE hApiListenPort = * (PHANDLE) PortHandle; + HANDLE ServerPort = (HANDLE) 0; + HANDLE ServerThread = (HANDLE) 0; + PCSRSS_PROCESS_DATA ProcessData = NULL; CsrInitProcessData(); + DPRINT("CSR: %s called", __FUNCTION__); + for (;;) { LPC_SECTION_READ LpcRead; ServerPort = NULL; - Status = NtListenPort(PortHandle, &Request.Header); + Status = NtListenPort (hApiListenPort, & Request.Header); if (!NT_SUCCESS(Status)) { DPRINT1("CSR: NtListenPort() failed\n"); break; } - Status = NtAcceptConnectPort(&ServerPort, - PortHandle, + Status = NtAcceptConnectPort(& ServerPort, + hApiListenPort, NULL, TRUE, 0, - &LpcRead); + & LpcRead); if (!NT_SUCCESS(Status)) { DPRINT1("CSR: NtAcceptConnectPort() failed\n"); @@ -215,7 +227,7 @@ ServerApiPortThread (PVOID PortHandle) NULL, (PTHREAD_START_ROUTINE)ClientConnectionThread, ServerPort, - &ServerThread, + & ServerThread, NULL); if (!NT_SUCCESS(Status)) { @@ -230,6 +242,7 @@ ServerApiPortThread (PVOID PortHandle) } NtClose(PortHandle); NtTerminateThread(NtCurrentThread(), Status); + return 0; } /********************************************************************** @@ -238,45 +251,88 @@ ServerApiPortThread (PVOID PortHandle) * * DESCRIPTION * Handle connection requests from SM to the port - * "\Windows\SbApiPort". + * "\Windows\SbApiPort". We will accept only one + * connection request (from the SM). */ -VOID STDCALL +DWORD STDCALL ServerSbApiPortThread (PVOID PortHandle) { - HANDLE hSbApiPortListen = (HANDLE) PortHandle; + HANDLE hSbApiPortListen = * (PHANDLE) PortHandle; HANDLE hConnectedPort = (HANDLE) 0; LPC_MAX_MESSAGE Request = {{0}}; + PVOID Context = NULL; NTSTATUS Status = STATUS_SUCCESS; - while (TRUE) + DPRINT("CSR: %s called\n", __FUNCTION__); + + Status = NtListenPort (hSbApiPortListen, & Request.Header); + if (!NT_SUCCESS(Status)) { - Status = NtListenPort (hSbApiPortListen, & Request.Header); - if (!NT_SUCCESS(Status)) - { - DPRINT1("CSR: %s: NtListenPort(SB) failed\n", __FUNCTION__); - break; - } + DPRINT1("CSR: %s: NtListenPort(SB) failed (Status=0x%08lx)\n", + __FUNCTION__, Status); + } else { +DPRINT("-- 1\n"); Status = NtAcceptConnectPort (& hConnectedPort, hSbApiPortListen, - NULL, - TRUE, - NULL, + NULL, + TRUE, + NULL, NULL); if(!NT_SUCCESS(Status)) { - DPRINT1("CSR: %s: NtAcceptConnectPort() failed\n", __FUNCTION__); - break; + DPRINT1("CSR: %s: NtAcceptConnectPort() failed (Status=0x%08lx)\n", + __FUNCTION__, Status); + } else { +DPRINT("-- 2\n"); + Status = NtCompleteConnectPort (hConnectedPort); + if(!NT_SUCCESS(Status)) + { + DPRINT1("CSR: %s: NtCompleteConnectPort() failed (Status=0x%08lx)\n", + __FUNCTION__, Status); + } else { +DPRINT("-- 3\n"); + PLPC_MESSAGE Reply = NULL; + /* + * Tell the init thread the SM gave the + * green light for boostrapping. + */ + Status = NtSetEvent (hBootstrapOk, NULL); + if(!NT_SUCCESS(Status)) + { + DPRINT1("CSR: %s: NtSetEvent failed (Status=0x%08lx)\n", + __FUNCTION__, Status); + } + /* Wait for messages from the SM */ +DPRINT("-- 4\n"); + while (TRUE) + { + Status = NtReplyWaitReceivePort(hConnectedPort, + Context, + Reply, + & Request.Header); + if(!NT_SUCCESS(Status)) + { + DPRINT1("CSR: %s: NtReplyWaitReceivePort failed (Status=0x%08lx)\n", + __FUNCTION__, Status); + break; + } + switch (Request.Header.MessageType)//fix .h PORT_MESSAGE_TYPE(Request)) + { + /* TODO */ + default: + DPRINT1("CSR: %s received message (type=%d)\n", + __FUNCTION__, Request.Header.MessageType); + } +DPRINT("-- 5\n"); + } + } } - Status = NtCompleteConnectPort (hConnectedPort); - if(!NT_SUCCESS(Status)) - { - DPRINT1("CSR: %s: NtCompleteConnectPort() failed\n", __FUNCTION__); - break; - } - /* TODO: create thread for the connected port */ } + DPRINT1("CSR: %s: terminating!\n", __FUNCTION__); + if(hConnectedPort) NtClose (hConnectedPort); NtClose (hSbApiPortListen); NtTerminateThread (NtCurrentThread(), Status); + return 0; } /* EOF */ diff --git a/reactos/subsys/csrss/csrss.c b/reactos/subsys/csrss/csrss.c index b2b885e6d12..9a2abea51df 100644 --- a/reactos/subsys/csrss/csrss.c +++ b/reactos/subsys/csrss/csrss.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "api.h" @@ -145,78 +146,37 @@ CsrpFreeCommandLine (HANDLE ProcessHeap, } -/********************************************************************** - * NAME PRIVATE - * CsrpOpenSmInitDoneEvent/0 - */ -static NTSTATUS STDCALL -CsrpOpenSmInitDoneEvent (PHANDLE CsrssInitEvent) -{ - OBJECT_ATTRIBUTES ObjectAttributes; - UNICODE_STRING EventName; - - DPRINT("CSR: %s called\n", __FUNCTION__); - - RtlInitUnicodeString(& EventName, - L"\\CsrssInitDone"); - InitializeObjectAttributes (& ObjectAttributes, - & EventName, - EVENT_ALL_ACCESS, - 0, - NULL); - return NtOpenEvent (CsrssInitEvent, - EVENT_ALL_ACCESS, - & ObjectAttributes); -} - /* Native process' entry point */ VOID STDCALL NtProcessStartup(PPEB Peb) { PRTL_USER_PROCESS_PARAMETERS RtlProcessParameters = NULL; COMMAND_LINE_ARGUMENT CmdLineArg = {0}; - HANDLE CsrssInitEvent = (HANDLE) 0; NTSTATUS Status = STATUS_SUCCESS; - DPRINT("CSR: %s\n", __FUNCTION__); + PrintString("ReactOS Client/Server Run-Time %s (Build %s)\n", + KERNEL_RELEASE_STR, + KERNEL_VERSION_BUILD_STR); RtlProcessParameters = RtlNormalizeProcessParams (Peb->ProcessParameters); /*================================================================== - * Parse the command line. + * Parse the command line: TODO actually parse the cl, because + * it is required to load hosted server DLLs. *================================================================*/ Status = CsrpParseCommandLine (Peb->ProcessHeap, RtlProcessParameters, & CmdLineArg); if(STATUS_SUCCESS != Status) { - DbgPrint("CSR: CsrpParseCommandLine failed (Status=0x%08lx)\n", - Status); + DPRINT1("CSR: %s: CsrpParseCommandLine failed (Status=0x%08lx)\n", + __FUNCTION__, Status); } - /* - * Open the SM notification event to notify we are OK after - * subsystem server initialization. - */ - Status = CsrpOpenSmInitDoneEvent(& CsrssInitEvent); - if (!NT_SUCCESS(Status)) - { - DbgPrint("CSR: CsrpOpenSmInitDoneEvent failed (Status=0x%08lx)\n", - Status); - } /*================================================================== * Initialize the Win32 environment subsystem server. *================================================================*/ if (CsrServerInitialization (CmdLineArg.Count, CmdLineArg.Vector) == TRUE) { - /*============================================================= - * Tell SM we are up and safe. If we fail to notify SM, it will - * die and the kernel will bugcheck with - * SESSION5_INITIALIZATION_FAILED. - * TODO: this won't be necessary, because CSR will call SM - * API SM_SESSION_COMPLETE. - *===========================================================*/ - NtSetEvent (CsrssInitEvent, NULL); - CsrpFreeCommandLine (Peb->ProcessHeap, & CmdLineArg); /* * Terminate the current thread only. diff --git a/reactos/subsys/csrss/include/api.h b/reactos/subsys/csrss/include/api.h index 0f8610ef0a6..06b358f5092 100644 --- a/reactos/subsys/csrss/include/api.h +++ b/reactos/subsys/csrss/include/api.h @@ -37,6 +37,7 @@ typedef struct _CSRSS_PROCESS_DATA ULONG HandleTableSize; Object_t ** HandleTable; HANDLE ProcessId; + HANDLE Process; ULONG ShutdownLevel; ULONG ShutdownFlags; HANDLE ConsoleEvent; @@ -82,6 +83,9 @@ PCSRSS_PROCESS_DATA ProcessData,\ PCSRSS_API_REQUEST Request,\ PCSRSS_API_REPLY Reply) +/* init.c */ +extern HANDLE hBootstrapOk; + /* api/process.c */ CSR_API(CsrConnectProcess); CSR_API(CsrCreateProcess); @@ -96,9 +100,9 @@ NTSTATUS FASTCALL CsrApiRegisterDefinitions(PCSRSS_API_DEFINITION NewDefinitions VOID FASTCALL CsrApiCallHandler(PCSRSS_PROCESS_DATA ProcessData, PCSRSS_API_REQUEST Request, PCSRSS_API_REPLY Reply); -VOID STDCALL ServerApiPortThread (PVOID PortHandle); -VOID STDCALL ServerSbApiPortThread (PVOID PortHandle); -VOID Console_Api( DWORD Ignored ); +DWORD STDCALL ServerApiPortThread (PVOID PortHandle); +DWORD STDCALL ServerSbApiPortThread (PVOID PortHandle); +DWORD STDCALL Console_Api( PVOID unused ); extern HANDLE CsrssApiHeap; diff --git a/reactos/subsys/csrss/init.c b/reactos/subsys/csrss/init.c index 106ad692f46..e8c0076da1f 100644 --- a/reactos/subsys/csrss/init.c +++ b/reactos/subsys/csrss/init.c @@ -27,10 +27,9 @@ /* GLOBALS ******************************************************************/ -HANDLE CsrInitEvent = INVALID_HANDLE_VALUE; -HANDLE CsrHeap = INVALID_HANDLE_VALUE; +HANDLE CsrHeap = (HANDLE) 0; -HANDLE CsrObjectDirectory = INVALID_HANDLE_VALUE; +HANDLE CsrObjectDirectory = (HANDLE) 0; UNICODE_STRING CsrDirectoryName; @@ -39,11 +38,24 @@ extern HANDLE CsrssApiHeap; static unsigned InitCompleteProcCount; static CSRPLUGIN_INIT_COMPLETE_PROC *InitCompleteProcs = NULL; +HANDLE hSbApiPort = (HANDLE) 0; + +HANDLE hBootstrapOk = (HANDLE) 0; + +HANDLE hSmApiPort = (HANDLE) 0; + +HANDLE hApiPort = (HANDLE) 0; + +/********************************************************************** + * CsrpAddInitCompleteProc/1 + */ static NTSTATUS FASTCALL -AddInitCompleteProc(CSRPLUGIN_INIT_COMPLETE_PROC Proc) +CsrpAddInitCompleteProc(CSRPLUGIN_INIT_COMPLETE_PROC Proc) { CSRPLUGIN_INIT_COMPLETE_PROC *NewProcs; + DPRINT("CSR: %s called\n", __FUNCTION__); + NewProcs = RtlAllocateHeap(CsrssApiHeap, 0, (InitCompleteProcCount + 1) * sizeof(CSRPLUGIN_INIT_COMPLETE_PROC)); @@ -64,12 +76,17 @@ AddInitCompleteProc(CSRPLUGIN_INIT_COMPLETE_PROC Proc) return STATUS_SUCCESS; } +/********************************************************************** + * CallInitComplete/0 + */ static BOOL FASTCALL CallInitComplete(void) { BOOL Ok; unsigned i; + DPRINT("CSR: %s called\n", __FUNCTION__); + Ok = TRUE; if (0 != InitCompleteProcCount) { @@ -86,8 +103,11 @@ CallInitComplete(void) ULONG InitializeVideoAddressSpace(VOID); +/********************************************************************** + * CsrpParseCommandLine/2 + */ static NTSTATUS -CsrParseCommandLine ( +CsrpParseCommandLine ( ULONG ArgumentCount, PWSTR *ArgumentArray ) @@ -95,6 +115,8 @@ CsrParseCommandLine ( NTSTATUS Status; OBJECT_ATTRIBUTES Attributes; + DPRINT("CSR: %s called\n", __FUNCTION__); + /* DbgPrint ("Arguments: %ld\n", ArgumentCount); for (i = 0; i < ArgumentCount; i++) @@ -114,21 +136,28 @@ CsrParseCommandLine ( NULL); Status = NtCreateDirectoryObject(&CsrObjectDirectory, - 0xF000F, + 0xF000F, /* ea:??? */ &Attributes); return Status; } - -static VOID -CsrInitVideo(VOID) +/********************************************************************** + * CsrpInitVideo/0 + * + * TODO: we need a virtual device for sessions other than + * TODO: the console one + */ +static NTSTATUS +CsrpInitVideo (ULONG argc, PWSTR* argv) { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING DeviceName; IO_STATUS_BLOCK Iosb; - HANDLE VideoHandle; - NTSTATUS Status; + HANDLE VideoHandle = (HANDLE) 0; + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("CSR: %s called\n", __FUNCTION__); InitializeVideoAddressSpace(); @@ -148,10 +177,23 @@ CsrInitVideo(VOID) { NtClose(VideoHandle); } + return Status; } -static NTSTATUS FASTCALL -InitWin32Csr() +/********************************************************************** + * CsrpInitWin32Csr/0 + * + * TODO: this function should be turned more general to load an + * TODO: hosted server DLL as received from the command line; + * TODO: for instance: ServerDll=winsrv:ConServerDllInitialization,2 + * TODO: ^method ^dll ^api ^sid + * TODO: + * TODO: CsrpHostServerDll (LPWSTR DllName, + * TODO: LPWSTR ApiName, + * TODO: DWORD ServerId) + */ +static NTSTATUS +CsrpInitWin32Csr (ULONG argc, PWSTR* argv) { NTSTATUS Status; UNICODE_STRING DllName; @@ -163,6 +205,8 @@ InitWin32Csr() PCSRSS_OBJECT_DEFINITION ObjectDefinitions; CSRPLUGIN_INIT_COMPLETE_PROC InitCompleteProc; + DPRINT("CSR: %s called\n", __FUNCTION__); + RtlInitUnicodeString(&DllName, L"win32csr.dll"); Status = LdrLoadDll(NULL, 0, &DllName, (PVOID *) &hInst); if (! NT_SUCCESS(Status)) @@ -196,7 +240,7 @@ InitWin32Csr() } if (NULL != InitCompleteProc) { - Status = AddInitCompleteProc(InitCompleteProc); + Status = CsrpAddInitCompleteProc(InitCompleteProc); } return Status; @@ -219,157 +263,293 @@ CSRSS_API_DEFINITION NativeDefinitions[] = { 0, 0, 0, NULL } }; -/********************************************************************** - * NAME - * CsrpRegisterSubsystem/0 - * - * DESCRIPTION - * Register CSRSS in the SM to manage IMAGE_SUBSYSTEM_WINDOWS_CUI - * processes (environment subsystem server). - * - * RETURN VALUE - * STATUS_SUCCESS on success. - */ -static NTSTATUS FASTCALL -CsrpRegisterSubsystem(PHANDLE hSmApiPort) +static NTSTATUS STDCALL +CsrpCreateListenPort (IN LPWSTR Name, + IN OUT PHANDLE Port, + IN PTHREAD_START_ROUTINE ListenThread) { - NTSTATUS Status = STATUS_SUCCESS; - UNICODE_STRING SbApiPortName; + NTSTATUS Status = STATUS_SUCCESS; + OBJECT_ATTRIBUTES PortAttributes; + UNICODE_STRING PortName; - RtlInitUnicodeString (& SbApiPortName, L"\\Windows\\SbApiPort"); - Status = SmConnectApiPort (& SbApiPortName, - (HANDLE)-1, //unused - IMAGE_SUBSYSTEM_WINDOWS_CUI, - hSmApiPort); + DPRINT("CSR: %s called\n", __FUNCTION__); + + RtlInitUnicodeString (& PortName, Name); + InitializeObjectAttributes (& PortAttributes, + & PortName, + 0, + NULL, + NULL); + Status = NtCreatePort ( Port, + & PortAttributes, + 260, /* TODO: make caller set it*/ + 328, /* TODO: make caller set it*/ + 0); /* TODO: make caller set it*/ if(!NT_SUCCESS(Status)) { - DPRINT("CSR: unable to connect to the SM (Status=0x%lx)\n", Status); + DPRINT1("CSR: %s: NtCreatePort failed (Status=%08lx)\n", + __FUNCTION__, Status); return Status; } - DisplayString(L"CSR: registered with SM\n"); + Status = RtlCreateUserThread(NtCurrentProcess(), + NULL, + FALSE, + 0, + NULL, + NULL, + (PTHREAD_START_ROUTINE) ListenThread, + Port, + NULL, + NULL); + return Status; +} + +/* === INIT ROUTINES === */ + +/********************************************************************** + * CsrpCreateCallbackPort/0 + */ +static NTSTATUS +CsrpCreateHeap (ULONG argc, PWSTR* argv) +{ + DPRINT("CSR: %s called\n", __FUNCTION__); + + CsrssApiHeap = RtlCreateHeap(HEAP_GROWABLE, + NULL, + 65536, + 65536, + NULL, + NULL); + if (CsrssApiHeap == NULL) + { + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; +} + +/********************************************************************** + * CsrpCreateCallbackPort/0 + */ +static NTSTATUS +CsrpCreateCallbackPort (ULONG argc, PWSTR* argv) +{ + DPRINT("CSR: %s called\n", __FUNCTION__); + + return CsrpCreateListenPort (L"\\Windows\\SbApiPort", + & hSbApiPort, + ServerSbApiPortThread); +} + +/********************************************************************** + * CsrpRegisterSubsystem/2 + */ +static NTSTATUS +CsrpRegisterSubsystem (ULONG argc, PWSTR* argv) +{ + NTSTATUS Status = STATUS_SUCCESS; + OBJECT_ATTRIBUTES BootstrapOkAttributes; + UNICODE_STRING Name; + + DPRINT("CSR: %s called\n", __FUNCTION__); + + /* + * Create the event object the callback port + * thread will signal *if* the SM will + * authorize us to bootstrap. + */ + RtlInitUnicodeString (& Name, L"\\CsrssBooting"); + InitializeObjectAttributes(& BootstrapOkAttributes, + & Name, + 0, NULL, NULL); + Status = NtCreateEvent (& hBootstrapOk, + EVENT_ALL_ACCESS, + & BootstrapOkAttributes, + SynchronizationEvent, + FALSE); + if(!NT_SUCCESS(Status)) + { + DPRINT("CSR: %s: NtCreateEvent failed (Status=0x%08lx)\n", + __FUNCTION__, Status); + return Status; + } + /* + * Let's tell the SM a new environment + * subsystem server is in the system. + */ + RtlInitUnicodeString (& Name, L"\\Windows\\SbApiPort"); + DPRINT("CSR: %s: registering with SM for\n IMAGE_SUBSYSTEM_WINDOWS_CUI == 3\n", __FUNCTION__); + Status = SmConnectApiPort (& Name, + hSbApiPort, + IMAGE_SUBSYSTEM_WINDOWS_CUI, + & hSmApiPort); + if(!NT_SUCCESS(Status)) + { + DPRINT("CSR: %s unable to connect to the SM (Status=0x%08lx)\n", + __FUNCTION__, Status); + NtClose (hBootstrapOk); + return Status; + } + /* + * Wait for SM to reply OK... If the SM + * won't answer, we hang here forever! + */ + DPRINT("CSR: %s: waiting for SM to OK boot...\n", __FUNCTION__); + Status = NtWaitForSingleObject (hBootstrapOk, + FALSE, + NULL); + NtClose (hBootstrapOk); return Status; } +/********************************************************************** + * CsrpCreateApiPort/0 + */ +static NTSTATUS +CsrpCreateApiPort (ULONG argc, PWSTR* argv) +{ + DPRINT("CSR: %s called\n", __FUNCTION__); + + return CsrpCreateListenPort (L"\\Windows\\ApiPort", + & hApiPort, + ServerApiPortThread); +} + +/********************************************************************** + * CsrpApiRegisterDef/0 + */ +static NTSTATUS +CsrpApiRegisterDef (ULONG argc, PWSTR* argv) +{ + return CsrApiRegisterDefinitions(NativeDefinitions); +} + +/********************************************************************** + * CsrpCCTS/2 + */ +static NTSTATUS +CsrpCCTS (ULONG argc, PWSTR* argv) +{ + return CsrClientConnectToServer(); +} + +/********************************************************************** + * CsrpRunWinlogon/0 + * + * Start the logon process (winlogon.exe). + * + * TODO: this should be moved in CsrpCreateSession/x (one per session) + * TODO: in its own desktop (one logon desktop per winstation). + */ +static NTSTATUS +CsrpRunWinlogon (ULONG argc, PWSTR* argv) +{ + NTSTATUS Status = STATUS_SUCCESS; + UNICODE_STRING ImagePath; + UNICODE_STRING CommandLine; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NULL; + RTL_PROCESS_INFO ProcessInfo; + + + DPRINT("CSR: %s called\n", __FUNCTION__); + + /* initialize the process parameters */ + RtlInitUnicodeString (& ImagePath, L"\\SystemRoot\\system32\\winlogon.exe"); + RtlInitUnicodeString (& CommandLine, L""); + RtlCreateProcessParameters(& ProcessParameters, + & ImagePath, + NULL, + NULL, + & CommandLine, + NULL, + NULL, + NULL, + NULL, + NULL); + /* Create the winlogon process */ + Status = RtlCreateUserProcess (& ImagePath, + OBJ_CASE_INSENSITIVE, + ProcessParameters, + NULL, + NULL, + NULL, + FALSE, + NULL, + NULL, + & ProcessInfo); + /* Cleanup */ + RtlDestroyProcessParameters (ProcessParameters); + if (!NT_SUCCESS(Status)) + { + DPRINT1("SM: %s: loading winlogon.exe failed (Status=%08lx)\n", + __FUNCTION__, Status); + } + ZwResumeThread(ProcessInfo.ThreadHandle, NULL); + return Status; +} + + + +typedef NTSTATUS (* CSR_INIT_ROUTINE)(ULONG, PWSTR*); + +struct { + BOOL Required; + CSR_INIT_ROUTINE EntryPoint; + PCHAR ErrorMessage; +} InitRoutine [] = { + {TRUE, CsrpCreateCallbackPort, "create the callback port \\Windows\\SbApiPort"}, + {TRUE, CsrpRegisterSubsystem, "register with SM"}, + {TRUE, CsrpCreateHeap, "create the CSR heap"}, + {TRUE, CsrpCreateApiPort, "create the api port \\Windows\\ApiPort"}, + {TRUE, CsrpParseCommandLine, "parse the command line"}, + {TRUE, CsrpInitVideo, "initialize video"}, + {TRUE, CsrpApiRegisterDef, "initialize api definitions"}, + {TRUE, CsrpCCTS, "connect client to server"}, + {TRUE, CsrpInitWin32Csr, "load usermode dll"}, + {TRUE, CsrpRunWinlogon, "run WinLogon"}, +}; /********************************************************************** * NAME * CsrServerInitialization * * DESCRIPTION - * Create a directory object (\windows) and a named LPC port - * (\windows\ApiPort) + * Initialize the Win32 environment subsystem server. * * RETURN VALUE * TRUE: Initialization OK; otherwise FALSE. */ -BOOL -STDCALL +BOOL STDCALL CsrServerInitialization ( ULONG ArgumentCount, PWSTR *ArgumentArray ) { - NTSTATUS Status; - HANDLE hSmApiPort = (HANDLE) 0; - OBJECT_ATTRIBUTES ObAttributes; - UNICODE_STRING PortName; - HANDLE ApiPortHandle; -// HANDLE hSbApiPort = (HANDLE) 0; + INT i = 0; + NTSTATUS Status = STATUS_SUCCESS; -DisplayString(L"CSR: CsrServerInitialization\n"); + DPRINT("CSR: %s called\n", __FUNCTION__); - Status = CsrpRegisterSubsystem(& hSmApiPort); - if (! NT_SUCCESS(Status)) - { - DPRINT1("CSR: Unable to register subsystem (Status: %x)\n", Status); - return FALSE; - } - - Status = CsrParseCommandLine (ArgumentCount, ArgumentArray); - if (! NT_SUCCESS(Status)) - { - DPRINT1("CSR: Unable to parse the command line (Status: %x)\n", Status); - return FALSE; - } - - CsrInitVideo(); - - CsrssApiHeap = RtlCreateHeap(HEAP_GROWABLE, - NULL, - 65536, - 65536, - NULL, - NULL); - if (CsrssApiHeap == NULL) - { - DPRINT1("CSR: Failed to create private heap, aborting\n"); - return FALSE; - } - - Status = CsrApiRegisterDefinitions(NativeDefinitions); - if (! NT_SUCCESS(Status)) - { - return Status; - } - - /* NEW NAMED PORT: \Windows\ApiPort */ - RtlRosInitUnicodeStringFromLiteral(&PortName, L"\\Windows\\ApiPort"); - InitializeObjectAttributes(&ObAttributes, - &PortName, - 0, - NULL, - NULL); - Status = NtCreatePort(&ApiPortHandle, - &ObAttributes, - 260, - 328, - 0); - if (! NT_SUCCESS(Status)) - { - DPRINT1("CSR: Unable to create \\Windows\\ApiPort (Status %x)\n", Status); - return FALSE; - } - Status = RtlCreateUserThread(NtCurrentProcess(), - NULL, - FALSE, - 0, - NULL, - NULL, - (PTHREAD_START_ROUTINE)ServerApiPortThread, - ApiPortHandle, - NULL, - NULL); - if (! NT_SUCCESS(Status)) - { - DPRINT1("CSR: Unable to create server thread\n"); - NtClose(ApiPortHandle); - return FALSE; - } - - /* TODO: create \Windows\SbApiPort */ - - Status = CsrClientConnectToServer(); - if (!NT_SUCCESS(Status)) - { - DPRINT1("CsrClientConnectToServer() failed (Status %x)\n", Status); - return FALSE; - } - Status = InitWin32Csr(); - if (! NT_SUCCESS(Status)) - { - DPRINT1("CSR: Unable to load usermode dll (Status %x)\n", Status); - return FALSE; - } - - if (CallInitComplete()) - { -#if 0 - Status = SmCompleteSession (hSmApiPort,hSbApiPort,ApiPortHandle); -#endif - NtClose (hSmApiPort); - return TRUE; - } - return FALSE; + for (i=0; i < (sizeof InitRoutine / sizeof InitRoutine[0]); i++) + { + Status = InitRoutine[i].EntryPoint(ArgumentCount,ArgumentArray); + if(!NT_SUCCESS(Status)) + { + DPRINT1("CSR: %s: failed to %s (Status=%08lx)\n", + __FUNCTION__, + InitRoutine[i].ErrorMessage, + Status); + if (InitRoutine[i].Required) + { + return FALSE; + } + } + } + if (CallInitComplete()) + { + Status = SmCompleteSession (hSmApiPort,hSbApiPort,hApiPort); + return TRUE; + } + return FALSE; } /* EOF */ diff --git a/reactos/subsys/csrss/win32csr/conio.c b/reactos/subsys/csrss/win32csr/conio.c index 4c82d872b59..da5c7a38657 100644 --- a/reactos/subsys/csrss/win32csr/conio.c +++ b/reactos/subsys/csrss/win32csr/conio.c @@ -74,48 +74,22 @@ ConioConsoleFromProcessData(PCSRSS_PROCESS_DATA ProcessData, PCSRSS_CONSOLE *Con VOID FASTCALL ConioConsoleCtrlEvent(DWORD Event, PCSRSS_PROCESS_DATA ProcessData) { - HANDLE Process, Thread; + HANDLE Thread; DPRINT("ConioConsoleCtrlEvent Parent ProcessId = %x\n", ProcessData->ProcessId); if (ProcessData->CtrlDispatcher) { - OBJECT_ATTRIBUTES ObjectAttributes; - CLIENT_ID ClientId; - NTSTATUS Status; - - ClientId.UniqueThread = NULL; - ClientId.UniqueProcess = ProcessData->ProcessId; - InitializeObjectAttributes(&ObjectAttributes, - NULL, - 0, - NULL, - NULL); - /* using OpenProcess is not optimal due to HANDLE vs. DWORD PIDs... */ - Status = NtOpenProcess(&Process, - PROCESS_DUP_HANDLE, - &ObjectAttributes, - &ClientId); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed for handle duplication, Status: 0x%x\n", Status); - return; - } - - DPRINT("ConioConsoleCtrlEvent Process Handle = %x\n", Process); - - Thread = CreateRemoteThread(Process, NULL, 0, + Thread = CreateRemoteThread(ProcessData->Process, NULL, 0, (LPTHREAD_START_ROUTINE) ProcessData->CtrlDispatcher, (PVOID) Event, 0, NULL); if (NULL == Thread) { - DPRINT1("Failed thread creation\n"); - CloseHandle(Process); + DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError()); return; } CloseHandle(Thread); - CloseHandle(Process); } } @@ -264,9 +238,6 @@ CsrInitConsole(PCSRSS_CONSOLE Console) CSR_API(CsrAllocConsole) { PCSRSS_CONSOLE Console; - OBJECT_ATTRIBUTES ObjectAttributes; - CLIENT_ID ClientId; - HANDLE Process; NTSTATUS Status; DPRINT("CsrAllocConsole\n"); @@ -319,35 +290,10 @@ CSR_API(CsrAllocConsole) return Reply->Status = Status; } - ClientId.UniqueThread = NULL; - ClientId.UniqueProcess = ProcessData->ProcessId; - InitializeObjectAttributes(&ObjectAttributes, - NULL, - 0, - NULL, - NULL); - - /* using OpenProcess is not optimal due to HANDLE vs. DWORD PIDs... */ - Status = NtOpenProcess(&Process, - PROCESS_DUP_HANDLE, - &ObjectAttributes, - &ClientId); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtOpenProcess() failed for handle duplication, Status: 0x%x\n", Status); - Console->Header.ReferenceCount--; - ProcessData->Console = 0; - Win32CsrReleaseObject(ProcessData, Reply->Data.AllocConsoleReply.OutputHandle); - Win32CsrReleaseObject(ProcessData, Reply->Data.AllocConsoleReply.InputHandle); - Reply->Status = Status; - return Status; - } - if (! DuplicateHandle(GetCurrentProcess(), ProcessData->Console->ActiveEvent, - Process, &ProcessData->ConsoleEvent, EVENT_ALL_ACCESS, FALSE, 0)) + ProcessData->Process, &ProcessData->ConsoleEvent, EVENT_ALL_ACCESS, FALSE, 0)) { DPRINT1("DuplicateHandle() failed: %d\n", GetLastError); - CloseHandle(Process); Console->Header.ReferenceCount--; Win32CsrReleaseObject(ProcessData, Reply->Data.AllocConsoleReply.OutputHandle); Win32CsrReleaseObject(ProcessData, Reply->Data.AllocConsoleReply.InputHandle); @@ -355,7 +301,6 @@ CSR_API(CsrAllocConsole) Reply->Status = Status; return Status; } - CloseHandle(Process); ProcessData->CtrlDispatcher = Request->Data.AllocConsoleRequest.CtrlDispatcher; DPRINT("CSRSS:CtrlDispatcher address: %x\n", ProcessData->CtrlDispatcher); InsertHeadList(&ProcessData->Console->ProcessList, &ProcessData->ProcessEntry); @@ -1381,8 +1326,8 @@ ConioProcessKey(MSG *msg, PCSRSS_CONSOLE Console, BOOL TextMode) } } -VOID -Console_Api(DWORD RefreshEvent) +DWORD STDCALL +Console_Api (PVOID unused) { /* keep reading events from the keyboard and stuffing them into the current console's input queue */ @@ -1409,6 +1354,7 @@ Console_Api(DWORD RefreshEvent) } PrivateCsrssAcquireOrReleaseInputOwnership(TRUE); + return 0; } CSR_API(CsrGetScreenBufferInfo) diff --git a/reactos/subsys/smss/client.c b/reactos/subsys/smss/client.c index 18f81fe0264..33dc6bf5477 100644 --- a/reactos/subsys/smss/client.c +++ b/reactos/subsys/smss/client.c @@ -42,39 +42,6 @@ struct _SM_CLIENT_DIRECTORY } SmpClientDirectory; -#if 0 -/********************************************************************** - * SmpRegisterSmss/0 - */ -static NTSTATUS -SmpRegisterSmss(VOID) -{ - NTSTATUS Status = STATUS_SUCCESS; - UNICODE_STRING SbApiPortName = {0,0,NULL}; - HANDLE hSmApiPort = (HANDLE) 0; - - DPRINT("SM: %s called\n",__FUNCTION__); - - Status = SmConnectApiPort(& SbApiPortName, - (HANDLE) -1, - IMAGE_SUBSYSTEM_NATIVE, - & hSmApiPort); - if(!NT_SUCCESS(Status)) - { - DPRINT("SM: %s: SMDLL!SmConnectApiPort failed (Status=0x%08lx)\n",__FUNCTION__,Status); - return Status; - } - Status = SmCompleteSession (hSmApiPort, (HANDLE)0, hSmApiPort); - if(!NT_SUCCESS(Status)) - { - DPRINT("SM: %s: SMDLL!SmCompleteSession failed (Status=0x%08lx)\n",__FUNCTION__,Status); - return Status; - } - NtClose(hSmApiPort); - return Status; -} -#endif - /********************************************************************** * SmInitializeClientManagement/0 */ @@ -85,26 +52,21 @@ SmInitializeClientManagement (VOID) RtlInitializeCriticalSection(& SmpClientDirectory.Lock); SmpClientDirectory.Count = 0; SmpClientDirectory.Client = NULL; -#if 0 - /* Register IMAGE_SUBSYSTE_NATIVE to be managed by SM */ - return SmpRegisterSmss(); -#endif return STATUS_SUCCESS; } /********************************************************************** - * SmpLookupClient/1 + * SmCompleteClientInitialization/1 * * DESCRIPTION - * Lookup the subsystem server descriptor given its image ID. - * - * SIDE EFFECTS - * SmpClientDirectory.Lock is released only on success. + * Lookup the subsystem server descriptor given the process handle + * of the subsystem server process. */ -static PSM_CLIENT_DATA STDCALL -SmpLookupClient (USHORT SubsystemId) +NTSTATUS STDCALL +SmCompleteClientInitialization (HANDLE hProcess) { + NTSTATUS Status = STATUS_SUCCESS; PSM_CLIENT_DATA Client = NULL; DPRINT("SM: %s called\n", __FUNCTION__); @@ -115,13 +77,71 @@ SmpLookupClient (USHORT SubsystemId) Client = SmpClientDirectory.Client; while (NULL != Client) { - if (SubsystemId == Client->SubsystemId) + if (hProcess == Client->ServerProcess) { - RtlLeaveCriticalSection (& SmpClientDirectory.Lock); - return Client; + Client->Initialized = TRUE; + break; } Client = Client->Next; } + Status = STATUS_NOT_FOUND; + } + RtlLeaveCriticalSection (& SmpClientDirectory.Lock); + return Status; +} + +/********************************************************************** + * SmpLookupClient/1 PRIVATE + * + * DESCRIPTION + * Lookup the subsystem server descriptor given its image ID. + * + * SIDE EFFECTS + * SmpClientDirectory.Lock is released only on success. + */ +static PSM_CLIENT_DATA FASTCALL +SmpLookupClientUnsafe (USHORT SubsystemId, + PSM_CLIENT_DATA * Parent) +{ + PSM_CLIENT_DATA Client = NULL; + + DPRINT("SM: %s(%d) called\n", __FUNCTION__, SubsystemId); + + if(NULL != Parent) + { + *Parent = NULL; + } + if (SmpClientDirectory.Count > 0) + { + Client = SmpClientDirectory.Client; + while (NULL != Client) + { + if (SubsystemId == Client->SubsystemId) + { + break; + } + if(NULL != Parent) + { + *Parent = Client; + } + Client = Client->Next; + } + } + return Client; +} + +static PSM_CLIENT_DATA STDCALL +SmpLookupClient (USHORT SubsystemId) +{ + PSM_CLIENT_DATA Client = NULL; + + DPRINT("SM: %s called\n", __FUNCTION__); + + RtlEnterCriticalSection (& SmpClientDirectory.Lock); + Client = SmpLookupClientUnsafe (SubsystemId, NULL); + if(NULL != Client) + { + RtlLeaveCriticalSection (& SmpClientDirectory.Lock); } /* * Note that we do *not* release SmpClientDirectory.Lock here @@ -138,7 +158,7 @@ NTSTATUS STDCALL SmCreateClient(PSM_PORT_MESSAGE Request, PSM_CLIENT_DATA * ClientData) { PSM_CLIENT_DATA pClient = NULL; - PSM_CONNECT_DATA ConnectData = (PSM_CONNECT_DATA) ((PBYTE) Request) + sizeof (LPC_REQUEST); + PSM_CONNECT_DATA ConnectData = SmpGetConnectData (Request); ULONG SbApiPortNameSize = SM_CONNECT_DATA_SIZE(*Request); DPRINT("SM: %s called\n", __FUNCTION__); @@ -148,7 +168,7 @@ SmCreateClient(PSM_PORT_MESSAGE Request, PSM_CLIENT_DATA * ClientData) */ if (SmpLookupClient(ConnectData->Subsystem)) { - DPRINT("SMSS: %s: attempt to register again subsystem %d.\n", + DPRINT("SM: %s: attempt to register again subsystem %d.\n", __FUNCTION__, ConnectData->Subsystem); return STATUS_UNSUCCESSFUL; @@ -209,16 +229,51 @@ SmCreateClient(PSM_PORT_MESSAGE Request, PSM_CLIENT_DATA * ClientData) /********************************************************************** * SmpDestroyClient/1 + * + * 1. close any handle + * 2. kill client process + * 3. release resources */ NTSTATUS STDCALL SmDestroyClient (ULONG SubsystemId) { + NTSTATUS Status = STATUS_SUCCESS; + PSM_CLIENT_DATA Parent = NULL; + PSM_CLIENT_DATA Client = NULL; + DPRINT("SM: %s called\n", __FUNCTION__); RtlEnterCriticalSection (& SmpClientDirectory.Lock); - /* TODO */ + Client = SmpLookupClientUnsafe (SubsystemId, & Parent); + if(NULL == Client) + { + DPRINT1("SM: %s: del req for non existent subsystem (id=%d)\n", + __FUNCTION__, SubsystemId); + Status = STATUS_NOT_FOUND; + } + else + { + /* 1st in the list? */ + if(NULL == Parent) + { + SmpClientDirectory.Client = Client->Next; + } + else + { + if(NULL != Parent) + { + Parent->Next = Client->Next; + } else { + DPRINT1("SM: %s: n-th has no parent!\n", __FUNCTION__); + Status = STATUS_UNSUCCESSFUL; /* FIXME */ + } + } + /* TODO: send shutdown or kill */ + RtlFreeHeap (SmpHeap, 0, Client); + -- SmpClientDirectory.Count; + } RtlLeaveCriticalSection (& SmpClientDirectory.Lock); - return STATUS_SUCCESS; + return Status; } /* EOF */ diff --git a/reactos/subsys/smss/init.c b/reactos/subsys/smss/init.c index f3a1126c98e..a8880e253ec 100644 --- a/reactos/subsys/smss/init.c +++ b/reactos/subsys/smss/init.c @@ -39,7 +39,7 @@ SmpSignalInitEvent(VOID) NTSTATUS Status = STATUS_SUCCESS; OBJECT_ATTRIBUTES ObjectAttributes = {0}; UNICODE_STRING EventName ={0}; - HANDLE ReactOSInitEvent = NULL; + HANDLE ReactOSInitEvent = (HANDLE) 0; RtlInitUnicodeString (& EventName, L"\\ReactOSInitDone"); InitializeObjectAttributes(&ObjectAttributes, @@ -92,16 +92,13 @@ struct { {TRUE, SmInitializeClientManagement, "initialize client management"}, {TRUE, SmLoadSubsystems, "load subsystems"}, {FALSE, SmpSignalInitEvent, "open ReactOS init notification event"}, - {TRUE, SmRunCsrss, "run csrss"}, - {TRUE, SmRunWinlogon, "run winlogon"}, - {TRUE, SmInitializeDbgSs, "initialize DbgSs"} }; NTSTATUS InitSessionManager(VOID) { - int i; - NTSTATUS Status; + INT i = 0; + NTSTATUS Status = STATUS_SUCCESS; for (i=0; i < (sizeof InitRoutine / sizeof InitRoutine[0]); i++) { @@ -118,8 +115,6 @@ InitSessionManager(VOID) } } } - - return(STATUS_SUCCESS); } diff --git a/reactos/subsys/smss/initss.c b/reactos/subsys/smss/initss.c index e24035a8bfb..464cfe6d689 100644 --- a/reactos/subsys/smss/initss.c +++ b/reactos/subsys/smss/initss.c @@ -36,37 +36,65 @@ HANDLE hSmApiPort = (HANDLE) 0; -/* TODO: this file should be totally rewritten +/* TODO: * * a) look if a special option is set for smss.exe in * HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options - * - * b) make smss register with itself for IMAGE_SUBSYSTEM_NATIVE - * (programmatically) - * - * d) make smss initialize Debug (DBGSS) and Windows (CSRSS) as described - * in the registry key Required="Debug Windows" - * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems - * - * e) make optional subsystems loadable (again: they must be described in the registry - * key Optional="Posix Os2" to be allowed to run) */ /********************************************************************** + * SmpRegisterSmss/0 + * + * DESCRIPTION + * Make smss register with itself for IMAGE_SUBSYSTEM_NATIVE + * (programmatically). This also opens hSmApiPort to be used + * in loading required subsystems. */ -NTSTATUS -SmLoadSubsystems(VOID) + +static NTSTATUS +SmpRegisterSmss(VOID) { - SYSTEM_LOAD_AND_CALL_IMAGE ImageInfo; - NTSTATUS Status = STATUS_SUCCESS; - WCHAR Data [MAX_PATH + 1]; - ULONG DataLength = sizeof Data; - ULONG DataType = 0; + NTSTATUS Status = STATUS_SUCCESS; + UNICODE_STRING SbApiPortName = {0,0,NULL}; + + + DPRINT("SM: %s called\n",__FUNCTION__); + + RtlInitUnicodeString (& SbApiPortName, L""); + Status = SmConnectApiPort(& SbApiPortName, + (HANDLE) -1, /* SM has no SB port */ + IMAGE_SUBSYSTEM_NATIVE, + & hSmApiPort); + if(!NT_SUCCESS(Status)) + { + DPRINT("SM: %s: SMLIB!SmConnectApiPort failed (Status=0x%08lx)\n", + __FUNCTION__,Status); + return Status; + } + DPRINT("SM self registered\n"); + /* + * Note that you don't need to call complete session + * because connection handling code autocompletes + * the client structure for IMAGE_SUBSYSTEM_NATIVE. + */ + return Status; +} - DPRINT("SM: loading subsystems\n"); - - /* Load Kmode subsystem (aka win32k.sys) */ +/********************************************************************** + * SmpLoadKernelModeSubsystem/0 + */ +static NTSTATUS +SmpLoadKernelModeSubsystem (VOID) +{ + NTSTATUS Status = STATUS_SUCCESS; + WCHAR Data [MAX_PATH + 1]; + ULONG DataLength = sizeof Data; + ULONG DataType = 0; + + + DPRINT("SM: %s called\n", __FUNCTION__); + Status = SmLookupSubsystem (L"Kmode", Data, & DataLength, @@ -74,121 +102,101 @@ SmLoadSubsystems(VOID) TRUE); if((STATUS_SUCCESS == Status) && (DataLength > sizeof Data[0])) { - WCHAR ImagePath [MAX_PATH + 1] = {0}; + WCHAR ImagePath [MAX_PATH + 1] = {0}; + SYSTEM_LOAD_AND_CALL_IMAGE ImageInfo; wcscpy (ImagePath, L"\\??\\"); wcscat (ImagePath, Data); RtlZeroMemory (& ImageInfo, sizeof ImageInfo); RtlInitUnicodeString (& ImageInfo.ModuleName, ImagePath); Status = NtSetSystemInformation(SystemLoadAndCallImage, - & ImageInfo, - sizeof ImageInfo); + & ImageInfo, + sizeof ImageInfo); if(!NT_SUCCESS(Status)) { - DPRINT("SM: loading Kmode failed (Status=0x%08lx)\n", - Status); - return Status; + DPRINT("SM: %s: loading Kmode failed (Status=0x%08lx)\n", + __FUNCTION__, Status); } } - /* TODO: load Required subsystems (Debug Windows) */ return Status; } -NTSTATUS -SmRunCsrss(VOID) +/********************************************************************** + * SmpLoadRequiredSubsystems/0 + */ +static NTSTATUS +SmpLoadRequiredSubsystems (VOID) { - NTSTATUS Status; - UNICODE_STRING UnicodeString; - OBJECT_ATTRIBUTES ObjectAttributes; - RTL_PROCESS_INFO ProcessInfo; - HANDLE CsrssInitEvent; - WCHAR ImagePath [MAX_PATH]; + NTSTATUS Status = STATUS_SUCCESS; + WCHAR Data [MAX_PATH + 1]; + ULONG DataLength = sizeof Data; + ULONG DataType = 0; - DPRINT("SM: initializing csrss\n"); + + DPRINT("SM: %s called\n", __FUNCTION__); - /* Run csrss.exe */ - RtlRosInitUnicodeStringFromLiteral(&UnicodeString, - L"\\CsrssInitDone"); - InitializeObjectAttributes(&ObjectAttributes, - &UnicodeString, - EVENT_ALL_ACCESS, - 0, - NULL); - Status = NtCreateEvent(&CsrssInitEvent, - EVENT_ALL_ACCESS, - &ObjectAttributes, - NotificationEvent, - FALSE); - if (!NT_SUCCESS(Status)) - { - DbgPrint("Failed to create csrss notification event\n"); - } + RtlZeroMemory (Data, DataLength); + Status = SmLookupSubsystem (L"Required", + Data, + & DataLength, + & DataType, + FALSE); + if((STATUS_SUCCESS == Status) && (DataLength > sizeof Data[0])) + { + PWCHAR Name = NULL; + ULONG Offset = 0; + + for (Name = Data; (Offset < DataLength); ) + { + if(L'\0' != *Name) + { + UNICODE_STRING Program; - /* - * Start the Win32 subsystem (csrss.exe) - */ + /* Run the current program */ + RtlInitUnicodeString (& Program, Name); + Status = SmExecuteProgram (hSmApiPort, & Program); + if(!NT_SUCCESS(Status)) + { + DPRINT1("SM: %s failed to run '%S' program (Status=0x%08lx)\n", + __FUNCTION__, Name, Status); + } + /* Look for the next program */ + while ((L'\0' != *Name) && (Offset < DataLength)) + { + ++ Name; + ++ Offset; + } + } + ++ Name; + ++ Offset; + } + } - /* initialize executable path */ - wcscpy(ImagePath, L"\\??\\"); - wcscat(ImagePath, SharedUserData->NtSystemRoot); - wcscat(ImagePath, L"\\system32\\csrss.exe"); - - Status = SmCreateUserProcess(ImagePath, - L"", - FALSE, /* wait */ - NULL, - FALSE, /* terminate */ - & ProcessInfo); - - if (!NT_SUCCESS(Status)) - { - DPRINT("SM: %s: Loading csrss.exe failed!\n", __FUNCTION__); - return(Status); - } - - Status = NtWaitForSingleObject(CsrssInitEvent, - FALSE, - NULL); - - Children[CHILD_CSRSS] = ProcessInfo.ProcessHandle; - - return Status; + return Status; } +/********************************************************************** + * SmLoadSubsystems/0 + */ NTSTATUS -SmRunWinlogon(VOID) +SmLoadSubsystems(VOID) { - NTSTATUS Status = STATUS_SUCCESS; - RTL_PROCESS_INFO ProcessInfo; - WCHAR ImagePath [MAX_PATH]; + NTSTATUS Status = STATUS_SUCCESS; - /* - * Start the logon process (winlogon.exe) - */ + + DPRINT("SM: loading subsystems\n"); - DPRINT("SM: starting winlogon\n"); - - /* initialize executable path */ - wcscpy(ImagePath, L"\\??\\"); - wcscat(ImagePath, SharedUserData->NtSystemRoot); - wcscat(ImagePath, L"\\system32\\winlogon.exe"); - - Status = SmCreateUserProcess(ImagePath, - L"", - FALSE, /* wait */ - NULL, - FALSE, /* terminate */ - & ProcessInfo); - if (!NT_SUCCESS(Status)) - { - DPRINT("SM: %s: Loading winlogon.exe failed!\n", __FUNCTION__); - NtTerminateProcess(Children[CHILD_CSRSS], 0); - return(Status); - } - - Children[CHILD_WINLOGON] = ProcessInfo.ProcessHandle; - - return Status; + /* SM self registers */ + Status = SmpRegisterSmss(); + if(!NT_SUCCESS(Status)) return Status; + /* Load Kmode subsystem (aka win32k.sys) */ + Status = SmpLoadKernelModeSubsystem(); + if(!NT_SUCCESS(Status)) return Status; + /* Load Required subsystems (Debug Windows) */ + Status = SmpLoadRequiredSubsystems(); + if(!NT_SUCCESS(Status)) return Status; + /* done */ + return Status; } /* EOF */ diff --git a/reactos/subsys/smss/initwkdll.c b/reactos/subsys/smss/initwkdll.c index e853d8b214d..df35724ea27 100644 --- a/reactos/subsys/smss/initwkdll.c +++ b/reactos/subsys/smss/initwkdll.c @@ -60,7 +60,7 @@ SmpKnownDllsQueryRoutine(PWSTR ValueName, (HANDLE)Context, NULL); Status = NtOpenFile(&FileHandle, - SYNCHRONIZE | FILE_EXECUTE, + SYNCHRONIZE | FILE_EXECUTE | FILE_READ_DATA, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ, diff --git a/reactos/subsys/smss/makefile b/reactos/subsys/smss/makefile index 823310ec167..3845a347e7a 100644 --- a/reactos/subsys/smss/makefile +++ b/reactos/subsys/smss/makefile @@ -6,7 +6,7 @@ TARGET_TYPE = program TARGET_APPTYPE = native -TARGET_SDKLIBS = ntdll.a smdll.a +TARGET_SDKLIBS = smlib.a ntdll.a TARGET_NAME = smss @@ -21,7 +21,7 @@ TARGET_OBJECTS = $(TARGET_NAME).o \ init.o initheap.o initenv.o initobdir.o initdosdev.o \ initrun.o initmv.o initwkdll.o initpage.o initss.o \ initreg.o \ - smapi.o smapicomp.o smapiexec.o \ + smapi.o smapicomp.o smapiexec.o smapiquery.o \ client.o debug.o print.o include $(PATH_TO_TOP)/rules.mak diff --git a/reactos/subsys/smss/smapi.c b/reactos/subsys/smss/smapi.c index 78cac41ce83..a1578507f7e 100644 --- a/reactos/subsys/smss/smapi.c +++ b/reactos/subsys/smss/smapi.c @@ -34,14 +34,63 @@ SM_PORT_API SmApi [] = SmCompSes, /* smapicomp.c */ SmInvalid, /* obsolete */ SmInvalid, /* unknown */ - SmExecPgm /* smapiexec.c */ + SmExecPgm, /* smapiexec.c */ + SmQryInfo /* smapyqry.c */ }; +/* TODO: optimize this address computation (it should be done + * with a macro) */ +PSM_CONNECT_DATA FASTCALL SmpGetConnectData (PSM_PORT_MESSAGE Request) +{ + PLPC_MAX_MESSAGE LpcMaxMessage = (PLPC_MAX_MESSAGE) Request; + return (PSM_CONNECT_DATA) & LpcMaxMessage->Data[0]; +} + #if !defined(__USE_NT_LPC__) NTSTATUS STDCALL SmpHandleConnectionRequest (PSM_PORT_MESSAGE Request); #endif +/********************************************************************** + * SmpCallback/2 + * + * The SM calls back a previously connected subsystem process to + * authorizes it to bootstrap (initialize). The SM connects to a + * named LPC port which name was sent in the connection data by the + * candidate subsystem server process. + */ +static NTSTATUS +SmpCallbackServer (PSM_PORT_MESSAGE Request, + PSM_CLIENT_DATA ClientData) +{ + NTSTATUS Status = STATUS_SUCCESS; + PSM_CONNECT_DATA ConnectData = SmpGetConnectData (Request); + UNICODE_STRING CallbackPortName; + ULONG CallbackPortNameLength = SM_SB_NAME_MAX_LENGTH; /* TODO: compute length */ + + DPRINT("SM: %s called\n", __FUNCTION__); + + if(IMAGE_SUBSYSTEM_NATIVE == ConnectData->Subsystem) + { + DPRINT("SM: %s: we do not need calling back SM!\n", + __FUNCTION__); + return STATUS_SUCCESS; + } + RtlCopyMemory (ClientData->SbApiPortName, + ConnectData->SbName, + CallbackPortNameLength); + RtlInitUnicodeString (& CallbackPortName, + ClientData->SbApiPortName); + Status = NtConnectPort (& ClientData->SbApiPort, + & CallbackPortName, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + return Status; +} /********************************************************************** * NAME @@ -51,20 +100,21 @@ SmpHandleConnectionRequest (PSM_PORT_MESSAGE Request); * Entry point for the listener thread of LPC port "\SmApiPort". */ VOID STDCALL -SmpApiConnectedThread(PVOID dummy) +SmpApiConnectedThread(PVOID pConnectedPort) { NTSTATUS Status = STATUS_SUCCESS; PVOID Unknown = NULL; PLPC_MESSAGE Reply = NULL; SM_PORT_MESSAGE Request = {{0}}; + HANDLE ConnectedPort = * (PHANDLE) pConnectedPort; - DPRINT("SM: %s running\n",__FUNCTION__); + DPRINT("SM: %s called\n", __FUNCTION__); while (TRUE) { DPRINT("SM: %s: waiting for message\n",__FUNCTION__); - Status = NtReplyWaitReceivePort(SmApiPort, + Status = NtReplyWaitReceivePort(ConnectedPort, (PULONG) & Unknown, Reply, (PLPC_MESSAGE) & Request); @@ -98,8 +148,13 @@ SmpApiConnectedThread(PVOID dummy) Reply = (PLPC_MESSAGE) & Request; } } + } else { + /* LPC failed */ + break; } } + NtClose (ConnectedPort); + NtTerminateThread (NtCurrentThread(), Status); } /********************************************************************** @@ -115,75 +170,112 @@ SmpApiConnectedThread(PVOID dummy) NTSTATUS STDCALL SmpHandleConnectionRequest (PSM_PORT_MESSAGE Request) { -#if defined(__USE_NT_LPC__) + PSM_CONNECT_DATA ConnectData = SmpGetConnectData (Request); NTSTATUS Status = STATUS_SUCCESS; + BOOL Accept = FALSE; PSM_CLIENT_DATA ClientData = NULL; + HANDLE hClientDataApiPort = (HANDLE) 0; + PHANDLE ClientDataApiPort = & hClientDataApiPort; + HANDLE hClientDataApiPortThread = (HANDLE) 0; + PHANDLE ClientDataApiPortThread = & hClientDataApiPortThread; PVOID Context = NULL; - DPRINT("SM: %s called\n",__FUNCTION__); + DPRINT("SM: %s called:\n SubSystemID=%d\n SbName=\"%S\"\n", + __FUNCTION__, ConnectData->Subsystem, ConnectData->SbName); - /* - * SmCreateClient/2 is called here explicitly to *fail*. - * If it succeeds, there is something wrong in the - * connection request. An environment subsystem *never* - * registers twice. (Security issue: maybe we will - * write this event into the security log). - */ - Status = SmCreateClient (Request, & ClientData); - if(STATUS_SUCCESS == Status) + if(sizeof (SM_CONNECT_DATA) == Request->Header.DataSize) { - /* OK: the client is an environment subsystem - * willing to manage a free image type. - * Accept it. - */ - Status = NtAcceptConnectPort (& ClientData->ApiPort, - Context, - (PLPC_MESSAGE) Request, - TRUE, //accept - NULL, - NULL); - if(NT_SUCCESS(Status)) + if(IMAGE_SUBSYSTEM_UNKNOWN == ConnectData->Subsystem) { - Status = NtCompleteConnectPort(ClientData->ApiPort); + /* + * This is not a call to register an image set, + * but a simple connection request from a process + * that will use the SM API. + */ + DPRINT("SM: %s: simple request\n", __FUNCTION__); + ClientDataApiPort = & hClientDataApiPort; + ClientDataApiPortThread = & hClientDataApiPortThread; + Accept = TRUE; + } else { + DPRINT("SM: %s: request to register an image set\n", __FUNCTION__); + /* + * Reject GUIs classes: only odd subsystem IDs are + * allowed to register here (tty mode images). + */ + if(1 == (ConnectData->Subsystem % 2)) + { + DPRINT("SM: %s: id = %d\n", __FUNCTION__, ConnectData->Subsystem); + /* + * SmCreateClient/2 is called here explicitly to *fail*. + * If it succeeds, there is something wrong in the + * connection request. An environment subsystem *never* + * registers twice. (security issue) + */ + Status = SmCreateClient (Request, & ClientData); + if(STATUS_SUCCESS == Status) + { + DPRINT("SM: %s: ClientData = 0x%08lx\n", + __FUNCTION__, ClientData); + /* + * OK: the client is an environment subsystem + * willing to manage a free image type. + */ + ClientDataApiPort = & ClientData->ApiPort; + ClientDataApiPortThread = & ClientData->ApiPortThread; + /* + * Call back the candidate environment subsystem + * server (use the port name sent in in the + * connection request message). + */ + Status = SmpCallbackServer (Request, ClientData); + if(NT_SUCCESS(Status)) + { + DPRINT("SM: %s: SmpCallbackServer OK\n", + __FUNCTION__); + Accept = TRUE; + } else { + DPRINT("SM: %s: SmpCallbackServer failed (Status=%08lx)\n", + __FUNCTION__, Status); + Status = SmDestroyClient (ConnectData->Subsystem); + } + } + } } - return STATUS_SUCCESS; - } else { - /* Reject the subsystem */ - Status = NtAcceptConnectPort (& ClientData->ApiPort, - Context, - (PLPC_MESSAGE) Request, - FALSE, //reject - NULL, - NULL); } + DPRINT("SM: %s: before NtAcceptConnectPort\n", __FUNCTION__); +#if defined(__USE_NT_LPC__) + Status = NtAcceptConnectPort (ClientDataApiPort, + Context, + (PLPC_MESSAGE) Request, + Accept, + NULL, + NULL); #else /* ReactOS LPC */ - NTSTATUS Status = STATUS_SUCCESS; - PSM_CLIENT_DATA ClientData = NULL; - - DPRINT("SM: %s called\n",__FUNCTION__); - - Status = SmCreateClient (Request, & ClientData); - if(STATUS_SUCCESS == Status) + Status = NtAcceptConnectPort (ClientDataApiPort, + SmApiPort, // ROS LPC requires the listen port here + Context, + Accept, + NULL, + NULL); +#endif + if(Accept) { - Status = NtAcceptConnectPort (& ClientData->ApiPort, - SmApiPort, - NULL, - TRUE, //accept - NULL, - NULL); - if (!NT_SUCCESS(Status)) + if(!NT_SUCCESS(Status)) { DPRINT1("SM: %s: NtAcceptConnectPort() failed (Status=0x%08lx)\n", - __FUNCTION__, Status); + __FUNCTION__, Status); return Status; } else { - Status = NtCompleteConnectPort(ClientData->ApiPort); + DPRINT("SM: %s: completing conn req\n", __FUNCTION__); + Status = NtCompleteConnectPort (*ClientDataApiPort); if (!NT_SUCCESS(Status)) { DPRINT1("SM: %s: NtCompleteConnectPort() failed (Status=0x%08lx)\n", __FUNCTION__, Status); return Status; } +#if !defined(__USE_NT_LPC__) /* ReactOS LPC */ + DPRINT("SM: %s: server side comm port thread (ROS LPC)\n", __FUNCTION__); Status = RtlCreateUserThread(NtCurrentProcess(), NULL, FALSE, @@ -191,8 +283,8 @@ SmpHandleConnectionRequest (PSM_PORT_MESSAGE Request) NULL, NULL, (PTHREAD_START_ROUTINE) SmpApiConnectedThread, - ClientData->ApiPort, - & ClientData->ApiPortThread, + ClientDataApiPort, + ClientDataApiPortThread, NULL); if (!NT_SUCCESS(Status)) { @@ -200,18 +292,11 @@ SmpHandleConnectionRequest (PSM_PORT_MESSAGE Request) __FUNCTION__, Status); return Status; } +#endif } - return STATUS_SUCCESS; - } else { - /* Reject the subsystem */ - Status = NtAcceptConnectPort (& ClientData->ApiPort, - SmApiPort, - NULL, - FALSE, //reject - NULL, - NULL); + Status = STATUS_SUCCESS; } -#endif /* defined __USE_NT_LPC__ */ + DPRINT("SM: %s done\n", __FUNCTION__); return Status; } @@ -231,6 +316,8 @@ SmpApiThread (HANDLE ListeningPort) { NTSTATUS Status = STATUS_SUCCESS; LPC_MAX_MESSAGE Request = {{0}}; + + DPRINT("SM: %s called\n", __FUNCTION__); while (TRUE) { diff --git a/reactos/subsys/smss/smapicomp.c b/reactos/subsys/smss/smapicomp.c index a655ef7cf4f..52ef9613954 100644 --- a/reactos/subsys/smss/smapicomp.c +++ b/reactos/subsys/smss/smapicomp.c @@ -4,10 +4,26 @@ * * Reactos Session Manager * + * -------------------------------------------------------------------- + * + * This software 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. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING.LIB. If not, write + * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + * MA 02139, USA. + * + * -------------------------------------------------------------------- */ - #include "smss.h" -#include #define NDEBUG #include @@ -18,9 +34,19 @@ */ SMAPI(SmCompSes) { - DPRINT("SM: %s called\n",__FUNCTION__); - Request->Status = STATUS_NOT_IMPLEMENTED; - return STATUS_SUCCESS; + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("SM: %s called from [%lx|%lx]\n", + __FUNCTION__, + Request->ClientId.UniqueProcessId, + Request->ClientId.UniqueThreadId); + + Status = SmCompleteClientInitialization (Request->Header.ClientId.UniqueProcess); + if(!NT_SUCCESS(Status)) + { + Request->Status = STATUS_UNSUCCESSFUL; + } + return Status; } diff --git a/reactos/subsys/smss/smapiexec.c b/reactos/subsys/smss/smapiexec.c index 9d1f119fe1e..7efc800b110 100644 --- a/reactos/subsys/smss/smapiexec.c +++ b/reactos/subsys/smss/smapiexec.c @@ -81,22 +81,18 @@ SmCreateUserProcess (LPWSTR ImagePath, NULL, NULL, pProcessInfo); + + RtlDestroyProcessParameters (ProcessParameters); + if (!NT_SUCCESS(Status)) { - CHAR AnsiBuffer [MAX_PATH]; - INT i = 0; - for(i=0;ImagePathString.Buffer[i];i++) - { - /* raw U -> A */ - AnsiBuffer [i] = (CHAR) (ImagePathString.Buffer[i] & 0xff); - } - - DPRINT1("SM: %s: Running \"%s\" failed (Status=0x%08lx)\n", - AnsiBuffer, __FUNCTION__, Status); + DPRINT1("SM: %s: Running \"%S\" failed (Status=0x%08lx)\n", + __FUNCTION__, ImagePathString.Buffer, Status); return Status; } - RtlDestroyProcessParameters (ProcessParameters); + ZwResumeThread(pProcessInfo->ThreadHandle, NULL); + /* Wait for process termination */ if(WaitForIt) @@ -145,7 +141,7 @@ SmLookupSubsystem (IN PWSTR Name, OBJECT_ATTRIBUTES Oa = {0}; HANDLE hKey = (HANDLE) 0; - DPRINT("SM: %s called\n", __FUNCTION__); + DPRINT("SM: %s(Name='%S') called\n", __FUNCTION__, Name); /* * Prepare the key name to scan and * related object attributes. @@ -268,7 +264,7 @@ SMAPI(SmExecPgm) RtlCopyMemory (Name, ExecPgm->Name, (sizeof ExecPgm->Name[0] * ExecPgm->NameLength)); - DPRINT("SM: %s: Name=[%wZ]\n", __FUNCTION__, Name); + DPRINT("SM: %s: Name='%S'\n", __FUNCTION__, Name); /* * Check if program name is internal * (Is this correct? Debug is in the registry too) @@ -285,18 +281,23 @@ SMAPI(SmExecPgm) } else { - WCHAR ImagePath [1024] = {0}; - ULONG ImagePathLength = sizeof ImagePath; - ULONG ImagePathType = REG_EXPAND_SZ; + WCHAR Data [MAX_PATH + 1] = {0}; + ULONG DataLength = sizeof Data; + ULONG DataType = REG_EXPAND_SZ; /* Lookup Name in the registry */ Status = SmLookupSubsystem (Name, - ImagePath, - & ImagePathLength, - & ImagePathType, + Data, + & DataLength, + & DataType, TRUE); /* expand */ if(NT_SUCCESS(Status)) { + WCHAR ImagePath [MAX_PATH + 1] = {0}; + + wcscpy (ImagePath, L"\\??\\"); + wcscat (ImagePath, Data); + /* Create native process */ Request->Status = SmCreateUserProcess(ImagePath, L"", /* FIXME */ diff --git a/reactos/subsys/smss/smapiquery.c b/reactos/subsys/smss/smapiquery.c new file mode 100644 index 00000000000..e93e593405a --- /dev/null +++ b/reactos/subsys/smss/smapiquery.c @@ -0,0 +1,46 @@ +/* $Id: $ + * + * smapiquery.c - SM_API_QUERY_INFORMATION + * + * Reactos Session Manager + * + * -------------------------------------------------------------------- + * + * This software 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. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING.LIB. If not, write + * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + * MA 02139, USA. + * + * -------------------------------------------------------------------- + */ +#include "smss.h" + +#define NDEBUG +#include + + +/********************************************************************** + * SmQryInfo/1 API + */ +SMAPI(SmQryInfo) +{ + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("SM: %s called\n", __FUNCTION__); + + Request->Status = STATUS_NOT_IMPLEMENTED; + return Status; +} + + +/* EOF */ diff --git a/reactos/subsys/smss/smss.c b/reactos/subsys/smss/smss.c index d95e54a39c0..b03197c0a70 100644 --- a/reactos/subsys/smss/smss.c +++ b/reactos/subsys/smss/smss.c @@ -40,7 +40,6 @@ NtProcessStartup(PPEB Peb) NTSTATUS Status; PROCESS_BASIC_INFORMATION PBI = {0}; - DisplayString(L"SMSS\n"); PrintString("ReactOS Session Manager %s (Build %s)\n", KERNEL_RELEASE_STR, KERNEL_VERSION_BUILD_STR); @@ -57,6 +56,7 @@ NtProcessStartup(PPEB Peb) } /* Initialize the system */ Status = InitSessionManager(); +#if 0 if (!NT_SUCCESS(Status)) { int i; @@ -91,6 +91,8 @@ ByeBye: 0,0,0,0,0); // NtTerminateProcess(NtCurrentProcess(), 0); +#endif + NtTerminateThread(NtCurrentThread(), Status); } /* EOF */ diff --git a/reactos/subsys/smss/smss.h b/reactos/subsys/smss/smss.h index 2bd3480616e..eae61437e30 100644 --- a/reactos/subsys/smss/smss.h +++ b/reactos/subsys/smss/smss.h @@ -49,12 +49,14 @@ NTSTATUS SmInitializeRegistry(VOID); /* initss.c */ NTSTATUS SmLoadSubsystems(VOID); -NTSTATUS SmRunCsrss(VOID); -NTSTATUS SmRunWinlogon(VOID); /* smapi.c */ #define SMAPI(n) \ NTSTATUS FASTCALL n (PSM_PORT_MESSAGE Request) +PSM_CONNECT_DATA FASTCALL SmpGetConnectData (PSM_PORT_MESSAGE); +NTSTATUS SmCreateApiPort(VOID); +VOID STDCALL SmpApiThread(PVOID); + /* smapiexec.c */ NTSTATUS STDCALL SmCreateUserProcess(LPWSTR ImagePath, @@ -74,8 +76,8 @@ NTSTATUS FASTCALL SmExecPgm(PSM_PORT_MESSAGE); /* smapicomp.c */ NTSTATUS FASTCALL SmCompSes(PSM_PORT_MESSAGE); -NTSTATUS SmCreateApiPort(VOID); -VOID STDCALL SmpApiThread(PVOID); +/* smapiquery.c */ +NTSTATUS FASTCALL SmQryInfo(PSM_PORT_MESSAGE); /* client.c */ typedef struct _SM_CLIENT_DATA @@ -91,9 +93,9 @@ typedef struct _SM_CLIENT_DATA } SM_CLIENT_DATA, *PSM_CLIENT_DATA; NTSTATUS SmInitializeClientManagement(VOID); -NTSTATUS SmpRegisterSmss(VOID); NTSTATUS STDCALL SmCreateClient(PSM_PORT_MESSAGE,PSM_CLIENT_DATA*); NTSTATUS STDCALL SmDestroyClient(ULONG); +NTSTATUS STDCALL SmCompleteClientInitialization (HANDLE hProcess); /* debug.c */ extern HANDLE DbgSsApiPort; diff --git a/reactos/subsys/smss/smss.xml b/reactos/subsys/smss/smss.xml index 6d6ba167f7d..626b1c2d35f 100644 --- a/reactos/subsys/smss/smss.xml +++ b/reactos/subsys/smss/smss.xml @@ -21,6 +21,7 @@ smapi.c smapicomp.c smapiexec.c + smapiquery.c smss.c smss.rc diff --git a/reactos/subsys/system/calc/De.rc b/reactos/subsys/system/calc/De.rc index aac23e3693b..2f0146d12de 100644 --- a/reactos/subsys/system/calc/De.rc +++ b/reactos/subsys/system/calc/De.rc @@ -1,155 +1,155 @@ -/* - * WineCalc (DE.rc) - * - * Copyright 2005 Rouven Wessling - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "windows.h" -#include "resource.h" -#include "winecalc.h" - -LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT - -STRINGTABLE DISCARDABLE -{ - IDS_APPNAME, "Rechner" - - IDS_COPYRIGHT1, "Calculator 5.0. Lizensiert unter der LGPL 2" - IDS_COPYRIGHT2 "Copyright 2003, James Briggs" - IDS_COPYRIGHT3 "San Jose, California, USA" - IDS_COPYRIGHT4 "james@ActionMessage.com" - IDS_COPYRIGHT5 "http://www.ActionMessage.com/winecalc/" - - IDS_BTN_BACKSPACE, "Rücktaste" - IDS_BTN_CLEAR_ENTRY, "CE" - IDS_BTN_CLEAR_ALL, "C" - IDS_BTN_MEM_CLEAR, "MC" - IDS_BTN_MEM_RECALL, "MR" - IDS_BTN_MEM_STORE, "MS" - IDS_BTN_MEM_PLUS, "M+" - IDS_BTN_MEM_STATUS_M, "M" - IDS_BTN_SQRT, "sqrt" - IDS_ERR_INVALID_INPUT, "Ungültige Eingabe für funktion." - IDS_ERR_DIVIDE_BY_ZERO, "Teilen durch 0 unmöglich." - IDS_ERR_UNDEFINED, "Das Ergebnis der funktion is undefiniert." -} - -MAIN_MENU MENU DISCARDABLE -{ - POPUP "&Bearbeiten" { - MENUITEM "&Kopieren Ctrl+C", IDM_COPY - MENUITEM "&Einfügen Ctrl+V", IDM_PASTE - } - POPUP "&Ansicht" { - MENUITEM "&Standard", IDM_MODE_STANDARD - MENUITEM "&Wissenschaftlich", IDM_MODE_SCIENTIFIC - MENUITEM SEPARATOR - MENUITEM "&Zifferngruppierung", IDM_DIGIT_GROUPING - } - POPUP "&?" { - MENUITEM "&Hilfethemen", IDM_HELP_TOPICS - MENUITEM SEPARATOR - MENUITEM "&Info", IDM_ABOUT - } -} - -SCIMS_MENU MENU DISCARDABLE -{ - POPUP "&Bearbeiten" { - MENUITEM "&Kopiren Ctrl+C", IDM_COPY - MENUITEM "&Einfügen Ctrl+V", IDM_PASTE - } - POPUP "&Anzeige" { - MENUITEM "&Standard", IDM_MODE_STANDARD - MENUITEM "&Wissenschaftlich", IDM_MODE_SCIENTIFIC - MENUITEM SEPARATOR - MENUITEM "&Hex\tF5", ID_CALC_NS_HEX - MENUITEM "De&zimal\tF6", ID_CALC_NS_DEC - MENUITEM "O&ktal\tF7", ID_CALC_NS_OCT - MENUITEM "&Binär\tF8", ID_CALC_NS_BIN - MENUITEM SEPARATOR - MENUITEM "&Deg\tF2", ID_CALC_MS_DEGREES - MENUITEM "&Rad\tF3", ID_CALC_MS_RADIANS - MENUITEM "&Grad\tF4", ID_CALC_MS_GRADS - MENUITEM SEPARATOR - MENUITEM "Zifferngr&uppierung", IDM_DIGIT_GROUPING - } - POPUP "&?" { - MENUITEM "&Hilfethemen", IDM_HELP_TOPICS - MENUITEM SEPARATOR - MENUITEM "&Info", IDM_ABOUT - } -} - -SCIWS_MENU MENU DISCARDABLE -{ - POPUP "&Bearbeiten" { - MENUITEM "&Kopieren Ctrl+C", IDM_COPY - MENUITEM "&Einfügen Ctrl+V", IDM_PASTE - } - POPUP "&Anzeige" { - MENUITEM "&Standard", IDM_MODE_STANDARD - MENUITEM "&Wissenschaftlich", IDM_MODE_SCIENTIFIC - MENUITEM SEPARATOR - MENUITEM "&Hex\tF5", ID_CALC_NS_HEX - MENUITEM "De&zimal\tF6", ID_CALC_NS_DEC - MENUITEM "O&ktal\tF7", ID_CALC_NS_OCT - MENUITEM "&Binär\tF8", ID_CALC_NS_BIN - MENUITEM SEPARATOR - MENUITEM "&Qword\tF12", ID_CALC_WS_QWORD - MENUITEM "Dw&ord\tF2", ID_CALC_WS_DWORD - MENUITEM "Wo&rd\tF3", ID_CALC_WS_WORD - MENUITEM "&Byte\tF4", ID_CALC_WS_BYTE - MENUITEM SEPARATOR - MENUITEM "&Zifferngruppierung", IDM_DIGIT_GROUPING - } - POPUP "&?" { - MENUITEM "&Hilfethemen", IDM_HELP_TOPICS - MENUITEM SEPARATOR - MENUITEM "&Info", IDM_ABOUT - } -} - -MAIN_MENU ACCELERATORS -BEGIN - VK_F1, IDV_HELP, VIRTKEY -END - -DLG_ABOUT DIALOG 12,0,120,82 -CAPTION "Info über GNU winecalc" -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -BEGIN - DEFPUSHBUTTON "OK", IDOK, 42, 60, 30, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP -END - -WHATS_THIS_MENU MENU DISCARDABLE -{ - POPUP "" { - MENUITEM "Was ist das?", IDM_WHATS_THIS - } -} - -DLG_STATS DIALOG 12,0,125,78 -CAPTION "Statistics Box" -FONT 9, "Tahoma" -STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE -BEGIN - DEFPUSHBUTTON "&RET", ID_STATS_RET, 4, 52, 25, 13, WS_TABSTOP | WS_GROUP - PUSHBUTTON "&LOAD", ID_STATS_LOAD, 34, 52, 25, 13, WS_TABSTOP | WS_GROUP - PUSHBUTTON "&CD" ID_STATS_CD, 64, 52, 25, 13, WS_TABSTOP | WS_GROUP - PUSHBUTTON "C&AD", ID_STATS_CAD, 94, 52, 25, 13, WS_TABSTOP | WS_GROUP -END +/* + * WineCalc (DE.rc) + * + * Copyright 2005 Rouven Wessling + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "windows.h" +#include "resource.h" +#include "winecalc.h" + +LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_APPNAME, "Rechner" + + IDS_COPYRIGHT1, "Rechner 5.0. Lizenziert unter der LGPL 2" + IDS_COPYRIGHT2 "Copyright 2003, James Briggs" + IDS_COPYRIGHT3 "San Jose, California, USA" + IDS_COPYRIGHT4 "james@ActionMessage.com" + IDS_COPYRIGHT5 "http://www.ActionMessage.com/winecalc/" + + IDS_BTN_BACKSPACE, "Rücktaste" + IDS_BTN_CLEAR_ENTRY, "CE" + IDS_BTN_CLEAR_ALL, "C" + IDS_BTN_MEM_CLEAR, "MC" + IDS_BTN_MEM_RECALL, "MR" + IDS_BTN_MEM_STORE, "MS" + IDS_BTN_MEM_PLUS, "M+" + IDS_BTN_MEM_STATUS_M, "M" + IDS_BTN_SQRT, "sqrt" + IDS_ERR_INVALID_INPUT, "Ungültige Eingabe für Funktion." + IDS_ERR_DIVIDE_BY_ZERO, "Teilen durch 0 unmöglich." + IDS_ERR_UNDEFINED, "Das Ergebnis der Funktion is undefiniert." +} + +MAIN_MENU MENU DISCARDABLE +{ + POPUP "&Bearbeiten" { + MENUITEM "&Kopieren Ctrl+C", IDM_COPY + MENUITEM "&Einfügen Ctrl+V", IDM_PASTE + } + POPUP "&Ansicht" { + MENUITEM "&Standard", IDM_MODE_STANDARD + MENUITEM "&Wissenschaftlich", IDM_MODE_SCIENTIFIC + MENUITEM SEPARATOR + MENUITEM "&Zifferngruppierung", IDM_DIGIT_GROUPING + } + POPUP "&?" { + MENUITEM "&Hilfethemen", IDM_HELP_TOPICS + MENUITEM SEPARATOR + MENUITEM "&Info", IDM_ABOUT + } +} + +SCIMS_MENU MENU DISCARDABLE +{ + POPUP "&Bearbeiten" { + MENUITEM "&Kopieren Ctrl+C", IDM_COPY + MENUITEM "&Einfügen Ctrl+V", IDM_PASTE + } + POPUP "&Anzeige" { + MENUITEM "&Standard", IDM_MODE_STANDARD + MENUITEM "&Wissenschaftlich", IDM_MODE_SCIENTIFIC + MENUITEM SEPARATOR + MENUITEM "&Hex\tF5", ID_CALC_NS_HEX + MENUITEM "De&zimal\tF6", ID_CALC_NS_DEC + MENUITEM "O&ktal\tF7", ID_CALC_NS_OCT + MENUITEM "&Binär\tF8", ID_CALC_NS_BIN + MENUITEM SEPARATOR + MENUITEM "&Deg\tF2", ID_CALC_MS_DEGREES + MENUITEM "&Rad\tF3", ID_CALC_MS_RADIANS + MENUITEM "&Grad\tF4", ID_CALC_MS_GRADS + MENUITEM SEPARATOR + MENUITEM "Zifferngr&uppierung", IDM_DIGIT_GROUPING + } + POPUP "&?" { + MENUITEM "&Hilfethemen", IDM_HELP_TOPICS + MENUITEM SEPARATOR + MENUITEM "&Info", IDM_ABOUT + } +} + +SCIWS_MENU MENU DISCARDABLE +{ + POPUP "&Bearbeiten" { + MENUITEM "&Kopieren Ctrl+C", IDM_COPY + MENUITEM "&Einfügen Ctrl+V", IDM_PASTE + } + POPUP "&Anzeige" { + MENUITEM "&Standard", IDM_MODE_STANDARD + MENUITEM "&Wissenschaftlich", IDM_MODE_SCIENTIFIC + MENUITEM SEPARATOR + MENUITEM "&Hex\tF5", ID_CALC_NS_HEX + MENUITEM "De&zimal\tF6", ID_CALC_NS_DEC + MENUITEM "O&ktal\tF7", ID_CALC_NS_OCT + MENUITEM "&Binär\tF8", ID_CALC_NS_BIN + MENUITEM SEPARATOR + MENUITEM "&Qword\tF12", ID_CALC_WS_QWORD + MENUITEM "Dw&ord\tF2", ID_CALC_WS_DWORD + MENUITEM "Wo&rd\tF3", ID_CALC_WS_WORD + MENUITEM "&Byte\tF4", ID_CALC_WS_BYTE + MENUITEM SEPARATOR + MENUITEM "&Zifferngruppierung", IDM_DIGIT_GROUPING + } + POPUP "&?" { + MENUITEM "&Hilfethemen", IDM_HELP_TOPICS + MENUITEM SEPARATOR + MENUITEM "&Info", IDM_ABOUT + } +} + +MAIN_MENU ACCELERATORS +BEGIN + VK_F1, IDV_HELP, VIRTKEY +END + +DLG_ABOUT DIALOG 12,0,120,82 +CAPTION "Info über GNU winecalc" +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +BEGIN + DEFPUSHBUTTON "OK", IDOK, 42, 60, 30, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + +WHATS_THIS_MENU MENU DISCARDABLE +{ + POPUP "" { + MENUITEM "Was ist das?", IDM_WHATS_THIS + } +} + +DLG_STATS DIALOG 12,0,125,78 +CAPTION "Statistics Box" +FONT 9, "Tahoma" +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE +BEGIN + DEFPUSHBUTTON "&RET", ID_STATS_RET, 4, 52, 25, 13, WS_TABSTOP | WS_GROUP + PUSHBUTTON "&LOAD", ID_STATS_LOAD, 34, 52, 25, 13, WS_TABSTOP | WS_GROUP + PUSHBUTTON "&CD" ID_STATS_CD, 64, 52, 25, 13, WS_TABSTOP | WS_GROUP + PUSHBUTTON "C&AD", ID_STATS_CAD, 94, 52, 25, 13, WS_TABSTOP | WS_GROUP +END diff --git a/reactos/subsys/system/calc/Sv.rc b/reactos/subsys/system/calc/Sv.rc new file mode 100644 index 00000000000..a1f33552f65 --- /dev/null +++ b/reactos/subsys/system/calc/Sv.rc @@ -0,0 +1,155 @@ +/* + * WineCalc (En.rc) + * + * Copyright 2005 David Nordenberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "windows.h" +#include "resource.h" +#include "winecalc.h" + +LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_APPNAME, "Kalkylatorn" + + IDS_COPYRIGHT1, "Kalkylatorn 5.0. Licenserad under LGPL 2" + IDS_COPYRIGHT2 "Copyright 2003, James Briggs" + IDS_COPYRIGHT3 "San Jose, California, USA" + IDS_COPYRIGHT4 "james@ActionMessage.com" + IDS_COPYRIGHT5 "http://www.ActionMessage.com/winecalc/" + + IDS_BTN_BACKSPACE, "Backsteg" + IDS_BTN_CLEAR_ENTRY, "CE" + IDS_BTN_CLEAR_ALL, "C" + IDS_BTN_MEM_CLEAR, "MC" + IDS_BTN_MEM_RECALL, "MR" + IDS_BTN_MEM_STORE, "MS" + IDS_BTN_MEM_PLUS, "M+" + IDS_BTN_MEM_STATUS_M, "M" + IDS_BTN_SQRT, "sqrt" + IDS_ERR_INVALID_INPUT, "Felaktig indata för funktion." + IDS_ERR_DIVIDE_BY_ZERO, "Kan inte dividera med noll." + IDS_ERR_UNDEFINED, "Funktionens resultat är odefinerat." +} + +MAIN_MENU MENU DISCARDABLE +{ + POPUP "&Redigera" { + MENUITEM "&Kopiera\tCtrl+C", IDM_COPY + MENUITEM "K&listra in\tCtrl+V", IDM_PASTE + } + POPUP "&Visa" { + MENUITEM "Standard", IDM_MODE_STANDARD + MENUITEM "Vetenskaplig", IDM_MODE_SCIENTIFIC + MENUITEM SEPARATOR + MENUITEM "Siffergruppering", IDM_DIGIT_GROUPING + } + POPUP "&Hjälp" { + MENUITEM "Hjälpavsnitt", IDM_HELP_TOPICS + MENUITEM SEPARATOR + MENUITEM "Om Kalkylatorn", IDM_ABOUT + } +} + +SCIMS_MENU MENU DISCARDABLE +{ + POPUP "&Redigera" { + MENUITEM "&Kopiera\tCtrl+C", IDM_COPY + MENUITEM "K&listra in\tCtrl+V", IDM_PASTE + } + POPUP "&Visa" { + MENUITEM "Standard", IDM_MODE_STANDARD + MENUITEM "Vetenskaplig", IDM_MODE_SCIENTIFIC + MENUITEM SEPARATOR + MENUITEM "Hex\tF5", ID_CALC_NS_HEX + MENUITEM "Decimal\tF6", ID_CALC_NS_DEC + MENUITEM "Oktal\tF7", ID_CALC_NS_OCT + MENUITEM "Binär\tF8", ID_CALC_NS_BIN + MENUITEM SEPARATOR + MENUITEM "Grader\tF2", ID_CALC_MS_DEGREES + MENUITEM "Radianer\tF3", ID_CALC_MS_RADIANS + MENUITEM "Gradienter\tF4", ID_CALC_MS_GRADS + MENUITEM SEPARATOR + MENUITEM "Siffergruppering", IDM_DIGIT_GROUPING + } + POPUP "&Hjälp" { + MENUITEM "Hjälpavsnitt", IDM_HELP_TOPICS + MENUITEM SEPARATOR + MENUITEM "Om Kalkylatorn", IDM_ABOUT + } +} + +SCIWS_MENU MENU DISCARDABLE +{ + POPUP "&Redigera" { + MENUITEM "&Kopiera\tCtrl+C", IDM_COPY + MENUITEM "K&listra in\tCtrl+V", IDM_PASTE + } + POPUP "&Visa" { + MENUITEM "Standard", IDM_MODE_STANDARD + MENUITEM "Vetenskaplig", IDM_MODE_SCIENTIFIC + MENUITEM SEPARATOR + MENUITEM "Hex\tF5", ID_CALC_NS_HEX + MENUITEM "Decimal\tF6", ID_CALC_NS_DEC + MENUITEM "Oktal\tF7", ID_CALC_NS_OCT + MENUITEM "Binär\tF8", ID_CALC_NS_BIN + MENUITEM SEPARATOR + MENUITEM "Qword\tF12", ID_CALC_WS_QWORD + MENUITEM "Dword\tF2", ID_CALC_WS_DWORD + MENUITEM "Word\tF3", ID_CALC_WS_WORD + MENUITEM "Byte\tF4", ID_CALC_WS_BYTE + MENUITEM SEPARATOR + MENUITEM "Siffergruppering", IDM_DIGIT_GROUPING + } + POPUP "&Hjälp" { + MENUITEM "Hjälpavsnitt", IDM_HELP_TOPICS + MENUITEM SEPARATOR + MENUITEM "Om Kalkylatorn", IDM_ABOUT + } +} + +MAIN_MENU ACCELERATORS +BEGIN + VK_F1, IDV_HELP, VIRTKEY +END + +DLG_ABOUT DIALOG 12,0,120,82 +CAPTION "Om GNU Kalkylatorn (winecalc)" +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +BEGIN + DEFPUSHBUTTON "OK", IDOK, 42, 60, 30, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + +WHATS_THIS_MENU MENU DISCARDABLE +{ + POPUP "" { + MENUITEM "Vad är det här?", IDM_WHATS_THIS + } +} + +DLG_STATS DIALOG 12,0,125,78 +CAPTION "Statistikruta" +FONT 9, "Tahoma" +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE +BEGIN + DEFPUSHBUTTON "&RET", ID_STATS_RET, 4, 52, 25, 13, WS_TABSTOP | WS_GROUP + PUSHBUTTON "&LOAD", ID_STATS_LOAD, 34, 52, 25, 13, WS_TABSTOP | WS_GROUP + PUSHBUTTON "&CD" ID_STATS_CD, 64, 52, 25, 13, WS_TABSTOP | WS_GROUP + PUSHBUTTON "C&AD", ID_STATS_CAD, 94, 52, 25, 13, WS_TABSTOP | WS_GROUP +END diff --git a/reactos/subsys/system/calc/calc.rc b/reactos/subsys/system/calc/calc.rc index 734bca2a16c..27069ea06d3 100644 --- a/reactos/subsys/system/calc/calc.rc +++ b/reactos/subsys/system/calc/calc.rc @@ -4,5 +4,3 @@ #include #include "rsrc.rc" -#include "De.rc" - diff --git a/reactos/subsys/system/calc/rsrc.rc b/reactos/subsys/system/calc/rsrc.rc index 53f73bfffb5..221463ac993 100644 --- a/reactos/subsys/system/calc/rsrc.rc +++ b/reactos/subsys/system/calc/rsrc.rc @@ -22,6 +22,7 @@ #include "resource.h" #include "En.rc" +#include "De.rc" #include "Es.rc" #include "Fr.rc" #include "It.rc" @@ -29,5 +30,5 @@ #include "Pt.rc" #include "Ru.rc" #include "Si.rc" - +#include "Sv.rc" LANGUAGE LANG_NEUTRAL,SUBLANG_NEUTRAL diff --git a/reactos/subsys/system/cmd/En.rc b/reactos/subsys/system/cmd/En.rc new file mode 100644 index 00000000000..74b9aaae3cd --- /dev/null +++ b/reactos/subsys/system/cmd/En.rc @@ -0,0 +1,273 @@ +#include "resource.h" +/* Start move all hard code string to En.rc + * By Magnus Olsen 2005 + */ + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT +STRINGTABLE DISCARDABLE +{ +STRING_ATTRIB_HELP, "Displays or changes file attributes.\n\n \ +ATTRIB [+R | -R] [+A | -A] [+S | -S] [+H | -H] file ...\n \ + [/S [/D]]\n\n \ + + Sets an attribute\n \ + - Clears an attribute\n \ + R Read-only file attribute\n \ + A Archive file attribute\n \ + S System file attribute\n \ + H Hidden file attribute\n \ + /S Processes matching files in the current directory\n \ + and all subdirectories\n \ + /D Processes direcories as well\n\n \ +Type ATTRIB without a parameter to display the attributes of all files." + +STRING_ALIAS_HELP, "Sets, removes or shows aliases.\n\n \ +ALIAS [alias=[command]]\n\n \ + alias Name for an alias.\n \ + command Text to be substituted for an alias.\n\n \ +To list all aliases:\n \ + ALIAS\n\n \ +To set a new or replace an existing alias:\n \ + ALIAS da=dir a:\n\n \ +To remove an alias from the alias list:\n \ + ALIAS da=" + +STRING_BEEP_HELP, "Beep the speaker.\n\nBEEP" + +STRING_CALL_HELP, "Calls one batch program from another.\n\n \ +CALL [drive:][path]filename [batch-parameter]\n\n \ + batch-parameter Specifies any command-line information required by the\n \ + batch program." + +STRING_CD_HELP, "Changes the current directory or displays it's name\n\n \ +CHDIR [drive:][path]\n \ +CHDIR[..|-]\n \ +CD [drive:][path]\n \ +CD[..|-]\n\n \ + .. parent directory\n \ + - previous directory\n\n \ +Type CD drive: to display the current directory on the specified drive.\n \ +Type CD without a parameter to display the current drive and directory. " + +STRING_CHCP_HELP, "Displays or sets the active code page number.\n\n \ +CHCP [nnn]\n\n \ + nnn Specifies the active code page number.\n\n \ +Type CHCP without a parameter to display the active code page number." + +STRING_CHOICE_HELP, "Waits for the user to choose one of a set of choices.\n\n \ +CHOICE [/C[:]choices][/N][/S][/T[:]c,nn][text]\n\n \ + /C[:]choices Specifies allowable keys. Default is YN.\n \ + /N Do not display choices and ? at the end of the prompt string.\n \ + /S Treat choice keys as case sensitive.\n \ + /T[:]c,nn Default choice to c after nn seconds.\n \ + text Prompt string to display.\n\n \ +ERRORLEVEL is set to offset of key user presses in choices." + +STRING_CLS_HELP, "Clears the screen.\n\nCLS" + +STRING_CMD_HELP1, "\nInternal commands available:\n" + +STRING_CMD_HELP2, "\nFeatures available:" + +STRING_CMD_HELP3," [aliases]" + +STRING_CMD_HELP4," [history]" + +STRING_CMD_HELP5," [unix filename completion]" + +STRING_CMD_HELP6," [directory stack]" + +STRING_CMD_HELP7," [redirections and piping]" + +STRING_CMD_HELP8, "Starts a new instance of the ReactOS command line interpreter.\n\n \ +CMD [/[C|K] command][/P][/Q][/T:bf]\n\n \ + /C command Runs the specified command and terminates.\n \ + /K command Runs the specified command and remains.\n \ + /P CMD becomes permanent and runs autoexec.bat\n \ + (cannot be terminated).\n \ + /T:bf Sets the background/foreground color (see COLOR command)." + +STRING_COLOR_HELP1, "Sets the default foreground and background colors.\n\n \ +COLOR [attr [/F]] \n\n \ + attr Specifies color attribute of console output\n \ + /F fill the console with color attribute\n\n \ +There are three ways to specify the colors:\n \ +1) [bright] name on [bright] name (only the first three letters are required)\n \ +2) decimal on decimal\n \ +3) two hex digits\n\n \ +Colors are:\n \ +dec hex name dec hex name\n \ +0 0 Black 8 8 Gray(Bright black)\n \ +1 1 Blue 9 9 Bright Blue\n \ +2 2 Green 10 A Bright Green\n \ +3 3 Cyan 11 B Bright Cyan\n \ +4 4 Red 12 C Bright Red\n \ +5 5 Magenta 13 D Bright Magenta\n \ +6 6 Yellow 14 E Bright Yellow\n \ +7 7 White 15 F Bright White" + +STRING_COPY_HELP1, "Overwrite %s (Yes/No/All)? " + +STRING_COPY_HELP2, "Copies one or more files to another location.\n\n \ +COPY [/V][/Y|/-Y][/A|/B] source [/A|/B]\n \ + [+ source [/A|/B] [+ ...]] [destination [/A|/B]]\n\n \ + source Specifies the file or files to be copied.\n \ + /A Indicates an ASCII text file.\n \ + /B Indicates a binary file.\n \ + destination Specifies the directory and/or filename for the new file(s).\n \ + /V Verifies that new files are written correctly.\n \ + /Y Suppresses prompting to confirm you want to overwrite an\n \ + existing destination file.\n \ + /-Y Causes prompting to confirm you want to overwrite an\n \ + existing destination file.\n\n \ +The switch /Y may be present in the COPYCMD environment variable.\n \ +..." + +STRING_DATE_HELP1, "\nEnter new date (mm%cdd%cyyyy): " + +STRING_DATE_HELP2, "\nEnter new date (dd%cmm%cyyyy): " + +STRING_DATE_HELP3, "\nEnter new date (yyyy%cmm%cdd): " + +STRING_DATE_HELP4, "Displays or sets the date.\n\n \ +DATE [/T][date]\n\n \ + /T display only\n\n \ +Type DATE without parameters to display the current date setting and\n \ +a prompt for a new one. Press ENTER to keep the same date." + +STRING_DEL_HELP1, "Deletes one or more files.\n\n \ +DEL [/%c /%c /%c /%c /%c /%c /%c] file ...\n \ +DELETE [/%c /%c /%c /%c /%c /%c /%c] file ...\n \ +ERASE [/%c /%c /%c /%c /%c /%c /%c] file ...\n\n \ + file Specifies the file(s) to delete.\n\n \ + /%c Nothing.\n \ + /%c Prompt. Ask before deleting each file.\n \ + /%c Total. Display total number of deleted files and freed disk space.\n \ + /%c Quiet.\n \ + /%c Wipe. Overwrite the file with random numbers before deleting it.\n \ + /%c Yes. Kill even *.* without asking.\n \ + /%c Zap. Delete hidden, read-only and system files).\n" + +STRING_DEL_HELP2, "All files in the directory will be deleted!\nAre you sure (Y/N)?" +STRING_DEL_HELP3, " %lu file deleted\n" +STRING_DEL_HELP4, " %lu files deleted\n" + +STRING_DELAY_HELP, "pause for n seconds or milliseconds\n \ +DELAY [/m]n\n\n \ + /m specifiy than n are milliseconds\n \ + otherwise n are seconds" + +STRING_DIR_HELP1, "DIR [drive:][path][filename] [/A[[:]attributes]] [/B] [/C] [/D] [/L] [/N]\n \ + [/O[[:]sortorder]] [/P] [/Q] [/S] [/T[[:]timefield]] [/W] [/X] [/4]\n\n \ + [drive:][path][filename]\n \ + Specifies drive, directory, and/or files to list.\n\n \ + /A Displays files with specified attributes.\n \ + attributes D Directories R Read-only files\n \ + H Hidden files A Files ready for archiving\n \ + S System files - Prefix meaning not\n \ + /B Uses bare format (no heading information or summary).\n \ + /C Display the thousand separator in file sizes. This is the\n \ + default. Use /-C to disable display of separator.\n \ + /D Same as wide but files are list sorted by column.\n \ + /L Uses lowercase.\n \ + /N New long list format where filenames are on the far right.\n \ + /O List by files in sorted order.\n \ + sortorder N By name (alphabetic) S By size (smallest first)\n \ + E By extension (alphabetic) D By date/time (oldest first)\n \ + G Group directories first - Prefix to reverse order\n \ + /P Pauses after each screenful of information.\n \ + /Q Display the owner of the file.\n \ + /S Displays files in specified directory and all subdirectories.\n \ + /T Controls which time field displayed or used for sorting\n \ + timefield C Creation\n \ + A Last Access\n \ + W Last Written\n \ + /W Uses wide list format.\n \ + /X This displays the short names generated for non-8dot3 file\n \ + names. The format is that of /N with the short name inserted\n \ + before the long name. If no short name is present, blanks are\n \ + displayed in its place.\n \ + /4 Displays four-digit years\n\n \ +Switches may be preset in the DIRCMD environment variable. Override\n \ +preset switches by prefixing any switch with - (hyphen)--for example, /-W.\n " + +STRING_DIR_HELP2, " Volume in drive %c is %s" +STRING_DIR_HELP3, " Volume in drive %c has no label" +STRING_DIR_HELP4, " Volume Serial Number is %04X-%04X\n" +STRING_DIR_HELP5, "\n Total Files Listed:\n%16i File(s)% 14s bytes\n" +STRING_DIR_HELP6, "%16i Dir(s)% 15s bytes\n" +STRING_DIR_HELP7, "\n Directory of %s\n\n" +STRING_DIR_HELP8, "%16i Dir(s)% 14s bytes\n" + +STRING_DIRSTACK_HELP1, "Stores the current directory for use by the POPD command, then\n \ +changes to the specified directory.\n\n \ +PUSHD [path | ..]\n\n \ + path Specifies the directory to make the current directory" + +STRING_DIRSTACK_HELP2, "Changes to the directory stored by the PUSHD command.\n\nPOPD" + +STRING_DIRSTACK_HELP3, "Prints the contents of the directory stack.\n\nDIRS" + +STRING_DIRSTACK_HELP4, "Directory stack empty" + + +STRING_EXIT_HELP, "Exits the command line interpreter.\n\nEXIT" + +STRING_MKDIR_HELP, "Creates a directory.\n\n \ +MKDIR [drive:]path\nMD [drive:]path" + +STRING_RMDIR_HELP, "Removes a directory.\n\n \ +RMDIR [drive:]path\nRD [drive:]path" + +STRING_REM_HELP, "Starts a comment line in a batch file.\n\nREM [Comment]" + + + + + + + + + + + +STRING_CHOICE_OPTION, "YN" +STRING_COPY_OPTION, "YNA" +STRING_DEL_OPTION, "AENPQSTWXYZ" + + +STRING_ALIAS_ERROR, "Command line too long after alias expansion!\n" +STRING_BATCH_ERROR, "Error opening batch file\n" +STRING_CHCP_ERROR1, "Active code page: %u\n" +STRING_CHCP_ERROR2, "Invalid parameter format - %s\n" +STRING_CHCP_ERROR3, "Parameter format incorrect - %s\n" +STRING_CHCP_ERROR4, "Invalid code page\n" +STRING_CHOICE_ERROR, "Invalid option. Expected format: /C[:]options" +STRING_CHOICE_ERROR_TXT, "Invalid option. Expected format: /T[:]c,nn" +STRING_CHOICE_ERROR_OPTION, "Illegal Option: %s" +STRING_CMD_ERROR1, "Can't redirect input from file %s\n" +STRING_CMD_ERROR2, "Error creating temporary file for pipe data\n" +STRING_CMD_ERROR3, "Can't redirect to file %s\n" +STRING_CMD_ERROR4, "Running %s...\n" +STRING_CMD_ERROR5, "Running cmdexit.bat...\n" +STRING_COLOR_ERROR1, "Same colors error! (Background and foreground can't be the same color)" +STRING_COLOR_ERROR2, "error in color specification" +STRING_COLOR_ERROR3, "Color %x\n" +STRING_COLOR_ERROR4, "same colors error!" +STRING_CONSOLE_ERROR, "Unknown error: %d\n" +STRING_COPY_ERROR1, "Error: Cannot open source - %s!\n" +STRING_COPY_ERROR2, "Error: Can't copy onto itself!\n" +STRING_COPY_ERROR3, "Error writing destination!\n" +STRING_COPY_ERROR4, "Error: Not implemented yet!\n" +STRING_DATE_ERROR, "Invalid date." +STRING_DEL_ERROR1, "Wildcards!\n\n" +STRING_DEL_ERROR2, "Full path: %s\n" +STRING_DEL_ERROR3, "File part: %s\n" +STRING_DEL_ERROR4, "Full filename: %s\n" +STRING_DEL_ERROR5, "The file %s will be deleted! " +STRING_DEL_ERROR6, "Are you sure (Y/N)?" +STRING_DEL_ERROR7, "Deleting: %s\n" +STRING_DEL_ERROR8, "No Wildcards!\n" +STRING_PARAM_ERROR, "Required parameter missing\n" + + +} diff --git a/reactos/subsys/system/cmd/alias.c b/reactos/subsys/system/cmd/alias.c index 820d26bcb4f..8c69eef43f8 100644 --- a/reactos/subsys/system/cmd/alias.c +++ b/reactos/subsys/system/cmd/alias.c @@ -24,10 +24,14 @@ * * 24-Jan-1998 (Eric Kohl ) * Redirection safe! + * + * 02-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #ifdef FEATURE_ALIASES @@ -233,6 +237,7 @@ VOID DestroyAlias (VOID) /* specified routines */ VOID ExpandAlias (LPTSTR cmd, INT maxlen) { + WCHAR szMsg[RC_STRING_MAX_SIZE]; unsigned n = 0, m, i, @@ -276,7 +281,8 @@ VOID ExpandAlias (LPTSTR cmd, INT maxlen) m = _tcslen (ptr->lpSubst); if ((int)(_tcslen (cmd) - len + m - n) > maxlen) { - ConErrPrintf (_T("Command line too long after alias expansion!\n")); + LoadString( GetModuleHandle(NULL), STRING_ALIAS_ERROR, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); /* the parser won't cause any problems with an empty line */ cmd[0] = _T('\0'); } @@ -300,25 +306,12 @@ VOID ExpandAlias (LPTSTR cmd, INT maxlen) INT CommandAlias (LPTSTR cmd, LPTSTR param) { LPTSTR ptr; + WCHAR szMsg[RC_STRING_MAX_SIZE]; if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Sets, removes or shows aliases.\n" - "\n" - "ALIAS [alias=[command]]\n" - "\n" - " alias Name for an alias.\n" - " command Text to be substituted for an alias.\n" - "\n" -// "For example:\n" - "To list all aliases:\n" - " ALIAS\n\n" - "To set a new or replace an existing alias:\n" - " ALIAS da=dir a:\n\n" - "To remove an alias from the alias list:\n" - " ALIAS da=" -// "Type ALIAS without a parameter to display the alias list.\n" - )); + LoadString( GetModuleHandle(NULL), STRING_ALIAS_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); return 0; } diff --git a/reactos/subsys/system/cmd/attrib.c b/reactos/subsys/system/cmd/attrib.c index e21f3a053f7..33548778a51 100644 --- a/reactos/subsys/system/cmd/attrib.c +++ b/reactos/subsys/system/cmd/attrib.c @@ -27,9 +27,13 @@ * * 23-Jan-1999 (Eric Kohl ) * Added handling of multiple filenames. + * + * 02-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_ATTRIB @@ -40,7 +44,7 @@ PrintAttribute (LPTSTR pszPath, LPTSTR pszFile, BOOL bRecurse) WIN32_FIND_DATA findData; HANDLE hFind; TCHAR szFullName[MAX_PATH]; - LPTSTR pszFileName; + LPTSTR pszFileName; /* prepare full file name buffer */ _tcscpy (szFullName, pszPath); @@ -116,6 +120,7 @@ ChangeAttribute (LPTSTR pszPath, LPTSTR pszFile, DWORD dwMask, TCHAR szFullName[MAX_PATH]; LPTSTR pszFileName; + /* prepare full file name buffer */ _tcscpy (szFullName, pszPath); pszFileName = szFullName + _tcslen (szFullName); @@ -192,6 +197,7 @@ INT CommandAttrib (LPTSTR cmd, LPTSTR param) BOOL bDirectories = FALSE; DWORD dwAttrib = 0; DWORD dwMask = 0; + WCHAR szMsg[RC_STRING_MAX_SIZE]; /* initialize strings */ szPath[0] = _T('\0'); @@ -200,19 +206,8 @@ INT CommandAttrib (LPTSTR cmd, LPTSTR param) /* print help */ if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Displays or changes file attributes.\n\n" - "ATTRIB [+R | -R] [+A | -A] [+S | -S] [+H | -H] file ...\n" - " [/S [/D]]\n\n" - " + Sets an attribute\n" - " - Clears an attribute\n" - " R Read-only file attribute\n" - " A Archive file attribute\n" - " S System file attribute\n" - " H Hidden file attribute\n" - " /S Processes matching files in the current directory\n" - " and all subdirectories\n" - " /D Processes direcories as well\n\n" - "Type ATTRIB without a parameter to display the attributes of all files.")); + LoadString( GetModuleHandle(NULL), STRING_ATTRIB_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); return 0; } diff --git a/reactos/subsys/system/cmd/batch.c b/reactos/subsys/system/cmd/batch.c index 36da188a91e..336227570c1 100644 --- a/reactos/subsys/system/cmd/batch.c +++ b/reactos/subsys/system/cmd/batch.c @@ -54,9 +54,13 @@ * * 23-Feb-2001 (Carl Nettelblad ) * Fixes made to get "for" working. + * + * 02-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" /* The stack of current batch contexts. @@ -215,6 +219,7 @@ VOID ExitBatch (LPTSTR msg) BOOL Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param) { HANDLE hFile; + WCHAR szMsg[RC_STRING_MAX_SIZE]; hFile = CreateFile (fullname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | @@ -227,7 +232,8 @@ BOOL Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param) if (hFile == INVALID_HANDLE_VALUE) { - ConErrPrintf (_T("Error opening batch file\n")); + LoadString( GetModuleHandle(NULL), STRING_BATCH_ERROR, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); return FALSE; } diff --git a/reactos/subsys/system/cmd/beep.c b/reactos/subsys/system/cmd/beep.c index 687f0b1a5bc..ae0c9bcdb4d 100644 --- a/reactos/subsys/system/cmd/beep.c +++ b/reactos/subsys/system/cmd/beep.c @@ -19,18 +19,25 @@ * * 20-Jan-1999 (Eric Kohl ) * Redirection ready! + * + * 02-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_BEEP INT cmd_beep (LPTSTR cmd, LPTSTR param) { + WCHAR szMsg[RC_STRING_MAX_SIZE]; + if (_tcsncmp (param, _T("/?"), 2) == 0) { - ConOutPuts (_T("Beep the speaker.\n\nBEEP")); + LoadString( GetModuleHandle(NULL), STRING_ALIAS_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); return 0; } diff --git a/reactos/subsys/system/cmd/call.c b/reactos/subsys/system/cmd/call.c index 05e728b68ea..32ca9f899a6 100644 --- a/reactos/subsys/system/cmd/call.c +++ b/reactos/subsys/system/cmd/call.c @@ -24,9 +24,13 @@ * * 20-Jan-1999 (Eric Kohl ) * Unicode and redirection safe! + * + * 02-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" /* @@ -41,16 +45,16 @@ INT cmd_call (LPTSTR cmd, LPTSTR param) { LPBATCH_CONTEXT n = NULL; + WCHAR szMsg[RC_STRING_MAX_SIZE]; #ifdef _DEBUG DebugPrintf (_T("cmd_call: (\'%s\',\'%s\')\n"), cmd, param); #endif if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Calls one batch program from another.\n\n" - "CALL [drive:][path]filename [batch-parameter]\n\n" - " batch-parameter Specifies any command-line information required by the\n" - " batch program.")); + LoadString( GetModuleHandle(NULL), STRING_CALL_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); + return 0; } diff --git a/reactos/subsys/system/cmd/chcp.c b/reactos/subsys/system/cmd/chcp.c index 3ebd03f4dd4..d21c9b1f40f 100644 --- a/reactos/subsys/system/cmd/chcp.c +++ b/reactos/subsys/system/cmd/chcp.c @@ -7,9 +7,12 @@ * 23-Dec-1998 (Eric Kohl ) * Started. * + * 02-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_CHCP @@ -19,14 +22,13 @@ INT CommandChcp (LPTSTR cmd, LPTSTR param) INT args; UINT uOldCodePage; UINT uNewCodePage; + WCHAR szMsg[RC_STRING_MAX_SIZE]; /* print help */ if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Displays or sets the active code page number.\n\n" - "CHCP [nnn]\n\n" - " nnn Specifies the active code page number.\n\n" - "Type CHCP without a parameter to display the active code page number.")); + LoadString( GetModuleHandle(NULL), STRING_CHCP_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); return 0; } @@ -36,14 +38,18 @@ INT CommandChcp (LPTSTR cmd, LPTSTR param) if (args == 0) { /* display active code page number */ - ConOutPrintf (_T("Active code page: %u\n"), GetConsoleCP ()); + LoadString( GetModuleHandle(NULL), STRING_CHCP_ERROR1, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), GetConsoleCP ()); + return 0; } if (args >= 2) { /* too many parameters */ - ConErrPrintf (_T("Invalid parameter format - %s\n"), param); + LoadString( GetModuleHandle(NULL), STRING_CHCP_ERROR2, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), param); + return 1; } @@ -55,14 +61,16 @@ INT CommandChcp (LPTSTR cmd, LPTSTR param) if (uNewCodePage == 0) { - ConErrPrintf (_T("Parameter format incorrect - %s\n"), arg[0]); + LoadString( GetModuleHandle(NULL), STRING_CHCP_ERROR3, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), arg[0]); freep (arg); return 1; } if (!SetConsoleCP (uNewCodePage)) { - ConErrPrintf (_T("Invalid code page\n")); + LoadString( GetModuleHandle(NULL), STRING_CHCP_ERROR4, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); } else { diff --git a/reactos/subsys/system/cmd/choice.c b/reactos/subsys/system/cmd/choice.c index 6cc6426d0db..16f216f8ebf 100644 --- a/reactos/subsys/system/cmd/choice.c +++ b/reactos/subsys/system/cmd/choice.c @@ -12,9 +12,15 @@ * * 26 Sep 1999 (Paolo Pantaleo) * Fixed timeout. + * + * 02 Apr 2005 (Magnus Olsen + * Remove Hardcode string so + * they can be translate + * */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_CHOICE @@ -97,7 +103,8 @@ IsKeyInString (LPTSTR lpString, TCHAR cKey, BOOL bCaseSensitive) INT CommandChoice (LPTSTR cmd, LPTSTR param) { - LPTSTR lpOptions = _T("YN"); + LPTSTR lpOptions; + TCHAR Options[2]; LPTSTR lpText = NULL; BOOL bNoPrompt = FALSE; BOOL bCaseSensitive = FALSE; @@ -114,21 +121,17 @@ CommandChoice (LPTSTR cmd, LPTSTR param) INT GCret; TCHAR Ch; DWORD amount,clk; + WCHAR szMsg[RC_STRING_MAX_SIZE]; + + LoadString( GetModuleHandle(NULL), STRING_CHOICE_OPTION, (LPTSTR) Options,sizeof(lpOptions)); + lpOptions = _T(Options); if (_tcsncmp (param, _T("/?"), 2) == 0) { - ConOutPuts (_T("Waits for the user to choose one of a set of choices.\n" - "\n" - "CHOICE [/C[:]choices][/N][/S][/T[:]c,nn][text]\n" - "\n" - " /C[:]choices Specifies allowable keys. Default is YN.\n" - " /N Do not display choices and ? at the end of the prompt string.\n" - " /S Treat choice keys as case sensitive.\n" - " /T[:]c,nn Default choice to c after nn seconds.\n" - " text Prompt string to display.\n" - "\n" - "ERRORLEVEL is set to offset of key user presses in choices.")); - return 0; + LoadString( GetModuleHandle(NULL), STRING_CHOICE_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); + + return 0; } /* retrieve text */ @@ -167,7 +170,9 @@ CommandChoice (LPTSTR cmd, LPTSTR param) if (_tcslen (lpOptions) == 0) { - ConErrPuts (_T("Invalid option. Expected format: /C[:]options")); + + LoadString( GetModuleHandle(NULL), STRING_CHOICE_ERROR, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPuts (_T((LPTSTR)szMsg)); freep (arg); return 1; } @@ -197,7 +202,8 @@ CommandChoice (LPTSTR cmd, LPTSTR param) if (*s != _T(',')) { - ConErrPuts (_T("Invalid option. Expected format: /T[:]c,nn")); + LoadString( GetModuleHandle(NULL), STRING_CHOICE_ERROR_TXT, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPuts (_T((LPTSTR)szMsg)); freep (arg); return 1; } @@ -208,7 +214,8 @@ CommandChoice (LPTSTR cmd, LPTSTR param) } else if (arg[i][0] == _T('/')) { - ConErrPrintf (_T("Illegal Option: %s"), arg[i]); + LoadString( GetModuleHandle(NULL), STRING_CHOICE_ERROR_OPTION, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), arg[i]); freep (arg); return 1; } diff --git a/reactos/subsys/system/cmd/cls.c b/reactos/subsys/system/cmd/cls.c index d1dfd3d6fe2..d44cddf3660 100644 --- a/reactos/subsys/system/cmd/cls.c +++ b/reactos/subsys/system/cmd/cls.c @@ -21,9 +21,13 @@ * * 20-Jan-1998 (Eric Kohl ) * Redirection ready! + * + * 02-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_CLS @@ -32,10 +36,12 @@ INT cmd_cls (LPTSTR cmd, LPTSTR param) DWORD dwWritten; CONSOLE_SCREEN_BUFFER_INFO csbi; COORD coPos; + WCHAR szMsg[RC_STRING_MAX_SIZE]; if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Clears the screen.\n\nCLS")); + LoadString( GetModuleHandle(NULL), STRING_CLS_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); return 0; } diff --git a/reactos/subsys/system/cmd/cmd.c b/reactos/subsys/system/cmd/cmd.c index 6a84fe66a05..a868f56658c 100644 --- a/reactos/subsys/system/cmd/cmd.c +++ b/reactos/subsys/system/cmd/cmd.c @@ -129,9 +129,13 @@ * * 12-Jul-2004 (Jens Collin ) * Added ShellExecute call when all else fails to be able to "launch" any file. + * + * 02-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #ifndef NT_SUCCESS #define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode) >= 0) @@ -548,6 +552,7 @@ VOID ParseCommandLine (LPTSTR cmd) { TCHAR cmdline[CMDLINE_LENGTH]; LPTSTR s; + WCHAR szMsg[RC_STRING_MAX_SIZE]; #ifdef FEATURE_REDIRECTION TCHAR in[CMDLINE_LENGTH] = _T(""); TCHAR out[CMDLINE_LENGTH] = _T(""); @@ -629,13 +634,15 @@ VOID ParseCommandLine (LPTSTR cmd) FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { - ConErrPrintf (_T("Can't redirect input from file %s\n"), in); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR1, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), in); return; } if (!SetStdHandle (STD_INPUT_HANDLE, hFile)) { - ConErrPrintf (_T("Can't redirect input from file %s\n"), in); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR1, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), in); return; } #ifdef _DEBUG @@ -659,7 +666,9 @@ VOID ParseCommandLine (LPTSTR cmd) TRUNCATE_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL); if (hFile[1] == INVALID_HANDLE_VALUE){ - ConErrPrintf (_T("Error creating temporary file for pipe data\n")); + + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR2, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); return; } @@ -711,13 +720,16 @@ VOID ParseCommandLine (LPTSTR cmd) FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { - ConErrPrintf (_T("Can't redirect to file %s\n"), out); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR3, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), out); + return; } if (!SetStdHandle (STD_OUTPUT_HANDLE, hFile)) { - ConErrPrintf (_T("Can't redirect to file %s\n"), out); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR3, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), out); return; } @@ -770,13 +782,17 @@ VOID ParseCommandLine (LPTSTR cmd) NULL); if (hFile == INVALID_HANDLE_VALUE) { - ConErrPrintf (_T("Can't redirect to file %s\n"), err); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR3, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), err); + return; } } if (!SetStdHandle (STD_ERROR_HANDLE, hFile)) { - ConErrPrintf (_T("Can't redirect to file %s\n"), err); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR3, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), err); + return; } @@ -906,6 +922,7 @@ ProcessInput (BOOL bFlag) LPTSTR ip; LPTSTR cp; BOOL bEchoThisLine; + do { @@ -1018,6 +1035,7 @@ ProcessInput (BOOL bFlag) */ BOOL WINAPI BreakHandler (DWORD dwCtrlType) { + if ((dwCtrlType != CTRL_C_EVENT) && (dwCtrlType != CTRL_BREAK_EVENT)) return FALSE; @@ -1058,26 +1076,36 @@ VOID RemoveBreakHandler (VOID) static VOID ShowCommands (VOID) { + WCHAR szMsg[RC_STRING_MAX_SIZE]; + /* print command list */ - ConOutPrintf (_T("\nInternal commands available:\n")); + LoadString( GetModuleHandle(NULL), STRING_CMD_HELP1, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg)); PrintCommandList (); /* print feature list */ - ConOutPuts (_T("\nFeatures available:")); -#ifdef FEATURE_ALIASES - ConOutPuts (_T(" [aliases]")); + LoadString( GetModuleHandle(NULL), STRING_CMD_HELP2, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); + +#ifdef FEATURE_ALIASES + LoadString( GetModuleHandle(NULL), STRING_CMD_HELP3, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); #endif #ifdef FEATURE_HISTORY - ConOutPuts (_T(" [history]")); + LoadString( GetModuleHandle(NULL), STRING_CMD_HELP4, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); #endif #ifdef FEATURE_UNIX_FILENAME_COMPLETION - ConOutPuts (_T(" [unix filename completion]")); + LoadString( GetModuleHandle(NULL), STRING_CMD_HELP5, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); #endif #ifdef FEATURE_DIRECTORY_STACK - ConOutPuts (_T(" [directory stack]")); + LoadString( GetModuleHandle(NULL), STRING_CMD_HELP6, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); #endif #ifdef FEATURE_REDIRECTION - ConOutPuts (_T(" [redirections and piping]")); + LoadString( GetModuleHandle(NULL), STRING_CMD_HELP7, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); #endif ConOutChar (_T('\n')); } @@ -1096,6 +1124,8 @@ Initialize (int argc, TCHAR* argv[]) TCHAR commandline[CMDLINE_LENGTH]; TCHAR ModuleName[_MAX_PATH + 1]; INT i; + WCHAR szMsg[RC_STRING_MAX_SIZE]; + //INT len; //TCHAR *ptr, *cmdLine; @@ -1124,15 +1154,8 @@ Initialize (int argc, TCHAR* argv[]) if (argc >= 2 && !_tcsncmp (argv[1], _T("/?"), 2)) { - ConOutPuts (_T("Starts a new instance of the ReactOS command line interpreter.\n" - "\n" - "CMD [/[C|K] command][/P][/Q][/T:bf]\n" - "\n" - " /C command Runs the specified command and terminates.\n" - " /K command Runs the specified command and remains.\n" - " /P CMD becomes permanent and runs autoexec.bat\n" - " (cannot be terminated).\n" - " /T:bf Sets the background/foreground color (see COLOR command).")); + LoadString( GetModuleHandle(NULL), STRING_CMD_HELP8, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); ExitProcess (0); } SetConsoleMode (hIn, ENABLE_PROCESSED_INPUT); @@ -1236,7 +1259,8 @@ Initialize (int argc, TCHAR* argv[]) if (IsExistingFile (_T("commandline"))) { - ConErrPrintf (_T("Running %s...\n", commandline)); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR4, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), commandline); ParseCommandLine (commandline); } } @@ -1267,15 +1291,20 @@ Initialize (int argc, TCHAR* argv[]) static VOID Cleanup (int argc, TCHAR *argv[]) { + WCHAR szMsg[RC_STRING_MAX_SIZE]; + /* run cmdexit.bat */ if (IsExistingFile (_T("cmdexit.bat"))) { - ConErrPrintf (_T("Running cmdexit.bat...\n")); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR5, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); + ParseCommandLine (_T("cmdexit.bat")); } else if (IsExistingFile (_T("\\cmdexit.bat"))) { - ConErrPrintf (_T("Running \\cmdexit.bat...\n")); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR5, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); ParseCommandLine (_T("\\cmdexit.bat")); } #ifndef __REACTOS__ @@ -1291,7 +1320,8 @@ static VOID Cleanup (int argc, TCHAR *argv[]) if (IsExistingFile (_T("commandline"))) { - ConErrPrintf (_T("Running %s...\n"), commandline); + LoadString( GetModuleHandle(NULL), STRING_CMD_ERROR4, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), commandline); ParseCommandLine (commandline); } } diff --git a/reactos/subsys/system/cmd/cmd.h b/reactos/subsys/system/cmd/cmd.h index f9aab35fe0f..4beb8148198 100644 --- a/reactos/subsys/system/cmd/cmd.h +++ b/reactos/subsys/system/cmd/cmd.h @@ -228,6 +228,7 @@ VOID error_sfile_not_found (LPTSTR); VOID error_file_not_found (VOID); VOID error_path_not_found (VOID); VOID error_too_many_parameters (LPTSTR); +VOID error_parameter_format(TCHAR); VOID error_invalid_switch (TCHAR); VOID error_invalid_parameter_format (LPTSTR); VOID error_out_of_memory (VOID); diff --git a/reactos/subsys/system/cmd/cmd.rc b/reactos/subsys/system/cmd/cmd.rc index 448bcd966b8..ad10b0dd4f4 100644 --- a/reactos/subsys/system/cmd/cmd.rc +++ b/reactos/subsys/system/cmd/cmd.rc @@ -7,5 +7,13 @@ #define REACTOS_STR_ORIGINAL_COPYRIGHT "Copyright (C) 1994-1998 Tim Norman and others\0" #define REACTOS_STR_LEGAL_COPYRIGHT "Copyright (C) 1998-2001 Eric Kohl and others\0" #include +#include "En.rc" + + 1 ICON DISCARDABLE res/terminal.ico + + + + + diff --git a/reactos/subsys/system/cmd/color.c b/reactos/subsys/system/cmd/color.c index 61cb290a912..a409149a488 100644 --- a/reactos/subsys/system/cmd/color.c +++ b/reactos/subsys/system/cmd/color.c @@ -16,44 +16,23 @@ * * 14-Oct-1999 (Paolo Pantaleo ) * 4nt's syntax implemented + * + * 03-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_COLOR static VOID ColorHelp (VOID) { - ConOutPuts (_T( - "Sets the default foreground and background colors.\n" - "\n" - "COLOR [attr [/F]] \n\n" - " attr Specifies color attribute of console output\n" - " /F fill the console with color attribute\n" - "\n" - "There are three ways to specify the colors:" - )); + WCHAR szMsg[RC_STRING_MAX_SIZE]; + LoadString( GetModuleHandle(NULL), STRING_COLOR_HELP1, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); - ConOutPuts (_T( - "\n" - "1) [bright] name on [bright] name (only the first three letters are required)\n" - "2) decimal on decimal\n" - "3) two hex digits\n" - "\n" - "Colors are:" - )); - - ConOutPuts (_T( - "dec hex name dec hex name\n" - "0 0 Black 8 8 Gray(Bright black)\n" - "1 1 Blue 9 9 Bright Blue\n" - "2 2 Green 10 A Bright Green\n" - "3 3 Cyan 11 B Bright Cyan\n" - "4 4 Red 12 C Bright Red\n" - "5 5 Magenta 13 D Bright Magenta\n" - "6 6 Yellow 14 E Bright Yellow\n" - "7 7 White 15 F Bright White")); } @@ -62,10 +41,12 @@ VOID SetScreenColor (WORD wColor, BOOL bFill) DWORD dwWritten; CONSOLE_SCREEN_BUFFER_INFO csbi; COORD coPos; + WCHAR szMsg[RC_STRING_MAX_SIZE]; if ((wColor & 0xF) == (wColor &0xF0) >> 4) { - ConErrPuts (_T("Same colors error! (Background and foreground can't be the same color)")); + LoadString( GetModuleHandle(NULL), STRING_COLOR_ERROR1, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPuts (_T((LPTSTR)szMsg)); } else { @@ -93,6 +74,8 @@ VOID SetScreenColor (WORD wColor, BOOL bFill) */ INT CommandColor (LPTSTR first, LPTSTR rest) { + WCHAR szMsg[RC_STRING_MAX_SIZE]; + if (_tcsncmp (rest, _T("/?"), 2) == 0) { ColorHelp (); @@ -109,15 +92,18 @@ INT CommandColor (LPTSTR first, LPTSTR rest) if (StringToColor (&wColor, &rest) == FALSE) { - ConErrPuts(_T("error in color specification")); + LoadString( GetModuleHandle(NULL), STRING_COLOR_ERROR2, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPuts (_T((LPTSTR)szMsg)); return 1; } - ConErrPrintf (_T("Color %x\n"), wColor); + LoadString( GetModuleHandle(NULL), STRING_COLOR_ERROR3, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), wColor); if ((wColor & 0xF) == (wColor &0xF0) >> 4) { - ConErrPuts (_T("same colors error!")); + LoadString( GetModuleHandle(NULL), STRING_COLOR_ERROR4, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), wColor); return 1; } diff --git a/reactos/subsys/system/cmd/config.h b/reactos/subsys/system/cmd/config.h index 65f8dac1f88..7f026b762a0 100644 --- a/reactos/subsys/system/cmd/config.h +++ b/reactos/subsys/system/cmd/config.h @@ -52,13 +52,9 @@ /* Define one of these to select the used locale. */ /* (date and time formats etc.) used in DATE, TIME, */ /* DIR, PROMPT etc. */ -#ifdef __REACTOS__ -#define LOCALE_DEFAULT -#else #define LOCALE_WINDOWS /* System locale */ /* #define LOCALE_GERMAN */ /* German locale */ /* #define LOCALE_DEFAULT */ /* United States locale */ -#endif #ifndef __REACTOS__ #define INCLUDE_CMD_ACTIVATE diff --git a/reactos/subsys/system/cmd/console.c b/reactos/subsys/system/cmd/console.c index 5d77e35dfdf..4c72ad446bb 100644 --- a/reactos/subsys/system/cmd/console.c +++ b/reactos/subsys/system/cmd/console.c @@ -7,9 +7,13 @@ * * 20-Jan-1999 (Eric Kohl ) * started + * + * 03-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #define OUTPUT_BUFFER_SIZE 4096 @@ -210,6 +214,7 @@ VOID ConOutFormatMessage (DWORD MessageId, ...) DWORD ret; LPTSTR text; va_list arg_ptr; + WCHAR szMsg[RC_STRING_MAX_SIZE]; va_start (arg_ptr, MessageId); ret = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, @@ -228,7 +233,8 @@ VOID ConOutFormatMessage (DWORD MessageId, ...) } else { - ConErrPrintf (_T("Unknown error: %d\n"), MessageId); + LoadString( GetModuleHandle(NULL), STRING_CONSOLE_ERROR, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); } } diff --git a/reactos/subsys/system/cmd/copy.c b/reactos/subsys/system/cmd/copy.c index 091b4616d41..c7a784812d4 100644 --- a/reactos/subsys/system/cmd/copy.c +++ b/reactos/subsys/system/cmd/copy.c @@ -20,9 +20,13 @@ * * 27-Oct-1998 (Eric Kohl ) * Disabled prompting when used in batch mode. + * + * 03-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_COPY @@ -294,8 +298,17 @@ Overwrite (LPTSTR fn) { TCHAR inp[10]; LPTSTR p; + + LPTSTR lpOptions; + TCHAR Options[3]; + WCHAR szMsg[RC_STRING_MAX_SIZE]; - ConOutPrintf (_T("Overwrite %s (Yes/No/All)? "), fn); + LoadString( GetModuleHandle(NULL), STRING_COPY_OPTION, (LPTSTR) Options,sizeof(Options)+sizeof(WCHAR)); + lpOptions = _T(Options); + + LoadString( GetModuleHandle(NULL), STRING_COPY_HELP1, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg)); + ConInString (inp, 10); ConOutPuts (_T("")); @@ -303,9 +316,9 @@ Overwrite (LPTSTR fn) for (p = inp; _istspace (*p); p++) ; - if (*p != _T('Y') && *p != _T('A')) + if (*p != lpOptions[0] && *p != lpOptions[2]) return 0; - if (*p == _T('A')) + if (*p == lpOptions[2]) return 2; return 1; @@ -326,6 +339,7 @@ int copy (LPTSTR source, LPTSTR dest, int append, LPDWORD lpdwFlags) DWORD dwWritten; DWORD i; BOOL bEof = FALSE; + WCHAR szMsg[RC_STRING_MAX_SIZE]; #ifdef _DEBUG DebugPrintf (_T("checking mode\n")); @@ -337,7 +351,8 @@ int copy (LPTSTR source, LPTSTR dest, int append, LPDWORD lpdwFlags) NULL, OPEN_EXISTING, 0, NULL); if (hFileSrc == INVALID_HANDLE_VALUE) { - ConErrPrintf (_T("Error: Cannot open source - %s!\n"), source); + LoadString( GetModuleHandle(NULL), STRING_COPY_ERROR1, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), source); return 0; } @@ -364,7 +379,9 @@ int copy (LPTSTR source, LPTSTR dest, int append, LPDWORD lpdwFlags) { if (!_tcscmp (dest, source)) { - ConErrPrintf (_T("Error: Can't copy onto itself!\n")); + LoadString( GetModuleHandle(NULL), STRING_COPY_ERROR2, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), source); + CloseHandle (hFileSrc); return 0; } @@ -438,7 +455,10 @@ int copy (LPTSTR source, LPTSTR dest, int append, LPDWORD lpdwFlags) WriteFile (hFileDest, buffer, dwRead, &dwWritten, NULL); if (dwWritten != dwRead) { - ConErrPrintf (_T("Error writing destination!\n")); + + LoadString( GetModuleHandle(NULL), STRING_COPY_ERROR3, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); + free (buffer); CloseHandle (hFileDest); CloseHandle (hFileSrc); @@ -618,6 +638,7 @@ INT cmd_copy (LPTSTR first, LPTSTR rest) TCHAR dir_d[_MAX_DIR]; TCHAR file_d[_MAX_FNAME]; TCHAR ext_d[_MAX_EXT]; + WCHAR szMsg[RC_STRING_MAX_SIZE]; int argc; int append; @@ -634,23 +655,8 @@ INT cmd_copy (LPTSTR first, LPTSTR rest) if (!_tcsncmp (rest, _T("/?"), 2)) { - ConOutPuts (_T("Copies one or more files to another location.\n" - "\n" - "COPY [/V][/Y|/-Y][/A|/B] source [/A|/B]\n" - " [+ source [/A|/B] [+ ...]] [destination [/A|/B]]\n" - "\n" - " source Specifies the file or files to be copied.\n" - " /A Indicates an ASCII text file.\n" - " /B Indicates a binary file.\n" - " destination Specifies the directory and/or filename for the new file(s).\n" - " /V Verifies that new files are written correctly.\n" - " /Y Suppresses prompting to confirm you want to overwrite an\n" - " existing destination file.\n" - " /-Y Causes prompting to confirm you want to overwrite an\n" - " existing destination file.\n" - "\n" - "The switch /Y may be present in the COPYCMD environment variable.\n" - "...")); + LoadString( GetModuleHandle(NULL), STRING_COPY_HELP2, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); return 1; } @@ -722,7 +728,9 @@ INT cmd_copy (LPTSTR first, LPTSTR rest) } else if (bDestFound && bWildcards) { - ConErrPrintf (_T("Error: Not implemented yet!\n")); + LoadString( GetModuleHandle(NULL), STRING_COPY_ERROR4, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); + DeleteFileList (sources); freep (p); return 0; diff --git a/reactos/subsys/system/cmd/date.c b/reactos/subsys/system/cmd/date.c index 90b9d713aa5..994614f2b50 100644 --- a/reactos/subsys/system/cmd/date.c +++ b/reactos/subsys/system/cmd/date.c @@ -26,9 +26,13 @@ * * 04-Feb-1999 (Eric Kohl ) * Fixed date input bug. + * + * 03-Apr-2005 (Magnus Olsen) ) + * Remove all hardcode string to En.rc */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_DATE @@ -43,22 +47,24 @@ static WORD awMonths[2][13] = static VOID PrintDateString (VOID) { + WCHAR szMsg[RC_STRING_MAX_SIZE]; + switch (nDateFormat) { case 0: /* mmddyy */ default: - ConOutPrintf (_T("\nEnter new date (mm%cdd%cyyyy): "), - cDateSeparator, cDateSeparator); + LoadString( GetModuleHandle(NULL), STRING_DATE_HELP1, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg), cDateSeparator, cDateSeparator); break; case 1: /* ddmmyy */ - ConOutPrintf (_T("\nEnter new date (dd%cmm%cyyyy): "), - cDateSeparator, cDateSeparator); + LoadString( GetModuleHandle(NULL), STRING_DATE_HELP2, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg), cDateSeparator, cDateSeparator); break; case 2: /* yymmdd */ - ConOutPrintf (_T("\nEnter new date (yyyy%cmm%cdd): "), - cDateSeparator, cDateSeparator); + LoadString( GetModuleHandle(NULL), STRING_DATE_HELP3, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg), cDateSeparator, cDateSeparator); break; } } @@ -183,14 +189,12 @@ INT cmd_date (LPTSTR cmd, LPTSTR param) INT i; BOOL bPrompt = TRUE; INT nDateString = -1; + WCHAR szMsg[RC_STRING_MAX_SIZE]; if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Displays or sets the date.\n\n" - "DATE [/T][date]\n\n" - " /T display only\n\n" - "Type DATE without parameters to display the current date setting and\n" - "a prompt for a new one. Press ENTER to keep the same date.")); + LoadString( GetModuleHandle(NULL), STRING_DATE_HELP4, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); return 0; } @@ -243,7 +247,9 @@ INT cmd_date (LPTSTR cmd, LPTSTR param) freep (arg); return 0; } - ConErrPuts (_T("Invalid date.")); + LoadString( GetModuleHandle(NULL), STRING_DATE_ERROR, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPuts (_T((LPTSTR)szMsg)); + } freep (arg); diff --git a/reactos/subsys/system/cmd/del.c b/reactos/subsys/system/cmd/del.c index 1483bb49c2a..aed01ad5583 100644 --- a/reactos/subsys/system/cmd/del.c +++ b/reactos/subsys/system/cmd/del.c @@ -35,6 +35,7 @@ */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_DEL @@ -108,29 +109,30 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) HANDLE hFile; WIN32_FIND_DATA f; + LPTSTR lpOptions; + TCHAR Options[11]; + WCHAR szMsg[RC_STRING_MAX_SIZE]; + LONG ch; + + + + LoadString( GetModuleHandle(NULL), STRING_DEL_OPTION, (LPTSTR) Options,sizeof(Options)+sizeof(WCHAR)); + lpOptions = _T(Options); + if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Deletes one or more files.\n" - "\n" - "DEL [/N /P /T /Q /W /Y /Z] file ...\n" - "DELETE [/N /P /T /Q /W /Y /Z] file ...\n" - "ERASE [/N /P /T /Q /W /Y /Z] file ...\n" - "\n" - " file Specifies the file(s) to delete.\n" - "\n" - " /N Nothing.\n" - " /P Prompt. Ask before deleting each file.\n" - " /T Total. Display total number of deleted files and freed disk space.\n" - " /Q Quiet.\n" - " /W Wipe. Overwrite the file with random numbers before deleting it.\n" - " /Y Yes. Kill even *.* without asking.\n" - " /Z Zap. Delete hidden, read-only and system files).\n")); - + LoadString( GetModuleHandle(NULL), STRING_DEL_HELP1, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg), + lpOptions[2],Options[3],lpOptions[6],lpOptions[4],lpOptions[7],lpOptions[9],lpOptions[10], + lpOptions[2],Options[3],lpOptions[6],lpOptions[4],lpOptions[7],lpOptions[9],lpOptions[10], + lpOptions[2],Options[3],lpOptions[6],lpOptions[4],lpOptions[7],lpOptions[9],lpOptions[10], + lpOptions[2],Options[3],lpOptions[6],lpOptions[4],lpOptions[7],lpOptions[9],lpOptions[10] + ); return 0; } arg = split (param, &args, FALSE); - + if (args > 0) { /* check for options anywhere in command line */ @@ -140,38 +142,48 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) { if (_tcslen (arg[i]) >= 2) { - switch (_totupper (arg[i][1])) - { - case _T('N'): - dwFlags |= DEL_NOTHING; - break; + + ch = _totupper (arg[i][1]); + + if (_totupper (lpOptions[2]) == ch) + { + dwFlags |= DEL_NOTHING; + } - case _T('P'): - dwFlags |= DEL_PROMPT; - break; + else if (_totupper (lpOptions[3]) == ch) + { + dwFlags |= DEL_PROMPT; + } + + else if (_totupper (lpOptions[4]) == ch) + { + dwFlags |= DEL_QUIET; + } - case _T('Q'): - dwFlags |= DEL_QUIET; - break; + else if (_totupper (lpOptions[5]) == ch) + { + dwFlags |= DEL_SUBDIR; + } - case _T('S'): - dwFlags |= DEL_SUBDIR; - break; + else if (_totupper (lpOptions[6]) == ch) + { + dwFlags |= DEL_TOTAL; + } - case _T('T'): - dwFlags |= DEL_TOTAL; - break; + else if (_totupper (lpOptions[7]) == ch) + { + dwFlags |= DEL_WIPE; + } - case _T('W'): - dwFlags |= DEL_WIPE; - break; - case _T('Y'): - dwFlags |= DEL_YES; - break; - case _T('Z'): - dwFlags |= DEL_ZAP; - break; - } + else if (_totupper (lpOptions[9]) == ch) + { + dwFlags |= DEL_YES; + } + + else if (_totupper (lpOptions[10]) == ch) + { + dwFlags |= DEL_ZAP; + } } @@ -199,8 +211,10 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) { if (!((dwFlags & DEL_YES) || (dwFlags & DEL_QUIET) || (dwFlags & DEL_PROMPT))) { - res = FilePromptYN (_T("All files in the directory will be deleted!\n" - "Are you sure (Y/N)?")); + + LoadString( GetModuleHandle(NULL), STRING_DEL_HELP2, (LPTSTR) szMsg,sizeof(szMsg)); + + res = FilePromptYN (_T( (LPTSTR) szMsg)); if ((res == PROMPT_NO) || (res == PROMPT_BREAK)) break; @@ -217,7 +231,8 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) { /* wildcards in filespec */ #ifdef _DEBUG - ConErrPrintf (_T("Wildcards!\n\n")); + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR1, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); #endif GetFullPathName (arg[i], @@ -226,8 +241,12 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) &pFilePart); #ifdef _DEBUG - ConErrPrintf (_T("Full path: %s\n"), szFullPath); - ConErrPrintf (_T("File part: %s\n"), pFilePart); + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR2, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), szFullPath); + + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR3, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), pFilePart); + #endif hFile = FindFirstFile (szFullPath, &f); @@ -249,13 +268,18 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) _tcscpy (pFilePart, f.cFileName); #ifdef _DEBUG - ConErrPrintf (_T("Full filename: %s\n"), szFullPath); + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR4, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); + #endif /* ask for deleting */ if (dwFlags & DEL_PROMPT) - { - ConErrPrintf (_T("The file %s will be deleted! "), szFullPath); - res = FilePromptYN (_T("Are you sure (Y/N)?")); + { + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR5, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), szFullPath); + + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR6, (LPTSTR) szMsg,sizeof(szMsg)); + res = FilePromptYN (_T((LPTSTR)szMsg)); if ((res == PROMPT_NO) || (res == PROMPT_BREAK)) { @@ -264,7 +288,10 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) } if (!(dwFlags & DEL_QUIET) && !(dwFlags & DEL_TOTAL)) - ConErrPrintf (_T("Deleting: %s\n"), szFullPath); + { + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR7, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), szFullPath); + } /* delete the file */ if (!(dwFlags & DEL_NOTHING)) @@ -307,7 +334,8 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) { /* no wildcards in filespec */ #ifdef _DEBUG - ConErrPrintf (_T("No Wildcards!\n")); + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR8, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); #endif GetFullPathName (arg[i], MAX_PATH, @@ -317,8 +345,11 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) /*ask for deleting */ if((dwFlags & DEL_PROMPT) && (FindFirstFile(szFullPath, &f) != INVALID_HANDLE_VALUE)) //Don't ask if the file doesn't exist, the following code will make the error-msg { - ConErrPrintf (_T("The file %s will be deleted! "), szFullPath); - res = FilePromptYN (_T("Are you sure (Y/N)?")); + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR5, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), szFullPath); + + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR6, (LPTSTR) szMsg,sizeof(szMsg)); + res = FilePromptYN (_T((LPTSTR)szMsg)); if ((res == PROMPT_NO) || (res == PROMPT_BREAK)) { @@ -327,10 +358,14 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) } #ifdef _DEBUG - ConErrPrintf (_T("Full path: %s\n"), szFullPath); + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR3, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), szFullPath); #endif if (!(dwFlags & DEL_QUIET) && !(dwFlags & DEL_TOTAL)) - ConOutPrintf (_T("Deleting %s\n"), szFullPath); + { + LoadString( GetModuleHandle(NULL), STRING_DEL_ERROR7, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg), szFullPath); + } if (!(dwFlags & DEL_NOTHING)) { @@ -380,12 +415,16 @@ INT CommandDelete (LPTSTR cmd, LPTSTR param) if (!(dwFlags & DEL_QUIET)) { - if (dwFiles == 0) - ConOutPrintf (_T(" 0 files deleted\n")); + if (dwFiles < 2) + { + LoadString( GetModuleHandle(NULL), STRING_DEL_HELP3, (LPTSTR) szMsg,sizeof(szMsg)); + } else - ConOutPrintf (_T(" %lu file%s deleted\n"), - dwFiles, - (dwFiles == 1) ? _T("") : _T("s")); + { + LoadString( GetModuleHandle(NULL), STRING_DEL_HELP4, (LPTSTR) szMsg,sizeof(szMsg)); + } + + ConOutPrintf (_T((LPTSTR)szMsg), dwFiles); } return 0; diff --git a/reactos/subsys/system/cmd/delay.c b/reactos/subsys/system/cmd/delay.c index 241baa991e2..6848f29e136 100644 --- a/reactos/subsys/system/cmd/delay.c +++ b/reactos/subsys/system/cmd/delay.c @@ -10,6 +10,7 @@ */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_DELAY @@ -18,17 +19,14 @@ INT CommandDelay (LPTSTR cmd, LPTSTR param) { DWORD val; DWORD mul=1000; + WCHAR szMsg[RC_STRING_MAX_SIZE]; if (_tcsncmp (param, _T("/?"), 2) == 0) { - ConOutPuts(_T( - "pause for n seconds or milliseconds" - "\n" - "DELAY [/m]n\n" - "\n" - " /m specifiy than n are milliseconds\n" - " otherwise n are seconds")); - return 0; + LoadString( GetModuleHandle(NULL), STRING_DELAY_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); + + return 0; } if (*param==0) diff --git a/reactos/subsys/system/cmd/dir.c b/reactos/subsys/system/cmd/dir.c index 31e8b4e8c1e..84fd5296ac3 100644 --- a/reactos/subsys/system/cmd/dir.c +++ b/reactos/subsys/system/cmd/dir.c @@ -119,27 +119,76 @@ * * 30-Apr-2004 (Filip Navara ) * Fix /w to print long names. + * + * 27-Feb-2005 (Konstantinos Paliouras ) + * Implemented all the switches that were missing, and made + * the ros dir very similar to windows dir. Major part of + * the code is rewritten. /p is removed, to be rewriten in + * the main cmd code. + * */ #include "precomp.h" +#include "resource.h" #ifdef INCLUDE_CMD_DIR -/* flag definitions */ -enum + +/* Time Field enumeration */ +enum ETimeField { - DIR_RECURSE = 0x0001, - DIR_PAGE = 0x0002, - DIR_WIDE = 0x0004, /* Rob Lake */ - DIR_BARE = 0x0008, /* Rob Lake */ - DIR_ALL = 0x0010, /* Rob Lake */ - DIR_LWR = 0x0020, /* Rob Lake */ - DIR_SORT = 0x0040, /* /O sort */ - DIR_NEW = 0x0080, /* /N new style */ - DIR_FOUR = 0x0100 /* /4 four digit year */ + TF_CREATIONDATE = 0, + TF_MODIFIEDDATE = 1, + TF_LASTACCESSEDDATE = 2 }; +/* Ordered by enumeration */ +enum EOrderBy +{ + ORDER_NAME = 0, + ORDER_SIZE = 1, + ORDER_DIRECTORY = 2, + ORDER_EXTENSION = 3, + ORDER_TIME = 4 +}; + +/* The struct for holding the switches */ +typedef struct TDirSwitchesFlags +{ + BOOL bBareFormat; /* Bare Format */ + BOOL bTSeperator; /* Thousands seperator */ + BOOL bWideList; /* Wide list format */ + BOOL bWideListColSort; /* Wide list format but sorted by column */ + BOOL bLowerCase; /* Uses lower case */ + BOOL bNewLongList; /* New long list */ + BOOL bPause; /* Pause per page */ + BOOL bUser; /* Displays the owner of file */ + BOOL bRecursive; /* Displays files in specified directory and all sub */ + BOOL bShortName; /* Displays the sort name of files if exist */ + BOOL b4Digit; /* Four digit year */ + struct + { + DWORD dwAttribVal; /* The desired state of attribute */ + DWORD dwAttribMask; /* Which attributes to check */ + BOOL bUnSet; /* A helper flag if "-" was given with the switch */ + BOOL bParSetted; /* A helper flag if parameters of switch were given */ + } stAttribs; /* Displays files with this attributes only */ + struct + { + enum EOrderBy eCriteria[3]; /* Criterias used to order by */ + BOOL bCriteriaRev[3]; /* If the criteria is in reversed order */ + short sCriteriaCount; /* The quantity of criterias */ + BOOL bUnSet; /* A helper flag if "-" was given with the switch */ + BOOL bParSetted; /* A helper flag if parameters of switch were given */ + } stOrderBy; /* Ordered by criterias */ + struct + { + enum ETimeField eTimeField; /* The time field that will be used for */ + BOOL bUnSet; /* A helper flag if "-" was given with the switch */ + BOOL bParSetted; /* A helper flag if parameters of switch were given */ + } stTimeField; /* The time field to display or use for sorting */ +}DIRSWITCHFLAGS, * LPDIRSWITCHFLAGS; typedef BOOL STDCALL (*PGETFREEDISKSPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); @@ -158,177 +207,347 @@ static ULARGE_INTEGER recurse_bytes; * displays help screen for dir * Rob Lake */ -static VOID Help (VOID) +static VOID + DirHelp (VOID) { - ConOutPuts(_T("Displays a list of files and subdirectories in a directory.\n" - "\n" - "DIR [drive:][path][filename] [/A] [/B] [/L] [/N] [/S] [/P] [/W] [/4]\n" - "\n" - " [drive:][path][filename]\n" - " Specifies drive, directory, and/or files to list.\n" - "\n" - " /A Displays files with HIDDEN SYSTEM attributes\n" - " default is ARCHIVE and READ ONLY\n" - " /B Uses bare format (no heading information or summary).\n" - " /L Uses lowercase.\n" - " /N New long list format where filenames are on the far right.\n" - " /S Displays files in specified directory and all subdirectories\n" - " /P Pauses after each screen full\n" - " /W Prints in wide format\n" - " /4 Display four digit years.\n" - "\n" - "Switches may be present in the DIRCMD environment variable. Use\n" - "of the - (hyphen) can turn off defined swtiches. Ex. /-W would\n" - "turn off printing in wide format.\n" - )); + WCHAR szMsg[RC_STRING_MAX_SIZE]; + LoadString( GetModuleHandle(NULL), STRING_DIR_HELP1, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); } + /* - * DirReadParam + * DirReadParameters * - * read the parameters from the command line + * Parse the parameters and switches of the command line and exports them */ -static BOOL -DirReadParam (LPTSTR line, LPTSTR *param, LPDWORD lpFlags) +static BOOL +DirReadParam (LPTSTR line, /* [IN] The line with the parameters & switches */ + LPTSTR *param, /* [OUT] The parameters after parsing */ + LPDIRSWITCHFLAGS lpFlags /* [IN/OUT] The flags after calculating switches */ + ) { - INT slash = 0; +TCHAR cCurSwitch; /* The current switch */ +TCHAR cCurChar; /* Current examing character */ +TCHAR cCurUChar; /* Current upper examing character */ +BOOL bNegative; /* Negative switch */ +BOOL bPNegative; /* Negative switch parameter */ +BOOL bIntoQuotes; /* A flag showing if we are in quotes (") */ +LPTSTR ptrLast; /* A pointer to the last character of param */ - if (!line) - return TRUE; + /* Initialize variables; */ + cCurSwitch = _T(' '); + bNegative = FALSE; + bPNegative = FALSE; + bIntoQuotes = FALSE; + /* No parameters yet */ *param = NULL; + ptrLast = NULL; + + /* We suppose that switch parameters + were given to avoid setting them to default + if the switch was not given */ + lpFlags->stAttribs.bParSetted = TRUE; + lpFlags->stOrderBy.bParSetted = TRUE; + lpFlags->stTimeField.bParSetted = TRUE; - /* scan the command line, processing switches */ + /* Main Loop (see README_DIR.txt) */ + /* scan the command line char per char, and we process its char */ while (*line) { - /* process switch */ - if (*line == _T('/') || slash) + /* we save current character as it is and its upper case */ + cCurChar = *line; + cCurUChar = _toupper(*line); + + /* 1st section (see README_DIR.txt) */ + /* When a switch is expecting */ + if (cCurSwitch == _T('/')) { - if (!slash) - line++; - slash = 0; - if (*line == _T('-')) - { - line++; - if (_totupper (*line) == _T('S')) - *lpFlags &= ~DIR_RECURSE; - else if (_totupper (*line) == _T('P')) - *lpFlags &= ~DIR_PAGE; - else if (_totupper (*line) == _T('W')) - *lpFlags &= ~DIR_WIDE; - else if (_totupper (*line) == _T('B')) - *lpFlags &= ~DIR_BARE; - else if (_totupper (*line) == _T('A')) - *lpFlags &= ~DIR_ALL; - else if (_totupper (*line) == _T('L')) - *lpFlags &= ~DIR_LWR; - else if (_totupper (*line) == _T('N')) - *lpFlags &= ~DIR_NEW; - else if (_totupper (*line) == _T('O')) - *lpFlags &= ~DIR_SORT; - else if (_totupper (*line) == _T('4')) - *lpFlags &= ~DIR_FOUR; - else + if ((cCurUChar == _T('A')) ||(cCurUChar == _T('T')) || (cCurUChar == _T('O'))) + { + cCurSwitch = cCurUChar; + switch (cCurUChar) { - error_invalid_switch ((TCHAR)_totupper (*line)); - return FALSE; + case _T('A'): + lpFlags->stAttribs.bUnSet = bNegative; + lpFlags->stAttribs.bParSetted = FALSE; + break; + case _T('T'): + lpFlags->stTimeField.bUnSet = bNegative; + lpFlags->stTimeField.bParSetted = FALSE; + break; + case _T('O'): + lpFlags->stOrderBy.bUnSet = bNegative; + lpFlags->stOrderBy.bParSetted = FALSE; + break; } - line++; - continue; } - else + else if (cCurUChar == _T('L')) + lpFlags->bLowerCase = ! bNegative; + else if (cCurUChar == _T('B')) + lpFlags->bBareFormat = ! bNegative; + else if (cCurUChar == _T('C')) + lpFlags->bTSeperator = ! bNegative; + else if (cCurUChar == _T('W')) + lpFlags->bWideList = ! bNegative; + else if (cCurUChar == _T('D')) + lpFlags->bWideListColSort = ! bNegative; + else if (cCurUChar == _T('N')) + lpFlags->bNewLongList = ! bNegative; + else if (cCurUChar == _T('P')) + lpFlags->bPause = ! bNegative; + else if (cCurUChar == _T('Q')) + lpFlags->bUser = ! bNegative; + else if (cCurUChar == _T('S')) + lpFlags->bRecursive = ! bNegative; + else if (cCurUChar == _T('X')) + lpFlags->bShortName = ! bNegative; + else if (cCurChar == _T('4')) + lpFlags->b4Digit = ! bNegative; + else if (cCurChar == _T('?')) { - if (_totupper (*line) == _T('S')) - *lpFlags |= DIR_RECURSE; - else if (_totupper (*line) == _T('P')) - *lpFlags |= DIR_PAGE; - else if (_totupper (*line) == _T('W')) - *lpFlags |= DIR_WIDE; - else if (_totupper (*line) == _T('B')) - *lpFlags |= DIR_BARE; - else if (_totupper (*line) == _T('A')) - *lpFlags |= DIR_ALL; - else if (_totupper (*line) == _T('L')) - *lpFlags |= DIR_LWR; - else if (_totupper (*line) == _T('N')) - *lpFlags |= DIR_NEW; - else if (_totupper (*line) == _T('O')) - *lpFlags |= DIR_SORT; - else if (_totupper (*line) == _T('4')) - *lpFlags |= DIR_FOUR; - else if (*line == _T('?')) - { - Help(); - return FALSE; - } - else - { - error_invalid_switch ((TCHAR)_totupper (*line)); - return FALSE; - } - line++; - continue; + DirHelp(); + return FALSE; } - } - - /* process parameter */ - if (!_istspace (*line)) - { - if (*param) + else if (cCurChar == _T('-')) + bNegative = TRUE; + else { - error_too_many_parameters (line); + error_invalid_switch ((TCHAR)_totupper (*line)); return FALSE; } - *param = line; + /* We check if we calculated the negative value and realese the flag */ + if ((cCurChar != _T('-')) && bNegative) + bNegative = FALSE; - /* skip to end of line or next whitespace or next / */ - if (*line != _T('\"')) - { - while (*line && !_istspace (*line) && *line != _T('/')) - line++; + /* if not a,o,t or - option then next parameter is not a switch */ + if ((cCurSwitch == _T('/')) && (!bNegative)) + cCurSwitch = _T(' '); - /* if end of line, return */ - if (!*line) - return TRUE; - } - else - { - /* skip over the initial quote */ - (*param)++; - line++; - - while (*line && *line != _T('"')) - line++; - - if (*line == _T('"')) - *line = 0; - else - return TRUE; - } - - /* if parameter, remember to process it later */ - if (*line == _T('/')) - slash = 1; - - *line++ = 0; - continue; } + /* 2nd section (see README_DIR.txt) */ + /* We are expecting parameter or the unknown */ + else if ((cCurSwitch == _T(' ')) || (cCurSwitch == _T('P'))) + { + if (cCurChar == _T('/')) + cCurSwitch = _T('/'); + /* Process a spacer */ + else if (cCurChar == _T(' ')) + { + if (!bIntoQuotes) + { + cCurSwitch = _T(' '); + if ((*param) && !(ptrLast)) + ptrLast = line; + } + + } + /* Process a quote */ + else if (cCurChar == _T('\"')) + { + bIntoQuotes = !bIntoQuotes; + if (!bIntoQuotes) ptrLast = line; + } + /* Process a character for parameter */ + else + { + if ((cCurSwitch == _T(' ')) && (*param)) + { + error_too_many_parameters(line); + return FALSE; + } + cCurSwitch = _T('P'); + if (!(*param)) + *param = line; + } + + } + /* 3rd section (see README_DIR.txt) */ + /* We are waiting for switch parameters */ + else + { + /* Check if there are no more switch parameters */ + if ((cCurChar == _T('/')) || ( cCurChar == _T(' '))) + { + /* Wrong desicion path, reprocess current character */ + cCurSwitch = cCurChar; + continue; + } + /* Process parameter switch */ + switch(cCurSwitch) + { + case _T('A'): /* Switch parameters for /A (attributes filter) */ + /* Ok a switch parameter was given */ + lpFlags->stAttribs.bParSetted = TRUE; + + if (cCurChar == _T(':')) + /* =V= dead command, used to make the "if" work */ + cCurChar = cCurChar; + else if(cCurChar == _T('-')) + bPNegative = TRUE; + else if(cCurUChar == _T('D')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_DIRECTORY; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_DIRECTORY; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_DIRECTORY; + } + else if(cCurUChar == _T('R')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_READONLY; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_READONLY; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_READONLY; + } + else if(cCurUChar == _T('H')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_HIDDEN; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_HIDDEN; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_HIDDEN; + } + else if(cCurUChar == _T('A')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_ARCHIVE; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_ARCHIVE; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_ARCHIVE; + } + else if(cCurUChar == _T('S')) + { + lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_SYSTEM; + if (bPNegative) + lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_SYSTEM; + else + lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_SYSTEM; + } + else + { + error_parameter_format((TCHAR)_totupper (*line)); + return FALSE; + } + break; + case _T('T'): /* Switch parameters for /T (time field) */ + + /* Ok a switch parameter was given */ + lpFlags->stTimeField.bParSetted = TRUE; + + if (cCurChar == _T(':')) + /* =V= dead command, used to make the "if" work */ + cCurChar = cCurChar; + else if(cCurUChar == _T('C')) + lpFlags->stTimeField.eTimeField= TF_CREATIONDATE ; + else if(cCurUChar == _T('A')) + lpFlags->stTimeField.eTimeField= TF_LASTACCESSEDDATE ; + else if(cCurUChar == _T('W')) + lpFlags->stTimeField.eTimeField= TF_MODIFIEDDATE ; + else + { + error_parameter_format((TCHAR)_totupper (*line)); + return FALSE; + } + break; + case _T('O'): /* Switch parameters for /O (order) */ + /* Ok a switch parameter was given */ + lpFlags->stOrderBy.bParSetted = TRUE; + + if (cCurChar == _T(':')) + /* <== dead command, used to make the "if" work */ + cCurChar = cCurChar; + else if(cCurChar == _T('-')) + bPNegative = TRUE; + else if(cCurUChar == _T('N')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_NAME; + } + else if(cCurUChar == _T('S')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_SIZE; + } + else if(cCurUChar == _T('G')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_DIRECTORY; + } + else if(cCurUChar == _T('E')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_EXTENSION; + } + else if(cCurUChar == _T('D')) + { + if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++; + lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative; + lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_TIME; + } + + else + { + error_parameter_format((TCHAR)_totupper (*line)); + return FALSE; + } + + + } + /* We check if we calculated the negative value and realese the flag */ + if ((cCurChar != _T('-')) && bPNegative) + bPNegative = FALSE; + } + line++; } + /* Terminate the parameters */ + if (ptrLast) *ptrLast = 0; - if (slash) + /* Calculate the switches with no switch paramater */ + if (!(lpFlags->stAttribs.bParSetted)) { - error_invalid_switch ((TCHAR)_totupper (*line)); - return FALSE; + lpFlags->stAttribs.dwAttribVal = 0L; + lpFlags->stAttribs.dwAttribMask = lpFlags->stAttribs.dwAttribVal; } + if (!(lpFlags->stOrderBy.bParSetted)) + { + lpFlags->stOrderBy.sCriteriaCount = 1; + lpFlags->stOrderBy.eCriteria[0] = ORDER_NAME; + lpFlags->stOrderBy.bCriteriaRev[0] = FALSE; + } + if (!(lpFlags->stOrderBy.bParSetted)) + lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE ; + /* Calculate the unsetted switches (the "-" prefixed)*/ + if (lpFlags->stAttribs.bUnSet) + { + lpFlags->stAttribs.bUnSet = FALSE; + lpFlags->stAttribs.dwAttribVal = 0L; + lpFlags->stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; + } + if (lpFlags->stOrderBy.bUnSet) + { + lpFlags->stOrderBy.bUnSet = FALSE; + lpFlags->stOrderBy.sCriteriaCount = 0; + } + if (lpFlags->stTimeField.bUnSet ) + { + lpFlags->stTimeField.bUnSet = FALSE; + lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE; + } return TRUE; } - /* * ExtendFilespec * @@ -507,9 +726,9 @@ DirParsePathspec (LPTSTR szPathspec, LPTSTR szPath, LPTSTR szFilespec) * incline * * increment our line if paginating, display message at end of screen - */ + *//*Maybe needed in future static BOOL -IncLine (LPINT pLine, DWORD dwFlags) +IncLine (LPINT pLine, LPDIRSWITCHFLAGS lpFlags) { BOOL error; CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo; @@ -518,16 +737,21 @@ IncLine (LPINT pLine, DWORD dwFlags) WindowHeight= lpConsoleScreenBufferInfo.srWindow.Bottom - lpConsoleScreenBufferInfo.srWindow.Top; - if (!WindowHeight) //That prevents bad behave if WindowHeight couln't calc + //That prevents bad behave if WindowHeight couln't calc + if (!WindowHeight) { WindowHeight= 1000000; } - if (!(dwFlags & DIR_PAGE)) + if (!(lpFlags->bPause)) return FALSE; (*pLine)++; - if (*pLine >= (int)maxy - 2 || *pLine >= WindowHeight) //Because I don't know if WindowsHeight work under all cases, perhaps then maxy is the right value + // Because I don't know if WindowsHeight + work under all cases, perhaps then maxy + is the right value + + if (*pLine >= (int)maxy - 2 || *pLine >= WindowHeight) { *pLine = 0; return (PagePrompt () == PROMPT_BREAK); @@ -535,7 +759,7 @@ IncLine (LPINT pLine, DWORD dwFlags) return FALSE; } - +*/ /* * PrintDirectoryHeader @@ -543,14 +767,15 @@ IncLine (LPINT pLine, DWORD dwFlags) * print the header for the dir command */ static BOOL -PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, DWORD dwFlags) +PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, LPDIRSWITCHFLAGS lpFlags) { TCHAR szRootName[MAX_PATH]; TCHAR szVolName[80]; DWORD dwSerialNr; LPTSTR p; + WCHAR szMsg[RC_STRING_MAX_SIZE]; - if (dwFlags & DIR_BARE) + if (lpFlags->bBareFormat) return(TRUE); /* build usable root path */ @@ -600,22 +825,27 @@ PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, DWORD dwFlags) } /* print drive info */ - ConOutPrintf(_T(" Volume in drive %c"), szRootName[0]); - + if (szVolName[0] != _T('\0')) - ConOutPrintf(_T(" is %s\n"), szVolName); + { + LoadString( GetModuleHandle(NULL), STRING_DIR_HELP2, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg), szRootName[0], szVolName); + } else - ConOutPrintf(_T(" has no label\n")); + { + LoadString( GetModuleHandle(NULL), STRING_DIR_HELP3, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg), szRootName[0]); + } + + - if (IncLine(pLine, dwFlags)) - return(FALSE); /* print the volume serial number if the return was successful */ - ConOutPrintf(_T(" Volume Serial Number is %04X-%04X\n"), - HIWORD(dwSerialNr), - LOWORD(dwSerialNr)); - if (IncLine(pLine, dwFlags)) - return(FALSE); + + LoadString( GetModuleHandle(NULL), STRING_DIR_HELP4, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg), + HIWORD(dwSerialNr), + LOWORD(dwSerialNr)); return(TRUE); } @@ -625,7 +855,7 @@ PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, DWORD dwFlags) * convert * * insert commas into a number - */ + *//* Maybe needed in future static INT ConvertULong (ULONG num, LPTSTR des, INT len) { @@ -656,10 +886,10 @@ ConvertULong (ULONG num, LPTSTR des, INT len) return n; } - +*/ static INT -ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len) +ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len, BOOL bPutSeperator) { TCHAR temp[32]; INT c = 0; @@ -676,7 +906,7 @@ ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len) temp[31] = 0; while (num.QuadPart > 0) { - if (((c + 1) % (nNumberGroups + 1)) == 0) + if ((((c + 1) % (nNumberGroups + 1)) == 0) && (bPutSeperator)) temp[30 - c++] = cThousandSeparator; temp[30 - c++] = (TCHAR)(num.QuadPart % 10) + _T('0'); num.QuadPart /= 10; @@ -691,44 +921,80 @@ ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len) static VOID -PrintFileDateTime (LPSYSTEMTIME dt, DWORD dwFlags) +DirPrintFileDateTime (TCHAR * lpDate, + TCHAR * lpTime, + LPWIN32_FIND_DATA lpFile, + LPDIRSWITCHFLAGS lpFlags) { - WORD wYear = (dwFlags & DIR_FOUR) ? dt->wYear : dt->wYear%100; +FILETIME ft; +SYSTEMTIME dt; +TCHAR szDate[30]; +TCHAR szTime[30]; +WORD wYear; + /* Select the right time field */ + switch (lpFlags->stTimeField.eTimeField) + { + case TF_CREATIONDATE: + if (!FileTimeToLocalFileTime(&lpFile->ftCreationTime, &ft)) + return; + FileTimeToSystemTime(&ft, &dt); + break; + case TF_LASTACCESSEDDATE : + if (!FileTimeToLocalFileTime(&lpFile->ftLastAccessTime, &ft)) + return; + FileTimeToSystemTime(&ft, &dt); + break; + case TF_MODIFIEDDATE: + if (!FileTimeToLocalFileTime(&lpFile->ftLastWriteTime, &ft)) + return; + FileTimeToSystemTime(&ft, &dt); + break; + } + + /* Format date */ + wYear = (lpFlags->b4Digit) ? dt.wYear : dt.wYear%100; switch (nDateFormat) { case 0: /* mmddyy */ default: - ConOutPrintf (_T("%.2d%c%.2d%c%d"), - dt->wMonth, cDateSeparator, dt->wDay, cDateSeparator, wYear); + _stprintf (szDate,_T("%02d%c%02d%c%0*d"), + dt.wMonth, cDateSeparator, + dt.wDay, cDateSeparator, + lpFlags->b4Digit?4:2, wYear); break; case 1: /* ddmmyy */ - ConOutPrintf (_T("%.2d%c%.2d%c%d"), - dt->wDay, cDateSeparator, dt->wMonth, cDateSeparator, wYear); + _stprintf (szDate, _T("%02d%c%02d%c%0*d"), + dt.wDay, cDateSeparator, dt.wMonth, + cDateSeparator,lpFlags->b4Digit?4:2, wYear); break; case 2: /* yymmdd */ - ConOutPrintf (_T("%d%c%.2d%c%.2d"), - wYear, cDateSeparator, dt->wMonth, cDateSeparator, dt->wDay); + _stprintf (szDate, _T("%0*d%c%02d%c%02d"), + lpFlags->b4Digit?4:2, wYear, cDateSeparator, + dt.wMonth, cDateSeparator, dt.wDay); break; } - + /* Format Time */ switch (nTimeFormat) { case 0: /* 12 hour format */ default: - ConOutPrintf (_T(" %2d%c%.2u%c"), - (dt->wHour == 0 ? 12 : (dt->wHour <= 12 ? dt->wHour : dt->wHour - 12)), + _stprintf (szTime,_T(" %02d%c%02u%c"), + (dt.wHour == 0 ? 12 : (dt.wHour <= 12 ? dt.wHour : dt.wHour - 12)), cTimeSeparator, - dt->wMinute, (dt->wHour <= 11 ? 'a' : 'p')); + dt.wMinute, (dt.wHour <= 11 ? 'a' : 'p')); break; case 1: /* 24 hour format */ - ConOutPrintf (_T(" %2d%c%.2u"), - dt->wHour, cTimeSeparator, dt->wMinute); + _stprintf (szTime, _T(" %02d%c%02u"), + dt.wHour, cTimeSeparator, dt.wMinute); break; } + /* Copy results */ + _tcscpy(lpDate, szDate); + _tcscpy(lpTime, szTime); } @@ -783,456 +1049,738 @@ static INT PrintSummary(LPTSTR szPath, ULONG ulFiles, ULONG ulDirs, - ULARGE_INTEGER bytes, + ULARGE_INTEGER u64Bytes, LPINT pLine, - DWORD dwFlags) + LPDIRSWITCHFLAGS lpFlags) { - TCHAR buffer[64]; - ULARGE_INTEGER uliFree; - TCHAR szRoot[] = _T("A:\\"); +TCHAR szBuffer[64]; +ULARGE_INTEGER uliFree; +TCHAR szRoot[] = _T("A:\\"); +WCHAR szMsg[RC_STRING_MAX_SIZE]; - if (dwFlags & DIR_BARE) - return(0); - /* Print number of files and bytes */ - ConvertULong (ulFiles, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %6s File%c"), - buffer, ulFiles == 1 ? _T(' ') : _T('s')); - - ConvertULargeInteger (bytes, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %15s byte%c\n"), - buffer, bytes.QuadPart == 1 ? _T(' ') : _T('s')); - - if (IncLine (pLine, dwFlags)) - return 1; - - /* Print number of dirs and bytes free */ - ConvertULong (ulDirs, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %6s Dir%c"), - buffer, ulDirs == 1 ? _T(' ') : _T('s')); - - if (!(dwFlags & DIR_RECURSE)) - { - szRoot[0] = szPath[0]; - GetUserDiskFreeSpace(szRoot, &uliFree); - ConvertULargeInteger (uliFree, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %15s bytes free\n"), buffer); - if (IncLine (pLine, dwFlags)) - return 1; - } - else - { - if ((dwFlags & DIR_BARE) == 0) - { - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - ConOutPrintf (_T("\n")); + /* Here we check if we didn't find anything */ + if (!(ulFiles + ulDirs)) + { + error_file_not_found(); + return 1; } - if (IncLine (pLine, dwFlags)) - return 1; + /* In bare format we don't print results */ + if (lpFlags->bBareFormat) + return 0; + + /* Print recursive specific results */ + if (lpFlags->bRecursive) + { + ConvertULargeInteger (u64Bytes, szBuffer, sizeof(szBuffer), lpFlags->bTSeperator); + + LoadString( GetModuleHandle(NULL), STRING_DIR_HELP5, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg),ulFiles, szBuffer); } + /* Print total directories and freespace */ + szRoot[0] = szPath[0]; + GetUserDiskFreeSpace(szRoot, &uliFree); + ConvertULargeInteger (uliFree, szBuffer, sizeof(szBuffer), lpFlags->bTSeperator); + LoadString( GetModuleHandle(NULL), STRING_DIR_HELP6, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg),ulDirs, szBuffer); + return 0; } +/* + * getExt + * + * Get the extension of a filename + */ +TCHAR* getExt(const TCHAR* file) +{ + + TCHAR* tmp = _tcsrchr(file,'.'); + return tmp?tmp+1:""; +} /* - * dir_list + * getName * - * list the files in the directory + * Get the name of the file without extension */ -static INT -DirList (LPTSTR szPath, LPTSTR szFilespec, LPINT pLine, DWORD dwFlags) +TCHAR * getName(const TCHAR* file, TCHAR * dest) { - TCHAR szFullPath[MAX_PATH]; - WIN32_FIND_DATA file; - ULARGE_INTEGER bytecount; - FILETIME ft; - SYSTEMTIME dt; - HANDLE hFile; - TCHAR buffer[32]; - ULONG filecount = 0; - ULONG dircount = 0; - INT count = 0; - SHORT screenwidth; - INT longestfname = 0; - - bytecount.QuadPart = 0; - - _tcscpy (szFullPath, szPath); - if (szFullPath[_tcslen(szFullPath) - 1] != _T('\\')) - _tcscat (szFullPath, _T("\\")); - _tcscat (szFullPath, szFilespec); - - hFile = FindFirstFile (szFullPath, &file); - if (hFile == INVALID_HANDLE_VALUE) +int iLen; +TCHAR* end; + + /* Check for "." and ".." folders */ + if ((_tcscmp(file, _T(".")) == 0) + || (_tcscmp(file, _T("..")) == 0)) { - /* Don't want to print anything if scanning recursively - * for a file. RL - */ - if ((dwFlags & DIR_RECURSE) == 0) - { - FindClose (hFile); - error_file_not_found (); - if (IncLine (pLine, dwFlags)) - return 0; - return 1; - } - FindClose (hFile); - return 0; + _tcscpy(dest,file); + return dest; } - /* Get the size of longest filename for wide listing. FN */ - if (dwFlags & DIR_WIDE && (dwFlags & DIR_BARE) == 0) + end = _tcsrchr(file,'.'); + if (!end) + iLen = _tcslen(file); + else + iLen = (end - file); + + + _tcsncpy(dest, file, iLen); + *(dest + iLen) = _T('\0'); + + return dest; +} +/* + * DirPrintNewList + * + * The function that prints in new style + */ +static int +DirPrintNewList(LPWIN32_FIND_DATA ptrFiles[], /* [IN]Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +DWORD i; /* An indexer for "for"s */ +TCHAR szSize[30]; /* The size of file */ +TCHAR szShortName[15]; /* The sort name */ +TCHAR szDate[20], szTime[20]; /* Date and time strings */ +int iSizeFormat; /* The format of size field */ +ULARGE_INTEGER u64FileSize; /* The file size */ + + for(i = 0;i < dwCount;i++) { - do + + /* Calculate size */ + if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + /* Directory */ { - if (_tcslen(file.cFileName) > longestfname) - { - longestfname = _tcslen(file.cFileName); - /* Directories get extra brackets around them. */ - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - longestfname += 2; - } + iSizeFormat = -14; + _tcscpy(szSize, _T("")); } - while (FindNextFile (hFile, &file)); - FindClose (hFile); + else + /* File */ + { + iSizeFormat = 14; + u64FileSize.HighPart = ptrFiles[i]->nFileSizeHigh; + u64FileSize.LowPart = ptrFiles[i]->nFileSizeLow; + ConvertULargeInteger(u64FileSize, szSize, 20,lpFlags->bTSeperator); + } + /* Calculate short name */ + szShortName[0] = _T('\0'); + if (lpFlags->bShortName) + _stprintf(szShortName," %-12s", ptrFiles[i]->cAlternateFileName); + + /* Format date and time */ + DirPrintFileDateTime(szDate,szTime,ptrFiles[i],lpFlags); - hFile = FindFirstFile (szFullPath, &file); - - /* Count the highest number of columns */ - GetScreenSize(&screenwidth, 0); - - /* For counting columns of output */ - count = 0; - - /* Increase by the number of spaces behind file name */ - longestfname += 3; + /* Print the line */ + ConOutPrintf("%10s %-8s %*s%s %s\n", + szDate, + szTime, + iSizeFormat, + szSize, + szShortName, + ptrFiles[i]->cFileName ); + } + return 0; +} +/* + * DirPrintWideList + * + * The function that prints in wide list + */ +static int +DirPrintWideList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +DWORD i,j; /* An indexer for "for"s */ +short iScreenWidth; /* The screen width */ +short iColumns; /* The columns (used by wide list) */ +short iLines; /* The lines (used by the wide list /d) */ +int iBiggestName; /* The biggest name, used for wide list */ +TCHAR szTempFname[MAX_PATH]; /* Temporary string */ - /* moved down here because if we are recursively searching and - * don't find any files, we don't want just to print - * Directory of C:\SOMEDIR - * with nothing else - * Rob Lake 06/13/98 - */ - if ((dwFlags & DIR_BARE) == 0) + /* Calculate biggest name */ + iBiggestName = 1; + for(i = 0;i < dwCount;i++) { - ConOutPrintf (_T(" Directory of %s\n"), szPath); - if (IncLine (pLine, dwFlags)) - return 1; - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - } - - do - { - /* next file, if user doesn't want all files */ - if (!(dwFlags & DIR_ALL) && - ((file.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) || - (file.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))) - continue; - - if (dwFlags & DIR_LWR) + if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - _tcslwr (file.cAlternateFileName); - } - - if (dwFlags & DIR_WIDE && (dwFlags & DIR_BARE) == 0) - { - ULARGE_INTEGER uliSize; - - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - _stprintf (buffer, _T("[%s]"), file.cFileName); - dircount++; - } - else - { - _stprintf (buffer, _T("%s"), file.cFileName); - filecount++; - } - - ConOutPrintf (_T("%*s"), - longestfname, buffer); - count++; - /* output as much columns as fits on the screen */ - if (count >= (screenwidth / longestfname)) - { - /* print the new line only if we aren't on the - * last column, in this case it wraps anyway */ - if (count * longestfname != screenwidth) - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - count = 0; - } - - uliSize.LowPart = file.nFileSizeLow; - uliSize.HighPart = file.nFileSizeHigh; - bytecount.QuadPart += uliSize.QuadPart; - } - else if (dwFlags & DIR_BARE) - { - ULARGE_INTEGER uliSize; - - if (_tcscmp (file.cFileName, _T(".")) == 0 || - _tcscmp (file.cFileName, _T("..")) == 0) - continue; - - if (dwFlags & DIR_RECURSE) - { - TCHAR dir[MAX_PATH]; - - _tcscpy (dir, szPath); - _tcscat (dir, _T("\\")); - if (dwFlags & DIR_LWR) - _tcslwr (dir); - ConOutPrintf (dir); - } - - ConOutPrintf (_T("%-13s\n"), file.cFileName); - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - dircount++; - else - filecount++; - if (IncLine (pLine, dwFlags)) - return 1; - - uliSize.LowPart = file.nFileSizeLow; - uliSize.HighPart = file.nFileSizeHigh; - bytecount.QuadPart += uliSize.QuadPart; + /* Directories need 2 additinal characters for brackets */ + if ((_tcslen(ptrFiles[i]->cFileName) + 2) > iBiggestName) + iBiggestName = _tcslen(ptrFiles[i]->cFileName) + 2; } else { - if (dwFlags & DIR_NEW) - { - /* print file date and time */ - if (FileTimeToLocalFileTime (&file.ftLastWriteTime, &ft)) - { - FileTimeToSystemTime (&ft, &dt); - PrintFileDateTime (&dt, dwFlags); - } - - /* print file size */ - if (file.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) - { - ConOutPrintf (_T(" ")); - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - dircount++; - } - else if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - ConOutPrintf (_T(" ")); - dircount++; - } - else - { - ULARGE_INTEGER uliSize; - - uliSize.LowPart = file.nFileSizeLow; - uliSize.HighPart = file.nFileSizeHigh; - - ConvertULargeInteger (uliSize, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %20s"), buffer); - - bytecount.QuadPart += uliSize.QuadPart; - filecount++; - } - - /* print long filename */ - ConOutPrintf (_T(" %s\n"), file.cFileName); - } - else - { - if (file.cFileName[0] == _T('.')) - ConOutPrintf (_T("%-13s "), file.cFileName); - else if (file.cAlternateFileName[0] == _T('\0')) - { - TCHAR szShortName[13]; - LPTSTR ext; - - _tcsncpy (szShortName, file.cFileName, 13); - ext = _tcschr (szShortName, _T('.')); - if (!ext) - ext = _T(""); - else - *ext++ = _T('\0'); - ConOutPrintf (_T("%-8s %-3s "), szShortName, ext); - } - else - { - LPTSTR ext; - - ext = _tcschr (file.cAlternateFileName, _T('.')); - if (!ext) - ext = _T(""); - else - *ext++ = _T('\0'); - ConOutPrintf (_T("%-8s %-3s "), file.cAlternateFileName, ext); - } - - /* print file size */ - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - ConOutPrintf (_T("%-14s"), _T("")); - dircount++; - } - else - { - ULARGE_INTEGER uliSize; - - uliSize.LowPart = file.nFileSizeLow; - uliSize.HighPart = file.nFileSizeHigh; - ConvertULargeInteger (uliSize, buffer, sizeof(buffer)); - ConOutPrintf (_T(" %10s "), buffer); - bytecount.QuadPart += uliSize.QuadPart; - filecount++; - } - - /* print file date and time */ - if (FileTimeToLocalFileTime (&file.ftLastWriteTime, &ft)) - { - FileTimeToSystemTime (&ft, &dt); - PrintFileDateTime (&dt, dwFlags); - } - - /* print long filename */ - ConOutPrintf (_T(" %s\n"), file.cFileName); - } - - if (IncLine (pLine, dwFlags)) - return 1; + if (_tcslen(ptrFiles[i]->cFileName) > iBiggestName) + iBiggestName = _tcslen(ptrFiles[i]->cFileName); } } - while (FindNextFile (hFile, &file)); - FindClose (hFile); + + /* Count the highest number of columns */ + GetScreenSize(&iScreenWidth, 0); + iColumns = iScreenWidth / iBiggestName; - /* Rob Lake, need to make clean output */ - /* JPP 07/08/1998 added check for count != 0 */ - if ((dwFlags & DIR_WIDE) && (count != 0)) + /* Check if there is enough space for spaces between names */ + if (((iBiggestName * iColumns) + iColumns) >= iScreenWidth) + iColumns --; + /* A last check at iColumns to avoid division by zero */ + if (!(iColumns)) iColumns = 1; + + /* Print Column sorted */ + if (lpFlags->bWideListColSort) { - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - } + /* Calculate the lines that will be printed */ + iLines = ceil((float)dwCount/(float)iColumns); - if (filecount || dircount) - { - recurse_dir_cnt += dircount; - recurse_file_cnt += filecount; - recurse_bytes.QuadPart += bytecount.QuadPart; + for (i = 0;i < iLines;i++) + { + for (j = 0;j < iColumns;j++) + { + DWORD temp = (j*iLines)+i; + if (temp >= dwCount) break; + if (ptrFiles[temp]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + _stprintf(szTempFname, "[%s]", ptrFiles[temp]->cFileName); + else + _stprintf(szTempFname, "%s", ptrFiles[temp]->cFileName); - /* print_summary */ - if (PrintSummary (szPath, filecount, dircount, bytecount, pLine, dwFlags)) - return 1; + ConOutPrintf("%-*s",iBiggestName + 1 , szTempFname ); + } + ConOutPrintf("\n"); + } } + /* Print Line sorted */ else { - error_file_not_found (); - return 1; + for (i = 0;i < dwCount;i++) + { + if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + _stprintf(szTempFname, "[%s]", ptrFiles[i]->cFileName); + else + _stprintf(szTempFname, "%s", ptrFiles[i]->cFileName); + + ConOutPrintf("%-*s", iBiggestName + 1, szTempFname ); + /* We print a new line at the end of each column + except for the case that it is the last item.*/ + if (!((i+1)%iColumns) && (i < (dwCount-1))) + ConOutPrintf("\n"); + } + /* Add a new line after the last item */ + ConOutPrintf("\n"); + } + return 0; +} +/* + * DirPrintOldList + * + * The function that prints in old style + */ +static int +DirPrintOldList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +DWORD i; /* An indexer for "for"s */ +TCHAR szName[10]; /* The name of file */ +TCHAR szExt[5]; /* The extension of file */ +TCHAR szDate[30],szTime[30]; /* Used to format time and date */ +TCHAR szSize[30]; /* The size of file */ +int iSizeFormat; /* The format of size field */ +ULARGE_INTEGER u64FileSize; /* The file size */ + + for(i = 0;i < dwCount;i++) + { + /* Broke 8.3 format */ + if (*ptrFiles[i]->cAlternateFileName ) + { + /* If the file is long named then we read the alter name */ + getName( ptrFiles[i]->cAlternateFileName, szName); + _tcscpy(szExt, getExt( ptrFiles[i]->cAlternateFileName)); + } + else + { + /* If the file is not long name we read its original name */ + getName( ptrFiles[i]->cFileName, szName); + _tcscpy(szExt, getExt( ptrFiles[i]->cFileName)); + } + + /* Calculate size */ + if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + /* Directory, no size it's a directory*/ + iSizeFormat = -17; + _tcscpy(szSize, _T("")); + } + else + { + /* File */ + iSizeFormat = 17; + u64FileSize.HighPart = ptrFiles[i]->nFileSizeHigh; + u64FileSize.LowPart = ptrFiles[i]->nFileSizeLow; + ConvertULargeInteger(u64FileSize, szSize, 20, lpFlags->bTSeperator); + } + + /* Format date and time */ + DirPrintFileDateTime(szDate,szTime,ptrFiles[i],lpFlags); + + /* Print the line */ + ConOutPrintf("%-8s %-3s %*s %s %s\n", + szName, /* The file's 8.3 name */ + szExt, /* The file's 8.3 extension */ + iSizeFormat, /* print format for size column */ + szSize, /* The size of file or "" for dirs */ + szDate, /* The date of file/dir */ + szTime); /* The time of file/dir */ + } + return 0; +} + +/* + * DirPrintBareList + * + * The function that prints in bare format + */ +static int +DirPrintBareList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +DWORD i; /* An indexer for "for"s */ +TCHAR szFullName[MAX_PATH]; /* The fullpath name of file */ + + for(i = 0;i < dwCount;i++) + { + if ((_tcscmp(ptrFiles[i]->cFileName, _T(".")) == 0) || + (_tcscmp(ptrFiles[i]->cFileName, _T("..")) == 0)) + { + /* at bare format we don't print "." and ".." folder */ + continue; + } + /* at recursive mode we print full path of file */ + if (lpFlags->bRecursive) + { + _tcscpy(szFullName, szCurPath); + _tcscat(szFullName, ptrFiles[i]->cFileName); + ConOutPrintf("%s\n", szFullName); + } + /* if we are not in recursive mode we print the file names */ + else + ConOutPrintf("%s\n",ptrFiles[i]->cFileName); + } + return 0; +} + +/* + * DirPrintFiles + * + * The functions that prints the files list + */ +static int +DirPrintFiles(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */ + DWORD dwCount, /* [IN] The quantity of files */ + TCHAR * szCurPath, /* [IN] Full path of current directory */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */ +{ +TCHAR szTemp[MAX_PATH]; /* A buffer to format the directory header */ +WCHAR szMsg[RC_STRING_MAX_SIZE]; + + /* Print directory header */ + _tcscpy(szTemp, szCurPath); + /* We cut the trailing \ of the full path */ + szTemp[_tcslen(szTemp)-1] = _T('\0'); + /* Condition to print header: + We are not printing in bare format + and if we are in recursive mode... we must have results */ + if (!(lpFlags->bBareFormat ) && !((lpFlags->bRecursive) && (dwCount <= 0))) + { + LoadString( GetModuleHandle(NULL), STRING_DIR_HELP7, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T( (LPTSTR)szMsg), szTemp); } + /* Bare format */ + if (lpFlags->bBareFormat) + { + DirPrintBareList(ptrFiles, dwCount, szCurPath, lpFlags); + } + /* New list style / Short names */ + else if(lpFlags->bShortName) + { + DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags); + } + /* Wide list */ + else if(lpFlags->bWideListColSort || lpFlags->bWideList) + { + DirPrintWideList(ptrFiles, dwCount, szCurPath, lpFlags); + } + /* New list style*/ + else if (lpFlags->bNewLongList ) + { + DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags); + } + /* If nothing is selected old list is the default */ + else + { + DirPrintOldList(ptrFiles, dwCount, szCurPath, lpFlags); + } return 0; } + /* - * _Read_Dir: Actual function that does recursive listing + * CompareFiles + * + * Compares 2 files based on the order criteria */ -static INT -DirRead (LPTSTR szPath, LPTSTR szFilespec, LPINT pLine, DWORD dwFlags) +static int + CompareFiles( + LPWIN32_FIND_DATA lpFile1, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */ + LPWIN32_FIND_DATA lpFile2, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we use to list */ { - TCHAR szFullPath[MAX_PATH]; - WIN32_FIND_DATA file; - HANDLE hFile; +/* u64 variables used to translate some broken 32bit info */ +ULARGE_INTEGER u64File1, u64File2; +int i; /* An indexer for "for"s */ +long iComp = 0; /* The comparison result */ + /* Calculate critiries by order given from user */ + for (i = 0;i < lpFlags->stOrderBy.sCriteriaCount;i++) + { + + /* Calculate criteria */ + switch(lpFlags->stOrderBy.eCriteria[i]) + { + case ORDER_SIZE: /* Order by size /o:s */ + /* concat the 32bit integers to a 64bit */ + u64File1.LowPart = lpFile1->nFileSizeLow; + u64File1.HighPart = lpFile1->nFileSizeHigh; + u64File2.LowPart = lpFile2->nFileSizeLow; + u64File2.HighPart = lpFile2->nFileSizeHigh; + + /* In case that differnce is too big for a long */ + if (u64File1.QuadPart < u64File2.QuadPart) + iComp = -1; + else if (u64File1.QuadPart > u64File2.QuadPart) + iComp = 1; + else + iComp = 0; + break; + + break; + case ORDER_DIRECTORY: /* Order by directory attribute /o:g */ + iComp = ((lpFile2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)- + (lpFile1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); + break; + case ORDER_EXTENSION: /* Order by extension name /o:e */ + iComp = _stricmp(getExt(lpFile1->cFileName),getExt(lpFile2->cFileName)); + break; + case ORDER_NAME: /* Order by filename /o:n */ + iComp = _stricmp(lpFile1->cFileName, lpFile2->cFileName); + break; + case ORDER_TIME: /* Order by file's time /o:t */ + + /* We compare files based on the time field selected by /t */ + switch(lpFlags->stTimeField.eTimeField) + { + case TF_CREATIONDATE: + /* concat the 32bit integers to a 64bit */ + u64File1.LowPart = lpFile1->ftCreationTime.dwLowDateTime; + u64File1.HighPart = lpFile1->ftCreationTime.dwHighDateTime ; + u64File2.LowPart = lpFile2->ftCreationTime.dwLowDateTime; + u64File2.HighPart = lpFile2->ftCreationTime.dwHighDateTime ; + break; + case TF_LASTACCESSEDDATE : + /* concat the 32bit integers to a 64bit */ + u64File1.LowPart = lpFile1->ftLastAccessTime.dwLowDateTime; + u64File1.HighPart = lpFile1->ftLastAccessTime.dwHighDateTime ; + u64File2.LowPart = lpFile2->ftLastAccessTime.dwLowDateTime; + u64File2.HighPart = lpFile2->ftLastAccessTime.dwHighDateTime ; + break; + case TF_MODIFIEDDATE: + /* concat the 32bit integers to a 64bit */ + u64File1.LowPart = lpFile1->ftLastWriteTime.dwLowDateTime; + u64File1.HighPart = lpFile1->ftLastWriteTime.dwHighDateTime ; + u64File2.LowPart = lpFile2->ftLastWriteTime.dwLowDateTime; + u64File2.HighPart = lpFile2->ftLastWriteTime.dwHighDateTime ; + break; + } + + /* In case that differnce is too big for a long */ + if (u64File1.QuadPart < u64File2.QuadPart) + iComp = -1; + else if (u64File1.QuadPart > u64File2.QuadPart) + iComp = 1; + else + iComp = 0; + break; + } + /* Reverse if desired */ + if (lpFlags->stOrderBy.bCriteriaRev[i]) + iComp *= -1; + + /* If that criteria was enough for distinguishing + the files/dirs,there is no need to calculate the others*/ + if (iComp != 0) break; + } + + /* Translate the value of iComp to boolean */ + if (iComp > 0) + return TRUE; + else + return FALSE; +} + +/* + * QsortFiles + * + * Sort files by the order criterias using quicksort method + */ +static void +QsortFiles( + LPWIN32_FIND_DATA ptrArray[], /* [IN/OUT] The array with file info pointers */ + int i, /* [IN] The index of first item in array */ + int j, /* [IN] The index to last item in array */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we will use to sort */ +{ +LPWIN32_FIND_DATA lpTemp; /* A temporary pointer */ + /* used for exchangin pointers */ +int First, Last, Temp; +BOOL Way; + + if (i < j) + { + First = i, Last = j, Way = TRUE; + while (i != j) + { + if (Way == CompareFiles(ptrArray[i], ptrArray[j], lpFlags)) + { + /* Swap the pointers of the array */ + lpTemp = ptrArray[i]; + ptrArray[i]= ptrArray[j]; + ptrArray[j] = lpTemp; + /* Swap the indexers for inverting sorting */ + Temp = i, i=j,j =Temp; + Way = !Way; + } + j += (!Way - Way); + + } + QsortFiles(ptrArray,First, i-1, lpFlags); + QsortFiles(ptrArray,i+1,Last, lpFlags); + } +} + + + +/* + * DirList + * + * The functions that does everything except for printing results + */ +static INT DirList (LPTSTR szPath, /* [IN] The path that dir starts */ + LPTSTR szFilespec, /* [IN] The type of file that we are looking for */ + LPINT pLine, /* FIXME: Maybe used for paginating */ + LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags of the listing */ +{ + +/* Internal linked list */ +struct TDirFindListNode + { + WIN32_FIND_DATA stFindInfo; + struct TDirFindListNode * ptrNext; +}; + +HANDLE hSearch; /* The handle of the search */ +HANDLE hRecSearch; /* The handle for searching recursivly */ +WIN32_FIND_DATA wfdFileInfo; /* The info of file that found */ +LPWIN32_FIND_DATA * ptrFileArray; /* An array of pointers with all the files */ +struct TDirFindListNode * ptrStartNode; /* The pointer to the first node */ +struct TDirFindListNode * ptrNextNode; /* A pointer used for relatives refernces */ +TCHAR szFullPath[MAX_PATH]; /* The full path that we are listing with trailing \ */ +TCHAR szFullFileSpec[MAX_PATH]; /* The full path with file specs that we ll request\ */ +TCHAR szBytes[20]; /* A string for converting ULARGE integer */ +DWORD dwCount; /* A counter of files found in directory */ +DWORD dwCountFiles; /* Counter for files */ +DWORD dwCountDirs; /* Counter for directories */ +ULARGE_INTEGER u64CountBytes; /* Counter for bytes */ +ULARGE_INTEGER u64Temp; /* A temporary counter */ +WCHAR szMsg[RC_STRING_MAX_SIZE]; + + /* Initialize Variables */ + ptrStartNode = NULL; + ptrNextNode = NULL; + dwCount = 0; + dwCountFiles = 0; + dwCountDirs = 0; + u64CountBytes.QuadPart = 0; + + /* Create szFullPath and szFullFileSpec */ _tcscpy (szFullPath, szPath); - if (szFullPath[_tcslen (szFullPath) - 1] != _T('\\')) + if (szFullPath[_tcslen(szFullPath) - 1] != _T('\\')) _tcscat (szFullPath, _T("\\")); - _tcscat (szFullPath, _T("*")); + _tcscpy (szFullFileSpec, szFullPath); + _tcscat (szFullFileSpec, szFilespec); - hFile = FindFirstFile (szFullPath, &file); - if (hFile == INVALID_HANDLE_VALUE) - return 1; + /* Prepare the linked list, first node is allocated */ + if ((ptrStartNode = malloc(sizeof(struct TDirFindListNode))) == NULL) + { +#ifdef _DEBUG + ConErrPrintf("DEBUG: Cannot allocate memory for ptrStartNode!\n"); +#endif + return 1; /* Error cannot allocate memory for 1st object */ + } + ptrNextNode = ptrStartNode; + /* Collect the results for the current folder */ + hSearch = FindFirstFile (szFullFileSpec, &wfdFileInfo); do { - /* don't list "." and ".." */ - if (_tcscmp (file.cFileName, _T(".")) == 0 || - _tcscmp (file.cFileName, _T("..")) == 0) - continue; - - if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + if (hSearch != INVALID_HANDLE_VALUE) { - _tcscpy (szFullPath, szPath); - if (szFullPath[_tcslen (szFullPath) - 1] != _T('\\')) - _tcscat (szFullPath, _T("\\")); - _tcscat (szFullPath, file.cFileName); + /* Here we filter all the specified attributes */ + if ((wfdFileInfo.dwFileAttributes & lpFlags->stAttribs.dwAttribMask ) + == (lpFlags->stAttribs.dwAttribMask & lpFlags->stAttribs.dwAttribVal )) + { + ptrNextNode->ptrNext = malloc(sizeof(struct TDirFindListNode)); + /* If malloc fails we go to next file in hope it works, + without braking the linked list! */ + if (ptrNextNode->ptrNext) + { + /* Copy the info of search at linked list */ + memcpy((void *)&ptrNextNode->ptrNext->stFindInfo, + (void *)&wfdFileInfo,sizeof(WIN32_FIND_DATA)); + + /* If lower case is selected do it here */ + if (lpFlags->bLowerCase) + { + _tcslwr(ptrNextNode->ptrNext->stFindInfo.cAlternateFileName); + _tcslwr(ptrNextNode->ptrNext->stFindInfo.cFileName); + } + + /* Continue at next node at linked list */ + ptrNextNode = ptrNextNode->ptrNext; + dwCount ++; + + /* Grab statistics */ + if (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + /* Directory */ + dwCountDirs++; + } + else + { + /* File */ + dwCountFiles++; + u64Temp.HighPart = wfdFileInfo.nFileSizeHigh; + u64Temp.LowPart = wfdFileInfo.nFileSizeLow; + u64CountBytes.QuadPart += u64Temp.QuadPart; + } + } + } - if (DirList (szFullPath, szFilespec, pLine, dwFlags)) - { - FindClose (hFile); - return 1; - } - if (DirRead (szFullPath, szFilespec, pLine, dwFlags) == 1) - { - FindClose (hFile); - return 1; - } - } - } - while (FindNextFile (hFile, &file)); + } + }while(FindNextFile(hSearch,&wfdFileInfo)); + FindClose(hSearch); - if (!FindClose (hFile)) - return 1; + /* Terminate list */ + ptrNextNode->ptrNext = NULL; + + /* Calculate and allocate space need for making an array of pointers */ + ptrFileArray = malloc(sizeof(LPWIN32_FIND_DATA)*dwCount); + if (!(ptrFileArray)) + { +#ifdef _DEBUG + ConErrPrintf("DEBUG: Cannot allocate memory for ptrFileArray!\n"); +#endif + goto _DirList_clear_n_exit; + } + + /* Create an array of pointers from the linked list + this will be used to sort and print data, rather than the list */ + ptrNextNode = ptrStartNode; + dwCount = 0; + while(ptrNextNode->ptrNext) + { + *(ptrFileArray + dwCount) = &ptrNextNode->ptrNext->stFindInfo; + ptrNextNode = ptrNextNode->ptrNext; + dwCount++; + } + + /* Sort Data if requested*/ + if (lpFlags->stOrderBy.sCriteriaCount > 0) + QsortFiles(ptrFileArray, 0, dwCount-1,lpFlags); + + /* Print Data */ + DirPrintFiles(ptrFileArray, dwCount, szFullPath, lpFlags); + + /* Free array */ + free(ptrFileArray); + + /* Print Directory Summary */ + /* Condition to print summary is: + If we are not in bare format and if we have results! */ + if (!(lpFlags->bBareFormat) && (dwCount > 0)) + { + ConvertULargeInteger(u64CountBytes, szBytes, 20, lpFlags->bTSeperator); + LoadString( GetModuleHandle(NULL), STRING_DIR_HELP8, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPrintf (_T((LPTSTR)szMsg),dwCountFiles, szBytes); + } + + /* Add statistics to recursive statistics*/ + recurse_dir_cnt += dwCountDirs; + recurse_file_cnt += dwCountFiles; + recurse_bytes.QuadPart += u64CountBytes.QuadPart; + + /* Do the recursive job if requested + the recursive is be done on ALL(indepent of their attribs) + directoried of the current one.*/ + if (lpFlags->bRecursive) + { + /* The new search is involving any *.* file */ + _tcscpy(szFullFileSpec, szFullPath); + _tcscat(szFullFileSpec, _T("*.*")); + hRecSearch = FindFirstFile (szFullFileSpec, &wfdFileInfo); + do + { + if (hRecSearch != INVALID_HANDLE_VALUE) + { + /* We search for directories other than "." and ".." */ + if ((_stricmp(wfdFileInfo.cFileName, _T(".")) != 0) + && (_stricmp(wfdFileInfo.cFileName, _T("..")) != 0 ) + && (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + /* Concat the path and the directory to do recursive */ + _tcscpy(szFullFileSpec, szFullPath); + _tcscat(szFullFileSpec, wfdFileInfo.cFileName); + /* We do the same for tha folder */ + DirList(szFullFileSpec, szFilespec, pLine,lpFlags); + } + } + }while(FindNextFile(hRecSearch,&wfdFileInfo)); + FindClose(hRecSearch); + } + +_DirList_clear_n_exit: +/* Deallocate memory */ + /* Free linked list */ + while (ptrStartNode) + { + ptrNextNode = ptrStartNode->ptrNext; + free(ptrStartNode); + ptrStartNode = ptrNextNode; + dwCount --; + } return 0; } -/* - * do_recurse: Sets up for recursive directory listing - */ -static INT -DirRecurse (LPTSTR szPath, LPTSTR szSpec, LPINT pLine, DWORD dwFlags) -{ - if (!PrintDirectoryHeader (szPath, pLine, dwFlags)) - return 1; - - if (DirList (szPath, szSpec, pLine, dwFlags)) - return 1; - - if ((dwFlags & DIR_BARE) == 0) - { - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - } - - if (DirRead (szPath, szSpec, pLine, dwFlags)) - return 1; - - if ((dwFlags & DIR_BARE) == 0) - ConOutPrintf (_T("Total files listed:\n")); - - dwFlags &= ~DIR_RECURSE; - - if (PrintSummary (szPath, recurse_file_cnt, - recurse_dir_cnt, recurse_bytes, pLine, dwFlags)) - return 1; - - if ((dwFlags & DIR_BARE) == 0) - { - ConOutPrintf (_T("\n")); - if (IncLine (pLine, dwFlags)) - return 1; - } - - return 0; -} - /* * dir @@ -1241,27 +1789,45 @@ DirRecurse (LPTSTR szPath, LPTSTR szSpec, LPINT pLine, DWORD dwFlags) */ INT CommandDir (LPTSTR first, LPTSTR rest) { - DWORD dwFlags = DIR_NEW | DIR_FOUR; - TCHAR dircmd[256]; + TCHAR dircmd[256]; /* A variable to store the DIRCMD enviroment variable */ TCHAR szPath[MAX_PATH]; TCHAR szFilespec[MAX_PATH]; LPTSTR param; INT nLine = 0; + DIRSWITCHFLAGS stFlags; - + /* Initialize variables */ recurse_dir_cnt = 0L; recurse_file_cnt = 0L; recurse_bytes.QuadPart = 0; + /* Initialize Switch Flags < Default switches are setted here!> */ + stFlags.b4Digit = TRUE; + stFlags.bBareFormat = FALSE; + stFlags.bLowerCase = FALSE; + stFlags.bNewLongList = TRUE; + stFlags.bPause = FALSE; + stFlags.bRecursive = FALSE; + stFlags.bShortName = FALSE; + stFlags.bTSeperator = TRUE; + stFlags.bUser = FALSE; + stFlags.bWideList = FALSE; + stFlags.bWideListColSort = FALSE; + stFlags.stTimeField.eTimeField = TF_MODIFIEDDATE; + stFlags.stTimeField.bUnSet = FALSE; + stFlags.stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; + stFlags.stAttribs.dwAttribVal = 0L; + stFlags.stAttribs.bUnSet = FALSE; + stFlags.stOrderBy.sCriteriaCount = 0; + stFlags.stOrderBy.bUnSet = FALSE; + /* read the parameters from the DIRCMD environment variable */ - if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, 256)) - { - if (!DirReadParam (dircmd, ¶m, &dwFlags)) - return 1; - } + if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, 256)) + if (!DirReadParam(dircmd, ¶m, &stFlags)) + return 1; /* read the parameters */ - if (!DirReadParam (rest, ¶m, &dwFlags)) + if (!DirReadParam(rest, ¶m, &stFlags)) return 1; /* default to current directory */ @@ -1272,21 +1838,44 @@ INT CommandDir (LPTSTR first, LPTSTR rest) if (DirParsePathspec (param, szPath, szFilespec)) return 1; - if (dwFlags & DIR_RECURSE) - { - if (IncLine (&nLine, dwFlags)) - return 0; - if (DirRecurse (szPath, szFilespec, &nLine, dwFlags)) +/* + Uncomment this to show the final state of switch flags*/ +/* + ConOutPrintf("Attributes mask/value %x/%x\n",stFlags.stAttribs.dwAttribMask,stFlags.stAttribs.dwAttribVal ); + ConOutPrintf("(B) Bare format : %i\n", stFlags.bBareFormat ); + ConOutPrintf("(C) Thousand : %i\n", stFlags.bTSeperator ); + ConOutPrintf("(W) Wide list : %i\n", stFlags.bWideList ); + ConOutPrintf("(D) Wide list sort by column : %i\n", stFlags.bWideListColSort ); + ConOutPrintf("(L) Lowercase : %i\n", stFlags.bLowerCase ); + ConOutPrintf("(N) New : %i\n", stFlags.bNewLongList ); + ConOutPrintf("(O) Order : %i\n", stFlags.stOrderBy.sCriteriaCount ); + int i; + for (i =0;i) * Improved chdir/cd command. + * + * 02-Apr-2004 (Magnus Olsen ) + * Remove all hard code string so they can be + * translate to other langues. */ #include "precomp.h" - +#include "resource.h" #ifdef INCLUDE_CMD_CHDIR @@ -154,21 +158,15 @@ INT cmd_chdir (LPTSTR cmd, LPTSTR param) LPTSTR lpOldPath; LPTSTR endofstring; /* pointer to the null character in the directory to change to */ LPTSTR lastquote; /* pointer to the last quotation mark in the directory to change to */ + WCHAR szMsg[RC_STRING_MAX_SIZE]; /*Should we better declare a variable containing _tsclen(dir) ? It's used a few times, but on the other hand paths are generally not very long*/ if (!_tcsncmp (param, _T("/?"), 2)) - { - ConOutPuts (_T("Changes the current directory or displays it's name\n\n" - "CHDIR [drive:][path]\n" - "CHDIR[..|-]\n" - "CD [drive:][path]\n" - "CD[..|-]\n\n" - " .. parent directory\n" - " - previous directory\n\n" - "Type CD drive: to display the current directory on the specified drive.\n" - "Type CD without a parameter to display the current drive and directory.")); + { + LoadString( GetModuleHandle(NULL), STRING_CD_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); return 0; } @@ -281,12 +279,13 @@ INT cmd_mkdir (LPTSTR cmd, LPTSTR param) LPTSTR place; /* used to search for the \ when no space is used */ LPTSTR *p = NULL; INT argc; - + WCHAR szMsg[RC_STRING_MAX_SIZE]; if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Creates a directory.\n\n" - "MKDIR [drive:]path\nMD [drive:]path")); + LoadString( GetModuleHandle(NULL), STRING_MKDIR_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); + return 0; } @@ -321,7 +320,8 @@ INT cmd_mkdir (LPTSTR cmd, LPTSTR param) if (!dir) { - ConErrPrintf (_T("Required parameter missing\n")); + LoadString( GetModuleHandle(NULL), STRING_PARAM_ERROR, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); return 1; } @@ -357,10 +357,15 @@ INT cmd_rmdir (LPTSTR cmd, LPTSTR param) LPTSTR *p = NULL; INT argc; + + WCHAR szMsg[RC_STRING_MAX_SIZE]; + if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Removes a directory.\n\n" - "RMDIR [drive:]path\nRD [drive:]path")); + LoadString( GetModuleHandle(NULL), STRING_RMDIR_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + + ConOutPuts (_T((LPTSTR)szMsg)); + return 0; } @@ -394,7 +399,9 @@ INT cmd_rmdir (LPTSTR cmd, LPTSTR param) if (!dir) { - ConErrPrintf (_T("Required parameter missing\n")); + LoadString( GetModuleHandle(NULL), STRING_PARAM_ERROR, (LPTSTR) szMsg,sizeof(szMsg)); + ConErrPrintf (_T((LPTSTR)szMsg)); + return 1; } @@ -423,9 +430,12 @@ INT cmd_rmdir (LPTSTR cmd, LPTSTR param) */ INT CommandExit (LPTSTR cmd, LPTSTR param) { + WCHAR szMsg[RC_STRING_MAX_SIZE]; + if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Exits the command line interpreter.\n\nEXIT")); + LoadString( GetModuleHandle(NULL), STRING_EXIT_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); return 0; } @@ -441,10 +451,12 @@ INT CommandExit (LPTSTR cmd, LPTSTR param) */ INT CommandRem (LPTSTR cmd, LPTSTR param) { + WCHAR szMsg[RC_STRING_MAX_SIZE]; + if (!_tcsncmp (param, _T("/?"), 2)) { - ConOutPuts (_T("Starts a comment line in a batch file.\n\n" - "REM [Comment]")); + LoadString( GetModuleHandle(NULL), STRING_REM_HELP, (LPTSTR) szMsg,sizeof(szMsg)); + ConOutPuts (_T((LPTSTR)szMsg)); } return 0; diff --git a/reactos/subsys/system/cmd/precomp.h b/reactos/subsys/system/cmd/precomp.h index d2e970a4f00..d8991b3aa33 100644 --- a/reactos/subsys/system/cmd/precomp.h +++ b/reactos/subsys/system/cmd/precomp.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "cmd.h" #include "config.h" diff --git a/reactos/subsys/system/cmd/res/terminal.ico b/reactos/subsys/system/cmd/res/terminal.ico index c3f80a07e3d..f3252f6cb01 100644 Binary files a/reactos/subsys/system/cmd/res/terminal.ico and b/reactos/subsys/system/cmd/res/terminal.ico differ diff --git a/reactos/subsys/system/cmd/resource.h b/reactos/subsys/system/cmd/resource.h new file mode 100644 index 00000000000..5a8814c55f2 --- /dev/null +++ b/reactos/subsys/system/cmd/resource.h @@ -0,0 +1,95 @@ +#define RC_STRING_MAX_SIZE 2048 + +#define STRING_CHOICE_OPTION 200 +#define STRING_COPY_OPTION 201 +#define STRING_DEL_OPTION 202 + +#define STRING_ALIAS_ERROR 300 +#define STRING_BATCH_ERROR 301 +#define STRING_CHCP_ERROR1 302 +#define STRING_CHCP_ERROR2 303 +#define STRING_CHCP_ERROR3 304 +#define STRING_CHCP_ERROR4 305 +#define STRING_CHOICE_ERROR 306 +#define STRING_CHOICE_ERROR_TXT 307 +#define STRING_CHOICE_ERROR_OPTION 308 +#define STRING_CMD_ERROR1 309 +#define STRING_CMD_ERROR2 310 +#define STRING_CMD_ERROR3 311 +#define STRING_CMD_ERROR4 312 +#define STRING_CMD_ERROR5 313 +#define STRING_COLOR_ERROR1 314 +#define STRING_COLOR_ERROR2 315 +#define STRING_COLOR_ERROR3 316 +#define STRING_COLOR_ERROR4 317 +#define STRING_CONSOLE_ERROR 318 +#define STRING_COPY_ERROR1 319 +#define STRING_COPY_ERROR2 320 +#define STRING_COPY_ERROR3 321 +#define STRING_COPY_ERROR4 322 +#define STRING_DATE_ERROR 323 +#define STRING_DEL_ERROR1 324 +#define STRING_DEL_ERROR2 325 +#define STRING_DEL_ERROR3 326 +#define STRING_DEL_ERROR4 327 +#define STRING_DEL_ERROR5 328 +#define STRING_DEL_ERROR6 329 +#define STRING_DEL_ERROR7 330 +#define STRING_DEL_ERROR8 331 +#define STRING_PARAM_ERROR 332 + +#define STRING_ATTRIB_HELP 400 +#define STRING_ALIAS_HELP 401 +#define STRING_BEEP_HELP 402 +#define STRING_CALL_HELP 403 +#define STRING_CD_HELP 404 +#define STRING_CHCP_HELP 405 +#define STRING_CHOICE_HELP 406 +#define STRING_CLS_HELP 407 +#define STRING_CMD_HELP1 408 +#define STRING_CMD_HELP2 409 +#define STRING_CMD_HELP3 410 +#define STRING_CMD_HELP4 411 +#define STRING_CMD_HELP5 412 +#define STRING_CMD_HELP6 413 +#define STRING_CMD_HELP7 414 +#define STRING_CMD_HELP8 415 +#define STRING_COLOR_HELP1 416 +#define STRING_COPY_HELP1 417 +#define STRING_COPY_HELP2 418 +#define STRING_DATE_HELP1 419 +#define STRING_DATE_HELP2 420 +#define STRING_DATE_HELP3 421 +#define STRING_DATE_HELP4 422 +#define STRING_DEL_HELP1 423 +#define STRING_DEL_HELP2 424 +#define STRING_DEL_HELP3 425 +#define STRING_DEL_HELP4 426 +#define STRING_DELAY_HELP 427 + +#define STRING_DIR_HELP1 428 +#define STRING_DIR_HELP2 429 +#define STRING_DIR_HELP3 430 +#define STRING_DIR_HELP4 431 +#define STRING_DIR_HELP5 432 +#define STRING_DIR_HELP6 433 +#define STRING_DIR_HELP7 434 +#define STRING_DIR_HELP8 435 + +#define STRING_DIRSTACK_HELP1 436 +#define STRING_DIRSTACK_HELP2 437 +#define STRING_DIRSTACK_HELP3 438 +#define STRING_DIRSTACK_HELP4 439 + +#define STRING_EXIT_HELP 440 +#define STRING_MKDIR_HELP 441 +#define STRING_RMDIR_HELP 442 +#define STRING_REM_HELP 443 + + + + + + + + diff --git a/reactos/subsys/system/cmd/todo.txt b/reactos/subsys/system/cmd/todo.txt index c1257593e93..3a9826fad93 100644 --- a/reactos/subsys/system/cmd/todo.txt +++ b/reactos/subsys/system/cmd/todo.txt @@ -5,8 +5,6 @@ Fix bugs :) Optimize the code! For size and speed. There are numerous places where the code is hardly optimal for either. -Sorting in DIR command ("dir /o..."). - ^S and ^Q to pause/resume displays. Improve DEL, COPY and MOVE commands. diff --git a/reactos/subsys/system/dhcp/Makefile b/reactos/subsys/system/dhcp/Makefile new file mode 100644 index 00000000000..34974ee379f --- /dev/null +++ b/reactos/subsys/system/dhcp/Makefile @@ -0,0 +1,30 @@ +# + +PATH_TO_TOP = ../../.. + +TARGET_TYPE = program + +TARGET_APPTYPE = windows + +TARGET_NAME = dhcp + +TARGET_CFLAGS = -D__REACTOS__ -D_WIN32_WINNT=0x0501 -D__USE_W32API -Iinclude + +TARGET_OBJECTS = adapter.o alloc.o compat.o dhclient.o dispatch.o hash.o \ + options.o privsep.o socket.o tables.o timer.o util.o + +TARGET_SDKLIBS = iphlpapi.a ws2_32.a ntdll.a + +TARGET_RC_SRCS = dhcp.rc + +TARGET_RC_BINSRC = + +TARGET_RC_BINARIES = + +default: all +DEP_OBJECTS = $(TARGET_OBJECTS) +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + +include $(TOOLS_PATH)/depend.mk diff --git a/reactos/subsys/system/dhcp/adapter.c b/reactos/subsys/system/dhcp/adapter.c new file mode 100644 index 00000000000..9edbfc2f137 --- /dev/null +++ b/reactos/subsys/system/dhcp/adapter.c @@ -0,0 +1,165 @@ +#include "rosdhcp.h" + +static LIST_ENTRY AdapterList; +static WSADATA wsd; +extern struct interface_info *ifi; + +DWORD GetAddress( PDHCP_ADAPTER Adapter ) { + PMIB_IPADDRTABLE AddressTable = NULL; + ULONG i, Size = 0, NumAddressRows; + DWORD Error = GetIpAddrTable( AddressTable, &Size, FALSE ); + + while( Error == ERROR_INSUFFICIENT_BUFFER ) { + free( AddressTable ); + AddressTable = malloc( Size ); + if( AddressTable ) + Error = GetIpAddrTable( AddressTable, &Size, FALSE ); + } + if( Error != ERROR_SUCCESS ) { + free( AddressTable ); + return Error; + } + + NumAddressRows = Size / sizeof(MIB_IPADDRTABLE); + for( i = 0; i < AddressTable->dwNumEntries; i++ ) { + DH_DbgPrint(MID_TRACE, + ("Finding address for adapter %d: (%d -> %x)\n", + Adapter->IfMib.dwIndex, + AddressTable->table[i].dwIndex, + AddressTable->table[i].dwAddr)); + if( Adapter->IfMib.dwIndex == AddressTable->table[i].dwIndex ) { + memcpy( &Adapter->IfAddr, &AddressTable->table[i], + sizeof( MIB_IPADDRROW ) ); + } + } +} + +void AdapterInit() { + PMIB_IFTABLE Table = malloc(sizeof(MIB_IFTABLE)); + DWORD Error, Size, i; + PDHCP_ADAPTER Adapter = NULL; + + WSAStartup(0x0101,&wsd); + + InitializeListHead( &AdapterList ); + + DH_DbgPrint(MID_TRACE,("Getting Adapter List...\n")); + + while( (Error = GetIfTable(Table, &Size, 0 )) == + ERROR_INSUFFICIENT_BUFFER ) { + DH_DbgPrint(MID_TRACE,("Error %d, New Buffer Size: %d\n", Error, Size)); + free( Table ); + Table = malloc( Size ); + } + + if( Error != NO_ERROR ) goto term; + + DH_DbgPrint(MID_TRACE,("Got Adapter List (%d entries)\n", Table->dwNumEntries)); + + for( i = 0; i < Table->dwNumEntries; i++ ) { + DH_DbgPrint(MID_TRACE,("Getting adapter %d attributes\n", i)); + Adapter = calloc( sizeof( DHCP_ADAPTER ) + Table->table[i].dwMtu, 1 ); + + if( Adapter ) { + memcpy( &Adapter->IfMib, &Table->table[i], + sizeof(Adapter->IfMib) ); + GetAddress( Adapter ); + InsertTailList( &AdapterList, &Adapter->ListEntry ); + Adapter->DhclientInfo.next = ifi; + Adapter->DhclientInfo.client = &Adapter->DhclientState; + Adapter->DhclientInfo.rbuf = Adapter->recv_buf; + Adapter->DhclientInfo.rbuf_max = Table->table[i].dwMtu; + Adapter->DhclientInfo.rbuf_len = + Adapter->DhclientInfo.rbuf_offset = 0; + Adapter->DhclientInfo.rfdesc = + Adapter->DhclientInfo.wfdesc = + socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + Adapter->ListenAddr.sin_family = AF_INET; + Adapter->ListenAddr.sin_port = htons(LOCAL_PORT); + Adapter->BindStatus = + (bind( Adapter->DhclientInfo.rfdesc, + (struct sockaddr *)&Adapter->ListenAddr, + sizeof(Adapter->ListenAddr) ) == 0) ? + 0 : WSAGetLastError(); + Adapter->DhclientState.config = &Adapter->DhclientConfig; + Adapter->DhclientConfig.initial_interval = DHCP_DISCOVER_INTERVAL; + Adapter->DhclientConfig.retry_interval = DHCP_DISCOVER_INTERVAL; + Adapter->DhclientConfig.select_interval = 1; + Adapter->DhclientConfig.reboot_timeout = DHCP_REBOOT_TIMEOUT; + Adapter->DhclientConfig.backoff_cutoff = DHCP_BACKOFF_MAX; + Adapter->DhclientState.interval = + Adapter->DhclientConfig.retry_interval; + strncpy(Adapter->DhclientInfo.name, Adapter->IfMib.bDescr, + sizeof(Adapter->DhclientInfo.name)); + DH_DbgPrint(MID_TRACE,("Adapter Name: [%s] (Bind Status %x)\n", + Adapter->DhclientInfo.name, + Adapter->BindStatus)); + ifi = &Adapter->DhclientInfo; + } + } + + DH_DbgPrint(MID_TRACE,("done with AdapterInit\n")); + +term: + if( Table ) free( Table ); +} + +void AdapterStop() { + PLIST_ENTRY ListEntry; + PDHCP_ADAPTER Adapter; + while( !IsListEmpty( &AdapterList ) ) { + ListEntry = (PLIST_ENTRY)RemoveHeadList( &AdapterList ); + Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry ); + free( Adapter ); + } + WSACleanup(); +} + +PDHCP_ADAPTER AdapterFindIndex( unsigned int indx ) { + PDHCP_ADAPTER Adapter; + PLIST_ENTRY ListEntry; + + for( ListEntry = AdapterList.Flink; + ListEntry != &AdapterList; + ListEntry = ListEntry->Flink ) { + Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry ); + if( Adapter->IfMib.dwIndex == indx ) return Adapter; + } + + return NULL; +} + +PDHCP_ADAPTER AdapterFindName( const WCHAR *name ) { + PDHCP_ADAPTER Adapter; + PLIST_ENTRY ListEntry; + + for( ListEntry = AdapterList.Flink; + ListEntry != &AdapterList; + ListEntry = ListEntry->Flink ) { + Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry ); + if( !wcsicmp( Adapter->IfMib.wszName, name ) ) return Adapter; + } + + return NULL; +} + +PDHCP_ADAPTER AdapterGetFirst() { + if( IsListEmpty( &AdapterList ) ) return NULL; else { + return CONTAINING_RECORD + ( AdapterList.Flink, DHCP_ADAPTER, ListEntry ); + } +} + +PDHCP_ADAPTER AdapterGetNext( PDHCP_ADAPTER This ) +{ + if( This->ListEntry.Flink == &AdapterList ) return NULL; + return CONTAINING_RECORD + ( This->ListEntry.Flink, DHCP_ADAPTER, ListEntry ); +} + +void if_register_send(struct interface_info *ip) { + +} + +void if_register_receive(struct interface_info *ip) { +} diff --git a/reactos/subsys/system/dhcp/alloc.c b/reactos/subsys/system/dhcp/alloc.c new file mode 100644 index 00000000000..5894a96e6f3 --- /dev/null +++ b/reactos/subsys/system/dhcp/alloc.c @@ -0,0 +1,79 @@ +/* $OpenBSD: alloc.c,v 1.9 2004/05/04 20:28:40 deraadt Exp $ */ + +/* Memory allocation... */ + +/* + * Copyright (c) 1995, 1996, 1998 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#include "rosdhcp.h" +#include "dhcpd.h" + +struct string_list * +new_string_list(size_t size) +{ + struct string_list *rval; + + rval = calloc(1, sizeof(struct string_list) + size); + if (rval != NULL) + rval->string = ((char *)rval) + sizeof(struct string_list); + return (rval); +} + +struct hash_table * +new_hash_table(int count) +{ + struct hash_table *rval; + + rval = calloc(1, sizeof(struct hash_table) - + (DEFAULT_HASH_SIZE * sizeof(struct hash_bucket *)) + + (count * sizeof(struct hash_bucket *))); + if (rval == NULL) + return (NULL); + rval->hash_count = count; + return (rval); +} + +struct hash_bucket * +new_hash_bucket(void) +{ + struct hash_bucket *rval = calloc(1, sizeof(struct hash_bucket)); + + return (rval); +} + +void free_hash_bucket(struct hash_bucket *hb) { free(hb); } diff --git a/reactos/subsys/system/dhcp/compat.c b/reactos/subsys/system/dhcp/compat.c new file mode 100644 index 00000000000..34ed1d488cd --- /dev/null +++ b/reactos/subsys/system/dhcp/compat.c @@ -0,0 +1,40 @@ +#include "rosdhcp.h" +#include "dhcpd.h" +#include "stdint.h" + +size_t strlcpy(char *d, const char *s, size_t bufsize) +{ + size_t len = strlen(s); + size_t ret = len; + if (bufsize > 0) { + if (len >= bufsize) + len = bufsize-1; + memcpy(d, s, len); + d[len] = 0; + } + return ret; +} + +// not really random :( +u_int32_t arc4random() +{ + static int did_srand = 0; + u_int32_t ret; + + if (!did_srand) { + srand(0); + did_srand = 1; + } + + ret = rand() << 10 ^ rand(); + return ret; +} + +int inet_aton(const char *cp, struct in_addr *inp) +{ + inp->S_un.S_addr = inet_addr(cp); + if (INADDR_NONE == inp->S_un.S_addr) + return 0; + + return 1; +} diff --git a/reactos/subsys/system/dhcp/design.txt b/reactos/subsys/system/dhcp/design.txt new file mode 100644 index 00000000000..17c9a29194b --- /dev/null +++ b/reactos/subsys/system/dhcp/design.txt @@ -0,0 +1,33 @@ +Acknowledgements: + + Tinus provided the initial port of these dhclient file. + +Ok I need these things: + +1) Adapter concept thingy + + Needs a name and index + Current IP address etc + interface_info + + Must be able to get one from an adapter index or name + Must query the ip address and such + Must be able to set the address + +2) System state doodad + + List of adapters + List of parameter changes + List of persistent stuff + + Must be able to initialize from the registry + (persistent stuff, some adapter info) + Save changes to persistent set + +3) Parameter change set + + TODO + +4) Persistent queries + + TODO \ No newline at end of file diff --git a/reactos/subsys/system/dhcp/dhclient.c b/reactos/subsys/system/dhcp/dhclient.c new file mode 100644 index 00000000000..9ead8b89419 --- /dev/null +++ b/reactos/subsys/system/dhcp/dhclient.c @@ -0,0 +1,2308 @@ +/* $OpenBSD: dhclient.c,v 1.62 2004/12/05 18:35:51 deraadt Exp $ */ + +/* + * Copyright 2004 Henning Brauer + * Copyright (c) 1995, 1996, 1997, 1998, 1999 + * The Internet Software Consortium. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + * + * This client was substantially modified and enhanced by Elliot Poger + * for use on Linux while he was working on the MosquitoNet project at + * Stanford. + * + * The current version owes much to Elliot's Linux enhancements, but + * was substantially reorganized and partially rewritten by Ted Lemon + * so as to use the same networking framework that the Internet Software + * Consortium DHCP server uses. Much system-specific configuration code + * was moved into a shell script so that as support for more operating + * systems is added, it will not be necessary to port and maintain + * system-specific configuration code to these operating systems - instead, + * the shell script can invoke the native tools to accomplish the same + * purpose. + */ + +#include +#include "rosdhcp.h" +#include "dhcpd.h" +#include "privsep.h" + +#define PERIOD 0x2e +#define hyphenchar(c) ((c) == 0x2d) +#define bslashchar(c) ((c) == 0x5c) +#define periodchar(c) ((c) == PERIOD) +#define asterchar(c) ((c) == 0x2a) +#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \ + ((c) >= 0x61 && (c) <= 0x7a)) +#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) + +#define borderchar(c) (alphachar(c) || digitchar(c)) +#define middlechar(c) (borderchar(c) || hyphenchar(c)) +#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) + +unsigned long debug_trace_level = DEBUG_ULTRA; +time_t cur_time; +time_t default_lease_time = 43200; /* 12 hours... */ + +char *path_dhclient_conf = _PATH_DHCLIENT_CONF; +char *path_dhclient_db = NULL; + +int log_perror = 1; +int privfd; +//int nullfd = -1; + +struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; +struct in_addr inaddr_any; +struct sockaddr_in sockaddr_broadcast; + +/* + * ASSERT_STATE() does nothing now; it used to be + * assert (state_is == state_shouldbe). + */ +#define ASSERT_STATE(state_is, state_shouldbe) {} + +#define TIME_MAX 2147483647 + +int log_priority; +int no_daemon; +int unknown_ok = 1; +int routefd; + +struct interface_info *ifi = NULL; + +void usage(void); +int check_option(struct client_lease *l, int option); +int ipv4addrs(char * buf); +int res_hnok(const char *dn); +char *option_as_string(unsigned int code, unsigned char *data, int len); +int fork_privchld(int, int); + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +time_t scripttime; + +#if 0 + +int +findproto(char *cp, int n) +{ + struct sockaddr *sa; + int i; + + if (n == 0) + return -1; + for (i = 1; i; i <<= 1) { + if (i & n) { + sa = (struct sockaddr *)cp; + switch (i) { + case RTA_IFA: + case RTA_DST: + case RTA_GATEWAY: + case RTA_NETMASK: + if (sa->sa_family == AF_INET) + return AF_INET; + if (sa->sa_family == AF_INET6) + return AF_INET6; + break; + case RTA_IFP: + break; + } + ADVANCE(cp, sa); + } + } + return (-1); +} + + +struct sockaddr * +get_ifa(char *cp, int n) +{ + struct sockaddr *sa; + int i; + + if (n == 0) + return (NULL); + for (i = 1; i; i <<= 1) + if (i & n) { + sa = (struct sockaddr *)cp; + if (i == RTA_IFA) + return (sa); + ADVANCE(cp, sa); + } + + return (NULL); +} +struct iaddr defaddr = { 4 }; + +/* ARGSUSED */ +void +routehandler(struct protocol *p) +{ + char msg[2048]; + struct rt_msghdr *rtm; + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + struct if_announcemsghdr *ifan; + struct client_lease *l; + time_t t = time(NULL); + struct sockaddr *sa; + struct iaddr a; + ssize_t n; + + n = read(routefd, &msg, sizeof(msg)); + rtm = (struct rt_msghdr *)msg; + if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen || + rtm->rtm_version != RTM_VERSION) + return; + + switch (rtm->rtm_type) { + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)rtm; + if (ifam->ifam_index != ifi->index) + break; + if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET) + break; + if (ifi == NULL) + goto die; + sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs); + if (sa == NULL) + goto die; + + if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf)) + error("king bula sez: len mismatch"); + memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len); + if (addr_eq(a, defaddr)) + break; + + for (l = ifi->client->active; l != NULL; l = l->next) + if (addr_eq(a, l->address)) + break; + + if (l != NULL) /* new addr is the one we set */ + break; + + goto die; + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + if (ifam->ifam_index != ifi->index) + break; + if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET) + break; + if (scripttime == 0 || t < scripttime + 10) + break; + goto die; + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + if (ifm->ifm_index != ifi->index) + break; + if ((rtm->rtm_flags & RTF_UP) == 0) + goto die; + break; + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *)rtm; + if (ifan->ifan_what == IFAN_DEPARTURE && + ifan->ifan_index == ifi->index) + goto die; + break; + default: + break; + } + return; + +die: + script_init("FAIL", NULL); + if (ifi->client->alias) + script_write_params("alias_", ifi->client->alias); + script_go(); + exit(1); +} +#endif + +int +main(int argc, char *argv[]) +{ + extern char *__progname; + int ch, fd, quiet = 0, i = 0; + int pipe_fd[2]; + struct passwd *pw; + + AdapterInit(); + + tzset(); + time(&cur_time); + + memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast)); + sockaddr_broadcast.sin_family = AF_INET; + sockaddr_broadcast.sin_port = htons(REMOTE_PORT); + sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; + inaddr_any.s_addr = INADDR_ANY; + + DH_DbgPrint(MID_TRACE,("DHCP Service Started\n")); + + read_client_conf(); + + if (!interface_link_status(ifi->name)) { + DH_DbgPrint(MID_TRACE,("%s: no link ", ifi->name)); + Sleep(1000); + while (!interface_link_status(ifi->name)) { + DH_DbgPrint(MID_TRACE,(".")); + if (++i > 10) { + DH_DbgPrint(MID_TRACE,("Giving up for now on adapter [%s]\n", ifi->name)); + } + Sleep(1000); + } + DH_DbgPrint(MID_TRACE,("Got link on [%s]\n", ifi->name)); + } + + DH_DbgPrint(MID_TRACE,("Discover Interfaces\n")); + + /* set up the interface */ + discover_interfaces(ifi); + + DH_DbgPrint + (MID_TRACE, + ("Setting init state and restarting interface %p\n",ifi)); + + ifi->client->state = S_INIT; + state_reboot(ifi); + + bootp_packet_handler = do_packet; + + DH_DbgPrint(MID_TRACE,("Going into dispatch()\n")); + + dispatch(); + + /* not reached */ + return (0); +} + +void +usage(void) +{ +// extern char *__progname; + +// fprintf(stderr, "usage: %s [-dqu] ", __progname); + fprintf(stderr, "usage: dhclient [-dqu] "); + fprintf(stderr, "[-c conffile] [-l leasefile] interface\n"); + exit(1); +} + +/* + * Individual States: + * + * Each routine is called from the dhclient_state_machine() in one of + * these conditions: + * -> entering INIT state + * -> recvpacket_flag == 0: timeout in this state + * -> otherwise: received a packet in this state + * + * Return conditions as handled by dhclient_state_machine(): + * Returns 1, sendpacket_flag = 1: send packet, reset timer. + * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). + * Returns 0: finish the nap which was interrupted for no good reason. + * + * Several per-interface variables are used to keep track of the process: + * active_lease: the lease that is being used on the interface + * (null pointer if not configured yet). + * offered_leases: leases corresponding to DHCPOFFER messages that have + * been sent to us by DHCP servers. + * acked_leases: leases corresponding to DHCPACK messages that have been + * sent to us by DHCP servers. + * sendpacket: DHCP packet we're trying to send. + * destination: IP address to send sendpacket to + * In addition, there are several relevant per-lease variables. + * T1_expiry, T2_expiry, lease_expiry: lease milestones + * In the active lease, these control the process of renewing the lease; + * In leases on the acked_leases list, this simply determines when we + * can no longer legitimately use the lease. + */ + +void +state_reboot(void *ipp) +{ + struct interface_info *ip = ipp; + + /* If we don't remember an active lease, go straight to INIT. */ + if (!ip->client->active || ip->client->active->is_bootp) { + state_init(ip); + return; + } + + /* We are in the rebooting state. */ + ip->client->state = S_REBOOTING; + + /* make_request doesn't initialize xid because it normally comes + from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, + so pick an xid now. */ + ip->client->xid = arc4random(); + + /* Make a DHCPREQUEST packet, and set appropriate per-interface + flags. */ + make_request(ip, ip->client->active); + ip->client->destination = iaddr_broadcast; + ip->client->first_sending = cur_time; + ip->client->interval = ip->client->config->initial_interval; + + /* Zap the medium list... */ + ip->client->medium = NULL; + + /* Send out the first DHCPREQUEST packet. */ + send_request(ip); +} + +/* + * Called when a lease has completely expired and we've + * been unable to renew it. + */ +void +state_init(void *ipp) +{ + struct interface_info *ip = ipp; + + ASSERT_STATE(state, S_INIT); + + /* Make a DHCPDISCOVER packet, and set appropriate per-interface + flags. */ + make_discover(ip, ip->client->active); + ip->client->xid = ip->client->packet.xid; + ip->client->destination = iaddr_broadcast; + ip->client->state = S_SELECTING; + ip->client->first_sending = cur_time; + ip->client->interval = ip->client->config->initial_interval; + + /* Add an immediate timeout to cause the first DHCPDISCOVER packet + to go out. */ + send_discover(ip); +} + +/* + * state_selecting is called when one or more DHCPOFFER packets + * have been received and a configurable period of time has passed. + */ +void +state_selecting(void *ipp) +{ + struct interface_info *ip = ipp; + struct client_lease *lp, *next, *picked; + + ASSERT_STATE(state, S_SELECTING); + + /* Cancel state_selecting and send_discover timeouts, since either + one could have got us here. */ + cancel_timeout(state_selecting, ip); + cancel_timeout(send_discover, ip); + + /* We have received one or more DHCPOFFER packets. Currently, + the only criterion by which we judge leases is whether or + not we get a response when we arp for them. */ + picked = NULL; + for (lp = ip->client->offered_leases; lp; lp = next) { + next = lp->next; + + /* Check to see if we got an ARPREPLY for the address + in this particular lease. */ + if (!picked) { + script_init("ARPCHECK", lp->medium); + script_write_params("check_", lp); + + /* If the ARPCHECK code detects another + machine using the offered address, it exits + nonzero. We need to send a DHCPDECLINE and + toss the lease. */ + if (script_go()) { + make_decline(ip, lp); + send_decline(ip); + goto freeit; + } + picked = lp; + picked->next = NULL; + } else { +freeit: + free_client_lease(lp); + } + } + ip->client->offered_leases = NULL; + + /* If we just tossed all the leases we were offered, go back + to square one. */ + if (!picked) { + ip->client->state = S_INIT; + state_init(ip); + return; + } + + /* If it was a BOOTREPLY, we can just take the address right now. */ + if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) { + ip->client->new = picked; + + /* Make up some lease expiry times + XXX these should be configurable. */ + ip->client->new->expiry = cur_time + 12000; + ip->client->new->renewal += cur_time + 8000; + ip->client->new->rebind += cur_time + 10000; + + ip->client->state = S_REQUESTING; + + /* Bind to the address we received. */ + bind_lease(ip); + return; + } + + /* Go to the REQUESTING state. */ + ip->client->destination = iaddr_broadcast; + ip->client->state = S_REQUESTING; + ip->client->first_sending = cur_time; + ip->client->interval = ip->client->config->initial_interval; + + /* Make a DHCPREQUEST packet from the lease we picked. */ + make_request(ip, picked); + ip->client->xid = ip->client->packet.xid; + + /* Toss the lease we picked - we'll get it back in a DHCPACK. */ + free_client_lease(picked); + + /* Add an immediate timeout to send the first DHCPREQUEST packet. */ + send_request(ip); +} + +/* state_requesting is called when we receive a DHCPACK message after + having sent out one or more DHCPREQUEST packets. */ + +void +dhcpack(struct packet *packet) +{ + struct interface_info *ip = packet->interface; + struct client_lease *lease; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (packet->interface->client->xid != packet->raw->xid || + (packet->interface->hw_address.hlen != packet->raw->hlen) || + (memcmp(packet->interface->hw_address.haddr, + packet->raw->chaddr, packet->raw->hlen))) + return; + + if (ip->client->state != S_REBOOTING && + ip->client->state != S_REQUESTING && + ip->client->state != S_RENEWING && + ip->client->state != S_REBINDING) + return; + + note("DHCPACK from %s", piaddr(packet->client_addr)); + + lease = packet_to_lease(packet); + if (!lease) { + note("packet_to_lease failed."); + return; + } + + ip->client->new = lease; + + /* Stop resending DHCPREQUEST. */ + cancel_timeout(send_request, ip); + + /* Figure out the lease time. */ + if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data) + ip->client->new->expiry = getULong( + ip->client->new->options[DHO_DHCP_LEASE_TIME].data); + else + ip->client->new->expiry = default_lease_time; + /* A number that looks negative here is really just very large, + because the lease expiry offset is unsigned. */ + if (ip->client->new->expiry < 0) + ip->client->new->expiry = TIME_MAX; + /* XXX should be fixed by resetting the client state */ + if (ip->client->new->expiry < 60) + ip->client->new->expiry = 60; + + /* Take the server-provided renewal time if there is one; + otherwise figure it out according to the spec. */ + if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len) + ip->client->new->renewal = getULong( + ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data); + else + ip->client->new->renewal = ip->client->new->expiry / 2; + + /* Same deal with the rebind time. */ + if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len) + ip->client->new->rebind = getULong( + ip->client->new->options[DHO_DHCP_REBINDING_TIME].data); + else + ip->client->new->rebind = ip->client->new->renewal + + ip->client->new->renewal / 2 + ip->client->new->renewal / 4; + + ip->client->new->expiry += cur_time; + /* Lease lengths can never be negative. */ + if (ip->client->new->expiry < cur_time) + ip->client->new->expiry = TIME_MAX; + ip->client->new->renewal += cur_time; + if (ip->client->new->renewal < cur_time) + ip->client->new->renewal = TIME_MAX; + ip->client->new->rebind += cur_time; + if (ip->client->new->rebind < cur_time) + ip->client->new->rebind = TIME_MAX; + + bind_lease(ip); +} + +void +bind_lease(struct interface_info *ip) +{ + /* Remember the medium. */ + ip->client->new->medium = ip->client->medium; + + /* Write out the new lease. */ + write_client_lease(ip, ip->client->new, 0); + + /* Run the client script with the new parameters. */ + script_init((ip->client->state == S_REQUESTING ? "BOUND" : + (ip->client->state == S_RENEWING ? "RENEW" : + (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))), + ip->client->new->medium); + if (ip->client->active && ip->client->state != S_REBOOTING) + script_write_params("old_", ip->client->active); + script_write_params("new_", ip->client->new); + if (ip->client->alias) + script_write_params("alias_", ip->client->alias); + script_go(); + + /* Replace the old active lease with the new one. */ + if (ip->client->active) + free_client_lease(ip->client->active); + ip->client->active = ip->client->new; + ip->client->new = NULL; + + /* Set up a timeout to start the renewal process. */ + add_timeout(ip->client->active->renewal, state_bound, ip); + + note("bound to %s -- renewal in %d seconds.", + piaddr(ip->client->active->address), + ip->client->active->renewal - cur_time); + ip->client->state = S_BOUND; + reinitialize_interfaces(); +// go_daemon(); +} + +/* + * state_bound is called when we've successfully bound to a particular + * lease, but the renewal time on that lease has expired. We are + * expected to unicast a DHCPREQUEST to the server that gave us our + * original lease. + */ +void +state_bound(void *ipp) +{ + struct interface_info *ip = ipp; + + ASSERT_STATE(state, S_BOUND); + + /* T1 has expired. */ + make_request(ip, ip->client->active); + ip->client->xid = ip->client->packet.xid; + + if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) { + memcpy(ip->client->destination.iabuf, ip->client->active-> + options[DHO_DHCP_SERVER_IDENTIFIER].data, 4); + ip->client->destination.len = 4; + } else + ip->client->destination = iaddr_broadcast; + + ip->client->first_sending = cur_time; + ip->client->interval = ip->client->config->initial_interval; + ip->client->state = S_RENEWING; + + /* Send the first packet immediately. */ + send_request(ip); +} + +void +bootp(struct packet *packet) +{ + struct iaddrlist *ap; + + if (packet->raw->op != BOOTREPLY) + return; + + /* If there's a reject list, make sure this packet's sender isn't + on it. */ + for (ap = packet->interface->client->config->reject_list; + ap; ap = ap->next) { + if (addr_eq(packet->client_addr, ap->addr)) { + note("BOOTREPLY from %s rejected.", piaddr(ap->addr)); + return; + } + } + dhcpoffer(packet); +} + +void +dhcp(struct packet *packet) +{ + struct iaddrlist *ap; + void (*handler)(struct packet *); + char *type; + + switch (packet->packet_type) { + case DHCPOFFER: + handler = dhcpoffer; + type = "DHCPOFFER"; + break; + case DHCPNAK: + handler = dhcpnak; + type = "DHCPNACK"; + break; + case DHCPACK: + handler = dhcpack; + type = "DHCPACK"; + break; + default: + return; + } + + /* If there's a reject list, make sure this packet's sender isn't + on it. */ + for (ap = packet->interface->client->config->reject_list; + ap; ap = ap->next) { + if (addr_eq(packet->client_addr, ap->addr)) { + note("%s from %s rejected.", type, piaddr(ap->addr)); + return; + } + } + (*handler)(packet); +} + +void +dhcpoffer(struct packet *packet) +{ + struct interface_info *ip = packet->interface; + struct client_lease *lease, *lp; + int i; + int arp_timeout_needed, stop_selecting; + char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ? + "DHCPOFFER" : "BOOTREPLY"; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (ip->client->state != S_SELECTING || + packet->interface->client->xid != packet->raw->xid || + (packet->interface->hw_address.hlen != packet->raw->hlen) || + (memcmp(packet->interface->hw_address.haddr, + packet->raw->chaddr, packet->raw->hlen))) + return; + + note("%s from %s", name, piaddr(packet->client_addr)); + + + /* If this lease doesn't supply the minimum required parameters, + blow it off. */ + for (i = 0; ip->client->config->required_options[i]; i++) { + if (!packet->options[ip->client->config-> + required_options[i]].len) { + note("%s isn't satisfactory.", name); + return; + } + } + + /* If we've already seen this lease, don't record it again. */ + for (lease = ip->client->offered_leases; + lease; lease = lease->next) { + if (lease->address.len == sizeof(packet->raw->yiaddr) && + !memcmp(lease->address.iabuf, + &packet->raw->yiaddr, lease->address.len)) { + debug("%s already seen.", name); + return; + } + } + + lease = packet_to_lease(packet); + if (!lease) { + note("packet_to_lease failed."); + return; + } + + /* If this lease was acquired through a BOOTREPLY, record that + fact. */ + if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len) + lease->is_bootp = 1; + + /* Record the medium under which this lease was offered. */ + lease->medium = ip->client->medium; + + /* Send out an ARP Request for the offered IP address. */ + script_init("ARPSEND", lease->medium); + script_write_params("check_", lease); + /* If the script can't send an ARP request without waiting, + we'll be waiting when we do the ARPCHECK, so don't wait now. */ + if (script_go()) + arp_timeout_needed = 0; + else + arp_timeout_needed = 2; + + /* Figure out when we're supposed to stop selecting. */ + stop_selecting = + ip->client->first_sending + ip->client->config->select_interval; + + /* If this is the lease we asked for, put it at the head of the + list, and don't mess with the arp request timeout. */ + if (lease->address.len == ip->client->requested_address.len && + !memcmp(lease->address.iabuf, + ip->client->requested_address.iabuf, + ip->client->requested_address.len)) { + lease->next = ip->client->offered_leases; + ip->client->offered_leases = lease; + } else { + /* If we already have an offer, and arping for this + offer would take us past the selection timeout, + then don't extend the timeout - just hope for the + best. */ + if (ip->client->offered_leases && + (cur_time + arp_timeout_needed) > stop_selecting) + arp_timeout_needed = 0; + + /* Put the lease at the end of the list. */ + lease->next = NULL; + if (!ip->client->offered_leases) + ip->client->offered_leases = lease; + else { + for (lp = ip->client->offered_leases; lp->next; + lp = lp->next) + ; /* nothing */ + lp->next = lease; + } + } + + /* If we're supposed to stop selecting before we've had time + to wait for the ARPREPLY, add some delay to wait for + the ARPREPLY. */ + if (stop_selecting - cur_time < arp_timeout_needed) + stop_selecting = cur_time + arp_timeout_needed; + + /* If the selecting interval has expired, go immediately to + state_selecting(). Otherwise, time out into + state_selecting at the select interval. */ + if (stop_selecting <= 0) + state_selecting(ip); + else { + add_timeout(stop_selecting, state_selecting, ip); + cancel_timeout(send_discover, ip); + } +} + +/* Allocate a client_lease structure and initialize it from the parameters + in the specified packet. */ + +struct client_lease * +packet_to_lease(struct packet *packet) +{ + struct client_lease *lease; + int i; + + lease = malloc(sizeof(struct client_lease)); + + if (!lease) { + warning("dhcpoffer: no memory to record lease."); + return (NULL); + } + + memset(lease, 0, sizeof(*lease)); + + /* Copy the lease options. */ + for (i = 0; i < 256; i++) { + if (packet->options[i].len) { + lease->options[i].data = + malloc(packet->options[i].len + 1); + if (!lease->options[i].data) { + warning("dhcpoffer: no memory for option %d", i); + free_client_lease(lease); + return (NULL); + } else { + memcpy(lease->options[i].data, + packet->options[i].data, + packet->options[i].len); + lease->options[i].len = + packet->options[i].len; + lease->options[i].data[lease->options[i].len] = + 0; + } + if (!check_option(lease,i)) { + /* ignore a bogus lease offer */ + warning("Invalid lease option - ignoring offer"); + free_client_lease(lease); + return (NULL); + } + } + } + + lease->address.len = sizeof(packet->raw->yiaddr); + memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len); + + /* If the server name was filled out, copy it. */ + if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || + !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) && + packet->raw->sname[0]) { + lease->server_name = malloc(DHCP_SNAME_LEN + 1); + if (!lease->server_name) { + warning("dhcpoffer: no memory for server name."); + free_client_lease(lease); + return (NULL); + } + memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN); + lease->server_name[DHCP_SNAME_LEN]='\0'; + if (!res_hnok(lease->server_name) ) { + warning("Bogus server name %s", lease->server_name ); + free_client_lease(lease); + return (NULL); + } + + } + + /* Ditto for the filename. */ + if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || + !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) && + packet->raw->file[0]) { + /* Don't count on the NUL terminator. */ + lease->filename = malloc(DHCP_FILE_LEN + 1); + if (!lease->filename) { + warning("dhcpoffer: no memory for filename."); + free_client_lease(lease); + return (NULL); + } + memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN); + lease->filename[DHCP_FILE_LEN]='\0'; + } + return lease; +} + +void +dhcpnak(struct packet *packet) +{ + struct interface_info *ip = packet->interface; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (packet->interface->client->xid != packet->raw->xid || + (packet->interface->hw_address.hlen != packet->raw->hlen) || + (memcmp(packet->interface->hw_address.haddr, + packet->raw->chaddr, packet->raw->hlen))) + return; + + if (ip->client->state != S_REBOOTING && + ip->client->state != S_REQUESTING && + ip->client->state != S_RENEWING && + ip->client->state != S_REBINDING) + return; + + note("DHCPNAK from %s", piaddr(packet->client_addr)); + + if (!ip->client->active) { + note("DHCPNAK with no active lease.\n"); + return; + } + + free_client_lease(ip->client->active); + ip->client->active = NULL; + + /* Stop sending DHCPREQUEST packets... */ + cancel_timeout(send_request, ip); + + ip->client->state = S_INIT; + state_init(ip); +} + +/* Send out a DHCPDISCOVER packet, and set a timeout to send out another + one after the right interval has expired. If we don't get an offer by + the time we reach the panic interval, call the panic function. */ + +void +send_discover(void *ipp) +{ + struct interface_info *ip = ipp; + int interval, increase = 1; + + DH_DbgPrint(MID_TRACE,("Doing discover on interface %p\n",ip)); + + /* Figure out how long it's been since we started transmitting. */ + interval = cur_time - ip->client->first_sending; + + /* If we're past the panic timeout, call the script and tell it + we haven't found anything for this interface yet. */ + if (interval > ip->client->config->timeout) { + state_panic(ip); + return; + } + + /* If we're selecting media, try the whole list before doing + the exponential backoff, but if we've already received an + offer, stop looping, because we obviously have it right. */ + if (!ip->client->offered_leases && + ip->client->config->media) { + int fail = 0; +again: + if (ip->client->medium) { + ip->client->medium = ip->client->medium->next; + increase = 0; + } + if (!ip->client->medium) { + if (fail) + error("No valid media types for %s!", ip->name); + ip->client->medium = ip->client->config->media; + increase = 1; + } + + note("Trying medium \"%s\" %d", ip->client->medium->string, + increase); + script_init("MEDIUM", ip->client->medium); + if (script_go()) + goto again; + } + + /* + * If we're supposed to increase the interval, do so. If it's + * currently zero (i.e., we haven't sent any packets yet), set + * it to one; otherwise, add to it a random number between zero + * and two times itself. On average, this means that it will + * double with every transmission. + */ + if (increase) { + if (!ip->client->interval) + ip->client->interval = + ip->client->config->initial_interval; + else { + ip->client->interval += (arc4random() >> 2) % + (2 * ip->client->interval); + } + + /* Don't backoff past cutoff. */ + if (ip->client->interval > + ip->client->config->backoff_cutoff) + ip->client->interval = + ((ip->client->config->backoff_cutoff / 2) + + ((arc4random() >> 2) % + ip->client->config->backoff_cutoff)); + } else if (!ip->client->interval) + ip->client->interval = + ip->client->config->initial_interval; + + /* If the backoff would take us to the panic timeout, just use that + as the interval. */ + if (cur_time + ip->client->interval > + ip->client->first_sending + ip->client->config->timeout) + ip->client->interval = + (ip->client->first_sending + + ip->client->config->timeout) - cur_time + 1; + + /* Record the number of seconds since we started sending. */ + if (interval < 65536) + ip->client->packet.secs = htons(interval); + else + ip->client->packet.secs = htons(65535); + ip->client->secs = ip->client->packet.secs; + + note("DHCPDISCOVER on %s to %s port %d interval %d", + ip->name, inet_ntoa(sockaddr_broadcast.sin_addr), + ntohs(sockaddr_broadcast.sin_port), ip->client->interval); + + /* Send out a packet. */ + (void)send_packet(ip, &ip->client->packet, ip->client->packet_length, + inaddr_any, &sockaddr_broadcast, NULL); + + DH_DbgPrint(MID_TRACE,("discover timeout: now %x -> then %x\n", + cur_time, cur_time + ip->client->interval)); + + add_timeout(cur_time + ip->client->interval, send_discover, ip); +} + +/* + * state_panic gets called if we haven't received any offers in a preset + * amount of time. When this happens, we try to use existing leases + * that haven't yet expired, and failing that, we call the client script + * and hope it can do something. + */ +void +state_panic(void *ipp) +{ + struct interface_info *ip = ipp; + struct client_lease *loop = ip->client->active; + struct client_lease *lp; + + note("No DHCPOFFERS received."); + + /* We may not have an active lease, but we may have some + predefined leases that we can try. */ + if (!ip->client->active && ip->client->leases) + goto activate_next; + + /* Run through the list of leases and see if one can be used. */ + while (ip->client->active) { + if (ip->client->active->expiry > cur_time) { + note("Trying recorded lease %s", + piaddr(ip->client->active->address)); + /* Run the client script with the existing + parameters. */ + script_init("TIMEOUT", + ip->client->active->medium); + script_write_params("new_", ip->client->active); + if (ip->client->alias) + script_write_params("alias_", + ip->client->alias); + + /* If the old lease is still good and doesn't + yet need renewal, go into BOUND state and + timeout at the renewal time. */ + if (!script_go()) { + if (cur_time < + ip->client->active->renewal) { + ip->client->state = S_BOUND; + note("bound: renewal in %d seconds.", + ip->client->active->renewal - + cur_time); + add_timeout( + ip->client->active->renewal, + state_bound, ip); + } else { + ip->client->state = S_BOUND; + note("bound: immediate renewal."); + state_bound(ip); + } + reinitialize_interfaces(); + //go_daemon(); + return; + } + } + + /* If there are no other leases, give up. */ + if (!ip->client->leases) { + ip->client->leases = ip->client->active; + ip->client->active = NULL; + break; + } + +activate_next: + /* Otherwise, put the active lease at the end of the + lease list, and try another lease.. */ + for (lp = ip->client->leases; lp->next; lp = lp->next) + ; + lp->next = ip->client->active; + if (lp->next) + lp->next->next = NULL; + ip->client->active = ip->client->leases; + ip->client->leases = ip->client->leases->next; + + /* If we already tried this lease, we've exhausted the + set of leases, so we might as well give up for + now. */ + if (ip->client->active == loop) + break; + else if (!loop) + loop = ip->client->active; + } + + /* No leases were available, or what was available didn't work, so + tell the shell script that we failed to allocate an address, + and try again later. */ + note("No working leases in persistent database - sleeping.\n"); + script_init("FAIL", NULL); + if (ip->client->alias) + script_write_params("alias_", ip->client->alias); + script_go(); + ip->client->state = S_INIT; + add_timeout(cur_time + ip->client->config->retry_interval, state_init, + ip); +// go_daemon(); +} + +void +send_request(void *ipp) +{ + struct interface_info *ip = ipp; + struct sockaddr_in destination; + struct in_addr from; + int interval; + + /* Figure out how long it's been since we started transmitting. */ + interval = cur_time - ip->client->first_sending; + + /* If we're in the INIT-REBOOT or REQUESTING state and we're + past the reboot timeout, go to INIT and see if we can + DISCOVER an address... */ + /* XXX In the INIT-REBOOT state, if we don't get an ACK, it + means either that we're on a network with no DHCP server, + or that our server is down. In the latter case, assuming + that there is a backup DHCP server, DHCPDISCOVER will get + us a new address, but we could also have successfully + reused our old address. In the former case, we're hosed + anyway. This is not a win-prone situation. */ + if ((ip->client->state == S_REBOOTING || + ip->client->state == S_REQUESTING) && + interval > ip->client->config->reboot_timeout) { +cancel: + ip->client->state = S_INIT; + cancel_timeout(send_request, ip); + state_init(ip); + return; + } + + /* If we're in the reboot state, make sure the media is set up + correctly. */ + if (ip->client->state == S_REBOOTING && + !ip->client->medium && + ip->client->active->medium ) { + script_init("MEDIUM", ip->client->active->medium); + + /* If the medium we chose won't fly, go to INIT state. */ + if (script_go()) + goto cancel; + + /* Record the medium. */ + ip->client->medium = ip->client->active->medium; + } + + /* If the lease has expired, relinquish the address and go back + to the INIT state. */ + if (ip->client->state != S_REQUESTING && + cur_time > ip->client->active->expiry) { + /* Run the client script with the new parameters. */ + script_init("EXPIRE", NULL); + script_write_params("old_", ip->client->active); + if (ip->client->alias) + script_write_params("alias_", ip->client->alias); + script_go(); + + /* Now do a preinit on the interface so that we can + discover a new address. */ + script_init("PREINIT", NULL); + if (ip->client->alias) + script_write_params("alias_", ip->client->alias); + script_go(); + + ip->client->state = S_INIT; + state_init(ip); + return; + } + + /* Do the exponential backoff... */ + if (!ip->client->interval) + ip->client->interval = ip->client->config->initial_interval; + else + ip->client->interval += ((arc4random() >> 2) % + (2 * ip->client->interval)); + + /* Don't backoff past cutoff. */ + if (ip->client->interval > + ip->client->config->backoff_cutoff) + ip->client->interval = + ((ip->client->config->backoff_cutoff / 2) + + ((arc4random() >> 2) % ip->client->interval)); + + /* If the backoff would take us to the expiry time, just set the + timeout to the expiry time. */ + if (ip->client->state != S_REQUESTING && + cur_time + ip->client->interval > + ip->client->active->expiry) + ip->client->interval = + ip->client->active->expiry - cur_time + 1; + + /* If the lease T2 time has elapsed, or if we're not yet bound, + broadcast the DHCPREQUEST rather than unicasting. */ + memset(&destination, 0, sizeof(destination)); + if (ip->client->state == S_REQUESTING || + ip->client->state == S_REBOOTING || + cur_time > ip->client->active->rebind) + destination.sin_addr.s_addr = INADDR_BROADCAST; + else + memcpy(&destination.sin_addr.s_addr, + ip->client->destination.iabuf, + sizeof(destination.sin_addr.s_addr)); + destination.sin_port = htons(REMOTE_PORT); + destination.sin_family = AF_INET; +// destination.sin_len = sizeof(destination); + + if (ip->client->state != S_REQUESTING) + memcpy(&from, ip->client->active->address.iabuf, + sizeof(from)); + else + from.s_addr = INADDR_ANY; + + /* Record the number of seconds since we started sending. */ + if (ip->client->state == S_REQUESTING) + ip->client->packet.secs = ip->client->secs; + else { + if (interval < 65536) + ip->client->packet.secs = htons(interval); + else + ip->client->packet.secs = htons(65535); + } + + note("DHCPREQUEST on %s to %s port %d", ip->name, + inet_ntoa(destination.sin_addr), ntohs(destination.sin_port)); + + /* Send out a packet. */ + (void) send_packet(ip, &ip->client->packet, ip->client->packet_length, + from, &destination, NULL); + + add_timeout(cur_time + ip->client->interval, send_request, ip); +} + +void +send_decline(void *ipp) +{ + struct interface_info *ip = ipp; + + note("DHCPDECLINE on %s to %s port %d", ip->name, + inet_ntoa(sockaddr_broadcast.sin_addr), + ntohs(sockaddr_broadcast.sin_port)); + + /* Send out a packet. */ + (void) send_packet(ip, &ip->client->packet, ip->client->packet_length, + inaddr_any, &sockaddr_broadcast, NULL); +} + +void +make_discover(struct interface_info *ip, struct client_lease *lease) +{ + unsigned char discover = DHCPDISCOVER; + struct tree_cache *options[256]; + struct tree_cache option_elements[256]; + int i; + + memset(option_elements, 0, sizeof(option_elements)); + memset(options, 0, sizeof(options)); + memset(&ip->client->packet, 0, sizeof(ip->client->packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ + i = DHO_DHCP_MESSAGE_TYPE; + options[i] = &option_elements[i]; + options[i]->value = &discover; + options[i]->len = sizeof(discover); + options[i]->buf_size = sizeof(discover); + options[i]->timeout = 0xFFFFFFFF; + + /* Request the options we want */ + i = DHO_DHCP_PARAMETER_REQUEST_LIST; + options[i] = &option_elements[i]; + options[i]->value = ip->client->config->requested_options; + options[i]->len = ip->client->config->requested_option_count; + options[i]->buf_size = + ip->client->config->requested_option_count; + options[i]->timeout = 0xFFFFFFFF; + + /* If we had an address, try to get it again. */ + if (lease) { + ip->client->requested_address = lease->address; + i = DHO_DHCP_REQUESTED_ADDRESS; + options[i] = &option_elements[i]; + options[i]->value = lease->address.iabuf; + options[i]->len = lease->address.len; + options[i]->buf_size = lease->address.len; + options[i]->timeout = 0xFFFFFFFF; + } else + ip->client->requested_address.len = 0; + + /* Send any options requested in the config file. */ + for (i = 0; i < 256; i++) + if (!options[i] && + ip->client->config->send_options[i].data) { + options[i] = &option_elements[i]; + options[i]->value = + ip->client->config->send_options[i].data; + options[i]->len = + ip->client->config->send_options[i].len; + options[i]->buf_size = + ip->client->config->send_options[i].len; + options[i]->timeout = 0xFFFFFFFF; + } + + /* Set up the option buffer... */ + ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, + options, 0, 0, 0, NULL, 0); + if (ip->client->packet_length < BOOTP_MIN_LEN) + ip->client->packet_length = BOOTP_MIN_LEN; + + ip->client->packet.op = BOOTREQUEST; + ip->client->packet.htype = ip->hw_address.htype; + ip->client->packet.hlen = ip->hw_address.hlen; + ip->client->packet.hops = 0; + ip->client->packet.xid = arc4random(); + ip->client->packet.secs = 0; /* filled in by send_discover. */ + ip->client->packet.flags = 0; + + memset(&(ip->client->packet.ciaddr), + 0, sizeof(ip->client->packet.ciaddr)); + memset(&(ip->client->packet.yiaddr), + 0, sizeof(ip->client->packet.yiaddr)); + memset(&(ip->client->packet.siaddr), + 0, sizeof(ip->client->packet.siaddr)); + memset(&(ip->client->packet.giaddr), + 0, sizeof(ip->client->packet.giaddr)); + memcpy(ip->client->packet.chaddr, + ip->hw_address.haddr, ip->hw_address.hlen); +} + + +void +make_request(struct interface_info *ip, struct client_lease * lease) +{ + unsigned char request = DHCPREQUEST; + struct tree_cache *options[256]; + struct tree_cache option_elements[256]; + int i; + + memset(options, 0, sizeof(options)); + memset(&ip->client->packet, 0, sizeof(ip->client->packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ + i = DHO_DHCP_MESSAGE_TYPE; + options[i] = &option_elements[i]; + options[i]->value = &request; + options[i]->len = sizeof(request); + options[i]->buf_size = sizeof(request); + options[i]->timeout = 0xFFFFFFFF; + + /* Request the options we want */ + i = DHO_DHCP_PARAMETER_REQUEST_LIST; + options[i] = &option_elements[i]; + options[i]->value = ip->client->config->requested_options; + options[i]->len = ip->client->config->requested_option_count; + options[i]->buf_size = + ip->client->config->requested_option_count; + options[i]->timeout = 0xFFFFFFFF; + + /* If we are requesting an address that hasn't yet been assigned + to us, use the DHCP Requested Address option. */ + if (ip->client->state == S_REQUESTING) { + /* Send back the server identifier... */ + i = DHO_DHCP_SERVER_IDENTIFIER; + options[i] = &option_elements[i]; + options[i]->value = lease->options[i].data; + options[i]->len = lease->options[i].len; + options[i]->buf_size = lease->options[i].len; + options[i]->timeout = 0xFFFFFFFF; + } + if (ip->client->state == S_REQUESTING || + ip->client->state == S_REBOOTING) { + ip->client->requested_address = lease->address; + i = DHO_DHCP_REQUESTED_ADDRESS; + options[i] = &option_elements[i]; + options[i]->value = lease->address.iabuf; + options[i]->len = lease->address.len; + options[i]->buf_size = lease->address.len; + options[i]->timeout = 0xFFFFFFFF; + } else + ip->client->requested_address.len = 0; + + /* Send any options requested in the config file. */ + for (i = 0; i < 256; i++) + if (!options[i] && + ip->client->config->send_options[i].data) { + options[i] = &option_elements[i]; + options[i]->value = + ip->client->config->send_options[i].data; + options[i]->len = + ip->client->config->send_options[i].len; + options[i]->buf_size = + ip->client->config->send_options[i].len; + options[i]->timeout = 0xFFFFFFFF; + } + + /* Set up the option buffer... */ + ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, + options, 0, 0, 0, NULL, 0); + if (ip->client->packet_length < BOOTP_MIN_LEN) + ip->client->packet_length = BOOTP_MIN_LEN; + + ip->client->packet.op = BOOTREQUEST; + ip->client->packet.htype = ip->hw_address.htype; + ip->client->packet.hlen = ip->hw_address.hlen; + ip->client->packet.hops = 0; + ip->client->packet.xid = ip->client->xid; + ip->client->packet.secs = 0; /* Filled in by send_request. */ + + /* If we own the address we're requesting, put it in ciaddr; + otherwise set ciaddr to zero. */ + if (ip->client->state == S_BOUND || + ip->client->state == S_RENEWING || + ip->client->state == S_REBINDING) { + memcpy(&ip->client->packet.ciaddr, + lease->address.iabuf, lease->address.len); + ip->client->packet.flags = 0; + } else { + memset(&ip->client->packet.ciaddr, 0, + sizeof(ip->client->packet.ciaddr)); + ip->client->packet.flags = 0; + } + + memset(&ip->client->packet.yiaddr, 0, + sizeof(ip->client->packet.yiaddr)); + memset(&ip->client->packet.siaddr, 0, + sizeof(ip->client->packet.siaddr)); + memset(&ip->client->packet.giaddr, 0, + sizeof(ip->client->packet.giaddr)); + memcpy(ip->client->packet.chaddr, + ip->hw_address.haddr, ip->hw_address.hlen); +} + +void +make_decline(struct interface_info *ip, struct client_lease *lease) +{ + struct tree_cache *options[256], message_type_tree; + struct tree_cache requested_address_tree; + struct tree_cache server_id_tree, client_id_tree; + unsigned char decline = DHCPDECLINE; + int i; + + memset(options, 0, sizeof(options)); + memset(&ip->client->packet, 0, sizeof(ip->client->packet)); + + /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */ + i = DHO_DHCP_MESSAGE_TYPE; + options[i] = &message_type_tree; + options[i]->value = &decline; + options[i]->len = sizeof(decline); + options[i]->buf_size = sizeof(decline); + options[i]->timeout = 0xFFFFFFFF; + + /* Send back the server identifier... */ + i = DHO_DHCP_SERVER_IDENTIFIER; + options[i] = &server_id_tree; + options[i]->value = lease->options[i].data; + options[i]->len = lease->options[i].len; + options[i]->buf_size = lease->options[i].len; + options[i]->timeout = 0xFFFFFFFF; + + /* Send back the address we're declining. */ + i = DHO_DHCP_REQUESTED_ADDRESS; + options[i] = &requested_address_tree; + options[i]->value = lease->address.iabuf; + options[i]->len = lease->address.len; + options[i]->buf_size = lease->address.len; + options[i]->timeout = 0xFFFFFFFF; + + /* Send the uid if the user supplied one. */ + i = DHO_DHCP_CLIENT_IDENTIFIER; + if (ip->client->config->send_options[i].len) { + options[i] = &client_id_tree; + options[i]->value = ip->client->config->send_options[i].data; + options[i]->len = ip->client->config->send_options[i].len; + options[i]->buf_size = ip->client->config->send_options[i].len; + options[i]->timeout = 0xFFFFFFFF; + } + + + /* Set up the option buffer... */ + ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, + options, 0, 0, 0, NULL, 0); + if (ip->client->packet_length < BOOTP_MIN_LEN) + ip->client->packet_length = BOOTP_MIN_LEN; + + ip->client->packet.op = BOOTREQUEST; + ip->client->packet.htype = ip->hw_address.htype; + ip->client->packet.hlen = ip->hw_address.hlen; + ip->client->packet.hops = 0; + ip->client->packet.xid = ip->client->xid; + ip->client->packet.secs = 0; /* Filled in by send_request. */ + ip->client->packet.flags = 0; + + /* ciaddr must always be zero. */ + memset(&ip->client->packet.ciaddr, 0, + sizeof(ip->client->packet.ciaddr)); + memset(&ip->client->packet.yiaddr, 0, + sizeof(ip->client->packet.yiaddr)); + memset(&ip->client->packet.siaddr, 0, + sizeof(ip->client->packet.siaddr)); + memset(&ip->client->packet.giaddr, 0, + sizeof(ip->client->packet.giaddr)); + memcpy(ip->client->packet.chaddr, + ip->hw_address.haddr, ip->hw_address.hlen); +} + +void +free_client_lease(struct client_lease *lease) +{ + int i; + + if (lease->server_name) + free(lease->server_name); + if (lease->filename) + free(lease->filename); + for (i = 0; i < 256; i++) { + if (lease->options[i].len) + free(lease->options[i].data); + } + free(lease); +} + +FILE *leaseFile; + +void +rewrite_client_leases(void) +{ + struct client_lease *lp; + + if (!leaseFile) { + leaseFile = fopen(path_dhclient_db, "w"); + if (!leaseFile) + error("can't create %s: %m", path_dhclient_db); + } else { + fflush(leaseFile); + rewind(leaseFile); + } + + for (lp = ifi->client->leases; lp; lp = lp->next) + write_client_lease(ifi, lp, 1); + if (ifi->client->active) + write_client_lease(ifi, ifi->client->active, 1); + + fflush(leaseFile); +// ftruncate(fileno(leaseFile), ftello(leaseFile)); +// fsync(fileno(leaseFile)); +} + +void +write_client_lease(struct interface_info *ip, struct client_lease *lease, + int rewrite) +{ + static int leases_written; + struct tm *t; + int i; + + if (!rewrite) { + if (leases_written++ > 20) { + rewrite_client_leases(); + leases_written = 0; + } + } + + /* If the lease came from the config file, we don't need to stash + a copy in the lease database. */ + if (lease->is_static) + return; + + if (!leaseFile) { /* XXX */ + leaseFile = fopen(path_dhclient_db, "w"); + if (!leaseFile) + error("can't create %s: %m", path_dhclient_db); + } + + fprintf(leaseFile, "lease {\n"); + if (lease->is_bootp) + fprintf(leaseFile, " bootp;\n"); + fprintf(leaseFile, " interface \"%s\";\n", ip->name); + fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address)); + if (lease->filename) + fprintf(leaseFile, " filename \"%s\";\n", lease->filename); + if (lease->server_name) + fprintf(leaseFile, " server-name \"%s\";\n", + lease->server_name); + if (lease->medium) + fprintf(leaseFile, " medium \"%s\";\n", lease->medium->string); + for (i = 0; i < 256; i++) + if (lease->options[i].len) + fprintf(leaseFile, " option %s %s;\n", + dhcp_options[i].name, + pretty_print_option(i, lease->options[i].data, + lease->options[i].len, 1, 1)); + + t = gmtime(&lease->renewal); + fprintf(leaseFile, " renew %d %d/%d/%d %02d:%02d:%02d;\n", + t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + t = gmtime(&lease->rebind); + fprintf(leaseFile, " rebind %d %d/%d/%d %02d:%02d:%02d;\n", + t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + t = gmtime(&lease->expiry); + fprintf(leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n", + t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + fprintf(leaseFile, "}\n"); + fflush(leaseFile); +} + +void +script_init(char *reason, struct string_list *medium) +{ + size_t len, mediumlen = 0; + struct imsg_hdr hdr; + struct buf *buf; + int errs; + + if (medium != NULL && medium->string != NULL) + mediumlen = strlen(medium->string); + + hdr.code = IMSG_SCRIPT_INIT; + hdr.len = sizeof(struct imsg_hdr) + + sizeof(size_t) + mediumlen + + sizeof(size_t) + strlen(reason); + + if ((buf = buf_open(hdr.len)) == NULL) + error("buf_open: %m"); + + errs = 0; + errs += buf_add(buf, &hdr, sizeof(hdr)); + errs += buf_add(buf, &mediumlen, sizeof(mediumlen)); + if (mediumlen > 0) + errs += buf_add(buf, medium->string, mediumlen); + len = strlen(reason); + errs += buf_add(buf, &len, sizeof(len)); + errs += buf_add(buf, reason, len); + + if (errs) + error("buf_add: %m"); + + if (buf_close(privfd, buf) == -1) + error("buf_close: %m"); +} + +void +priv_script_init(char *reason, char *medium) +{ + struct interface_info *ip = ifi; + + if (ip) { + // XXX Do we need to do anything? + } +} + +void +priv_script_write_params(char *prefix, struct client_lease *lease) +{ + struct interface_info *ip = ifi; + u_int8_t dbuf[1500]; + int i, len = 0; + char tbuf[128]; + +#if 0 + script_set_env(ip->client, prefix, "ip_address", + piaddr(lease->address)); +#endif + + if (lease->options[DHO_SUBNET_MASK].len && + (lease->options[DHO_SUBNET_MASK].len < + sizeof(lease->address.iabuf))) { + struct iaddr netmask, subnet, broadcast; + + memcpy(netmask.iabuf, lease->options[DHO_SUBNET_MASK].data, + lease->options[DHO_SUBNET_MASK].len); + netmask.len = lease->options[DHO_SUBNET_MASK].len; + + subnet = subnet_number(lease->address, netmask); + if (subnet.len) { +#if 0 + script_set_env(ip->client, prefix, "network_number", + piaddr(subnet)); +#endif + if (!lease->options[DHO_BROADCAST_ADDRESS].len) { + broadcast = broadcast_addr(subnet, netmask); + if (broadcast.len) +#if 0 + script_set_env(ip->client, prefix, + "broadcast_address", + piaddr(broadcast)); +#else + ; +#endif + } + } + } + +#if 0 + if (lease->filename) + script_set_env(ip->client, prefix, "filename", lease->filename); + if (lease->server_name) + script_set_env(ip->client, prefix, "server_name", + lease->server_name); +#endif + + for (i = 0; i < 256; i++) { + u_int8_t *dp = NULL; + + if (ip->client->config->defaults[i].len) { + if (lease->options[i].len) { + switch ( + ip->client->config->default_actions[i]) { + case ACTION_DEFAULT: + dp = lease->options[i].data; + len = lease->options[i].len; + break; + case ACTION_SUPERSEDE: +supersede: + dp = ip->client-> + config->defaults[i].data; + len = ip->client-> + config->defaults[i].len; + break; + case ACTION_PREPEND: + len = ip->client-> + config->defaults[i].len + + lease->options[i].len; + if (len > sizeof(dbuf)) { + warning("no space to %s %s", + "prepend option", + dhcp_options[i].name); + goto supersede; + } + dp = dbuf; + memcpy(dp, + ip->client-> + config->defaults[i].data, + ip->client-> + config->defaults[i].len); + memcpy(dp + ip->client-> + config->defaults[i].len, + lease->options[i].data, + lease->options[i].len); + dp[len] = '\0'; + break; + case ACTION_APPEND: + len = ip->client-> + config->defaults[i].len + + lease->options[i].len; + if (len > sizeof(dbuf)) { + warning("no space to %s %s", + "append option", + dhcp_options[i].name); + goto supersede; + } + dp = dbuf; + memcpy(dp, + lease->options[i].data, + lease->options[i].len); + memcpy(dp + lease->options[i].len, + ip->client-> + config->defaults[i].data, + ip->client-> + config->defaults[i].len); + dp[len] = '\0'; + } + } else { + dp = ip->client-> + config->defaults[i].data; + len = ip->client-> + config->defaults[i].len; + } + } else if (lease->options[i].len) { + len = lease->options[i].len; + dp = lease->options[i].data; + } else { + len = 0; + } +#if 0 + if (len) { + char name[256]; + + if (dhcp_option_ev_name(name, sizeof(name), + &dhcp_options[i])) + script_set_env(ip->client, prefix, name, + pretty_print_option(i, dp, len, 0, 0)); + } +#endif + } +#if 0 + snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry); + script_set_env(ip->client, prefix, "expiry", tbuf); +#endif +} + +void +script_write_params(char *prefix, struct client_lease *lease) +{ + size_t fn_len = 0, sn_len = 0, pr_len = 0; + struct imsg_hdr hdr; + struct buf *buf; + int errs, i; + + if (lease->filename != NULL) + fn_len = strlen(lease->filename); + if (lease->server_name != NULL) + sn_len = strlen(lease->server_name); + if (prefix != NULL) + pr_len = strlen(prefix); + + hdr.code = IMSG_SCRIPT_WRITE_PARAMS; + hdr.len = sizeof(hdr) + sizeof(struct client_lease) + + sizeof(size_t) + fn_len + sizeof(size_t) + sn_len + + sizeof(size_t) + pr_len; + + for (i = 0; i < 256; i++) + hdr.len += sizeof(int) + lease->options[i].len; + + scripttime = time(NULL); + + if ((buf = buf_open(hdr.len)) == NULL) + error("buf_open: %m"); + + errs = 0; + errs += buf_add(buf, &hdr, sizeof(hdr)); + errs += buf_add(buf, lease, sizeof(struct client_lease)); + errs += buf_add(buf, &fn_len, sizeof(fn_len)); + errs += buf_add(buf, lease->filename, fn_len); + errs += buf_add(buf, &sn_len, sizeof(sn_len)); + errs += buf_add(buf, lease->server_name, sn_len); + errs += buf_add(buf, &pr_len, sizeof(pr_len)); + errs += buf_add(buf, prefix, pr_len); + + for (i = 0; i < 256; i++) { + errs += buf_add(buf, &lease->options[i].len, + sizeof(lease->options[i].len)); + errs += buf_add(buf, lease->options[i].data, + lease->options[i].len); + } + + if (errs) + error("buf_add: %m"); + + if (buf_close(privfd, buf) == -1) + error("buf_close: %m"); +} + +int +script_go(void) +{ + struct imsg_hdr hdr; + struct buf *buf; + int ret; + + scripttime = time(NULL); + + hdr.code = IMSG_SCRIPT_GO; + hdr.len = sizeof(struct imsg_hdr); + + if ((buf = buf_open(hdr.len)) == NULL) + error("buf_open: %m"); + + if (buf_add(buf, &hdr, sizeof(hdr))) + error("buf_add: %m"); + + if (buf_close(privfd, buf) == -1) + error("buf_close: %m"); + + memset(&hdr, 0, sizeof(hdr)); + //bzero(&hdr, sizeof(hdr)); + buf_read(privfd, &hdr, sizeof(hdr)); + if (hdr.code != IMSG_SCRIPT_GO_RET) + error("unexpected msg type %u", hdr.code); + if (hdr.len != sizeof(hdr) + sizeof(int)) + error("received corrupted message"); + buf_read(privfd, &ret, sizeof(ret)); + + return (ret); +} + +#if 0 +int +priv_script_go(void) +{ + char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI"; + static char client_path[] = CLIENT_PATH; + struct interface_info *ip = ifi; + int pid, wpid, wstatus; + + scripttime = time(NULL); + + if (ip) { + scriptName = ip->client->config->script_name; + envp = ip->client->scriptEnv; + } else { + scriptName = top_level_config.script_name; + epp[0] = reason; + epp[1] = client_path; + epp[2] = NULL; + envp = epp; + } + + argv[0] = scriptName; + argv[1] = NULL; + + pid = fork(); + if (pid < 0) { + error("fork: %m"); + wstatus = 0; + } else if (pid) { + do { + wpid = wait(&wstatus); + } while (wpid != pid && wpid > 0); + if (wpid < 0) { + error("wait: %m"); + wstatus = 0; + } + } else { + execve(scriptName, argv, envp); + error("execve (%s, ...): %m", scriptName); + } + + if (ip) + script_flush_env(ip->client); + + return (wstatus & 0xff); +} +#endif + +#if 0 +void +script_set_env(struct client_state *client, const char *prefix, + const char *name, const char *value) +{ + int i, j, namelen; + + namelen = strlen(name); + + for (i = 0; client->scriptEnv[i]; i++) + if (strncmp(client->scriptEnv[i], name, namelen) == 0 && + client->scriptEnv[i][namelen] == '=') + break; + + if (client->scriptEnv[i]) + /* Reuse the slot. */ + free(client->scriptEnv[i]); + else { + /* New variable. Expand if necessary. */ + if (i >= client->scriptEnvsize - 1) { + char **newscriptEnv; + int newscriptEnvsize = client->scriptEnvsize + 50; + + newscriptEnv = realloc(client->scriptEnv, + newscriptEnvsize); + if (newscriptEnv == NULL) { + free(client->scriptEnv); + client->scriptEnv = NULL; + client->scriptEnvsize = 0; + error("script_set_env: no memory for variable"); + } + client->scriptEnv = newscriptEnv; + client->scriptEnvsize = newscriptEnvsize; + } + /* need to set the NULL pointer at end of array beyond + the new slot. */ + client->scriptEnv[i + 1] = NULL; + } + /* Allocate space and format the variable in the appropriate slot. */ + client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 + + strlen(value) + 1); + if (client->scriptEnv[i] == NULL) + error("script_set_env: no memory for variable assignment"); + + /* No `` or $() command substitution allowed in environment values! */ + for (j=0; j < strlen(value); j++) + switch (value[j]) { + case '`': + case '$': + error("illegal character (%c) in value '%s'", value[j], + value); + /* not reached */ + } + snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) + + 1 + strlen(value) + 1, "%s%s=%s", prefix, name, value); +} + +void +script_flush_env(struct client_state *client) +{ + int i; + + for (i = 0; client->scriptEnv[i]; i++) { + free(client->scriptEnv[i]); + client->scriptEnv[i] = NULL; + } + client->scriptEnvsize = 0; +} +#endif + +int +dhcp_option_ev_name(char *buf, size_t buflen, struct dhcp_option *option) +{ + int i; + + for (i = 0; option->name[i]; i++) { + if (i + 1 == buflen) + return 0; + if (option->name[i] == '-') + buf[i] = '_'; + else + buf[i] = option->name[i]; + } + + buf[i] = 0; + return 1; +} + +#if 0 +void +go_daemon(void) +{ + static int state = 0; + + if (no_daemon || state) + return; + + state = 1; + + /* Stop logging to stderr... */ + log_perror = 0; + + if (daemon(1, 0) == -1) + error("daemon"); + + /* we are chrooted, daemon(3) fails to open /dev/null */ + if (nullfd != -1) { + dup2(nullfd, STDIN_FILENO); + dup2(nullfd, STDOUT_FILENO); + dup2(nullfd, STDERR_FILENO); + close(nullfd); + nullfd = -1; + } +} +#endif + +int +check_option(struct client_lease *l, int option) +{ + char *opbuf; + char *sbuf; + + /* we use this, since this is what gets passed to dhclient-script */ + + opbuf = pretty_print_option(option, l->options[option].data, + l->options[option].len, 0, 0); + + sbuf = option_as_string(option, l->options[option].data, + l->options[option].len); + + switch (option) { + case DHO_SUBNET_MASK: + case DHO_TIME_SERVERS: + case DHO_NAME_SERVERS: + case DHO_ROUTERS: + case DHO_DOMAIN_NAME_SERVERS: + case DHO_LOG_SERVERS: + case DHO_COOKIE_SERVERS: + case DHO_LPR_SERVERS: + case DHO_IMPRESS_SERVERS: + case DHO_RESOURCE_LOCATION_SERVERS: + case DHO_SWAP_SERVER: + case DHO_BROADCAST_ADDRESS: + case DHO_NIS_SERVERS: + case DHO_NTP_SERVERS: + case DHO_NETBIOS_NAME_SERVERS: + case DHO_NETBIOS_DD_SERVER: + case DHO_FONT_SERVERS: + case DHO_DHCP_SERVER_IDENTIFIER: + if (!ipv4addrs(opbuf)) { + warning("Invalid IP address in option: %s", opbuf); + return (0); + } + return (1) ; + case DHO_HOST_NAME: + case DHO_DOMAIN_NAME: + case DHO_NIS_DOMAIN: + if (!res_hnok(sbuf)) { + warning("Bogus Host Name option %d: %s (%s)", option, + sbuf, opbuf); + return (0); + } + return (1); + case DHO_PAD: + case DHO_TIME_OFFSET: + case DHO_BOOT_SIZE: + case DHO_MERIT_DUMP: + case DHO_ROOT_PATH: + case DHO_EXTENSIONS_PATH: + case DHO_IP_FORWARDING: + case DHO_NON_LOCAL_SOURCE_ROUTING: + case DHO_POLICY_FILTER: + case DHO_MAX_DGRAM_REASSEMBLY: + case DHO_DEFAULT_IP_TTL: + case DHO_PATH_MTU_AGING_TIMEOUT: + case DHO_PATH_MTU_PLATEAU_TABLE: + case DHO_INTERFACE_MTU: + case DHO_ALL_SUBNETS_LOCAL: + case DHO_PERFORM_MASK_DISCOVERY: + case DHO_MASK_SUPPLIER: + case DHO_ROUTER_DISCOVERY: + case DHO_ROUTER_SOLICITATION_ADDRESS: + case DHO_STATIC_ROUTES: + case DHO_TRAILER_ENCAPSULATION: + case DHO_ARP_CACHE_TIMEOUT: + case DHO_IEEE802_3_ENCAPSULATION: + case DHO_DEFAULT_TCP_TTL: + case DHO_TCP_KEEPALIVE_INTERVAL: + case DHO_TCP_KEEPALIVE_GARBAGE: + case DHO_VENDOR_ENCAPSULATED_OPTIONS: + case DHO_NETBIOS_NODE_TYPE: + case DHO_NETBIOS_SCOPE: + case DHO_X_DISPLAY_MANAGER: + case DHO_DHCP_REQUESTED_ADDRESS: + case DHO_DHCP_LEASE_TIME: + case DHO_DHCP_OPTION_OVERLOAD: + case DHO_DHCP_MESSAGE_TYPE: + case DHO_DHCP_PARAMETER_REQUEST_LIST: + case DHO_DHCP_MESSAGE: + case DHO_DHCP_MAX_MESSAGE_SIZE: + case DHO_DHCP_RENEWAL_TIME: + case DHO_DHCP_REBINDING_TIME: + case DHO_DHCP_CLASS_IDENTIFIER: + case DHO_DHCP_CLIENT_IDENTIFIER: + case DHO_DHCP_USER_CLASS_ID: + case DHO_END: + return (1); + default: + warning("unknown dhcp option value 0x%x", option); + return (unknown_ok); + } +} + +int +res_hnok(const char *dn) +{ + int pch = PERIOD, ch = *dn++; + + while (ch != '\0') { + int nch = *dn++; + + if (periodchar(ch)) { + ; + } else if (periodchar(pch)) { + if (!borderchar(ch)) + return (0); + } else if (periodchar(nch) || nch == '\0') { + if (!borderchar(ch)) + return (0); + } else { + if (!middlechar(ch)) + return (0); + } + pch = ch, ch = nch; + } + return (1); +} + +/* Does buf consist only of dotted decimal ipv4 addrs? + * return how many if so, + * otherwise, return 0 + */ +int +ipv4addrs(char * buf) +{ + struct in_addr jnk; + int count = 0; + + while (inet_aton(buf, &jnk) == 1){ + count++; + while (periodchar(*buf) || digitchar(*buf)) + buf++; + if (*buf == '\0') + return (count); + while (*buf == ' ') + buf++; + } + return (0); +} + + +char * +option_as_string(unsigned int code, unsigned char *data, int len) +{ + static char optbuf[32768]; /* XXX */ + char *op = optbuf; + int opleft = sizeof(optbuf); + unsigned char *dp = data; + + if (code > 255) + error("option_as_string: bad code %d", code); + + for (; dp < data + len; dp++) { + if (!isascii(*dp) || !isprint(*dp)) { + if (dp + 1 != data + len || *dp != 0) { + snprintf(op, opleft, "\\%03o", *dp); + op += 4; + opleft -= 4; + } + } else if (*dp == '"' || *dp == '\'' || *dp == '$' || + *dp == '`' || *dp == '\\') { + *op++ = '\\'; + *op++ = *dp; + opleft -= 2; + } else { + *op++ = *dp; + opleft--; + } + } + if (opleft < 1) + goto toobig; + *op = 0; + return optbuf; +toobig: + warning("dhcp option too large"); + return ""; +} + +#if 0 +int +fork_privchld(int fd, int fd2) +{ + struct pollfd pfd[1]; + int nfds; + + switch (fork()) { + case -1: + error("cannot fork"); + case 0: + break; + default: + return (0); + } + + setproctitle("%s [priv]", ifi->name); + + dup2(nullfd, STDIN_FILENO); + dup2(nullfd, STDOUT_FILENO); + dup2(nullfd, STDERR_FILENO); + close(nullfd); + close(fd2); + + for (;;) { + pfd[0].fd = fd; + pfd[0].events = POLLIN; + if ((nfds = poll(pfd, 1, INFTIM)) == -1) + if (errno != EINTR) + error("poll error"); + + if (nfds == 0 || !(pfd[0].revents & POLLIN)) + continue; + + dispatch_imsg(fd); + } +} +#endif diff --git a/reactos/subsys/system/dhcp/dhcp.rc b/reactos/subsys/system/dhcp/dhcp.rc new file mode 100644 index 00000000000..35e404f893e --- /dev/null +++ b/reactos/subsys/system/dhcp/dhcp.rc @@ -0,0 +1,6 @@ +/* $Id: regsvr32.rc 12852 2005-01-06 13:58:04Z mf $ */ + +#define REACTOS_STR_FILE_DESCRIPTION "DHCP Client Service" +#define REACTOS_STR_INTERNAL_NAME "dhcp\0" +#define REACTOS_STR_ORIGINAL_FILENAME "dhcp.exe\0" +#include diff --git a/reactos/subsys/system/dhcp/dhcpmain.c b/reactos/subsys/system/dhcp/dhcpmain.c new file mode 100644 index 00000000000..ea88fc8fe27 --- /dev/null +++ b/reactos/subsys/system/dhcp/dhcpmain.c @@ -0,0 +1,72 @@ +/* $Id:$ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Service + * FILE: subsys/system/dhcp + * PURPOSE: DHCP client service entry point + * PROGRAMMER: Art Yerkes (arty@users.sf.net) + * UPDATE HISTORY: + * Created 03/08/2005 + */ + +#include +#include "dhcpd.h" +#include "version.h" + +typedef struct _DHCP_API_REQUEST { + int type; + UINT flags; + LPDHCPAPI_CLASSID class_id; + DHCP_API_PARAMS_ARRAY vendor_params; + DHCP_API_PARAMS_ARRAY general_params; + LPWSTR request_id, adapter_name; +} DHCP_API_REQUEST; + +typedef struct _DHCP_MANAGED_ADAPTER { + LPWSTR adapter_name, hostname, dns_server; + UINT adapter_index; + struct sockaddr_in address, netmask; + struct interface_info *dhcp_info; +} DHCP_MANAGED_ADAPTER; + +#define DHCP_REQUESTPARAM WM_USER + 0 +#define DHCP_PARAMCHANGE WM_USER + 1 +#define DHCP_CANCELREQUEST WM_USER + 2 +#define DHCP_NOPARAMCHANGE WM_USER + 3 +#define DHCP_MANAGEADAPTER WM_USER + 4 +#define DHCP_UNMANAGEADAPTER WM_USER + 5 + +UINT DhcpEventTimer; +HANDLE DhcpServiceThread; +DWORD DhcpServiceThreadId; +LIST_ENTRY ManagedAdapters; + +LRESULT WINAPI ServiceThread( PVOID Data ) { + MSG msg; + + while( GetMessage( &msg, 0, 0, 0 ) ) { + switch( msg.message ) { + case DHCP_MANAGEADAPTER: + + break; + + case DHCP_UNMANAGEADAPTER: + break; + + case DHCP_REQUESTPARAM: + break; + + case DHCP_CANCELREQUEST: + break; + + case DHCP_PARAMCHANGE: + break; + + case DHCP_NOPARAMCHANGE: + break; + } + } +} + +int main( int argc, char **argv ) { +} diff --git a/reactos/subsys/system/dhcp/dispatch.c b/reactos/subsys/system/dhcp/dispatch.c new file mode 100644 index 00000000000..19f806a7b73 --- /dev/null +++ b/reactos/subsys/system/dhcp/dispatch.c @@ -0,0 +1,579 @@ +/* $OpenBSD: dispatch.c,v 1.31 2004/09/21 04:07:03 david Exp $ */ + +/* + * Copyright 2004 Henning Brauer + * Copyright (c) 1995, 1996, 1997, 1998, 1999 + * The Internet Software Consortium. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#include "rosdhcp.h" +#include "dhcpd.h" +//#include + +//#include +//#include +//#include + +struct protocol *protocols = NULL; +struct timeout *timeouts = NULL; +static struct timeout *free_timeouts = NULL; +static int interfaces_invalidated = FALSE; +void (*bootp_packet_handler)(struct interface_info *, + struct dhcp_packet *, int, unsigned int, + struct iaddr, struct hardware *); + +static int interface_status(struct interface_info *ifinfo); + +/* + * Use getifaddrs() to get a list of all the attached interfaces. For + * each interface that's of type INET and not the loopback interface, + * register that interface with the network I/O software, figure out + * what subnet it's on, and add it to the list of interfaces. + */ +#if 0 +void +discover_interfaces(struct interface_info *iface) +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_in foo; + struct ifreq *tif; + + if (getifaddrs(&ifap) != 0) + error("getifaddrs failed"); + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if ((ifa->ifa_flags & IFF_LOOPBACK) || + (ifa->ifa_flags & IFF_POINTOPOINT) || + (!(ifa->ifa_flags & IFF_UP))) + continue; + + + if (strcmp(iface->name, ifa->ifa_name)) + continue; + + /* + * If we have the capability, extract link information + * and record it in a linked list. + */ + if (ifa->ifa_addr->sa_family == AF_LINK) { + struct sockaddr_dl *foo = + (struct sockaddr_dl *)ifa->ifa_addr; + + iface->index = foo->sdl_index; + iface->hw_address.hlen = foo->sdl_alen; + iface->hw_address.htype = HTYPE_ETHER; /* XXX */ + memcpy(iface->hw_address.haddr, + LLADDR(foo), foo->sdl_alen); + } else if (ifa->ifa_addr->sa_family == AF_INET) { + struct iaddr addr; + + memcpy(&foo, ifa->ifa_addr, sizeof(foo)); + if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) + continue; + if (!iface->ifp) { + int len = IFNAMSIZ + ifa->ifa_addr->sa_len; + if ((tif = malloc(len)) == NULL) + error("no space to remember ifp"); + strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ); + memcpy(&tif->ifr_addr, ifa->ifa_addr, + ifa->ifa_addr->sa_len); + iface->ifp = tif; + iface->primary_address = foo.sin_addr; + } + addr.len = 4; + memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len); + } + } + + if (!iface->ifp) + error("%s: not found", iface->name); + + /* Register the interface... */ + if_register_receive(iface); + if_register_send(iface); + add_protocol(iface->name, iface->rfdesc, got_one, iface); + freeifaddrs(ifap); +} +#else +void +discover_interfaces(struct interface_info *iface) +{ + NTSTATUS Status; + ULONG dim; + char TmpName [IFNAMSIZ]; + + PIP_ADAPTER_INFO pAdapterInfo; + PIP_ADAPTER_INFO pAdapter = NULL; + + pAdapterInfo = malloc(sizeof(IP_ADAPTER_INFO)); + dim = sizeof(IP_ADAPTER_INFO); + + if (GetAdaptersInfo( pAdapterInfo, &dim) != ERROR_SUCCESS) { + free(pAdapterInfo); + pAdapterInfo = (IP_ADAPTER_INFO *) malloc (dim); + } + + if ((Status = GetAdaptersInfo( pAdapterInfo, &dim)) != NO_ERROR) { + note("Error %x", Status); + free (pAdapterInfo); + return; + } + + for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) { + /* we only do ethernet */ + note("found: %s %x\n", pAdapter->AdapterName, pAdapter->Address); + if (pAdapter->Type != MIB_IF_TYPE_ETHERNET) { + continue; + } + note ("found ethernet\n"); + + iface->hw_address.hlen = pAdapter->AddressLength; + iface->hw_address.htype = HTYPE_ETHER; + memcpy (&iface->hw_address.haddr[0], + pAdapter->Address, iface->hw_address.hlen); + + if (pAdapter->IpAddressList.IpAddress.String) + iface->primary_address.S_un.S_addr = inet_addr(pAdapter->IpAddressList.IpAddress.String); + + } + if (iface) { + if_register_receive(iface); + if_register_send(iface); + add_protocol(iface->name, iface->rfdesc, got_one, iface); + } + free (pAdapterInfo); +} +#endif + +void +reinitialize_interfaces(void) +{ + interfaces_invalidated = 1; +} + +/* + * Wait for packets to come in using poll(). When a packet comes in, + * call receive_packet to receive the packet and possibly strip hardware + * addressing information from it, and then call through the + * bootp_packet_handler hook to try to do something with it. + */ +void +dispatch(void) +{ + int count, i, to_msec, nfds = 0; + struct protocol *l; + fd_set fds; + time_t howlong; + struct timeval timeval; + + for (l = protocols; l; l = l->next) + nfds++; + + FD_ZERO(&fds); + +// fds = malloc(nfds * sizeof(struct pollfd)); +// if (fds == NULL) +// error("Can't allocate poll structures."); + + do { + DH_DbgPrint(MID_TRACE,("Cycling dispatch()\n")); + /* + * Call any expired timeouts, and then if there's still + * a timeout registered, time out the select call then. + */ + another: + if (timeouts) { + DH_DbgPrint(MID_TRACE,("Some timeouts are available\n")); + + struct timeout *t; + + if (timeouts->when <= cur_time) { + DH_DbgPrint(MID_TRACE,("Calling timeout %x %p %x\n", + timeouts->when, + timeouts->func, + timeouts->what)); + t = timeouts; + timeouts = timeouts->next; + (*(t->func))(t->what); + t->next = free_timeouts; + free_timeouts = t; + goto another; + } + + /* + * Figure timeout in milliseconds, and check for + * potential overflow, so we can cram into an + * int for poll, while not polling with a + * negative timeout and blocking indefinitely. + */ + howlong = timeouts->when - cur_time; + if (howlong > INT_MAX / 1000) + howlong = INT_MAX / 1000; + to_msec = howlong * 1000; + } else + to_msec = -1; + + /* Set up the descriptors to be polled. */ + for (i = 0, l = protocols; l; l = l->next) { + struct interface_info *ip = l->local; + + if (ip && (l->handler != got_one || !ip->dead)) { + DH_DbgPrint(MID_TRACE,("l->fd %d\n", l->fd)); + + FD_SET(l->fd, &fds); +// fds[i].fd = l->fd; +// fds[i].events = POLLIN; +// fds[i].revents = 0; + i++; + } + } + + if (i == 0) + error("No live interfaces to poll on - exiting."); + + /* Wait for a packet or a timeout... XXX */ + timeval.tv_sec = to_msec / 1000; + timeval.tv_usec = (to_msec % 1000) * 1000; + DH_DbgPrint(MID_TRACE,("select(%d,%d.%03d) =>\n", + nfds,timeval.tv_sec,timeval.tv_usec/1000)); + count = select(nfds, &fds, NULL, NULL, &timeval); + DH_DbgPrint(MID_TRACE,(" => %d\n", count)); + + /* Not likely to be transitory... */ + if (count == SOCKET_ERROR) { + if (errno == EAGAIN || errno == EINTR) { + time(&cur_time); + continue; + } else + error("poll: %m"); + } + + /* Get the current time... */ + time(&cur_time); + + i = 0; + for (l = protocols; l; l = l->next) { + struct interface_info *ip; + ip = l->local; + if (!FD_ISSET(l->fd, &fds)) { +//.revents & (POLLIN | POLLHUP))) { +// fds[i].revents = 0; + if (ip && (l->handler != got_one || + !ip->dead)) { + DH_DbgPrint(MID_TRACE,("Handling %x\n", l)); + (*(l->handler))(l); + if (interfaces_invalidated) + break; + } + i++; + } + interfaces_invalidated = 0; + } + DH_DbgPrint(MID_TRACE,("Done\n")); + } while (1); +} + +void +got_one(struct protocol *l) +{ + struct sockaddr_in from; + struct hardware hfrom; + struct iaddr ifrom; + ssize_t result; + union { + /* + * Packet input buffer. Must be as large as largest + * possible MTU. + */ + unsigned char packbuf[4095]; + struct dhcp_packet packet; + } u; + struct interface_info *ip = l->local; + + if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from, + &hfrom)) == -1) { + warning("receive_packet failed on %s: %s", ip->name, + strerror(errno)); + ip->errors++; + if ((!interface_status(ip)) || + (ip->noifmedia && ip->errors > 20)) { + /* our interface has gone away. */ + warning("Interface %s no longer appears valid.", + ip->name); + ip->dead = 1; + interfaces_invalidated = 1; + close(l->fd); + remove_protocol(l); + free(ip); + } + return; + } + if (result == 0) + return; + + if (bootp_packet_handler) { + ifrom.len = 4; + memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len); + + (*bootp_packet_handler)(ip, &u.packet, result, + from.sin_port, ifrom, &hfrom); + } +} + +#if 0 +int +interface_status(struct interface_info *ifinfo) +{ + char *ifname = ifinfo->name; + int ifsock = ifinfo->rfdesc; + struct ifreq ifr; + struct ifmediareq ifmr; + + /* get interface flags */ + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) { + syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) on %s: %m", ifname); + goto inactive; + } + + /* + * if one of UP and RUNNING flags is dropped, + * the interface is not active. + */ + if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) + goto inactive; + + /* Next, check carrier on the interface, if possible */ + if (ifinfo->noifmedia) + goto active; + memset(&ifmr, 0, sizeof(ifmr)); + strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); + if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + if (errno != EINVAL) { + syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m", + ifname); + + ifinfo->noifmedia = 1; + goto active; + } + /* + * EINVAL (or ENOTTY) simply means that the interface + * does not support the SIOCGIFMEDIA ioctl. We regard it alive. + */ + ifinfo->noifmedia = 1; + goto active; + } + if (ifmr.ifm_status & IFM_AVALID) { + switch (ifmr.ifm_active & IFM_NMASK) { + case IFM_ETHER: + if (ifmr.ifm_status & IFM_ACTIVE) + goto active; + else + goto inactive; + break; + default: + goto inactive; + } + } +inactive: + return (0); +active: + return (1); +} +#else +int +interface_status(struct interface_info *ifinfo) +{ + return (1); +} +#endif + +void +add_timeout(time_t when, void (*where)(void *), void *what) +{ + struct timeout *t, *q; + + DH_DbgPrint(MID_TRACE,("Adding timeout %x %p %x\n", when, where, what)); + /* See if this timeout supersedes an existing timeout. */ + t = NULL; + for (q = timeouts; q; q = q->next) { + if (q->func == where && q->what == what) { + if (t) + t->next = q->next; + else + timeouts = q->next; + break; + } + t = q; + } + + /* If we didn't supersede a timeout, allocate a timeout + structure now. */ + if (!q) { + if (free_timeouts) { + q = free_timeouts; + free_timeouts = q->next; + q->func = where; + q->what = what; + } else { + q = malloc(sizeof(struct timeout)); + if (!q) + error("Can't allocate timeout structure!"); + q->func = where; + q->what = what; + } + } + + q->when = when; + + /* Now sort this timeout into the timeout list. */ + + /* Beginning of list? */ + if (!timeouts || timeouts->when > q->when) { + q->next = timeouts; + timeouts = q; + return; + } + + /* Middle of list? */ + for (t = timeouts; t->next; t = t->next) { + if (t->next->when > q->when) { + q->next = t->next; + t->next = q; + return; + } + } + + /* End of list. */ + t->next = q; + q->next = NULL; +} + +void +cancel_timeout(void (*where)(void *), void *what) +{ + struct timeout *t, *q; + + /* Look for this timeout on the list, and unlink it if we find it. */ + t = NULL; + for (q = timeouts; q; q = q->next) { + if (q->func == where && q->what == what) { + if (t) + t->next = q->next; + else + timeouts = q->next; + break; + } + t = q; + } + + /* If we found the timeout, put it on the free list. */ + if (q) { + q->next = free_timeouts; + free_timeouts = q; + } +} + +/* Add a protocol to the list of protocols... */ +void +add_protocol(char *name, int fd, void (*handler)(struct protocol *), + void *local) +{ + struct protocol *p; + + p = malloc(sizeof(*p)); + if (!p) + error("can't allocate protocol struct for %s", name); + + p->fd = fd; + p->handler = handler; + p->local = local; + p->next = protocols; + protocols = p; +} + +void +remove_protocol(struct protocol *proto) +{ + struct protocol *p, *next, *prev; + + prev = NULL; + for (p = protocols; p; p = next) { + next = p->next; + if (p == proto) { + if (prev) + prev->next = p->next; + else + protocols = p->next; + free(p); + } + } +} + +int +interface_link_status(char *ifname) +{ +#if 0 + struct ifmediareq ifmr; + int sock; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + error("Can't create socket"); + + memset(&ifmr, 0, sizeof(ifmr)); + strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); + if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) { + /* EINVAL -> link state unknown. treat as active */ + if (errno != EINVAL) + syslog(LOG_DEBUG, "ioctl(SIOCGIFMEDIA) on %s: %m", + ifname); + close(sock); + return (1); + } + close(sock); + + if (ifmr.ifm_status & IFM_AVALID) { + if ((ifmr.ifm_active & IFM_NMASK) == IFM_ETHER) { + if (ifmr.ifm_status & IFM_ACTIVE) + return (1); + else + return (0); + } + } +#endif + return (1); +} diff --git a/reactos/subsys/system/dhcp/hash.c b/reactos/subsys/system/dhcp/hash.c new file mode 100644 index 00000000000..c47c562879b --- /dev/null +++ b/reactos/subsys/system/dhcp/hash.c @@ -0,0 +1,164 @@ +/* hash.c + + Routines for manipulating hash tables... */ + +/* + * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: hash.c,v 1.9.2.3 1999/04/09 17:39:41 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "rosdhcp.h" + +static INLINE int do_hash PROTO ((unsigned char *, int, int)); + +struct hash_table *new_hash () +{ + struct hash_table *rv = new_hash_table (DEFAULT_HASH_SIZE); + if (!rv) + return rv; + memset (&rv -> buckets [0], 0, + DEFAULT_HASH_SIZE * sizeof (struct hash_bucket *)); + return rv; +} + +static INLINE int do_hash (name, len, size) + unsigned char *name; + int len; + int size; +{ + register int accum = 0; + register unsigned char *s = name; + int i = len; + while (i--) { + /* Add the character in... */ + accum += *s++; + /* Add carry back in... */ + while (accum > 255) { + accum = (accum & 255) + (accum >> 8); + } + } + return accum % size; +} + +void add_hash (table, name, len, pointer) + struct hash_table *table; + int len; + unsigned char *name; + unsigned char *pointer; +{ + int hashno; + struct hash_bucket *bp; + + if (!table) + return; + if (!len) + len = strlen ((char *)name); + + hashno = do_hash (name, len, table -> hash_count); + bp = new_hash_bucket (); + + if (!bp) { + warn ("Can't add %s to hash table.", name); + return; + } + bp -> name = name; + bp -> value = pointer; + bp -> next = table -> buckets [hashno]; + bp -> len = len; + table -> buckets [hashno] = bp; +} + +void delete_hash_entry (table, name, len) + struct hash_table *table; + int len; + unsigned char *name; +{ + int hashno; + struct hash_bucket *bp, *pbp = (struct hash_bucket *)0; + + if (!table) + return; + if (!len) + len = strlen ((char *)name); + + hashno = do_hash (name, len, table -> hash_count); + + /* Go through the list looking for an entry that matches; + if we find it, delete it. */ + for (bp = table -> buckets [hashno]; bp; bp = bp -> next) { + if ((!bp -> len && + !strcmp ((char *)bp -> name, (char *)name)) || + (bp -> len == len && + !memcmp (bp -> name, name, len))) { + if (pbp) { + pbp -> next = bp -> next; + } else { + table -> buckets [hashno] = bp -> next; + } + free_hash_bucket (bp, "delete_hash_entry"); + break; + } + pbp = bp; /* jwg, 9/6/96 - nice catch! */ + } +} + +unsigned char *hash_lookup (table, name, len) + struct hash_table *table; + unsigned char *name; + int len; +{ + int hashno; + struct hash_bucket *bp; + + if (!table) + return (unsigned char *)0; + + if (!len) + len = strlen ((char *)name); + + hashno = do_hash (name, len, table -> hash_count); + + for (bp = table -> buckets [hashno]; bp; bp = bp -> next) { + if (len == bp -> len && !memcmp (bp -> name, name, len)) + return bp -> value; + } + return (unsigned char *)0; +} diff --git a/reactos/subsys/system/dhcp/include/cdefs.h b/reactos/subsys/system/dhcp/include/cdefs.h new file mode 100644 index 00000000000..2bc67a5251a --- /dev/null +++ b/reactos/subsys/system/dhcp/include/cdefs.h @@ -0,0 +1,57 @@ +/* cdefs.h + + Standard C definitions... */ + +/* + * Copyright (c) 1996 The Internet Software Consortium. + * All Rights Reserved. + * Copyright (c) 1995 RadioMail Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of RadioMail Corporation, the Internet Software + * Consortium nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RADIOMAIL CORPORATION, THE INTERNET + * SOFTWARE CONSORTIUM AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL RADIOMAIL CORPORATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software was written for RadioMail Corporation by Ted Lemon + * under a contract with Vixie Enterprises. Further modifications have + * been made for the Internet Software Consortium under a contract + * with Vixie Laboratories. + */ + +#if (defined (__GNUC__) || defined (__STDC__)) && !defined (BROKEN_ANSI) +#define PROTO(x) x +#define KandR(x) +#define ANSI_DECL(x) x +#if defined (__GNUC__) +#define INLINE inline +#else +#define INLINE +#endif /* __GNUC__ */ +#else +#define PROTO(x) () +#define KandR(x) x +#define ANSI_DECL(x) +#define INLINE +#endif /* __GNUC__ || __STDC__ */ diff --git a/reactos/subsys/system/dhcp/include/debug.h b/reactos/subsys/system/dhcp/include/debug.h new file mode 100644 index 00000000000..2cac97489e0 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/debug.h @@ -0,0 +1,84 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/debug.h + * PURPOSE: Debugging support macros + * DEFINES: DBG - Enable debug output + * NASSERT - Disable assertions + */ +#ifndef __DEBUG_H +#define __DEBUG_H + +#define NORMAL_MASK 0x000000FF +#define SPECIAL_MASK 0xFFFFFF00 +#define MIN_TRACE 0x00000001 +#define MID_TRACE 0x00000002 +#define MAX_TRACE 0x00000003 + +#define DEBUG_ADAPTER 0x00000100 +#define DEBUG_ULTRA 0xFFFFFFFF + +#ifdef DBG + +extern unsigned long debug_trace_level; + +#ifdef _MSC_VER + +#define DH_DbgPrint(_t_, _x_) \ + if (((debug_trace_level & NORMAL_MASK) >= _t_) || \ + ((debug_trace_level & _t_) > NORMAL_MASK)) { \ + DbgPrint("(%s:%d) ", __FILE__, __LINE__); \ + DbgPrint _x_ ; \ + } + +#else /* _MSC_VER */ + +#define DH_DbgPrint(_t_, _x_) \ + if (((debug_trace_level & NORMAL_MASK) >= _t_) || \ + ((debug_trace_level & _t_) > NORMAL_MASK)) { \ + DbgPrint("(%s:%d)(%s) ", __FILE__, __LINE__, __FUNCTION__); \ + DbgPrint _x_ ; \ + } + +#endif /* _MSC_VER */ + +#define ASSERT_IRQL(x) ASSERT(KeGetCurrentIrql() <= (x)) + +#else /* DBG */ + +#define DH_DbgPrint(_t_, _x_) + +#endif /* DBG */ + + +#define assert(x) ASSERT(x) +#define assert_irql(x) ASSERT_IRQL(x) + + +#ifdef _MSC_VER + +#define UNIMPLEMENTED \ + TI_DbgPrint(MIN_TRACE, ("The function at %s:%d is unimplemented, \ + but come back another day.\n", __FILE__, __LINE__)); + +#else /* _MSC_VER */ + +#define UNIMPLEMENTED \ + TI_DbgPrint(MIN_TRACE, ("(%s:%d)(%s) is unimplemented, \ + but come back another day.\n", __FILE__, __LINE__, __FUNCTION__)); + +#endif /* _MSC_VER */ + + +#define CHECKPOINT \ + do { TI_DbgPrint(DEBUG_CHECK, ("(%s:%d)\n", __FILE__, __LINE__)); } while(0); + +#define CP CHECKPOINT + +#define ASSERT_KM_POINTER(_x) \ + ASSERT(((PVOID)_x) != (PVOID)0xcccccccc); \ + ASSERT(((PVOID)_x) >= (PVOID)0x80000000); + +#endif /* __DEBUG_H */ + +/* EOF */ diff --git a/reactos/subsys/system/dhcp/include/dhcp.h b/reactos/subsys/system/dhcp/include/dhcp.h new file mode 100644 index 00000000000..8ac8ed3a9e6 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/dhcp.h @@ -0,0 +1,169 @@ +/* $OpenBSD: dhcp.h,v 1.5 2004/05/04 15:49:49 deraadt Exp $ */ + +/* Protocol structures... */ + +/* + * Copyright (c) 1995, 1996 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#define DHCP_UDP_OVERHEAD (14 + /* Ethernet header */ \ + 20 + /* IP header */ \ + 8) /* UDP header */ +#define DHCP_SNAME_LEN 64 +#define DHCP_FILE_LEN 128 +#define DHCP_FIXED_NON_UDP 236 +#define DHCP_FIXED_LEN (DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD) + /* Everything but options. */ +#define DHCP_MTU_MAX 1500 +#define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN) + +#define BOOTP_MIN_LEN 300 +#define DHCP_MIN_LEN 548 + +struct dhcp_packet { + u_int8_t op; /* Message opcode/type */ + u_int8_t htype; /* Hardware addr type (see net/if_types.h) */ + u_int8_t hlen; /* Hardware addr length */ + u_int8_t hops; /* Number of relay agent hops from client */ + u_int32_t xid; /* Transaction ID */ + u_int16_t secs; /* Seconds since client started looking */ + u_int16_t flags; /* Flag bits */ + struct in_addr ciaddr; /* Client IP address (if already in use) */ + struct in_addr yiaddr; /* Client IP address */ + struct in_addr siaddr; /* IP address of next server to talk to */ + struct in_addr giaddr; /* DHCP relay agent IP address */ + unsigned char chaddr[16]; /* Client hardware address */ + char sname[DHCP_SNAME_LEN]; /* Server name */ + char file[DHCP_FILE_LEN]; /* Boot filename */ + unsigned char options[DHCP_OPTION_LEN]; + /* Optional parameters + (actual length dependent on MTU). */ +}; + +/* BOOTP (rfc951) message types */ +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +/* Possible values for flags field... */ +#define BOOTP_BROADCAST 32768L + +/* Possible values for hardware type (htype) field... */ +#define HTYPE_ETHER 1 /* Ethernet */ +#define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */ +#define HTYPE_FDDI 8 /* FDDI... */ + +/* Magic cookie validating dhcp options field (and bootp vendor + extensions field). */ +#define DHCP_OPTIONS_COOKIE "\143\202\123\143" + + +/* DHCP Option codes: */ + +#define DHO_PAD 0 +#define DHO_SUBNET_MASK 1 +#define DHO_TIME_OFFSET 2 +#define DHO_ROUTERS 3 +#define DHO_TIME_SERVERS 4 +#define DHO_NAME_SERVERS 5 +#define DHO_DOMAIN_NAME_SERVERS 6 +#define DHO_LOG_SERVERS 7 +#define DHO_COOKIE_SERVERS 8 +#define DHO_LPR_SERVERS 9 +#define DHO_IMPRESS_SERVERS 10 +#define DHO_RESOURCE_LOCATION_SERVERS 11 +#define DHO_HOST_NAME 12 +#define DHO_BOOT_SIZE 13 +#define DHO_MERIT_DUMP 14 +#define DHO_DOMAIN_NAME 15 +#define DHO_SWAP_SERVER 16 +#define DHO_ROOT_PATH 17 +#define DHO_EXTENSIONS_PATH 18 +#define DHO_IP_FORWARDING 19 +#define DHO_NON_LOCAL_SOURCE_ROUTING 20 +#define DHO_POLICY_FILTER 21 +#define DHO_MAX_DGRAM_REASSEMBLY 22 +#define DHO_DEFAULT_IP_TTL 23 +#define DHO_PATH_MTU_AGING_TIMEOUT 24 +#define DHO_PATH_MTU_PLATEAU_TABLE 25 +#define DHO_INTERFACE_MTU 26 +#define DHO_ALL_SUBNETS_LOCAL 27 +#define DHO_BROADCAST_ADDRESS 28 +#define DHO_PERFORM_MASK_DISCOVERY 29 +#define DHO_MASK_SUPPLIER 30 +#define DHO_ROUTER_DISCOVERY 31 +#define DHO_ROUTER_SOLICITATION_ADDRESS 32 +#define DHO_STATIC_ROUTES 33 +#define DHO_TRAILER_ENCAPSULATION 34 +#define DHO_ARP_CACHE_TIMEOUT 35 +#define DHO_IEEE802_3_ENCAPSULATION 36 +#define DHO_DEFAULT_TCP_TTL 37 +#define DHO_TCP_KEEPALIVE_INTERVAL 38 +#define DHO_TCP_KEEPALIVE_GARBAGE 39 +#define DHO_NIS_DOMAIN 40 +#define DHO_NIS_SERVERS 41 +#define DHO_NTP_SERVERS 42 +#define DHO_VENDOR_ENCAPSULATED_OPTIONS 43 +#define DHO_NETBIOS_NAME_SERVERS 44 +#define DHO_NETBIOS_DD_SERVER 45 +#define DHO_NETBIOS_NODE_TYPE 46 +#define DHO_NETBIOS_SCOPE 47 +#define DHO_FONT_SERVERS 48 +#define DHO_X_DISPLAY_MANAGER 49 +#define DHO_DHCP_REQUESTED_ADDRESS 50 +#define DHO_DHCP_LEASE_TIME 51 +#define DHO_DHCP_OPTION_OVERLOAD 52 +#define DHO_DHCP_MESSAGE_TYPE 53 +#define DHO_DHCP_SERVER_IDENTIFIER 54 +#define DHO_DHCP_PARAMETER_REQUEST_LIST 55 +#define DHO_DHCP_MESSAGE 56 +#define DHO_DHCP_MAX_MESSAGE_SIZE 57 +#define DHO_DHCP_RENEWAL_TIME 58 +#define DHO_DHCP_REBINDING_TIME 59 +#define DHO_DHCP_CLASS_IDENTIFIER 60 +#define DHO_DHCP_CLIENT_IDENTIFIER 61 +#define DHO_DHCP_USER_CLASS_ID 77 +#define DHO_END 255 + +/* DHCP message types. */ +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 diff --git a/reactos/subsys/system/dhcp/include/dhcpd.h b/reactos/subsys/system/dhcp/include/dhcpd.h new file mode 100644 index 00000000000..7754d6d0aca --- /dev/null +++ b/reactos/subsys/system/dhcp/include/dhcpd.h @@ -0,0 +1,487 @@ +/* $OpenBSD: dhcpd.h,v 1.33 2004/05/06 22:29:15 deraadt Exp $ */ + +/* + * Copyright (c) 2004 Henning Brauer + * Copyright (c) 1995, 1996, 1997, 1998, 1999 + * The Internet Software Consortium. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#ifndef DHCPD_H +#define DHCPD_H + +#include +#include +#include "stdint.h" + +#define IFNAMSIZ MAX_INTERFACE_NAME_LEN + +#define ETH_ALEN 6 +#define ETHER_ADDR_LEN ETH_ALEN +struct ether_header +{ + u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */ + u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */ + u_int16_t ether_type; /* packet type ID field */ +} __attribute__ ((__packed__)); + +struct ip + { + unsigned int ip_hl:4; /* header length */ + unsigned int ip_v:4; /* version */ + u_int8_t ip_tos; /* type of service */ + u_short ip_len; /* total length */ + u_short ip_id; /* identification */ + u_short ip_off; /* fragment offset field */ +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_int8_t ip_ttl; /* time to live */ + u_int8_t ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct in_addr ip_src, ip_dst; /* source and dest address */ + }; + +struct udphdr { + u_int16_t uh_sport; /* source port */ + u_int16_t uh_dport; /* destination port */ + u_int16_t uh_ulen; /* udp length */ + u_int16_t uh_sum; /* udp checksum */ +}; + +#define ETHERTYPE_IP 0x0800 +#define IPTOS_LOWDELAY 0x10 +#define ARPHRD_ETHER 1 + +// FIXME: I have no idea what this should be! +#define SIZE_T_MAX 1600 + +#define USE_SOCKET_RECEIVE +#define USE_SOCKET_SEND + +#include + +//#include +//#include +#include +#include +//#include +//#include + +//#include +//#include +//#include + +//#include +//#include + +#include +#include +#include +#include +//#include +//#include +#include +#include +#include +#include +#include +//#include +#include +#include + +#include "dhcp.h" +#include "tree.h" + +#define LOCAL_PORT 68 +#define REMOTE_PORT 67 + +struct option_data { + int len; + u_int8_t *data; +}; + +struct string_list { + struct string_list *next; + char *string; +}; + +struct iaddr { + int len; + unsigned char iabuf[16]; +}; + +struct iaddrlist { + struct iaddrlist *next; + struct iaddr addr; +}; + +struct packet { + struct dhcp_packet *raw; + int packet_length; + int packet_type; + int options_valid; + int client_port; + struct iaddr client_addr; + struct interface_info *interface; + struct hardware *haddr; + struct option_data options[256]; +}; + +struct hardware { + u_int8_t htype; + u_int8_t hlen; + u_int8_t haddr[16]; +}; + +struct client_lease { + struct client_lease *next; + time_t expiry, renewal, rebind; + struct iaddr address; + char *server_name; + char *filename; + struct string_list *medium; + unsigned int is_static : 1; + unsigned int is_bootp : 1; + struct option_data options[256]; +}; + +/* Possible states in which the client can be. */ +enum dhcp_state { + S_REBOOTING, + S_INIT, + S_SELECTING, + S_REQUESTING, + S_BOUND, + S_RENEWING, + S_REBINDING +}; + +struct client_config { + struct option_data defaults[256]; + enum { + ACTION_DEFAULT, + ACTION_SUPERSEDE, + ACTION_PREPEND, + ACTION_APPEND + } default_actions[256]; + + struct option_data send_options[256]; + u_int8_t required_options[256]; + u_int8_t requested_options[256]; + int requested_option_count; + time_t timeout; + time_t initial_interval; + time_t retry_interval; + time_t select_interval; + time_t reboot_timeout; + time_t backoff_cutoff; + struct string_list *media; + char *script_name; + enum { IGNORE, ACCEPT, PREFER } + bootp_policy; + struct string_list *medium; + struct iaddrlist *reject_list; +}; + +struct client_state { + struct client_lease *active; + struct client_lease *new; + struct client_lease *offered_leases; + struct client_lease *leases; + struct client_lease *alias; + enum dhcp_state state; + struct iaddr destination; + u_int32_t xid; + u_int16_t secs; + time_t first_sending; + time_t interval; + struct string_list *medium; + struct dhcp_packet packet; + int packet_length; + struct iaddr requested_address; + struct client_config *config; +}; + +struct interface_info { + struct interface_info *next; + struct hardware hw_address; + struct in_addr primary_address; + char name[IFNAMSIZ]; + int rfdesc; + int wfdesc; + unsigned char *rbuf; + size_t rbuf_max; + size_t rbuf_offset; + size_t rbuf_len; + struct client_state *client; + int noifmedia; + int errors; + int dead; + u_int16_t index; +}; + +struct timeout { + struct timeout *next; + time_t when; + void (*func)(void *); + void *what; +}; + +struct protocol { + struct protocol *next; + int fd; + void (*handler)(struct protocol *); + void *local; +}; + +#define DEFAULT_HASH_SIZE 97 + +struct hash_bucket { + struct hash_bucket *next; + unsigned char *name; + int len; + unsigned char *value; +}; + +struct hash_table { + int hash_count; + struct hash_bucket *buckets[DEFAULT_HASH_SIZE]; +}; + +/* Default path to dhcpd config file. */ +#define _PATH_DHCLIENT_CONF "/etc/dhclient.conf" +#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases" +#define DHCPD_LOG_FACILITY LOG_DAEMON + +#define MAX_TIME 0x7fffffff +#define MIN_TIME 0 + +/* External definitions... */ + +/* options.c */ +int cons_options(struct packet *, struct dhcp_packet *, int, + struct tree_cache **, int, int, int, u_int8_t *, int); +char *pretty_print_option(unsigned int, + unsigned char *, int, int, int); +void do_packet(struct interface_info *, struct dhcp_packet *, + int, unsigned int, struct iaddr, struct hardware *); + +/* errwarn.c */ +extern int warnings_occurred; +void error(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +int warning(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +int note(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +int debug(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +int parse_warn(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))); + +/* conflex.c */ +extern int lexline, lexchar; +extern char *token_line, *tlname; +extern char comments[4096]; +extern int comment_index; +extern int eol_token; +void new_parse(char *); +int next_token(char **, FILE *); +int peek_token(char **, FILE *); + +/* parse.c */ +void skip_to_semi(FILE *); +int parse_semi(FILE *); +char *parse_string(FILE *); +int parse_ip_addr(FILE *, struct iaddr *); +void parse_hardware_param(FILE *, struct hardware *); +void parse_lease_time(FILE *, time_t *); +unsigned char *parse_numeric_aggregate(FILE *, unsigned char *, int *, + int, int, int); +void convert_num(unsigned char *, char *, int, int); +time_t parse_date(FILE *); + +/* tree.c */ +pair cons(caddr_t, pair); + +/* alloc.c */ +struct string_list *new_string_list(size_t size); +struct hash_table *new_hash_table(int); +struct hash_bucket *new_hash_bucket(void); + +/* bpf.c */ +int if_register_bpf(struct interface_info *); +void if_register_send(struct interface_info *); +void if_register_receive(struct interface_info *); +ssize_t send_packet(struct interface_info *, struct dhcp_packet *, size_t, + struct in_addr, struct sockaddr_in *, struct hardware *); +ssize_t receive_packet(struct interface_info *, unsigned char *, size_t, + struct sockaddr_in *, struct hardware *); + +/* dispatch.c */ +extern void (*bootp_packet_handler)(struct interface_info *, + struct dhcp_packet *, int, unsigned int, struct iaddr, struct hardware *); +void discover_interfaces(struct interface_info *); +void reinitialize_interfaces(void); +void dispatch(void); +void got_one(struct protocol *); +void add_timeout(time_t, void (*)(void *), void *); +void cancel_timeout(void (*)(void *), void *); +void add_protocol(char *, int, void (*)(struct protocol *), void *); +void remove_protocol(struct protocol *); +int interface_link_status(char *); + +/* hash.c */ +struct hash_table *new_hash(void); +void add_hash(struct hash_table *, unsigned char *, int, unsigned char *); +unsigned char *hash_lookup(struct hash_table *, unsigned char *, int); + +/* tables.c */ +extern struct dhcp_option dhcp_options[256]; +extern unsigned char dhcp_option_default_priority_list[]; +extern int sizeof_dhcp_option_default_priority_list; +extern struct hash_table universe_hash; +extern struct universe dhcp_universe; +void initialize_universes(void); + +/* convert.c */ +u_int32_t getULong(unsigned char *); +int32_t getLong(unsigned char *); +u_int16_t getUShort(unsigned char *); +int16_t getShort(unsigned char *); +void putULong(unsigned char *, u_int32_t); +void putLong(unsigned char *, int32_t); +void putUShort(unsigned char *, unsigned int); +void putShort(unsigned char *, int); + +/* inet.c */ +struct iaddr subnet_number(struct iaddr, struct iaddr); +struct iaddr broadcast_addr(struct iaddr, struct iaddr); +int addr_eq(struct iaddr, struct iaddr); +char *piaddr(struct iaddr); + +/* dhclient.c */ +extern char *path_dhclient_conf; +extern char *path_dhclient_db; +extern time_t cur_time; +extern int log_priority; +extern int log_perror; + +extern struct client_config top_level_config; + +void dhcpoffer(struct packet *); +void dhcpack(struct packet *); +void dhcpnak(struct packet *); + +void send_discover(void *); +void send_request(void *); +void send_decline(void *); + +void state_reboot(void *); +void state_init(void *); +void state_selecting(void *); +void state_requesting(void *); +void state_bound(void *); +void state_panic(void *); + +void bind_lease(struct interface_info *); + +void make_discover(struct interface_info *, struct client_lease *); +void make_request(struct interface_info *, struct client_lease *); +void make_decline(struct interface_info *, struct client_lease *); + +void free_client_lease(struct client_lease *); +void rewrite_client_leases(void); +void write_client_lease(struct interface_info *, struct client_lease *, int); + +void priv_script_init(char *, char *); +void priv_script_write_params(char *, struct client_lease *); +int priv_script_go(void); + +void script_init(char *, struct string_list *); +void script_write_params(char *, struct client_lease *); +int script_go(void); +void client_envadd(struct client_state *, + const char *, const char *, const char *, ...); +void script_set_env(struct client_state *, const char *, const char *, + const char *); +void script_flush_env(struct client_state *); +int dhcp_option_ev_name(char *, size_t, struct dhcp_option *); + +struct client_lease *packet_to_lease(struct packet *); +void go_daemon(void); +void client_location_changed(void); + +void bootp(struct packet *); +void dhcp(struct packet *); + +/* packet.c */ +void assemble_hw_header(struct interface_info *, unsigned char *, + int *, struct hardware *); +void assemble_udp_ip_header(unsigned char *, int *, u_int32_t, u_int32_t, + unsigned int, unsigned char *, int); +ssize_t decode_hw_header(unsigned char *, int, struct hardware *); +ssize_t decode_udp_ip_header(unsigned char *, int, struct sockaddr_in *, + unsigned char *, int); + +/* ethernet.c */ +void assemble_ethernet_header(struct interface_info *, unsigned char *, + int *, struct hardware *); +ssize_t decode_ethernet_header(struct interface_info *, unsigned char *, + int, struct hardware *); + +/* clparse.c */ +int read_client_conf(void); +void read_client_leases(void); +void parse_client_statement(FILE *, struct interface_info *, + struct client_config *); +int parse_X(FILE *, u_int8_t *, int); +int parse_option_list(FILE *, u_int8_t *); +void parse_interface_declaration(FILE *, struct client_config *); +struct interface_info *interface_or_dummy(char *); +void make_client_state(struct interface_info *); +void make_client_config(struct interface_info *, struct client_config *); +void parse_client_lease_statement(FILE *, int); +void parse_client_lease_declaration(FILE *, struct client_lease *, + struct interface_info **); +struct dhcp_option *parse_option_decl(FILE *, struct option_data *); +void parse_string_list(FILE *, struct string_list **, int); +void parse_reject_statement(FILE *, struct client_config *); + +/* privsep.c */ +struct buf *buf_open(size_t); +int buf_add(struct buf *, void *, size_t); +int buf_close(int, struct buf *); +ssize_t buf_read(int, void *, size_t); +void dispatch_imsg(int); + +#endif/*DHCPD_H*/ diff --git a/reactos/subsys/system/dhcp/include/dhctoken.h b/reactos/subsys/system/dhcp/include/dhctoken.h new file mode 100644 index 00000000000..2aeb5303af1 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/dhctoken.h @@ -0,0 +1,136 @@ +/* dhctoken.h + + Tokens for config file lexer and parser. */ + +/* + * Copyright (c) 1995, 1996, 1997, 1998, 1999 + * The Internet Software Consortium. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#define SEMI ';' +#define DOT '.' +#define COLON ':' +#define COMMA ',' +#define SLASH '/' +#define LBRACE '{' +#define RBRACE '}' + +#define FIRST_TOKEN HOST +#define HOST 256 +#define HARDWARE 257 +#define FILENAME 258 +#define FIXED_ADDR 259 +#define OPTION 260 +#define ETHERNET 261 +#define STRING 262 +#define NUMBER 263 +#define NUMBER_OR_NAME 264 +#define NAME 265 +#define TIMESTAMP 266 +#define STARTS 267 +#define ENDS 268 +#define UID 269 +#define CLASS 270 +#define LEASE 271 +#define RANGE 272 +#define PACKET 273 +#define CIADDR 274 +#define YIADDR 275 +#define SIADDR 276 +#define GIADDR 277 +#define SUBNET 278 +#define NETMASK 279 +#define DEFAULT_LEASE_TIME 280 +#define MAX_LEASE_TIME 281 +#define VENDOR_CLASS 282 +#define USER_CLASS 283 +#define SHARED_NETWORK 284 +#define SERVER_NAME 285 +#define DYNAMIC_BOOTP 286 +#define SERVER_IDENTIFIER 287 +#define DYNAMIC_BOOTP_LEASE_CUTOFF 288 +#define DYNAMIC_BOOTP_LEASE_LENGTH 289 +#define BOOT_UNKNOWN_CLIENTS 290 +#define NEXT_SERVER 291 +#define TOKEN_RING 292 +#define GROUP 293 +#define ONE_LEASE_PER_CLIENT 294 +#define GET_LEASE_HOSTNAMES 295 +#define USE_HOST_DECL_NAMES 296 +#define SEND 297 +#define CLIENT_IDENTIFIER 298 +#define REQUEST 299 +#define REQUIRE 300 +#define TIMEOUT 301 +#define RETRY 302 +#define SELECT_TIMEOUT 303 +#define SCRIPT 304 +#define INTERFACE 305 +#define RENEW 306 +#define REBIND 307 +#define EXPIRE 308 +#define UNKNOWN_CLIENTS 309 +#define ALLOW 310 +#define BOOTP 311 +#define DENY 312 +#define BOOTING 313 +#define DEFAULT 314 +#define MEDIA 315 +#define MEDIUM 316 +#define ALIAS 317 +#define REBOOT 318 +#define ABANDONED 319 +#define BACKOFF_CUTOFF 320 +#define INITIAL_INTERVAL 321 +#define NAMESERVER 322 +#define DOMAIN 323 +#define SEARCH 324 +#define SUPERSEDE 325 +#define APPEND 326 +#define PREPEND 327 +#define HOSTNAME 328 +#define CLIENT_HOSTNAME 329 +#define REJECT 330 +#define FDDI 331 +#define USE_LEASE_ADDR_FOR_DEFAULT_ROUTE 332 +#define AUTHORITATIVE 333 +#define TOKEN_NOT 334 +#define ALWAYS_REPLY_RFC1048 335 + +#define is_identifier(x) ((x) >= FIRST_TOKEN && \ + (x) != STRING && \ + (x) != NUMBER && \ + (x) != EOF) diff --git a/reactos/subsys/system/dhcp/include/hash.h b/reactos/subsys/system/dhcp/include/hash.h new file mode 100644 index 00000000000..1bebb3140f8 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/hash.h @@ -0,0 +1,56 @@ +/* hash.h + + Definitions for hashing... */ + +/* + * Copyright (c) 1995, 1996 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#define DEFAULT_HASH_SIZE 97 + +struct hash_bucket { + struct hash_bucket *next; + unsigned char *name; + int len; + unsigned char *value; +}; + +struct hash_table { + int hash_count; + struct hash_bucket *buckets [DEFAULT_HASH_SIZE]; +}; + diff --git a/reactos/subsys/system/dhcp/include/inet.h b/reactos/subsys/system/dhcp/include/inet.h new file mode 100644 index 00000000000..a45f92265de --- /dev/null +++ b/reactos/subsys/system/dhcp/include/inet.h @@ -0,0 +1,52 @@ +/* inet.h + + Portable definitions for internet addresses */ + +/* + * Copyright (c) 1996 The Internet Software Consortium. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +/* An internet address of up to 128 bits. */ + +typedef struct _iaddr { + int len; + unsigned char iabuf [16]; +} iaddr; + +typedef struct _iaddrlist { + struct _iaddrlist *next; + iaddr addr; +} iaddrlist; diff --git a/reactos/subsys/system/dhcp/include/osdep.h b/reactos/subsys/system/dhcp/include/osdep.h new file mode 100644 index 00000000000..71a985980e1 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/osdep.h @@ -0,0 +1,294 @@ +/* osdep.h + + Operating system dependencies... */ + +/* + * Copyright (c) 1996, 1997, 1998, 1999 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE INTERNET SOFTWARE CONSORTIUM OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software was written for the Internet Software Consortium by Ted Lemon + * under a contract with Vixie Laboratories. + */ + +#include "site.h" + +/* Porting:: + + If you add a new network API, you must add a check for it below: */ + +#if !defined (USE_SOCKETS) && \ + !defined (USE_SOCKET_SEND) && \ + !defined (USE_SOCKET_RECEIVE) && \ + !defined (USE_RAW_SOCKETS) && \ + !defined (USE_RAW_SEND) && \ + !defined (USE_SOCKET_RECEIVE) && \ + !defined (USE_BPF) && \ + !defined (USE_BPF_SEND) && \ + !defined (USE_BPF_RECEIVE) && \ + !defined (USE_LPF) && \ + !defined (USE_LPF_SEND) && \ + !defined (USE_LPF_RECEIVE) && \ + !defined (USE_NIT) && \ + !defined (USE_NIT_SEND) && \ + !defined (USE_NIT_RECEIVE) && \ + !defined (USR_DLPI_SEND) && \ + !defined (USE_DLPI_RECEIVE) +# define USE_DEFAULT_NETWORK +#endif + + +/* Porting:: + + If you add a new system configuration file, include it here: */ + +#if defined (sun) +# if defined (__svr4__) || defined (__SVR4) +# include "cf/sunos5-5.h" +# else +# include "cf/sunos4.h" +# endif +#endif + +#ifdef aix +# include "cf/aix.h" +#endif + +#ifdef bsdi +# include "cf/bsdos.h" +#endif + +#ifdef __NetBSD__ +# include "cf/netbsd.h" +#endif + +#ifdef __FreeBSD__ +# include "cf/freebsd.h" +#endif + +#if defined (__osf__) && defined (__alpha) +# include "cf/alphaosf.h" +#endif + +#ifdef ultrix +# include "cf/ultrix.h" +#endif + +#ifdef linux +# include "cf/linux.h" +#endif + +#ifdef SCO +# include "cf/sco.h" +#endif + +#if defined (hpux) || defined (__hpux) +# include "cf/hpux.h" +#endif + +#ifdef __QNX__ +# include "cf/qnx.h" +#endif + +#ifdef __CYGWIN32__ +# include "cf/cygwin32.h" +#endif + +#ifdef __APPLE__ +# include "cf/rhapsody.h" +#else +# if defined (NeXT) +# include "cf/nextstep.h" +# endif +#endif + +#if defined(IRIX) || defined(__sgi) +# include "cf/irix.h" +#endif + +#if !defined (TIME_MAX) +# define TIME_MAX 2147483647 +#endif + +/* Porting:: + + If you add a new network API, and have it set up so that it can be + used for sending or receiving, but doesn't have to be used for both, + then set up an ifdef like the ones below: */ + +#ifdef USE_SOCKETS +# define USE_SOCKET_SEND +# define USE_SOCKET_RECEIVE +#endif + +#ifdef USE_RAW_SOCKETS +# define USE_RAW_SEND +# define USE_SOCKET_RECEIVE +#endif + +#ifdef USE_BPF +# define USE_BPF_SEND +# define USE_BPF_RECEIVE +#endif + +#ifdef USE_LPF +# define USE_LPF_SEND +# define USE_LPF_RECEIVE +#endif + +#ifdef USE_NIT +# define USE_NIT_SEND +# define USE_NIT_RECEIVE +#endif + +#ifdef USE_DLPI +# define USE_DLPI_SEND +# define USE_DLPI_RECEIVE +#endif + +#ifdef USE_UPF +# define USE_UPF_SEND +# define USE_UPF_RECEIVE +#endif + +/* Porting:: + + If you add support for sending packets directly out an interface, + and your support does not do ARP or routing, you must use a fallback + mechanism to deal with packets that need to be sent to routers. + Currently, all low-level packet interfaces use BSD sockets as a + fallback. */ + +#if defined (USE_BPF_SEND) || defined (USE_NIT_SEND) || \ + defined (USE_DLPI_SEND) || defined (USE_UPF_SEND) || defined (USE_LPF_SEND) +# define USE_SOCKET_FALLBACK +# define USE_FALLBACK +#endif + +/* Porting:: + + If you add support for sending packets directly out an interface + and need to be able to assemble packets, add the USE_XXX_SEND + definition for your interface to the list tested below. */ + +#if defined (USE_RAW_SEND) || defined (USE_BPF_SEND) || \ + defined (USE_NIT_SEND) || defined (USE_UPF_SEND) || \ + defined (USE_DLPI_SEND) || defined (USE_LPF_SEND) +# define PACKET_ASSEMBLY +#endif + +/* Porting:: + + If you add support for receiving packets directly from an interface + and need to be able to decode raw packets, add the USE_XXX_RECEIVE + definition for your interface to the list tested below. */ + +#if defined (USE_RAW_RECEIVE) || defined (USE_BPF_SEND) || \ + defined (USE_NIT_RECEIVE) || defined (USE_UPF_RECEIVE) || \ + defined (USE_DLPI_RECEIVE) || \ + defined (USE_LPF_SEND) || \ + (defined (USE_SOCKET_SEND) && defined (SO_BINDTODEVICE)) +# define PACKET_DECODING +#endif + +/* If we don't have a DLPI packet filter, we have to filter in userland. + Probably not worth doing, actually. */ +#if defined (USE_DLPI_RECEIVE) && !defined (USE_DLPI_PFMOD) +# define USERLAND_FILTER +#endif + +/* jmp_buf is assumed to be a struct unless otherwise defined in the + system header. */ +#ifndef jbp_decl +# define jbp_decl(x) jmp_buf *x +#endif +#ifndef jref +# define jref(x) (&(x)) +#endif +#ifndef jdref +# define jdref(x) (*(x)) +#endif +#ifndef jrefproto +# define jrefproto jmp_buf * +#endif + +#ifndef BPF_FORMAT +# define BPF_FORMAT "/dev/bpf%d" +#endif + +#if defined (IFF_POINTOPOINT) && !defined (HAVE_IFF_POINTOPOINT) +# define HAVE_IFF_POINTOPOINT +#endif + +#if defined (AF_LINK) && !defined (HAVE_AF_LINK) +# define HAVE_AF_LINK +#endif + +#if defined (ARPHRD_TUNNEL) && !defined (HAVE_ARPHRD_TUNNEL) +# define HAVE_ARPHRD_TUNNEL +#endif + +#if defined (ARPHRD_LOOPBACK) && !defined (HAVE_ARPHRD_LOOPBACK) +# define HAVE_ARPHRD_LOOPBACK +#endif + +#if defined (ARPHRD_ROSE) && !defined (HAVE_ARPHRD_ROSE) +# define HAVE_ARPHRD_ROSE +#endif + +#if defined (ARPHRD_IEEE802) && !defined (HAVE_ARPHRD_IEEE802) +# define HAVE_ARPHRD_IEEE802 +#endif + +#if defined (ARPHRD_FDDI) && !defined (HAVE_ARPHRD_FDDI) +# define HAVE_ARPHRD_FDDI +#endif + +#if defined (ARPHRD_AX25) && !defined (HAVE_ARPHRD_AX25) +# define HAVE_ARPHRD_AX25 +#endif + +#if defined (ARPHRD_NETROM) && !defined (HAVE_ARPHRD_NETROM) +# define HAVE_ARPHRD_NETROM +#endif + +#if defined (ARPHRD_METRICOM) && !defined (HAVE_ARPHRD_METRICOM) +# define HAVE_ARPHRD_METRICOM +#endif + +#if defined (SO_BINDTODEVICE) && !defined (HAVE_SO_BINDTODEVICE) +# define HAVE_SO_BINDTODEVICE +#endif + +#if defined (SIOCGIFHWADDR) && !defined (HAVE_SIOCGIFHWADDR) +# define HAVE_SIOCGIFHWADDR +#endif + +#if defined (AF_LINK) && !defined (HAVE_AF_LINK) +# define HAVE_AF_LINK +#endif diff --git a/reactos/subsys/system/dhcp/include/predec.h b/reactos/subsys/system/dhcp/include/predec.h new file mode 100644 index 00000000000..7f2515beb33 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/predec.h @@ -0,0 +1,7 @@ +#ifndef REACTOS_PREDEC_H +#define REACTOS_PREDEC_H + +struct iaddr; +struct interface_info; + +#endif diff --git a/reactos/subsys/system/dhcp/include/privsep.h b/reactos/subsys/system/dhcp/include/privsep.h new file mode 100644 index 00000000000..e1fc52d5b69 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/privsep.h @@ -0,0 +1,47 @@ +/* $OpenBSD: privsep.h,v 1.2 2004/05/04 18:51:18 henning Exp $ */ + +/* + * Copyright (c) 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +//#include +//#include + +struct buf { + u_char *buf; + size_t size; + size_t wpos; + size_t rpos; +}; + +enum imsg_code { + IMSG_NONE, + IMSG_SCRIPT_INIT, + IMSG_SCRIPT_WRITE_PARAMS, + IMSG_SCRIPT_GO, + IMSG_SCRIPT_GO_RET +}; + +struct imsg_hdr { + enum imsg_code code; + size_t len; +}; + +struct buf *buf_open(size_t); +int buf_add(struct buf *, void *, size_t); +int buf_close(int, struct buf *); +ssize_t buf_read(int sock, void *, size_t); diff --git a/reactos/subsys/system/dhcp/include/rosdhcp.h b/reactos/subsys/system/dhcp/include/rosdhcp.h new file mode 100644 index 00000000000..6c093ecbe3f --- /dev/null +++ b/reactos/subsys/system/dhcp/include/rosdhcp.h @@ -0,0 +1,55 @@ +#ifndef ROSDHCP_H +#define ROSDHCP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdint.h" +#include "predec.h" +#include "debug.h" +#define IFNAMSIZ MAX_INTERFACE_NAME_LEN +#undef interface /* wine/objbase.h -- Grrr */ + +#undef IGNORE +#undef ACCEPT +#undef PREFER +#define DHCP_DISCOVER_INTERVAL 15 +#define DHCP_REBOOT_TIMEOUT 300 +#define DHCP_BACKOFF_MAX 300 +#define _PATH_DHCLIENT_PID "\\systemroot\\system32\\drivers\\etc\\dhclient.pid" + +typedef void *VOIDPTR; + +#define NTOS_MODE_USER +#include +typedef u_int32_t uintTIME; +#define TIME uintTIME +#include "dhcpd.h" + +#define INLINE inline +#define PROTO(x) x + +typedef void (*handler_t) PROTO ((struct packet *)); + +typedef struct _DHCP_ADAPTER { + LIST_ENTRY ListEntry; + MIB_IFROW IfMib; + MIB_IPADDRROW IfAddr; + SOCKADDR Address; + struct interface_info DhclientInfo; + struct client_state DhclientState; + struct client_config DhclientConfig; + struct sockaddr_in ListenAddr; + unsigned int BindStatus; + char recv_buf[1]; +} DHCP_ADAPTER, *PDHCP_ADAPTER; + +#define random rand +#define srandom srand + +#endif/*ROSDHCP_H*/ diff --git a/reactos/subsys/system/dhcp/include/site.h b/reactos/subsys/system/dhcp/include/site.h new file mode 100644 index 00000000000..30fdb703005 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/site.h @@ -0,0 +1,100 @@ +/* Site-specific definitions. + + For supported systems, you shouldn't need to make any changes here. + However, you may want to, in order to deal with site-specific + differences. */ + +/* Add any site-specific definitions and inclusions here... */ + +/* #include */ +/* #define SITE_FOOBAR */ + +/* Define this if you don't want dhcpd to run as a daemon and do want + to see all its output printed to stdout instead of being logged via + syslog(). This also makes dhcpd use the dhcpd.conf in its working + directory and write the dhcpd.leases file there. */ + +/* #define DEBUG */ + +/* Define this to see what the parser is parsing. You probably don't + want to see this. */ + +/* #define DEBUG_TOKENS */ + +/* Define this to see dumps of incoming and outgoing packets. This + slows things down quite a bit... */ + +/* #define DEBUG_PACKET */ + +/* Define this if you want to see dumps of tree evaluations. The most + common reason for doing this is to watch what happens with DNS name + lookups. */ + +/* #define DEBUG_EVAL */ + +/* Define this if you want the dhcpd.pid file to go somewhere other than + the default (which varies from system to system, but is usually either + /etc or /var/run. */ + +/* #define _PATH_DHCPD_PID "/var/run/dhcpd.pid" */ + +/* Define this if you want the dhcpd.leases file (the dynamic lease database) + to go somewhere other than the default location, which is normally + /etc/dhcpd.leases. */ + +/* #define _PATH_DHCPD_DB "/etc/dhcpd.leases" */ + +/* Define this if you want the dhcpd.conf file to go somewhere other than + the default location. By default, it goes in /etc/dhcpd.conf. */ + +/* #define _PATH_DHCPD_CONF "/etc/dhcpd.conf" */ + +/* Network API definitions. You do not need to choose one of these - if + you don't choose, one will be chosen for you in your system's config + header. DON'T MESS WITH THIS UNLESS YOU KNOW WHAT YOU'RE DOING!!! */ + +/* Define this to use the standard BSD socket API. + + On many systems, the BSD socket API does not provide the ability to + send packets to the 255.255.255.255 broadcast address, which can + prevent some clients (e.g., Win95) from seeing replies. This is + not a problem on Solaris. + + In addition, the BSD socket API will not work when more than one + network interface is configured on the server. + + However, the BSD socket API is about as efficient as you can get, so if + the aforementioned problems do not matter to you, or if no other + API is supported for your system, you may want to go with it. */ + +/* #define USE_SOCKETS */ + +/* Define this to use the Sun Streams NIT API. + + The Sun Streams NIT API is only supported on SunOS 4.x releases. */ + +/* #define USE_NIT */ + +/* Define this to use the Berkeley Packet Filter API. + + The BPF API is available on all 4.4-BSD derivatives, including + NetBSD, FreeBSD and BSDI's BSD/OS. It's also available on + DEC Alpha OSF/1 in a compatibility mode supported by the Alpha OSF/1 + packetfilter interface. */ + +/* #define USE_BPF */ + +/* Define this to use the raw socket API. + + The raw socket API is provided on many BSD derivatives, and provides + a way to send out raw IP packets. It is only supported for sending + packets - packets must be received with the regular socket API. + This code is experimental - I've never gotten it to actually transmit + a packet to the 255.255.255.255 broadcast address - so use it at your + own risk. */ + +/* #define USE_RAW_SOCKETS */ + +/* Define this to change the logging facility used by dhcpd. */ + +/* #define DHCPD_LOG_FACILITY LOG_DAEMON */ diff --git a/reactos/subsys/system/dhcp/include/stdint.h b/reactos/subsys/system/dhcp/include/stdint.h new file mode 100644 index 00000000000..988c30359e9 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/stdint.h @@ -0,0 +1,13 @@ +#ifndef REACTOS_STDINT_H +#define REACTOS_STDINT_H + +typedef signed char int8_t; +typedef unsigned char u_int8_t; +typedef short int16_t; +typedef unsigned short u_int16_t; +typedef int int32_t; +typedef unsigned int u_int32_t; + +typedef char *caddr_t; + +#endif diff --git a/reactos/subsys/system/dhcp/include/sysconf.h b/reactos/subsys/system/dhcp/include/sysconf.h new file mode 100644 index 00000000000..5feb4c75c70 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/sysconf.h @@ -0,0 +1,52 @@ +/* systat.h + + Definitions for systat protocol... */ + +/* + * Copyright (c) 1997 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#define SYSCONF_SOCKET "/var/run/sysconf" + +struct sysconf_header { + u_int32_t type; /* Type of status message... */ + u_int32_t length; /* Length of message. */ +}; + +/* Message types... */ +#define NETWORK_LOCATION_CHANGED 1 + diff --git a/reactos/subsys/system/dhcp/include/tree.h b/reactos/subsys/system/dhcp/include/tree.h new file mode 100644 index 00000000000..367ffa7d9a1 --- /dev/null +++ b/reactos/subsys/system/dhcp/include/tree.h @@ -0,0 +1,66 @@ +/* $OpenBSD: tree.h,v 1.5 2004/05/06 22:29:15 deraadt Exp $ */ + +/* Definitions for address trees... */ + +/* + * Copyright (c) 1995 The Internet Software Consortium. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +/* A pair of pointers, suitable for making a linked list. */ +typedef struct _pair { + caddr_t car; + struct _pair *cdr; +} *pair; + +struct tree_cache { + unsigned char *value; + int len; + int buf_size; + time_t timeout; +}; + +struct universe { + char *name; + struct hash_table *hash; + struct dhcp_option *options[256]; +}; + +struct dhcp_option { + char *name; + char *format; + struct universe *universe; + unsigned char code; +}; diff --git a/reactos/subsys/system/dhcp/include/version.h b/reactos/subsys/system/dhcp/include/version.h new file mode 100644 index 00000000000..303fbfa332b --- /dev/null +++ b/reactos/subsys/system/dhcp/include/version.h @@ -0,0 +1,3 @@ +/* Current version of ISC DHCP Distribution. */ + +#define DHCP_VERSION "2.0pl5" diff --git a/reactos/subsys/system/dhcp/memory.c b/reactos/subsys/system/dhcp/memory.c new file mode 100644 index 00000000000..3673948dcba --- /dev/null +++ b/reactos/subsys/system/dhcp/memory.c @@ -0,0 +1,915 @@ +/* memory.c + + Memory-resident database... */ + +/* + * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: memory.c,v 1.35.2.4 1999/05/27 17:47:43 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "rosdhcp.h" +#include "dhcpd.h" + +struct subnet *subnets; +struct shared_network *shared_networks; +static struct hash_table *host_hw_addr_hash; +static struct hash_table *host_uid_hash; +static struct hash_table *lease_uid_hash; +static struct hash_table *lease_ip_addr_hash; +static struct hash_table *lease_hw_addr_hash; +struct lease *dangling_leases; + +static struct hash_table *vendor_class_hash; +static struct hash_table *user_class_hash; + +void enter_host (hd) + struct host_decl *hd; +{ + struct host_decl *hp = (struct host_decl *)0; + struct host_decl *np = (struct host_decl *)0; + + hd -> n_ipaddr = (struct host_decl *)0; + + if (hd -> interface.hlen) { + if (!host_hw_addr_hash) + host_hw_addr_hash = new_hash (); + else + hp = (struct host_decl *) + hash_lookup (host_hw_addr_hash, + hd -> interface.haddr, + hd -> interface.hlen); + + /* If there isn't already a host decl matching this + address, add it to the hash table. */ + if (!hp) + add_hash (host_hw_addr_hash, + hd -> interface.haddr, hd -> interface.hlen, + (unsigned char *)hd); + } + + /* If there was already a host declaration for this hardware + address, add this one to the end of the list. */ + + if (hp) { + for (np = hp; np -> n_ipaddr; np = np -> n_ipaddr) + ; + np -> n_ipaddr = hd; + } + + + if (hd -> group -> options [DHO_DHCP_CLIENT_IDENTIFIER]) { + if (!tree_evaluate (hd -> group -> options + [DHO_DHCP_CLIENT_IDENTIFIER])) + return; + + /* If there's no uid hash, make one; otherwise, see if + there's already an entry in the hash for this host. */ + if (!host_uid_hash) { + host_uid_hash = new_hash (); + hp = (struct host_decl *)0; + } else + hp = (struct host_decl *) hash_lookup + (host_uid_hash, + hd -> group -> options + [DHO_DHCP_CLIENT_IDENTIFIER] -> value, + hd -> group -> options + [DHO_DHCP_CLIENT_IDENTIFIER] -> len); + + /* If there's already a host declaration for this + client identifier, add this one to the end of the + list. Otherwise, add it to the hash table. */ + if (hp) { + /* Don't link it in twice... */ + if (!np) { + for (np = hp; np -> n_ipaddr; + np = np -> n_ipaddr) + ; + np -> n_ipaddr = hd; + } + } else { + add_hash (host_uid_hash, + hd -> group -> options + [DHO_DHCP_CLIENT_IDENTIFIER] -> value, + hd -> group -> options + [DHO_DHCP_CLIENT_IDENTIFIER] -> len, + (unsigned char *)hd); + } + } +} + +struct host_decl *find_hosts_by_haddr (htype, haddr, hlen) + int htype; + unsigned char *haddr; + int hlen; +{ + struct host_decl *foo; + + foo = (struct host_decl *)hash_lookup (host_hw_addr_hash, + haddr, hlen); + return foo; +} + +struct host_decl *find_hosts_by_uid (data, len) + unsigned char *data; + int len; +{ + struct host_decl *foo; + + foo = (struct host_decl *)hash_lookup (host_uid_hash, data, len); + return foo; +} + +/* More than one host_decl can be returned by find_hosts_by_haddr or + find_hosts_by_uid, and each host_decl can have multiple addresses. + Loop through the list of hosts, and then for each host, through the + list of addresses, looking for an address that's in the same shared + network as the one specified. Store the matching address through + the addr pointer, update the host pointer to point at the host_decl + that matched, and return the subnet that matched. */ + +subnet *find_host_for_network (struct host_decl **host, iaddr *addr, + shared_network *share) +{ + int i; + subnet *subnet; + iaddr ip_address; + struct host_decl *hp; + + for (hp = *host; hp; hp = hp -> n_ipaddr) { + if (!hp -> fixed_addr || !tree_evaluate (hp -> fixed_addr)) + continue; + for (i = 0; i < hp -> fixed_addr -> len; i += 4) { + ip_address.len = 4; + memcpy (ip_address.iabuf, + hp -> fixed_addr -> value + i, 4); + subnet = find_grouped_subnet (share, ip_address); + if (subnet) { + *addr = ip_address; + *host = hp; + return subnet; + } + } + } + return (struct _subnet *)0; +} + +void new_address_range (iaddr low, iaddr high, subnet *subnet, int dynamic) +{ + lease *address_range, *lp, *plp; + iaddr net; + int min, max, i; + char lowbuf [16], highbuf [16], netbuf [16]; + shared_network *share = subnet -> shared_network; + struct hostent *h; + struct in_addr ia; + + /* All subnets should have attached shared network structures. */ + if (!share) { + strcpy (netbuf, piaddr (subnet -> net)); + error ("No shared network for network %s (%s)", + netbuf, piaddr (subnet -> netmask)); + } + + /* Initialize the hash table if it hasn't been done yet. */ + if (!lease_uid_hash) + lease_uid_hash = new_hash (); + if (!lease_ip_addr_hash) + lease_ip_addr_hash = new_hash (); + if (!lease_hw_addr_hash) + lease_hw_addr_hash = new_hash (); + + /* Make sure that high and low addresses are in same subnet. */ + net = subnet_number (low, subnet -> netmask); + if (!addr_eq (net, subnet_number (high, subnet -> netmask))) { + strcpy (lowbuf, piaddr (low)); + strcpy (highbuf, piaddr (high)); + strcpy (netbuf, piaddr (subnet -> netmask)); + error ("Address range %s to %s, netmask %s spans %s!", + lowbuf, highbuf, netbuf, "multiple subnets"); + } + + /* Make sure that the addresses are on the correct subnet. */ + if (!addr_eq (net, subnet -> net)) { + strcpy (lowbuf, piaddr (low)); + strcpy (highbuf, piaddr (high)); + strcpy (netbuf, piaddr (subnet -> netmask)); + error ("Address range %s to %s not on net %s/%s!", + lowbuf, highbuf, piaddr (subnet -> net), netbuf); + } + + /* Get the high and low host addresses... */ + max = host_addr (high, subnet -> netmask); + min = host_addr (low, subnet -> netmask); + + /* Allow range to be specified high-to-low as well as low-to-high. */ + if (min > max) { + max = min; + min = host_addr (high, subnet -> netmask); + } + + /* Get a lease structure for each address in the range. */ + address_range = new_leases (max - min + 1, "new_address_range"); + if (!address_range) { + strcpy (lowbuf, piaddr (low)); + strcpy (highbuf, piaddr (high)); + error ("No memory for address range %s-%s.", lowbuf, highbuf); + } + memset (address_range, 0, (sizeof *address_range) * (max - min + 1)); + + /* Fill in the last lease if it hasn't been already... */ + if (!share -> last_lease) { + share -> last_lease = &address_range [0]; + } + + /* Fill out the lease structures with some minimal information. */ + for (i = 0; i < max - min + 1; i++) { + address_range [i].ip_addr = + ip_addr (subnet -> net, subnet -> netmask, i + min); + address_range [i].starts = + address_range [i].timestamp = MIN_TIME; + address_range [i].ends = MIN_TIME; + address_range [i].subnet = subnet; + address_range [i].shared_network = share; + address_range [i].flags = dynamic ? DYNAMIC_BOOTP_OK : 0; + + memcpy (&ia, address_range [i].ip_addr.iabuf, 4); + + if (subnet -> group -> get_lease_hostnames) { + h = gethostbyaddr ((char *)&ia, sizeof ia, AF_INET); + if (!h) + warn ("No hostname for %s", inet_ntoa (ia)); + else { + address_range [i].hostname = + malloc (strlen (h -> h_name) + 1); + if (!address_range [i].hostname) + error ("no memory for hostname %s.", + h -> h_name); + strcpy (address_range [i].hostname, + h -> h_name); + } + } + + /* Link this entry into the list. */ + address_range [i].next = share -> leases; + address_range [i].prev = (struct lease *)0; + share -> leases = &address_range [i]; + if (address_range [i].next) + address_range [i].next -> prev = share -> leases; + add_hash (lease_ip_addr_hash, + address_range [i].ip_addr.iabuf, + address_range [i].ip_addr.len, + (unsigned char *)&address_range [i]); + } + + /* Find out if any dangling leases are in range... */ + plp = (struct lease *)0; + for (lp = dangling_leases; lp; lp = lp -> next) { + iaddr lnet; + int lhost; + + lnet = subnet_number (lp -> ip_addr, subnet -> netmask); + lhost = host_addr (lp -> ip_addr, subnet -> netmask); + + /* If it's in range, fill in the real lease structure with + the dangling lease's values, and remove the lease from + the list of dangling leases. */ + if (addr_eq (lnet, subnet -> net) && + lhost >= i && lhost <= max) { + if (plp) { + plp -> next = lp -> next; + } else { + dangling_leases = lp -> next; + } + lp -> next = (struct lease *)0; + address_range [lhost - i].hostname = lp -> hostname; + address_range [lhost - i].client_hostname = + lp -> client_hostname; + supersede_lease (&address_range [lhost - i], lp, 0); + free_lease (lp, "new_address_range"); + } else + plp = lp; + } +} + +subnet *find_subnet (iaddr addr) +{ + subnet *rv; + + for (rv = subnets; rv; rv = rv -> next_subnet) { + if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) + return rv; + } + return (subnet *)0; +} + +subnet *find_grouped_subnet (shared_network *share, iaddr addr) +{ + subnet *rv; + + for (rv = share -> subnets; rv; rv = rv -> next_sibling) { + if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) + return rv; + } + return (subnet *)0; +} + +int subnet_inner_than (struct _subnet *subnet, struct _subnet *scan, int warnp) +{ + if (addr_eq (subnet_number (subnet -> net, scan -> netmask), + scan -> net) || + addr_eq (subnet_number (scan -> net, subnet -> netmask), + subnet -> net)) { + char n1buf [16]; + int i, j; + for (i = 0; i < 32; i++) + if (subnet -> netmask.iabuf [3 - (i >> 3)] + & (1 << (i & 7))) + break; + for (j = 0; j < 32; j++) + if (scan -> netmask.iabuf [3 - (j >> 3)] & + (1 << (j & 7))) + break; + strcpy (n1buf, piaddr (subnet -> net)); + if (warnp) + warn ("%ssubnet %s/%d conflicts with subnet %s/%d", + "Warning: ", n1buf, 32 - i, + piaddr (scan -> net), 32 - j); + if (i < j) + return 1; + } + return 0; +} + +/* Enter a new subnet into the subnet list. */ + +void enter_subnet (struct _subnet *subnet) +{ + struct _subnet *scan, *prev = (struct _subnet *)0; + + /* Check for duplicates... */ + for (scan = subnets; scan; scan = scan -> next_subnet) { + /* When we find a conflict, make sure that the + subnet with the narrowest subnet mask comes + first. */ + if (subnet_inner_than (subnet, scan, 1)) { + if (prev) { + prev -> next_subnet = subnet; + } else + subnets = subnet; + subnet -> next_subnet = scan; + return; + } + prev = scan; + } + + /* XXX use the BSD radix tree code instead of a linked list. */ + subnet -> next_subnet = subnets; + subnets = subnet; +} + +/* Enter a new shared network into the shared network list. */ + +void enter_shared_network (shared_network *share) +{ + /* XXX Sort the nets into a balanced tree to make searching quicker. */ + share -> next = shared_networks; + shared_networks = share; +} + +/* Enter a lease into the system. This is called by the parser each + time it reads in a new lease. If the subnet for that lease has + already been read in (usually the case), just update that lease; + otherwise, allocate temporary storage for the lease and keep it around + until we're done reading in the config file. */ + +void enter_lease (struct _lease *lease) +{ + struct _lease *comp = find_lease_by_ip_addr (lease -> ip_addr); + + /* If we don't have a place for this lease yet, save it for + later. */ + if (!comp) { + comp = new_lease ("enter_lease"); + if (!comp) { + error ("No memory for lease %s\n", + piaddr (lease -> ip_addr)); + } + *comp = *lease; + comp -> next = dangling_leases; + comp -> prev = (struct lease *)0; + dangling_leases = comp; + } else { + /* Record the hostname information in the lease. */ + comp -> hostname = lease -> hostname; + comp -> client_hostname = lease -> client_hostname; + supersede_lease (comp, lease, 0); + } +} + +/* Replace the data in an existing lease with the data in a new lease; + adjust hash tables to suit, and insertion sort the lease into the + list of leases by expiry time so that we can always find the oldest + lease. */ + +int supersede_lease (struct _lease *comp, struct _lease *lease, int commit) +{ + int enter_uid = 0; + int enter_hwaddr = 0; + struct _lease *lp; + + /* Static leases are not currently kept in the database... */ + if (lease -> flags & STATIC_LEASE) + return 1; + + /* If the existing lease hasn't expired and has a different + unique identifier or, if it doesn't have a unique + identifier, a different hardware address, then the two + leases are in conflict. If the existing lease has a uid + and the new one doesn't, but they both have the same + hardware address, and dynamic bootp is allowed on this + lease, then we allow that, in case a dynamic BOOTP lease is + requested *after* a DHCP lease has been assigned. */ + + if (!(lease -> flags & ABANDONED_LEASE) && + comp -> ends > cur_time && + (((comp -> uid && lease -> uid) && + (comp -> uid_len != lease -> uid_len || + memcmp (comp -> uid, lease -> uid, comp -> uid_len))) || + (!comp -> uid && + ((comp -> hardware_addr.htype != + lease -> hardware_addr.htype) || + (comp -> hardware_addr.hlen != + lease -> hardware_addr.hlen) || + memcmp (comp -> hardware_addr.haddr, + lease -> hardware_addr.haddr, + comp -> hardware_addr.hlen))))) { + warn ("Lease conflict at %s", + piaddr (comp -> ip_addr)); + return 0; + } else { + /* If there's a Unique ID, dissociate it from the hash + table and free it if necessary. */ + if (comp -> uid) { + uid_hash_delete (comp); + enter_uid = 1; + if (comp -> uid != &comp -> uid_buf [0]) { + free (comp -> uid); + comp -> uid_max = 0; + comp -> uid_len = 0; + } + comp -> uid = (unsigned char *)0; + } else + enter_uid = 1; + + if (comp -> hardware_addr.htype && + ((comp -> hardware_addr.hlen != + lease -> hardware_addr.hlen) || + (comp -> hardware_addr.htype != + lease -> hardware_addr.htype) || + memcmp (comp -> hardware_addr.haddr, + lease -> hardware_addr.haddr, + comp -> hardware_addr.hlen))) { + hw_hash_delete (comp); + enter_hwaddr = 1; + } else if (!comp -> hardware_addr.htype) + enter_hwaddr = 1; + + /* Copy the data files, but not the linkages. */ + comp -> starts = lease -> starts; + if (lease -> uid) { + if (lease -> uid_len < sizeof (lease -> uid_buf)) { + memcpy (comp -> uid_buf, + lease -> uid, lease -> uid_len); + comp -> uid = &comp -> uid_buf [0]; + comp -> uid_max = sizeof comp -> uid_buf; + } else if (lease -> uid != &lease -> uid_buf [0]) { + comp -> uid = lease -> uid; + comp -> uid_max = lease -> uid_max; + lease -> uid = (unsigned char *)0; + lease -> uid_max = 0; + } else { + error ("corrupt lease uid."); /* XXX */ + } + } else { + comp -> uid = (unsigned char *)0; + comp -> uid_max = 0; + } + comp -> uid_len = lease -> uid_len; + comp -> host = lease -> host; + comp -> hardware_addr = lease -> hardware_addr; + comp -> flags = ((lease -> flags & ~PERSISTENT_FLAGS) | + (comp -> flags & ~EPHEMERAL_FLAGS)); + + /* Record the lease in the uid hash if necessary. */ + if (enter_uid && lease -> uid) { + uid_hash_add (comp); + } + + /* Record it in the hardware address hash if necessary. */ + if (enter_hwaddr && lease -> hardware_addr.htype) { + hw_hash_add (comp); + } + + /* Remove the lease from its current place in the + timeout sequence. */ + if (comp -> prev) { + comp -> prev -> next = comp -> next; + } else { + comp -> shared_network -> leases = comp -> next; + } + if (comp -> next) { + comp -> next -> prev = comp -> prev; + } + if (comp -> shared_network -> last_lease == comp) { + comp -> shared_network -> last_lease = comp -> prev; + } + + /* Find the last insertion point... */ + if (comp == comp -> shared_network -> insertion_point || + !comp -> shared_network -> insertion_point) { + lp = comp -> shared_network -> leases; + } else { + lp = comp -> shared_network -> insertion_point; + } + + if (!lp) { + /* Nothing on the list yet? Just make comp the + head of the list. */ + comp -> shared_network -> leases = comp; + comp -> shared_network -> last_lease = comp; + } else if (lp -> ends > lease -> ends) { + /* Skip down the list until we run out of list + or find a place for comp. */ + while (lp -> next && lp -> ends > lease -> ends) { + lp = lp -> next; + } + if (lp -> ends > lease -> ends) { + /* If we ran out of list, put comp + at the end. */ + lp -> next = comp; + comp -> prev = lp; + comp -> next = (struct lease *)0; + comp -> shared_network -> last_lease = comp; + } else { + /* If we didn't, put it between lp and + the previous item on the list. */ + if ((comp -> prev = lp -> prev)) + comp -> prev -> next = comp; + comp -> next = lp; + lp -> prev = comp; + } + } else { + /* Skip up the list until we run out of list + or find a place for comp. */ + while (lp -> prev && lp -> ends < lease -> ends) { + lp = lp -> prev; + } + if (lp -> ends < lease -> ends) { + /* If we ran out of list, put comp + at the beginning. */ + lp -> prev = comp; + comp -> next = lp; + comp -> prev = (struct lease *)0; + comp -> shared_network -> leases = comp; + } else { + /* If we didn't, put it between lp and + the next item on the list. */ + if ((comp -> next = lp -> next)) + comp -> next -> prev = comp; + comp -> prev = lp; + lp -> next = comp; + } + } + comp -> shared_network -> insertion_point = comp; + comp -> ends = lease -> ends; + } + + /* Return zero if we didn't commit the lease to permanent storage; + nonzero if we did. */ + return commit && write_lease (comp) && commit_leases (); +} + +/* Release the specified lease and re-hash it as appropriate. */ + +void release_lease (struct _lease *lease) +{ + struct _lease lt; + + lt = *lease; + if (lt.ends > cur_time) { + lt.ends = cur_time; + supersede_lease (lease, <, 1); + } +} + +/* Abandon the specified lease (set its timeout to infinity and its + particulars to zero, and re-hash it as appropriate. */ + +void abandon_lease (struct _lease *lease, char *message) +{ + struct _lease lt; + + lease -> flags |= ABANDONED_LEASE; + lt = *lease; + lt.ends = cur_time; + warn ("Abandoning IP address %s: %s", + piaddr (lease -> ip_addr), message); + lt.hardware_addr.htype = 0; + lt.hardware_addr.hlen = 0; + lt.uid = (unsigned char *)0; + lt.uid_len = 0; + supersede_lease (lease, <, 1); +} + +/* Locate the lease associated with a given IP address... */ + +lease *find_lease_by_ip_addr (iaddr addr) +{ + lease *lease = (struct _lease *)hash_lookup (lease_ip_addr_hash, + addr.iabuf, + addr.len); + return lease; +} + +lease *find_lease_by_uid (unsigned char *uid, int len) +{ + lease *lease = (struct lease *)hash_lookup (lease_uid_hash, + uid, len); + return lease; +} + +lease *find_lease_by_hw_addr (unsigned char *hwaddr, int hwlen) +{ + struct _lease *lease = + (struct _lease *)hash_lookup (lease_hw_addr_hash, + hwaddr, hwlen); + return lease; +} + +/* Add the specified lease to the uid hash. */ + +void uid_hash_add (lease *lease) +{ + struct _lease *head = find_lease_by_uid (lease -> uid, lease -> uid_len); + struct _lease *scan; + +#ifdef DEBUG + if (lease -> n_uid) + abort (); +#endif + + /* If it's not in the hash, just add it. */ + if (!head) + add_hash (lease_uid_hash, lease -> uid, + lease -> uid_len, (unsigned char *)lease); + else { + /* Otherwise, attach it to the end of the list. */ + for (scan = head; scan -> n_uid; scan = scan -> n_uid) +#ifdef DEBUG + if (scan == lease) + abort () +#endif + ; + scan -> n_uid = lease; + } +} + +/* Delete the specified lease from the uid hash. */ + +void uid_hash_delete (lease *lease) +{ + struct _lease *head = + find_lease_by_uid (lease -> uid, lease -> uid_len); + struct _lease *scan; + + /* If it's not in the hash, we have no work to do. */ + if (!head) { + lease -> n_uid = (struct lease *)0; + return; + } + + /* If the lease we're freeing is at the head of the list, + remove the hash table entry and add a new one with the + next lease on the list (if there is one). */ + if (head == lease) { + delete_hash_entry (lease_uid_hash, + lease -> uid, lease -> uid_len); + if (lease -> n_uid) + add_hash (lease_uid_hash, + lease -> n_uid -> uid, + lease -> n_uid -> uid_len, + (unsigned char *)(lease -> n_uid)); + } else { + /* Otherwise, look for the lease in the list of leases + attached to the hash table entry, and remove it if + we find it. */ + for (scan = head; scan -> n_uid; scan = scan -> n_uid) { + if (scan -> n_uid == lease) { + scan -> n_uid = scan -> n_uid -> n_uid; + break; + } + } + } + lease -> n_uid = (struct lease *)0; +} + +/* Add the specified lease to the hardware address hash. */ + +void hw_hash_add (lease *lease) +{ + struct _lease *head = + find_lease_by_hw_addr (lease -> hardware_addr.haddr, + lease -> hardware_addr.hlen); + struct _lease *scan; + + /* If it's not in the hash, just add it. */ + if (!head) + add_hash (lease_hw_addr_hash, + lease -> hardware_addr.haddr, + lease -> hardware_addr.hlen, + (unsigned char *)lease); + else { + /* Otherwise, attach it to the end of the list. */ + for (scan = head; scan -> n_hw; scan = scan -> n_hw) + ; + scan -> n_hw = lease; + } +} + +/* Delete the specified lease from the hardware address hash. */ + +void hw_hash_delete (lease *lease) +{ + struct _lease *head = + find_lease_by_hw_addr (lease -> hardware_addr.haddr, + lease -> hardware_addr.hlen); + struct _lease *scan; + + /* If it's not in the hash, we have no work to do. */ + if (!head) { + lease -> n_hw = (struct lease *)0; + return; + } + + /* If the lease we're freeing is at the head of the list, + remove the hash table entry and add a new one with the + next lease on the list (if there is one). */ + if (head == lease) { + delete_hash_entry (lease_hw_addr_hash, + lease -> hardware_addr.haddr, + lease -> hardware_addr.hlen); + if (lease -> n_hw) + add_hash (lease_hw_addr_hash, + lease -> n_hw -> hardware_addr.haddr, + lease -> n_hw -> hardware_addr.hlen, + (unsigned char *)(lease -> n_hw)); + } else { + /* Otherwise, look for the lease in the list of leases + attached to the hash table entry, and remove it if + we find it. */ + for (scan = head; scan -> n_hw; scan = scan -> n_hw) { + if (scan -> n_hw == lease) { + scan -> n_hw = scan -> n_hw -> n_hw; + break; + } + } + } + lease -> n_hw = (struct lease *)0; +} + + +struct class *add_class (type, name) + int type; + char *name; +{ + struct class *class = new_class ("add_class"); + char *tname = (char *)malloc (strlen (name) + 1); + + if (!vendor_class_hash) + vendor_class_hash = new_hash (); + if (!user_class_hash) + user_class_hash = new_hash (); + + if (!tname || !class || !vendor_class_hash || !user_class_hash) + return (struct class *)0; + + memset (class, 0, sizeof *class); + strcpy (tname, name); + class -> name = tname; + + if (type) + add_hash (user_class_hash, + (unsigned char *)tname, strlen (tname), + (unsigned char *)class); + else + add_hash (vendor_class_hash, + (unsigned char *)tname, strlen (tname), + (unsigned char *)class); + return class; +} + +struct class *find_class (type, name, len) + int type; + unsigned char *name; + int len; +{ + struct class *class = + (struct class *)hash_lookup (type + ? user_class_hash + : vendor_class_hash, name, len); + return class; +} + +struct group *clone_group (group, caller) + struct group *group; + char *caller; +{ + struct group *g = new_group (caller); + if (!g) + error ("%s: can't allocate new group", caller); + *g = *group; + return g; +} + +/* Write all interesting leases to permanent storage. */ + +void write_leases () +{ + lease *l; + shared_network *s; + + for (s = shared_networks; s; s = (shared_network *)s -> next) { + for (l = s -> leases; l; l = l -> next) { + if (l -> hardware_addr.hlen || + l -> uid_len || + (l -> flags & ABANDONED_LEASE)) + if (!write_lease (l)) + error ("Can't rewrite lease database"); + } + } + if (!commit_leases ()) + error ("Can't commit leases to new database: %m"); +} + +void dump_subnets () +{ + struct _lease *l; + shared_network *s; + subnet *n; + + note ("Subnets:"); + for (n = subnets; n; n = n -> next_subnet) { + debug (" Subnet %s", piaddr (n -> net)); + debug (" netmask %s", + piaddr (n -> netmask)); + } + note ("Shared networks:"); + for (s = shared_networks; s; s = (shared_network *)s -> next) { + note (" %s", s -> name); + for (l = s -> leases; l; l = l -> next) { + print_lease (l); + } + if (s -> last_lease) { + debug (" Last Lease:"); + print_lease (s -> last_lease); + } + } +} diff --git a/reactos/subsys/system/dhcp/options.c b/reactos/subsys/system/dhcp/options.c new file mode 100644 index 00000000000..a037712982f --- /dev/null +++ b/reactos/subsys/system/dhcp/options.c @@ -0,0 +1,718 @@ +/* $OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $ */ + +/* DHCP options parsing and reassembly. */ + +/* + * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#include + +#define DHCP_OPTION_DATA +#include "rosdhcp.h" +#include "dhcpd.h" + +int bad_options = 0; +int bad_options_max = 5; + +void parse_options(struct packet *); +void parse_option_buffer(struct packet *, unsigned char *, int); +int store_options(unsigned char *, int, struct tree_cache **, + unsigned char *, int, int, int, int); + + +/* + * Parse all available options out of the specified packet. + */ +void +parse_options(struct packet *packet) +{ + /* Initially, zero all option pointers. */ + memset(packet->options, 0, sizeof(packet->options)); + + /* If we don't see the magic cookie, there's nothing to parse. */ + if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) { + packet->options_valid = 0; + return; + } + + /* + * Go through the options field, up to the end of the packet or + * the End field. + */ + parse_option_buffer(packet, &packet->raw->options[4], + packet->packet_length - DHCP_FIXED_NON_UDP - 4); + + /* + * If we parsed a DHCP Option Overload option, parse more + * options out of the buffer(s) containing them. + */ + if (packet->options_valid && + packet->options[DHO_DHCP_OPTION_OVERLOAD].data) { + if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) + parse_option_buffer(packet, + (unsigned char *)packet->raw->file, + sizeof(packet->raw->file)); + if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) + parse_option_buffer(packet, + (unsigned char *)packet->raw->sname, + sizeof(packet->raw->sname)); + } +} + +/* + * Parse options out of the specified buffer, storing addresses of + * option values in packet->options and setting packet->options_valid if + * no errors are encountered. + */ +void +parse_option_buffer(struct packet *packet, + unsigned char *buffer, int length) +{ + unsigned char *s, *t, *end = buffer + length; + int len, code; + + for (s = buffer; *s != DHO_END && s < end; ) { + code = s[0]; + + /* Pad options don't have a length - just skip them. */ + if (code == DHO_PAD) { + s++; + continue; + } + if (s + 2 > end) { + len = 65536; + goto bogus; + } + + /* + * All other fields (except end, see above) have a + * one-byte length. + */ + len = s[1]; + + /* + * If the length is outrageous, silently skip the rest, + * and mark the packet bad. Unfortunately some crappy + * dhcp servers always seem to give us garbage on the + * end of a packet. so rather than keep refusing, give + * up and try to take one after seeing a few without + * anything good. + */ + if (s + len + 2 > end) { + bogus: + bad_options++; + warning("option %s (%d) %s.", + dhcp_options[code].name, len, + "larger than buffer"); + if (bad_options == bad_options_max) { + packet->options_valid = 1; + bad_options = 0; + warning("Many bogus options seen in offers. " + "Taking this offer in spite of bogus " + "options - hope for the best!"); + } else { + warning("rejecting bogus offer."); + packet->options_valid = 0; + } + return; + } + /* + * If we haven't seen this option before, just make + * space for it and copy it there. + */ + if (!packet->options[code].data) { + if (!(t = calloc(1, len + 1))) + error("Can't allocate storage for option %s.", + dhcp_options[code].name); + /* + * Copy and NUL-terminate the option (in case + * it's an ASCII string. + */ + memcpy(t, &s[2], len); + t[len] = 0; + packet->options[code].len = len; + packet->options[code].data = t; + } else { + /* + * If it's a repeat, concatenate it to whatever + * we last saw. This is really only required + * for clients, but what the heck... + */ + t = calloc(1, len + packet->options[code].len + 1); + if (!t) + error("Can't expand storage for option %s.", + dhcp_options[code].name); + memcpy(t, packet->options[code].data, + packet->options[code].len); + memcpy(t + packet->options[code].len, + &s[2], len); + packet->options[code].len += len; + t[packet->options[code].len] = 0; + free(packet->options[code].data); + packet->options[code].data = t; + } + s += len + 2; + } + packet->options_valid = 1; +} + +/* + * cons options into a big buffer, and then split them out into the + * three separate buffers if needed. This allows us to cons up a set of + * vendor options using the same routine. + */ +int +cons_options(struct packet *inpacket, struct dhcp_packet *outpacket, + int mms, struct tree_cache **options, + int overload, /* Overload flags that may be set. */ + int terminate, int bootpp, u_int8_t *prl, int prl_len) +{ + unsigned char priority_list[300], buffer[4096]; + int priority_len, main_buffer_size, mainbufix, bufix; + int option_size, length; + + /* + * If the client has provided a maximum DHCP message size, use + * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use + * up to the minimum IP MTU size (576 bytes). + * + * XXX if a BOOTP client specifies a max message size, we will + * honor it. + */ + if (!mms && + inpacket && + inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data && + (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >= + sizeof(u_int16_t))) + mms = getUShort( + inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data); + + if (mms) + main_buffer_size = mms - DHCP_FIXED_LEN; + else if (bootpp) + main_buffer_size = 64; + else + main_buffer_size = 576 - DHCP_FIXED_LEN; + + if (main_buffer_size > sizeof(buffer)) + main_buffer_size = sizeof(buffer); + + /* Preload the option priority list with mandatory options. */ + priority_len = 0; + priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE; + priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER; + priority_list[priority_len++] = DHO_DHCP_LEASE_TIME; + priority_list[priority_len++] = DHO_DHCP_MESSAGE; + + /* + * If the client has provided a list of options that it wishes + * returned, use it to prioritize. Otherwise, prioritize based + * on the default priority list. + */ + if (inpacket && + inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) { + int prlen = + inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len; + if (prlen + priority_len > sizeof(priority_list)) + prlen = sizeof(priority_list) - priority_len; + + memcpy(&priority_list[priority_len], + inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data, + prlen); + priority_len += prlen; + prl = priority_list; + } else if (prl) { + if (prl_len + priority_len > sizeof(priority_list)) + prl_len = sizeof(priority_list) - priority_len; + + memcpy(&priority_list[priority_len], prl, prl_len); + priority_len += prl_len; + prl = priority_list; + } else { + memcpy(&priority_list[priority_len], + dhcp_option_default_priority_list, + sizeof_dhcp_option_default_priority_list); + priority_len += sizeof_dhcp_option_default_priority_list; + } + + /* Copy the options into the big buffer... */ + option_size = store_options( + buffer, + (main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) + + ((overload & 2) ? DHCP_SNAME_LEN : 0)), + options, priority_list, priority_len, main_buffer_size, + (main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)), + terminate); + + /* Put the cookie up front... */ + memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4); + mainbufix = 4; + + /* + * If we're going to have to overload, store the overload option + * at the beginning. If we can, though, just store the whole + * thing in the packet's option buffer and leave it at that. + */ + if (option_size <= main_buffer_size - mainbufix) { + memcpy(&outpacket->options[mainbufix], + buffer, option_size); + mainbufix += option_size; + if (mainbufix < main_buffer_size) + outpacket->options[mainbufix++] = DHO_END; + length = DHCP_FIXED_NON_UDP + mainbufix; + } else { + outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD; + outpacket->options[mainbufix++] = 1; + if (option_size > + main_buffer_size - mainbufix + DHCP_FILE_LEN) + outpacket->options[mainbufix++] = 3; + else + outpacket->options[mainbufix++] = 1; + + memcpy(&outpacket->options[mainbufix], + buffer, main_buffer_size - mainbufix); + bufix = main_buffer_size - mainbufix; + length = DHCP_FIXED_NON_UDP + mainbufix; + if (overload & 1) { + if (option_size - bufix <= DHCP_FILE_LEN) { + memcpy(outpacket->file, + &buffer[bufix], option_size - bufix); + mainbufix = option_size - bufix; + if (mainbufix < DHCP_FILE_LEN) + outpacket->file[mainbufix++] = (char)DHO_END; + while (mainbufix < DHCP_FILE_LEN) + outpacket->file[mainbufix++] = (char)DHO_PAD; + } else { + memcpy(outpacket->file, + &buffer[bufix], DHCP_FILE_LEN); + bufix += DHCP_FILE_LEN; + } + } + if ((overload & 2) && option_size < bufix) { + memcpy(outpacket->sname, + &buffer[bufix], option_size - bufix); + + mainbufix = option_size - bufix; + if (mainbufix < DHCP_SNAME_LEN) + outpacket->file[mainbufix++] = (char)DHO_END; + while (mainbufix < DHCP_SNAME_LEN) + outpacket->file[mainbufix++] = (char)DHO_PAD; + } + } + return (length); +} + +/* + * Store all the requested options into the requested buffer. + */ +int +store_options(unsigned char *buffer, int buflen, struct tree_cache **options, + unsigned char *priority_list, int priority_len, int first_cutoff, + int second_cutoff, int terminate) +{ + int bufix = 0, option_stored[256], i, ix, tto; + + /* Zero out the stored-lengths array. */ + memset(option_stored, 0, sizeof(option_stored)); + + /* + * Copy out the options in the order that they appear in the + * priority list... + */ + for (i = 0; i < priority_len; i++) { + /* Code for next option to try to store. */ + int code = priority_list[i]; + int optstart; + + /* + * Number of bytes left to store (some may already have + * been stored by a previous pass). + */ + int length; + + /* If no data is available for this option, skip it. */ + if (!options[code]) { + continue; + } + + /* + * The client could ask for things that are mandatory, + * in which case we should avoid storing them twice... + */ + if (option_stored[code]) + continue; + option_stored[code] = 1; + + /* We should now have a constant length for the option. */ + length = options[code]->len; + + /* Do we add a NUL? */ + if (terminate && dhcp_options[code].format[0] == 't') { + length++; + tto = 1; + } else + tto = 0; + + /* Try to store the option. */ + + /* + * If the option's length is more than 255, we must + * store it in multiple hunks. Store 255-byte hunks + * first. However, in any case, if the option data will + * cross a buffer boundary, split it across that + * boundary. + */ + ix = 0; + + optstart = bufix; + while (length) { + unsigned char incr = length > 255 ? 255 : length; + + /* + * If this hunk of the buffer will cross a + * boundary, only go up to the boundary in this + * pass. + */ + if (bufix < first_cutoff && + bufix + incr > first_cutoff) + incr = first_cutoff - bufix; + else if (bufix < second_cutoff && + bufix + incr > second_cutoff) + incr = second_cutoff - bufix; + + /* + * If this option is going to overflow the + * buffer, skip it. + */ + if (bufix + 2 + incr > buflen) { + bufix = optstart; + break; + } + + /* Everything looks good - copy it in! */ + buffer[bufix] = code; + buffer[bufix + 1] = incr; + if (tto && incr == length) { + memcpy(buffer + bufix + 2, + options[code]->value + ix, incr - 1); + buffer[bufix + 2 + incr - 1] = 0; + } else + memcpy(buffer + bufix + 2, + options[code]->value + ix, incr); + length -= incr; + ix += incr; + bufix += 2 + incr; + } + } + return (bufix); +} + +/* + * Format the specified option so that a human can easily read it. + */ +char * +pretty_print_option(unsigned int code, unsigned char *data, int len, + int emit_commas, int emit_quotes) +{ + static char optbuf[32768]; /* XXX */ + int hunksize = 0, numhunk = -1, numelem = 0; + char fmtbuf[32], *op = optbuf; + int i, j, k, opleft = sizeof(optbuf); + unsigned char *dp = data; + struct in_addr foo; + char comma; + + /* Code should be between 0 and 255. */ + if (code > 255) + error("pretty_print_option: bad code %d", code); + + if (emit_commas) + comma = ','; + else + comma = ' '; + + /* Figure out the size of the data. */ + for (i = 0; dhcp_options[code].format[i]; i++) { + if (!numhunk) { + warning("%s: Excess information in format string: %s", + dhcp_options[code].name, + &(dhcp_options[code].format[i])); + break; + } + numelem++; + fmtbuf[i] = dhcp_options[code].format[i]; + switch (dhcp_options[code].format[i]) { + case 'A': + --numelem; + fmtbuf[i] = 0; + numhunk = 0; + break; + case 'X': + for (k = 0; k < len; k++) + if (!isascii(data[k]) || + !isprint(data[k])) + break; + if (k == len) { + fmtbuf[i] = 't'; + numhunk = -2; + } else { + fmtbuf[i] = 'x'; + hunksize++; + comma = ':'; + numhunk = 0; + } + fmtbuf[i + 1] = 0; + break; + case 't': + fmtbuf[i] = 't'; + fmtbuf[i + 1] = 0; + numhunk = -2; + break; + case 'I': + case 'l': + case 'L': + hunksize += 4; + break; + case 's': + case 'S': + hunksize += 2; + break; + case 'b': + case 'B': + case 'f': + hunksize++; + break; + case 'e': + break; + default: + warning("%s: garbage in format string: %s", + dhcp_options[code].name, + &(dhcp_options[code].format[i])); + break; + } + } + + /* Check for too few bytes... */ + if (hunksize > len) { + warning("%s: expecting at least %d bytes; got %d", + dhcp_options[code].name, hunksize, len); + return (""); + } + /* Check for too many bytes... */ + if (numhunk == -1 && hunksize < len) + warning("%s: %d extra bytes", + dhcp_options[code].name, len - hunksize); + + /* If this is an array, compute its size. */ + if (!numhunk) + numhunk = len / hunksize; + /* See if we got an exact number of hunks. */ + if (numhunk > 0 && numhunk * hunksize < len) + warning("%s: %d extra bytes at end of array", + dhcp_options[code].name, len - numhunk * hunksize); + + /* A one-hunk array prints the same as a single hunk. */ + if (numhunk < 0) + numhunk = 1; + + /* Cycle through the array (or hunk) printing the data. */ + for (i = 0; i < numhunk; i++) { + for (j = 0; j < numelem; j++) { + int opcount; + switch (fmtbuf[j]) { + case 't': + if (emit_quotes) { + *op++ = '"'; + opleft--; + } + for (; dp < data + len; dp++) { + if (!isascii(*dp) || + !isprint(*dp)) { + if (dp + 1 != data + len || + *dp != 0) { + snprintf(op, opleft, + "\\%03o", *dp); + op += 4; + opleft -= 4; + } + } else if (*dp == '"' || + *dp == '\'' || + *dp == '$' || + *dp == '`' || + *dp == '\\') { + *op++ = '\\'; + *op++ = *dp; + opleft -= 2; + } else { + *op++ = *dp; + opleft--; + } + } + if (emit_quotes) { + *op++ = '"'; + opleft--; + } + + *op = 0; + break; + case 'I': + foo.s_addr = htonl(getULong(dp)); + opcount = strlcpy(op, inet_ntoa(foo), opleft); + if (opcount >= opleft) + goto toobig; + opleft -= opcount; + dp += 4; + break; + case 'l': + opcount = snprintf(op, opleft, "%ld", + (long)getLong(dp)); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + dp += 4; + break; + case 'L': + opcount = snprintf(op, opleft, "%ld", + (unsigned long)getULong(dp)); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + dp += 4; + break; + case 's': + opcount = snprintf(op, opleft, "%d", + getShort(dp)); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + dp += 2; + break; + case 'S': + opcount = snprintf(op, opleft, "%d", + getUShort(dp)); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + dp += 2; + break; + case 'b': + opcount = snprintf(op, opleft, "%d", + *(char *)dp++); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + break; + case 'B': + opcount = snprintf(op, opleft, "%d", *dp++); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + break; + case 'x': + opcount = snprintf(op, opleft, "%x", *dp++); + if (opcount >= opleft || opcount == -1) + goto toobig; + opleft -= opcount; + break; + case 'f': + opcount = strlcpy(op, + *dp++ ? "true" : "false", opleft); + if (opcount >= opleft) + goto toobig; + opleft -= opcount; + break; + default: + warning("Unexpected format code %c", fmtbuf[j]); + } + op += strlen(op); + opleft -= strlen(op); + if (opleft < 1) + goto toobig; + if (j + 1 < numelem && comma != ':') { + *op++ = ' '; + opleft--; + } + } + if (i + 1 < numhunk) { + *op++ = comma; + opleft--; + } + if (opleft < 1) + goto toobig; + + } + return (optbuf); + toobig: + warning("dhcp option too large"); + return (""); +} + +void +do_packet(struct interface_info *interface, struct dhcp_packet *packet, + int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom) +{ + struct packet tp; + int i; + + if (packet->hlen > sizeof(packet->chaddr)) { + note("Discarding packet with invalid hlen."); + return; + } + + memset(&tp, 0, sizeof(tp)); + tp.raw = packet; + tp.packet_length = len; + tp.client_port = from_port; + tp.client_addr = from; + tp.interface = interface; + tp.haddr = hfrom; + + parse_options(&tp); + if (tp.options_valid && + tp.options[DHO_DHCP_MESSAGE_TYPE].data) + tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0]; + if (tp.packet_type) + dhcp(&tp); + else + bootp(&tp); + + /* Free the data associated with the options. */ + for (i = 0; i < 256; i++) + if (tp.options[i].len && tp.options[i].data) + free(tp.options[i].data); +} diff --git a/reactos/subsys/system/dhcp/privsep.c b/reactos/subsys/system/dhcp/privsep.c new file mode 100644 index 00000000000..9f36a942d4d --- /dev/null +++ b/reactos/subsys/system/dhcp/privsep.c @@ -0,0 +1,237 @@ +/* $OpenBSD: privsep.c,v 1.7 2004/05/10 18:34:42 deraadt Exp $ */ + +/* + * Copyright (c) 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "rosdhcp.h" +#include "dhcpd.h" +#include "privsep.h" + +struct buf * +buf_open(size_t len) +{ + struct buf *buf; + + if ((buf = calloc(1, sizeof(struct buf))) == NULL) + return (NULL); + if ((buf->buf = malloc(len)) == NULL) { + free(buf); + return (NULL); + } + buf->size = len; + + return (buf); +} + +int +buf_add(struct buf *buf, void *data, size_t len) +{ + if (buf->wpos + len > buf->size) + return (-1); + + memcpy(buf->buf + buf->wpos, data, len); + buf->wpos += len; + return (0); +} + +int +buf_close(int sock, struct buf *buf) +{ + ssize_t n; + + do { + n = write(sock, buf->buf + buf->rpos, buf->size - buf->rpos); + if (n != -1) + buf->rpos += n; + if (n == 0) { /* connection closed */ + errno = 0; + return (-1); + } + } while (n == -1 && (errno == EAGAIN || errno == EINTR)); + + if (buf->rpos < buf->size) + error("short write: wanted %lu got %ld bytes", + (unsigned long)buf->size, (long)buf->rpos); + + free(buf->buf); + free(buf); + return (n); +} + +ssize_t +buf_read(int sock, void *buf, size_t nbytes) +{ + ssize_t n, r = 0; + char *p = buf; + + do { + n = read(sock, p, nbytes); + if (n == 0) + error("connection closed"); + if (n != -1) { + r += n; + p += n; + nbytes -= n; + } + } while (n == -1 && (errno == EINTR || errno == EAGAIN)); + + if (n == -1) + error("buf_read: %m"); + + if (r < nbytes) + error("short read: wanted %lu got %ld bytes", + (unsigned long)nbytes, (long)r); + + return (r); +} + +void +dispatch_imsg(int fd) +{ + struct imsg_hdr hdr; + char *medium, *reason, *filename, + *servername, *prefix; + size_t medium_len, reason_len, filename_len, + servername_len, prefix_len, totlen; + struct client_lease lease; + int ret, i, optlen; + struct buf *buf; + + buf_read(fd, &hdr, sizeof(hdr)); + + switch (hdr.code) { + case IMSG_SCRIPT_INIT: + if (hdr.len < sizeof(hdr) + sizeof(size_t)) + error("corrupted message received"); + buf_read(fd, &medium_len, sizeof(medium_len)); + if (hdr.len < medium_len + sizeof(size_t) + sizeof(hdr) + + sizeof(size_t) || medium_len == SIZE_T_MAX) + error("corrupted message received"); + if (medium_len > 0) { + if ((medium = calloc(1, medium_len + 1)) == NULL) + error("%m"); + buf_read(fd, medium, medium_len); + } else + medium = NULL; + + buf_read(fd, &reason_len, sizeof(reason_len)); + if (hdr.len < medium_len + reason_len + sizeof(hdr) || + reason_len == SIZE_T_MAX) + error("corrupted message received"); + if (reason_len > 0) { + if ((reason = calloc(1, reason_len + 1)) == NULL) + error("%m"); + buf_read(fd, reason, reason_len); + } else + reason = NULL; + +// priv_script_init(reason, medium); + free(reason); + free(medium); + break; + case IMSG_SCRIPT_WRITE_PARAMS: + //bzero(&lease, sizeof lease); + memset(&lease, 0, sizeof(lease)); + totlen = sizeof(hdr) + sizeof(lease) + sizeof(size_t); + if (hdr.len < totlen) + error("corrupted message received"); + buf_read(fd, &lease, sizeof(lease)); + + buf_read(fd, &filename_len, sizeof(filename_len)); + totlen += filename_len + sizeof(size_t); + if (hdr.len < totlen || filename_len == SIZE_T_MAX) + error("corrupted message received"); + if (filename_len > 0) { + if ((filename = calloc(1, filename_len + 1)) == NULL) + error("%m"); + buf_read(fd, filename, filename_len); + } else + filename = NULL; + + buf_read(fd, &servername_len, sizeof(servername_len)); + totlen += servername_len + sizeof(size_t); + if (hdr.len < totlen || servername_len == SIZE_T_MAX) + error("corrupted message received"); + if (servername_len > 0) { + if ((servername = + calloc(1, servername_len + 1)) == NULL) + error("%m"); + buf_read(fd, servername, servername_len); + } else + servername = NULL; + + buf_read(fd, &prefix_len, sizeof(prefix_len)); + totlen += prefix_len; + if (hdr.len < totlen || prefix_len == SIZE_T_MAX) + error("corrupted message received"); + if (prefix_len > 0) { + if ((prefix = calloc(1, prefix_len + 1)) == NULL) + error("%m"); + buf_read(fd, prefix, prefix_len); + } else + prefix = NULL; + + for (i = 0; i < 256; i++) { + totlen += sizeof(optlen); + if (hdr.len < totlen) + error("corrupted message received"); + buf_read(fd, &optlen, sizeof(optlen)); + lease.options[i].data = NULL; + lease.options[i].len = optlen; + if (optlen > 0) { + totlen += optlen; + if (hdr.len < totlen || optlen == SIZE_T_MAX) + error("corrupted message received"); + lease.options[i].data = + calloc(1, optlen + 1); + if (lease.options[i].data == NULL) + error("%m"); + buf_read(fd, lease.options[i].data, optlen); + } + } + lease.server_name = servername; + lease.filename = filename; + +// priv_script_write_params(prefix, &lease); + + free(servername); + free(filename); + free(prefix); + for (i = 0; i < 256; i++) + if (lease.options[i].len > 0) + free(lease.options[i].data); + break; + case IMSG_SCRIPT_GO: + if (hdr.len != sizeof(hdr)) + error("corrupted message received"); + +// ret = priv_script_go(); + + hdr.code = IMSG_SCRIPT_GO_RET; + hdr.len = sizeof(struct imsg_hdr) + sizeof(int); + if ((buf = buf_open(hdr.len)) == NULL) + error("buf_open: %m"); + if (buf_add(buf, &hdr, sizeof(hdr))) + error("buf_add: %m"); + if (buf_add(buf, &ret, sizeof(ret))) + error("buf_add: %m"); + if (buf_close(fd, buf) == -1) + error("buf_close: %m"); + break; + default: + error("received unknown message, code %d", hdr.code); + } +} diff --git a/reactos/subsys/system/dhcp/socket.c b/reactos/subsys/system/dhcp/socket.c new file mode 100644 index 00000000000..3c7d398bc74 --- /dev/null +++ b/reactos/subsys/system/dhcp/socket.c @@ -0,0 +1,39 @@ +#include "rosdhcp.h" + +SOCKET ServerSocket; + +void SocketInit() { + ServerSocket = socket( AF_INET, SOCK_DGRAM, 0 ); +} + +ssize_t send_packet( struct interface_info *ip, + struct dhcp_packet *p, + size_t size, + struct in_addr addr, + struct sockaddr_in *broadcast, + struct hardware *hardware ) { + int result = + sendto( ip->wfdesc, (char *)p, size, 0, + (struct sockaddr *)broadcast, sizeof(*broadcast) ); + + if (result < 0) { + note ("send_packet: %x", result); + if (result == WSAENETUNREACH) + note ("send_packet: please consult README file%s", + " regarding broadcast address."); + } + + return result; +} + +ssize_t receive_packet(struct interface_info *ip, + unsigned char *packet_data, + size_t packet_len, + struct sockaddr_in *dest, + struct hardware *hardware ) { + int recv_addr_size = sizeof(*dest); + int result = + recvfrom (ip -> rfdesc, (char *)packet_data, packet_len, 0, + (struct sockaddr *)dest, &recv_addr_size ); + return result; +} diff --git a/reactos/subsys/system/dhcp/tables.c b/reactos/subsys/system/dhcp/tables.c new file mode 100644 index 00000000000..7eb9c766b6f --- /dev/null +++ b/reactos/subsys/system/dhcp/tables.c @@ -0,0 +1,692 @@ +/* tables.c + + Tables of information... */ + +/* + * Copyright (c) 1995, 1996 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: tables.c,v 1.13.2.4 1999/04/24 16:46:44 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "rosdhcp.h" + +/* DHCP Option names, formats and codes, from RFC1533. + + Format codes: + + e - end of data + I - IP address + l - 32-bit signed integer + L - 32-bit unsigned integer + s - 16-bit signed integer + S - 16-bit unsigned integer + b - 8-bit signed integer + B - 8-bit unsigned integer + t - ASCII text + f - flag (true or false) + A - array of whatever precedes (e.g., IA means array of IP addresses) +*/ + +struct universe dhcp_universe; +struct dhcp_option dhcp_options [256] = { + { "pad", "", &dhcp_universe, 0 }, + { "subnet-mask", "I", &dhcp_universe, 1 }, + { "time-offset", "l", &dhcp_universe, 2 }, + { "routers", "IA", &dhcp_universe, 3 }, + { "time-servers", "IA", &dhcp_universe, 4 }, + { "ien116-name-servers", "IA", &dhcp_universe, 5 }, + { "domain-name-servers", "IA", &dhcp_universe, 6 }, + { "log-servers", "IA", &dhcp_universe, 7 }, + { "cookie-servers", "IA", &dhcp_universe, 8 }, + { "lpr-servers", "IA", &dhcp_universe, 9 }, + { "impress-servers", "IA", &dhcp_universe, 10 }, + { "resource-location-servers", "IA", &dhcp_universe, 11 }, + { "host-name", "X", &dhcp_universe, 12 }, + { "boot-size", "S", &dhcp_universe, 13 }, + { "merit-dump", "t", &dhcp_universe, 14 }, + { "domain-name", "t", &dhcp_universe, 15 }, + { "swap-server", "I", &dhcp_universe, 16 }, + { "root-path", "t", &dhcp_universe, 17 }, + { "extensions-path", "t", &dhcp_universe, 18 }, + { "ip-forwarding", "f", &dhcp_universe, 19 }, + { "non-local-source-routing", "f", &dhcp_universe, 20 }, + { "policy-filter", "IIA", &dhcp_universe, 21 }, + { "max-dgram-reassembly", "S", &dhcp_universe, 22 }, + { "default-ip-ttl", "B", &dhcp_universe, 23 }, + { "path-mtu-aging-timeout", "L", &dhcp_universe, 24 }, + { "path-mtu-plateau-table", "SA", &dhcp_universe, 25 }, + { "interface-mtu", "S", &dhcp_universe, 26 }, + { "all-subnets-local", "f", &dhcp_universe, 27 }, + { "broadcast-address", "I", &dhcp_universe, 28 }, + { "perform-mask-discovery", "f", &dhcp_universe, 29 }, + { "mask-supplier", "f", &dhcp_universe, 30 }, + { "router-discovery", "f", &dhcp_universe, 31 }, + { "router-solicitation-address", "I", &dhcp_universe, 32 }, + { "static-routes", "IIA", &dhcp_universe, 33 }, + { "trailer-encapsulation", "f", &dhcp_universe, 34 }, + { "arp-cache-timeout", "L", &dhcp_universe, 35 }, + { "ieee802-3-encapsulation", "f", &dhcp_universe, 36 }, + { "default-tcp-ttl", "B", &dhcp_universe, 37 }, + { "tcp-keepalive-interval", "L", &dhcp_universe, 38 }, + { "tcp-keepalive-garbage", "f", &dhcp_universe, 39 }, + { "nis-domain", "t", &dhcp_universe, 40 }, + { "nis-servers", "IA", &dhcp_universe, 41 }, + { "ntp-servers", "IA", &dhcp_universe, 42 }, + { "vendor-encapsulated-options", "X", &dhcp_universe, 43 }, + { "netbios-name-servers", "IA", &dhcp_universe, 44 }, + { "netbios-dd-server", "IA", &dhcp_universe, 45 }, + { "netbios-node-type", "B", &dhcp_universe, 46 }, + { "netbios-scope", "t", &dhcp_universe, 47 }, + { "font-servers", "IA", &dhcp_universe, 48 }, + { "x-display-manager", "IA", &dhcp_universe, 49 }, + { "dhcp-requested-address", "I", &dhcp_universe, 50 }, + { "dhcp-lease-time", "L", &dhcp_universe, 51 }, + { "dhcp-option-overload", "B", &dhcp_universe, 52 }, + { "dhcp-message-type", "B", &dhcp_universe, 53 }, + { "dhcp-server-identifier", "I", &dhcp_universe, 54 }, + { "dhcp-parameter-request-list", "BA", &dhcp_universe, 55 }, + { "dhcp-message", "t", &dhcp_universe, 56 }, + { "dhcp-max-message-size", "S", &dhcp_universe, 57 }, + { "dhcp-renewal-time", "L", &dhcp_universe, 58 }, + { "dhcp-rebinding-time", "L", &dhcp_universe, 59 }, + { "dhcp-class-identifier", "t", &dhcp_universe, 60 }, + { "dhcp-client-identifier", "X", &dhcp_universe, 61 }, + { "option-62", "X", &dhcp_universe, 62 }, + { "option-63", "X", &dhcp_universe, 63 }, + { "nisplus-domain", "t", &dhcp_universe, 64 }, + { "nisplus-servers", "IA", &dhcp_universe, 65 }, + { "tftp-server-name", "t", &dhcp_universe, 66 }, + { "bootfile-name", "t", &dhcp_universe, 67 }, + { "mobile-ip-home-agent", "IA", &dhcp_universe, 68 }, + { "smtp-server", "IA", &dhcp_universe, 69 }, + { "pop-server", "IA", &dhcp_universe, 70 }, + { "nntp-server", "IA", &dhcp_universe, 71 }, + { "www-server", "IA", &dhcp_universe, 72 }, + { "finger-server", "IA", &dhcp_universe, 73 }, + { "irc-server", "IA", &dhcp_universe, 74 }, + { "streettalk-server", "IA", &dhcp_universe, 75 }, + { "streettalk-directory-assistance-server", "IA", &dhcp_universe, 76 }, + { "user-class", "t", &dhcp_universe, 77 }, + { "option-78", "X", &dhcp_universe, 78 }, + { "option-79", "X", &dhcp_universe, 79 }, + { "option-80", "X", &dhcp_universe, 80 }, + { "option-81", "X", &dhcp_universe, 81 }, + { "option-82", "X", &dhcp_universe, 82 }, + { "option-83", "X", &dhcp_universe, 83 }, + { "option-84", "X", &dhcp_universe, 84 }, + { "nds-servers", "IA", &dhcp_universe, 85 }, + { "nds-tree-name", "X", &dhcp_universe, 86 }, + { "nds-context", "X", &dhcp_universe, 87 }, + { "option-88", "X", &dhcp_universe, 88 }, + { "option-89", "X", &dhcp_universe, 89 }, + { "option-90", "X", &dhcp_universe, 90 }, + { "option-91", "X", &dhcp_universe, 91 }, + { "option-92", "X", &dhcp_universe, 92 }, + { "option-93", "X", &dhcp_universe, 93 }, + { "option-94", "X", &dhcp_universe, 94 }, + { "option-95", "X", &dhcp_universe, 95 }, + { "option-96", "X", &dhcp_universe, 96 }, + { "option-97", "X", &dhcp_universe, 97 }, + { "option-98", "X", &dhcp_universe, 98 }, + { "option-99", "X", &dhcp_universe, 99 }, + { "option-100", "X", &dhcp_universe, 100 }, + { "option-101", "X", &dhcp_universe, 101 }, + { "option-102", "X", &dhcp_universe, 102 }, + { "option-103", "X", &dhcp_universe, 103 }, + { "option-104", "X", &dhcp_universe, 104 }, + { "option-105", "X", &dhcp_universe, 105 }, + { "option-106", "X", &dhcp_universe, 106 }, + { "option-107", "X", &dhcp_universe, 107 }, + { "option-108", "X", &dhcp_universe, 108 }, + { "option-109", "X", &dhcp_universe, 109 }, + { "option-110", "X", &dhcp_universe, 110 }, + { "option-111", "X", &dhcp_universe, 111 }, + { "option-112", "X", &dhcp_universe, 112 }, + { "option-113", "X", &dhcp_universe, 113 }, + { "option-114", "X", &dhcp_universe, 114 }, + { "option-115", "X", &dhcp_universe, 115 }, + { "option-116", "X", &dhcp_universe, 116 }, + { "option-117", "X", &dhcp_universe, 117 }, + { "option-118", "X", &dhcp_universe, 118 }, + { "option-119", "X", &dhcp_universe, 119 }, + { "option-120", "X", &dhcp_universe, 120 }, + { "option-121", "X", &dhcp_universe, 121 }, + { "option-122", "X", &dhcp_universe, 122 }, + { "option-123", "X", &dhcp_universe, 123 }, + { "option-124", "X", &dhcp_universe, 124 }, + { "option-125", "X", &dhcp_universe, 125 }, + { "option-126", "X", &dhcp_universe, 126 }, + { "option-127", "X", &dhcp_universe, 127 }, + { "option-128", "X", &dhcp_universe, 128 }, + { "option-129", "X", &dhcp_universe, 129 }, + { "option-130", "X", &dhcp_universe, 130 }, + { "option-131", "X", &dhcp_universe, 131 }, + { "option-132", "X", &dhcp_universe, 132 }, + { "option-133", "X", &dhcp_universe, 133 }, + { "option-134", "X", &dhcp_universe, 134 }, + { "option-135", "X", &dhcp_universe, 135 }, + { "option-136", "X", &dhcp_universe, 136 }, + { "option-137", "X", &dhcp_universe, 137 }, + { "option-138", "X", &dhcp_universe, 138 }, + { "option-139", "X", &dhcp_universe, 139 }, + { "option-140", "X", &dhcp_universe, 140 }, + { "option-141", "X", &dhcp_universe, 141 }, + { "option-142", "X", &dhcp_universe, 142 }, + { "option-143", "X", &dhcp_universe, 143 }, + { "option-144", "X", &dhcp_universe, 144 }, + { "option-145", "X", &dhcp_universe, 145 }, + { "option-146", "X", &dhcp_universe, 146 }, + { "option-147", "X", &dhcp_universe, 147 }, + { "option-148", "X", &dhcp_universe, 148 }, + { "option-149", "X", &dhcp_universe, 149 }, + { "option-150", "X", &dhcp_universe, 150 }, + { "option-151", "X", &dhcp_universe, 151 }, + { "option-152", "X", &dhcp_universe, 152 }, + { "option-153", "X", &dhcp_universe, 153 }, + { "option-154", "X", &dhcp_universe, 154 }, + { "option-155", "X", &dhcp_universe, 155 }, + { "option-156", "X", &dhcp_universe, 156 }, + { "option-157", "X", &dhcp_universe, 157 }, + { "option-158", "X", &dhcp_universe, 158 }, + { "option-159", "X", &dhcp_universe, 159 }, + { "option-160", "X", &dhcp_universe, 160 }, + { "option-161", "X", &dhcp_universe, 161 }, + { "option-162", "X", &dhcp_universe, 162 }, + { "option-163", "X", &dhcp_universe, 163 }, + { "option-164", "X", &dhcp_universe, 164 }, + { "option-165", "X", &dhcp_universe, 165 }, + { "option-166", "X", &dhcp_universe, 166 }, + { "option-167", "X", &dhcp_universe, 167 }, + { "option-168", "X", &dhcp_universe, 168 }, + { "option-169", "X", &dhcp_universe, 169 }, + { "option-170", "X", &dhcp_universe, 170 }, + { "option-171", "X", &dhcp_universe, 171 }, + { "option-172", "X", &dhcp_universe, 172 }, + { "option-173", "X", &dhcp_universe, 173 }, + { "option-174", "X", &dhcp_universe, 174 }, + { "option-175", "X", &dhcp_universe, 175 }, + { "option-176", "X", &dhcp_universe, 176 }, + { "option-177", "X", &dhcp_universe, 177 }, + { "option-178", "X", &dhcp_universe, 178 }, + { "option-179", "X", &dhcp_universe, 179 }, + { "option-180", "X", &dhcp_universe, 180 }, + { "option-181", "X", &dhcp_universe, 181 }, + { "option-182", "X", &dhcp_universe, 182 }, + { "option-183", "X", &dhcp_universe, 183 }, + { "option-184", "X", &dhcp_universe, 184 }, + { "option-185", "X", &dhcp_universe, 185 }, + { "option-186", "X", &dhcp_universe, 186 }, + { "option-187", "X", &dhcp_universe, 187 }, + { "option-188", "X", &dhcp_universe, 188 }, + { "option-189", "X", &dhcp_universe, 189 }, + { "option-190", "X", &dhcp_universe, 190 }, + { "option-191", "X", &dhcp_universe, 191 }, + { "option-192", "X", &dhcp_universe, 192 }, + { "option-193", "X", &dhcp_universe, 193 }, + { "option-194", "X", &dhcp_universe, 194 }, + { "option-195", "X", &dhcp_universe, 195 }, + { "option-196", "X", &dhcp_universe, 196 }, + { "option-197", "X", &dhcp_universe, 197 }, + { "option-198", "X", &dhcp_universe, 198 }, + { "option-199", "X", &dhcp_universe, 199 }, + { "option-200", "X", &dhcp_universe, 200 }, + { "option-201", "X", &dhcp_universe, 201 }, + { "option-202", "X", &dhcp_universe, 202 }, + { "option-203", "X", &dhcp_universe, 203 }, + { "option-204", "X", &dhcp_universe, 204 }, + { "option-205", "X", &dhcp_universe, 205 }, + { "option-206", "X", &dhcp_universe, 206 }, + { "option-207", "X", &dhcp_universe, 207 }, + { "option-208", "X", &dhcp_universe, 208 }, + { "option-209", "X", &dhcp_universe, 209 }, + { "option-210", "X", &dhcp_universe, 210 }, + { "option-211", "X", &dhcp_universe, 211 }, + { "option-212", "X", &dhcp_universe, 212 }, + { "option-213", "X", &dhcp_universe, 213 }, + { "option-214", "X", &dhcp_universe, 214 }, + { "option-215", "X", &dhcp_universe, 215 }, + { "option-216", "X", &dhcp_universe, 216 }, + { "option-217", "X", &dhcp_universe, 217 }, + { "option-218", "X", &dhcp_universe, 218 }, + { "option-219", "X", &dhcp_universe, 219 }, + { "option-220", "X", &dhcp_universe, 220 }, + { "option-221", "X", &dhcp_universe, 221 }, + { "option-222", "X", &dhcp_universe, 222 }, + { "option-223", "X", &dhcp_universe, 223 }, + { "option-224", "X", &dhcp_universe, 224 }, + { "option-225", "X", &dhcp_universe, 225 }, + { "option-226", "X", &dhcp_universe, 226 }, + { "option-227", "X", &dhcp_universe, 227 }, + { "option-228", "X", &dhcp_universe, 228 }, + { "option-229", "X", &dhcp_universe, 229 }, + { "option-230", "X", &dhcp_universe, 230 }, + { "option-231", "X", &dhcp_universe, 231 }, + { "option-232", "X", &dhcp_universe, 232 }, + { "option-233", "X", &dhcp_universe, 233 }, + { "option-234", "X", &dhcp_universe, 234 }, + { "option-235", "X", &dhcp_universe, 235 }, + { "option-236", "X", &dhcp_universe, 236 }, + { "option-237", "X", &dhcp_universe, 237 }, + { "option-238", "X", &dhcp_universe, 238 }, + { "option-239", "X", &dhcp_universe, 239 }, + { "option-240", "X", &dhcp_universe, 240 }, + { "option-241", "X", &dhcp_universe, 241 }, + { "option-242", "X", &dhcp_universe, 242 }, + { "option-243", "X", &dhcp_universe, 243 }, + { "option-244", "X", &dhcp_universe, 244 }, + { "option-245", "X", &dhcp_universe, 245 }, + { "option-246", "X", &dhcp_universe, 246 }, + { "option-247", "X", &dhcp_universe, 247 }, + { "option-248", "X", &dhcp_universe, 248 }, + { "option-249", "X", &dhcp_universe, 249 }, + { "option-250", "X", &dhcp_universe, 250 }, + { "option-251", "X", &dhcp_universe, 251 }, + { "option-252", "X", &dhcp_universe, 252 }, + { "option-253", "X", &dhcp_universe, 253 }, + { "option-254", "X", &dhcp_universe, 254 }, + { "option-end", "e", &dhcp_universe, 255 }, +}; + +/* Default dhcp option priority list (this is ad hoc and should not be + mistaken for a carefully crafted and optimized list). */ +unsigned char dhcp_option_default_priority_list [] = { + DHO_DHCP_REQUESTED_ADDRESS, + DHO_DHCP_OPTION_OVERLOAD, + DHO_DHCP_MAX_MESSAGE_SIZE, + DHO_DHCP_RENEWAL_TIME, + DHO_DHCP_REBINDING_TIME, + DHO_DHCP_CLASS_IDENTIFIER, + DHO_DHCP_CLIENT_IDENTIFIER, + DHO_SUBNET_MASK, + DHO_TIME_OFFSET, + DHO_ROUTERS, + DHO_TIME_SERVERS, + DHO_NAME_SERVERS, + DHO_DOMAIN_NAME_SERVERS, + DHO_HOST_NAME, + DHO_LOG_SERVERS, + DHO_COOKIE_SERVERS, + DHO_LPR_SERVERS, + DHO_IMPRESS_SERVERS, + DHO_RESOURCE_LOCATION_SERVERS, + DHO_HOST_NAME, + DHO_BOOT_SIZE, + DHO_MERIT_DUMP, + DHO_DOMAIN_NAME, + DHO_SWAP_SERVER, + DHO_ROOT_PATH, + DHO_EXTENSIONS_PATH, + DHO_IP_FORWARDING, + DHO_NON_LOCAL_SOURCE_ROUTING, + DHO_POLICY_FILTER, + DHO_MAX_DGRAM_REASSEMBLY, + DHO_DEFAULT_IP_TTL, + DHO_PATH_MTU_AGING_TIMEOUT, + DHO_PATH_MTU_PLATEAU_TABLE, + DHO_INTERFACE_MTU, + DHO_ALL_SUBNETS_LOCAL, + DHO_BROADCAST_ADDRESS, + DHO_PERFORM_MASK_DISCOVERY, + DHO_MASK_SUPPLIER, + DHO_ROUTER_DISCOVERY, + DHO_ROUTER_SOLICITATION_ADDRESS, + DHO_STATIC_ROUTES, + DHO_TRAILER_ENCAPSULATION, + DHO_ARP_CACHE_TIMEOUT, + DHO_IEEE802_3_ENCAPSULATION, + DHO_DEFAULT_TCP_TTL, + DHO_TCP_KEEPALIVE_INTERVAL, + DHO_TCP_KEEPALIVE_GARBAGE, + DHO_NIS_DOMAIN, + DHO_NIS_SERVERS, + DHO_NTP_SERVERS, + DHO_VENDOR_ENCAPSULATED_OPTIONS, + DHO_NETBIOS_NAME_SERVERS, + DHO_NETBIOS_DD_SERVER, + DHO_NETBIOS_NODE_TYPE, + DHO_NETBIOS_SCOPE, + DHO_FONT_SERVERS, + DHO_X_DISPLAY_MANAGER, + DHO_DHCP_PARAMETER_REQUEST_LIST, + + /* Presently-undefined options... */ + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, +}; + +int sizeof_dhcp_option_default_priority_list = + sizeof dhcp_option_default_priority_list; + + +char *hardware_types [] = { + "unknown-0", + "ethernet", + "unknown-2", + "unknown-3", + "unknown-4", + "unknown-5", + "token-ring", + "unknown-7", + "fddi", + "unknown-9", + "unknown-10", + "unknown-11", + "unknown-12", + "unknown-13", + "unknown-14", + "unknown-15", + "unknown-16", + "unknown-17", + "unknown-18", + "unknown-19", + "unknown-20", + "unknown-21", + "unknown-22", + "unknown-23", + "unknown-24", + "unknown-25", + "unknown-26", + "unknown-27", + "unknown-28", + "unknown-29", + "unknown-30", + "unknown-31", + "unknown-32", + "unknown-33", + "unknown-34", + "unknown-35", + "unknown-36", + "unknown-37", + "unknown-38", + "unknown-39", + "unknown-40", + "unknown-41", + "unknown-42", + "unknown-43", + "unknown-44", + "unknown-45", + "unknown-46", + "unknown-47", + "unknown-48", + "unknown-49", + "unknown-50", + "unknown-51", + "unknown-52", + "unknown-53", + "unknown-54", + "unknown-55", + "unknown-56", + "unknown-57", + "unknown-58", + "unknown-59", + "unknown-60", + "unknown-61", + "unknown-62", + "unknown-63", + "unknown-64", + "unknown-65", + "unknown-66", + "unknown-67", + "unknown-68", + "unknown-69", + "unknown-70", + "unknown-71", + "unknown-72", + "unknown-73", + "unknown-74", + "unknown-75", + "unknown-76", + "unknown-77", + "unknown-78", + "unknown-79", + "unknown-80", + "unknown-81", + "unknown-82", + "unknown-83", + "unknown-84", + "unknown-85", + "unknown-86", + "unknown-87", + "unknown-88", + "unknown-89", + "unknown-90", + "unknown-91", + "unknown-92", + "unknown-93", + "unknown-94", + "unknown-95", + "unknown-96", + "unknown-97", + "unknown-98", + "unknown-99", + "unknown-100", + "unknown-101", + "unknown-102", + "unknown-103", + "unknown-104", + "unknown-105", + "unknown-106", + "unknown-107", + "unknown-108", + "unknown-109", + "unknown-110", + "unknown-111", + "unknown-112", + "unknown-113", + "unknown-114", + "unknown-115", + "unknown-116", + "unknown-117", + "unknown-118", + "unknown-119", + "unknown-120", + "unknown-121", + "unknown-122", + "unknown-123", + "unknown-124", + "unknown-125", + "unknown-126", + "unknown-127", + "unknown-128", + "unknown-129", + "unknown-130", + "unknown-131", + "unknown-132", + "unknown-133", + "unknown-134", + "unknown-135", + "unknown-136", + "unknown-137", + "unknown-138", + "unknown-139", + "unknown-140", + "unknown-141", + "unknown-142", + "unknown-143", + "unknown-144", + "unknown-145", + "unknown-146", + "unknown-147", + "unknown-148", + "unknown-149", + "unknown-150", + "unknown-151", + "unknown-152", + "unknown-153", + "unknown-154", + "unknown-155", + "unknown-156", + "unknown-157", + "unknown-158", + "unknown-159", + "unknown-160", + "unknown-161", + "unknown-162", + "unknown-163", + "unknown-164", + "unknown-165", + "unknown-166", + "unknown-167", + "unknown-168", + "unknown-169", + "unknown-170", + "unknown-171", + "unknown-172", + "unknown-173", + "unknown-174", + "unknown-175", + "unknown-176", + "unknown-177", + "unknown-178", + "unknown-179", + "unknown-180", + "unknown-181", + "unknown-182", + "unknown-183", + "unknown-184", + "unknown-185", + "unknown-186", + "unknown-187", + "unknown-188", + "unknown-189", + "unknown-190", + "unknown-191", + "unknown-192", + "unknown-193", + "unknown-194", + "unknown-195", + "unknown-196", + "unknown-197", + "unknown-198", + "unknown-199", + "unknown-200", + "unknown-201", + "unknown-202", + "unknown-203", + "unknown-204", + "unknown-205", + "unknown-206", + "unknown-207", + "unknown-208", + "unknown-209", + "unknown-210", + "unknown-211", + "unknown-212", + "unknown-213", + "unknown-214", + "unknown-215", + "unknown-216", + "unknown-217", + "unknown-218", + "unknown-219", + "unknown-220", + "unknown-221", + "unknown-222", + "unknown-223", + "unknown-224", + "unknown-225", + "unknown-226", + "unknown-227", + "unknown-228", + "unknown-229", + "unknown-230", + "unknown-231", + "unknown-232", + "unknown-233", + "unknown-234", + "unknown-235", + "unknown-236", + "unknown-237", + "unknown-238", + "unknown-239", + "unknown-240", + "unknown-241", + "unknown-242", + "unknown-243", + "unknown-244", + "unknown-245", + "unknown-246", + "unknown-247", + "unknown-248", + "unknown-249", + "unknown-250", + "unknown-251", + "unknown-252", + "unknown-253", + "unknown-254", + "unknown-255" }; + + + +struct hash_table universe_hash; + +void initialize_universes() +{ + int i; + + dhcp_universe.name = "dhcp"; + dhcp_universe.hash = new_hash (); + if (!dhcp_universe.hash) + error ("Can't allocate dhcp option hash table."); + for (i = 0; i < 256; i++) { + dhcp_universe.options [i] = &dhcp_options [i]; + add_hash (dhcp_universe.hash, + (unsigned char *)dhcp_options [i].name, 0, + (unsigned char *)&dhcp_options [i]); + } + universe_hash.hash_count = DEFAULT_HASH_SIZE; + add_hash (&universe_hash, + (unsigned char *)dhcp_universe.name, 0, + (unsigned char *)&dhcp_universe); +} diff --git a/reactos/subsys/system/dhcp/timer.c b/reactos/subsys/system/dhcp/timer.c new file mode 100644 index 00000000000..ccd817188ec --- /dev/null +++ b/reactos/subsys/system/dhcp/timer.c @@ -0,0 +1,2 @@ +#include "rosdhcp.h" + diff --git a/reactos/subsys/system/dhcp/tree.c b/reactos/subsys/system/dhcp/tree.c new file mode 100644 index 00000000000..9a49e5e3577 --- /dev/null +++ b/reactos/subsys/system/dhcp/tree.c @@ -0,0 +1,412 @@ +/* tree.c + + Routines for manipulating parse trees... */ + +/* + * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This software has been written for the Internet Software Consortium + * by Ted Lemon in cooperation with Vixie + * Enterprises. To learn more about the Internet Software Consortium, + * see ``http://www.vix.com/isc''. To learn more about Vixie + * Enterprises, see ``http://www.vix.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: tree.c,v 1.10 1997/05/09 08:14:57 mellon Exp $ Copyright (c) 1995, 1996, 1997 The Internet Software Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "rosdhcp.h" + +static TIME tree_evaluate_recurse PROTO ((int *, unsigned char **, int *, + struct tree *)); +static TIME do_host_lookup PROTO ((int *, unsigned char **, int *, + struct dns_host_entry *)); +static void do_data_copy PROTO ((int *, unsigned char **, int *, + unsigned char *, int)); + +pair cons (car, cdr) + caddr_t car; + pair cdr; +{ + pair foo = (pair)dmalloc (sizeof *foo, "cons"); + if (!foo) + error ("no memory for cons."); + foo -> car = car; + foo -> cdr = cdr; + return foo; +} + +struct tree_cache *tree_cache (tree) + struct tree *tree; +{ + struct tree_cache *tc; + + tc = new_tree_cache ("tree_cache"); + if (!tc) + return 0; + tc -> value = (unsigned char *)0; + tc -> len = tc -> buf_size = 0; + tc -> timeout = 0; + tc -> tree = tree; + return tc; +} + +struct tree *tree_host_lookup (name) + char *name; +{ + struct tree *nt; + nt = new_tree ("tree_host_lookup"); + if (!nt) + error ("No memory for host lookup tree node."); + nt -> op = TREE_HOST_LOOKUP; + nt -> data.host_lookup.host = enter_dns_host (name); + return nt; +} + +struct dns_host_entry *enter_dns_host (name) + char *name; +{ + struct dns_host_entry *dh; + + if (!(dh = (struct dns_host_entry *)dmalloc + (sizeof (struct dns_host_entry), "enter_dns_host")) + || !(dh -> hostname = dmalloc (strlen (name) + 1, + "enter_dns_host"))) + error ("Can't allocate space for new host."); + strcpy (dh -> hostname, name); + dh -> data = (unsigned char *)0; + dh -> data_len = 0; + dh -> buf_len = 0; + dh -> timeout = 0; + return dh; +} + +struct tree *tree_const (data, len) + unsigned char *data; + int len; +{ + struct tree *nt; + if (!(nt = new_tree ("tree_const")) + || !(nt -> data.const_val.data = + (unsigned char *)dmalloc (len, "tree_const"))) + error ("No memory for constant data tree node."); + nt -> op = TREE_CONST; + memcpy (nt -> data.const_val.data, data, len); + nt -> data.const_val.len = len; + return nt; +} + +struct tree *tree_concat (left, right) + struct tree *left, *right; +{ + struct tree *nt; + + /* If we're concatenating a null tree to a non-null tree, just + return the non-null tree; if both trees are null, return + a null tree. */ + if (!left) + return right; + if (!right) + return left; + + /* If both trees are constant, combine them. */ + if (left -> op == TREE_CONST && right -> op == TREE_CONST) { + unsigned char *buf = dmalloc (left -> data.const_val.len + + right -> data.const_val.len, + "tree_concat"); + if (!buf) + error ("No memory to concatenate constants."); + memcpy (buf, left -> data.const_val.data, + left -> data.const_val.len); + memcpy (buf + left -> data.const_val.len, + right -> data.const_val.data, + right -> data.const_val.len); + dfree (left -> data.const_val.data, "tree_concat"); + dfree (right -> data.const_val.data, "tree_concat"); + left -> data.const_val.data = buf; + left -> data.const_val.len += right -> data.const_val.len; + free_tree (right, "tree_concat"); + return left; + } + + /* Otherwise, allocate a new node to concatenate the two. */ + if (!(nt = new_tree ("tree_concat"))) + error ("No memory for data tree concatenation node."); + nt -> op = TREE_CONCAT; + nt -> data.concat.left = left; + nt -> data.concat.right = right; + return nt; +} + +struct tree *tree_limit (tree, limit) + struct tree *tree; + int limit; +{ + struct tree *rv; + + /* If the tree we're limiting is constant, limit it now. */ + if (tree -> op == TREE_CONST) { + if (tree -> data.const_val.len > limit) + tree -> data.const_val.len = limit; + return tree; + } + + /* Otherwise, put in a node which enforces the limit on evaluation. */ + rv = new_tree ("tree_limit"); + if (!rv) + return (struct tree *)0; + rv -> op = TREE_LIMIT; + rv -> data.limit.tree = tree; + rv -> data.limit.limit = limit; + return rv; +} + +int tree_evaluate (tree_cache) + struct tree_cache *tree_cache; +{ + unsigned char *bp = tree_cache -> value; + int bc = tree_cache -> buf_size; + int bufix = 0; + + /* If there's no tree associated with this cache, it evaluates + to a constant and that was detected at startup. */ + if (!tree_cache -> tree) + return 1; + + /* Try to evaluate the tree without allocating more memory... */ + tree_cache -> timeout = tree_evaluate_recurse (&bufix, &bp, &bc, + tree_cache -> tree); + + /* No additional allocation needed? */ + if (bufix <= bc) { + tree_cache -> len = bufix; + return 1; + } + + /* If we can't allocate more memory, return with what we + have (maybe nothing). */ + if (!(bp = (unsigned char *)dmalloc (bufix, "tree_evaluate"))) + return 0; + + /* Record the change in conditions... */ + bc = bufix; + bufix = 0; + + /* Note that the size of the result shouldn't change on the + second call to tree_evaluate_recurse, since we haven't + changed the ``current'' time. */ + tree_evaluate_recurse (&bufix, &bp, &bc, tree_cache -> tree); + + /* Free the old buffer if needed, then store the new buffer + location and size and return. */ + if (tree_cache -> value) + dfree (tree_cache -> value, "tree_evaluate"); + tree_cache -> value = bp; + tree_cache -> len = bufix; + tree_cache -> buf_size = bc; + return 1; +} + +static TIME tree_evaluate_recurse (bufix, bufp, bufcount, tree) + int *bufix; + unsigned char **bufp; + int *bufcount; + struct tree *tree; +{ + int limit; + TIME t1, t2; + + switch (tree -> op) { + case TREE_CONCAT: + t1 = tree_evaluate_recurse (bufix, bufp, bufcount, + tree -> data.concat.left); + t2 = tree_evaluate_recurse (bufix, bufp, bufcount, + tree -> data.concat.right); + if (t1 > t2) + return t2; + return t1; + + case TREE_HOST_LOOKUP: + return do_host_lookup (bufix, bufp, bufcount, + tree -> data.host_lookup.host); + + case TREE_CONST: + do_data_copy (bufix, bufp, bufcount, + tree -> data.const_val.data, + tree -> data.const_val.len); + t1 = MAX_TIME; + return t1; + + case TREE_LIMIT: + limit = *bufix + tree -> data.limit.limit; + t1 = tree_evaluate_recurse (bufix, bufp, bufcount, + tree -> data.limit.tree); + *bufix = limit; + return t1; + + default: + warn ("Bad node id in tree: %d."); + t1 = MAX_TIME; + return t1; + } +} + +static TIME do_host_lookup (bufix, bufp, bufcount, dns) + int *bufix; + unsigned char **bufp; + int *bufcount; + struct dns_host_entry *dns; +{ + struct hostent *h; + int i; + int new_len; + +#ifdef DEBUG_EVAL + debug ("time: now = %d dns = %d %d diff = %d", + cur_time, dns -> timeout, cur_time - dns -> timeout); +#endif + + /* If the record hasn't timed out, just copy the data and return. */ + if (cur_time <= dns -> timeout) { +#ifdef DEBUG_EVAL + debug ("easy copy: %x %d %x", + dns -> data, dns -> data_len, + dns -> data ? *(int *)(dns -> data) : 0); +#endif + do_data_copy (bufix, bufp, bufcount, + dns -> data, dns -> data_len); + return dns -> timeout; + } +#ifdef DEBUG_EVAL + debug ("Looking up %s", dns -> hostname); +#endif + + /* Otherwise, look it up... */ + h = gethostbyname (dns -> hostname); + if (!h) { +#ifndef NO_H_ERRNO + switch (h_errno) { + case HOST_NOT_FOUND: +#endif + warn ("%s: host unknown.", dns -> hostname); +#ifndef NO_H_ERRNO + break; + case TRY_AGAIN: + warn ("%s: temporary name server failure", + dns -> hostname); + break; + case NO_RECOVERY: + warn ("%s: name server failed", dns -> hostname); + break; + case NO_DATA: + warn ("%s: no A record associated with address", + dns -> hostname); + } +#endif /* !NO_H_ERRNO */ + + /* Okay to try again after a minute. */ + return cur_time + 60; + } + +#ifdef DEBUG_EVAL + debug ("Lookup succeeded; first address is %x", + h -> h_addr_list [0]); +#endif + + /* Count the number of addresses we got... */ + for (i = 0; h -> h_addr_list [i]; i++) + ; + + /* Do we need to allocate more memory? */ + new_len = i * h -> h_length; + if (dns -> buf_len < i) { + unsigned char *buf = + (unsigned char *)dmalloc (new_len, "do_host_lookup"); + /* If we didn't get more memory, use what we have. */ + if (!buf) { + new_len = dns -> buf_len; + if (!dns -> buf_len) { + dns -> timeout = cur_time + 60; + return dns -> timeout; + } + } else { + if (dns -> data) + dfree (dns -> data, "do_host_lookup"); + dns -> data = buf; + dns -> buf_len = new_len; + } + } + + /* Addresses are conveniently stored one to the buffer, so we + have to copy them out one at a time... :'( */ + for (i = 0; i < new_len / h -> h_length; i++) { + memcpy (dns -> data + h -> h_length * i, + h -> h_addr_list [i], h -> h_length); + } +#ifdef DEBUG_EVAL + debug ("dns -> data: %x h -> h_addr_list [0]: %x", + *(int *)(dns -> data), h -> h_addr_list [0]); +#endif + dns -> data_len = new_len; + + /* Set the timeout for an hour from now. + XXX This should really use the time on the DNS reply. */ + dns -> timeout = cur_time + 3600; + +#ifdef DEBUG_EVAL + debug ("hard copy: %x %d %x", + dns -> data, dns -> data_len, *(int *)(dns -> data)); +#endif + do_data_copy (bufix, bufp, bufcount, dns -> data, dns -> data_len); + return dns -> timeout; +} + +static void do_data_copy (bufix, bufp, bufcount, data, len) + int *bufix; + unsigned char **bufp; + int *bufcount; + unsigned char *data; + int len; +{ + int space = *bufcount - *bufix; + + /* If there's more space than we need, use only what we need. */ + if (space > len) + space = len; + + /* Copy as much data as will fit, then increment the buffer index + by the amount we actually had to copy, which could be more. */ + if (space > 0) + memcpy (*bufp + *bufix, data, space); + *bufix += len; +} diff --git a/reactos/subsys/system/dhcp/util.c b/reactos/subsys/system/dhcp/util.c new file mode 100644 index 00000000000..22d3948f920 --- /dev/null +++ b/reactos/subsys/system/dhcp/util.c @@ -0,0 +1,106 @@ +#include +#include "rosdhcp.h" + +char *piaddr( struct iaddr addr ) { + struct sockaddr_in *sa = (struct sockaddr_in *)addr.iabuf; + return inet_ntoa( sa->sin_addr ); +} + +int note( char *format, ... ) { + va_list arg_begin; + va_start( arg_begin, format ); + char buf[0x100]; + int ret; + + ret = vsnprintf( buf, sizeof(buf), format, arg_begin ); + + DbgPrint("NOTE: %s\n", buf); + + return ret; +} + +int debug( char *format, ... ) { + va_list arg_begin; + va_start( arg_begin, format ); + char buf[0x100]; + int ret; + + ret = vsnprintf( buf, sizeof(buf), format, arg_begin ); + + DbgPrint("DEBUG: %s\n", buf); + + return ret; +} + +int warn( char *format, ... ) { + va_list arg_begin; + va_start( arg_begin, format ); + char buf[0x100]; + int ret; + + ret = vsnprintf( buf, sizeof(buf), format, arg_begin ); + + DbgPrint("WARN: %s\n", buf); + + return ret; +} + +int warning( char *format, ... ) { + va_list arg_begin; + va_start( arg_begin, format ); + char buf[0x100]; + int ret; + + ret = vsnprintf( buf, sizeof(buf), format, arg_begin ); + + DbgPrint("WARNING: %s\n", buf); + + return ret; +} + +void error( char *format, ... ) { + va_list arg_begin; + va_start( arg_begin, format ); + char buf[0x100]; + + vsnprintf( buf, sizeof(buf), format, arg_begin ); + + DbgPrint("ERROR: %s\n", buf); +} + +int16_t getShort( unsigned char *data ) { + return 0; +} + +u_int16_t getUShort( unsigned char *data ) { + return 0; +} + +int32_t getLong( unsigned char *data ) { + return 0; +} + +u_int32_t getULong( unsigned char *data ) { + return 0; +} + +int addr_eq( struct iaddr a, struct iaddr b ) { + return a.len == b.len && !memcmp( a.iabuf, b.iabuf, a.len ); +} + +void *dmalloc( int size, char *name ) { return malloc( size ); } +void dfree( void *v, char *name ) { free( v ); } + +int read_client_conf(void) { + return 0; +} + +struct iaddr broadcast_addr( struct iaddr addr, struct iaddr mask ) { + struct iaddr bcast = { 0 }; + return bcast; +} + +struct iaddr subnet_number( struct iaddr addr, struct iaddr mask ) { + struct iaddr bcast = { 0 }; + return bcast; +} diff --git a/reactos/subsys/system/explorer/Jamfile b/reactos/subsys/system/explorer/Jamfile index 7f4de0d38f0..643c9ad65db 100644 --- a/reactos/subsys/system/explorer/Jamfile +++ b/reactos/subsys/system/explorer/Jamfile @@ -24,7 +24,8 @@ exe explorer : shell/regfs.cpp shell/fatfs.cpp shell/webchild.cpp - shell/startup.c + services/startup.c + services/shellservices.cpp taskbar/desktopbar.cpp taskbar/quicklaunch.cpp taskbar/startmenu.cpp diff --git a/reactos/subsys/system/explorer/Makefile b/reactos/subsys/system/explorer/Makefile index 27f6852474c..e8ccaca9494 100644 --- a/reactos/subsys/system/explorer/Makefile +++ b/reactos/subsys/system/explorer/Makefile @@ -17,7 +17,7 @@ TARGET_INSTALLDIR := . TARGET_CFLAGS := \ -D__USE_W32API -DWIN32 -D_ROS_ \ -D_WIN32_IE=0x0600 -D_WIN32_WINNT=0x0501 -DWINVER=0x0500 \ - -DUNICODE -fexceptions -Wall \ + -DUNICODE -fexceptions -Wall -g \ -I../../../include/expat TARGET_CPPFLAGS := $(TARGET_CFLAGS) @@ -25,7 +25,7 @@ TARGET_CPPFLAGS := $(TARGET_CFLAGS) TARGET_RCFLAGS := -D__USE_W32API -DWIN32 -D_ROS_ -D__WINDRES__ TARGET_SDKLIBS := \ - gdi32.a comctl32.a ole32.a oleaut32.a shell32.a expat.a \ + gdi32.a user32.a comctl32.a ole32.a oleaut32.a shell32.a expat.a \ notifyhook.a ws2_32.a msimg32.a TARGET_GCCLIBS := stdc++ uuid @@ -45,10 +45,11 @@ TARGET_OBJECTS := \ shell/pane.o \ shell/regfs.o \ shell/shellbrowser.o \ - shell/startup.o \ shell/unixfs.o \ shell/webchild.o \ shell/winfs.o \ + services/startup.o \ + services/shellservices.o \ taskbar/desktopbar.o \ taskbar/taskbar.o \ taskbar/startmenu.o \ diff --git a/reactos/subsys/system/explorer/Makefile.MinGW b/reactos/subsys/system/explorer/Makefile.MinGW index 569efe10674..a75dc2de0e6 100644 --- a/reactos/subsys/system/explorer/Makefile.MinGW +++ b/reactos/subsys/system/explorer/Makefile.MinGW @@ -38,7 +38,7 @@ CXXFLAGS = $(CFLAGS) EXEC_SUFFIX = .exe RES_SUFFIX = .coff -VPATH = shell utility taskbar desktop dialogs +VPATH = shell utility taskbar desktop dialogs services PROGRAM = explorer @@ -51,6 +51,7 @@ OBJECTS = \ window.o \ dragdropimpl.o \ shellbrowserimpl.o \ + shellservices.o \ explorer.o \ entries.o \ winfs.o \ diff --git a/reactos/subsys/system/explorer/Makefile.PCH b/reactos/subsys/system/explorer/Makefile.PCH index 35c1a6dcfe2..496831bf539 100644 --- a/reactos/subsys/system/explorer/Makefile.PCH +++ b/reactos/subsys/system/explorer/Makefile.PCH @@ -39,7 +39,7 @@ CXXFLAGS = $(CFLAGS) EXEC_SUFFIX = .exe RES_SUFFIX = .coff -VPATH = shell utility taskbar desktop dialogs +VPATH = shell utility taskbar desktop dialogs services PROGRAM = explorer @@ -69,6 +69,7 @@ OBJECTS = \ desktopbar.o \ taskbar.o \ startmenu.o \ + shellservices.o \ traynotify.o \ quicklaunch.o \ favorites.o \ diff --git a/reactos/subsys/system/explorer/Makefile.Wine b/reactos/subsys/system/explorer/Makefile.Wine index 39d0deb4d48..70e36d058e6 100644 --- a/reactos/subsys/system/explorer/Makefile.Wine +++ b/reactos/subsys/system/explorer/Makefile.Wine @@ -10,7 +10,7 @@ EXTRA_OBJS = notifyhook.dll libexpat.dll EXTRALIBS = $(LIBUUID) C_SRCS = \ - shell/startup.c \ + services/startup.c \ utility/splitpath.c CPP_SRCS = \ @@ -34,6 +34,7 @@ CPP_SRCS = \ shell/regfs.cpp \ shell/fatfs.cpp \ shell/webchild.cpp \ + services/shellservices.cpp \ taskbar/desktopbar.cpp \ taskbar/taskbar.cpp \ taskbar/startmenu.cpp \ diff --git a/reactos/subsys/system/explorer/bak/shellhook.cpp b/reactos/subsys/system/explorer/bak/shellhook.cpp deleted file mode 100644 index 750ea7fc63e..00000000000 --- a/reactos/subsys/system/explorer/bak/shellhook.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2003 Martin Fuchs - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - - // - // Explorer clone - // - // shellhook.cpp - // - // Martin Fuchs, 17.08.2003 - // - - -#include "../utility/utility.h" - -#include "shellhook.h" - - -static HINSTANCE s_hInstance; - -struct SharedMem { - HWND _callback_hwnd; - UINT _callback_msg; - HHOOK _hshellHook; -}; - -static HANDLE s_hMapObject; -static SharedMem* s_pSharedMem; - - -BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID) -{ - switch(dwReason) { - case DLL_PROCESS_ATTACH: { - s_hInstance = hInst; - DisableThreadLibraryCalls(hInst); - s_hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, - 0, sizeof(SharedMem), _T("ROSExplorerShellHookSharedMem")); - if (!s_hMapObject) - return FALSE; - - // The first process to attach initializes memory. - BOOL bInit = GetLastError()!=ERROR_ALREADY_EXISTS; - - // Get a pointer to the file-mapped shared memory. - s_pSharedMem = (SharedMem*) MapViewOfFile(s_hMapObject, FILE_MAP_WRITE, 0, 0, 0); - if (!s_pSharedMem) - return FALSE; - - // Initialize memory if this is the first process. - if (bInit) - memset(s_pSharedMem, 0, sizeof(SharedMem)); - break;} - - case DLL_PROCESS_DETACH: - UnmapViewOfFile(s_pSharedMem); - s_pSharedMem = NULL; - CloseHandle(s_hMapObject); - break; - } - - return TRUE; -} - - -static void DoCallBack(int code, WPARAM wparam, LPARAM lparam) -{ - UINT callback_msg = s_pSharedMem->_callback_msg; - HWND callback_hwnd = s_pSharedMem->_callback_hwnd; - - if (callback_msg) - PostMessage(callback_hwnd, callback_msg, wparam, code); // lparam unused -} - - -LRESULT CALLBACK ShellHookProc(int code, WPARAM wparam, LPARAM lparam) -{ - DoCallBack(code, wparam, lparam); - - return CallNextHookEx(s_pSharedMem->_hshellHook, code, wparam, lparam); -} - - -void InstallShellHook(HWND callback_hwnd, UINT callback_msg) -{ - s_pSharedMem->_callback_hwnd = callback_hwnd; - s_pSharedMem->_callback_msg = callback_msg; - - s_pSharedMem->_hshellHook = SetWindowsHookEx(WH_SHELL, ShellHookProc, s_hInstance, 0); -} - -void DeinstallShellHook() -{ - s_pSharedMem->_callback_hwnd = 0; - s_pSharedMem->_callback_msg = 0; - - UnhookWindowsHookEx(s_pSharedMem->_hshellHook); - s_pSharedMem->_hshellHook = 0; -} diff --git a/reactos/subsys/system/explorer/bak/shellhook.dsp b/reactos/subsys/system/explorer/bak/shellhook.dsp deleted file mode 100644 index e19dacab8b1..00000000000 --- a/reactos/subsys/system/explorer/bak/shellhook.dsp +++ /dev/null @@ -1,113 +0,0 @@ -# Microsoft Developer Studio Project File - Name="shellhook" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=shellhook - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "shellhook.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "shellhook.mak" CFG="shellhook - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "shellhook - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "shellhook - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.cmd -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "shellhook - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "shellhook___Win32_Release" -# PROP BASE Intermediate_Dir "shellhook___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_ROS_" /D _WIN32_IE=0x0501 /D _WIN32_WINNT=0x0501 /D "_SHELLHOOK_IMPL" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x407 /d "NDEBUG" -# ADD RSC /l 0x407 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.cmd -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 -# ADD LINK32 /nologo /subsystem:windows /dll /machine:I386 - -!ELSEIF "$(CFG)" == "shellhook - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "shellhook___Win32_Debug" -# PROP BASE Intermediate_Dir "shellhook___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_ROS_" /D _WIN32_IE=0x0501 /D _WIN32_WINNT=0x0501 /D "_SHELLHOOK_IMPL" /FR /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x407 /d "_DEBUG" -# ADD RSC /l 0x407 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.cmd -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "shellhook - Win32 Release" -# Name "shellhook - Win32 Debug" -# Begin Group "Header Files" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=.\taskbar\shellhook.h -# End Source File -# Begin Source File - -SOURCE=.\utility\utility.h -# End Source File -# Begin Source File - -SOURCE=.\utility\window.h -# End Source File -# End Group -# Begin Source File - -SOURCE=.\taskbar\shellhook.cpp -# End Source File -# End Target -# End Project diff --git a/reactos/subsys/system/explorer/desktop/desktop.cpp b/reactos/subsys/system/explorer/desktop/desktop.cpp index 41b08b21786..2c0cbad51b6 100644 --- a/reactos/subsys/system/explorer/desktop/desktop.cpp +++ b/reactos/subsys/system/explorer/desktop/desktop.cpp @@ -155,8 +155,8 @@ static BOOL CALLBACK SwitchDesktopEnumFct(HWND hwnd, LPARAM lparam) { WindowSet& windows = *(WindowSet*)lparam; - if (IsWindowVisible(hwnd)) - if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop) + if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop) + if (IsWindowVisible(hwnd)) windows.insert(hwnd); return TRUE; @@ -212,12 +212,17 @@ static BOOL CALLBACK MinimizeDesktopEnumFct(HWND hwnd, LPARAM lparam) { list& minimized = *(list*)lparam; - if (IsWindowVisible(hwnd)) - if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop) - if (!IsIconic(hwnd)) { + if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop) + if (IsWindowVisible(hwnd) && !IsIconic(hwnd)) { + RECT rect; + + if (GetWindowRect(hwnd,&rect)) + if (rect.right>0 && rect.bottom>0 && + rect.right>rect.left && rect.bottom>rect.top) { minimized.push_back(MinimizeStruct(hwnd, GetWindowStyle(hwnd))); ShowWindowAsync(hwnd, SW_MINIMIZE); } + } return TRUE; } @@ -436,7 +441,7 @@ LRESULT DesktopWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) case WM_DESTROY: ///@todo use IShellBrowser::GetViewStateStream() and _pShellView->SaveViewState() to store view state - + if (SetShellWindow) SetShellWindow(0); break; @@ -526,7 +531,7 @@ LRESULT DesktopShellView::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) { switch(nmsg) { case WM_CONTEXTMENU: - if (!DoContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), _cm_ifs)) + if (!DoContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam))) DoDesktopContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); break; @@ -558,7 +563,7 @@ int DesktopShellView::Notify(int id, NMHDR* pnmh) return super::Notify(id, pnmh); } -bool DesktopShellView::DoContextMenu(int x, int y, CtxMenuInterfaces& cm_ifs) +bool DesktopShellView::DoContextMenu(int x, int y) { IDataObject* selection; @@ -588,7 +593,7 @@ bool DesktopShellView::DoContextMenu(int x, int y, CtxMenuInterfaces& cm_ifs) for(int i=pida->cidl; i>0; --i) apidl[i-1] = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[i]); - hr = ShellFolderContextMenu(ShellFolder(parent_pidl), _hwnd, pida->cidl, apidl, x, y, cm_ifs); + hr = ShellFolderContextMenu(ShellFolder(parent_pidl), _hwnd, pida->cidl, apidl, x, y, _cm_ifs); selection->Release(); @@ -612,7 +617,6 @@ HRESULT DesktopShellView::DoDesktopContextMenu(int x, int y) hr = pcm->QueryContextMenu(hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST-1, CMF_NORMAL|CMF_EXPLORE); if (SUCCEEDED(hr)) { - AppendMenu(hmenu, 0, FCIDM_SHVIEWLAST-3, ResString(IDS_PROPERTIES_EXPLORER)); AppendMenu(hmenu, MF_SEPARATOR, 0, NULL); AppendMenu(hmenu, 0, FCIDM_SHVIEWLAST-1, ResString(IDS_ABOUT_EXPLORER)); @@ -620,24 +624,6 @@ HRESULT DesktopShellView::DoDesktopContextMenu(int x, int y) _cm_ifs.reset(); - if (idCmd == FCIDM_SHVIEWLAST-3) { - ShellExecute (_hwnd, _T("open"), _T("c:\\reactos\\system32\\rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0"), NULL, NULL, SW_SHOWNORMAL); - - - //explorer_about(_hwnd); - //system("c:\\reactos\\system32\\cmd.exe"); - - //ShellExecute (_hwnd, _T("open"), _T("%SystemRoot%\\system32\\cmd.exe"), NULL, NULL, SW_SHOWNORMAL); - - //ShellExecute(NULL,"open","c:\\windows\\system32\\cmd.exe",NULL,NULL,SW_SHOWNORMAL); - //WCHAR* pFile="%SystemRoot%\system32\cmd.exe"; - //int rcode; - //TCHAR pFile[256]; - //strcopy("%SystemRoot%\\system32\\cmd.exe",pFile); - //ShellExecute(NULL, NULL, pFile, NULL, "C:\\", SW_SHOW); - //HINSTANCE rcode=ShellExecute(NULL, "open", pFile, NULL, "C:\\", SW_SHOW); - //ShellExecute(hwnd, __TEXT("open"), __TEXT("%SystemRoot%\system32\cmd.exe"), __TEXT("c:\"), SH_SHOW); - } if (idCmd == FCIDM_SHVIEWLAST-1) { explorer_about(_hwnd); } else if (idCmd) { diff --git a/reactos/subsys/system/explorer/desktop/desktop.h b/reactos/subsys/system/explorer/desktop/desktop.h index 751ffe4e18c..00e9dca75cb 100644 --- a/reactos/subsys/system/explorer/desktop/desktop.h +++ b/reactos/subsys/system/explorer/desktop/desktop.h @@ -180,7 +180,7 @@ protected: int Command(int id, int code); int Notify(int id, NMHDR* pnmh); - bool DoContextMenu(int x, int y, CtxMenuInterfaces& cm_ifs); + bool DoContextMenu(int x, int y); HRESULT DoDesktopContextMenu(int x, int y); void PositionIcons(int dir=1); diff --git a/reactos/subsys/system/explorer/dialogs/searchprogram.cpp b/reactos/subsys/system/explorer/dialogs/searchprogram.cpp index 5db205ac2d0..f9ad962ba84 100644 --- a/reactos/subsys/system/explorer/dialogs/searchprogram.cpp +++ b/reactos/subsys/system/explorer/dialogs/searchprogram.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2003, 2004 Martin Fuchs + * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -42,10 +42,11 @@ int CollectProgramsThread::Run() } catch(COMException&) { } - try { - collect_programs(SpecialFolderPath(CSIDL_PROGRAMS, _hwnd)); - } catch(COMException&) { - } + if (_alive) + try { + collect_programs(SpecialFolderPath(CSIDL_PROGRAMS, _hwnd)); + } catch(COMException&) { + } if (_alive) _cache_valid = true; @@ -60,16 +61,13 @@ void CollectProgramsThread::collect_programs(const ShellPath& path) dir->smart_scan(SORT_NONE, /*SCAN_EXTRACT_ICONS|*/SCAN_FILESYSTEM); - for(Entry*entry=dir->_down; entry; entry=entry->_next) { - if (!_alive) - break; - + for(Entry*entry=dir->_down; _alive && entry; entry=entry->_next) { if (entry->_shell_attribs & SFGAO_HIDDEN) continue; - if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) collect_programs(entry->create_absolute_pidl()); - } else if (entry->_shell_attribs & SFGAO_LINK) + else if (entry->_shell_attribs & SFGAO_LINK) if (_alive) _callback(entry, _para); } @@ -95,7 +93,7 @@ FindProgramDlg::FindProgramDlg(HWND hwnd) _thread(collect_programs_callback, hwnd, this), _sort(_list_ctrl, CompareFunc/*, (LPARAM)this*/) { - SetWindowIcon(hwnd, IDI_REACTOS/*IDI_SEARCH*/); + SetWindowIcon(hwnd, IDI_SEARCH); _resize_mgr.Add(IDC_FILTER, RESIZE_X); _resize_mgr.Add(IDC_CHECK_ENTRIES, MOVE_X); @@ -139,6 +137,8 @@ FindProgramDlg::FindProgramDlg(HWND hwnd) FindProgramDlg::~FindProgramDlg() { + _thread.Stop(); + unregister_pretranslate(_hwnd); } @@ -262,6 +262,10 @@ void FindProgramDlg::add_entry(const FPDEntry& cache_entry) LRESULT FindProgramDlg::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) { switch(nmsg) { + case WM_CLOSE: + ListView_SetImageList(_list_ctrl, 0, LVSIL_SMALL); // detach system image list + goto def; + case PM_TRANSLATE_MSG: { MSG* pmsg = (MSG*) lparam; @@ -270,7 +274,7 @@ LRESULT FindProgramDlg::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) return FALSE;} - default: + default: def: return super::WndProc(nmsg, wparam, lparam); } diff --git a/reactos/subsys/system/explorer/doc/TODO.txt b/reactos/subsys/system/explorer/doc/TODO.txt index 06d29af573c..572ce5c0cfb 100644 --- a/reactos/subsys/system/explorer/doc/TODO.txt +++ b/reactos/subsys/system/explorer/doc/TODO.txt @@ -28,3 +28,9 @@ - Search Programs -> performance monitor.msv -> Abort + + Martin, I would have a whish concerning explorer: I often want to start a program and give it some parameters. So nice featue: "Start with param..." + Nonvo: shell extension + tinus: we should think about installing some default shell extensions + m-fuchs: perhaps, but it'd be nice if they actually were shell extensions + diff --git a/reactos/subsys/system/explorer/explorer.cpp b/reactos/subsys/system/explorer/explorer.cpp index 90e91efff22..e2e0b94a2ba 100644 --- a/reactos/subsys/system/explorer/explorer.cpp +++ b/reactos/subsys/system/explorer/explorer.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2003, 2004 Martin Fuchs + * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -41,10 +41,15 @@ #include "dialogs/settings.h" // for MdiSdiDlg +#include "services/shellservices.h" + extern "C" int initialize_gdb_stub(); // start up GDB stub +DynamicLoadLibFct g_SHDOCVW_ShellDDEInit(TEXT("SHDOCVW"), 118); + + ExplorerGlobals g_Globals; @@ -382,7 +387,7 @@ const Icon& IconCache::extract(const String& path) SHFILEINFO sfi; -#if 1 // use system image list +#if 1 // use system image list - the "search program dialog" needs it HIMAGELIST himlSys = (HIMAGELIST) SHGetFileInfo(path, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX|SHGFI_SMALLICON); if (himlSys) { @@ -705,6 +710,19 @@ int main(int argc, char* argv[]) #endif // __MINGW && UNICODE +static bool SetShellReadyEvent(LPCTSTR evtName) +{ + HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, evtName); + if (!hEvent) + return false; + + SetEvent(hEvent); + CloseHandle(hEvent); + + return true; +} + + int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd) { CONTEXT("WinMain()"); @@ -729,6 +747,14 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL RegSetValueEx(hkey, TEXT("Shell"), 0, REG_SZ, (LPBYTE)path, l*sizeof(TCHAR)); RegCloseKey(hkey); } + + if (!RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"), &hkey)) { + + ///@todo save previous shell application in config file + + RegSetValueEx(hkey, TEXT("Shell"), 0, REG_SZ, (LPBYTE)TEXT(""), l*sizeof(TCHAR)); + RegCloseKey(hkey); + } } HWND shellWindow = GetShellWindow(); @@ -750,10 +776,14 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL } startup_desktop = TRUE; - } else + } else { // create desktop window and task bar only, if there is no other shell and we are // the first explorer instance + // MS Explorer looks additionally into the registry entry HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\shell, + // to decide wether it is currently configured as shell application. startup_desktop = !any_desktop_running; + } + bool autostart = !any_desktop_running; @@ -795,6 +825,21 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL } #endif + + if (startup_desktop) { + // hide the XP login screen (Credit to Nicolas Escuder) + // another undocumented event: "Global\\msgina: ReturnToWelcome" + if (!SetShellReadyEvent(TEXT("msgina: ShellReadyEvent"))) + SetShellReadyEvent(TEXT("Global\\msgina: ShellReadyEvent")); + } + + if (!any_desktop_running) { + // launch the shell DDE server + if (g_SHDOCVW_ShellDDEInit) + (*g_SHDOCVW_ShellDDEInit)(TRUE); + } + + bool use_gdb_stub = false; // !IsDebuggerPresent(); if (_tcsstr(lpCmdLine,TEXT("-debug"))) @@ -827,17 +872,31 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL g_Globals.read_persistent(); if (startup_desktop) { + WaitCursor wait; + g_Globals._desktops.init(); g_Globals._hwndDesktop = DesktopWindow::Create(); #ifdef _USE_HDESK g_Globals._desktops.get_current_Desktop()->_hwndDesktop = g_Globals._hwndDesktop; #endif + } - if (autostart) { - char* argv[] = {"", "s"}; // call startup routine in SESSION_START mode - startup(2, argv); - } + if (g_Globals._hwndDesktop) + g_Globals._desktop_mode = true; + + Thread* pSSOThread = NULL; + + if (startup_desktop) { + // launch SSO thread to allow message processing independent from the explorer main thread + pSSOThread = new SSOThread; + pSSOThread->Start(); + } + + /**TODO launching autostart programs can be moved into a background thread. */ + if (autostart) { + char* argv[] = {"", "s"}; // call startup routine in SESSION_START mode + startup(2, argv); } /**TODO fix command line handling */ @@ -846,13 +905,21 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL lpCmdLine[_tcslen(lpCmdLine)-1] = '\0'; } - if (g_Globals._hwndDesktop) - g_Globals._desktop_mode = true; - int ret = explorer_main(hInstance, lpCmdLine, nShowCmd); // write configuration file g_Globals.write_persistent(); + if (pSSOThread) { + pSSOThread->Stop(); + delete pSSOThread; + } + + if (!any_desktop_running) { + // shutdown the shell DDE server + if (g_SHDOCVW_ShellDDEInit) + (*g_SHDOCVW_ShellDDEInit)(FALSE); + } + return ret; } diff --git a/reactos/subsys/system/explorer/explorer.dsp b/reactos/subsys/system/explorer/explorer.dsp index eb9763211db..8035c7f945e 100644 --- a/reactos/subsys/system/explorer/explorer.dsp +++ b/reactos/subsys/system/explorer/explorer.dsp @@ -702,11 +702,6 @@ SOURCE=.\shell\shellfs.h # End Source File # Begin Source File -SOURCE=.\shell\startup.c -# SUBTRACT CPP /YX /Yc /Yu -# End Source File -# Begin Source File - SOURCE=.\shell\unixfs.cpp # PROP Exclude_From_Build 1 # End Source File @@ -785,5 +780,22 @@ SOURCE=.\precomp.cpp SOURCE=.\precomp.h # End Source File # End Group +# Begin Group "services" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\services\shellservices.cpp +# End Source File +# Begin Source File + +SOURCE=.\services\shellservices.h +# End Source File +# Begin Source File + +SOURCE=.\services\startup.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# End Group # End Target # End Project diff --git a/reactos/subsys/system/explorer/explorer.xml b/reactos/subsys/system/explorer/explorer.xml index 525be8ac86e..3a4d3a8a511 100644 --- a/reactos/subsys/system/explorer/explorer.xml +++ b/reactos/subsys/system/explorer/explorer.xml @@ -1,4 +1,6 @@ + -luuid + -lstdc++ -fexceptions . include/expat @@ -10,9 +12,9 @@ 0x0501 0x0500 - uuid kernel32 gdi32 + user32 ws2_32 msimg32 comctl32 @@ -39,11 +41,14 @@ pane.cpp regfs.cpp shellbrowser.cpp - startup.c unixfs.cpp webchild.cpp winfs.cpp + + shellservices.cpp + startup.c + desktopbar.cpp favorites.cpp diff --git a/reactos/subsys/system/explorer/explorer_intres.h b/reactos/subsys/system/explorer/explorer_intres.h index 94f13f4737c..4bb9e8faf81 100644 --- a/reactos/subsys/system/explorer/explorer_intres.h +++ b/reactos/subsys/system/explorer/explorer_intres.h @@ -46,7 +46,7 @@ #define IDS_NOTIFY_AUTOHIDE 42 #define IDS_SHOW_HIDDEN_ICONS 43 #define IDS_HIDE_ICONS 44 -#define IDS_PROPERTIES_EXPLORER 45 +#define IDS_TERMINATE 45 #define IDI_REACTOS 100 #define IDI_EXPLORER 101 #define IDI_STARTMENU 102 diff --git a/reactos/subsys/system/explorer/explorer_intres.rc b/reactos/subsys/system/explorer/explorer_intres.rc index cd25356988f..e52dea5750c 100644 --- a/reactos/subsys/system/explorer/explorer_intres.rc +++ b/reactos/subsys/system/explorer/explorer_intres.rc @@ -126,6 +126,7 @@ BEGIN IDS_TITLE "ReactOS Explorer" IDS_START "Începe" IDS_LOGOFF "Închide sesiunea ..." + IDS_TERMINATE "Închide sesiunea" IDS_SHUTDOWN "Oprire calculator ..." IDS_LAUNCH "Pornire ..." IDS_START_HELP "Ajutor" @@ -583,6 +584,7 @@ BEGIN IDS_TITLE "Reactos Explorer" IDS_START "Start" IDS_LOGOFF "Abmelden..." + IDS_TERMINATE "ROS Explorer beenden" IDS_SHUTDOWN "Herunterfahren..." IDS_LAUNCH "Starten..." IDS_START_HELP "Hilfe" @@ -736,7 +738,7 @@ BEGIN BEGIN MENUITEM "&Next Level\t+", 301 MENUITEM "Expand &Tree\t*", 302 - MENUITEM "Expand &all\tStrg+*", 303 + MENUITEM "Expand &all\tCtrl+*", 303 MENUITEM "Collapse &Tree\t-", 304 MENUITEM SEPARATOR MENUITEM "&Mark Childs", 505 @@ -1075,6 +1077,7 @@ BEGIN IDS_TITLE "Reactos Explorer" IDS_START "Start" IDS_LOGOFF "Log Off..." + IDS_TERMINATE "Terminate ROS Explorer" IDS_SHUTDOWN "Turn Off..." IDS_LAUNCH "Run..." IDS_START_HELP "Help" @@ -1103,7 +1106,6 @@ BEGIN IDS_ALL_USERS "All Users\\" IDS_SEARCH "Search" IDS_ABOUT_EXPLORER "&About Explorer..." - IDS_PROPERTIES_EXPLORER "&Properties" IDS_LAUNCH_MANY_PROGRAMS "You have selected more than one program.\nAre you sure you want to launch all of them?" IDS_DESKTOPBAR_SETTINGS "Desktop Settings" @@ -1539,6 +1541,7 @@ BEGIN IDS_TITLE "Reactos Explorer" IDS_START "Iniciar" IDS_LOGOFF "Salir..." + IDS_TERMINATE "Salir" IDS_SHUTDOWN "Apagar..." IDS_LAUNCH "Ejecutar..." IDS_START_HELP "Ayuda" @@ -1709,6 +1712,7 @@ BEGIN IDS_TITLE "Explorateur Reactos" IDS_START "Démarrer" IDS_LOGOFF "Déconnexion ..." + IDS_TERMINATE "Déconnexion" IDS_SHUTDOWN "Arrêter..." IDS_LAUNCH "Exécuter..." IDS_START_HELP "Aide" @@ -1922,6 +1926,7 @@ BEGIN IDS_TITLE "Explorador do Reactos" IDS_START "Iniciar" IDS_LOGOFF "Terminar sessão..." + IDS_TERMINATE "Terminar ROS Explorador" IDS_SHUTDOWN "Desligar..." IDS_LAUNCH "Executar..." IDS_START_HELP "Ajuda" @@ -1977,6 +1982,497 @@ END #endif // Portuguese (Portugal) resources ///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// Swedish (SE) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_SWE) +#ifdef _WIN32 +LANGUAGE LANG_SWEDISH, SUBLANG_SWEDISH +#pragma code_page(850) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDM_MDIFRAME MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&Arkiv" + BEGIN + MENUITEM "&Avsluta", ID_FILE_EXIT + END + POPUP "&Visa" + BEGIN + MENUITEM "&Verktygsfält", ID_VIEW_TOOL_BAR + MENUITEM "&Extrafält", ID_VIEW_EXTRA_BAR + MENUITEM "E&nhetsfält", ID_VIEW_DRIVE_BAR, CHECKED + MENUITEM "S&idfält", ID_VIEW_SIDE_BAR + MENUITEM "&Statusfält", ID_VIEW_STATUSBAR + MENUITEM SEPARATOR + MENUITEM "&Uppdatera\tF5", ID_REFRESH + MENUITEM "F&ullskärm\tCtrl+Shift+S", ID_VIEW_FULLSCREEN + MENUITEM "SDI", ID_VIEW_SDI + END + POPUP "&Fönster" + BEGIN + MENUITEM "Nytt &fönster", ID_WINDOW_NEW + MENUITEM "Överlappande\tShift+F5", ID_WINDOW_CASCADE + MENUITEM "Ordna &horizontellt", ID_WINDOW_TILE_HORZ + MENUITEM "Ordna &vertikalt\tShift+F4", ID_WINDOW_TILE_VERT + MENUITEM "Ordna automatiskt", ID_WINDOW_AUTOSORT + MENUITEM "Ordna &symboler", ID_WINDOW_ARRANGE + END + POPUP "&Verktyg" + BEGIN + MENUITEM "&Alternativ", ID_TOOLS_OPTIONS + END + POPUP "&Hjälp" + BEGIN + MENUITEM "Explorer &FAQ...", ID_EXPLORER_FAQ + MENUITEM "&Om Explorer...", ID_ABOUT_EXPLORER + MENUITEM "Om &OS...", ID_ABOUT_WINDOWS + END +END + +IDM_WINEFILE MENU FIXED IMPURE +BEGIN + POPUP "&Arkiv" + BEGIN + MENUITEM "&Öppna\tEnter", 101 + MENUITEM "&Flytta...\tF7", 106 + MENUITEM "&Kopiera...\tF8", 107 + MENUITEM "&I urklipp...\tF9", 118 + MENUITEM "&Ta bort\tDel", 108 + MENUITEM "&Byt namn...", 109 + MENUITEM "&Egenskaper...\tAlt+Enter", ID_EDIT_PROPERTIES + MENUITEM SEPARATOR + MENUITEM "&Komprimera...", 119 + MENUITEM "Packa &upp...", 120 + MENUITEM SEPARATOR + MENUITEM "&Kör...", ID_EXECUTE + MENUITEM "&Skriv ut...", 102 + MENUITEM "Associera...", 103 + MENUITEM SEPARATOR + MENUITEM "Skapa mapp...", ID_EXECUTE + MENUITEM "&Sök...", 104 + MENUITEM "&Välj filer...", 116 + MENUITEM SEPARATOR + MENUITEM "&Avsluta\tAlt+X", ID_FILE_EXIT + END + POPUP "&Disk" + BEGIN + MENUITEM "&Kopiera disk...", 201 + MENUITEM "&Namnge disk...", 202 + MENUITEM SEPARATOR + MENUITEM "&Formatera disk...", 203 + MENUITEM SEPARATOR + MENUITEM "Anslut &Nätverksenhet", 252 + MENUITEM "&Koppla ifrån Nätverksenhet", 253 + MENUITEM SEPARATOR + MENUITEM "Dela ut...", 254 + MENUITEM "&Ta bort utdelning...", 255 + MENUITEM SEPARATOR + MENUITEM "&Välj enhet...", 251 + END + POPUP "&Mappar" + BEGIN + MENUITEM "&Nästa nivå\t+", 301 + MENUITEM "Utöka &träd\t*", 302 + MENUITEM "Utöka &alla\tCtrl+*", 303 + MENUITEM "Kollapsa &träd\t-", 304 + MENUITEM SEPARATOR + MENUITEM "&Markera undermappar", 505 + END + POPUP "&Visa" + BEGIN + MENUITEM "&Träd och mapp", 413 + MENUITEM "Bara tr&äd", 411 + MENUITEM "Bara &mapp", 412 + MENUITEM SEPARATOR + MENUITEM "De&la", 414 + MENUITEM SEPARATOR + MENUITEM "&Namn", ID_VIEW_NAME + MENUITEM "&Alla Fildetaljer", ID_VIEW_ALL_ATTRIBUTES + , CHECKED + MENUITEM "&Vissa detaljer...", ID_VIEW_SELECTED_ATTRIBUTES + MENUITEM SEPARATOR + MENUITEM "&Sortera efter namn", 404 + MENUITEM "Sortera efter t&yp", 405 + MENUITEM "Sortera efter st&orlek", 406 + MENUITEM "Sortera efter &datum", 407 + MENUITEM SEPARATOR + MENUITEM "Sortera efter &...", 409 + END + POPUP "&Inställningar" + BEGIN + MENUITEM "&Bekräftelse...", 65535 + MENUITEM "&Teckensnitt...", 65535 + MENUITEM "&Konfigurera verktygsfält...", 65535 + MENUITEM SEPARATOR + MENUITEM "&Verktygsfält", ID_VIEW_TOOL_BAR, CHECKED + MENUITEM "&Enhetsfält", ID_VIEW_DRIVE_BAR, CHECKED + MENUITEM "&Sidfält", ID_VIEW_SIDE_BAR + MENUITEM "St&atusfält", ID_VIEW_STATUSBAR, CHECKED + MENUITEM "F&ullskärm\tCtrl+Shift+S", ID_VIEW_FULLSCREEN + MENUITEM SEPARATOR + MENUITEM "&Symbol efter körning", 65535 + MENUITEM "&Spara ändringar vid stängning", 511 + END + POPUP "&Säkerhet" + BEGIN + MENUITEM "&Åtkomst...", 605 + MENUITEM "&Loggning...", 606 + MENUITEM "&Ägare...", 607 + END + POPUP "&Fönster" + BEGIN + MENUITEM "Nytt &fönster", ID_WINDOW_NEW + MENUITEM "Överlappande\tCtrl+F5", ID_WINDOW_CASCADE + MENUITEM "Ordna &horizontellt", ID_WINDOW_TILE_HORZ + MENUITEM "Ordna &vertikalt\tCtrl+F4", ID_WINDOW_TILE_VERT + MENUITEM "Ordna automatiskt", ID_WINDOW_AUTOSORT + MENUITEM "Ordna &symboler", ID_WINDOW_ARRANGE + MENUITEM "&Uppdatera\tF5", ID_REFRESH + END + POPUP "&?" + BEGIN + MENUITEM "&Hjälpavsnitt\tF1", ID_HELP + MENUITEM "Hjälp &sök...\tF1", ID_HELP + MENUITEM "&Använda hjälpen\tF1", ID_HELP_USING + MENUITEM SEPARATOR + MENUITEM "Information om &Winefile...", ID_ABOUT_WINEFILE + END +END + +IDM_DESKTOPBAR MENU DISCARDABLE +BEGIN + POPUP "" + BEGIN + MENUITEM "&Inställningar...", ID_DESKTOPBAR_SETTINGS + MENUITEM "&Aktivitetshanteraren...", ID_TASKMGR + MENUITEM SEPARATOR + MENUITEM "&Om Explorer...", ID_ABOUT_EXPLORER + END +END + +IDM_VOLUME MENU DISCARDABLE +BEGIN + POPUP "" + BEGIN + MENUITEM "Öppna volymkontroll", ID_TRAY_VOLUME + MENUITEM "Justera ljudinställningar", ID_VOLUME_PROPERTIES + END +END + +IDM_NOTIFYAREA MENU DISCARDABLE +BEGIN + POPUP "" + BEGIN + MENUITEM "&Visa dolda ikoner", ID_SHOW_HIDDEN_ICONS + MENUITEM "Visa &ikonknapp", ID_SHOW_ICON_BUTTON + MENUITEM "&Konfigurera meddelanden...", ID_CONFIG_NOTIFYAREA + MENUITEM "Justera &datum och &tid...", ID_CONFIG_TIME + MENUITEM SEPARATOR + MENUITEM "&Om Explorer...", ID_ABOUT_EXPLORER + END +END + +IDM_SDIFRAME MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&Arkiv" + BEGIN + MENUITEM "&Avsluta", ID_FILE_EXIT + END + POPUP "&Visa" + BEGIN + MENUITEM "&Verktygsfält", ID_VIEW_TOOL_BAR + MENUITEM "&Sidfält", ID_VIEW_SIDE_BAR, GRAYED + MENUITEM "&Statusfält", ID_VIEW_STATUSBAR + MENUITEM SEPARATOR + MENUITEM "&Uppdatera\tF5", ID_REFRESH + MENUITEM "&Fullskärm\tCtrl+Shift+S", ID_VIEW_FULLSCREEN + MENUITEM "&MDI", ID_VIEW_MDI + END + POPUP "&Verktyg" + BEGIN + MENUITEM "&Alternativ", ID_TOOLS_OPTIONS + END + POPUP "&Hjälp" + BEGIN + MENUITEM "Explorer &FAQ...", ID_EXPLORER_FAQ + MENUITEM "&Om Explorer...", ID_ABOUT_EXPLORER + MENUITEM "Om &OS...", ID_ABOUT_WINDOWS + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_EXECUTE DIALOG FIXED IMPURE 15, 13, 210, 63 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Kör" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "",101,"Static",SS_SIMPLE | SS_NOPREFIX,3,6,162,10 + CONTROL "&Kommando:",-1,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,3, + 18,60,10 + EDITTEXT 201,3,29,134,12,ES_AUTOHSCROLL + CONTROL "&Som symbol",214,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3, + 45,71,12 + DEFPUSHBUTTON "&OK",1,158,6,47,14 + PUSHBUTTON "&Avbryt",2,158,23,47,14 + PUSHBUTTON "&Hjälp",254,158,43,47,14 +END + +IDD_SEARCH_PROGRAM DIALOGEX 0, 0, 200, 65 +STYLE WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "Sök program på startmenyn" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "&Filter:",IDC_STATIC,7,9,18,8 + EDITTEXT IDC_FILTER,34,7,100,14,ES_AUTOHSCROLL + CONTROL "List1",IDC_PROGRAMS_FOUND,"SysListView32",LVS_REPORT | + LVS_SHOWSELALWAYS | LVS_SORTASCENDING | WS_BORDER | + WS_TABSTOP,7,25,186,33 + PUSHBUTTON "&Kontrollera poster",IDC_CHECK_ENTRIES,143,7,50,14 +END + +IDD_DESKBAR_DESKTOP DIALOG DISCARDABLE 0, 0, 212, 194 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Skrivbordsinställningar" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Välj från vilket hörn ock åt vilket håll du föredrar att rada upp ikonerna:", + IDC_STATIC,7,7,166,8 + CONTROL "vä. och neråt",IDC_ICON_ALIGN_0,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,7,25,46,44 + CONTROL "övre åt hö.",IDC_ICON_ALIGN_1,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,57,25,46,44 + CONTROL "övre åt vä.",IDC_ICON_ALIGN_2,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,110,25,46,44 + CONTROL "hö. och neråt",IDC_ICON_ALIGN_3,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,159,25,46,44 + CONTROL "vä. och uppåt",IDC_ICON_ALIGN_4,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,7,73,46,44 + CONTROL "nedre åt hö.",IDC_ICON_ALIGN_5,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,57,73,46,44 + CONTROL "nedre åt vä.",IDC_ICON_ALIGN_6,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,110,73,46,44 + CONTROL "hö. och uppåt",IDC_ICON_ALIGN_7,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,159,73,46,44 + CONTROL "sidor och ner",IDC_ICON_ALIGN_8,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,7,121,46,44 + CONTROL "kanter",IDC_ICON_ALIGN_9,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,57,121,46,44 + CONTROL "runt om",IDC_ICON_ALIGN_10,"Button",BS_OWNERDRAW | + BS_BOTTOM | WS_TABSTOP,110,121,46,44 + CONTROL "",IDC_ICON_ALIGN_11,"Button",BS_OWNERDRAW | BS_BOTTOM | + WS_TABSTOP,159,121,46,44 + CONTROL "Visa &versionsnummer",ID_DESKTOP_VERSION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,177,91,10 +END + +IDD_DESKBAR_TASKBAR DIALOG DISCARDABLE 0, 0, 210, 194 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Aktivitetsfältsinställningar" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "visa &klockan",ID_SHOW_CLOCK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,152,52,10 + CONTROL "&Göm inaktiva meddelandeikoner", + ID_HIDE_INACTIVE_ICONS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,174,111,10 + PUSHBUTTON "&Meddelanden...",ID_CONFIG_NOTIFYAREA,153,173,50,14 +END + +IDD_DESKBAR_STARTMENU DIALOG DISCARDABLE 0, 0, 210, 194 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Startmenyinställningar" +FONT 8, "MS Sans Serif" +BEGIN +END + +IDD_NOTIFYAREA DIALOGEX 0, 0, 208, 174 +STYLE WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "Inställningar för meddelandeikoner" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Tree1",IDC_NOTIFY_ICONS,"SysTreeView32",TVS_HASLINES | + TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,7,194,31 + LTEXT "&Hjälptext:",IDC_LABEL1,7,44,40,8 + EDITTEXT IDC_NOTIFY_TOOLTIP,58,42,143,14,ES_AUTOHSCROLL + LTEXT "&Fönstertitel:",IDC_LABEL2,7,63,44,8 + EDITTEXT IDC_NOTIFY_TITLE,58,60,143,14,ES_AUTOHSCROLL + LTEXT "&Modulsökväg:",IDC_LABEL3,7,81,43,8 + EDITTEXT IDC_NOTIFY_MODULE,58,78,143,14,ES_AUTOHSCROLL + GROUPBOX "&Visningsläge",IDC_LABEL4,7,96,157,28 + CONTROL "v&isa",IDC_NOTIFY_SHOW,"Button",BS_AUTORADIOBUTTON | + WS_TABSTOP,15,108,33,10 + CONTROL "&dölj",IDC_NOTIFY_HIDE,"Button",BS_AUTORADIOBUTTON,66, + 108,29,10 + CONTROL "dölj a&utomatiskt",IDC_NOTIFY_AUTOHIDE,"Button", + BS_AUTORADIOBUTTON,112,108,43,10 + ICON "",IDC_PICTURE,173,101,21,20 + LTEXT "&Senast ändrad:",IDC_LABEL6,7,132,43,8 + EDITTEXT IDC_LAST_CHANGE,59,129,105,14,ES_AUTOHSCROLL | + ES_READONLY + CONTROL "visa d&olda",ID_SHOW_HIDDEN_ICONS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,154,56,10 + DEFPUSHBUTTON "&OK",IDOK,91,153,50,14,WS_GROUP + PUSHBUTTON "&Avbryt",IDCANCEL,151,153,50,14 +END + +IDD_MDI_SDI DIALOGEX 0, 0, 188, 126 +STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "Välj MDI / SDI Läge" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Välj det gränssnitt du föredrar:", + IDC_STATIC,7,7,160,8 + CONTROL "&MDI (multiple document interface)",IDC_MDI,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,7,30,121,10 + CONTROL "&SDI (single document interface)",IDC_SDI,"Button", + BS_AUTORADIOBUTTON,7,53,115,10 + LTEXT "Den här inställningen kommer att gälla som standard i alla nya explorer-fönster.", + IDC_STATIC,7,76,174,22 + DEFPUSHBUTTON "&OK",IDOK,29,105,50,14,WS_GROUP + PUSHBUTTON "&Avbryt",IDCANCEL,106,105,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_SEARCH_PROGRAM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 193 + TOPMARGIN, 7 + BOTTOMMARGIN, 58 + END + + IDD_DESKBAR_DESKTOP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 205 + TOPMARGIN, 7 + BOTTOMMARGIN, 187 + END + + IDD_DESKBAR_TASKBAR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 203 + TOPMARGIN, 7 + BOTTOMMARGIN, 187 + END + + IDD_DESKBAR_STARTMENU, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 203 + TOPMARGIN, 7 + BOTTOMMARGIN, 187 + END + + IDD_NOTIFYAREA, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 201 + TOPMARGIN, 7 + BOTTOMMARGIN, 167 + END + + IDD_MDI_SDI, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 181 + TOPMARGIN, 7 + BOTTOMMARGIN, 119 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_TITLE "Reactos Explorer" + IDS_START "Start" + IDS_LOGOFF "Logga ut..." + IDS_TERMINATE "Logga ut" + IDS_SHUTDOWN "Stäng av..." + IDS_LAUNCH "Kör..." + IDS_START_HELP "Hjälp" + IDS_SEARCH_FILES "Sök..." + IDS_DOCUMENTS "Dokument" + IDS_FAVORITES "Favoriter" + IDS_PROGRAMS "Program" + IDS_SETTINGS "Inställningar" + IDS_EXPLORE "Utforska" + IDS_EMPTY "(tom)" + IDS_RECENT "Senaste dokumenten" + IDS_ADMIN "Administration" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_NETWORK "Nätverk" + IDS_CONNECTIONS "Nätverksanslutningar" + IDS_DRIVES "Enheter" + IDS_SEARCH_COMPUTER "Sök dator..." + IDS_SETTINGS_MENU "Inställningsmeny" + IDS_CONTROL_PANEL "Kontrollpanelen" + IDS_PRINTERS "Skrivare" + IDS_BROWSE "Utforska filer" + IDS_SEARCH_PRG "Sök program..." + IDS_ALL_USERS "Alla användare\\" + IDS_SEARCH "Sök" + IDS_ABOUT_EXPLORER "&Om Explorer..." + IDS_LAUNCH_MANY_PROGRAMS + "Du har valt fler än ett program.\nÄr du säker på att du vill öppna dem alla?" + IDS_DESKTOPBAR_SETTINGS "Skrivbordsinställningar" + IDS_DESKTOP "Skrivbord" + IDS_TASKBAR "Aktivitetsfält" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_STARTMENU "Startmeny" + IDS_MINIMIZE_ALL "minimera alla fönster" + IDS_DESKTOP_NUM "Skrivbord %d" + IDS_VOLUME "Volym" + IDS_ITEMS_CUR "aktuella meddelanden" + IDS_ITEMS_CONFIGURED "konfiguration" + IDS_ITEMS_VISIBLE "synlig" + IDS_ITEMS_HIDDEN "dold" + IDS_NOTIFY_SHOW "visa" + IDS_NOTIFY_HIDE "dölj" + IDS_NOTIFY_AUTOHIDE "dölj automatiskt" + IDS_SHOW_HIDDEN_ICONS "Visa dolda ikoner" + IDS_HIDE_ICONS "Dölj ikoner" +END + +#endif // Swedish (SE) resources +///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED diff --git a/reactos/subsys/system/explorer/externals.h b/reactos/subsys/system/explorer/externals.h index eee42742ece..50e7650868c 100644 --- a/reactos/subsys/system/explorer/externals.h +++ b/reactos/subsys/system/explorer/externals.h @@ -31,9 +31,6 @@ extern "C" { #endif - // launch start programs -extern int startup(int argc, char *argv[]); - // explorer main routine extern int explorer_main(HINSTANCE hinstance, LPTSTR lpCmdLine, int cmdshow); diff --git a/reactos/subsys/system/explorer/make_explorer.dsp b/reactos/subsys/system/explorer/make_explorer.dsp index ac74928ae8c..ad364110d4d 100644 --- a/reactos/subsys/system/explorer/make_explorer.dsp +++ b/reactos/subsys/system/explorer/make_explorer.dsp @@ -183,10 +183,6 @@ CFG=make_explorer - Win32 bjam # Begin Source File -SOURCE=..\..\..\ChangeLog -# End Source File -# Begin Source File - SOURCE=.\Jamfile # End Source File # Begin Source File diff --git a/reactos/subsys/system/explorer/res/computer.ico b/reactos/subsys/system/explorer/res/computer.ico index beb28a65a33..d62870d0837 100644 Binary files a/reactos/subsys/system/explorer/res/computer.ico and b/reactos/subsys/system/explorer/res/computer.ico differ diff --git a/reactos/subsys/system/explorer/res/explorer.ico b/reactos/subsys/system/explorer/res/explorer.ico index 8aa5104e7cc..09e4a52d122 100644 Binary files a/reactos/subsys/system/explorer/res/explorer.ico and b/reactos/subsys/system/explorer/res/explorer.ico differ diff --git a/reactos/subsys/system/explorer/res/folder.ico b/reactos/subsys/system/explorer/res/folder.ico index c8cb74f3fac..11ac83d299a 100644 Binary files a/reactos/subsys/system/explorer/res/folder.ico and b/reactos/subsys/system/explorer/res/folder.ico differ diff --git a/reactos/subsys/system/explorer/res/logov.bmp b/reactos/subsys/system/explorer/res/logov.bmp index 58e1603c536..6e9b865f1a2 100644 Binary files a/reactos/subsys/system/explorer/res/logov.bmp and b/reactos/subsys/system/explorer/res/logov.bmp differ diff --git a/reactos/subsys/system/explorer/res/reactos.ico b/reactos/subsys/system/explorer/res/reactos.ico index 9de75081b22..16cebc5698d 100644 Binary files a/reactos/subsys/system/explorer/res/reactos.ico and b/reactos/subsys/system/explorer/res/reactos.ico differ diff --git a/reactos/subsys/system/explorer/res/ros-big.ico b/reactos/subsys/system/explorer/res/ros-big.ico index 7721efe24bd..4c9642ee731 100644 Binary files a/reactos/subsys/system/explorer/res/ros-big.ico and b/reactos/subsys/system/explorer/res/ros-big.ico differ diff --git a/reactos/subsys/system/explorer/res/startmenu.ico b/reactos/subsys/system/explorer/res/startmenu.ico index 4dbf601bbae..52411bf87e9 100644 Binary files a/reactos/subsys/system/explorer/res/startmenu.ico and b/reactos/subsys/system/explorer/res/startmenu.ico differ diff --git a/reactos/subsys/system/explorer/services/shellservices.cpp b/reactos/subsys/system/explorer/services/shellservices.cpp new file mode 100644 index 00000000000..4b47ce87423 --- /dev/null +++ b/reactos/subsys/system/explorer/services/shellservices.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2005 Martin Fuchs + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + // + // Explorer clone + // + // shellservices.cpp + // + // Martin Fuchs, 28.03.2005 + // + + +#include "precomp.h" + +#include "shellservices.h" + + +int SSOThread::Run() +{ + ComInit usingCOM(COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE|COINIT_SPEED_OVER_MEMORY); + + HKEY hkey; + CLSID clsid; + WCHAR name[MAX_PATH], value[MAX_PATH]; + + typedef vector*> SSOVector; + SSOVector sso_ptrs; + + if (!RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ShellServiceObjectDelayLoad"), &hkey)) { + for(int idx=0; ; ++idx) { + DWORD name_len = MAX_PATH; + DWORD value_len = sizeof(value); + + if (RegEnumValueW(hkey, idx, name, &name_len, 0, NULL, (LPBYTE)&value, &value_len)) + break; + + if (!_alive) + break; + + SIfacePtr* sso_ptr = new SIfacePtr; + + if (CLSIDFromString(value, &clsid) == NOERROR) { + if (SUCCEEDED(sso_ptr->CreateInstance(clsid, IID_IOleCommandTarget))) { + if (SUCCEEDED((*sso_ptr)->Exec(&CGID_ShellServiceObject, OLECMDID_NEW, OLECMDEXECOPT_DODEFAULT, NULL, NULL))) + sso_ptrs.push_back(sso_ptr); + } + } + } + + RegCloseKey(hkey); + } + + if (!sso_ptrs.empty()) { + MSG msg; + + while(_alive) { + if (MsgWaitForMultipleObjects(1, &_evtFinish, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0+0) + break; // _evtFinish has been set. + + while(_alive) { + if (!PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + break; + + if (msg.message == WM_QUIT) + break; + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + // shutdown all running Shell Service Objects + for(SSOVector::iterator it=sso_ptrs.begin(); it!=sso_ptrs.end(); ++it) { + SIfacePtr* sso_ptr = *it; + (*sso_ptr)->Exec(&CGID_ShellServiceObject, OLECMDID_SAVE, OLECMDEXECOPT_DODEFAULT, NULL, NULL); + delete sso_ptr; + } + } + + return 0; +} diff --git a/reactos/subsys/system/explorer/bak/shellhook.h b/reactos/subsys/system/explorer/services/shellservices.h similarity index 64% rename from reactos/subsys/system/explorer/bak/shellhook.h rename to reactos/subsys/system/explorer/services/shellservices.h index 620735b58f6..ecda2819069 100644 --- a/reactos/subsys/system/explorer/bak/shellhook.h +++ b/reactos/subsys/system/explorer/services/shellservices.h @@ -1,5 +1,5 @@ /* - * Copyright 2003 Martin Fuchs + * Copyright 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,27 +18,19 @@ // - // Explorer and Desktop clone + // Explorer clone // - // shellhook.h + // shellservices.h // - // Martin Fuchs, 17.08.2003 + // Martin Fuchs, 28.03.2005 // -#ifdef _SHELLHOOK_IMPL -#define DECL_SHELLHOOK __declspec(dllexport) -#else -#define DECL_SHELLHOOK __declspec(dllimport) -#endif + // launch start programs +extern "C" int startup(int argc, char *argv[]); -#ifdef __cplusplus -extern "C" { -#endif - -DECL_SHELLHOOK void InstallShellHook(HWND callback_hwnd, UINT callback_msg); -DECL_SHELLHOOK void DeinstallShellHook(); - -#ifdef __cplusplus + // load Shell Service Objects (volume control, printer/network icons, ...) +struct SSOThread : public Thread +{ + int Run(); }; -#endif diff --git a/reactos/subsys/system/explorer/shell/startup.c b/reactos/subsys/system/explorer/services/startup.c similarity index 100% rename from reactos/subsys/system/explorer/shell/startup.c rename to reactos/subsys/system/explorer/services/startup.c diff --git a/reactos/subsys/system/explorer/shell/entries.cpp b/reactos/subsys/system/explorer/shell/entries.cpp index 4f12f01e474..3dfff9ea5cc 100644 --- a/reactos/subsys/system/explorer/shell/entries.cpp +++ b/reactos/subsys/system/explorer/shell/entries.cpp @@ -323,7 +323,7 @@ void Entry::extract_icon() ICON_ID icon_id = ICID_NONE; - if (get_path(path)) + if (get_path(path) && _tcsncmp(path,TEXT("::{"),3)) icon_id = g_Globals._icon_cache.extract(path); if (icon_id == ICID_NONE) { diff --git a/reactos/subsys/system/explorer/shell/mainframe.cpp b/reactos/subsys/system/explorer/shell/mainframe.cpp index 10699aa7b6d..e0462659742 100644 --- a/reactos/subsys/system/explorer/shell/mainframe.cpp +++ b/reactos/subsys/system/explorer/shell/mainframe.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2003, 2004 Martin Fuchs + * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -40,19 +40,19 @@ extern HWND create_webchildwindow(const WebChildWndInfo& info); HWND MainFrameBase::Create(LPCTSTR path, bool mdi, UINT cmdshow) { - HWND hMainFrame; + HWND hFrame; #ifndef _NO_MDI ///@todo implement command line option to switch between MDI and SDI if (mdi) - hMainFrame = MDIMainFrame::Create(); + hFrame = MDIMainFrame::Create(); else #endif - hMainFrame = SDIMainFrame::Create(); + hFrame = SDIMainFrame::Create(); - if (hMainFrame) { + if (hFrame) { HWND hwndOld = g_Globals._hMainWnd; - g_Globals._hMainWnd = hMainFrame; + g_Globals._hMainWnd = hFrame; if (path) { static String sPath = path; // copy path to avoid accessing freed memory @@ -62,8 +62,8 @@ HWND MainFrameBase::Create(LPCTSTR path, bool mdi, UINT cmdshow) if (hwndOld) DestroyWindow(hwndOld); - ShowWindow(hMainFrame, cmdshow); - UpdateWindow(hMainFrame); + ShowWindow(hFrame, cmdshow); + UpdateWindow(hFrame); bool valid_dir = false; @@ -78,12 +78,12 @@ HWND MainFrameBase::Create(LPCTSTR path, bool mdi, UINT cmdshow) // Open the first child window after initializing the application if (valid_dir) - PostMessage(hMainFrame, PM_OPEN_WINDOW, 0, (LPARAM)path); + PostMessage(hFrame, PM_OPEN_WINDOW, 0, (LPARAM)path); else - PostMessage(hMainFrame, PM_OPEN_WINDOW, OWM_EXPLORE|OWM_DETAILS, 0); + PostMessage(hFrame, PM_OPEN_WINDOW, OWM_EXPLORE|OWM_DETAILS, 0); } - return hMainFrame; + return hFrame; } @@ -736,7 +736,6 @@ MDIMainFrame::MDIMainFrame(HWND hwnd) extraBtns.idCommand = ID_WEB_WINDOW; SendMessage(_hextrabar, TB_INSERTBUTTON, INT_MAX, (LPARAM)&extraBtns); -#define W_VER_NT 0 if ((HIWORD(GetVersion())>>14) == W_VER_NT) { // insert NT object namespace button extraBtns.iString = SendMessage(_hextrabar, TB_ADDSTRING, 0, (LPARAM)TEXT("NT Obj\0")); @@ -843,34 +842,34 @@ HWND MDIMainFrame::Create() HWND MDIMainFrame::Create(LPCTSTR path, int mode) { - HWND hMainFrame = Create(); - if (!hMainFrame) + HWND hFrame = Create(); + if (!hFrame) return 0; - ShowWindow(hMainFrame, SW_SHOW); + ShowWindow(hFrame, SW_SHOW); - MDIMainFrame* pMainFrame = GET_WINDOW(MDIMainFrame, hMainFrame); + MDIMainFrame* pMainFrame = GET_WINDOW(MDIMainFrame, hFrame); if (pMainFrame) pMainFrame->CreateChild(path, mode); - return hMainFrame; + return hFrame; } HWND MDIMainFrame::Create(LPCITEMIDLIST pidl, int mode) { - HWND hMainFrame = Create(); - if (!hMainFrame) + HWND hFrame = Create(); + if (!hFrame) return 0; - ShowWindow(hMainFrame, SW_SHOW); + ShowWindow(hFrame, SW_SHOW); - MDIMainFrame* pMainFrame = GET_WINDOW(MDIMainFrame, hMainFrame); + MDIMainFrame* pMainFrame = GET_WINDOW(MDIMainFrame, hFrame); if (pMainFrame) pMainFrame->CreateChild(pidl, mode); - return hMainFrame; + return hFrame; } @@ -1316,6 +1315,22 @@ HWND SDIMainFrame::Create(LPCITEMIDLIST pidl, int mode) return hFrame; } +HWND SDIMainFrame::Create(LPCTSTR path, int mode) +{ + HWND hFrame = Create(); + if (!hFrame) + return 0; + + ShowWindow(hFrame, SW_SHOW); + + MDIMainFrame* pMainFrame = GET_WINDOW(MDIMainFrame, hFrame); + + if (pMainFrame) + pMainFrame->CreateChild(path, mode); + + return hFrame; +} + LRESULT SDIMainFrame::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) { switch(nmsg) { @@ -1637,17 +1652,16 @@ void SDIMainFrame::jump_to(LPCTSTR path, int mode) void SDIMainFrame::jump_to(LPCITEMIDLIST path, int mode) { -/*@@todo if (_shellBrowser.get() && (_shellpath_info._open_mode&~OWM_PIDL)==(mode&~OWM_PIDL)) { ShellPath shell_path = path; _shellBrowser->jump_to(shell_path); _shellpath_info._shell_path = shell_path; - } else */{ + } else { _shellpath_info._open_mode = mode; _shellpath_info._shell_path = path; - _shellpath_info._root_shell_path = DesktopFolderPath(); //@@ + _shellpath_info._root_shell_path = SpecialFolderPath(CSIDL_DRIVES, _hwnd); //@@ update_shell_browser(); } diff --git a/reactos/subsys/system/explorer/shell/mainframe.h b/reactos/subsys/system/explorer/shell/mainframe.h index 5cdc3206ce5..f8e02cdf0fc 100644 --- a/reactos/subsys/system/explorer/shell/mainframe.h +++ b/reactos/subsys/system/explorer/shell/mainframe.h @@ -135,6 +135,7 @@ struct SDIMainFrame : public ExtContextMenuHandlerT< SDIMainFrame(HWND hwnd); static HWND Create(); + static HWND Create(LPCTSTR path, int mode=OWM_EXPLORE|OWM_DETAILS); static HWND Create(LPCITEMIDLIST pidl, int mode=OWM_EXPLORE|OWM_DETAILS|OWM_PIDL); protected: diff --git a/reactos/subsys/system/explorer/shell/pane.cpp b/reactos/subsys/system/explorer/shell/pane.cpp index 975379939b5..ea72acbee3b 100644 --- a/reactos/subsys/system/explorer/shell/pane.cpp +++ b/reactos/subsys/system/explorer/shell/pane.cpp @@ -163,7 +163,7 @@ void Pane::init() HKEY hkeyExplorer = 0; DWORD len = sizeof(_clrCompressed); - if (RegOpenKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), &hkeyExplorer) || + if (RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer"), &hkeyExplorer) || RegQueryValueEx(hkeyExplorer, TEXT("AltColor"), 0, NULL, (LPBYTE)&_clrCompressed, &len) || len!=sizeof(_clrCompressed)) _clrCompressed = RGB(0,0,255); diff --git a/reactos/subsys/system/explorer/shell/regfs.cpp b/reactos/subsys/system/explorer/shell/regfs.cpp index e804dfdaaaa..1f6506d62f0 100644 --- a/reactos/subsys/system/explorer/shell/regfs.cpp +++ b/reactos/subsys/system/explorer/shell/regfs.cpp @@ -43,9 +43,9 @@ void RegDirectory::read_directory(int scan_flags) _tcscpy(buffer, (LPCTSTR)_path); LPTSTR pname = buffer + _tcslen(buffer); - HKEY hKey; + HKEY hkey; - if (!RegOpenKeyEx(_hKeyRoot, *buffer=='\\'?buffer+1:buffer, 0, STANDARD_RIGHTS_READ|KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, &hKey)) { + if (!RegOpenKeyEx(_hKeyRoot, *buffer=='\\'?buffer+1:buffer, 0, STANDARD_RIGHTS_READ|KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, &hkey)) { if (pname[-1] != '\\') *pname++ = '\\'; @@ -60,7 +60,7 @@ void RegDirectory::read_directory(int scan_flags) DWORD name_len = MAX_PATH; DWORD class_len = MAX_PATH; - if (RegEnumKeyEx(hKey, idx, name, &name_len, 0, class_name, &class_len, &w32fd.ftLastWriteTime)) + if (RegEnumKeyEx(hkey, idx, name, &name_len, 0, class_name, &class_len, &w32fd.ftLastWriteTime)) break; w32fd.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; @@ -88,7 +88,7 @@ void RegDirectory::read_directory(int scan_flags) TCHAR value[MAX_PATH]; LONG value_len = sizeof(value); - if (!RegQueryValue(hKey, NULL, value, &value_len) && value_len>1) { + if (!RegQueryValue(hkey, NULL, value, &value_len) && value_len>1) { memset(&w32fd, 0, sizeof(WIN32_FIND_DATA)); lstrcpy(w32fd.cFileName, TEXT("(Default)")); @@ -114,7 +114,7 @@ void RegDirectory::read_directory(int scan_flags) for(int idx=0; ; ++idx) { DWORD name_len = MAX_PATH; - if (RegEnumValue(hKey, idx, name, &name_len, 0, &type, NULL, NULL)) + if (RegEnumValue(hkey, idx, name, &name_len, 0, &type, NULL, NULL)) break; memset(&w32fd, 0, sizeof(WIN32_FIND_DATA)); @@ -147,7 +147,7 @@ void RegDirectory::read_directory(int scan_flags) TCHAR value[MAX_PATH]; DWORD value_len = sizeof(value); - if (!RegQueryValueEx(hKey, name, NULL, NULL, (LPBYTE)value, &value_len)) { + if (!RegQueryValueEx(hkey, name, NULL, NULL, (LPBYTE)value, &value_len)) { if (type==REG_SZ || type==REG_EXPAND_SZ || type==REG_LINK) entry->_content = _tcsdup(value); else if (type == REG_DWORD) { @@ -171,7 +171,7 @@ void RegDirectory::read_directory(int scan_flags) if (last) last->_next = NULL; - RegCloseKey(hKey); + RegCloseKey(hkey); } _down = first_entry; diff --git a/reactos/subsys/system/explorer/shell/shellbrowser.cpp b/reactos/subsys/system/explorer/shell/shellbrowser.cpp index 91a4dd45e07..61bc2ddc5cb 100644 --- a/reactos/subsys/system/explorer/shell/shellbrowser.cpp +++ b/reactos/subsys/system/explorer/shell/shellbrowser.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2003, 2004 Martin Fuchs + * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,6 +50,8 @@ ShellBrowser::ShellBrowser(HWND hwnd, HWND left_hwnd, WindowHandle& right_hwnd, ShellBrowser::~ShellBrowser() { + TreeView_SetImageList(_left_hwnd, 0, TVSIL_NORMAL); + if (_pShellView) _pShellView->Release(); @@ -71,7 +73,7 @@ LRESULT ShellBrowser::Init(HWND hWndFrame) _hWndFrame = hWndFrame; - const String& root_name = GetDesktopFolder().get_name(_create_info._root_shell_path, SHGDN_FORPARSING); + const String& root_name = GetDesktopFolder().get_name(_create_info._root_shell_path, SHGDN_FORADDRESSBAR); _root._drive_type = DRIVE_UNKNOWN; lstrcpy(_root._volname, root_name); @@ -483,6 +485,9 @@ MDIShellBrowserChild::MDIShellBrowserChild(HWND hwnd, const ShellChildWndInfo& i _shellpath_info(info) //@@ copies info -> no referenz to _create_info ! { /**todo Conversion of shell path into path string -> store into URL history + const String& path = GetDesktopFolder().get_name(info._shell_path, SHGDN_FORADDRESSBAR); + const String& parsingpath = GetDesktopFolder().get_name(info._shell_path, SHGDN_FORPARSING); + // store path into history if (info._path && *info._path) _url_history.push(info._path); diff --git a/reactos/subsys/system/explorer/shell/shellbrowser.h b/reactos/subsys/system/explorer/shell/shellbrowser.h index d256a6b03c2..8d554c43a7a 100644 --- a/reactos/subsys/system/explorer/shell/shellbrowser.h +++ b/reactos/subsys/system/explorer/shell/shellbrowser.h @@ -1,5 +1,5 @@ /* - * Copyright 2003, 2004 Martin Fuchs + * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -135,6 +135,9 @@ struct ShellBrowser : public IShellBrowserImpl void UpdateFolderView(IShellFolder* folder); HTREEITEM select_entry(HTREEITEM hitem, Entry* entry, bool expand=true); + // for SDIMainFrame + void jump_to(LPCITEMIDLIST pidl); + protected: HWND _hwnd; HWND _left_hwnd; @@ -158,9 +161,6 @@ protected: void InitializeTree(HIMAGELIST himl); bool InitDragDrop(); - - // for SDIMainFrame - void jump_to(LPCITEMIDLIST pidl); }; diff --git a/reactos/subsys/system/explorer/shell/shellfs.cpp b/reactos/subsys/system/explorer/shell/shellfs.cpp index d6360979dae..afb0e9e484b 100644 --- a/reactos/subsys/system/explorer/shell/shellfs.cpp +++ b/reactos/subsys/system/explorer/shell/shellfs.cpp @@ -235,7 +235,7 @@ void ShellDirectory::read_directory(int scan_flags) TCHAR buffer[MAX_PATH]; - if ((scan_flags&SCAN_FILESYSTEM) && get_path(buffer)) { + if ((scan_flags&SCAN_FILESYSTEM) && get_path(buffer) && _tcsncmp(buffer,TEXT("::{"),3)) { Entry* entry = NULL; // eliminate useless GCC warning by initializing entry LPTSTR p = buffer + _tcslen(buffer); diff --git a/reactos/subsys/system/explorer/shell/shellfs.h b/reactos/subsys/system/explorer/shell/shellfs.h index 9f8276d18db..34f8af8dcbf 100644 --- a/reactos/subsys/system/explorer/shell/shellfs.h +++ b/reactos/subsys/system/explorer/shell/shellfs.h @@ -1,5 +1,5 @@ /* - * Copyright 2003, 2004 Martin Fuchs + * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -58,7 +58,7 @@ struct ShellDirectory : public ShellEntry, public Directory { CONTEXT("ShellDirectory::ShellDirectory()"); - lstrcpy(_data.cFileName, root_folder.get_name(shell_path, SHGDN_FORPARSING)); + lstrcpy(_data.cFileName, root_folder.get_name(shell_path, SHGDN_FORADDRESSBAR)); _data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; _shell_attribs = SFGAO_FOLDER; diff --git a/reactos/subsys/system/explorer/taskbar/desktopbar.cpp b/reactos/subsys/system/explorer/taskbar/desktopbar.cpp index 651420d7401..be20039d0b8 100644 --- a/reactos/subsys/system/explorer/taskbar/desktopbar.cpp +++ b/reactos/subsys/system/explorer/taskbar/desktopbar.cpp @@ -41,9 +41,13 @@ DesktopBar::DesktopBar(HWND hwnd) : super(hwnd), +#ifdef _ROS_ _trayIcon(hwnd, ID_TRAY_VOLUME) +#else + WM_TASKBARCREATED(RegisterWindowMessage(WINMSG_TASKBARCREATED)) +#endif { - SetWindowIcon(hwnd, IDI_REACTOS/*IDI_SEARCH*/); // icon in for TrayNotifyDlg + SetWindowIcon(hwnd, IDI_REACTOS); SystemParametersInfo(SPI_GETWORKAREA, 0, &_work_area_org, 0); } @@ -107,6 +111,12 @@ LRESULT DesktopBar::Init(LPCREATESTRUCT pcs) // create tray notification area _hwndNotify = NotifyArea::Create(_hwnd); + + // notify all top level windows about the successfully created desktop bar + //@@ Use SendMessage() instead of PostMessage() to avoid problems with delayed created shell service objects? + PostMessage(HWND_BROADCAST, WM_TASKBARCREATED, 0, 0); + + _hwndQuickLaunch = QuickLaunchBar::Create(_hwnd); // create rebar window to manage task and quick launch bar @@ -147,10 +157,8 @@ LRESULT DesktopBar::Init(LPCREATESTRUCT pcs) SendMessage(_hwndrebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand); #endif - RegisterHotkeys(); - // notify all top level windows about the successfully created desktop bar - PostMessage(HWND_BROADCAST, WM_TASKBARCREATED, 0, 0); + RegisterHotkeys(); // prepare Startmenu, but hide it for now _startMenuRoot = GET_WINDOW(StartMenuRoot, StartMenuRoot::Create(_hwnd)); @@ -332,6 +340,7 @@ int DesktopBar::Command(int id, int code) PostMessage(_hwndQuickLaunch, PM_UPDATE_DESKTOP, desktop_idx, 0); break;} +#ifdef _ROS_ case ID_TRAY_VOLUME: launch_file(_hwnd, TEXT("sndvol32.exe"), SW_SHOWNORMAL); // launch volume control application break; @@ -339,6 +348,7 @@ int DesktopBar::Command(int id, int code) case ID_VOLUME_PROPERTIES: launch_cpanel(_hwnd, TEXT("mmsys.cpl")); break; +#endif default: if (_hwndQuickLaunch) @@ -381,6 +391,8 @@ LRESULT DesktopBar::ProcessCopyData(COPYDATASTRUCT* pcd) } +#ifdef _ROS_ + void DesktopBar::AddTrayIcons() { _trayIcon.Add(SmallIcon(IDI_SPEAKER), ResString(IDS_VOLUME)); @@ -410,3 +422,5 @@ void DesktopBar::TrayDblClick(UINT id, int btn) break; } } + +#endif diff --git a/reactos/subsys/system/explorer/taskbar/desktopbar.h b/reactos/subsys/system/explorer/taskbar/desktopbar.h index beb8467ccce..c0d8e1c8cbb 100644 --- a/reactos/subsys/system/explorer/taskbar/desktopbar.h +++ b/reactos/subsys/system/explorer/taskbar/desktopbar.h @@ -50,22 +50,33 @@ #define IDC_NETWORK 0x100E #define IDC_CONNECTIONS 0x100F #define IDC_DRIVES 0x1010 -#define IDC_SETTINGS_MENU 0x1011 -#define IDC_CONTROL_PANEL 0x1012 +#define IDC_CONTROL_PANEL 0x1011 +#define IDC_SETTINGS_MENU 0x1012 #define IDC_PRINTERS 0x1013 -#define IDC_BROWSE 0x1014 -#define IDC_SEARCH_PROGRAM 0x1015 -#define IDC_SEARCH 0x1016 +#define IDC_PRINTERS_MENU 0x1014 +#define IDC_BROWSE 0x1015 +#define IDC_SEARCH_PROGRAM 0x1016 +#define IDC_SEARCH 0x1017 +#define IDC_TERMINATE 0x1018 #define IDC_FIRST_MENU 0x3000 /// desktop bar window, also known as "system tray" -struct DesktopBar : public TrayIconControllerTemplate< - OwnerDrawParent > +struct DesktopBar : public +#ifdef _ROS_ + TrayIconControllerTemplate< + OwnerDrawParent > +#else + OwnerDrawParent +#endif { +#ifdef _ROS_ typedef TrayIconControllerTemplate< OwnerDrawParent > super; +#else + typedef OwnerDrawParent super; +#endif DesktopBar(HWND hwnd); ~DesktopBar(); @@ -93,9 +104,13 @@ protected: struct StartMenuRoot* _startMenuRoot; +#ifdef _ROS_ TrayIcon _trayIcon; void AddTrayIcons(); virtual void TrayClick(UINT id, int btn); virtual void TrayDblClick(UINT id, int btn); +#else + const UINT WM_TASKBARCREATED; +#endif }; diff --git a/reactos/subsys/system/explorer/taskbar/quicklaunch.cpp b/reactos/subsys/system/explorer/taskbar/quicklaunch.cpp index f10fb26a7f3..af8d6b53f34 100644 --- a/reactos/subsys/system/explorer/taskbar/quicklaunch.cpp +++ b/reactos/subsys/system/explorer/taskbar/quicklaunch.cpp @@ -286,6 +286,10 @@ int QuickLaunchBar::Notify(int id, NMHDR* pnmh) #ifdef TTF_DI_SETITEM ttdi->uFlags |= TTF_DI_SETITEM; #endif + + // enable multiline tooltips (break at CR/LF and for very long one-line strings) + SendMessage(pnmh->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 400); + break;} return super::Notify(id, pnmh); diff --git a/reactos/subsys/system/explorer/taskbar/startmenu.cpp b/reactos/subsys/system/explorer/taskbar/startmenu.cpp index a113bfb1651..bb3664c39c4 100644 --- a/reactos/subsys/system/explorer/taskbar/startmenu.cpp +++ b/reactos/subsys/system/explorer/taskbar/startmenu.cpp @@ -425,7 +425,7 @@ LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) break; case PM_SELECT_ENTRY: - SelectButtonIndex(0, wparam?true:false); + SelectButtonIndex(0, wparam!=0); break; #ifdef _LIGHT_STARTMENU @@ -1690,6 +1690,9 @@ LRESULT StartMenuRoot::Init(LPCREATESTRUCT pcs) AddButton(ResString(IDS_SHUTDOWN), ICID_LOGOFF, false, IDC_SHUTDOWN); + AddButton(ResString(IDS_TERMINATE), ICID_LOGOFF, false, IDC_TERMINATE); + + #ifdef __MINGW32__ RegCloseKey(hkeyAdv); RegCloseKey(hkey); @@ -1830,9 +1833,11 @@ int StartMenuHandler::Command(int id, int code) break; case IDC_LOGOFF: - /* The shell32 Dialog prompts about some system setting change. This is not what we want to display here. CloseStartMenu(id); - ShowRestartDialog(g_Globals._hwndDesktopBar, EWX_LOGOFF);*/ + ShowLogoffDialog(g_Globals._hwndDesktopBar); + break; + + case IDC_TERMINATE: DestroyWindow(GetParent(_hwnd)); break; @@ -1849,21 +1854,50 @@ int StartMenuHandler::Command(int id, int code) ExplorerPropertySheet(g_Globals._hwndDesktopBar); break; + case IDC_CONTROL_PANEL: { + CloseStartMenu(id); +#ifndef _NO_MDI + XMLPos explorer_options = g_Globals.get_cfg("general/explorer"); + bool mdi = XMLBool(explorer_options, "mdi", true); + + if (mdi) + MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0); + else +#endif + SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0); + break;} + case IDC_SETTINGS_MENU: CreateSubmenu(id, CSIDL_CONTROLS, ResString(IDS_SETTINGS_MENU)); break; - case IDC_PRINTERS: + case IDC_PRINTERS: { + CloseStartMenu(id); #ifdef _ROS_ // to be removed when printer folder will be implemented MessageBox(0, TEXT("printer folder not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK); #else - CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS)); -#endif - break; +#ifndef _NO_MDI + XMLPos explorer_options = g_Globals.get_cfg("general/explorer"); + bool mdi = XMLBool(explorer_options, "mdi", true); - case IDC_CONTROL_PANEL: - CloseStartMenu(id); - //@@SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0); + if (mdi) + MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0); + else +#endif + SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0); +#endif + break;} + + case IDC_PRINTERS_MENU: + CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS)); +/* StartMenuFolders new_folders; + + try { + new_folders.push_back(ShellPath(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"))); + } catch(COMException&) { + } + + CreateSubmenu(id, new_folders, ResString(IDS_PRINTERS));*/ break; case IDC_ADMIN: @@ -1890,7 +1924,7 @@ int StartMenuHandler::Command(int id, int code) break; case IDC_DRIVES: - ///@todo exclude removeable drives + ///@todo exclude removable drives CreateSubmenu(id, CSIDL_DRIVES, ResString(IDS_DRIVES)); break; @@ -1952,7 +1986,6 @@ void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner) // Show "Run..." dialog if (RunFileDlg) { #ifndef _ROS_ /* FIXME: our shell32 always expects Ansi strings */ -#define W_VER_NT 0 if ((HIWORD(GetVersion())>>14) == W_VER_NT) { WCHAR wTitle[40], wText[256]; @@ -1967,24 +2000,29 @@ void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner) } } -void StartMenuHandler::ShowRestartDialog(HWND hwndOwner, UINT flags) +void StartMenuHandler::ShowLogoffDialog(HWND hwndOwner) { - static DynamicFct RestartDlg(TEXT("SHELL32"), 59); + static DynamicFct LogoffWindowsDialog(TEXT("SHELL32"), 54); +// static DynamicFct RestartDialog(TEXT("SHELL32"), 59); - if (RestartDlg) - (*RestartDlg)(hwndOwner, (LPWSTR)L"You selected .\n\n", flags); ///@todo ANSI string conversion if needed + if (LogoffWindowsDialog) + (*LogoffWindowsDialog)(0); +/* The RestartDialog function prompts about some system setting change. This is not what we want to display here. + else if (RestartDialog) + return (*RestartDialog)(hwndOwner, (LPWSTR)L"You selected .\n\n", EWX_LOGOFF) == 1; ///@todo ANSI string conversion if needed +*/ else - MessageBox(hwndOwner, TEXT("RestartDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK); + MessageBox(hwndOwner, TEXT("LogoffWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK); } void ShowExitWindowsDialog(HWND hwndOwner) { - static DynamicFct ExitWindowsDlg(TEXT("SHELL32"), 60); + static DynamicFct ExitWindowsDialog(TEXT("SHELL32"), 60); - if (ExitWindowsDlg) - (*ExitWindowsDlg)(hwndOwner); + if (ExitWindowsDialog) + (*ExitWindowsDialog)(hwndOwner); else - MessageBox(hwndOwner, TEXT("ExitWindowsDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK); + MessageBox(hwndOwner, TEXT("ExitWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK); } @@ -1992,16 +2030,11 @@ void SettingsMenu::AddEntries() { super::AddEntries(); -#ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003) - if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL)) -#endif - AddButton(ResString(IDS_CONTROL_PANEL), ICID_CONFIG, false, IDC_CONTROL_PANEL); - #ifdef _ROS_ // to be removed when printer/network will be implemented - AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, false, IDC_PRINTERS); + AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, false, IDC_PRINTERS_MENU); AddButton(ResString(IDS_CONNECTIONS), ICID_NETWORK, false, IDC_CONNECTIONS); #else - AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, true, IDC_PRINTERS); + AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, true, IDC_PRINTERS_MENU); AddButton(ResString(IDS_CONNECTIONS), ICID_NETWORK, true, IDC_CONNECTIONS); #endif AddButton(ResString(IDS_ADMIN), ICID_CONFIG, true, IDC_ADMIN); @@ -2012,6 +2045,13 @@ void SettingsMenu::AddEntries() AddButton(ResString(IDS_SETTINGS_MENU), ICID_CONFIG, true, IDC_SETTINGS_MENU); AddButton(ResString(IDS_DESKTOPBAR_SETTINGS), ICID_CONFIG, false, ID_DESKTOPBAR_SETTINGS); + + AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, false, IDC_PRINTERS); + +#ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003) + if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL)) +#endif + AddButton(ResString(IDS_CONTROL_PANEL), ICID_CONFIG, false, IDC_CONTROL_PANEL); } void BrowseMenu::AddEntries() diff --git a/reactos/subsys/system/explorer/taskbar/startmenu.h b/reactos/subsys/system/explorer/taskbar/startmenu.h index 21bb72d31af..1840bc9f7f3 100644 --- a/reactos/subsys/system/explorer/taskbar/startmenu.h +++ b/reactos/subsys/system/explorer/taskbar/startmenu.h @@ -321,9 +321,11 @@ typedef void (WINAPI* RUNFILEDLG)(HWND hwndOwner, HICON hIcon, LPCSTR lpstrDirec #define RFF_NOSEPARATEMEM 0x20 // Removes the Separate Memory Space check box (Windows NT only). - // declare more undocumented shell32 functions + // declare more previously undocumented shell32 functions typedef void (WINAPI* EXITWINDOWSDLG)(HWND hwndOwner); +typedef int (WINAPI* LOGOFFWINDOWSDIALOG)(UINT flags); typedef int (WINAPI* RESTARTWINDOWSDLG)(HWND hwndOwner, LPCWSTR reason, UINT flags); +typedef int (WINAPI* RESTARTWINDOWSDLGEX)(HWND hWndOwner, LPCWSTR lpwstrReason, DWORD uFlags, DWORD uReason); typedef BOOL (WINAPI* SHFINDFILES)(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch); typedef BOOL (WINAPI* SHFINDCOMPUTER)(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch); @@ -347,7 +349,7 @@ protected: int Command(int id, int code); static void ShowLaunchDialog(HWND hwndOwner); - static void ShowRestartDialog(HWND hwndOwner, UINT flags); + static void ShowLogoffDialog(HWND hwndOwner); static void ShowSearchDialog(); static void ShowSearchComputer(); }; diff --git a/reactos/subsys/system/explorer/taskbar/taskbar.cpp b/reactos/subsys/system/explorer/taskbar/taskbar.cpp index c18f1a1c550..4cf42dde267 100644 --- a/reactos/subsys/system/explorer/taskbar/taskbar.cpp +++ b/reactos/subsys/system/explorer/taskbar/taskbar.cpp @@ -32,6 +32,21 @@ #include "traynotify.h" // for NOTIFYAREA_WIDTH_DEF +DynamicFct g_SetTaskmanWindow(TEXT("user32"), "SetTaskmanWindow"); +DynamicFct g_RegisterShellHookWindow(TEXT("user32"), "RegisterShellHookWindow"); +DynamicFct g_DeregisterShellHookWindow(TEXT("user32"), "DeregisterShellHookWindow"); + +/* +DynamicFct g_RegisterShellHook(TEXT("shell32"), (LPCSTR)0xb5); + + // constants for RegisterShellHook() +#define RSH_UNREGISTER 0 +#define RSH_REGISTER 1 +#define RSH_REGISTER_PROGMAN 2 +#define RSH_REGISTER_TASKMAN 3 +*/ + + TaskBarEntry::TaskBarEntry() { _id = 0; @@ -53,16 +68,40 @@ TaskBarMap::~TaskBarMap() TaskBar::TaskBar(HWND hwnd) - : super(hwnd) + : super(hwnd), + WM_SHELLHOOK(RegisterWindowMessage(WINMSG_SHELLHOOK)) { _last_btn_width = 0; + + _mmMetrics_org.cbSize = sizeof(MINIMIZEDMETRICS); + + SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, sizeof(_mmMetrics_org), &_mmMetrics_org, 0); + + // configure the window manager to hide windows when they are minimized + // This is neccessary to enable shell hook messages. + if (!(_mmMetrics_org.iArrange & ARW_HIDE)) { + MINIMIZEDMETRICS _mmMetrics_new = _mmMetrics_org; + + _mmMetrics_new.iArrange |= ARW_HIDE; + + SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, sizeof(_mmMetrics_new), &_mmMetrics_new, 0); + } } TaskBar::~TaskBar() { - KillTimer(_hwnd, 0); +// if (g_RegisterShellHook) +// (*g_RegisterShellHook)(_hwnd, RSH_UNREGISTER); - //DeinstallShellHook(); + if (g_DeregisterShellHookWindow) + (*g_DeregisterShellHookWindow)(_hwnd); + else + KillTimer(_hwnd, 0); + + if (g_SetTaskmanWindow) + (*g_SetTaskmanWindow)(0); + + SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, sizeof(_mmMetrics_org), &_mmMetrics_org, 0); } HWND TaskBar::Create(HWND hwndParent) @@ -95,12 +134,32 @@ LRESULT TaskBar::Init(LPCREATESTRUCT pcs) _next_id = IDC_FIRST_APP; - //InstallShellHook(_hwnd, PM_SHELLHOOK_NOTIFY); + // register the taskbar window as task manager window to make the following call to RegisterShellHookWindow working + if (g_SetTaskmanWindow) + (*g_SetTaskmanWindow)(_hwnd); + if (g_RegisterShellHookWindow) { + LOG(TEXT("Using shell hooks for notification of shell events.")); + + (*g_RegisterShellHookWindow)(_hwnd); + } else { + LOG(TEXT("Shell hooks not available.")); + + SetTimer(_hwnd, 0, 200, NULL); + } + +/* Alternatively we could use the RegisterShellHook() function in SHELL32, but this is not yet implemented in the WINE code. + if (g_RegisterShellHook) { + (*g_RegisterShellHook)(0, RSH_REGISTER); + + if ((HIWORD(GetVersion())>>14) == W_VER_NT) + (*g_RegisterShellHook)(_hwnd, RSH_REGISTER_TASKMAN); + else + (*g_RegisterShellHook)(_hwnd, RSH_REGISTER); + } +*/ Refresh(); - SetTimer(_hwnd, 0, 200, NULL); - return 0; } @@ -124,28 +183,27 @@ LRESULT TaskBar::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) break; // avoid displaying context menu for application button _and_ desktop bar at the same time goto def;} -/* -//#define PM_SHELLHOOK_NOTIFY (WM_APP+0x10) - case PM_SHELLHOOK_NOTIFY: { - int code = lparam; - - switch(code) { - case HSHELL_WINDOWCREATED: - case HSHELL_WINDOWDESTROYED: - case HSHELL_WINDOWACTIVATED: - case HSHELL_WINDOWREPLACED: - Refresh(); - break; - } - Refresh(); - break;} -*/ case PM_GET_LAST_ACTIVE: return (LRESULT)(HWND)_last_foreground_wnd; default: def: - return super::WndProc(nmsg, wparam, lparam); + if (nmsg == WM_SHELLHOOK) { + switch(wparam) { + case HSHELL_WINDOWCREATED: + case HSHELL_WINDOWDESTROYED: + case HSHELL_WINDOWACTIVATED: + case HSHELL_REDRAW: +#ifdef HSHELL_FLASH + case HSHELL_FLASH: + case HSHELL_RUDEAPPACTIVATED: +#endif + Refresh(); + break; + } + } else { + return super::WndProc(nmsg, wparam, lparam); + } } return 0; @@ -393,6 +451,7 @@ BOOL CALLBACK TaskBar::EnumWndProc(HWND hwnd, LPARAM lparam) entry._fsState = btn.fsState; +#ifdef _ROS_ // now handled by activating the ARW_HIDE flag with SystemParametersInfo(SPI_SETMINIMIZEDMETRICS) // move minimized windows out of sight if (IsIconic(hwnd)) { RECT rect; @@ -402,6 +461,7 @@ BOOL CALLBACK TaskBar::EnumWndProc(HWND hwnd, LPARAM lparam) if (rect.bottom > 0) SetWindowPos(hwnd, 0, -32000, -32000, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE); } +#endif } return TRUE; diff --git a/reactos/subsys/system/explorer/taskbar/taskbar.h b/reactos/subsys/system/explorer/taskbar/taskbar.h index bc1d4e0bb03..76512eed6d9 100644 --- a/reactos/subsys/system/explorer/taskbar/taskbar.h +++ b/reactos/subsys/system/explorer/taskbar/taskbar.h @@ -84,7 +84,10 @@ protected: TaskBarMap _map; int _next_id; WindowHandle _last_foreground_wnd; - int _last_btn_width; + int _last_btn_width; + MINIMIZEDMETRICS _mmMetrics_org; + + const UINT WM_SHELLHOOK; LRESULT Init(LPCREATESTRUCT pcs); LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam); diff --git a/reactos/subsys/system/explorer/taskbar/traynotify.cpp b/reactos/subsys/system/explorer/taskbar/traynotify.cpp index 072e387318c..b77e639fca4 100644 --- a/reactos/subsys/system/explorer/taskbar/traynotify.cpp +++ b/reactos/subsys/system/explorer/taskbar/traynotify.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2003, 2004 Martin Fuchs + * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -104,13 +104,23 @@ NotifyInfo::NotifyInfo() #define NID_SIZE_A5 (sizeof(NOTIFYICONDATAA)-sizeof(GUID)) #define NID_SIZE_A3 (sizeof(NOTIFYICONDATAA)-sizeof(GUID)-(128-64)*sizeof(CHAR)) -NotifyInfo& NotifyInfo::operator=(NOTIFYICONDATA* pnid) +bool NotifyInfo::modify(NOTIFYICONDATA* pnid) { - _hWnd = pnid->hWnd; - _uID = pnid->uID; + bool changes = false; - if (pnid->uFlags & NIF_MESSAGE) - _uCallbackMessage = pnid->uCallbackMessage; + if (_hWnd!=pnid->hWnd || _uID!=pnid->uID) { + _hWnd = pnid->hWnd; + _uID = pnid->uID; + + changes = true; + } + + if (pnid->uFlags & NIF_MESSAGE) { + if (_uCallbackMessage != pnid->uCallbackMessage) { + _uCallbackMessage = pnid->uCallbackMessage; + changes = true; + } + } if (pnid->uFlags & NIF_ICON) { // Some applications destroy the icon immediatelly after completing the @@ -119,15 +129,25 @@ NotifyInfo& NotifyInfo::operator=(NOTIFYICONDATA* pnid) DestroyIcon(_hIcon); _hIcon = (HICON) CopyImage(pnid->hIcon, IMAGE_ICON, NOTIFYICON_SIZE, NOTIFYICON_SIZE, 0); + + changes = true; ///@todo compare icon } #ifdef NIF_STATE // as of 21.08.2003 missing in MinGW headers - if (pnid->uFlags & NIF_STATE) - _dwState = (_dwState&~pnid->dwStateMask) | (pnid->dwState&pnid->dwStateMask); + if (pnid->uFlags & NIF_STATE) { + DWORD new_state = (_dwState&~pnid->dwStateMask) | (pnid->dwState&pnid->dwStateMask); + + if (_dwState != new_state) { + _dwState = new_state; + changes = true; + } + } #endif // store tool tip text - if (pnid->uFlags & NIF_TIP) + if (pnid->uFlags & NIF_TIP) { + String new_text; + if (pnid->cbSize==NID_SIZE_W6 || pnid->cbSize==NID_SIZE_W5 || pnid->cbSize==NID_SIZE_W3) { // UNICODE version of NOTIFYICONDATA structure LPCWSTR txt = (LPCWSTR)pnid->szTip; @@ -139,7 +159,12 @@ NotifyInfo& NotifyInfo::operator=(NOTIFYICONDATA* pnid) if (!txt[l]) break; - _tipText.assign(txt, l); + new_text.assign(txt, l); + + if (new_text != _tipText) { + _tipText = new_text; + changes = true; + } } else if (pnid->cbSize==NID_SIZE_A6 || pnid->cbSize==NID_SIZE_A5 || pnid->cbSize==NID_SIZE_A3) { LPCSTR txt = (LPCSTR)pnid->szTip; int max_len = pnid->cbSize==NID_SIZE_A3? 64: 128; @@ -149,20 +174,35 @@ NotifyInfo& NotifyInfo::operator=(NOTIFYICONDATA* pnid) if (!txt[l]) break; - _tipText.assign(txt, l); + new_text.assign(txt, l); + + if (new_text != _tipText) { + _tipText = new_text; + changes = true; + } } + } TCHAR title[MAX_PATH]; - if (GetWindowText(_hWnd, title, MAX_PATH)) - _windowTitle = title; + DWORD pid; + GetWindowThreadProcessId(_hWnd, &pid); - create_name(); + // avoid to send WM_GETTEXT messages to the own process + if (pid != GetCurrentProcessId()) + if (GetWindowText(_hWnd, title, MAX_PATH)) { + if (_windowTitle != title) { + _windowTitle = title; + changes = true; + } + } - ///@todo test for real changes - _lastChange = GetTickCount(); + if (changes) { + create_name(); + _lastChange = GetTickCount(); + } - return *this; + return changes; } @@ -194,7 +234,7 @@ static bool get_hide_clock_from_registry() bool hide_clock = false; // check if the clock should be hidden - if (!RegOpenKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects2"), &hkeyStuckRects) && + if (!RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects2"), &hkeyStuckRects) && !RegQueryValueEx(hkeyStuckRects, TEXT("Settings"), 0, NULL, (LPBYTE)buffer, &len) && len==sizeof(buffer) && buffer[0]==sizeof(buffer)) hide_clock = buffer[2] & 0x08? true: false; @@ -338,7 +378,7 @@ HWND NotifyArea::Create(HWND hwndParent) ClientRect clnt(hwndParent); return Window::Create(WINDOW_CREATOR(NotifyArea), WS_EX_STATICEDGE, - wcTrayNotify, TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE, + wcTrayNotify, TITLE_TRAYNOTIFY, WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN, clnt.right-(NOTIFYAREA_WIDTH_DEF+1), 1, NOTIFYAREA_WIDTH_DEF, clnt.bottom-2, hwndParent); } @@ -448,7 +488,11 @@ LRESULT NotifyArea::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) (*AllowSetForegroundWindow)(pid); } - SendMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg); + // use PostMessage() for notifcation icons of Shell Service Objects in the own process + if (pid == GetCurrentProcessId()) + PostMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg); + else + SendMessage(entry._hWnd, entry._uCallbackMessage, entry._uID, nmsg); } } else if (_icon_map.erase(entry)) // delete icons without valid owner window @@ -511,13 +555,16 @@ int NotifyArea::Notify(int id, NMHDR* pnmh) static ResString sShowIcons(IDS_SHOW_HIDDEN_ICONS); static ResString sHideIcons(IDS_HIDE_ICONS); - pdi->lpszText = (LPTSTR)(_show_hidden?sHideIcons:sShowIcons).c_str(); + pdi->lpszText = (LPTSTR)(_show_hidden? sHideIcons: sShowIcons).c_str(); } else { NotifyIconSet::iterator found = IconHitTest(pt); if (found != _sorted_icons.end()) { NotifyInfo& entry = const_cast(*found); // Why does GCC 3.3 need this additional const_cast ?! + // enable multiline tooltips (break at CR/LF and for very long one-line strings) + SendMessage(pnmh->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 400); + pdi->lpszText = (LPTSTR)entry._tipText.c_str(); } } @@ -540,18 +587,34 @@ LRESULT NotifyArea::ProcessTrayNotification(int notify_code, NOTIFYICONDATA* pni case NIM_ADD: case NIM_MODIFY: if ((int)pnid->uID >= 0) { ///@todo This is a fix for Windows Task Manager. - NotifyInfo& entry = _icon_map[pnid] = pnid; + NotifyInfo& entry = _icon_map[pnid]; // a new entry? if (entry._idx == -1) entry._idx = ++_next_idx; + /* equivalent code using iterator::find(); + NotifyIconMap::iterator found = _icon_map.find(pnid); + NotifyInfo* pentry; + // a new entry? + if (found == _icon_map.end()) { + pentry = &_icon_map[pnid]; + pentry->_idx = ++_next_idx; + } else { + pentry = &found->second; + } + NotifyInfo& entry = *pentry; + */ + bool changes = entry.modify(pnid); #if NOTIFYICON_VERSION>=3 // as of 21.08.2003 missing in MinGW headers - if (DetermineHideState(entry) && entry._mode==NIM_HIDE) + if (DetermineHideState(entry) && entry._mode==NIM_HIDE) { entry._dwState |= NIS_HIDDEN; + changes = true; + } #endif - UpdateIcons(); ///@todo call only if really changes occurred + if (changes) + UpdateIcons(); ///@todo call only if really changes occurred return TRUE; } @@ -842,7 +905,7 @@ TrayNotifyDlg::TrayNotifyDlg(HWND hwnd) _show_hidden_org = _pNotifyArea->_show_hidden; } - SetWindowIcon(hwnd, IDI_REACTOS/*IDI_SEARCH*/); + SetWindowIcon(hwnd, IDI_REACTOS); _haccel = LoadAccelerators(g_Globals._hInstance, MAKEINTRESOURCE(IDA_TRAYNOTIFY)); @@ -990,7 +1053,7 @@ void TrayNotifyDlg::InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyI void TrayNotifyDlg::InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyIconDlgInfo& entry, HDC hdc, HICON hicon, NOTIFYICONMODE mode) { - int idx = _info.size(); + int idx = _info.size() + 1; _info[idx] = entry; String mode_str = string_from_mode(mode); @@ -1100,42 +1163,10 @@ int TrayNotifyDlg::Notify(int id, NMHDR* pnmh) switch(pnmh->code) { case TVN_SELCHANGED: { NMTREEVIEW* pnmtv = (NMTREEVIEW*)pnmh; - LPARAM lparam = pnmtv->itemNew.lParam; - - if (lparam) { - const NotifyIconDlgInfo& entry = _info[lparam]; - - SetDlgItemText(_hwnd, IDC_NOTIFY_TOOLTIP, entry._tipText); - SetDlgItemText(_hwnd, IDC_NOTIFY_TITLE, entry._windowTitle); - SetDlgItemText(_hwnd, IDC_NOTIFY_MODULE, entry._modulePath); - - CheckRadioButton(_hwnd, IDC_NOTIFY_SHOW, IDC_NOTIFY_AUTOHIDE, IDC_NOTIFY_SHOW+entry._mode); - - String change_str; - if (entry._lastChange) - change_str.printf(TEXT("before %d s"), (GetTickCount()-entry._lastChange+500)/1000); - SetDlgItemText(_hwnd, IDC_LAST_CHANGE, change_str); - - HICON hicon = 0; //get_window_icon_big(entry._hWnd, false); - - // If we could not find an icon associated with the owner window, try to load one from the owning module. - if (!hicon && !entry._modulePath.empty()) { - hicon = ExtractIcon(g_Globals._hInstance, entry._modulePath, 0); - - if (!hicon) { - SHFILEINFO sfi; - - if (SHGetFileInfo(entry._modulePath, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_LARGEICON)) - hicon = sfi.hIcon; - } - } - - if (hicon) { - SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, (LPARAM)hicon, 0); - DestroyIcon(hicon); - } else - SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, 0, 0); + int idx = pnmtv->itemNew.lParam; + if (idx) { + RefreshProperties(_info[idx]); _selectedItem = pnmtv->itemNew.hItem; } else { /* @@ -1151,14 +1182,48 @@ int TrayNotifyDlg::Notify(int id, NMHDR* pnmh) return 0; } +void TrayNotifyDlg::RefreshProperties(const NotifyIconDlgInfo& entry) +{ + SetDlgItemText(_hwnd, IDC_NOTIFY_TOOLTIP, entry._tipText); + SetDlgItemText(_hwnd, IDC_NOTIFY_TITLE, entry._windowTitle); + SetDlgItemText(_hwnd, IDC_NOTIFY_MODULE, entry._modulePath); + + CheckRadioButton(_hwnd, IDC_NOTIFY_SHOW, IDC_NOTIFY_AUTOHIDE, IDC_NOTIFY_SHOW+entry._mode); + + String change_str; + if (entry._lastChange) + change_str.printf(TEXT("before %d s"), (GetTickCount()-entry._lastChange+500)/1000); + SetDlgItemText(_hwnd, IDC_LAST_CHANGE, change_str); + + HICON hicon = 0; //get_window_icon_big(entry._hWnd, false); + + // If we could not find an icon associated with the owner window, try to load one from the owning module. + if (!hicon && !entry._modulePath.empty()) { + hicon = ExtractIcon(g_Globals._hInstance, entry._modulePath, 0); + + if (!hicon) { + SHFILEINFO sfi; + + if (SHGetFileInfo(entry._modulePath, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_LARGEICON)) + hicon = sfi.hIcon; + } + } + + if (hicon) { + SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, (LPARAM)hicon, 0); + DestroyIcon(hicon); + } else + SendMessage(GetDlgItem(_hwnd, IDC_PICTURE), STM_SETICON, 0, 0); +} + void TrayNotifyDlg::SetIconMode(NOTIFYICONMODE mode) { - LPARAM lparam = TreeView_GetItemData(_tree_ctrl, _selectedItem); + int idx = TreeView_GetItemData(_tree_ctrl, _selectedItem); - if (!lparam) + if (!idx) return; - NotifyIconConfig& entry = _info[lparam]; + NotifyIconConfig& entry = _info[idx]; if (entry._mode != mode) { entry._mode = mode; @@ -1290,6 +1355,8 @@ void ClockWindow::Paint() { PaintCanvas canvas(_hwnd); + FillRect(canvas, &canvas.rcPaint, GetSysColorBrush(COLOR_BTNFACE)); + BkMode bkmode(canvas, TRANSPARENT); FontSelection font(canvas, GetStockFont(ANSI_VAR_FONT)); diff --git a/reactos/subsys/system/explorer/taskbar/traynotify.h b/reactos/subsys/system/explorer/taskbar/traynotify.h index 40dda6b4feb..7d90406ab35 100644 --- a/reactos/subsys/system/explorer/taskbar/traynotify.h +++ b/reactos/subsys/system/explorer/taskbar/traynotify.h @@ -98,7 +98,7 @@ struct NotifyInfo : public NotifyIconIndex, public NotifyIconConfig friend bool operator<(const NotifyInfo& a, const NotifyInfo& b) {return a._idx < b._idx;} - NotifyInfo& operator=(NOTIFYICONDATA* pnid); + bool modify(NOTIFYICONDATA* pnid); int _idx; // display index HICON _hIcon; @@ -230,6 +230,7 @@ protected: void InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyInfo&, HDC); void InsertItem(HTREEITEM hparent, HTREEITEM after, const NotifyIconDlgInfo&, HDC, HICON, NOTIFYICONMODE); void SetIconMode(NOTIFYICONMODE mode); + void RefreshProperties(const NotifyIconDlgInfo& entry); }; diff --git a/reactos/subsys/system/explorer/utility/utility.cpp b/reactos/subsys/system/explorer/utility/utility.cpp index 5e8f63d17e4..8d6f9ab542f 100644 --- a/reactos/subsys/system/explorer/utility/utility.cpp +++ b/reactos/subsys/system/explorer/utility/utility.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2003, 2004 Martin Fuchs + * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -281,11 +281,11 @@ String get_windows_version_str() } else { TCHAR type[80]; DWORD dwBufLen; - HKEY hKey; + HKEY hkey; - if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"), 0, KEY_QUERY_VALUE, &hKey)) { - RegQueryValueEx(hKey, TEXT("ProductType"), NULL, NULL, (LPBYTE)type, &dwBufLen); - RegCloseKey(hKey); + if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"), 0, KEY_QUERY_VALUE, &hkey)) { + RegQueryValueEx(hkey, TEXT("ProductType"), NULL, NULL, (LPBYTE)type, &dwBufLen); + RegCloseKey(hkey); if (!_tcsicmp(TEXT("WINNT"), type)) str += TEXT(" Workstation"); diff --git a/reactos/subsys/system/explorer/utility/utility.h b/reactos/subsys/system/explorer/utility/utility.h index c5de7091467..db8af0f91bf 100644 --- a/reactos/subsys/system/explorer/utility/utility.h +++ b/reactos/subsys/system/explorer/utility/utility.h @@ -59,6 +59,8 @@ #define _MAX_PATH 260 #endif +#define W_VER_NT 0 // constant for HIWORD(GetVersion())>>14 + #ifdef __cplusplus extern "C" { @@ -315,16 +317,22 @@ protected: struct Thread { Thread() - : _alive(false) + : _alive(false), + _destroy(false) { _hThread = INVALID_HANDLE_VALUE; + _evtFinish = CreateEvent(NULL, TRUE, FALSE, NULL); } virtual ~Thread() { Stop(); + CloseHandle(_evtFinish); CloseHandle(_hThread); + + if (_destroy) + delete this; } void Start() @@ -337,6 +345,8 @@ struct Thread void Stop() { + SetEvent(_evtFinish); + if (_alive) { { Lock lock(_crit_sect); @@ -358,7 +368,9 @@ protected: static DWORD WINAPI ThreadProc(void* para); HANDLE _hThread; + HANDLE _evtFinish; bool _alive; + bool _destroy; }; diff --git a/reactos/subsys/system/explorer/utility/window.cpp b/reactos/subsys/system/explorer/utility/window.cpp index bbeba49d988..11673f82707 100644 --- a/reactos/subsys/system/explorer/utility/window.cpp +++ b/reactos/subsys/system/explorer/utility/window.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2003, 2004 Martin Fuchs + * Copyright 2003, 2004, 2005 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -277,7 +277,7 @@ LRESULT CALLBACK Window::WindowWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPAR LRESULT Window::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) { -/*@@TODO: replaced by StartMenu::TrackStartmenu() +/**@todo: replaced by StartMenu::TrackStartmenu() HWND hwnd = _hwnd; // close startup menu and other popup menus @@ -352,7 +352,7 @@ LRESULT CALLBACK SubclassedWindow::SubclassedWndProc(HWND hwnd, UINT nmsg, WPARA LRESULT SubclassedWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam) { -/*@@TODO: replaced by StartMenu::TrackStartmenu() +/**@todo: replaced by StartMenu::TrackStartmenu() // close startup menu and other popup menus // This functionality is for tray notification icons missing in MS Windows. if (nmsg == WM_SETFOCUS) diff --git a/reactos/subsys/system/explorer/utility/window.h b/reactos/subsys/system/explorer/utility/window.h index 4f716085ca0..1e60a738250 100644 --- a/reactos/subsys/system/explorer/utility/window.h +++ b/reactos/subsys/system/explorer/utility/window.h @@ -984,6 +984,8 @@ enum {TRAYBUTTON_LEFT=0, TRAYBUTTON_RIGHT, TRAYBUTTON_MIDDLE}; #define WINMSG_TASKBARCREATED TEXT("TaskbarCreated") +#define WINMSG_SHELLHOOK TEXT("SHELLHOOK") + struct TrayIcon { diff --git a/reactos/subsys/system/explorer/utility/xmlstorage.cpp b/reactos/subsys/system/explorer/utility/xmlstorage.cpp index 1da629a3bad..2d87601498f 100644 --- a/reactos/subsys/system/explorer/utility/xmlstorage.cpp +++ b/reactos/subsys/system/explorer/utility/xmlstorage.cpp @@ -4,7 +4,7 @@ // // xmlstorage.cpp // - // Copyright (c) 2004, Martin Fuchs + // Copyright (c) 2004, 2005 Martin Fuchs // diff --git a/reactos/subsys/system/explorer/utility/xmlstorage.h b/reactos/subsys/system/explorer/utility/xmlstorage.h index d57cd0cd937..8a0be9b2800 100644 --- a/reactos/subsys/system/explorer/utility/xmlstorage.h +++ b/reactos/subsys/system/explorer/utility/xmlstorage.h @@ -4,7 +4,7 @@ // // xmlstorage.h // - // Copyright (c) 2004, Martin Fuchs + // Copyright (c) 2004, 2005 Martin Fuchs // diff --git a/reactos/subsys/system/format/format.xml b/reactos/subsys/system/format/format.xml index 6b00bca359c..ec904da6b9b 100644 --- a/reactos/subsys/system/format/format.xml +++ b/reactos/subsys/system/format/format.xml @@ -1,6 +1,8 @@ . + ntdll + kernel32 fmifs format.c diff --git a/reactos/subsys/system/format/makefile b/reactos/subsys/system/format/makefile index fdca1aad718..29c17d266d8 100755 --- a/reactos/subsys/system/format/makefile +++ b/reactos/subsys/system/format/makefile @@ -14,7 +14,7 @@ TARGET_NAME = format TARGET_INSTALLDIR = system32 -TARGET_CFLAGS = -Werror -Wall -Wno-format +TARGET_CFLAGS = -D__USE_W32API -Werror -Wall -Wno-format TARGET_OBJECTS = \ $(TARGET_NAME).o diff --git a/reactos/subsys/system/msiexec/msiexec.c b/reactos/subsys/system/msiexec/msiexec.c index 5c0054ba982..fbd51e8f088 100644 --- a/reactos/subsys/system/msiexec/msiexec.c +++ b/reactos/subsys/system/msiexec/msiexec.c @@ -67,9 +67,9 @@ static const char UsageStr[] = "Copyright 2004 Vincent Béron\n"; static const WCHAR ActionAdmin[] = { - 'A','C','T','I','O','N','=','A','D','M','I','N',' ',0 }; + 'A','C','T','I','O','N','=','A','D','M','I','N',0 }; static const WCHAR RemoveAll[] = { - 'R','E','M','O','V','E','=','A','L','L',' ',0 }; + 'R','E','M','O','V','E','=','A','L','L',0 }; static const WCHAR InstallRunOnce[] = { 'S','o','f','t','w','a','r','e','\\', diff --git a/reactos/subsys/system/notepad/Da.rc b/reactos/subsys/system/notepad/Da.rc index 5415e20c12f..1e62287dfa8 100644 --- a/reactos/subsys/system/notepad/Da.rc +++ b/reactos/subsys/system/notepad/Da.rc @@ -23,7 +23,7 @@ LANGUAGE LANG_DANISH, SUBLANG_DEFAULT MAIN_MENU MENU { - POPUP "&Filef" { + POPUP "&Fil" { MENUITEM "&Ny...", CMD_NEW MENUITEM "Å&bn\tEnter", CMD_OPEN MENUITEM "&Gem", CMD_SAVE @@ -58,7 +58,7 @@ POPUP "&Hj MENUITEM SEPARATOR POPUP "&Om Notesblok..." { MENUITEM "&Licens", CMD_LICENSE - MENUITEM "&NO WARRANTY", CMD_NO_WARRANTY + MENUITEM "&Ingen Garanti", CMD_NO_WARRANTY MENUITEM "&Om WINE", CMD_ABOUT_WINE } } @@ -94,15 +94,15 @@ PUSHBUTTON "&Hj STRINGTABLE DISCARDABLE { STRING_PAGESETUP_HEADERVALUE, "&n" /* FIXME */ -STRING_PAGESETUP_FOOTERVALUE, "Page &s" /* FIXME */ +STRING_PAGESETUP_FOOTERVALUE, "Side &s" STRING_PAGESETUP_LEFTVALUE, "20 mm" /* FIXME */ STRING_PAGESETUP_RIGHTVALUE, "20 mm" /* FIXME */ STRING_PAGESETUP_TOPVALUE, "25 mm" /* FIXME */ STRING_PAGESETUP_BOTTOMVALUE, "25 mm" /* FIXME */ STRING_NOTEPAD, "Notesblok" -STRING_ERROR, "FEJL" -STRING_WARNING, "ADVARSEL" +STRING_ERROR, "Fejl" +STRING_WARNING, "Advarsel" STRING_INFO, "Information" STRING_UNTITLED, "(ikke-navngivet)" @@ -114,10 +114,10 @@ STRING_TOOLARGE, "Filen '%s' er for stor til Note \nBrug en anden editor til at redigere filen." STRING_NOTEXT, "Du har ikke skrevet noget tekst. \ \nSkriv noget tekst, og prøv så igen" -STRING_DOESNOTEXIST, "File '%s'\ndoes not exist\n\n \ -Do you want to create a new file ?" -STRING_NOTSAVED, "File '%s'\nhas been modified\n\n \ -Would you like to save the changes ?" +STRING_DOESNOTEXIST, "Filen '%s'\neksistere ikke\n\n \ +Ønkser du at oprette en ny fil?" +STRING_NOTSAVED, "Filen '%s'\ner blevet ændret\n\n \ +Ønsker du at gemme ændringerne?" STRING_NOTFOUND, "Kan ikke finde '%s'." STRING_OUT_OF_MEMORY, "Der er ikke nok hukommelse til at udføre \ denne operation. \nAfslut et eller flere aktive programmer for at frigøre \ diff --git a/reactos/subsys/system/reactos/Sv.rc b/reactos/subsys/system/reactos/Sv.rc new file mode 100644 index 00000000000..5c3ae5ee6a6 --- /dev/null +++ b/reactos/subsys/system/reactos/Sv.rc @@ -0,0 +1,31 @@ +/* + * Swedish (SE) resources + * + * Copyright (C) 2005 David Nordenberg + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT + +/* String Tables */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAPTION "ReactOS installation" + IDS_TEXT "Du kan inte installera ReactOS direkt från den här CDn!\n\n"\ + "Starta om datorn med hjälp av skivan för att installera ReactOS." +END + +/* EOF */ diff --git a/reactos/subsys/system/reactos/reactos.rc b/reactos/subsys/system/reactos/reactos.rc index ca9be0792bb..018699c382f 100644 --- a/reactos/subsys/system/reactos/reactos.rc +++ b/reactos/subsys/system/reactos/reactos.rc @@ -18,5 +18,6 @@ IDI_MAIN ICON "res/reactos.ico" /* Language-specific resources */ #include "De.rc" #include "En.rc" +#include "Sv.rc" /* EOF */ diff --git a/reactos/subsys/system/reactos/res/reactos.ico b/reactos/subsys/system/reactos/res/reactos.ico index fffc2801755..1be85f581ff 100644 Binary files a/reactos/subsys/system/reactos/res/reactos.ico and b/reactos/subsys/system/reactos/res/reactos.ico differ diff --git a/reactos/subsys/system/reporterror/En.rc b/reactos/subsys/system/reporterror/En.rc index 226da1211c0..0ed32cc5153 100644 --- a/reactos/subsys/system/reporterror/En.rc +++ b/reactos/subsys/system/reporterror/En.rc @@ -14,7 +14,7 @@ STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION CAPTION "Error reporting assistant" FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN - LTEXT "Please describe what you were trying to do when the error occurred.\n\nClick 'Next' to send the report to the ReacOS project or 'Cancel' to not send any information.",-1,10,0,225,40 + LTEXT "Please describe what you were trying to do when the error occurred.\n\nClick 'Next' to send the report to the ReactOS project or 'Cancel' to not send any information.",-1,10,0,225,40 LTEXT "Your e-mail address (optional):",-1,10,40,120,20 EDITTEXT IDE_SUBMIT_REPORT_YOUR_EMAIL, 110,40,150,12 LTEXT "Description of problem (optional):",-1,10,55,120,20 diff --git a/reactos/subsys/system/reporterror/dk.rc b/reactos/subsys/system/reporterror/dk.rc new file mode 100644 index 00000000000..e0b93b9657b --- /dev/null +++ b/reactos/subsys/system/reporterror/dk.rc @@ -0,0 +1,54 @@ +LANGUAGE LANG_DANISH, SUBLANG_DEFAULT + +IDD_FIRSTPAGE DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Fejl Meddeler Assistenten" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Systemet er blev genoprettet fra en fejl.", -1, 115, 15, 160, 24 + LTEXT "Ønsker du at indsende en fejl rapport til ReactOS Projektet?", -1, 115, 35, 160, 17 +END + +IDD_SUBMIT_REPORT DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Fejl Meddeler Assistenten" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Beskriv venligst hvad du prøvede at gøre inden fejlen opstod.\n\nKlik på 'Næste' for at indsende rapporten til ReactOS Projektet eller 'Fortryd' for ikke at indsende nogle informationer.",-1,10,0,225,40 + LTEXT "Din e-mail adresse (valgfri):",-1,10,40,120,20 + EDITTEXT IDE_SUBMIT_REPORT_YOUR_EMAIL, 110,40,150,12 + LTEXT "Beskrivelse af problemet (valgfri):",-1,10,55,120,20 + EDITTEXT IDE_SUBMIT_REPORT_PROBLEM_DESCRIPTION,10,65,250,55,ES_WANTRETURN|ES_MULTILINE|ES_LEFT|WS_BORDER|WS_TABSTOP|WS_VSCROLL +END + +IDD_SUBMITTING_REPORT DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Fejl Meddeler Assistenten" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "{STATUS}",IDC_SUBMISSION_STATUS,25,58,225,8 + CONTROL "", IDC_SUBMITTING_IN_PROGRESS, "msctls_progress32", PBS_MARQUEE | WS_CHILD | WS_VISIBLE | WS_BORDER, 25, 80, 225, 8 +END + +IDD_SUBMITTED_REPORT DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Fejl Meddeler Assistenten" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Din fejl rapport blev vellykket indsendt til ReactOS Projektet. Mange Tak.",-1,25,5,225,20 +END + +STRINGTABLE +BEGIN + IDS_WIZARD_NAME "Rapporterings Fejl ved indsendelse til ReactOS Projektet" + IDS_FAILED_TO_CONTACT_SERVER "Systemet var ikke i stand til at kontakte Fejl Rapportings Serveren.\nVær venlig at sikre dig at dit system er forbundet til Internettet." + IDS_FAILED_TO_DELIVER_ERROR_REPORT "Systemet var ikke i stand til at levere Fejl Rapporten." +END + +STRINGTABLE +BEGIN + IDS_CONTACTING_SERVER "Kontakter Fejl Rapporterings Serveren Vent Venligst..." + IDS_FAILED_TO_INITIALIZE_WINSOCK "Fejl opstod da man forsøgte at initialisere Winsock 2.0 (Reactos Fejl Kode %d)" + IDS_FAILED_TO_LOCATE_SERVER "Kunne ikke komme i kontakt med Fejl Rapportings Serveren. Prøv Venligst igen Senere.." +END + diff --git a/reactos/subsys/system/reporterror/reporterror.rc b/reactos/subsys/system/reporterror/reporterror.rc index fc0a1134081..eecc9786190 100644 --- a/reactos/subsys/system/reporterror/reporterror.rc +++ b/reactos/subsys/system/reporterror/reporterror.rc @@ -16,3 +16,4 @@ IDB_HEADER BITMAP "resources/header.bmp" #include "En.rc" #include "De.rc" +#include "Dk.rc" diff --git a/reactos/subsys/system/services/database.c b/reactos/subsys/system/services/database.c index 033e01916e1..1835dca4cb1 100644 --- a/reactos/subsys/system/services/database.c +++ b/reactos/subsys/system/services/database.c @@ -327,7 +327,7 @@ ScmCreateServiceDataBase(VOID) NULL); Status = RtlpNtOpenKey(&ServicesKey, - 0x10001, + KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &ObjectAttributes, 0); if (!NT_SUCCESS(Status)) diff --git a/reactos/subsys/system/services/makefile b/reactos/subsys/system/services/makefile index 006eec4d7bb..37442872d85 100644 --- a/reactos/subsys/system/services/makefile +++ b/reactos/subsys/system/services/makefile @@ -14,7 +14,7 @@ TARGET_SDKLIBS = ntdll.a kernel32.a user32.a TARGET_OBJECTS = $(TARGET_NAME).o database.o -TARGET_CFLAGS = -Wall -Werror +TARGET_CFLAGS = -D__USE_W32API -Wall -Werror include $(PATH_TO_TOP)/rules.mak diff --git a/reactos/subsys/system/services/services.c b/reactos/subsys/system/services/services.c index 4db7c444ecb..1a70ef11f3f 100644 --- a/reactos/subsys/system/services/services.c +++ b/reactos/subsys/system/services/services.c @@ -40,7 +40,7 @@ #define NDEBUG #include - +int WINAPI RegisterServicesProcess(DWORD ServicesProcessId); /* GLOBALS ******************************************************************/ diff --git a/reactos/subsys/system/services/services.xml b/reactos/subsys/system/services/services.xml index f98776b69bc..46004e193ef 100644 --- a/reactos/subsys/system/services/services.xml +++ b/reactos/subsys/system/services/services.xml @@ -1,5 +1,6 @@ . + ntdll kernel32 user32 diff --git a/reactos/subsys/system/sndvol32/Sv.rc b/reactos/subsys/system/sndvol32/Sv.rc new file mode 100644 index 00000000000..867d6f19567 --- /dev/null +++ b/reactos/subsys/system/sndvol32/Sv.rc @@ -0,0 +1,61 @@ +///////////////////////////////////////////////////////////////////////////// +// Swedish (SE) resources +/* + * Copyright 2005 David Nordenberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT + +IDM_MAINMENU MENU DISCARDABLE +BEGIN + POPUP "&Alternativ" + BEGIN + MENUITEM "&Inställningar", IDC_PROPERTIES + MENUITEM "A&vancerade kontroller", IDC_ADVANCED_CONTROLS + MENUITEM SEPARATOR + MENUITEM "&Avsluta", IDC_EXIT + END + POPUP "&Hjälp" + BEGIN + MENUITEM "&Hjälpavsnitt", IDC_HELP_TOPICS + MENUITEM SEPARATOR + MENUITEM "&Om ...", IDC_ABOUT + END +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_SNDVOL32 "Volymkontroll" + IDS_NOMIXERDEVICES "Det finns inga ljudmixningsenheter tillgängliga! Programmet kommer nu att avslutas." +END + +IDD_PREFERENCES DIALOGEX 0, 0, 224, 250 +STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Inställningar" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "&Mixerenhet:", -1, 7,8,48,9 + COMBOBOX IDC_MIXERDEVICE, 55,8,162,80, CBS_DROPDOWNLIST | WS_TABSTOP + GROUPBOX "Justera volymen för:", -1, 7,25,211,77 + PUSHBUTTON "&Uppspelning", IDC_PLAYBACK, 13,43,47,8, BS_AUTORADIOBUTTON + PUSHBUTTON "&Inspelning", IDC_RECORDING, 13,61,47,8, BS_AUTORADIOBUTTON + PUSHBUTTON "&Andra:", IDC_OTHER, 13,80,42,8, BS_AUTORADIOBUTTON | WS_DISABLED + COMBOBOX IDC_LINE, 55,80,155,50, CBS_DROPDOWNLIST | WS_TABSTOP | WS_DISABLED + + PUSHBUTTON "OK", IDOK, 114,226,50,14 + PUSHBUTTON "Avbryt", IDCANCEL, 168,226,50,14 +END diff --git a/reactos/subsys/system/sndvol32/sndvol32.rc b/reactos/subsys/system/sndvol32/sndvol32.rc index b729c75f1a5..addf9df702a 100644 --- a/reactos/subsys/system/sndvol32/sndvol32.rc +++ b/reactos/subsys/system/sndvol32/sndvol32.rc @@ -15,4 +15,5 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US IDI_MAINAPP ICON DISCARDABLE resources/sndvol32.ico #include "En.rc" -#include "DE.rc" +#include "De.rc" +#include "Sv.rc" diff --git a/reactos/subsys/system/taskmgr/De.rc b/reactos/subsys/system/taskmgr/De.rc index ab07c1ccebe..ad892364c7e 100644 --- a/reactos/subsys/system/taskmgr/De.rc +++ b/reactos/subsys/system/taskmgr/De.rc @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////// -// Deutsch (Austria) -// resources by Klemens Friedl frik85 at hotmail dot com http://www.reactos.net.tc +// Deutsch +// resource file by Klemens Friedl (frik85@reactos.at; http://frik85.reactos.at) #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 @@ -583,6 +583,7 @@ STRINGTABLE DISCARDABLE BEGIN IDS_APP_TITLE "Task-Manager" IDC_TASKMGR "Task-Manager" + IDS_IDLE_PROCESS "Leerlaufprozess" END STRINGTABLE DISCARDABLE @@ -649,5 +650,63 @@ BEGIN IDS_LICENSE "Dieses Programm ist kostenlos; Sie können es frei verteilen mit od. ohne Änderungen unter der GNU Lesser General Public License wie es von der Free Software Foundation veröffentlicht wurde; entweder Version 2.1 der Lizenz, oder eine spätere Version (ihrer Wahl).\r\n\r\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA." END -#endif // Deutsch (Austria) resources by Klemens Friedl +STRINGTABLE DISCARDABLE +BEGIN + IDS_TAB_APPS "Anwendnungen" + IDS_TAB_PROCESSES "Prozesse" + IDS_TAB_PERFORMANCE "Systemleistung" + IDS_TAB_TASK "Anwendung" + IDS_TAB_STATUS "Status" + IDS_TAB_IMAGENAME "Name" + IDS_TAB_PID "PID" + IDS_TAB_USERNAME "Benutzername" + IDS_TAB_SESSIONID "Sitzungserkennung" + IDS_TAB_CPU "CPU-Auslastung" + IDS_TAB_CPUTIME "CPU-Zeit" + IDS_TAB_MEMUSAGE "Speicherauslastung" + IDS_TAB_PEAKMEMUSAGE "Veränd. der Speicherauslastung" + IDS_TAB_MEMDELTA "Maximale Speicherauslastung" + IDS_TAB_PAGEFAULT "Seitenfehler" + IDS_TAB_PFDELTA "Maximale Seitenfehler" + IDS_TAB_VMSIZE "Virtuelle Arbeitsspeichergröße" + IDS_TAB_PAGEDPOOL "Paged Pool" + IDS_TAB_NPPOOL "NP Pool" + IDS_TAB_BASEPRI "Basispri" + IDS_TAB_HANDLES "Handleanzahl" + IDS_TAB_THREADS "Threadanzahl" + IDS_TAB_USERPBJECTS "Benutzer-Objekte" + IDS_TAB_GDIOBJECTS "GDI-Objekte" + IDS_TAB_IOREADS "E/A (Lesen)" + IDS_TAB_IOWRITES "E/A (Schreiben)" + IDS_TAB_IOOTHER "E/A (Andere)" + IDS_TAB_IOREADBYTES "E/A-Bytes (Lesen)" + IDS_TAB_IOWRITESBYTES "E/A-Bytes (Schreiben)" + IDS_TAB_IOOTHERBYTES "E/A-Bytes (Andere)" + IDS_MENU_SELECTCOLUMNS "&Spalten auswählen..." + IDS_MENU_16BITTASK "&16-Bit-Tasks anzeigen" + IDS_MENU_WINDOWS "&Fenster" + IDS_MENU_LARGEICONS "&Große Symbole" + IDS_MENU_SMALLICONS "Kl&eine Symbole" + IDS_MENU_DETAILS "&Details" + IDS_MENU_ONEGRAPHALLCPUS "&Alle CPUs in einem Diagramm" + IDS_MENU_ONEGRAPHPERCPU "&Ein Diagramm pro CPU" + IDS_MENU_CPUHISTORY "&CPU Verlauf" + IDS_MENU_SHOWKERNELTIMES "&Kernel-Zeiten anzeigen" + IDS_CREATENEWTASK "Neuen Task erstellen" + IDS_CREATENEWTASK_DESC "Geben Sie Programme, Ordner, Dokumente oder Internetressourcen an, die geöffnet werden sollen." + IDS_MSG_ACCESSPROCESSAFF "Unable to Access or Set Process Affinity" + IDS_MSG_PROCESSONEPRO "Der Prozess muss eine Zugehörigkeit zu mindestens einem Prozessor aufweisen." + IDS_MSG_INVALIDOPTION "Ungültige Auswahl" + IDS_MSG_UNABLEDEBUGPROCESS "Unable to Debug Process" + IDS_MSG_WARNINGDEBUG "WARNUNG: Das Debuggen dieses Prozesses kann zu Datenverlust führen.\nSind Sie sicher, dass Sie diesen Prozess debuggen möchten?" + IDS_MSG_TASKMGRWARNING "Warnung vom Task-Manager" + IDS_MSG_WARNINGTERMINATING "WARNUNG: Das Abbrechen eines Prozesses kann zu\nunerwünschten Ergebnissen, einschließlich Datenverlust und\nSysteminstabilität, führen. Zustand und Daten des Prozesses\nwerden nicht mher gespeichert. Sind Sie sicher, dass Sie\nden Prozess abbrechen möchten?" + IDS_MSG_UNABLETERMINATEPRO "Unable to Terminate Process" + IDS_MSG_UNABLECHANGEPRIORITY "Unable to Change Priority" + IDS_MSG_WARNINGCHANGEPRIORITY "WARNUNG: Das Ändern der Prioritätsklasse dieses Prozesses\nkann zu unerwünschten Ergebnissen, einschl. Systeminstabilität, führen.\nSind Sie sicher, dass Sie ändern möchten?" + IDS_MSG_TRAYICONCPUUSAGE "CPU-Auslastung: %d%%" + +END + +#endif ///////////////////////////////////////////////////////////////////////////// diff --git a/reactos/subsys/system/taskmgr/Dk.rc b/reactos/subsys/system/taskmgr/Dk.rc new file mode 100644 index 00000000000..ddb7851061d --- /dev/null +++ b/reactos/subsys/system/taskmgr/Dk.rc @@ -0,0 +1,622 @@ +///////////////////////////////////////////////////////////////////////////// +// Danish resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_DANISH, SUBLANG_DEFAULT +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_TASKMANAGER MENU DISCARDABLE +BEGIN + POPUP "&Fil" + BEGIN + MENUITEM "&Ny Opgave (Kør...)", ID_FILE_NEW + MENUITEM SEPARATOR + MENUITEM "A&slut Opgavestyring", ID_FILE_EXIT + END + POPUP "&Indstillinger" + BEGIN + MENUITEM "&Altid Øverst", ID_OPTIONS_ALWAYSONTOP + , CHECKED + MENUITEM "&Minimere Ved Brug", ID_OPTIONS_MINIMIZEONUSE + , CHECKED + MENUITEM "&Skjul Når Minimeret", ID_OPTIONS_HIDEWHENMINIMIZED + , CHECKED + MENUITEM "&Vis 16-bit Opgaver", ID_OPTIONS_SHOW16BITTASKS + , CHECKED + END + POPUP "&Vis" + BEGIN + MENUITEM "&Opdatere Nu", ID_VIEW_REFRESH + POPUP "&Opdaterings Hastighed" + BEGIN + MENUITEM "&Høj", ID_VIEW_UPDATESPEED_HIGH + MENUITEM "&Normal", ID_VIEW_UPDATESPEED_NORMAL + , CHECKED + MENUITEM "&Lav", ID_VIEW_UPDATESPEED_LOW + MENUITEM "&Pauset", ID_VIEW_UPDATESPEED_PAUSED + + END + MENUITEM SEPARATOR + MENUITEM "St&ore Ikoner", ID_VIEW_LARGE + MENUITEM "S&må Ikoner", ID_VIEW_SMALL + MENUITEM "&Detaljer", ID_VIEW_DETAILS, CHECKED + MENUITEM "&Vælg Kolonner...", ID_VIEW_SELECTCOLUMNS + POPUP "&CPU Historie" + BEGIN + MENUITEM "&En Graf, Alle CPUer", ID_VIEW_CPUHISTORY_ONEGRAPHALL + + MENUITEM "En Graf &Pr CPU", ID_VIEW_CPUHISTORY_ONEGRAPHPERCPU + , CHECKED + END + MENUITEM "&Vis Kernel Tider", ID_VIEW_SHOWKERNELTIMES + END + POPUP "&Vinduer" + BEGIN + MENUITEM "Tile &Horisontalt", ID_WINDOWS_TILEHORIZONTALLY + MENUITEM "Tile &Verticalt", ID_WINDOWS_TILEVERTICALLY + MENUITEM "&Minimere", ID_WINDOWS_MINIMIZE + MENUITEM "Ma&ksimeree", ID_WINDOWS_MAXIMIZE + MENUITEM "&Cascade", ID_WINDOWS_CASCADE + MENUITEM "&Bring til front", ID_WINDOWS_BRINGTOFRONT + END + POPUP "&Hjælp" + BEGIN + MENUITEM "Opgavestyring &Hjælpe Emner", ID_HELP_TOPICS + MENUITEM SEPARATOR + MENUITEM "&Omkring Opgavestyring", ID_HELP_ABOUT + END +END + +IDR_WINDOWSMENU MENU DISCARDABLE +BEGIN + MENUITEM "Tile &Horisontalt", ID_WINDOWS_TILEHORIZONTALLY + MENUITEM "Tile &Verticalt", ID_WINDOWS_TILEVERTICALLY + MENUITEM "&Minimere", ID_WINDOWS_MINIMIZE + MENUITEM "Ma&ksimere", ID_WINDOWS_MAXIMIZE + MENUITEM "&Detaljer", ID_WINDOWS_CASCADE + MENUITEM "&Bring til front", ID_WINDOWS_BRINGTOFRONT +END + +IDR_APPLICATION_PAGE_CONTEXT1 MENU DISCARDABLE +BEGIN + POPUP "DUMMY" + BEGIN + MENUITEM "&Ny Opgave (Kør...)", ID_FILE_NEW + MENUITEM SEPARATOR + MENUITEM "St&ore ikoner", ID_VIEW_LARGE + MENUITEM "S&må Ikoner", ID_VIEW_SMALL + MENUITEM "&Detaljer", ID_VIEW_DETAILS, CHECKED + END +END + +IDR_APPLICATION_PAGE_CONTEXT2 MENU DISCARDABLE +BEGIN + POPUP "DUMMY" + BEGIN + MENUITEM "&Skift Til", ID_APPLICATION_PAGE_SWITCHTO + MENUITEM "&Bring til front", ID_WINDOWS_BRINGTOFRONT + MENUITEM SEPARATOR + MENUITEM "Tile &Horisontalt", ID_WINDOWS_TILEHORIZONTALLY + MENUITEM "Tile &Verticalt", ID_WINDOWS_TILEVERTICALLY + MENUITEM "&Minimere", ID_WINDOWS_MINIMIZE + MENUITEM "Ma&ksimere", ID_WINDOWS_MAXIMIZE + MENUITEM "&Cascade", ID_WINDOWS_CASCADE + MENUITEM SEPARATOR + MENUITEM "&Afslut Opgave", ID_APPLICATION_PAGE_ENDTASK + MENUITEM "&Gå Til Process", ID_APPLICATION_PAGE_GOTOPROCESS + + END +END + +IDR_TRAY_POPUP MENU DISCARDABLE +BEGIN + POPUP "DUMMY" + BEGIN + MENUITEM "&Gendan", ID_RESTORE + MENUITEM "&Luk", ID_FILE_EXIT + MENUITEM SEPARATOR + MENUITEM "&Altid Øverest", ID_OPTIONS_ALWAYSONTOP + END +END + +IDR_PROCESS_PAGE_CONTEXT MENU DISCARDABLE +BEGIN + POPUP "DUMMY" + BEGIN + MENUITEM "&Afslut Process", ID_PROCESS_PAGE_ENDPROCESS + MENUITEM "Afslut Process &Træ", ID_PROCESS_PAGE_ENDPROCESSTREE + + MENUITEM "&Debug", ID_PROCESS_PAGE_DEBUG + MENUITEM SEPARATOR + POPUP "Sæt &prioritet" + BEGIN + MENUITEM "&Højeste", ID_PROCESS_PAGE_SETPRIORITY_REALTIME + + MENUITEM "&Høj", ID_PROCESS_PAGE_SETPRIORITY_HIGH + + MENUITEM "&Over Normal", ID_PROCESS_PAGE_SETPRIORITY_ABOVENORMAL + + MENUITEM "&Normal", ID_PROCESS_PAGE_SETPRIORITY_NORMAL + + MENUITEM "&Under Normal", ID_PROCESS_PAGE_SETPRIORITY_BELOWNORMAL + + MENUITEM "&Lav", ID_PROCESS_PAGE_SETPRIORITY_LOW + + END + MENUITEM "Sæt &Affinitet...", ID_PROCESS_PAGE_SETAFFINITY + MENUITEM "Redigere Debug &Kanaler...", ID_PROCESS_PAGE_DEBUGCHANNELS + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 259, 210 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Omkring Opgavestyring" +FONT 8, "Tahoma" +BEGIN + CONTROL "Opgavestyring v1.0\nKopibeskyttet (C) 1999 - 2001\naf Brian Palmer (brianp@reactos.org)", + IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,53,28, + 122,26 + DEFPUSHBUTTON "OK",IDOK,183,189,44,14,WS_GROUP + ICON IDI_TASKMANAGER,IDC_STATIC,19,30,20,20 + EDITTEXT IDC_LICENSE_EDIT,53,63,174,107,ES_MULTILINE | + ES_READONLY | WS_VSCROLL +END + +IDD_TASKMGR_DIALOG DIALOG DISCARDABLE 0, 0, 264, 246 +STYLE DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Opgavestyring" +MENU IDR_TASKMANAGER +FONT 8, "Tahoma" +BEGIN + CONTROL "Tab1",IDC_TAB,"SysTabControl32",WS_TABSTOP,3,3,257,228 +END + +IDD_APPLICATION_PAGE DIALOG DISCARDABLE 0, 0, 247, 210 +STYLE DS_CONTROL | WS_CHILD | WS_CLIPCHILDREN +FONT 8, "Tahoma" +BEGIN + CONTROL "List2",IDC_APPLIST,"SysListView32",LVS_REPORT | + LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,7,233,177 + PUSHBUTTON "&Ny Opgave...",IDC_NEWTASK,187,189,53,14 + PUSHBUTTON "&Skift Til",IDC_SWITCHTO,131,189,53,14,WS_DISABLED + PUSHBUTTON "&Afslut Opgave",IDC_ENDTASK,75,189,53,14,WS_DISABLED +END + +IDD_PROCESS_PAGE DIALOG DISCARDABLE 0, 0, 247, 210 +STYLE DS_CONTROL | WS_CHILD | WS_CLIPCHILDREN +CAPTION "KMK" +FONT 8, "Tahoma" +BEGIN + CONTROL "List2",IDC_PROCESSLIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_OWNERDATA | + WS_BORDER | WS_TABSTOP,7,7,233,177 + PUSHBUTTON "&Afslut Proces",IDC_ENDPROCESS,171,189,69,14 + CONTROL "&Vis Processor fra alle brugere",IDC_SHOWALLPROCESSES, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,191,111,10 +END + +//IDD_PERFORMANCE_PAGE DIALOGEX 0, 0, 247, 210 +IDD_PERFORMANCE_PAGE DIALOGEX DISCARDABLE 0, 0, 247, 210 +STYLE DS_CONTROL | WS_CHILD | WS_CLIPCHILDREN +FONT 8, "Tahoma" +BEGIN + GROUPBOX "CPU Forbrug",IDC_CPU_USAGE_FRAME,5,5,60,54,WS_TABSTOP, WS_EX_TRANSPARENT + GROUPBOX "Hukommelse Forbrug",IDC_MEM_USAGE_FRAME,5,63,60,54,BS_LEFTTEXT, WS_EX_TRANSPARENT + GROUPBOX "Ialt",IDC_TOTALS_FRAME,5,122,111,39,0,WS_EX_TRANSPARENT + GROUPBOX "Aftaget Forbrug (K)",IDC_COMMIT_CHARGE_FRAME,5,166,111,39,0,WS_EX_TRANSPARENT + GROUPBOX "Fysisk Hukommelse (K)",IDC_PHYSICAL_MEMORY_FRAME,131,122,111,39,0,WS_EX_TRANSPARENT + GROUPBOX "Kernel Hukommelse (K)",IDC_KERNEL_MEMORY_FRAME,131,166,111,39,0,WS_EX_TRANSPARENT + LTEXT "Håndtag",IDS_TOTALS_HANDLE_COUNT,12,131,27,8 + LTEXT "Tråde",IDS_TOTALS_THREAD_COUNT,12,140,27,8 + LTEXT "Processor",IDS_TOTALS_PROCESS_COUNT,12,149,34,8 + EDITTEXT IDC_TOTALS_HANDLE_COUNT,65,131,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_TOTALS_THREAD_COUNT,65,140,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_TOTALS_PROCESS_COUNT,65,149,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + LTEXT "Ialt",IDS_COMMIT_CHARGE_TOTAL,12,175,27,8 + LTEXT "Begrænsning",IDS_COMMIT_CHARGE_LIMIT,12,184,15,8 + LTEXT "Top",IDS_COMMIT_CHARGE_PEAK,12,193,34,8 + EDITTEXT IDC_COMMIT_CHARGE_TOTAL,65,174,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_COMMIT_CHARGE_LIMIT,65,184,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_COMMIT_CHARGE_PEAK,65,193,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + LTEXT "Ialt",IDS_PHYSICAL_MEMORY_TOTAL,137,131,27,8 + LTEXT "Tilgængelig",IDS_PHYSICAL_MEMORY_AVAILABLE,137,140,30,8 + LTEXT "System Cache",IDS_PHYSICAL_MEMORY_SYSTEM_CACHE,137,149,46,8 + EDITTEXT IDC_PHYSICAL_MEMORY_TOTAL,185,131,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_PHYSICAL_MEMORY_AVAILABLE,185,140,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_PHYSICAL_MEMORY_SYSTEM_CACHE,185,149,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + LTEXT "Ialt",IDS_KERNEL_MEMORY_TOTAL,137,174,27,8 + LTEXT "Allokeret",IDS_KERNEL_MEMORY_PAGED,137,184,21,8 + LTEXT "Ikke Allokeret",IDS_KERNEL_MEMORY_NONPAGED,137,193,34,8 + EDITTEXT IDC_KERNEL_MEMORY_TOTAL,185,174,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_KERNEL_MEMORY_PAGED,185,184,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_KERNEL_MEMORY_NONPAGED,185,193,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + GROUPBOX "CPU Usage History",IDC_CPU_USAGE_HISTORY_FRAME,74,5,168,54,0,WS_EX_TRANSPARENT + GROUPBOX "Hukommelses Forbrug Historik",IDC_MEMORY_USAGE_HISTORY_FRAME,74,63,168,54,0,WS_EX_TRANSPARENT + PUSHBUTTON "CPU Forbrug Visning",IDC_CPU_USAGE_GRAPH,12,17,47,37,0, + WS_EX_CLIENTEDGE + PUSHBUTTON "Hukommelses Forbrug Visning",IDC_MEM_USAGE_GRAPH,12,75,47,37,0, + WS_EX_CLIENTEDGE + PUSHBUTTON "CPU Forbrug Historik",IDC_CPU_USAGE_HISTORY_GRAPH,81,17, + 153,37,0,WS_EX_CLIENTEDGE + PUSHBUTTON "Hukommelses Forbrug Historik",IDC_MEM_USAGE_HISTORY_GRAPH,81,75, + 153,37,0,WS_EX_CLIENTEDGE +END + +IDD_DEBUG_CHANNELS_DIALOG DIALOG DISCARDABLE 0, 0, 247, 210 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Debug Kanaler" +FONT 8, "Tahoma" +BEGIN + CONTROL "List2",IDC_DEBUG_CHANNELS_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP | + LVS_SORTASCENDING,7,7,233,177 + PUSHBUTTON "Luk",IDOK,171,189,69,14 +END + +IDD_AFFINITY_DIALOG DIALOG DISCARDABLE 0, 0, 231, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Processor Affinitet" +FONT 8, "Tahoma" +BEGIN + DEFPUSHBUTTON "OK",IDOK,120,133,50,14 + PUSHBUTTON "Fortryd",IDCANCEL,174,133,50,14 + LTEXT "Processor Affinitets indstillingen kontrollere hvilken CPUer processen har lov at eksekvere på.", + IDC_STATIC,5,5,220,16 + CONTROL "CPU 0",IDC_CPU0,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 11,28,37,10 + CONTROL "CPU 1",IDC_CPU1,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,11,41,37,10 + CONTROL "CPU 2",IDC_CPU2,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,11,54,37,10 + CONTROL "CPU 3",IDC_CPU3,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,11,67,37,10 + CONTROL "CPU 4",IDC_CPU4,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,11,80,37,10 + CONTROL "CPU 5",IDC_CPU5,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,11,93,37,10 + CONTROL "CPU 6",IDC_CPU6,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,11,106,37,10 + CONTROL "CPU 7",IDC_CPU7,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,11,119,37,10 + CONTROL "CPU 8",IDC_CPU8,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,61,28,37,10 + CONTROL "CPU 9",IDC_CPU9,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,61,41,37,10 + CONTROL "CPU 10",IDC_CPU10,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,54,41,10 + CONTROL "CPU 11",IDC_CPU11,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,67,41,10 + CONTROL "CPU 12",IDC_CPU12,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,80,41,10 + CONTROL "CPU 13",IDC_CPU13,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,93,41,10 + CONTROL "CPU 14",IDC_CPU14,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,106,41,10 + CONTROL "CPU 15",IDC_CPU15,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,119,41,10 + CONTROL "CPU 16",IDC_CPU16,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,28,41,10 + CONTROL "CPU 17",IDC_CPU17,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,41,41,10 + CONTROL "CPU 18",IDC_CPU18,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,54,41,10 + CONTROL "CPU 19",IDC_CPU19,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,67,41,10 + CONTROL "CPU 20",IDC_CPU20,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,80,41,10 + CONTROL "CPU 21",IDC_CPU21,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,93,41,10 + CONTROL "CPU 22",IDC_CPU22,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,106,41,10 + CONTROL "CPU 23",IDC_CPU23,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,119,41,10 + CONTROL "CPU 24",IDC_CPU24,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,28,41,10 + CONTROL "CPU 25",IDC_CPU25,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,41,41,10 + CONTROL "CPU 26",IDC_CPU26,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,54,41,10 + CONTROL "CPU 27",IDC_CPU27,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,67,41,10 + CONTROL "CPU 28",IDC_CPU28,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,80,41,10 + CONTROL "CPU 29",IDC_CPU29,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,93,41,10 + CONTROL "CPU 30",IDC_CPU30,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,106,41,10 + CONTROL "CPU 31",IDC_CPU31,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,119,41,10 +END + +IDD_COLUMNS_DIALOG DIALOG DISCARDABLE 0, 0, 195, 199 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Vælg Kolonner" +FONT 8, "Tahoma" +BEGIN + DEFPUSHBUTTON "OK",IDOK,84,178,50,14 + PUSHBUTTON "Fortryd",IDCANCEL,138,178,50,14 + LTEXT "Vælg de Kolonner som skal vises under Processor.", + IDC_STATIC,7,7,181,17 + CONTROL "&Billede Navn",IDC_IMAGENAME,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,7,28,56,10 + CONTROL "&PID (Process Identifikation)",IDC_PID,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7, 39, 100, 10 + CONTROL "&CPU Forbrug",IDC_CPUUSAGE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,50,53,10 + CONTROL "CPU &Tid",IDC_CPUTIME,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,61,48,10 + CONTROL "&Huk. Forbrug",IDC_MEMORYUSAGE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,72,63,10 + CONTROL "Huk. Forbrug &Delta",IDC_MEMORYUSAGEDELTA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,83,82,10 + CONTROL "To&p Huk. Forbrug",IDC_PEAKMEMORYUSAGE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,94,82,10 + CONTROL "Side &Fejl",IDC_PAGEFAULTS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,105,53,10 + CONTROL "&USER Objektor",IDC_USEROBJECTS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,116,62,10 + CONTROL "I/O Læsning",IDC_IOREADS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,127,49,10 + CONTROL "I/O Læste Bytes",IDC_IOREADBYTES,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,138,65,10 + CONTROL "&Session ID",IDC_SESSIONID,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,149,50,10 + CONTROL "Bruger &Navn",IDC_USERNAME,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,160,51,10 + CONTROL "Side F&ejl Delta",IDC_PAGEFAULTSDELTA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,107,28,72,10 + CONTROL "&Virtuel Huk. Størrelse",IDC_VIRTUALMEMORYSIZE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,107,39,77,10 + CONTROL "Cach&ed Pool",IDC_PAGEDPOOL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,107,50,53,10 + CONTROL "Ikke C&ached Pool",IDC_NONPAGEDPOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,107,61,67,10 + CONTROL "Base P&rioritet",IDC_BASEPRIORITY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,107,72,55,10 + CONTROL "Antal Af &Håndtag",IDC_HANDLECOUNT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,107,83,59,10 + CONTROL "&Antal Af Tråde",IDC_THREADCOUNT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,107,94,59,10 + CONTROL "GDI Objektor",IDC_GDIOBJECTS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,107,105,55,10 + CONTROL "I/O Skrivning",IDC_IOWRITES,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,107,116,49,10 + CONTROL "I/O Skrevet Bytes",IDC_IOWRITEBYTES,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,107,127,65,10 + CONTROL "I/O Andet",IDC_IOOTHER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,107,138,46,10 + CONTROL "I/O Andre Bytes",IDC_IOOTHERBYTES,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,107,149,65,10 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""resource.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 252 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_TASKMGR_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 260 + TOPMARGIN, 3 + BOTTOMMARGIN, 231 + END + + IDD_APPLICATION_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 240 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_PROCESS_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 240 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_PERFORMANCE_PAGE, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 242 + VERTGUIDE, 12 + VERTGUIDE, 65 + VERTGUIDE, 110 + TOPMARGIN, 5 + BOTTOMMARGIN, 205 + END + + IDD_AFFINITY_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 224 + VERTGUIDE, 11 + VERTGUIDE, 61 + VERTGUIDE, 116 + VERTGUIDE, 175 + TOPMARGIN, 7 + BOTTOMMARGIN, 147 + HORZGUIDE, 28 + HORZGUIDE, 38 + HORZGUIDE, 51 + HORZGUIDE, 64 + HORZGUIDE, 77 + HORZGUIDE, 90 + HORZGUIDE, 103 + HORZGUIDE, 116 + HORZGUIDE, 129 + END + + IDD_COLUMNS_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 188 + VERTGUIDE, 107 + TOPMARGIN, 7 + BOTTOMMARGIN, 192 + HORZGUIDE, 28 + END +END +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_APP_TITLE "Opgavestyring" + IDC_TASKMGR "Opgavestyring" + IDS_IDLE_PROCESS "System Idle Process" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Kører et nyt Program" + ID_OPTIONS_ALWAYSONTOP "Opgavestyring bliver i front af alle de andre vinduer med mindre Opgavestyring er minimeret" + ID_OPTIONS_MINIMIZEONUSE + "Opgavestyring er minimeret når en Gå til operation er fortaget" + ID_OPTIONS_HIDEWHENMINIMIZED "Skjul Opgavestyring ved minimering" + ID_VIEW_REFRESH "Gennemtving Opgavestyring til at opdatere nu" + ID_VIEW_LARGE "Vis Opgaver med store ikoner" + ID_VIEW_SMALL "Vis Opgaver med små ikoner" + ID_VIEW_DETAILS "Vis information om alle opgaver" + ID_VIEW_UPDATESPEED_HIGH "Opdatere visning 2 gange per sekund" + ID_VIEW_UPDATESPEED_NORMAL "Opdatere visning hvert 2 sekund" + ID_VIEW_UPDATESPEED_LOW "Opdatere visning hvert 4 sekund" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_UPDATESPEED_PAUSED "Opdatere ikke visning automatisk" + ID_WINDOWS_TILEHORIZONTALLY + "Tiles the windows horizontally on the desktop" + ID_WINDOWS_TILEVERTICALLY "Tiles the windows vertically on the desktop" + ID_WINDOWS_MINIMIZE "Minimizes the windows" + ID_WINDOWS_MAXIMIZE "Maximizes the windows" + ID_WINDOWS_CASCADE "Cascades the windows diagonally on the desktop" + ID_WINDOWS_BRINGTOFRONT "Brings the window front, but does not switch to it" + ID_HELP_TOPICS "Viser Opgavestyring Hjælpe Emner" + ID_HELP_ABOUT "Viser program information, version nummer, of kopibeskyttelse" + ID_FILE_EXIT "Exits the Task Manager application" + ID_OPTIONS_SHOW16BITTASKS + "Viser 16-bit opgaver som kører under ntvdm.exe" + ID_VIEW_SELECTCOLUMNS "Vælg hvilke kolonner der vil være tilgængelig under Processor Siden" + ID_VIEW_SHOWKERNELTIMES "Viser kernel tider i performance graffen" + ID_VIEW_CPUHISTORY_ONEGRAPHALL + "En enkelt Historie graf der viser det totale CPU forbrug" + ID_VIEW_CPUHISTORY_ONEGRAPHPERCPU "Hver cpu har dens egen historie graf" + ID_APPLICATION_PAGE_SWITCHTO + "Bringer opgaven til fronten, og skifter fokus til denne opgave" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_ENDTASK "Fortæller den valgt opgave at den skal lukkes" + ID_GOTOPROCESS "Switches the focus to the process of the selected task" + ID_RESTORE "Gendanner Opgavestyring fra sin skjule position" + ID_PROCESS_PAGE_ENDPROCESS "Removes the process from the system" + ID_PROCESS_PAGE_ENDPROCESSTREE + "Removes this process and all descendants from the system" + ID_PROCESS_PAGE_DEBUG "Attaches the debugger to this process" + ID_PROCESS_PAGE_SETAFFINITY + "Kontrollere hvilken processorer processen er tilladt at køre på!" + ID_PROCESS_PAGE_SETPRIORITY_REALTIME + "Sets process to the REALTIME priority class" + ID_PROCESS_PAGE_SETPRIORITY_HIGH "Sets process to the HIGH priority class" + ID_PROCESS_PAGE_SETPRIORITY_ABOVENORMAL + "Sæt proces til Over Normal Priotet Klasse" + ID_PROCESS_PAGE_SETPRIORITY_NORMAL + "Sæt proces til Normal Priotet Klasse" + ID_PROCESS_PAGE_SETPRIORITY_BELOWNORMAL + "Sæt proces til Under Normal Priotet Klasse" + ID_PROCESS_PAGE_SETPRIORITY_LOW "Sæt proces til Lav Priotet Klasse" + IDS_LICENSE "Dette program er fri software; du kan formidle det og/eller ændre det under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/reactos/subsys/system/taskmgr/En.rc b/reactos/subsys/system/taskmgr/En.rc index 05188f948be..9fa11c8a159 100644 --- a/reactos/subsys/system/taskmgr/En.rc +++ b/reactos/subsys/system/taskmgr/En.rc @@ -538,6 +538,7 @@ STRINGTABLE DISCARDABLE BEGIN IDS_APP_TITLE "Task Manager" IDC_TASKMGR "Task Manager" + IDS_IDLE_PROCESS "System Idle Process" END STRINGTABLE DISCARDABLE @@ -604,6 +605,66 @@ BEGIN IDS_LICENSE "This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA." END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_TAB_APPS "Applications" + IDS_TAB_PROCESSES "Processes" + IDS_TAB_PERFORMANCE "Performance" + IDS_TAB_TASK "Task" + IDS_TAB_STATUS "Status" + IDS_TAB_IMAGENAME "Image Name" + IDS_TAB_PID "PID" + IDS_TAB_USERNAME "Username" + IDS_TAB_SESSIONID "Session ID" + IDS_TAB_CPU "CPU" + IDS_TAB_CPUTIME "CPU Time" + IDS_TAB_MEMUSAGE "Mem Usage" + IDS_TAB_PEAKMEMUSAGE "Peak Mem Usage" + IDS_TAB_MEMDELTA "Mem Delta" + IDS_TAB_PAGEFAULT "Page Faults" + IDS_TAB_PFDELTA "PF Delta" + IDS_TAB_VMSIZE "VM Size" + IDS_TAB_PAGEDPOOL "Paged Pool" + IDS_TAB_NPPOOL "NP Pool" + IDS_TAB_BASEPRI "Base Pri" + IDS_TAB_HANDLES "Handles" + IDS_TAB_THREADS "Threads" + IDS_TAB_USERPBJECTS "USER Objects" + IDS_TAB_GDIOBJECTS "GDI Objects" + IDS_TAB_IOREADS "I/O Reads" + IDS_TAB_IOWRITES "I/O Writes" + IDS_TAB_IOOTHER "I/O Other" + IDS_TAB_IOREADBYTES "I/O Read Bytes" + IDS_TAB_IOWRITESBYTES "I/O Write Bytes" + IDS_TAB_IOOTHERBYTES "I/O Other Bytes" + IDS_MENU_SELECTCOLUMNS "&Select Columns..." + IDS_MENU_16BITTASK "&Show 16-bit tasks" + IDS_MENU_WINDOWS "&Windows" + IDS_MENU_LARGEICONS "Lar&ge Icons" + IDS_MENU_SMALLICONS "S&mall Icons" + IDS_MENU_DETAILS "&Details" + IDS_MENU_ONEGRAPHALLCPUS "&One Graph, All CPUs" + IDS_MENU_ONEGRAPHPERCPU "One Graph &Per CPU" + IDS_MENU_CPUHISTORY "&CPU History" + IDS_MENU_SHOWKERNELTIMES "&Show Kernel Times" + IDS_CREATENEWTASK "Create New Task" + IDS_CREATENEWTASK_DESC "Type the name of a program, folder, document, or Internet resource, and Task Manager will open it for you." + IDS_MSG_ACCESSPROCESSAFF "Unable to Access or Set Process Affinity" + IDS_MSG_PROCESSONEPRO "The process must have affinity with at least one processor." + IDS_MSG_INVALIDOPTION "Invalid Option" + IDS_MSG_UNABLEDEBUGPROCESS "Unable to Debug Process" + IDS_MSG_WARNINGDEBUG "WARNING: Debugging this process may result in loss of data.\nAre you sure you wish to attach the debugger?" + IDS_MSG_TASKMGRWARNING "Task Manager Warning" + IDS_MSG_WARNINGTERMINATING "WARNING: Terminating a process can cause undesired\nresults including loss of data and system instability. The\nprocess will not be given the chance to save its state or\ndata before it is terminated. Are you sure you want to\nterminate the process?" + IDS_MSG_UNABLETERMINATEPRO "Unable to Terminate Process" + IDS_MSG_UNABLECHANGEPRIORITY "Unable to Change Priority" + IDS_MSG_WARNINGCHANGEPRIORITY "WARNING: Changing the priority class of this process may\ncause undesired results including system instability. Are you\nsure you want to change the priority class?" + IDS_MSG_TRAYICONCPUUSAGE "CPU Usage: %d%%" + +END + + #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/reactos/subsys/system/taskmgr/Es.rc b/reactos/subsys/system/taskmgr/Es.rc index c0b22333593..de594bd6dc8 100644 --- a/reactos/subsys/system/taskmgr/Es.rc +++ b/reactos/subsys/system/taskmgr/Es.rc @@ -611,6 +611,7 @@ STRINGTABLE DISCARDABLE BEGIN IDS_APP_TITLE "Administrador de Tareas" IDC_TASKMGR "Administrador de Tareas" + IDS_IDLE_PROCESS "System Idle Process" END STRINGTABLE DISCARDABLE diff --git a/reactos/subsys/system/taskmgr/Sv.rc b/reactos/subsys/system/taskmgr/Sv.rc new file mode 100644 index 00000000000..9cd0205a16b --- /dev/null +++ b/reactos/subsys/system/taskmgr/Sv.rc @@ -0,0 +1,686 @@ +///////////////////////////////////////////////////////////////////////////// +// Swedish (SE) resources +/* + * Copyright 2005 David Nordenberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_SWE) +#ifdef _WIN32 +LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT +#pragma code_page(850) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_TASKMANAGER MENU DISCARDABLE +BEGIN + POPUP "&Arkiv" + BEGIN + MENUITEM "&Ny Aktivitet (Kör...)", ID_FILE_NEW + MENUITEM SEPARATOR + MENUITEM "&Avsluta aktivitetshanteraren", ID_FILE_EXIT + END + POPUP "&Alternativ" + BEGIN + MENUITEM "&Alltid överst", ID_OPTIONS_ALWAYSONTOP + , CHECKED + MENUITEM "&Minimera vid användning", ID_OPTIONS_MINIMIZEONUSE + , CHECKED + MENUITEM "&Göm minimerad", ID_OPTIONS_HIDEWHENMINIMIZED + , CHECKED + MENUITEM "&Visa 16-bitarsaktiviteter", ID_OPTIONS_SHOW16BITTASKS + , CHECKED + END + POPUP "&Visa" + BEGIN + MENUITEM "&Uppdatera", ID_VIEW_REFRESH + POPUP "U&ppdateringshastighet" + BEGIN + MENUITEM "&Hög", ID_VIEW_UPDATESPEED_HIGH + MENUITEM "&Normal", ID_VIEW_UPDATESPEED_NORMAL + , CHECKED + MENUITEM "&Låg", ID_VIEW_UPDATESPEED_LOW + MENUITEM "&Fryst", ID_VIEW_UPDATESPEED_PAUSED + + END + MENUITEM SEPARATOR + MENUITEM "&Stora ikoner", ID_VIEW_LARGE + MENUITEM "S&må ikoner", ID_VIEW_SMALL + MENUITEM "&Detaljer", ID_VIEW_DETAILS, CHECKED + MENUITEM "&Välj kolumner...", ID_VIEW_SELECTCOLUMNS + POPUP "&CPU Historik" + BEGIN + MENUITEM "En visare för &alla processorer", ID_VIEW_CPUHISTORY_ONEGRAPHALL + + MENUITEM "&En visare per processor", ID_VIEW_CPUHISTORY_ONEGRAPHPERCPU + , CHECKED + END + MENUITEM "&Visa kernel-tider", ID_VIEW_SHOWKERNELTIMES + END + POPUP "&Fönster" + BEGIN + MENUITEM "Ordna &horizontellt", ID_WINDOWS_TILEHORIZONTALLY + MENUITEM "Ordna &vertikalt", ID_WINDOWS_TILEVERTICALLY + MENUITEM "&Minimera", ID_WINDOWS_MINIMIZE + MENUITEM "Ma&ximera", ID_WINDOWS_MAXIMIZE + MENUITEM "&Överlappande", ID_WINDOWS_CASCADE + MENUITEM "&För till förgrunden", ID_WINDOWS_BRINGTOFRONT + END + POPUP "&Hjälp" + BEGIN + MENUITEM "Aktivitetshanteraren &hjälpavsnitt", ID_HELP_TOPICS + MENUITEM SEPARATOR + MENUITEM "&Om Aktivitetshanteraren", ID_HELP_ABOUT + END +END + +IDR_WINDOWSMENU MENU DISCARDABLE +BEGIN + MENUITEM "Ordna &horizontellt", ID_WINDOWS_TILEHORIZONTALLY + MENUITEM "Ordna &vertikalt", ID_WINDOWS_TILEVERTICALLY + MENUITEM "&Minimera", ID_WINDOWS_MINIMIZE + MENUITEM "Ma&ximera", ID_WINDOWS_MAXIMIZE + MENUITEM "&Överlappande", ID_WINDOWS_CASCADE + MENUITEM "&För till förgrunden", ID_WINDOWS_BRINGTOFRONT +END + +IDR_APPLICATION_PAGE_CONTEXT1 MENU DISCARDABLE +BEGIN + POPUP "DUMMY" + BEGIN + MENUITEM "&Ny aktivitet (Kör...)", ID_FILE_NEW + MENUITEM SEPARATOR + MENUITEM "&Stora ikoner", ID_VIEW_LARGE + MENUITEM "S&må ikoner", ID_VIEW_SMALL + MENUITEM "&Detaljer", ID_VIEW_DETAILS, CHECKED + END +END + +IDR_APPLICATION_PAGE_CONTEXT2 MENU DISCARDABLE +BEGIN + POPUP "DUMMY" + BEGIN + MENUITEM "&Växla till", ID_APPLICATION_PAGE_SWITCHTO + MENUITEM "&För till förgrunden", ID_WINDOWS_BRINGTOFRONT + MENUITEM SEPARATOR + MENUITEM "Ordna &horizontellt", ID_WINDOWS_TILEHORIZONTALLY + MENUITEM "Ordna &vertikalt", ID_WINDOWS_TILEVERTICALLY + MENUITEM "&Minimera", ID_WINDOWS_MINIMIZE + MENUITEM "Ma&ximera", ID_WINDOWS_MAXIMIZE + MENUITEM "&Överlappande", ID_WINDOWS_CASCADE + MENUITEM SEPARATOR + MENUITEM "&Avsluta aktiviteten", ID_APPLICATION_PAGE_ENDTASK + MENUITEM "&Gå till process", ID_APPLICATION_PAGE_GOTOPROCESS + + END +END + +IDR_TRAY_POPUP MENU DISCARDABLE +BEGIN + POPUP "DUMMY" + BEGIN + MENUITEM "&Återställ", ID_RESTORE + MENUITEM "&Stäng", ID_FILE_EXIT + MENUITEM SEPARATOR + MENUITEM "&Alltid överst", ID_OPTIONS_ALWAYSONTOP + END +END + +IDR_PROCESS_PAGE_CONTEXT MENU DISCARDABLE +BEGIN + POPUP "DUMMY" + BEGIN + MENUITEM "&Avsluta process", ID_PROCESS_PAGE_ENDPROCESS + MENUITEM "Avsluta process&träd", ID_PROCESS_PAGE_ENDPROCESSTREE + + MENUITEM "&Felsök", ID_PROCESS_PAGE_DEBUG + MENUITEM SEPARATOR + POPUP "Ändra &prioritet" + BEGIN + MENUITEM "&Realtid", ID_PROCESS_PAGE_SETPRIORITY_REALTIME + + MENUITEM "&Hög", ID_PROCESS_PAGE_SETPRIORITY_HIGH + + MENUITEM "&Över normal", ID_PROCESS_PAGE_SETPRIORITY_ABOVENORMAL + + MENUITEM "&Normal", ID_PROCESS_PAGE_SETPRIORITY_NORMAL + + MENUITEM "&Under normal", ID_PROCESS_PAGE_SETPRIORITY_BELOWNORMAL + + MENUITEM "&Låg", ID_PROCESS_PAGE_SETPRIORITY_LOW + + END + MENUITEM "Välj &källa (FIXME)...", ID_PROCESS_PAGE_SETAFFINITY + MENUITEM "Ändra felsökningskanaler...", ID_PROCESS_PAGE_DEBUGCHANNELS + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 259, 210 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Om Aktivitetshanteraren" +FONT 8, "Tahoma" +BEGIN + CONTROL "Aktivitetshanteraren v1.0\nCopyright (C) 1999 - 2001\nby Brian Palmer (brianp@reactos.org)", + IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,53,28, + 122,26 + DEFPUSHBUTTON "OK",IDOK,183,189,44,14,WS_GROUP + ICON IDI_TASKMANAGER,IDC_STATIC,19,30,20,20 + EDITTEXT IDC_LICENSE_EDIT,53,63,174,107,ES_MULTILINE | + ES_READONLY | WS_VSCROLL +END + +IDD_TASKMGR_DIALOG DIALOG DISCARDABLE 0, 0, 264, 246 +STYLE DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Aktivitetshanteraren" +MENU IDR_TASKMANAGER +FONT 8, "Tahoma" +BEGIN + CONTROL "Tab1",IDC_TAB,"SysTabControl32",WS_TABSTOP,3,3,257,228 +END + +IDD_APPLICATION_PAGE DIALOG DISCARDABLE 0, 0, 247, 210 +STYLE DS_CONTROL | WS_CHILD | WS_CLIPCHILDREN +FONT 8, "Tahoma" +BEGIN + CONTROL "List2",IDC_APPLIST,"SysListView32",LVS_REPORT | + LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,7,233,177 + PUSHBUTTON "&Ny aktivitet...",IDC_NEWTASK,175,189,65,14 + PUSHBUTTON "&Växla till",IDC_SWITCHTO,120,189,53,14,WS_DISABLED + PUSHBUTTON "&Avsluta aktiviteten",IDC_ENDTASK,34,189,83,14,WS_DISABLED +END + +IDD_PROCESS_PAGE DIALOG DISCARDABLE 0, 0, 247, 210 +STYLE DS_CONTROL | WS_CHILD | WS_CLIPCHILDREN +FONT 8, "Tahoma" +BEGIN + CONTROL "List2",IDC_PROCESSLIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_OWNERDATA | + WS_BORDER | WS_TABSTOP,7,7,233,177 + PUSHBUTTON "&Avsluta process",IDC_ENDPROCESS,171,189,69,14 + CONTROL "&Visa processer från alla användare",IDC_SHOWALLPROCESSES, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,191,156,10 +END + +//IDD_PERFORMANCE_PAGE DIALOGEX 0, 0, 247, 210 +IDD_PERFORMANCE_PAGE DIALOGEX DISCARDABLE 0, 0, 247, 210 +STYLE DS_CONTROL | WS_CHILD | WS_CLIPCHILDREN +FONT 8, "Tahoma" +BEGIN + GROUPBOX "Processoranv.",IDC_CPU_USAGE_FRAME,5,5,60,54,WS_TABSTOP, WS_EX_TRANSPARENT + GROUPBOX "Minnesanv.",IDC_MEM_USAGE_FRAME,5,63,60,54,BS_LEFTTEXT, WS_EX_TRANSPARENT + GROUPBOX "Totalt",IDC_TOTALS_FRAME,5,122,111,39,0,WS_EX_TRANSPARENT + GROUPBOX "Upptaget minne (K)",IDC_COMMIT_CHARGE_FRAME,5,166,111,39,0,WS_EX_TRANSPARENT + GROUPBOX "Fysiskt minne (K)",IDC_PHYSICAL_MEMORY_FRAME,131,122,111,39,0,WS_EX_TRANSPARENT + GROUPBOX "Kernel minne (K)",IDC_KERNEL_MEMORY_FRAME,131,166,111,39,0,WS_EX_TRANSPARENT + LTEXT "Referenser",IDS_TOTALS_HANDLE_COUNT,12,131,41,8 + LTEXT "Trådar",IDS_TOTALS_THREAD_COUNT,12,140,27,8 + LTEXT "Processer",IDS_TOTALS_PROCESS_COUNT,12,149,36,8 + EDITTEXT IDC_TOTALS_HANDLE_COUNT,65,131,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_TOTALS_THREAD_COUNT,65,140,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_TOTALS_PROCESS_COUNT,65,149,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + LTEXT "Totalt",IDS_COMMIT_CHARGE_TOTAL,12,175,27,8 + LTEXT "Gräns",IDS_COMMIT_CHARGE_LIMIT,12,184,21,8 + LTEXT "Topp",IDS_COMMIT_CHARGE_PEAK,12,193,34,8 + EDITTEXT IDC_COMMIT_CHARGE_TOTAL,65,174,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_COMMIT_CHARGE_LIMIT,65,184,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_COMMIT_CHARGE_PEAK,65,193,45,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + LTEXT "Totalt",IDS_PHYSICAL_MEMORY_TOTAL,137,131,27,8 + LTEXT "Tillgängligt",IDS_PHYSICAL_MEMORY_AVAILABLE,137,140,46,8 + LTEXT "Systemcache",IDS_PHYSICAL_MEMORY_SYSTEM_CACHE,137,149,46,8 + EDITTEXT IDC_PHYSICAL_MEMORY_TOTAL,185,131,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_PHYSICAL_MEMORY_AVAILABLE,185,140,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_PHYSICAL_MEMORY_SYSTEM_CACHE,185,149,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + LTEXT "Totalt",IDS_KERNEL_MEMORY_TOTAL,137,174,27,8 + LTEXT "Växlat",IDS_KERNEL_MEMORY_PAGED,137,184,24,8 + LTEXT "Icke växlat",IDS_KERNEL_MEMORY_NONPAGED,137,193,44,8 + EDITTEXT IDC_KERNEL_MEMORY_TOTAL,185,174,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_KERNEL_MEMORY_PAGED,185,184,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + EDITTEXT IDC_KERNEL_MEMORY_NONPAGED,185,193,48,8,ES_RIGHT | + ES_READONLY | ES_NUMBER | NOT WS_BORDER + GROUPBOX "Processorhistorik",IDC_CPU_USAGE_HISTORY_FRAME,74,5,168,54,0,WS_EX_TRANSPARENT + GROUPBOX "Minneshistorik",IDC_MEMORY_USAGE_HISTORY_FRAME,74,63,168,54,0,WS_EX_TRANSPARENT + PUSHBUTTON "Processoranvändning",IDC_CPU_USAGE_GRAPH,12,17,47,37,0, + WS_EX_CLIENTEDGE + PUSHBUTTON "Minnesanvändning",IDC_MEM_USAGE_GRAPH,12,75,47,37,0, + WS_EX_CLIENTEDGE + PUSHBUTTON "Processorhistorik",IDC_CPU_USAGE_HISTORY_GRAPH,81,17, + 153,37,0,WS_EX_CLIENTEDGE + PUSHBUTTON "Minneshistorik",IDC_MEM_USAGE_HISTORY_GRAPH,81,75, + 153,37,0,WS_EX_CLIENTEDGE +END + +IDD_DEBUG_CHANNELS_DIALOG DIALOG DISCARDABLE 0, 0, 247, 210 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Felsökningskanaler" +FONT 8, "Tahoma" +BEGIN + CONTROL "List2",IDC_DEBUG_CHANNELS_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP | + LVS_SORTASCENDING,7,7,233,177 + PUSHBUTTON "Stäng",IDOK,171,189,69,14 +END + +IDD_AFFINITY_DIALOG DIALOG DISCARDABLE 0, 0, 231, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Processorsamhörighet" +FONT 8, "Tahoma" +BEGIN + DEFPUSHBUTTON "OK",IDOK,120,133,50,14 + PUSHBUTTON "Avbryt",IDCANCEL,174,133,50,14 + LTEXT "Processorsamhörighets-inställningen bestämmer vilka processorer processen tillåts exekvera på", + IDC_STATIC,5,5,220,16 + CONTROL "Processor 0",IDC_CPU0,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 6,28,55,10 + CONTROL "Processor 1",IDC_CPU1,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,6,41,55,10 + CONTROL "Processor 2",IDC_CPU2,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,6,54,55,10 + CONTROL "Processor 3",IDC_CPU3,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,6,67,55,10 + CONTROL "Processor 4",IDC_CPU4,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,6,80,55,10 + CONTROL "Processor 5",IDC_CPU5,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,6,93,55,10 + CONTROL "Processor 6",IDC_CPU6,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,6,106,55,10 + CONTROL "Processor 7",IDC_CPU7,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,6,119,55,10 + CONTROL "Processor 8",IDC_CPU8,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,61,28,55,10 + CONTROL "Processor 9",IDC_CPU9,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,61,41,55,10 + CONTROL "Processor 10",IDC_CPU10,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,54,55,10 + CONTROL "Processor 11",IDC_CPU11,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,67,55,10 + CONTROL "Processor 12",IDC_CPU12,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,80,55,10 + CONTROL "Processor 13",IDC_CPU13,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,93,55,10 + CONTROL "Processor 14",IDC_CPU14,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,106,55,10 + CONTROL "Processor 15",IDC_CPU15,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,61,119,55,10 + CONTROL "Processor 16",IDC_CPU16,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,28,55,10 + CONTROL "Processor 17",IDC_CPU17,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,41,55,10 + CONTROL "Processor 18",IDC_CPU18,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,54,55,10 + CONTROL "Processor 19",IDC_CPU19,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,67,55,10 + CONTROL "Processor 20",IDC_CPU20,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,80,55,10 + CONTROL "Processor 21",IDC_CPU21,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,93,55,10 + CONTROL "Processor 22",IDC_CPU22,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,106,55,10 + CONTROL "Processor 23",IDC_CPU23,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,116,119,55,10 + CONTROL "Processor 24",IDC_CPU24,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,28,55,10 + CONTROL "Processor 25",IDC_CPU25,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,41,55,10 + CONTROL "Processor 26",IDC_CPU26,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,54,55,10 + CONTROL "Processor 27",IDC_CPU27,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,67,55,10 + CONTROL "Processor 28",IDC_CPU28,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,80,55,10 + CONTROL "Processor 29",IDC_CPU29,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,93,55,10 + CONTROL "Processor 30",IDC_CPU30,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,106,55,10 + CONTROL "Processor 31",IDC_CPU31,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,175,119,55,10 +END + +IDD_COLUMNS_DIALOG DIALOG DISCARDABLE 0, 0, 275, 199 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Välj kolumner" +FONT 8, "Tahoma" +BEGIN + DEFPUSHBUTTON "OK",IDOK,84,178,50,14 + PUSHBUTTON "Avbryt",IDCANCEL,138,178,50,14 + LTEXT "Välj de kolumner som skall synas på process-sidan i Aktivitetshanteraren.", + IDC_STATIC,7,7,181,17 + CONTROL "&Processnamn",IDC_IMAGENAME,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,7,28,60,10 + CONTROL "P&ID (Processidentifierare)",IDC_PID,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,39,119,10 + CONTROL "Processoranvändnin&g",IDC_CPUUSAGE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,50,89,10 + CONTROL "Pro&cessortid",IDC_CPUTIME,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,61,62,10 + CONTROL "&Minnesanvändning",IDC_MEMORYUSAGE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,72,77,10 + CONTROL "Minnesanvändnings&förändring",IDC_MEMORYUSAGEDELTA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,83,121,10 + CONTROL "T&oppvärde minnesanvändning",IDC_PEAKMEMORYUSAGE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,94,121,10 + CONTROL "&Sidfel",IDC_PAGEFAULTS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,105,56,10 + CONTROL "&USER Objekt",IDC_USEROBJECTS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,116,64,10 + CONTROL "Antal I/O-l&äsningar",IDC_IOREADS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,127,88,10 + CONTROL "I/O, anta&l tecken lästa",IDC_IOREADBYTES,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,138,119,10 + CONTROL "Sessions-I&D",IDC_SESSIONID,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,149,58,10 + CONTROL "Användar&namn",IDC_USERNAME,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,160,62,10 + CONTROL "Sidfelsf&örändring",IDC_PAGEFAULTSDELTA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,133,28,82,10 + CONTROL "Storl&ek i virtuella minnet",IDC_VIRTUALMEMORYSIZE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,133,39,116,10 + CONTROL "Växlings&bar pool",IDC_PAGEDPOOL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,133,50,76,10 + CONTROL "Ic&ke växlingsbar pool",IDC_NONPAGEDPOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,133,61,96,10 + CONTROL "&Basprioritet",IDC_BASEPRIORITY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,133,72,60,10 + CONTROL "Antal referenser (&handles)",IDC_HANDLECOUNT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,133,83,117,10 + CONTROL "Antal tr&ådar",IDC_THREADCOUNT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,133,94,60,10 + CONTROL "GDI-ob&jekt",IDC_GDIOBJECTS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,133,105,52,10 + CONTROL "An&tal I/O-Skrivningar",IDC_IOWRITES,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,133,116,97,10 + CONTROL "I/O, &antal tecken skrivna",IDC_IOWRITEBYTES,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,133,127,113,10 + CONTROL "And&ra I/O åtgärder",IDC_IOOTHER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,133,138,89,10 + CONTROL "Antal tecken, andra I/O åtgärder",IDC_IOOTHERBYTES,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,133,149,141,10 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""resource.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 252 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_TASKMGR_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 260 + TOPMARGIN, 3 + BOTTOMMARGIN, 231 + END + + IDD_APPLICATION_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 240 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_PROCESS_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 240 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_PERFORMANCE_PAGE, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 242 + VERTGUIDE, 12 + VERTGUIDE, 65 + VERTGUIDE, 110 + TOPMARGIN, 5 + BOTTOMMARGIN, 205 + END + + IDD_AFFINITY_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 224 + VERTGUIDE, 11 + VERTGUIDE, 61 + VERTGUIDE, 116 + VERTGUIDE, 175 + TOPMARGIN, 7 + BOTTOMMARGIN, 147 + HORZGUIDE, 28 + HORZGUIDE, 38 + HORZGUIDE, 51 + HORZGUIDE, 64 + HORZGUIDE, 77 + HORZGUIDE, 90 + HORZGUIDE, 103 + HORZGUIDE, 116 + HORZGUIDE, 129 + END + + IDD_COLUMNS_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 188 + VERTGUIDE, 107 + TOPMARGIN, 7 + BOTTOMMARGIN, 192 + HORZGUIDE, 28 + END +END +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_APP_TITLE "Aktivitetshanteraren" + IDC_TASKMGR "Aktivitetshanteraren" + IDS_IDLE_PROCESS "System Idle Process" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Kör ett nytt program" + ID_OPTIONS_ALWAYSONTOP "Aktivitetshanteraren förblir i förgrunden om den inte minimeras" + ID_OPTIONS_MINIMIZEONUSE + "Aktivitetshanteraren minimeras när en Växla till operation utförs" + ID_OPTIONS_HIDEWHENMINIMIZED "Göm Aktivitetshanteraren när den minimeras" + ID_VIEW_REFRESH "Tvinga Aktivitetshanteraren att uppdatera nu, oavsätt vald uppdateringshastighet" + ID_VIEW_LARGE "Visa aktiviteter med stora ikoner" + ID_VIEW_SMALL "Visa aktiviteter med små ikoner" + ID_VIEW_DETAILS "Visa information om varje aktivitet" + ID_VIEW_UPDATESPEED_HIGH "Uppdaterar värden två gånger per sekund" + ID_VIEW_UPDATESPEED_NORMAL "Uppdaterar värden varannan sekund" + ID_VIEW_UPDATESPEED_LOW "Uppdaterar värden var 4:e sekund" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_UPDATESPEED_PAUSED "Uppdaterar inte automatiskt" + ID_WINDOWS_TILEHORIZONTALLY + "Ordna fönstren horizontellt på skrivbordet" + ID_WINDOWS_TILEVERTICALLY "Ordna fönstren vertikalt på skrivborder" + ID_WINDOWS_MINIMIZE "Minimerar fönstret" + ID_WINDOWS_MAXIMIZE "Maximerar fönstret" + ID_WINDOWS_CASCADE "Ordna fönstren diagonalt överlappande på skrivbordet" + ID_WINDOWS_BRINGTOFRONT "För fönstret till förgrunden men växla inte till det" + ID_HELP_TOPICS "Visa Aktivitetshanterarens hjälpavsnitt" + ID_HELP_ABOUT "Visa programinformation, versionsnummer, och upphovsrätt" + ID_FILE_EXIT "Avsluta Aktivitetshanteraren" + ID_OPTIONS_SHOW16BITTASKS + "Visa 16-bitarsprocesser under tillhörande ntvdm.exe" + ID_VIEW_SELECTCOLUMNS "Välj de kolumner som ska synas på process-sidan" + ID_VIEW_SHOWKERNELTIMES "Visar kernel-tider i prestandagrafer" + ID_VIEW_CPUHISTORY_ONEGRAPHALL + "En enda historik-graf visar totala processoranvändningen" + ID_VIEW_CPUHISTORY_ONEGRAPHPERCPU "Varje processor har sin egen historik-graf" + ID_APPLICATION_PAGE_SWITCHTO + "För aktiviteten till förgrunden och växla till den" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_ENDTASK "Ber de valda aktiviteterna att avslutas" + ID_GOTOPROCESS "Ändrar fokus till den valda aktivitetens process" + ID_RESTORE "Återställer Aktivitetshanteraren från sitt gömda tillstånd" + ID_PROCESS_PAGE_ENDPROCESS "Tar bort processen från systemet" + ID_PROCESS_PAGE_ENDPROCESSTREE + "Tar bort den här processen och underprocesser från systemet" + ID_PROCESS_PAGE_DEBUG "Kopplar felsökaren till den här processen" + ID_PROCESS_PAGE_SETAFFINITY + "Kontrollerar vilka processorer processen tillåts att köras på" + ID_PROCESS_PAGE_SETPRIORITY_REALTIME + "Ändrar processens prioritet till realtidsläge" + ID_PROCESS_PAGE_SETPRIORITY_HIGH "Ändrar processens prioritet till hög" + ID_PROCESS_PAGE_SETPRIORITY_ABOVENORMAL + "Ändrar processens prioritet till över normal" + ID_PROCESS_PAGE_SETPRIORITY_NORMAL + "Ändrar processens prioritet till normal" + ID_PROCESS_PAGE_SETPRIORITY_BELOWNORMAL + "Ändrar processens prioritet till under normal" + ID_PROCESS_PAGE_SETPRIORITY_LOW "Ändrar processens prioritet till låg" + IDS_LICENSE "This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA." +END + + +STRINGTABLE DISCARDABLE +BEGIN + IDS_TAB_APPS "Program" + IDS_TAB_PROCESSES "Processer" + IDS_TAB_PERFORMANCE "Prestanda" + IDS_TAB_TASK "Aktivitet" + IDS_TAB_STATUS "Status" + IDS_TAB_IMAGENAME "Processnamn" + IDS_TAB_PID "PID" + IDS_TAB_USERNAME "Användarnamn" + IDS_TAB_SESSIONID "Sessions-ID" + IDS_TAB_CPU "Processor" + IDS_TAB_CPUTIME "Processortid" + IDS_TAB_MEMUSAGE "Minnesanvändning" + IDS_TAB_PEAKMEMUSAGE "Toppvärde minne" + IDS_TAB_MEMDELTA "Minnesvärdesförändring" + IDS_TAB_PAGEFAULT "Sidfel" + IDS_TAB_PFDELTA "Sidfelsförändring" + IDS_TAB_VMSIZE "VM Storlek" + IDS_TAB_PAGEDPOOL "Växlingsbar pool" + IDS_TAB_NPPOOL "Oväxlingsbar pool" + IDS_TAB_BASEPRI "Basprioritet" + IDS_TAB_HANDLES "Referenser" + IDS_TAB_THREADS "Trådar" + IDS_TAB_USERPBJECTS "USER Objekt" + IDS_TAB_GDIOBJECTS "GDI Objekt" + IDS_TAB_IOREADS "I/O-läsningar" + IDS_TAB_IOWRITES "I/O-skrivningar" + IDS_TAB_IOOTHER "I/O-övriga" + IDS_TAB_IOREADBYTES "Antal tecken lästa, andra I/O-åtgärder" + IDS_TAB_IOWRITESBYTES "Antal tecken skrivna, andra I/O-åtgärder" + IDS_TAB_IOOTHERBYTES "I/O, antal tecken" + IDS_MENU_SELECTCOLUMNS "&Välj kolumner..." + IDS_MENU_16BITTASK "&Visa 16-bitsprocesser" + IDS_MENU_WINDOWS "&Fönster" + IDS_MENU_LARGEICONS "&Stora ikoner" + IDS_MENU_SMALLICONS "S&må ikoner" + IDS_MENU_DETAILS "&Detaljer" + IDS_MENU_ONEGRAPHALLCPUS "En visare för &alla processorer" + IDS_MENU_ONEGRAPHPERCPU "&En visare per processor" + IDS_MENU_CPUHISTORY "&Processorhistorik" + IDS_MENU_SHOWKERNELTIMES "&Visa kerneltider" + IDS_CREATENEWTASK "Ny aktivitet" + IDS_CREATENEWTASK_DESC "Skriv namnet på ett program, en mapp, en web-adress eller ett dokument för att öppna det." + IDS_MSG_ACCESSPROCESSAFF "Kunde inte komma åt eller ändra processorsamhörigheten" + IDS_MSG_PROCESSONEPRO "Processen måste ha samhörighet med minst en processor." + IDS_MSG_INVALIDOPTION "Ogiltigt val" + IDS_MSG_UNABLEDEBUGPROCESS "Kunde inte felsöka processen" + IDS_MSG_WARNINGDEBUG "VARNING: Felsökning av den här processen kan resultera i förlorad data.\nÄr du säker på att du vill ansluta felsökaren?" + IDS_MSG_TASKMGRWARNING "Aktivitetshanteraren varning" + IDS_MSG_WARNINGTERMINATING "VARNING: Ett avslutande av en process kan orsaka\noönskade effekter och påverka systemets stabilitet. Processen\nkommer inte att ges chans att spara sitt arbete innan\nden avslutas. Är du säker på att du vill avsluta processen?" + IDS_MSG_UNABLETERMINATEPRO "Kunde inte avsluta processen" + IDS_MSG_UNABLECHANGEPRIORITY "Kunde inte ändra prioritet" + IDS_MSG_WARNINGCHANGEPRIORITY "VARNING: Ändring av prioritetsklassen hos den här processen kan\norsaka oönskade effekter och påverka systemets stabilitet. Är du\nsäker på att du vill ändra prioritetsklassen?" + IDS_MSG_TRAYICONCPUUSAGE "CPU-anv: %d%%" + +END + + +#endif // Swedish (SE) resources +///////////////////////////////////////////////////////////////////////////// diff --git a/reactos/subsys/system/taskmgr/about.c b/reactos/subsys/system/taskmgr/about.c index af32cc94bf7..9cd9d472c44 100644 --- a/reactos/subsys/system/taskmgr/about.c +++ b/reactos/subsys/system/taskmgr/about.c @@ -21,14 +21,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include - -#include "about.h" INT_PTR CALLBACK AboutDialogWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); diff --git a/reactos/subsys/system/taskmgr/affinity.c b/reactos/subsys/system/taskmgr/affinity.c index 636df36a5aa..3afacd3b459 100644 --- a/reactos/subsys/system/taskmgr/affinity.c +++ b/reactos/subsys/system/taskmgr/affinity.c @@ -4,6 +4,7 @@ * affinity.cpp * * Copyright (C) 1999 - 2001 Brian Palmer + * 2005 Klemens Friedl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,25 +22,16 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "procpage.h" -#include "affinity.h" -#include "perfdata.h" HANDLE hProcessAffinityHandle; +TCHAR szTemp[256]; +TCHAR szTempA[256]; INT_PTR CALLBACK AffinityDialogWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); void ProcessPage_OnSetAffinity(void) { - LV_ITEM lvitem; + LV_ITEM lvitem; ULONG Index; DWORD dwProcessId; TCHAR strErrorText[260]; @@ -59,7 +51,8 @@ void ProcessPage_OnSetAffinity(void) hProcessAffinityHandle = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_SET_INFORMATION, FALSE, dwProcessId); if (!hProcessAffinityHandle) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Access or Set Process Affinity"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_ACCESSPROCESSAFF, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } DialogBox(hInst, MAKEINTRESOURCE(IDD_AFFINITY_DIALOG), hMainWnd, AffinityDialogWndProc); @@ -86,7 +79,8 @@ AffinityDialogWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) if (!GetProcessAffinityMask(hProcessAffinityHandle, &dwProcessAffinityMask, &dwSystemAffinityMask)) { GetLastErrorText(strErrorText, 260); EndDialog(hDlg, 0); - MessageBox(hMainWnd, strErrorText, _T("Unable to Access or Set Process Affinity"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_ACCESSPROCESSAFF, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } /* @@ -321,7 +315,9 @@ AffinityDialogWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) * of it's cpu time. */ if (!dwProcessAffinityMask) { - MessageBox(hDlg, _T("The process must have affinity with at least one processor."), _T("Invalid Option"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_PROCESSONEPRO, szTemp, 256); + LoadString(hInst, IDS_MSG_INVALIDOPTION, szTempA, 256); + MessageBox(hDlg, szTemp, szTempA, MB_OK|MB_ICONSTOP); return TRUE; } @@ -331,7 +327,8 @@ AffinityDialogWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) if (!SetProcessAffinityMask(hProcessAffinityHandle, dwProcessAffinityMask)) { GetLastErrorText(strErrorText, 260); EndDialog(hDlg, LOWORD(wParam)); - MessageBox(hMainWnd, strErrorText, _T("Unable to Access or Set Process Affinity"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_ACCESSPROCESSAFF, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } EndDialog(hDlg, LOWORD(wParam)); diff --git a/reactos/subsys/system/taskmgr/applpage.c b/reactos/subsys/system/taskmgr/applpage.c index eca2e3fb6bc..ee547ceb86d 100644 --- a/reactos/subsys/system/taskmgr/applpage.c +++ b/reactos/subsys/system/taskmgr/applpage.c @@ -4,6 +4,7 @@ * applicationpage.cpp * * Copyright (C) 1999 - 2001 Brian Palmer + * 2005 Klemens Friedl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,15 +22,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include - -#include "applpage.h" -#include "procpage.h" typedef struct { @@ -95,12 +87,13 @@ ApplicationPageWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) /* Initialize the application page's controls */ column.mask = LVCF_TEXT|LVCF_WIDTH; - _tcscpy(szTemp, _T("Task")); + + LoadString(hInst, IDS_TAB_TASK, szTemp, 256); column.pszText = szTemp; column.cx = 250; ListView_InsertColumn(hApplicationPageListCtrl, 0, &column); /* Add the "Task" column */ column.mask = LVCF_TEXT|LVCF_WIDTH; - _tcscpy(szTemp, _T("Status")); + LoadString(hInst, IDS_TAB_STATUS, szTemp, 256); column.pszText = szTemp; column.cx = 95; ListView_InsertColumn(hApplicationPageListCtrl, 1, &column); /* Add the "Status" column */ @@ -368,7 +361,7 @@ void AddOrUpdateHwnd(HWND hWnd, TCHAR *szTitle, HICON hIcon, BOOL bHung) /* It is not already in the list so add it */ else { - pAPLI = (LPAPPLICATION_PAGE_LIST_ITEM)malloc(sizeof(APPLICATION_PAGE_LIST_ITEM)); + pAPLI = (LPAPPLICATION_PAGE_LIST_ITEM)HeapAlloc(GetProcessHeap(), 0, sizeof(APPLICATION_PAGE_LIST_ITEM)); pAPLI->hWnd = hWnd; pAPLI->hIcon = hIcon; @@ -407,7 +400,7 @@ void AddOrUpdateHwnd(HWND hWnd, TCHAR *szTitle, HICON hIcon, BOOL bHung) ImageList_Remove(hImageListSmall, item.iItem); ListView_DeleteItem(hApplicationPageListCtrl, item.iItem); - free(pAPLI); + HeapFree(GetProcessHeap(), 0, pAPLI); bItemRemoved = TRUE; } } @@ -702,7 +695,7 @@ void ApplicationPage_OnWindowsTileHorizontally(void) HWND* hWndArray; int nWndCount; - hWndArray = (HWND*)malloc(sizeof(HWND) * ListView_GetItemCount(hApplicationPageListCtrl)); + hWndArray = (HWND*)HeapAlloc(GetProcessHeap(), 0, sizeof(HWND) * ListView_GetItemCount(hApplicationPageListCtrl)); nWndCount = 0; for (i=0; i + * 2005 Klemens Friedl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,17 +22,9 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include - -#include "column.h" -#include "procpage.h" UINT ColumnDataHints[25]; +TCHAR szTemp[256]; int InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat, int nWidth, int nSubItem); INT_PTR CALLBACK ColumnsDialogWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); @@ -40,56 +33,126 @@ void AddColumns(void) { int size; - if (TaskManagerSettings.Column_ImageName) - InsertColumn(0, _T("Image Name"), LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[0], -1); - if (TaskManagerSettings.Column_PID) - InsertColumn(1, _T("PID"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[1], -1); - if (TaskManagerSettings.Column_UserName) - InsertColumn(2, _T("Username"), LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[2], -1); - if (TaskManagerSettings.Column_SessionID) - InsertColumn(3, _T("Session ID"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[3], -1); - if (TaskManagerSettings.Column_CPUUsage) - InsertColumn(4, _T("CPU"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[4], -1); - if (TaskManagerSettings.Column_CPUTime) - InsertColumn(5, _T("CPU Time"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[5], -1); - if (TaskManagerSettings.Column_MemoryUsage) - InsertColumn(6, _T("Mem Usage"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[6], -1); - if (TaskManagerSettings.Column_PeakMemoryUsage) - InsertColumn(7, _T("Peak Mem Usage"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[7], -1); - if (TaskManagerSettings.Column_MemoryUsageDelta) - InsertColumn(8, _T("Mem Delta"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[8], -1); - if (TaskManagerSettings.Column_PageFaults) - InsertColumn(9, _T("Page Faults"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[9], -1); - if (TaskManagerSettings.Column_PageFaultsDelta) - InsertColumn(10, _T("PF Delta"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[10], -1); - if (TaskManagerSettings.Column_VirtualMemorySize) - InsertColumn(11, _T("VM Size"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[11], -1); - if (TaskManagerSettings.Column_PagedPool) - InsertColumn(12, _T("Paged Pool"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[12], -1); - if (TaskManagerSettings.Column_NonPagedPool) - InsertColumn(13, _T("NP Pool"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[13], -1); - if (TaskManagerSettings.Column_BasePriority) - InsertColumn(14, _T("Base Pri"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[14], -1); - if (TaskManagerSettings.Column_HandleCount) - InsertColumn(15, _T("Handles"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[15], -1); - if (TaskManagerSettings.Column_ThreadCount) - InsertColumn(16, _T("Threads"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[16], -1); - if (TaskManagerSettings.Column_USERObjects) - InsertColumn(17, _T("USER Objects"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[17], -1); - if (TaskManagerSettings.Column_GDIObjects) - InsertColumn(18, _T("GDI Objects"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[18], -1); - if (TaskManagerSettings.Column_IOReads) - InsertColumn(19, _T("I/O Reads"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[19], -1); - if (TaskManagerSettings.Column_IOWrites) - InsertColumn(20, _T("I/O Writes"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[20], -1); - if (TaskManagerSettings.Column_IOOther) - InsertColumn(21, _T("I/O Other"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[21], -1); - if (TaskManagerSettings.Column_IOReadBytes) - InsertColumn(22, _T("I/O Read Bytes"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[22], -1); - if (TaskManagerSettings.Column_IOWriteBytes) - InsertColumn(23, _T("I/O Write Bytes"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[23], -1); - if (TaskManagerSettings.Column_IOOtherBytes) - InsertColumn(24, _T("I/O Other Bytes"), LVCFMT_RIGHT, TaskManagerSettings.ColumnSizeArray[24], -1); + if (TaskManagerSettings.Column_ImageName) { + LoadString(hInst, IDS_TAB_IMAGENAME, szTemp, 256); + InsertColumn(0, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[0], -1); + } + if (TaskManagerSettings.Column_PID) { + LoadString(hInst, IDS_TAB_PID, szTemp, 256); + InsertColumn(1, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[1], -1); + } + if (TaskManagerSettings.Column_UserName) { + LoadString(hInst, IDS_TAB_USERNAME, szTemp, 256); + InsertColumn(2, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[2], -1); + } + if (TaskManagerSettings.Column_SessionID) { + LoadString(hInst, IDS_TAB_SESSIONID, szTemp, 256); + InsertColumn(3, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[3], -1); + } + if (TaskManagerSettings.Column_CPUUsage) { + LoadString(hInst, IDS_TAB_CPU, szTemp, 256); + InsertColumn(4, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[4], -1); + } + + if (TaskManagerSettings.Column_CPUTime) { + LoadString(hInst, IDS_TAB_CPUTIME, szTemp, 256); + InsertColumn(5, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[5], -1); + } + + if (TaskManagerSettings.Column_MemoryUsage) { + LoadString(hInst, IDS_TAB_MEMUSAGE, szTemp, 256); + InsertColumn(6, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[6], -1); + } + + if (TaskManagerSettings.Column_PeakMemoryUsage) { + LoadString(hInst, IDS_TAB_PEAKMEMUSAGE, szTemp, 256); + InsertColumn(7, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[7], -1); + } + + if (TaskManagerSettings.Column_MemoryUsageDelta) { + LoadString(hInst, IDS_TAB_MEMDELTA, szTemp, 256); + InsertColumn(8, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[8], -1); + } + + if (TaskManagerSettings.Column_PageFaults) { + LoadString(hInst, IDS_TAB_PAGEFAULT, szTemp, 256); + InsertColumn(9, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[9], -1); + } + + if (TaskManagerSettings.Column_PageFaultsDelta) { + LoadString(hInst, IDS_TAB_PFDELTA, szTemp, 256); + InsertColumn(10, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[10], -1); + } + + if (TaskManagerSettings.Column_VirtualMemorySize) { + LoadString(hInst, IDS_TAB_VMSIZE, szTemp, 256); + InsertColumn(11, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[11], -1); + } + + if (TaskManagerSettings.Column_PagedPool) { + LoadString(hInst, IDS_TAB_PAGEDPOOL, szTemp, 256); + InsertColumn(12, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[12], -1); + } + + if (TaskManagerSettings.Column_NonPagedPool) { + LoadString(hInst, IDS_TAB_NPPOOL, szTemp, 256); + InsertColumn(13, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[13], -1); + } + + if (TaskManagerSettings.Column_BasePriority) { + LoadString(hInst, IDS_TAB_BASEPRI, szTemp, 256); + InsertColumn(14, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[14], -1); + } + + if (TaskManagerSettings.Column_HandleCount) { + LoadString(hInst, IDS_TAB_HANDLES, szTemp, 256); + InsertColumn(15, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[15], -1); + } + + if (TaskManagerSettings.Column_ThreadCount) { + LoadString(hInst, IDS_TAB_THREADS, szTemp, 256); + InsertColumn(16, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[16], -1); + } + + if (TaskManagerSettings.Column_USERObjects) { + LoadString(hInst, IDS_TAB_USERPBJECTS, szTemp, 256); + InsertColumn(17, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[17], -1); + } + + if (TaskManagerSettings.Column_GDIObjects) { + LoadString(hInst, IDS_TAB_GDIOBJECTS, szTemp, 256); + InsertColumn(18, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[18], -1); + } + + if (TaskManagerSettings.Column_IOReads) { + LoadString(hInst, IDS_TAB_IOREADS, szTemp, 256); + InsertColumn(19, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[19], -1); + } + + if (TaskManagerSettings.Column_IOWrites) { + LoadString(hInst, IDS_TAB_IOWRITES, szTemp, 256); + InsertColumn(20, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[20], -1); + } + + if (TaskManagerSettings.Column_IOOther) { + LoadString(hInst, IDS_TAB_IOOTHER, szTemp, 256); + InsertColumn(21, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[21], -1); + } + + if (TaskManagerSettings.Column_IOReadBytes) { + LoadString(hInst, IDS_TAB_IOREADBYTES, szTemp, 256); + InsertColumn(22, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[22], -1); + } + + if (TaskManagerSettings.Column_IOWriteBytes) { + LoadString(hInst, IDS_TAB_IOWRITESBYTES, szTemp, 256); + InsertColumn(23, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[23], -1); + } + + if (TaskManagerSettings.Column_IOOtherBytes) { + LoadString(hInst, IDS_TAB_IOOTHERBYTES, szTemp, 256); + InsertColumn(24, szTemp, LVCFMT_LEFT, TaskManagerSettings.ColumnSizeArray[24], -1); + } size = SendMessage(hProcessPageHeaderCtrl, HDM_GETITEMCOUNT, 0, 0); SendMessage(hProcessPageHeaderCtrl, HDM_SETORDERARRAY, (WPARAM) size, (LPARAM) &TaskManagerSettings.ColumnOrderArray); @@ -196,127 +259,176 @@ void SaveColumnSettings(void) SendMessage(hProcessPageHeaderCtrl, HDM_GETITEM, i, (LPARAM) &hditem); - if (_tcsicmp(text, _T("Image Name")) == 0) + LoadString(hInst, IDS_TAB_IMAGENAME, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_ImageName = TRUE; TaskManagerSettings.ColumnSizeArray[0] = hditem.cxy; } - if (_tcsicmp(text, _T("PID")) == 0) + + LoadString(hInst, IDS_TAB_PID, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_PID = TRUE; TaskManagerSettings.ColumnSizeArray[1] = hditem.cxy; } - if (_tcsicmp(text, _T("Username")) == 0) + + LoadString(hInst, IDS_TAB_USERNAME, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_UserName = TRUE; TaskManagerSettings.ColumnSizeArray[2] = hditem.cxy; } - if (_tcsicmp(text, _T("Session ID")) == 0) + + LoadString(hInst, IDS_TAB_SESSIONID, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_SessionID = TRUE; TaskManagerSettings.ColumnSizeArray[3] = hditem.cxy; } - if (_tcsicmp(text, _T("CPU")) == 0) + + LoadString(hInst, IDS_TAB_CPU, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_CPUUsage = TRUE; TaskManagerSettings.ColumnSizeArray[4] = hditem.cxy; } - if (_tcsicmp(text, _T("CPU Time")) == 0) + + LoadString(hInst, IDS_TAB_CPUTIME, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_CPUTime = TRUE; TaskManagerSettings.ColumnSizeArray[5] = hditem.cxy; } - if (_tcsicmp(text, _T("Mem Usage")) == 0) + + LoadString(hInst, IDS_TAB_MEMUSAGE, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_MemoryUsage = TRUE; TaskManagerSettings.ColumnSizeArray[6] = hditem.cxy; } - if (_tcsicmp(text, _T("Peak Mem Usage")) == 0) + + LoadString(hInst, IDS_TAB_PEAKMEMUSAGE, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_PeakMemoryUsage = TRUE; TaskManagerSettings.ColumnSizeArray[7] = hditem.cxy; } - if (_tcsicmp(text, _T("Mem Delta")) == 0) + + LoadString(hInst, IDS_TAB_MEMDELTA, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_MemoryUsageDelta = TRUE; TaskManagerSettings.ColumnSizeArray[8] = hditem.cxy; } - if (_tcsicmp(text, _T("Page Faults")) == 0) + + LoadString(hInst, IDS_TAB_PAGEFAULT, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_PageFaults = TRUE; TaskManagerSettings.ColumnSizeArray[9] = hditem.cxy; } - if (_tcsicmp(text, _T("PF Delta")) == 0) + + LoadString(hInst, IDS_TAB_PFDELTA, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_PageFaultsDelta = TRUE; TaskManagerSettings.ColumnSizeArray[10] = hditem.cxy; } - if (_tcsicmp(text, _T("VM Size")) == 0) + + LoadString(hInst, IDS_TAB_VMSIZE, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_VirtualMemorySize = TRUE; TaskManagerSettings.ColumnSizeArray[11] = hditem.cxy; } - if (_tcsicmp(text, _T("Paged Pool")) == 0) + + LoadString(hInst, IDS_TAB_PAGEDPOOL, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_PagedPool = TRUE; TaskManagerSettings.ColumnSizeArray[12] = hditem.cxy; } - if (_tcsicmp(text, _T("NP Pool")) == 0) + + LoadString(hInst, IDS_TAB_NPPOOL, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_NonPagedPool = TRUE; TaskManagerSettings.ColumnSizeArray[13] = hditem.cxy; } - if (_tcsicmp(text, _T("Base Pri")) == 0) + + LoadString(hInst, IDS_TAB_BASEPRI, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_BasePriority = TRUE; TaskManagerSettings.ColumnSizeArray[14] = hditem.cxy; } - if (_tcsicmp(text, _T("Handles")) == 0) + + LoadString(hInst, IDS_TAB_HANDLES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_HandleCount = TRUE; TaskManagerSettings.ColumnSizeArray[15] = hditem.cxy; } - if (_tcsicmp(text, _T("Threads")) == 0) + + LoadString(hInst, IDS_TAB_THREADS, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_ThreadCount = TRUE; TaskManagerSettings.ColumnSizeArray[16] = hditem.cxy; } - if (_tcsicmp(text, _T("USER Objects")) == 0) + + LoadString(hInst, IDS_TAB_USERPBJECTS, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_USERObjects = TRUE; TaskManagerSettings.ColumnSizeArray[17] = hditem.cxy; } - if (_tcsicmp(text, _T("GDI Objects")) == 0) + + LoadString(hInst, IDS_TAB_GDIOBJECTS, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_GDIObjects = TRUE; TaskManagerSettings.ColumnSizeArray[18] = hditem.cxy; } - if (_tcsicmp(text, _T("I/O Reads")) == 0) + + LoadString(hInst, IDS_TAB_IOREADS, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_IOReads = TRUE; TaskManagerSettings.ColumnSizeArray[19] = hditem.cxy; } - if (_tcsicmp(text, _T("I/O Writes")) == 0) + + LoadString(hInst, IDS_TAB_IOWRITES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_IOWrites = TRUE; TaskManagerSettings.ColumnSizeArray[20] = hditem.cxy; } - if (_tcsicmp(text, _T("I/O Other")) == 0) + + LoadString(hInst, IDS_TAB_IOOTHER, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_IOOther = TRUE; TaskManagerSettings.ColumnSizeArray[21] = hditem.cxy; } - if (_tcsicmp(text, _T("I/O Read Bytes")) == 0) + + LoadString(hInst, IDS_TAB_IOREADBYTES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_IOReadBytes = TRUE; TaskManagerSettings.ColumnSizeArray[22] = hditem.cxy; } - if (_tcsicmp(text, _T("I/O Write Bytes")) == 0) + + LoadString(hInst, IDS_TAB_IOWRITESBYTES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_IOWriteBytes = TRUE; TaskManagerSettings.ColumnSizeArray[23] = hditem.cxy; } - if (_tcsicmp(text, _T("I/O Other Bytes")) == 0) + + LoadString(hInst, IDS_TAB_IOOTHERBYTES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) { TaskManagerSettings.Column_IOOtherBytes = TRUE; TaskManagerSettings.ColumnSizeArray[24] = hditem.cxy; @@ -491,55 +603,104 @@ void UpdateColumnDataHints(void) SendMessage(hProcessPageHeaderCtrl, HDM_GETITEM, Index, (LPARAM) &hditem); - if (_tcsicmp(text, _T("Image Name")) == 0) + LoadString(hInst, IDS_TAB_IMAGENAME, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_IMAGENAME; - if (_tcsicmp(text, _T("PID")) == 0) + + LoadString(hInst, IDS_TAB_PID, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_PID; - if (_tcsicmp(text, _T("Username")) == 0) + + LoadString(hInst, IDS_TAB_USERNAME, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_USERNAME; - if (_tcsicmp(text, _T("Session ID")) == 0) + + LoadString(hInst, IDS_TAB_SESSIONID, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_SESSIONID; - if (_tcsicmp(text, _T("CPU")) == 0) + + LoadString(hInst, IDS_TAB_CPU, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_CPUUSAGE; - if (_tcsicmp(text, _T("CPU Time")) == 0) + + LoadString(hInst, IDS_TAB_CPUTIME, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_CPUTIME; - if (_tcsicmp(text, _T("Mem Usage")) == 0) + + LoadString(hInst, IDS_TAB_MEMUSAGE, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_MEMORYUSAGE; - if (_tcsicmp(text, _T("Peak Mem Usage")) == 0) + + LoadString(hInst, IDS_TAB_PEAKMEMUSAGE, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_PEAKMEMORYUSAGE; - if (_tcsicmp(text, _T("Mem Delta")) == 0) + + LoadString(hInst, IDS_TAB_MEMDELTA, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_MEMORYUSAGEDELTA; - if (_tcsicmp(text, _T("Page Faults")) == 0) + + LoadString(hInst, IDS_TAB_PAGEFAULT, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_PAGEFAULTS; - if (_tcsicmp(text, _T("PF Delta")) == 0) + + LoadString(hInst, IDS_TAB_PFDELTA, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_PAGEFAULTSDELTA; - if (_tcsicmp(text, _T("VM Size")) == 0) + + LoadString(hInst, IDS_TAB_VMSIZE, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_VIRTUALMEMORYSIZE; - if (_tcsicmp(text, _T("Paged Pool")) == 0) + + LoadString(hInst, IDS_TAB_PAGEDPOOL, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_PAGEDPOOL; - if (_tcsicmp(text, _T("NP Pool")) == 0) + + LoadString(hInst, IDS_TAB_NPPOOL, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_NONPAGEDPOOL; - if (_tcsicmp(text, _T("Base Pri")) == 0) + + LoadString(hInst, IDS_TAB_BASEPRI, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_BASEPRIORITY; - if (_tcsicmp(text, _T("Handles")) == 0) + + LoadString(hInst, IDS_TAB_HANDLES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_HANDLECOUNT; - if (_tcsicmp(text, _T("Threads")) == 0) + + LoadString(hInst, IDS_TAB_THREADS, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_THREADCOUNT; - if (_tcsicmp(text, _T("USER Objects")) == 0) + + LoadString(hInst, IDS_TAB_USERPBJECTS, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_USEROBJECTS; - if (_tcsicmp(text, _T("GDI Objects")) == 0) + + LoadString(hInst, IDS_TAB_GDIOBJECTS, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_GDIOBJECTS; - if (_tcsicmp(text, _T("I/O Reads")) == 0) + + LoadString(hInst, IDS_TAB_IOREADS, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_IOREADS; - if (_tcsicmp(text, _T("I/O Writes")) == 0) + + LoadString(hInst, IDS_TAB_IOWRITES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_IOWRITES; - if (_tcsicmp(text, _T("I/O Other")) == 0) + + LoadString(hInst, IDS_TAB_IOOTHER, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_IOOTHER; - if (_tcsicmp(text, _T("I/O Read Bytes")) == 0) + + LoadString(hInst, IDS_TAB_IOREADBYTES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_IOREADBYTES; - if (_tcsicmp(text, _T("I/O Write Bytes")) == 0) + + LoadString(hInst, IDS_TAB_IOWRITESBYTES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_IOWRITEBYTES; - if (_tcsicmp(text, _T("I/O Other Bytes")) == 0) + + LoadString(hInst, IDS_TAB_IOOTHERBYTES, szTemp, 256); + if (_tcsicmp(text, szTemp) == 0) ColumnDataHints[Index] = COLUMN_IOOTHERBYTES; } } diff --git a/reactos/subsys/system/taskmgr/dbgchnl.c b/reactos/subsys/system/taskmgr/dbgchnl.c index c031bc7179b..aedc5f995c5 100644 --- a/reactos/subsys/system/taskmgr/dbgchnl.c +++ b/reactos/subsys/system/taskmgr/dbgchnl.c @@ -21,20 +21,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "procpage.h" -#include "perfdata.h" -#include "column.h" -#include "proclist.h" -#include "dbgchnl.h" -#include /* TODO: * - the dialog box could be non modal diff --git a/reactos/subsys/system/taskmgr/debug.c b/reactos/subsys/system/taskmgr/debug.c index 21e0bf705ba..a6b9b4c92d9 100644 --- a/reactos/subsys/system/taskmgr/debug.c +++ b/reactos/subsys/system/taskmgr/debug.c @@ -4,6 +4,7 @@ * debug.cpp * * Copyright (C) 1999 - 2001 Brian Palmer + * 2005 Klemens Friedl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,31 +22,23 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "procpage.h" -#include "perfdata.h" void ProcessPage_OnDebug(void) { - LVITEM lvitem; + LVITEM lvitem; ULONG Index; DWORD dwProcessId; TCHAR strErrorText[260]; - HKEY hKey; + HKEY hKey; TCHAR strDebugPath[260]; TCHAR strDebugger[260]; DWORD dwDebuggerSize; - PROCESS_INFORMATION pi; - STARTUPINFO si; - HANDLE hDebugEvent; + PROCESS_INFORMATION pi; + STARTUPINFO si; + HANDLE hDebugEvent; + TCHAR szTemp[256]; + TCHAR szTempA[256]; + for (Index=0; Index<(ULONG)ListView_GetItemCount(hProcessPageListCtrl); Index++) { @@ -66,17 +59,22 @@ void ProcessPage_OnDebug(void) if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0)) return; - if (MessageBox(hMainWnd, _T("WARNING: Debugging this process may result in loss of data.\nAre you sure you wish to attach the debugger?"), _T("Task Manager Warning"), MB_YESNO|MB_ICONWARNING) != IDYES) + LoadString(hInst, IDS_MSG_WARNINGDEBUG, szTemp, 256); + LoadString(hInst, IDS_MSG_TASKMGRWARNING, szTempA, 256); + + if (MessageBox(hMainWnd, szTemp, szTempA, MB_YESNO|MB_ICONWARNING) != IDYES) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Debug Process"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLEDEBUGPROCESS, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"), 0, KEY_READ, &hKey) != ERROR_SUCCESS) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Debug Process"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLEDEBUGPROCESS, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } @@ -84,7 +82,8 @@ void ProcessPage_OnDebug(void) if (RegQueryValueEx(hKey, _T("Debugger"), NULL, NULL, (LPBYTE)strDebugger, &dwDebuggerSize) != ERROR_SUCCESS) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Debug Process"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLEDEBUGPROCESS, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); RegCloseKey(hKey); return; } @@ -95,7 +94,8 @@ void ProcessPage_OnDebug(void) if (!hDebugEvent) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Debug Process"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLEDEBUGPROCESS, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } @@ -107,7 +107,8 @@ void ProcessPage_OnDebug(void) if (!CreateProcess(NULL, strDebugPath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Debug Process"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLEDEBUGPROCESS, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } CloseHandle(hDebugEvent); diff --git a/reactos/subsys/system/taskmgr/endproc.c b/reactos/subsys/system/taskmgr/endproc.c index 03f5b2dd22c..df0073baebd 100644 --- a/reactos/subsys/system/taskmgr/endproc.c +++ b/reactos/subsys/system/taskmgr/endproc.c @@ -4,6 +4,7 @@ * endproc.cpp * * Copyright (C) 1999 - 2001 Brian Palmer + * 2005 Klemens Friedl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,17 +22,9 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "endproc.h" -#include "procpage.h" -#include "perfdata.h" + +TCHAR szTemp[256]; +TCHAR szTempA[256]; void ProcessPage_OnEndProcess(void) { @@ -60,7 +53,9 @@ void ProcessPage_OnEndProcess(void) if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0)) return; - if (MessageBox(hMainWnd, _T("WARNING: Terminating a process can cause undesired\nresults including loss of data and system instability. The\nprocess will not be given the chance to save its state or\ndata before it is terminated. Are you sure you want to\nterminate the process?"), _T("Task Manager Warning"), MB_YESNO|MB_ICONWARNING) != IDYES) + LoadString(hInst, IDS_MSG_WARNINGTERMINATING, szTemp, 256); + LoadString(hInst, IDS_MSG_TASKMGRWARNING, szTempA, 256); + if (MessageBox(hMainWnd, szTemp, szTempA, MB_YESNO|MB_ICONWARNING) != IDYES) return; hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId); @@ -68,14 +63,16 @@ void ProcessPage_OnEndProcess(void) if (!hProcess) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Terminate Process"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLETERMINATEPRO, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } if (!TerminateProcess(hProcess, 0)) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Terminate Process"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLETERMINATEPRO, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } CloseHandle(hProcess); @@ -108,7 +105,9 @@ void ProcessPage_OnEndProcessTree(void) if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0)) return; - if (MessageBox(hMainWnd, _T("WARNING: Terminating a process can cause undesired\nresults including loss of data and system instability. The\nprocess will not be given the chance to save its state or\ndata before it is terminated. Are you sure you want to\nterminate the process?"), _T("Task Manager Warning"), MB_YESNO|MB_ICONWARNING) != IDYES) + LoadString(hInst, IDS_MSG_WARNINGTERMINATING, szTemp, 256); + LoadString(hInst, IDS_MSG_TASKMGRWARNING, szTempA, 256); + if (MessageBox(hMainWnd, szTemp, szTempA, MB_YESNO|MB_ICONWARNING) != IDYES) return; hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId); @@ -116,14 +115,16 @@ void ProcessPage_OnEndProcessTree(void) if (!hProcess) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Terminate Process"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLETERMINATEPRO, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } if (!TerminateProcess(hProcess, 0)) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Terminate Process"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLETERMINATEPRO, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } CloseHandle(hProcess); diff --git a/reactos/subsys/system/taskmgr/font.c b/reactos/subsys/system/taskmgr/font.c index d5369bacdeb..1b599c5eb11 100644 --- a/reactos/subsys/system/taskmgr/font.c +++ b/reactos/subsys/system/taskmgr/font.c @@ -21,14 +21,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include - -#include "font.h" void Font_DrawText(HDC hDC, LPCTSTR lpszText, int x, int y) { diff --git a/reactos/subsys/system/taskmgr/graph.c b/reactos/subsys/system/taskmgr/graph.c index 861e36d9930..51f4f73b261 100644 --- a/reactos/subsys/system/taskmgr/graph.c +++ b/reactos/subsys/system/taskmgr/graph.c @@ -21,18 +21,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "graph.h" -#include "font.h" -#include "perfdata.h" - LONG OldGraphWndProc; diff --git a/reactos/subsys/system/taskmgr/graphctl.c b/reactos/subsys/system/taskmgr/graphctl.c index 65ad43efd62..d8e2392c885 100644 --- a/reactos/subsys/system/taskmgr/graphctl.c +++ b/reactos/subsys/system/taskmgr/graphctl.c @@ -21,15 +21,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include - -#include -#include "graphctl.h" LONG OldGraphCtrlWndProc; diff --git a/reactos/subsys/system/taskmgr/makefile b/reactos/subsys/system/taskmgr/makefile index 75bf4f700f7..cd1ba71805a 100644 --- a/reactos/subsys/system/taskmgr/makefile +++ b/reactos/subsys/system/taskmgr/makefile @@ -16,7 +16,7 @@ TARGET_INSTALLDIR = system32 TARGET_PCH = precomp.h -TARGET_CFLAGS = -Werror -Wall -DDBG -D_WIN32_IE=0x0501 -D_WIN32_WINNT=0x0501 -D__USE_W32API +TARGET_CFLAGS = -Werror -Wall -DDBG -D_WIN32_IE=0x0501 -D_WIN32_WINNT=0x0501 -D__USE_W32API TARGET_SDKLIBS = ntdll.a kernel32.a user32.a gdi32.a comctl32.a diff --git a/reactos/subsys/system/taskmgr/optnmenu.c b/reactos/subsys/system/taskmgr/optnmenu.c index ea159017afd..5377b58e123 100644 --- a/reactos/subsys/system/taskmgr/optnmenu.c +++ b/reactos/subsys/system/taskmgr/optnmenu.c @@ -27,15 +27,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include - -#include "optnmenu.h" -#include "procpage.h" void TaskManager_OnOptionsAlwaysOnTop(void) { diff --git a/reactos/subsys/system/taskmgr/perfdata.c b/reactos/subsys/system/taskmgr/perfdata.c index 595c43a2994..d1efd5d769b 100644 --- a/reactos/subsys/system/taskmgr/perfdata.c +++ b/reactos/subsys/system/taskmgr/perfdata.c @@ -21,19 +21,8 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include -#include "perfdata.h" -PROCNTQSI NtQuerySystemInformation = NULL; -PROCGGR pGetGuiResources = NULL; -PROCGPIC pGetProcessIoCounters = NULL; -CRITICAL_SECTION PerfDataCriticalSection; +CRITICAL_SECTION PerfDataCriticalSection; PPERFDATA pPerfDataOld = NULL; /* Older perf data (saved to establish delta values) */ PPERFDATA pPerfData = NULL; /* Most recent copy of perf data */ ULONG ProcessCountOld = 0; @@ -48,21 +37,14 @@ SYSTEM_PERFORMANCE_INFORMATION SystemPerfInfo; SYSTEM_BASIC_INFORMATION SystemBasicInfo; SYSTEM_CACHE_INFORMATION SystemCacheInfo; SYSTEM_HANDLE_INFORMATION SystemHandleInfo; -PSYSTEM_PROCESSORTIME_INFO SystemProcessorTimeInfo = NULL; +PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemProcessorTimeInfo = NULL; BOOL PerfDataInitialize(void) { - LONG status; - - NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "NtQuerySystemInformation"); - pGetGuiResources = (PROCGGR)GetProcAddress(GetModuleHandle(_T("user32.dll")), "GetGuiResources"); - pGetProcessIoCounters = (PROCGPIC)GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "GetProcessIoCounters"); + NTSTATUS status; InitializeCriticalSection(&PerfDataCriticalSection); - if (!NtQuerySystemInformation) - return FALSE; - /* * Get number of processors in the system */ @@ -75,15 +57,13 @@ BOOL PerfDataInitialize(void) void PerfDataUninitialize(void) { - NtQuerySystemInformation = NULL; - DeleteCriticalSection(&PerfDataCriticalSection); } void PerfDataRefresh(void) { ULONG ulSize; - LONG status; + NTSTATUS status; LPBYTE pBuffer; ULONG BufferSize; PSYSTEM_PROCESS_INFORMATION pSPI; @@ -94,18 +74,14 @@ void PerfDataRefresh(void) TCHAR szTemp[MAX_PATH]; DWORD dwSize; SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo; - SYSTEM_TIME_INFORMATION SysTimeInfo; + SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo; SYSTEM_CACHE_INFORMATION SysCacheInfo; LPBYTE SysHandleInfoData; - PSYSTEM_PROCESSORTIME_INFO SysProcessorTimeInfo; + PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SysProcessorTimeInfo; double CurrentKernelTime; - - if (!NtQuerySystemInformation) - return; - /* Get new system time */ - status = NtQuerySystemInformation(SystemTimeInformation, &SysTimeInfo, sizeof(SysTimeInfo), 0); + status = NtQuerySystemInformation(SystemTimeOfDayInformation, &SysTimeInfo, sizeof(SysTimeInfo), 0); if (status != NO_ERROR) return; @@ -120,8 +96,8 @@ void PerfDataRefresh(void) return; /* Get processor time information */ - SysProcessorTimeInfo = (PSYSTEM_PROCESSORTIME_INFO)malloc(sizeof(SYSTEM_PROCESSORTIME_INFO) * SystemBasicInfo.bKeNumberProcessors); - status = NtQuerySystemInformation(SystemProcessorTimeInformation, SysProcessorTimeInfo, sizeof(SYSTEM_PROCESSORTIME_INFO) * SystemBasicInfo.bKeNumberProcessors, &ulSize); + SysProcessorTimeInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)HeapAlloc(GetProcessHeap(), 0, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * SystemBasicInfo.NumberProcessors); + status = NtQuerySystemInformation(SystemProcessorPerformanceInformation, SysProcessorTimeInfo, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * SystemBasicInfo.NumberProcessors, &ulSize); if (status != NO_ERROR) return; @@ -133,12 +109,12 @@ void PerfDataRefresh(void) do { BufferSize += 0x10000; - SysHandleInfoData = (LPBYTE)malloc(BufferSize); + SysHandleInfoData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, BufferSize); status = NtQuerySystemInformation(SystemHandleInformation, SysHandleInfoData, BufferSize, &ulSize); if (status == 0xC0000004 /*STATUS_INFO_LENGTH_MISMATCH*/) { - free(SysHandleInfoData); + HeapFree(GetProcessHeap(), 0, SysHandleInfoData); } } while (status == 0xC0000004 /*STATUS_INFO_LENGTH_MISMATCH*/); @@ -151,12 +127,12 @@ void PerfDataRefresh(void) do { BufferSize += 0x10000; - pBuffer = (LPBYTE)malloc(BufferSize); + pBuffer = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, BufferSize); status = NtQuerySystemInformation(SystemProcessInformation, pBuffer, BufferSize, &ulSize); if (status == 0xC0000004 /*STATUS_INFO_LENGTH_MISMATCH*/) { - free(pBuffer); + HeapFree(GetProcessHeap(), 0, pBuffer); } } while (status == 0xC0000004 /*STATUS_INFO_LENGTH_MISMATCH*/); @@ -177,7 +153,7 @@ void PerfDataRefresh(void) * Save system processor time info */ if (SystemProcessorTimeInfo) { - free(SystemProcessorTimeInfo); + HeapFree(GetProcessHeap(), 0, SystemProcessorTimeInfo); } SystemProcessorTimeInfo = SysProcessorTimeInfo; @@ -185,9 +161,9 @@ void PerfDataRefresh(void) * Save system handle info */ memcpy(&SystemHandleInfo, SysHandleInfoData, sizeof(SYSTEM_HANDLE_INFORMATION)); - free(SysHandleInfoData); + HeapFree(GetProcessHeap(), 0, SysHandleInfoData); - for (CurrentKernelTime=0, Idx=0; IdxRelativeOffset == 0) + if (pSPI->NextEntryOffset == 0) break; - pSPI = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pSPI + pSPI->RelativeOffset); + pSPI = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pSPI + pSPI->NextEntryOffset); } /* Now alloc a new PERFDATA array and fill in the data */ if (pPerfDataOld) { - free(pPerfDataOld); + HeapFree(GetProcessHeap(), 0, pPerfDataOld); } pPerfDataOld = pPerfData; - pPerfData = (PPERFDATA)malloc(sizeof(PERFDATA) * ProcessCount); + pPerfData = (PPERFDATA)HeapAlloc(GetProcessHeap(), 0, sizeof(PERFDATA) * ProcessCount); pSPI = (PSYSTEM_PROCESS_INFORMATION)pBuffer; for (Idx=0; IdxProcessId) { + if (pPerfDataOld[Idx2].ProcessId == pSPI->UniqueProcessId) { pPDOld = &pPerfDataOld[Idx2]; break; } @@ -249,25 +225,26 @@ void PerfDataRefresh(void) /* Clear out process perf data structure */ memset(&pPerfData[Idx], 0, sizeof(PERFDATA)); - if (pSPI->Name.Buffer) - wcscpy(pPerfData[Idx].ImageName, pSPI->Name.Buffer); + if (pSPI->ImageName.Buffer) + wcscpy(pPerfData[Idx].ImageName, pSPI->ImageName.Buffer); else - wcscpy(pPerfData[Idx].ImageName, L"System Idle Process"); + LoadStringW(hInst, IDS_IDLE_PROCESS, pPerfData[Idx].ImageName, + sizeof(pPerfData[Idx].ImageName) / sizeof(pPerfData[Idx].ImageName[0])); - pPerfData[Idx].ProcessId = pSPI->ProcessId; + pPerfData[Idx].ProcessId = pSPI->UniqueProcessId; if (pPDOld) { double CurTime = Li2Double(pSPI->KernelTime) + Li2Double(pSPI->UserTime); double OldTime = Li2Double(pPDOld->KernelTime) + Li2Double(pPDOld->UserTime); double CpuTime = (CurTime - OldTime) / dbSystemTime; - CpuTime = CpuTime * 100.0 / (double)SystemBasicInfo.bKeNumberProcessors; /* + 0.5; */ + CpuTime = CpuTime * 100.0 / (double)SystemBasicInfo.NumberProcessors; /* + 0.5; */ pPerfData[Idx].CPUUsage = (ULONG)CpuTime; } pPerfData[Idx].CPUTime.QuadPart = pSPI->UserTime.QuadPart + pSPI->KernelTime.QuadPart; - pPerfData[Idx].WorkingSetSizeBytes = pSPI->TotalWorkingSetSizeBytes; - pPerfData[Idx].PeakWorkingSetSizeBytes = pSPI->PeakWorkingSetSizeBytes; + pPerfData[Idx].WorkingSetSizeBytes = pSPI->WorkingSetSize; + pPerfData[Idx].PeakWorkingSetSizeBytes = pSPI->PeakWorkingSetSize; if (pPDOld) - pPerfData[Idx].WorkingSetSizeDelta = labs((LONG)pSPI->TotalWorkingSetSizeBytes - (LONG)pPDOld->WorkingSetSizeBytes); + pPerfData[Idx].WorkingSetSizeDelta = labs((LONG)pSPI->WorkingSetSize - (LONG)pPDOld->WorkingSetSizeBytes); else pPerfData[Idx].WorkingSetSizeDelta = 0; pPerfData[Idx].PageFaultCount = pSPI->PageFaultCount; @@ -275,15 +252,15 @@ void PerfDataRefresh(void) pPerfData[Idx].PageFaultCountDelta = labs((LONG)pSPI->PageFaultCount - (LONG)pPDOld->PageFaultCount); else pPerfData[Idx].PageFaultCountDelta = 0; - pPerfData[Idx].VirtualMemorySizeBytes = pSPI->TotalVirtualSizeBytes; - pPerfData[Idx].PagedPoolUsagePages = pSPI->TotalPagedPoolUsagePages; - pPerfData[Idx].NonPagedPoolUsagePages = pSPI->TotalNonPagedPoolUsagePages; + pPerfData[Idx].VirtualMemorySizeBytes = pSPI->VirtualSize; + pPerfData[Idx].PagedPoolUsagePages = pSPI->QuotaPagedPoolUsage; + pPerfData[Idx].NonPagedPoolUsagePages = pSPI->QuotaPeakNonPagedPoolUsage; pPerfData[Idx].BasePriority = pSPI->BasePriority; pPerfData[Idx].HandleCount = pSPI->HandleCount; - pPerfData[Idx].ThreadCount = pSPI->ThreadCount; + pPerfData[Idx].ThreadCount = pSPI->NumberOfThreads; pPerfData[Idx].SessionId = pSPI->SessionId; - hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pSPI->ProcessId); + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pSPI->UniqueProcessId); if (hProcess) { if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_IMPERSONATE, &hProcessToken)) { ImpersonateLoggedOnUser(hProcessToken); @@ -306,19 +283,16 @@ int MultiByteToWideChar( RevertToSelf(); CloseHandle(hProcessToken); } - if (pGetGuiResources) { - pPerfData[Idx].USERObjectCount = pGetGuiResources(hProcess, GR_USEROBJECTS); - pPerfData[Idx].GDIObjectCount = pGetGuiResources(hProcess, GR_GDIOBJECTS); - } - if (pGetProcessIoCounters) - pGetProcessIoCounters(hProcess, &pPerfData[Idx].IOCounters); + pPerfData[Idx].USERObjectCount = GetGuiResources(hProcess, GR_USEROBJECTS); + pPerfData[Idx].GDIObjectCount = GetGuiResources(hProcess, GR_GDIOBJECTS); + GetProcessIoCounters(hProcess, &pPerfData[Idx].IOCounters); CloseHandle(hProcess); } pPerfData[Idx].UserTime.QuadPart = pSPI->UserTime.QuadPart; pPerfData[Idx].KernelTime.QuadPart = pSPI->KernelTime.QuadPart; - pSPI = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pSPI + pSPI->RelativeOffset); + pSPI = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pSPI + pSPI->NextEntryOffset); } - free(pBuffer); + HeapFree(GetProcessHeap(), 0, pBuffer); LeaveCriticalSection(&PerfDataCriticalSection); } @@ -365,7 +339,7 @@ ULONG PerfDataGetProcessId(ULONG Index) EnterCriticalSection(&PerfDataCriticalSection); if (Index < ProcessCount) - ProcessId = pPerfData[Index].ProcessId; + ProcessId = (ULONG)pPerfData[Index].ProcessId; else ProcessId = 0; @@ -677,8 +651,8 @@ ULONG PerfDataGetCommitChargeTotalK(void) EnterCriticalSection(&PerfDataCriticalSection); - Total = SystemPerfInfo.MmTotalCommitedPages; - PageSize = SystemBasicInfo.uPageSize; + Total = SystemPerfInfo.TotalCommittedPages; + PageSize = SystemBasicInfo.PhysicalPageSize; LeaveCriticalSection(&PerfDataCriticalSection); @@ -694,8 +668,8 @@ ULONG PerfDataGetCommitChargeLimitK(void) EnterCriticalSection(&PerfDataCriticalSection); - Limit = SystemPerfInfo.MmTotalCommitLimit; - PageSize = SystemBasicInfo.uPageSize; + Limit = SystemPerfInfo.TotalCommitLimit; + PageSize = SystemBasicInfo.PhysicalPageSize; LeaveCriticalSection(&PerfDataCriticalSection); @@ -711,8 +685,8 @@ ULONG PerfDataGetCommitChargePeakK(void) EnterCriticalSection(&PerfDataCriticalSection); - Peak = SystemPerfInfo.MmPeakLimit; - PageSize = SystemBasicInfo.uPageSize; + Peak = SystemPerfInfo.PeakCommitment; + PageSize = SystemBasicInfo.PhysicalPageSize; LeaveCriticalSection(&PerfDataCriticalSection); @@ -730,9 +704,9 @@ ULONG PerfDataGetKernelMemoryTotalK(void) EnterCriticalSection(&PerfDataCriticalSection); - Paged = SystemPerfInfo.PoolPagedBytes; - NonPaged = SystemPerfInfo.PoolNonPagedBytes; - PageSize = SystemBasicInfo.uPageSize; + Paged = SystemPerfInfo.PagedPoolUsage; + NonPaged = SystemPerfInfo.NonPagedPoolUsage; + PageSize = SystemBasicInfo.PhysicalPageSize; LeaveCriticalSection(&PerfDataCriticalSection); @@ -751,8 +725,8 @@ ULONG PerfDataGetKernelMemoryPagedK(void) EnterCriticalSection(&PerfDataCriticalSection); - Paged = SystemPerfInfo.PoolPagedBytes; - PageSize = SystemBasicInfo.uPageSize; + Paged = SystemPerfInfo.PagedPoolUsage; + PageSize = SystemBasicInfo.PhysicalPageSize; LeaveCriticalSection(&PerfDataCriticalSection); @@ -768,8 +742,8 @@ ULONG PerfDataGetKernelMemoryNonPagedK(void) EnterCriticalSection(&PerfDataCriticalSection); - NonPaged = SystemPerfInfo.PoolNonPagedBytes; - PageSize = SystemBasicInfo.uPageSize; + NonPaged = SystemPerfInfo.NonPagedPoolUsage; + PageSize = SystemBasicInfo.PhysicalPageSize; LeaveCriticalSection(&PerfDataCriticalSection); @@ -785,8 +759,8 @@ ULONG PerfDataGetPhysicalMemoryTotalK(void) EnterCriticalSection(&PerfDataCriticalSection); - Total = SystemBasicInfo.uMmNumberOfPhysicalPages; - PageSize = SystemBasicInfo.uPageSize; + Total = SystemBasicInfo.NumberOfPhysicalPages; + PageSize = SystemBasicInfo.PhysicalPageSize; LeaveCriticalSection(&PerfDataCriticalSection); @@ -802,8 +776,8 @@ ULONG PerfDataGetPhysicalMemoryAvailableK(void) EnterCriticalSection(&PerfDataCriticalSection); - Available = SystemPerfInfo.MmAvailablePages; - PageSize = SystemBasicInfo.uPageSize; + Available = SystemPerfInfo.AvailablePages; + PageSize = SystemBasicInfo.PhysicalPageSize; LeaveCriticalSection(&PerfDataCriticalSection); @@ -820,7 +794,7 @@ ULONG PerfDataGetPhysicalMemorySystemCacheK(void) EnterCriticalSection(&PerfDataCriticalSection); SystemCache = SystemCacheInfo.CurrentSize; - PageSize = SystemBasicInfo.uPageSize; + PageSize = SystemBasicInfo.PhysicalPageSize; LeaveCriticalSection(&PerfDataCriticalSection); @@ -836,7 +810,7 @@ ULONG PerfDataGetSystemHandleCount(void) EnterCriticalSection(&PerfDataCriticalSection); - HandleCount = SystemHandleInfo.Count; + HandleCount = SystemHandleInfo.NumberOfHandles; LeaveCriticalSection(&PerfDataCriticalSection); diff --git a/reactos/subsys/system/taskmgr/perfdata.h b/reactos/subsys/system/taskmgr/perfdata.h index 44766e7558e..27f8dc860e1 100644 --- a/reactos/subsys/system/taskmgr/perfdata.h +++ b/reactos/subsys/system/taskmgr/perfdata.h @@ -27,27 +27,12 @@ extern "C" { #endif - -#if 0 -typedef struct _TIME { - DWORD LowPart; - LONG HighPart; -} TIME, *PTIME; -#endif - -typedef ULARGE_INTEGER TIME, *PTIME; - -/* typedef WCHAR UNICODE_STRING; */ -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING, *PUNICODE_STRING; +#define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + (double)((x).LowPart)) typedef struct _PERFDATA { WCHAR ImageName[MAX_PATH]; - ULONG ProcessId; + HANDLE ProcessId; WCHAR UserName[MAX_PATH]; ULONG SessionId; ULONG CPUUsage; @@ -71,277 +56,6 @@ typedef struct _PERFDATA TIME KernelTime; } PERFDATA, *PPERFDATA; -typedef struct _CLIENT_ID -{ - HANDLE UniqueProcess; - HANDLE UniqueThread; -} CLIENT_ID, *PCLIENT_ID; - -typedef enum _KWAIT_REASON -{ - Executive, - FreePage, - PageIn, - PoolAllocation, - DelayExecution, - Suspended, - UserRequest, - WrExecutive, - WrFreePage, - WrPageIn, - WrDelayExecution, - WrSuspended, - WrUserRequest, - WrQueue, - WrLpcReceive, - WrLpcReply, - WrVirtualMemory, - WrPageOut, - WrRendezvous, - Spare2, - Spare3, - Spare4, - Spare5, - Spare6, - WrKernel, - MaximumWaitReason, -} KWAIT_REASON; - -/* SystemProcessThreadInfo (5) */ -typedef struct _SYSTEM_THREAD_INFORMATION -{ - TIME KernelTime; - TIME UserTime; - TIME CreateTime; - ULONG TickCount; - ULONG StartEIP; - CLIENT_ID ClientId; - ULONG DynamicPriority; - ULONG BasePriority; - ULONG nSwitches; - DWORD State; - KWAIT_REASON WaitReason; - -} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; - -typedef struct SYSTEM_PROCESS_INFORMATION -{ - ULONG RelativeOffset; - ULONG ThreadCount; - ULONG Unused1 [6]; - TIME CreateTime; - TIME UserTime; - TIME KernelTime; - UNICODE_STRING Name; - ULONG BasePriority; - ULONG ProcessId; - ULONG ParentProcessId; - ULONG HandleCount; - ULONG SessionId; - ULONG Unused2; - ULONG PeakVirtualSizeBytes; - ULONG TotalVirtualSizeBytes; - ULONG PageFaultCount; - ULONG PeakWorkingSetSizeBytes; - ULONG TotalWorkingSetSizeBytes; - ULONG PeakPagedPoolUsagePages; - ULONG TotalPagedPoolUsagePages; - ULONG PeakNonPagedPoolUsagePages; - ULONG TotalNonPagedPoolUsagePages; - ULONG TotalPageFileUsageBytes; - ULONG PeakPageFileUsageBytes; - ULONG TotalPrivateBytes; - SYSTEM_THREAD_INFORMATION ThreadSysInfo [1]; - -} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; - -typedef struct -{ - DWORD dwUnknown1; - ULONG uKeMaximumIncrement; - ULONG uPageSize; - ULONG uMmNumberOfPhysicalPages; - ULONG uMmLowestPhysicalPage; - ULONG uMmHighestPhysicalPage; - ULONG uAllocationGranularity; - PVOID pLowestUserAddress; - PVOID pMmHighestUserAddress; - ULONG uKeActiveProcessors; - BYTE bKeNumberProcessors; - BYTE bUnknown2; - WORD wUnknown3; -} SYSTEM_BASIC_INFORMATION; - -/* SystemPerformanceInfo (2) */ -typedef struct _SYSTEM_PERFORMANCE_INFORMATION -{ - LARGE_INTEGER /*TotalProcessorTime*/liIdleTime; - LARGE_INTEGER IoReadTransferCount; - LARGE_INTEGER IoWriteTransferCount; - LARGE_INTEGER IoOtherTransferCount; - ULONG IoReadOperationCount; - ULONG IoWriteOperationCount; - ULONG IoOtherOperationCount; - ULONG MmAvailablePages; - ULONG MmTotalCommitedPages; - ULONG MmTotalCommitLimit; - ULONG MmPeakLimit; - ULONG PageFaults; - ULONG WriteCopies; - ULONG TransitionFaults; - ULONG Unknown1; - ULONG DemandZeroFaults; - ULONG PagesInput; - ULONG PagesRead; - ULONG Unknown2; - ULONG Unknown3; - ULONG PagesOutput; - ULONG PageWrites; - ULONG Unknown4; - ULONG Unknown5; - ULONG PoolPagedBytes; - ULONG PoolNonPagedBytes; - ULONG Unknown6; - ULONG Unknown7; - ULONG Unknown8; - ULONG Unknown9; - ULONG MmTotalSystemFreePtes; - ULONG MmSystemCodepage; - ULONG MmTotalSystemDriverPages; - ULONG MmTotalSystemCodePages; - ULONG Unknown10; - ULONG Unknown11; - ULONG Unknown12; - ULONG MmSystemCachePage; - ULONG MmPagedPoolPage; - ULONG MmSystemDriverPage; - ULONG CcFastReadNoWait; - ULONG CcFastReadWait; - ULONG CcFastReadResourceMiss; - ULONG CcFastReadNotPossible; - ULONG CcFastMdlReadNoWait; - ULONG CcFastMdlReadWait; - ULONG CcFastMdlReadResourceMiss; - ULONG CcFastMdlReadNotPossible; - ULONG CcMapDataNoWait; - ULONG CcMapDataWait; - ULONG CcMapDataNoWaitMiss; - ULONG CcMapDataWaitMiss; - ULONG CcPinMappedDataCount; - ULONG CcPinReadNoWait; - ULONG CcPinReadWait; - ULONG CcPinReadNoWaitMiss; - ULONG CcPinReadWaitMiss; - ULONG CcCopyReadNoWait; - ULONG CcCopyReadWait; - ULONG CcCopyReadNoWaitMiss; - ULONG CcCopyReadWaitMiss; - ULONG CcMdlReadNoWait; - ULONG CcMdlReadWait; - ULONG CcMdlReadNoWaitMiss; - ULONG CcMdlReadWaitMiss; - ULONG CcReadaheadIos; - ULONG CcLazyWriteIos; - ULONG CcLazyWritePages; - ULONG CcDataFlushes; - ULONG CcDataPages; - ULONG ContextSwitches; - ULONG Unknown13; - ULONG Unknown14; - ULONG SystemCalls; - -} SYSTEM_PERFORMANCE_INFORMATION, *PSYSTEM_PERFORMANCE_INFORMATION; - -typedef struct -{ - LARGE_INTEGER liKeBootTime; - LARGE_INTEGER liKeSystemTime; - LARGE_INTEGER liExpTimeZoneBias; - ULONG uCurrentTimeZoneId; - DWORD dwReserved; -} SYSTEM_TIME_INFORMATION; - -/* SystemCacheInformation (21) */ -typedef struct _SYSTEM_CACHE_INFORMATION -{ - ULONG CurrentSize; - ULONG PeakSize; - ULONG PageFaultCount; - ULONG MinimumWorkingSet; - ULONG MaximumWorkingSet; - ULONG Unused[4]; - -} SYSTEM_CACHE_INFORMATION; - -/* SystemPageFileInformation (18) */ -typedef -struct _SYSTEM_PAGEFILE_INFORMATION -{ - ULONG RelativeOffset; - ULONG CurrentSizePages; - ULONG TotalUsedPages; - ULONG PeakUsedPages; - UNICODE_STRING PagefileFileName; - -} SYSTEM_PAGEFILE_INFORMATION, *PSYSTEM_PAGEFILE_INFORMATION; - -/* SystemHandleInformation (16) */ -/* (see ontypes.h) */ -typedef -struct _SYSTEM_HANDLE_ENTRY -{ - ULONG OwnerPid; - BYTE ObjectType; - BYTE HandleFlags; - USHORT HandleValue; - PVOID ObjectPointer; - ULONG AccessMask; - -} SYSTEM_HANDLE_ENTRY, *PSYSTEM_HANDLE_ENTRY; - -typedef -struct _SYSTEM_HANDLE_INFORMATION -{ - ULONG Count; - SYSTEM_HANDLE_ENTRY Handle [1]; - -} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; - -/* SystemProcessorPerformanceInformation (8) */ -typedef -struct _SYSTEM_PROCESSORTIME_INFO -{ - LARGE_INTEGER IdleTime; - LARGE_INTEGER KernelTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER DpcTime; - LARGE_INTEGER InterruptTime; - ULONG InterruptCount; - ULONG Unused; - -} SYSTEM_PROCESSORTIME_INFO, *PSYSTEM_PROCESSORTIME_INFO; - -#define SystemBasicInformation 0 -#define SystemPerformanceInformation 2 -#define SystemTimeInformation 3 -#define SystemProcessInformation 5 -#define SystemProcessorTimeInformation 8 -#define SystemHandleInformation 16 -#define SystemPageFileInformation 18 -#define SystemCacheInformation 21 - -#define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + (double)((x).LowPart)) - -#define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 -#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 - -#define GR_GDIOBJECTS 0 /* Count of GDI objects */ -#define GR_USEROBJECTS 1 /* Count of USER objects */ - -typedef LONG (WINAPI *PROCNTQSI)(UINT,PVOID,ULONG,PULONG); -typedef DWORD (WINAPI *PROCGGR)(HANDLE,DWORD); -typedef BOOL (WINAPI *PROCGPIC)(HANDLE,PIO_COUNTERS); - BOOL PerfDataInitialize(void); void PerfDataUninitialize(void); void PerfDataRefresh(void); diff --git a/reactos/subsys/system/taskmgr/perfpage.c b/reactos/subsys/system/taskmgr/perfpage.c index 119fbe384ba..b3e9a12dc07 100644 --- a/reactos/subsys/system/taskmgr/perfpage.c +++ b/reactos/subsys/system/taskmgr/perfpage.c @@ -21,19 +21,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "perfpage.h" -#include "perfdata.h" - -#include "graph.h" -#include "graphctl.h" TGraphCtrl PerformancePageCpuUsageHistoryGraph; TGraphCtrl PerformancePageMemUsageHistoryGraph; diff --git a/reactos/subsys/system/taskmgr/precomp.h b/reactos/subsys/system/taskmgr/precomp.h index 16ac717d58c..f4d5e011207 100644 --- a/reactos/subsys/system/taskmgr/precomp.h +++ b/reactos/subsys/system/taskmgr/precomp.h @@ -1,3 +1,30 @@ -#define WIN32_LEAN_AND_MEAN /* Exclude rarely-used stuff from Windows headers */ +#define NTOS_MODE_USER +#include #include +#include +#include +#include +#include +#include +#include +#include + #include "taskmgr.h" +#include "perfdata.h" +#include "perfpage.h" +#include "about.h" +#include "procpage.h" +#include "proclist.h" +#include "affinity.h" +#include "applpage.h" +#include "column.h" +#include "dbgchnl.h" +#include "debug.h" +#include "endproc.h" +#include "font.h" +#include "graph.h" +#include "graphctl.h" +#include "optnmenu.h" +#include "priority.h" +#include "run.h" +#include "trayicon.h" diff --git a/reactos/subsys/system/taskmgr/priority.c b/reactos/subsys/system/taskmgr/priority.c index c87969a9e2c..888d7abf378 100644 --- a/reactos/subsys/system/taskmgr/priority.c +++ b/reactos/subsys/system/taskmgr/priority.c @@ -4,6 +4,7 @@ * priority.c * * Copyright (C) 1999 - 2001 Brian Palmer + * 2005 Klemens Friedl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,17 +22,9 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "priority.h" -#include "procpage.h" -#include "perfdata.h" + +TCHAR szTemp[256]; +TCHAR szTempA[256]; void ProcessPage_OnSetPriorityRealTime(void) { @@ -60,7 +53,9 @@ void ProcessPage_OnSetPriorityRealTime(void) if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0)) return; - if (MessageBox(hMainWnd, _T("WARNING: Changing the priority class of this process may\ncause undesired results including system instability. Are you\nsure you want to change the priority class?"), _T("Task Manager Warning"), MB_YESNO|MB_ICONWARNING) != IDYES) + LoadString(hInst, IDS_MSG_WARNINGCHANGEPRIORITY, szTemp, 256); + LoadString(hInst, IDS_MSG_TASKMGRWARNING, szTempA, 256); + if (MessageBox(hMainWnd, szTemp, szTempA, MB_YESNO|MB_ICONWARNING) != IDYES) return; hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, dwProcessId); @@ -68,14 +63,16 @@ void ProcessPage_OnSetPriorityRealTime(void) if (!hProcess) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } if (!SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS)) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } CloseHandle(hProcess); @@ -108,7 +105,9 @@ void ProcessPage_OnSetPriorityHigh(void) if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0)) return; - if (MessageBox(hMainWnd, _T("WARNING: Changing the priority class of this process may\ncause undesired results including system instability. Are you\nsure you want to change the priority class?"), _T("Task Manager Warning"), MB_YESNO|MB_ICONWARNING) != IDYES) + LoadString(hInst, IDS_MSG_WARNINGCHANGEPRIORITY, szTemp, 256); + LoadString(hInst, IDS_MSG_TASKMGRWARNING, szTempA, 256); + if (MessageBox(hMainWnd, szTemp, szTempA, MB_YESNO|MB_ICONWARNING) != IDYES) return; hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, dwProcessId); @@ -116,14 +115,16 @@ void ProcessPage_OnSetPriorityHigh(void) if (!hProcess) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } if (!SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } CloseHandle(hProcess); @@ -156,7 +157,9 @@ void ProcessPage_OnSetPriorityAboveNormal(void) if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0)) return; - if (MessageBox(hMainWnd, _T("WARNING: Changing the priority class of this process may\ncause undesired results including system instability. Are you\nsure you want to change the priority class?"), _T("Task Manager Warning"), MB_YESNO|MB_ICONWARNING) != IDYES) + LoadString(hInst, IDS_MSG_WARNINGCHANGEPRIORITY, szTemp, 256); + LoadString(hInst, IDS_MSG_TASKMGRWARNING, szTempA, 256); + if (MessageBox(hMainWnd, szTemp, szTempA, MB_YESNO|MB_ICONWARNING) != IDYES) return; hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, dwProcessId); @@ -164,14 +167,16 @@ void ProcessPage_OnSetPriorityAboveNormal(void) if (!hProcess) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } if (!SetPriorityClass(hProcess, ABOVE_NORMAL_PRIORITY_CLASS)) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } CloseHandle(hProcess); @@ -204,7 +209,9 @@ void ProcessPage_OnSetPriorityNormal(void) if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0)) return; - if (MessageBox(hMainWnd, _T("WARNING: Changing the priority class of this process may\ncause undesired results including system instability. Are you\nsure you want to change the priority class?"), _T("Task Manager Warning"), MB_YESNO|MB_ICONWARNING) != IDYES) + LoadString(hInst, IDS_MSG_WARNINGCHANGEPRIORITY, szTemp, 256); + LoadString(hInst, IDS_MSG_TASKMGRWARNING, szTempA, 256); + if (MessageBox(hMainWnd, szTemp, szTempA, MB_YESNO|MB_ICONWARNING) != IDYES) return; hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, dwProcessId); @@ -212,14 +219,16 @@ void ProcessPage_OnSetPriorityNormal(void) if (!hProcess) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } if (!SetPriorityClass(hProcess, NORMAL_PRIORITY_CLASS)) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } CloseHandle(hProcess); @@ -252,7 +261,9 @@ void ProcessPage_OnSetPriorityBelowNormal(void) if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0)) return; - if (MessageBox(hMainWnd, _T("WARNING: Changing the priority class of this process may\ncause undesired results including system instability. Are you\nsure you want to change the priority class?"), _T("Task Manager Warning"), MB_YESNO|MB_ICONWARNING) != IDYES) + LoadString(hInst, IDS_MSG_WARNINGCHANGEPRIORITY, szTemp, 256); + LoadString(hInst, IDS_MSG_TASKMGRWARNING, szTempA, 256); + if (MessageBox(hMainWnd, szTemp, szTempA, MB_YESNO|MB_ICONWARNING) != IDYES) return; hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, dwProcessId); @@ -260,14 +271,16 @@ void ProcessPage_OnSetPriorityBelowNormal(void) if (!hProcess) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } if (!SetPriorityClass(hProcess, BELOW_NORMAL_PRIORITY_CLASS)) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } CloseHandle(hProcess); @@ -300,7 +313,9 @@ void ProcessPage_OnSetPriorityLow(void) if ((ListView_GetSelectedCount(hProcessPageListCtrl) != 1) || (dwProcessId == 0)) return; - if (MessageBox(hMainWnd, _T("WARNING: Changing the priority class of this process may\ncause undesired results including system instability. Are you\nsure you want to change the priority class?"), _T("Task Manager Warning"), MB_YESNO|MB_ICONWARNING) != IDYES) + LoadString(hInst, IDS_MSG_WARNINGCHANGEPRIORITY, szTemp, 256); + LoadString(hInst, IDS_MSG_TASKMGRWARNING, szTempA, 256); + if (MessageBox(hMainWnd, szTemp, szTempA, MB_YESNO|MB_ICONWARNING) != IDYES) return; hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, dwProcessId); @@ -308,14 +323,16 @@ void ProcessPage_OnSetPriorityLow(void) if (!hProcess) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); return; } if (!SetPriorityClass(hProcess, IDLE_PRIORITY_CLASS)) { GetLastErrorText(strErrorText, 260); - MessageBox(hMainWnd, strErrorText, _T("Unable to Change Priority"), MB_OK|MB_ICONSTOP); + LoadString(hInst, IDS_MSG_UNABLECHANGEPRIORITY, szTemp, 256); + MessageBox(hMainWnd, strErrorText, szTemp, MB_OK|MB_ICONSTOP); } CloseHandle(hProcess); diff --git a/reactos/subsys/system/taskmgr/proclist.c b/reactos/subsys/system/taskmgr/proclist.c index 3b544a64dc4..281147c867d 100644 --- a/reactos/subsys/system/taskmgr/proclist.c +++ b/reactos/subsys/system/taskmgr/proclist.c @@ -21,17 +21,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "procpage.h" -#include "proclist.h" -#include "perfdata.h" INT_PTR CALLBACK ProcessListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); diff --git a/reactos/subsys/system/taskmgr/procpage.c b/reactos/subsys/system/taskmgr/procpage.c index 7d872f9ca40..1e1d8e9c5f7 100644 --- a/reactos/subsys/system/taskmgr/procpage.c +++ b/reactos/subsys/system/taskmgr/procpage.c @@ -21,21 +21,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "procpage.h" -#include "perfdata.h" -#include "column.h" -#include "proclist.h" -#include "dbgchnl.h" -#include "endproc.h" -#include HWND hProcessPage; /* Process List Property Page */ diff --git a/reactos/subsys/system/taskmgr/resource.h b/reactos/subsys/system/taskmgr/resource.h index 49a45774ddd..e82c9d9d94d 100644 --- a/reactos/subsys/system/taskmgr/resource.h +++ b/reactos/subsys/system/taskmgr/resource.h @@ -2,6 +2,7 @@ // Microsoft Developer Studio generated include file. // Used by TaskMgr.rc // +#define IDS_IDLE_PROCESS 102 #define IDD_TASKMGR_DIALOG 102 #define IDD_ABOUTBOX 103 #define IDS_APP_TITLE 103 @@ -183,6 +184,61 @@ #define IDS_LICENSE 32816 #define IDC_STATIC -1 +#define IDS_TAB_APPS 310 +#define IDS_TAB_PROCESSES 311 +#define IDS_TAB_PERFORMANCE 312 +#define IDS_TAB_TASK 313 +#define IDS_TAB_STATUS 314 +#define IDS_TAB_IMAGENAME 315 +#define IDS_TAB_PID 316 +#define IDS_TAB_USERNAME 317 +#define IDS_TAB_SESSIONID 318 +#define IDS_TAB_CPU 319 +#define IDS_TAB_CPUTIME 320 +#define IDS_TAB_MEMUSAGE 321 +#define IDS_TAB_PEAKMEMUSAGE 322 +#define IDS_TAB_MEMDELTA 323 +#define IDS_TAB_PAGEFAULT 324 +#define IDS_TAB_PFDELTA 325 +#define IDS_TAB_VMSIZE 326 +#define IDS_TAB_PAGEDPOOL 327 +#define IDS_TAB_NPPOOL 328 +#define IDS_TAB_BASEPRI 329 +#define IDS_TAB_HANDLES 330 +#define IDS_TAB_THREADS 331 +#define IDS_TAB_USERPBJECTS 332 +#define IDS_TAB_GDIOBJECTS 333 +#define IDS_TAB_IOREADS 334 +#define IDS_TAB_IOWRITES 335 +#define IDS_TAB_IOOTHER 336 +#define IDS_TAB_IOREADBYTES 337 +#define IDS_TAB_IOWRITESBYTES 338 +#define IDS_TAB_IOOTHERBYTES 339 +#define IDS_MENU_SELECTCOLUMNS 340 +#define IDS_MENU_16BITTASK 341 +#define IDS_MENU_WINDOWS 342 +#define IDS_MENU_LARGEICONS 343 +#define IDS_MENU_SMALLICONS 344 +#define IDS_MENU_DETAILS 345 +#define IDS_MENU_ONEGRAPHALLCPUS 346 +#define IDS_MENU_ONEGRAPHPERCPU 347 +#define IDS_MENU_CPUHISTORY 348 +#define IDS_MENU_SHOWKERNELTIMES 349 +#define IDS_CREATENEWTASK 350 +#define IDS_CREATENEWTASK_DESC 351 +#define IDS_MSG_ACCESSPROCESSAFF 352 +#define IDS_MSG_PROCESSONEPRO 353 +#define IDS_MSG_INVALIDOPTION 354 +#define IDS_MSG_UNABLEDEBUGPROCESS 355 +#define IDS_MSG_WARNINGDEBUG 356 +#define IDS_MSG_TASKMGRWARNING 357 +#define IDS_MSG_WARNINGTERMINATING 358 +#define IDS_MSG_UNABLETERMINATEPRO 359 +#define IDS_MSG_UNABLECHANGEPRIORITY 360 +#define IDS_MSG_WARNINGCHANGEPRIORITY 361 +#define IDS_MSG_TRAYICONCPUUSAGE 362 + + // Next default values for new objects // #ifdef APSTUDIO_INVOKED diff --git a/reactos/subsys/system/taskmgr/run.c b/reactos/subsys/system/taskmgr/run.c index 88ff88d981f..08a228bf737 100644 --- a/reactos/subsys/system/taskmgr/run.c +++ b/reactos/subsys/system/taskmgr/run.c @@ -4,6 +4,7 @@ * run.c * * Copyright (C) 1999 - 2001 Brian Palmer + * 2005 Klemens Friedl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,24 +22,25 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include - -#include "run.h" void TaskManager_OnFileNew(void) { - HMODULE hShell32; - RUNFILEDLG RunFileDlg; + HMODULE hShell32; + RUNFILEDLG RunFileDlg; OSVERSIONINFO versionInfo; WCHAR wTitle[40]; WCHAR wText[256]; - char szTitle[40] = "Create New Task"; - char szText[256] = "Type the name of a program, folder, document, or Internet resource, and Task Manager will open it for you."; + TCHAR szTemp[256]; + char szTitle[40]; + char szText[256]; + + /* Load language string from resource file */ + LoadString(hInst, IDS_CREATENEWTASK, szTemp, 40); + strcpy(szTitle,szTemp); + + LoadString(hInst, IDS_CREATENEWTASK_DESC, szTemp, 256); + strcpy(szText,szTemp); + hShell32 = LoadLibrary(_T("SHELL32.DLL")); RunFileDlg = (RUNFILEDLG)(FARPROC)GetProcAddress(hShell32, (char*)((long)0x3D)); diff --git a/reactos/subsys/system/taskmgr/taskmgr.c b/reactos/subsys/system/taskmgr/taskmgr.c index ccb3648c5da..751fe7e2f2e 100644 --- a/reactos/subsys/system/taskmgr/taskmgr.c +++ b/reactos/subsys/system/taskmgr/taskmgr.c @@ -4,6 +4,7 @@ * TaskMgr.c : Defines the entry point for the application. * * Copyright (C) 1999 - 2001 Brian Palmer + * 2005 Klemens Friedl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,29 +22,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "resource.h" -#include "applpage.h" -#include "procpage.h" -#include "perfpage.h" -#include "run.h" -#include "perfdata.h" -#include "optnmenu.h" -#include "affinity.h" -#include "priority.h" -#include "debug.h" -#include "endproc.h" -#include "column.h" -#include "about.h" -#include "trayicon.h" -#include "dbgchnl.h" #define STATUS_WINDOW 2001 @@ -257,7 +235,7 @@ TaskManagerWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) OnAbout(); break; case ID_FILE_EXIT: - DestroyWindow(hDlg); + EndDialog(hDlg, IDOK); break; } break; @@ -481,17 +459,17 @@ BOOL OnCreate(HWND hWnd) #endif /* Insert tabs */ - _tcscpy(szTemp, _T("Applications")); + LoadString(hInst, IDS_TAB_APPS, szTemp, 256); memset(&item, 0, sizeof(TCITEM)); item.mask = TCIF_TEXT; item.pszText = szTemp; TabCtrl_InsertItem(hTabWnd, 0, &item); - _tcscpy(szTemp, _T("Processes")); + LoadString(hInst, IDS_TAB_PROCESSES, szTemp, 256); memset(&item, 0, sizeof(TCITEM)); item.mask = TCIF_TEXT; item.pszText = szTemp; TabCtrl_InsertItem(hTabWnd, 1, &item); - _tcscpy(szTemp, _T("Performance")); + LoadString(hInst, IDS_TAB_PERFORMANCE, szTemp, 256); memset(&item, 0, sizeof(TCITEM)); item.mask = TCIF_TEXT; item.pszText = szTemp; @@ -590,23 +568,12 @@ BOOL OnCreate(HWND hWnd) TabCtrl_SetCurFocus/*Sel*/(hTabWnd, 2); TabCtrl_SetCurFocus/*Sel*/(hTabWnd, nActivePage); - if (TaskManagerSettings.UpdateSpeed == 0) - KillTimer(hWnd, 1); - else if (TaskManagerSettings.UpdateSpeed == 1) - { - KillTimer(hWnd, 1); + if (TaskManagerSettings.UpdateSpeed == 1) SetTimer(hWnd, 1, 1000, NULL); - } else if (TaskManagerSettings.UpdateSpeed == 2) - { - KillTimer(hWnd, 1); SetTimer(hWnd, 1, 2000, NULL); - } else if (TaskManagerSettings.UpdateSpeed == 4) - { - KillTimer(hWnd, 1); SetTimer(hWnd, 1, 4000, NULL); - } /* * Refresh the performance data @@ -965,6 +932,7 @@ void TaskManager_OnTabWndSelChange(void) HMENU hOptionsMenu; HMENU hViewMenu; HMENU hSubMenu; + TCHAR szTemp[256]; hMenu = GetMenu(hMainWnd); hViewMenu = GetSubMenu(hMenu, 2); @@ -983,13 +951,22 @@ void TaskManager_OnTabWndSelChange(void) ShowWindow(hProcessPage, SW_HIDE); ShowWindow(hPerformancePage, SW_HIDE); BringWindowToTop(hApplicationPage); - AppendMenu(hViewMenu, MF_STRING, ID_VIEW_LARGE, _T("Lar&ge Icons")); - AppendMenu(hViewMenu, MF_STRING, ID_VIEW_SMALL, _T("S&mall Icons")); - AppendMenu(hViewMenu, MF_STRING, ID_VIEW_DETAILS, _T("&Details")); + + LoadString(hInst, IDS_MENU_LARGEICONS, szTemp, 256); + AppendMenu(hViewMenu, MF_STRING, ID_VIEW_LARGE, szTemp); + + LoadString(hInst, IDS_MENU_SMALLICONS, szTemp, 256); + AppendMenu(hViewMenu, MF_STRING, ID_VIEW_SMALL, szTemp); + + LoadString(hInst, IDS_MENU_DETAILS, szTemp, 256); + AppendMenu(hViewMenu, MF_STRING, ID_VIEW_DETAILS, szTemp); if (GetMenuItemCount(hMenu) <= 4) { hSubMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_WINDOWSMENU)); - InsertMenu(hMenu, 3, MF_BYPOSITION|MF_POPUP, (UINT)hSubMenu, _T("&Windows")); + + LoadString(hInst, IDS_MENU_WINDOWS, szTemp, 256); + InsertMenu(hMenu, 3, MF_BYPOSITION|MF_POPUP, (UINT)hSubMenu, szTemp); + DrawMenuBar(hMainWnd); } if (TaskManagerSettings.View_LargeIcons) @@ -1009,8 +986,13 @@ void TaskManager_OnTabWndSelChange(void) ShowWindow(hProcessPage, SW_SHOW); ShowWindow(hPerformancePage, SW_HIDE); BringWindowToTop(hProcessPage); - AppendMenu(hViewMenu, MF_STRING, ID_VIEW_SELECTCOLUMNS, _T("&Select Columns...")); - AppendMenu(hOptionsMenu, MF_STRING, ID_OPTIONS_SHOW16BITTASKS, _T("&Show 16-bit tasks")); + + LoadString(hInst, IDS_MENU_SELECTCOLUMNS, szTemp, 256); + AppendMenu(hViewMenu, MF_STRING, ID_VIEW_SELECTCOLUMNS, szTemp); + + LoadString(hInst, IDS_MENU_16BITTASK, szTemp, 256); + AppendMenu(hOptionsMenu, MF_STRING, ID_OPTIONS_SHOW16BITTASKS, szTemp); + if (TaskManagerSettings.Show16BitTasks) CheckMenuItem(hOptionsMenu, ID_OPTIONS_SHOW16BITTASKS, MF_BYCOMMAND|MF_CHECKED); if (GetMenuItemCount(hMenu) > 4) @@ -1034,10 +1016,19 @@ void TaskManager_OnTabWndSelChange(void) DrawMenuBar(hMainWnd); } hSubMenu = CreatePopupMenu(); - AppendMenu(hSubMenu, MF_STRING, ID_VIEW_CPUHISTORY_ONEGRAPHALL, _T("&One Graph, All CPUs")); - AppendMenu(hSubMenu, MF_STRING, ID_VIEW_CPUHISTORY_ONEGRAPHPERCPU, _T("One Graph &Per CPU")); - AppendMenu(hViewMenu, MF_STRING|MF_POPUP, (UINT)hSubMenu, _T("&CPU History")); - AppendMenu(hViewMenu, MF_STRING, ID_VIEW_SHOWKERNELTIMES, _T("&Show Kernel Times")); + + LoadString(hInst, IDS_MENU_ONEGRAPHALLCPUS, szTemp, 256); + AppendMenu(hSubMenu, MF_STRING, ID_VIEW_CPUHISTORY_ONEGRAPHALL, szTemp); + + LoadString(hInst, IDS_MENU_ONEGRAPHPERCPU, szTemp, 256); + AppendMenu(hSubMenu, MF_STRING, ID_VIEW_CPUHISTORY_ONEGRAPHPERCPU, szTemp); + + LoadString(hInst, IDS_MENU_CPUHISTORY, szTemp, 256); + AppendMenu(hViewMenu, MF_STRING|MF_POPUP, (UINT)hSubMenu, szTemp); + + LoadString(hInst, IDS_MENU_SHOWKERNELTIMES, szTemp, 256); + AppendMenu(hViewMenu, MF_STRING, ID_VIEW_SHOWKERNELTIMES, szTemp); + if (TaskManagerSettings.ShowKernelTimes) CheckMenuItem(hViewMenu, ID_VIEW_SHOWKERNELTIMES, MF_BYCOMMAND|MF_CHECKED); else diff --git a/reactos/subsys/system/taskmgr/taskmgr.rc b/reactos/subsys/system/taskmgr/taskmgr.rc index 40e3ed11ad5..6f2eb525668 100644 --- a/reactos/subsys/system/taskmgr/taskmgr.rc +++ b/reactos/subsys/system/taskmgr/taskmgr.rc @@ -248,6 +248,8 @@ IDB_FONT BITMAP DISCARDABLE res_dir/font.bmp } */ #include "En.rc" -#include "Es.rc" #include "De.rc" +#include "Dk.rc" +#include "Es.rc" +#include "Sv.rc" diff --git a/reactos/subsys/system/taskmgr/trayicon.c b/reactos/subsys/system/taskmgr/trayicon.c index 1955b7a6948..dfc4e5a1c88 100644 --- a/reactos/subsys/system/taskmgr/trayicon.c +++ b/reactos/subsys/system/taskmgr/trayicon.c @@ -4,6 +4,7 @@ * trayicon.c * * Copyright (C) 1999 - 2001 Brian Palmer + * 2005 Klemens Friedl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,17 +22,6 @@ */ #include "precomp.h" -#include -#include -#include -#include -#include -#include -#include - -#include "trayicon.h" -#include "perfdata.h" -#include "shellapi.h" HICON TrayIcon_GetProcessorUsageIcon(void) { @@ -186,9 +176,10 @@ BOOL TrayIcon_ShellRemoveTrayIcon(void) BOOL TrayIcon_ShellUpdateTrayIcon(void) { - NOTIFYICONDATA nid; - HICON hIcon = NULL; + NOTIFYICONDATA nid; + HICON hIcon = NULL; BOOL bRetVal; + TCHAR szTemp[256]; memset(&nid, 0, sizeof(NOTIFYICONDATA)); @@ -200,7 +191,8 @@ BOOL TrayIcon_ShellUpdateTrayIcon(void) nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; nid.uCallbackMessage = WM_ONTRAYICON; nid.hIcon = hIcon; - wsprintf(nid.szTip, _T("CPU Usage: %d%%"), PerfDataGetProcessorUsage()); + LoadString(hInst, IDS_MSG_TRAYICONCPUUSAGE, szTemp, 256); + wsprintf(nid.szTip, szTemp, PerfDataGetProcessorUsage()); bRetVal = Shell_NotifyIcon(NIM_MODIFY, &nid); diff --git a/reactos/subsys/system/vmwinst/Dk.rc b/reactos/subsys/system/vmwinst/Dk.rc new file mode 100644 index 00000000000..a96528c7f34 --- /dev/null +++ b/reactos/subsys/system/vmwinst/Dk.rc @@ -0,0 +1,130 @@ +LANGUAGE LANG_DANISH, SUBLANG_DEFAULT + +IDD_WELCOMEPAGE DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Driver til Grafikkort Installation" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "ReactOS Installation har opdaget at du køre ReactOS under VMware(r).", -1, 100, 8, 160, 24 + LTEXT "Hvis du ønsker at installere VMware(r) SVGA driveren Klik 'Næste', Ellers klik på 'Fortryd' for at afbryde.", -1, 100, 140, 160, 17 +END + +IDD_INSERT_VMWARE_TOOLS DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "VMware Installation - Driver til Grafikkort" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "ReactOS Installationen indeholder ingen VMware(r) SVGA driver.\n\nDerfor gå til menuen 'VM' i VMware(r) Arbejdsstation og vælg 'Install VMware tools...'\n\n\nKlik 'Næste' for at forsætte eller klik 'Fortryd' for at undlade at installere SVGA driveren.",-1,25,25,225,85 +END + +IDD_INSTALLING_VMWARE_TOOLS DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "VMware Installation - Driver til Grafikkort" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "{STATUS}",IDC_INSTALLINGSTATUS,25,68,225,8 + CONTROL "", IDC_INSTALLINGPROGRESS, "msctls_progress32", PBS_MARQUEE | WS_CHILD | WS_VISIBLE | WS_BORDER, 25, 80, 225, 8 +END + +IDD_CONFIG DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "VMware Installation - Driver til Grafikkort" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "WMware(r) SVGA driver er blevet installeret med succes. Vælg din favorit Skærm Opløsning:",-1,25,5,225,20 + PUSHBUTTON "640x480", 540, 25, 30, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "800x600", 1400, 25, 45, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "1024x768", 1792, 25, 60, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "1152x864", 2016, 25, 75, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "1280x960", 2240, 110, 30, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "1280x1024", 2304, 110, 45, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "1400x1050", 2450, 110, 60, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "1600x1200", 2800, 110, 75, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "1792x1344", 3136, 195, 30, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "1856x1392", 3248, 195, 45, 65, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "1920x1440", 3360, 195, 60, 50, 15, BS_AUTORADIOBUTTON + LTEXT "Vælg din favorit farve kvalitet:", -1, 25, 93, 225, 12 + COMBOBOX IDC_COLORQUALITY, 25, 105, 75, 80, CBS_DROPDOWNLIST | WS_TABSTOP +END + +IDD_INSTALLATION_FAILED DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "VMware Installation - Driver til Grafikkort" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "ReactOS Installationen var ikke i stand til at installere WMware(r) SVGA driver.\n\n\nKlik på 'Færdig' for at forsætte installations processen.",-1,25,25,225,85 +END + +IDD_CHOOSEACTION DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "VMware Installation - Driver til Grafikkort" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "WMware(r) SVGA driver er allerede installeret.\n\nVælg venligst hvad du ønsker at gøre:",-1,25,5,225,30 + PUSHBUTTON "Konfigurer Skærm indstillinger", IDC_CONFIGSETTINGS, 25, 40, 200, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "Benyt en anden Grafikkort Driver", IDC_USEOTHERDRIVER, 25, 55, 200, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "Afinstallere WMware(r) SVGA driver", IDC_UNINSTALL, 25, 70, 200, 15, BS_AUTORADIOBUTTON + LTEXT "Klik på 'Næste' for at forsætte eller klik på 'Fortryd' for at afslutte guiden.", -1, 25, 93, 225, 12 +END + +IDD_SELECTDRIVER DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "VMware Installation - Driver til Grafikkort" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Vælg venligst den driver til grafikkortet du ønsker at bruge:",-1,25,25,225,15 + PUSHBUTTON "ReactOS VGA Driver", IDC_VGA, 25, 40, 225, 15, BS_AUTORADIOBUTTON + PUSHBUTTON "ReactOS VBE Driver (SVGA)", IDC_VBE, 25, 55, 225, 15, BS_AUTORADIOBUTTON + LTEXT "Klik på 'Færdig' for at afslutte operationen eller klik på 'fortryd' for at afslutte Installationen uden at gemme ændringerne.", -1, 25, 93, 225, 20 +END + +IDD_DOUNINSTALL DIALOGEX 0, 0, PROPSHEETWIDTH, PROPSHEETHEIGHT +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "VMware Installation - Driver til Grafikkort" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "ReactOS installation skal til at afinstallere din VMware(r) SVGA driver!!!\n\n\nKlik på 'Færdig' for at afinstallere driveren eller klik på 'Fortryd' for at afslutte uden at gemme.",-1,25,25,225,85 +END + +STRINGTABLE +BEGIN + IDS_WIZARD_NAME "VMware(r) detected" + IDS_FAILEDTOLOCATEDRIVERS "The ReactOS Setup couldn't find the required files.\nPlease make sure the VMware(r) tools CD-ROM is inserted." + IDS_FAILEDTOCOPYFILES "The ReactOS Setup failed to copy the required files.\nPlease make sure the VMware(r) tools CD-ROM is inserted.\n" + IDS_FAILEDTOACTIVATEDRIVER "Failed to activate the VMware(r) SVGA driver!" + IDS_FAILEDTOSELVGADRIVER "Failed to activate the ReactOS VGA driver!" + IDS_FAILEDTOSELVBEDRIVER "Failed to activate the ReactOS VBE driver!" + IDS_UNINSTNOTICE "The VMware(r) SVGA driver files have not yet been deleted.\nPlease delete them manually after a system reboot." +END + +STRINGTABLE +BEGIN + 10001 "Laveste (8BPP)" + 10002 "Mellem (16BPP)" + 10003 "Højeste (32BPP)" +END + +STRINGTABLE +BEGIN + IDS_SEARCHINGFORCDROM "Søger efter VMware(r) Værktøjs Cdrom..." + IDS_COPYINGFILES "Overfører filer..." + IDS_ENABLINGDRIVER "Aktivere VMware(r) SVGA Driver..." +END + +STRINGTABLE +BEGIN + IDD_INSERT_VMWARE_TOOLSTITLE "VMware(r) SVGA driver installations Guide" + IDD_INSERT_VMWARE_TOOLSSUBTITLE "Vær venlig at indsættee VMware Værktøjs Cdrom´en." + IDD_INSTALLING_VMWARE_TOOLSTITLE "VMware(r) SVGA driver installations Guide" + IDD_INSTALLING_VMWARE_TOOLSSUBTITLE "Søger efter og overfører VMware(r) SVGA driver." + IDD_CONFIGTITLE "VMware(r) SVGA driver installations Guide" + IDD_CONFIGSUBTITLE "Vælg din favorit Skærmopløsning." + IDD_INSTALLATION_FAILEDTITLE "VMware(r) SVGA driver installations Guide" + IDD_INSTALLATION_FAILEDSUBTITLE "WMware(r) SVGA driver blev ikke installeret." + IDD_CHOOSEACTIONTITLE "VMware(r) SVGA driver installations Guide" + IDD_CHOOSEACTIONSUBTITLE "Vælg hvad du Ønsker at gøre." + IDD_SELECTDRIVERTITLE "VMware (r) SVGA driver installations Guide" + IDD_SELECTDRIVERSUBTITLE "Vælge en driver." + IDD_DOUNINSTALLTITLE "VMware (r) SVGA driver installations Guide" + IDD_DOUNINSTALLSUBTITLE "Afinstallere driveren." +END diff --git a/reactos/subsys/system/vmwinst/vmwinst.c b/reactos/subsys/system/vmwinst/vmwinst.c index 23309f6e2b7..3fd9e3057af 100644 --- a/reactos/subsys/system/vmwinst/vmwinst.c +++ b/reactos/subsys/system/vmwinst/vmwinst.c @@ -1091,9 +1091,6 @@ CreateWizard(VOID) psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER); /* Display the wizard */ - PropertySheet(&psh); - - return (LONG)(PropertySheet(&psh) != -1); } diff --git a/reactos/subsys/system/vmwinst/vmwinst.rc b/reactos/subsys/system/vmwinst/vmwinst.rc index 4cdd70c5b44..327d3cd090c 100644 --- a/reactos/subsys/system/vmwinst/vmwinst.rc +++ b/reactos/subsys/system/vmwinst/vmwinst.rc @@ -22,8 +22,8 @@ IDB_HEADER BITMAP "resources/header.bmp" * files. Note that you can and may override resources which also have * a neutral version. This is to get localized bitmaps for example. */ - #include "En.rc" #include "De.rc" +#include "Dk.rc" #include "Es.rc" diff --git a/reactos/subsys/system/welcome/Dk.rc b/reactos/subsys/system/welcome/Dk.rc new file mode 100644 index 00000000000..5325dd1b2ce --- /dev/null +++ b/reactos/subsys/system/welcome/Dk.rc @@ -0,0 +1,62 @@ +/* $Id$ */ + +LANGUAGE LANG_DANISH, SUBLANG_DEFAULT + +/* String Tables */ + +/* Default settings */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_APPTITLE "ReactOS - Velkommen" + IDS_DEFAULTTOPICTITLE "ReactOS" + IDS_DEFAULTTOPICDESC "Velkommen til ReactOs operativ System.\n\nKlik på et emne til venstre." +// IDS_CHECKTEXT "Vis denne dialog igen ved opstart" +// IDS_CLOSETEXT "Afslut" +END + +/* Topic buttons */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_TOPICBUTTON0 "Installer ReactOS" + IDS_TOPICBUTTON1 "Gennemse Cdrom" + IDS_TOPICBUTTON2 "Afslut" +// IDS_TOPICBUTTON3 "Empty Topic 3" +// IDS_TOPICBUTTON4 "Empty Topic 4" +// IDS_TOPICBUTTON5 "Empty Topic 5" +// IDS_TOPICBUTTON6 "Empty Topic 6" +// IDS_TOPICBUTTON7 "Empty Topic 7" +// IDS_TOPICBUTTON8 "Empty Topic 8" +// IDS_TOPICBUTTON9 "Empty Topic 9" +END + +/* Topic titles */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_TOPICTITLE0 "Installer ReactOS" + IDS_TOPICTITLE1 "Gennemse Cdrom" + IDS_TOPICTITLE2 "Afslut" +// IDS_TOPICTITLE3 "Empty Topic Title 3" +// IDS_TOPICTITLE4 "Empty Topic Title 4" +// IDS_TOPICTITLE5 "Empty Topic Title 5" +// IDS_TOPICTITLE6 "Empty Topic Title 6" +// IDS_TOPICTITLE7 "Empty Topic Title 7" +// IDS_TOPICTITLE8 "Empty Topic Title 8" +// IDS_TOPICTITLE9 "Empty Topic Title 9" +END + +/* Topic descriptions */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_TOPICDESC0 "Opret en ny ReactOS installation på din computer eller opgradere en eksisterende installation." + IDS_TOPICDESC1 "Gennemse Reactos Installations Cdrom" + IDS_TOPICDESC2 "Klik på Afslut for at lukke." +// IDS_TOPICDESC3 "Topic description 3.\n\nDescribe topic 3 here." +// IDS_TOPICDESC4 "Topic description 4.\n\nDescribe topic 4 here." +// IDS_TOPICDESC5 "Topic description 5.\n\nDescribe topic 5 here." +// IDS_TOPICDESC6 "Topic description 6.\n\nDescribe topic 6 here." +// IDS_TOPICDESC7 "Topic description 7.\n\nDescribe topic 7 here." +// IDS_TOPICDESC8 "Topic description 8.\n\nDescribe topic 8 here." +// IDS_TOPICDESC9 "Topic description 9.\n\nDescribe topic 9 here." +END + +/* EOF */ diff --git a/reactos/subsys/system/welcome/Sv.rc b/reactos/subsys/system/welcome/Sv.rc new file mode 100644 index 00000000000..0eb4a0bb4d8 --- /dev/null +++ b/reactos/subsys/system/welcome/Sv.rc @@ -0,0 +1,80 @@ +/* + * Swedish (SE) resources + * + * Copyright (C) 2005 David Nordenberg + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT + +/* String Tables */ + +/* Default settings */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_APPTITLE "ReactOS - Välkommen" + IDS_DEFAULTTOPICTITLE "ReactOS" + IDS_DEFAULTTOPICDESC "Välkommen till React Operating System.\n\nKlicka på ett avsnitt till vänster." +// IDS_CHECKTEXT "Show this dialog again" +// IDS_CLOSETEXT "Exit" +END + +/* Topic buttons */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_TOPICBUTTON0 "Installera ReactOS" + IDS_TOPICBUTTON1 "Utforska skivan" + IDS_TOPICBUTTON2 "Avsluta" +// IDS_TOPICBUTTON3 "Empty Topic 3" +// IDS_TOPICBUTTON4 "Empty Topic 4" +// IDS_TOPICBUTTON5 "Empty Topic 5" +// IDS_TOPICBUTTON6 "Empty Topic 6" +// IDS_TOPICBUTTON7 "Empty Topic 7" +// IDS_TOPICBUTTON8 "Empty Topic 8" +// IDS_TOPICBUTTON9 "Empty Topic 9" +END + +/* Topic titles */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_TOPICTITLE0 "Installera ReactOS" + IDS_TOPICTITLE1 "Utforska skivan" + IDS_TOPICTITLE2 "Avsluta" +// IDS_TOPICTITLE3 "Empty Topic Title 3" +// IDS_TOPICTITLE4 "Empty Topic Title 4" +// IDS_TOPICTITLE5 "Empty Topic Title 5" +// IDS_TOPICTITLE6 "Empty Topic Title 6" +// IDS_TOPICTITLE7 "Empty Topic Title 7" +// IDS_TOPICTITLE8 "Empty Topic Title 8" +// IDS_TOPICTITLE9 "Empty Topic Title 9" +END + +/* Topic descriptions */ +STRINGTABLE DISCARDABLE +BEGIN + IDS_TOPICDESC0 "Skapa en ny ReactOS installation på den här datorn eller uppgradera en befintlig." + IDS_TOPICDESC1 "Utforska skivan." + IDS_TOPICDESC2 "Avsluta det här programmet." +// IDS_TOPICDESC3 "Topic description 3.\n\nDescribe topic 3 here." +// IDS_TOPICDESC4 "Topic description 4.\n\nDescribe topic 4 here." +// IDS_TOPICDESC5 "Topic description 5.\n\nDescribe topic 5 here." +// IDS_TOPICDESC6 "Topic description 6.\n\nDescribe topic 6 here." +// IDS_TOPICDESC7 "Topic description 7.\n\nDescribe topic 7 here." +// IDS_TOPICDESC8 "Topic description 8.\n\nDescribe topic 8 here." +// IDS_TOPICDESC9 "Topic description 9.\n\nDescribe topic 9 here." +END + +/* EOF */ diff --git a/reactos/subsys/system/welcome/res/welcome.ico b/reactos/subsys/system/welcome/res/welcome.ico index fffc2801755..1be85f581ff 100644 Binary files a/reactos/subsys/system/welcome/res/welcome.ico and b/reactos/subsys/system/welcome/res/welcome.ico differ diff --git a/reactos/subsys/system/welcome/welcome.rc b/reactos/subsys/system/welcome/welcome.rc index c1c9b6a7480..5c14ef2ace9 100755 --- a/reactos/subsys/system/welcome/welcome.rc +++ b/reactos/subsys/system/welcome/welcome.rc @@ -47,8 +47,10 @@ END /* Language-specific resources */ -#include "De.rc" #include "En.rc" +#include "De.rc" +#include "Dk.rc" #include "Es.rc" +#include "Sv.rc" /* EOF */ diff --git a/reactos/subsys/system/winlogon/setup.c b/reactos/subsys/system/winlogon/setup.c index 10aeb48940f..16344a1d01d 100644 --- a/reactos/subsys/system/winlogon/setup.c +++ b/reactos/subsys/system/winlogon/setup.c @@ -84,7 +84,7 @@ SetSetupType (DWORD dwSetupType) dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\Setup", //TEXT("SYSTEM\\Setup"), 0, - KEY_QUERY_VALUE, + KEY_SET_VALUE, &hKey); if (dwError != ERROR_SUCCESS) { diff --git a/reactos/subsys/system/winlogon/winlogon.h b/reactos/subsys/system/winlogon/winlogon.h index 168a4ca34d6..d0647824b55 100644 --- a/reactos/subsys/system/winlogon/winlogon.h +++ b/reactos/subsys/system/winlogon/winlogon.h @@ -27,7 +27,7 @@ #ifndef __WINLOGON_MAIN_H__ #define __WINLOGON_MAIN_H__ -#include +#include VOID WINAPI WlxUseCtrlAltDel(HANDLE hWlx); VOID WINAPI WlxSetContextPointer(HANDLE hWlx, PVOID pWlxContext); diff --git a/reactos/subsys/system/winlogon/wlx.c b/reactos/subsys/system/winlogon/wlx.c index ba3c73b6636..aec4dfc8a17 100644 --- a/reactos/subsys/system/winlogon/wlx.c +++ b/reactos/subsys/system/winlogon/wlx.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/reactos/subsys/win32k/eng/objects.h b/reactos/subsys/win32k/eng/objects.h index bf10f1c4cf5..a8b8d5fe55c 100644 --- a/reactos/subsys/win32k/eng/objects.h +++ b/reactos/subsys/win32k/eng/objects.h @@ -130,6 +130,8 @@ typedef BOOL STDCALL (*PFN_GradientFill)(SURFOBJ*, CLIPOBJ*, XLATEOBJ*, TRIVERTE typedef struct _WNDGDI { WNDOBJ WndObj; + LIST_ENTRY ListEntry; + HWND Hwnd; CLIPOBJ *ClientClipObj; WNDOBJCHANGEPROC ChangeProc; FLONG Flags; diff --git a/reactos/subsys/win32k/eng/window.c b/reactos/subsys/win32k/eng/window.c index 4831da45563..1882b2e44a4 100644 --- a/reactos/subsys/win32k/eng/window.c +++ b/reactos/subsys/win32k/eng/window.c @@ -26,6 +26,9 @@ * REVISION HISTORY: * 16/11/2004: Created */ + +/* TODO: Check how the WNDOBJ implementation should behave with a driver on windows. */ + #include /* @@ -33,9 +36,9 @@ */ VOID FASTCALL -IntEngWndChanged( - IN WNDOBJ *pwo, - IN FLONG flChanged) +IntEngWndCallChangeProc( + IN WNDOBJ *pwo, + IN FLONG flChanged) { WNDGDI *WndObjInt = ObjToGDI(pwo, WND); @@ -62,23 +65,145 @@ IntEngWndChanged( WndObjInt->ChangeProc(pwo, flChanged); } +/* + * Fills the CLIPOBJ and client rect of the WNDOBJ with the data from the given WINDOW_OBJECT + */ +BOOLEAN +FASTCALL +IntEngWndUpdateClipObj( + WNDGDI *WndObjInt, + PWINDOW_OBJECT Window) +{ + HRGN hVisRgn; + PROSRGNDATA visRgn; + CLIPOBJ *ClipObj = NULL; + CLIPOBJ *OldClipObj; + + hVisRgn = VIS_ComputeVisibleRegion(Window, TRUE, TRUE, TRUE); + if (hVisRgn != NULL) + { + NtGdiOffsetRgn(hVisRgn, Window->ClientRect.left, Window->ClientRect.top); + visRgn = RGNDATA_LockRgn(hVisRgn); + if (visRgn != NULL) + { + if (visRgn->rdh.nCount > 0) + { + ClipObj = IntEngCreateClipRegion(visRgn->rdh.nCount, (PRECTL)visRgn->Buffer, + (PRECTL)&visRgn->rdh.rcBound); + DPRINT("Created visible region with %d rects\n", visRgn->rdh.nCount); + DPRINT(" BoundingRect: %d, %d %d, %d\n", + visRgn->rdh.rcBound.left, visRgn->rdh.rcBound.top, + visRgn->rdh.rcBound.right, visRgn->rdh.rcBound.bottom); + { + INT i; + for (i = 0; i < visRgn->rdh.nCount; i++) + { + DPRINT(" Rect #%d: %d,%d %d,%d\n", i+1, + visRgn->Buffer[i].left, visRgn->Buffer[i].top, + visRgn->Buffer[i].right, visRgn->Buffer[i].bottom); + } + } + } + RGNDATA_UnlockRgn(hVisRgn); + } + else + { + DPRINT1("Warning: Couldn't lock visible region of window DC\n"); + } + } + else + { + DPRINT1("Warning: VIS_ComputeVisibleRegion failed!\n"); + } + + if (ClipObj == NULL) + { + /* Fall back to client rect */ + ClipObj = IntEngCreateClipRegion(1, (PRECTL)&Window->ClientRect, + (PRECTL)&Window->ClientRect); + } + + if (ClipObj == NULL) + { + DPRINT1("Warning: IntEngCreateClipRegion() failed!\n"); + return FALSE; + } + + RtlCopyMemory(&WndObjInt->WndObj.coClient, ClipObj, sizeof (CLIPOBJ)); + RtlCopyMemory(&WndObjInt->WndObj.rclClient, &Window->ClientRect, sizeof (RECT)); + OldClipObj = InterlockedExchangePointer(&WndObjInt->ClientClipObj, ClipObj); + if (OldClipObj != NULL) + IntEngDeleteClipRegion(OldClipObj); + + return TRUE; +} + +/* + * Updates all WNDOBJs of the given WINDOW_OBJECT and calls the change-procs. + */ +VOID +FASTCALL +IntEngWindowChanged( + PWINDOW_OBJECT Window, + FLONG flChanged) +{ + PLIST_ENTRY CurrentEntry; + WNDGDI *Current; + + ASSERT_IRQL(PASSIVE_LEVEL); + + ExAcquireFastMutex(&Window->WndObjListLock); + CurrentEntry = Window->WndObjListHead.Flink; + while (CurrentEntry != &Window->WndObjListHead) + { + Current = CONTAINING_RECORD(CurrentEntry, WNDGDI, ListEntry); + + if (Current->WndObj.pvConsumer != NULL) + { + /* Update the WNDOBJ */ + switch (flChanged) + { + case WOC_RGN_CLIENT: + /* Update the clipobj and client rect of the WNDOBJ */ + IntEngWndUpdateClipObj(Current, Window); + break; + + case WOC_DELETE: + /* FIXME: Should the WNDOBJs be deleted by win32k or by the driver? */ + break; + } + + /* Call the change proc */ + IntEngWndCallChangeProc(&Current->WndObj, flChanged); + + /* HACK: Send WOC_CHANGED after WOC_RGN_CLIENT */ + if (flChanged == WOC_RGN_CLIENT) + { + IntEngWndCallChangeProc(&Current->WndObj, WOC_CHANGED); + } + + CurrentEntry = CurrentEntry->Flink; + } + } + + ExReleaseFastMutex(&Window->WndObjListLock); +} + /* * @implemented */ WNDOBJ* STDCALL EngCreateWnd( - SURFOBJ *pso, - HWND hwnd, - WNDOBJCHANGEPROC pfn, - FLONG fl, - int iPixelFormat - ) + SURFOBJ *pso, + HWND hwnd, + WNDOBJCHANGEPROC pfn, + FLONG fl, + int iPixelFormat) { WNDGDI *WndObjInt = NULL; WNDOBJ *WndObjUser = NULL; PWINDOW_OBJECT Window; - CLIPOBJ *ClientClipObj; DPRINT("EngCreateWnd: pso = 0x%x, hwnd = 0x%x, pfn = 0x%x, fl = 0x%x, pixfmt = %d\n", pso, hwnd, pfn, fl, iPixelFormat); @@ -99,27 +224,30 @@ EngCreateWnd( return NULL; } - ClientClipObj = IntEngCreateClipRegion(1, (PRECTL)&Window->ClientRect, - (PRECTL)&Window->ClientRect); - if (ClientClipObj == NULL) + /* Fill the clipobj */ + WndObjInt->ClientClipObj = NULL; + if (!IntEngWndUpdateClipObj(WndObjInt, Window)) { IntReleaseWindowObject(Window); EngFreeMem(WndObjInt); return NULL; } - /* fill user object */ + /* Fill user object */ WndObjUser = GDIToObj(WndObjInt, WND); WndObjUser->psoOwner = pso; WndObjUser->pvConsumer = NULL; - RtlCopyMemory(&WndObjUser->rclClient, &Window->ClientRect, sizeof (RECT)); - RtlCopyMemory(&WndObjUser->coClient, ClientClipObj, sizeof (CLIPOBJ)); - /* fill internal object */ + /* Fill internal object */ + WndObjInt->Hwnd = hwnd; WndObjInt->ChangeProc = pfn; WndObjInt->Flags = fl; WndObjInt->PixelFormat = iPixelFormat; - WndObjInt->ClientClipObj = ClientClipObj; + + /* associate object with window */ + ExAcquireFastMutex(&Window->WndObjListLock); + InsertTailList(&Window->WndObjListHead, &WndObjInt->ListEntry); + ExReleaseFastMutex(&Window->WndObjListLock); /* release resources */ IntReleaseWindowObject(Window); @@ -135,12 +263,31 @@ EngCreateWnd( */ VOID STDCALL -EngDeleteWnd ( IN WNDOBJ *pwo ) +EngDeleteWnd( + IN WNDOBJ *pwo) { WNDGDI *WndObjInt = ObjToGDI(pwo, WND); - + PWINDOW_OBJECT Window; + DPRINT("EngDeleteWnd: pwo = 0x%x\n", pwo); + /* Get window object */ + Window = IntGetWindowObject(WndObjInt->Hwnd); + if (Window == NULL) + { + DPRINT1("Warning: Couldnt get window object for WndObjInt->Hwnd!!!\n"); + RemoveEntryList(&WndObjInt->ListEntry); + } + else + { + /* Remove object from window */ + ExAcquireFastMutex(&Window->WndObjListLock); + RemoveEntryList(&WndObjInt->ListEntry); + ExReleaseFastMutex(&Window->WndObjListLock); + IntReleaseWindowObject(Window); + } + + /* Free resources */ IntEngDeleteClipRegion(WndObjInt->ClientClipObj); EngFreeMem(WndObjInt); } @@ -152,10 +299,9 @@ EngDeleteWnd ( IN WNDOBJ *pwo ) BOOL STDCALL WNDOBJ_bEnum( - IN WNDOBJ *pwo, - IN ULONG cj, - OUT ULONG *pul - ) + IN WNDOBJ *pwo, + IN ULONG cj, + OUT ULONG *pul) { WNDGDI *WndObjInt = ObjToGDI(pwo, WND); BOOL Ret; @@ -174,11 +320,10 @@ WNDOBJ_bEnum( ULONG STDCALL WNDOBJ_cEnumStart( - IN WNDOBJ *pwo, - IN ULONG iType, - IN ULONG iDirection, - IN ULONG cLimit - ) + IN WNDOBJ *pwo, + IN ULONG iType, + IN ULONG iDirection, + IN ULONG cLimit) { WNDGDI *WndObjInt = ObjToGDI(pwo, WND); ULONG Ret; @@ -200,9 +345,8 @@ WNDOBJ_cEnumStart( VOID STDCALL WNDOBJ_vSetConsumer( - IN WNDOBJ *pwo, - IN PVOID pvConsumer - ) + IN WNDOBJ *pwo, + IN PVOID pvConsumer) { BOOL Hack; @@ -211,12 +355,19 @@ WNDOBJ_vSetConsumer( Hack = (pwo->pvConsumer == NULL); pwo->pvConsumer = pvConsumer; - /* HACKHACKHACK */ + /* HACKHACKHACK + * + * MSDN says that the WNDOBJCHANGEPROC will be called with the most recent state + * when a WNDOBJ is created - we do it here because most drivers will need pvConsumer + * in the callback to identify the WNDOBJ I think. + * + * - blight + */ if (Hack) { - IntEngWndChanged(pwo, WOC_RGN_CLIENT); - IntEngWndChanged(pwo, WOC_CHANGED); - IntEngWndChanged(pwo, WOC_DRAWN); + IntEngWndCallChangeProc(pwo, WOC_RGN_CLIENT); + IntEngWndCallChangeProc(pwo, WOC_CHANGED); + IntEngWndCallChangeProc(pwo, WOC_DRAWN); } } diff --git a/reactos/subsys/win32k/include/desktop.h b/reactos/subsys/win32k/include/desktop.h index 58949bf6d7e..42069fedb46 100644 --- a/reactos/subsys/win32k/include/desktop.h +++ b/reactos/subsys/win32k/include/desktop.h @@ -14,12 +14,28 @@ extern PWNDCLASS_OBJECT DesktopWindowClass; extern HDC ScreenDeviceContext; extern BOOL g_PaintDesktopVersion; +typedef struct _SHELL_HOOK_WINDOW +{ + LIST_ENTRY ListEntry; + HWND hWnd; +} SHELL_HOOK_WINDOW, *PSHELL_HOOK_WINDOW; + NTSTATUS FASTCALL InitDesktopImpl(VOID); NTSTATUS FASTCALL CleanupDesktopImpl(VOID); +NTSTATUS +STDCALL +IntDesktopObjectCreate(PVOID ObjectBody, + PVOID Parent, + PWSTR RemainingPath, + struct _OBJECT_ATTRIBUTES* ObjectAttributes); + +VOID STDCALL +IntDesktopObjectDelete(PVOID DeletedObject); + VOID FASTCALL IntGetDesktopWorkArea(PDESKTOP_OBJECT Desktop, PRECT Rect); @@ -69,6 +85,11 @@ IntParseDesktopPath(PEPROCESS Process, BOOL FASTCALL IntDesktopUpdatePerUserSettings(BOOL bEnable); +BOOL IntRegisterShellHookWindow(HWND hWnd); +BOOL IntDeRegisterShellHookWindow(HWND hWnd); + +VOID IntShellHookNotify(WPARAM Message, LPARAM lParam); + #define IntIsActiveDesktop(Desktop) \ ((Desktop)->WindowStation->ActiveDesktop == (Desktop)) diff --git a/reactos/subsys/win32k/include/dib.h b/reactos/subsys/win32k/include/dib.h index 80a2fc4d7a3..49a13af6256 100644 --- a/reactos/subsys/win32k/include/dib.h +++ b/reactos/subsys/win32k/include/dib.h @@ -17,6 +17,6 @@ RGBQUAD * FASTCALL DIB_MapPaletteColors(PDC dc, CONST BITMAPINFO* lpbmi); HPALETTE FASTCALL -BuildDIBPalette (PBITMAPINFO bmi, PINT paletteType); +BuildDIBPalette (CONST BITMAPINFO *bmi, PINT paletteType); #endif /* _WIN32K_DIB_H */ diff --git a/reactos/subsys/win32k/include/eng.h b/reactos/subsys/win32k/include/eng.h index cafed3f9bff..c0bde5fc228 100644 --- a/reactos/subsys/win32k/include/eng.h +++ b/reactos/subsys/win32k/include/eng.h @@ -15,4 +15,9 @@ IntEngMaskBlt(SURFOBJ *DestObj, BRUSHOBJ *Brush, POINTL *BrushOrigin); +VOID FASTCALL +IntEngWindowChanged( + PWINDOW_OBJECT Window, + FLONG flChanged); + #endif /* _WIN32K_ENG_H */ diff --git a/reactos/subsys/win32k/include/input.h b/reactos/subsys/win32k/include/input.h index 052dff854d0..00ec42f79ca 100644 --- a/reactos/subsys/win32k/include/input.h +++ b/reactos/subsys/win32k/include/input.h @@ -8,6 +8,7 @@ InitInputImpl(VOID); NTSTATUS FASTCALL InitKeyboardImpl(VOID); PUSER_MESSAGE_QUEUE W32kGetPrimitiveMessageQueue(VOID); +VOID W32kUnregisterPrimitiveMessageQueue(VOID); PKBDTABLES W32kGetDefaultKeyLayout(VOID); VOID FASTCALL W32kKeyProcessMessage(LPMSG Msg, PKBDTABLES KeyLayout); BOOL FASTCALL IntBlockInput(PW32THREAD W32Thread, BOOL BlockIt); diff --git a/reactos/subsys/win32k/include/intgdi.h b/reactos/subsys/win32k/include/intgdi.h index 32f8ab11567..9d609313b69 100644 --- a/reactos/subsys/win32k/include/intgdi.h +++ b/reactos/subsys/win32k/include/intgdi.h @@ -9,16 +9,29 @@ IntGdiCreateBrushXlate(PDC Dc, GDIBRUSHOBJ *BrushObj, BOOLEAN *Failed); VOID FASTCALL IntGdiInitBrushInstance(GDIBRUSHINST *BrushInst, PGDIBRUSHOBJ BrushObj, XLATEOBJ *XlateObj); -HBRUSH FASTCALL -IntGdiCreateBrushIndirect(PLOGBRUSH lb); -HBRUSH FASTCALL -IntGdiCreateDIBPatternBrush(HGLOBAL hDIBPacked, - UINT ColorSpec); +HBRUSH STDCALL +IntGdiCreateDIBBrush( + CONST BITMAPINFO *BitmapInfo, + UINT ColorSpec, + UINT BitmapInfoSize, + CONST VOID *PackedDIB); -HBRUSH FASTCALL -IntGdiCreateDIBPatternBrushPt(CONST VOID *PackedDIB, - UINT Usage); +HBRUSH STDCALL +IntGdiCreateHatchBrush( + INT Style, + COLORREF Color); + +HBRUSH STDCALL +IntGdiCreatePatternBrush( + HBITMAP hBitmap); + +HBRUSH STDCALL +IntGdiCreateSolidBrush( + COLORREF Color); + +HBRUSH STDCALL +IntGdiCreateNullBrush(VOID); BOOL FASTCALL IntPatBlt( @@ -130,7 +143,8 @@ HDC FASTCALL IntGdiCreateDC(PUNICODE_STRING Driver, PUNICODE_STRING Device, PUNICODE_STRING Output, - CONST PDEVMODEW InitData); + CONST PDEVMODEW InitData, + BOOL CreateAsIC); COLORREF FASTCALL IntGetDCColor(HDC hDC, ULONG Object); @@ -192,5 +206,21 @@ IntGdiEscape(PDC dc, LPCSTR InData, LPVOID OutData); +BOOL +FASTCALL +IntEnumDisplaySettings( + IN PUNICODE_STRING pDeviceName OPTIONAL, + IN DWORD iModeNum, + IN OUT LPDEVMODEW pDevMode, + IN DWORD dwFlags); + +LONG +FASTCALL +IntChangeDisplaySettings( + IN PUNICODE_STRING pDeviceName OPTIONAL, + IN LPDEVMODEW pDevMode, + IN DWORD dwflags, + IN PVOID lParam OPTIONAL); + #endif /* _WIN32K_INTGDI_H */ diff --git a/reactos/subsys/win32k/include/window.h b/reactos/subsys/win32k/include/window.h index 46055dfffce..e6529dade61 100644 --- a/reactos/subsys/win32k/include/window.h +++ b/reactos/subsys/win32k/include/window.h @@ -99,6 +99,9 @@ typedef struct _WINDOW_OBJECT ULONG Status; /* counter for tiled child windows */ ULONG TiledCounter; + /* WNDOBJ list */ + LIST_ENTRY WndObjListHead; + FAST_MUTEX WndObjListLock; } WINDOW_OBJECT; /* PWINDOW_OBJECT already declared at top of file */ /* Window flags. */ @@ -199,6 +202,9 @@ IntGetAncestor(PWINDOW_OBJECT Wnd, UINT Type); PWINDOW_OBJECT FASTCALL IntGetParent(PWINDOW_OBJECT Wnd); +PWINDOW_OBJECT FASTCALL +IntGetOwner(PWINDOW_OBJECT Wnd); + PWINDOW_OBJECT FASTCALL IntGetParentObject(PWINDOW_OBJECT Wnd); diff --git a/reactos/subsys/win32k/include/winsta.h b/reactos/subsys/win32k/include/winsta.h index 5b57c075c62..73daafb4070 100644 --- a/reactos/subsys/win32k/include/winsta.h +++ b/reactos/subsys/win32k/include/winsta.h @@ -23,6 +23,29 @@ InitWindowStationImpl(VOID); NTSTATUS FASTCALL CleanupWindowStationImpl(VOID); +NTSTATUS +STDCALL +IntWinStaObjectCreate(PVOID ObjectBody, + PVOID Parent, + PWSTR RemainingPath, + struct _OBJECT_ATTRIBUTES* ObjectAttributes); + +VOID STDCALL +IntWinStaObjectDelete(PVOID DeletedObject); + +PVOID STDCALL +IntWinStaObjectFind(PVOID Object, + PWSTR Name, + ULONG Attributes); + +NTSTATUS +STDCALL +IntWinStaObjectParse(PVOID Object, + PVOID *NextObject, + PUNICODE_STRING FullPath, + PWSTR *Path, + ULONG Attributes); + NTSTATUS FASTCALL IntValidateWindowStationHandle( HWINSTA WindowStation, diff --git a/reactos/subsys/win32k/main/dllmain.c b/reactos/subsys/win32k/main/dllmain.c index 3742656eee7..6270b3e6c64 100644 --- a/reactos/subsys/win32k/main/dllmain.c +++ b/reactos/subsys/win32k/main/dllmain.c @@ -35,11 +35,45 @@ typedef NTSTATUS (STDCALL *PW32_THREAD_CALLBACK)( struct _ETHREAD *Thread, BOOLEAN Create); +/* + * Callbacks used for Win32 objects... this define won't be needed after the Object Manager + * rewrite -- Alex + */ +typedef NTSTATUS STDCALL_FUNC +(*OBJECT_CREATE_ROUTINE)(PVOID ObjectBody, + PVOID Parent, + PWSTR RemainingPath, + struct _OBJECT_ATTRIBUTES* ObjectAttributes); + +typedef NTSTATUS STDCALL_FUNC +(*OBJECT_PARSE_ROUTINE)(PVOID Object, + PVOID *NextObject, + PUNICODE_STRING FullPath, + PWSTR *Path, + ULONG Attributes); + +typedef VOID STDCALL_FUNC +(*OBJECT_DELETE_ROUTINE)(PVOID DeletedObject); + +typedef PVOID STDCALL_FUNC +(*OBJECT_FIND_ROUTINE)(PVOID WinStaObject, + PWSTR Name, + ULONG Attributes); + +typedef struct _W32_OBJECT_CALLBACK { + OBJECT_CREATE_ROUTINE WinStaCreate; + OBJECT_PARSE_ROUTINE WinStaParse; + OBJECT_DELETE_ROUTINE WinStaDelete; + OBJECT_FIND_ROUTINE WinStaFind; + OBJECT_CREATE_ROUTINE DesktopCreate; + OBJECT_DELETE_ROUTINE DesktopDelete; +} W32_OBJECT_CALLBACK, *PW32_OBJECT_CALLBACK; + VOID STDCALL PsEstablishWin32Callouts( PW32_PROCESS_CALLBACK W32ProcessCallback, PW32_THREAD_CALLBACK W32ThreadCallback, - PVOID Param3, + PW32_OBJECT_CALLBACK W32ObjectCallback, PVOID Param4, ULONG W32ThreadSize, ULONG W32ProcessSize); @@ -215,6 +249,7 @@ DriverEntry ( { NTSTATUS Status; BOOLEAN Result; + W32_OBJECT_CALLBACK Win32kObjectCallbacks; /* * Register user mode call interface @@ -230,13 +265,22 @@ DriverEntry ( DPRINT1("Adding system services failed!\n"); return STATUS_UNSUCCESSFUL; } - + + /* + * Register Object Manager Callbacks + */ + Win32kObjectCallbacks.WinStaCreate = IntWinStaObjectCreate; + Win32kObjectCallbacks.WinStaParse = IntWinStaObjectParse; + Win32kObjectCallbacks.WinStaDelete = IntWinStaObjectDelete; + Win32kObjectCallbacks.WinStaFind = IntWinStaObjectFind; + Win32kObjectCallbacks.DesktopCreate = IntDesktopObjectCreate; + Win32kObjectCallbacks.DesktopDelete = IntDesktopObjectDelete; /* * Register our per-process and per-thread structures. */ PsEstablishWin32Callouts (Win32kProcessCallback, Win32kThreadCallback, - 0, + &Win32kObjectCallbacks, 0, sizeof(W32THREAD), sizeof(W32PROCESS)); diff --git a/reactos/subsys/win32k/misc/object.c b/reactos/subsys/win32k/misc/object.c index 7b382bbbfea..d93175d4b51 100644 --- a/reactos/subsys/win32k/misc/object.c +++ b/reactos/subsys/win32k/misc/object.c @@ -34,10 +34,10 @@ #define NDEBUG #include -#define HEADER_TO_BODY(ObjectHeader) \ +#define USER_HEADER_TO_BODY(ObjectHeader) \ ((PVOID)(((PUSER_OBJECT_HEADER)ObjectHeader) + 1)) -#define BODY_TO_HEADER(ObjectBody) \ +#define USER_BODY_TO_HEADER(ObjectBody) \ ((PUSER_OBJECT_HEADER)(((PUSER_OBJECT_HEADER)ObjectBody) - 1)) /* FUNCTIONS *****************************************************************/ @@ -125,7 +125,7 @@ ObmpCloseAllHandles(PUSER_HANDLE_TABLE HandleTable) if (ObjectBody != NULL) { - PUSER_OBJECT_HEADER ObjectHeader = BODY_TO_HEADER(ObjectBody); + PUSER_OBJECT_HEADER ObjectHeader = USER_BODY_TO_HEADER(ObjectBody); ObmReferenceObjectByPointer(ObjectBody, otUnknown); ObjectHeader->HandleCount--; @@ -191,7 +191,7 @@ ObmpDeleteHandle(PUSER_HANDLE_TABLE HandleTable, if (ObjectBody != NULL) { - ObjectHeader = BODY_TO_HEADER(ObjectBody); + ObjectHeader = USER_BODY_TO_HEADER(ObjectBody); ObjectHeader->HandleCount--; ObmReferenceObjectByPointer(ObjectBody, otUnknown); Entry->ObjectBody = NULL; @@ -219,7 +219,7 @@ ObmpInitializeObject(PUSER_HANDLE_TABLE HandleTable, if (Handle != NULL) { Status = ObmCreateHandle(HandleTable, - HEADER_TO_BODY(ObjectHeader), + USER_HEADER_TO_BODY(ObjectHeader), Handle); } @@ -230,7 +230,7 @@ ObmpInitializeObject(PUSER_HANDLE_TABLE HandleTable, ULONG FASTCALL ObmGetReferenceCount(PVOID ObjectBody) { - PUSER_OBJECT_HEADER ObjectHeader = BODY_TO_HEADER(ObjectBody); + PUSER_OBJECT_HEADER ObjectHeader = USER_BODY_TO_HEADER(ObjectBody); return ObjectHeader->RefCount; } @@ -238,7 +238,7 @@ ObmGetReferenceCount(PVOID ObjectBody) ULONG FASTCALL ObmGetHandleCount(PVOID ObjectBody) { - PUSER_OBJECT_HEADER ObjectHeader = BODY_TO_HEADER(ObjectBody); + PUSER_OBJECT_HEADER ObjectHeader = USER_BODY_TO_HEADER(ObjectBody); return ObjectHeader->HandleCount; } @@ -260,7 +260,7 @@ ObmReferenceObject(PVOID ObjectBody) return; } - ObjectHeader = BODY_TO_HEADER(ObjectBody); + ObjectHeader = USER_BODY_TO_HEADER(ObjectBody); ObjectHeader->RefCount++; @@ -284,7 +284,7 @@ ObmDereferenceObject(PVOID ObjectBody) return; } - ObjectHeader = BODY_TO_HEADER(ObjectBody); + ObjectHeader = USER_BODY_TO_HEADER(ObjectBody); ObjectHeader->RefCount--; ObmpPerformRetentionChecks(ObjectHeader); @@ -303,7 +303,7 @@ ObmReferenceObjectByPointer(PVOID ObjectBody, { PUSER_OBJECT_HEADER ObjectHeader; - ObjectHeader = BODY_TO_HEADER(ObjectBody); + ObjectHeader = USER_BODY_TO_HEADER(ObjectBody); if ((ObjectType != otUnknown) && (ObjectHeader->Type != ObjectType)) { @@ -331,7 +331,7 @@ ObmCreateObject(PUSER_HANDLE_TABLE HandleTable, return NULL; } - ObjectBody = HEADER_TO_BODY(ObjectHeader); + ObjectBody = USER_HEADER_TO_BODY(ObjectHeader); RtlZeroMemory(ObjectBody, ObjectSize); @@ -369,7 +369,7 @@ ObmCreateHandle(PUSER_HANDLE_TABLE HandleTable, if (ObjectBody != NULL) { - BODY_TO_HEADER(ObjectBody)->HandleCount++; + USER_BODY_TO_HEADER(ObjectBody)->HandleCount++; } ObmpLockHandleTable(HandleTable); @@ -456,11 +456,12 @@ ObmReferenceObjectByHandle(PUSER_HANDLE_TABLE HandleTable, ObmpUnlockHandleTable(HandleTable); - ObjectHeader = BODY_TO_HEADER(ObjectBody); + ObjectHeader = USER_BODY_TO_HEADER(ObjectBody); if ((ObjectType != otUnknown) && (ObjectHeader->Type != ObjectType)) { DPRINT1("Object type mismatch 0x%x 0x%x\n", ObjectType, ObjectHeader->Type); + ObmDereferenceObject(ObjectBody); return STATUS_UNSUCCESSFUL; } diff --git a/reactos/subsys/win32k/ntddraw/ddraw.c b/reactos/subsys/win32k/ntddraw/ddraw.c index 943dfb1618b..9e088691c13 100644 --- a/reactos/subsys/win32k/ntddraw/ddraw.c +++ b/reactos/subsys/win32k/ntddraw/ddraw.c @@ -274,4 +274,50 @@ BOOL STDCALL NtGdiDdAttachSurface( } */ + + +DWORD STDCALL NtGdiDdGetDriverInfo( + HANDLE hDirectDrawLocal, + PDD_GETDRIVERINFODATA puGetDriverInfoData) + +{ + DWORD pdwNumHeaps; + VIDEOMEMORY *pvmList = NULL; + DWORD pdwNumFourCC; + DWORD *pdwFourCC = NULL; + DWORD ddRVal; + + + PDD_DIRECTDRAW pDirectDraw = GDIOBJ_LockObj(hDirectDrawLocal, GDI_OBJECT_TYPE_DIRECTDRAW); + + ddRVal = pDirectDraw->DrvGetDirectDrawInfo( + pDirectDraw->Global.dhpdev,(PDD_HALINFO) puGetDriverInfoData, + &pdwNumHeaps, pvmList, &pdwNumFourCC, pdwFourCC); + + GDIOBJ_UnlockObj(hDirectDrawLocal); + + return ddRVal; +} + + + +DWORD STDCALL NtGdiDdWaitForVerticalBlank( + HANDLE hDirectDrawLocal, + PDD_WAITFORVERTICALBLANKDATA puWaitForVerticalBlankData +) +{ + DWORD ddRVal; + PDD_DIRECTDRAW pDirectDraw = GDIOBJ_LockObj(hDirectDrawLocal, GDI_OBJECT_TYPE_DIRECTDRAW); + + puWaitForVerticalBlankData->lpDD = pDirectDraw->Local.lpGbl; + + ddRVal = pDirectDraw->DdWaitForVerticalBlank(puWaitForVerticalBlankData); + + GDIOBJ_UnlockObj(hDirectDrawLocal); + + return ddRVal; +} + + + /* EOF */ diff --git a/reactos/subsys/win32k/ntddraw/stubs.c b/reactos/subsys/win32k/ntddraw/stubs.c index 104492244b9..1f2eb06d3fa 100644 --- a/reactos/subsys/win32k/ntddraw/stubs.c +++ b/reactos/subsys/win32k/ntddraw/stubs.c @@ -16,6 +16,17 @@ #include +DWORD STDCALL NtGdiDdGetAvailDriverMemory( + HANDLE hDirectDrawLocal, + PDD_GETAVAILDRIVERMEMORYDATA puGetAvailDriverMemoryData +) +{ + UNIMPLEMENTED + + + return 0; +} + BOOL STDCALL NtGdiD3dContextCreate( HANDLE hDirectDrawLocal, HANDLE hSurfColor, @@ -308,15 +319,6 @@ DWORD STDCALL NtGdiDdFlipToGDISurface( return 0; } -DWORD STDCALL NtGdiDdGetAvailDriverMemory( - HANDLE hDirectDraw, - PDD_GETAVAILDRIVERMEMORYDATA puGetAvailDriverMemoryData -) -{ - UNIMPLEMENTED - - return 0; -} DWORD STDCALL NtGdiDdGetBltStatus( HANDLE hSurface, @@ -338,15 +340,7 @@ HDC STDCALL NtGdiDdGetDC( return 0; } -DWORD STDCALL NtGdiDdGetDriverInfo( - HANDLE hDirectDraw, - PDD_GETDRIVERINFODATA puGetDriverInfoData -) -{ - UNIMPLEMENTED - return 0; -} DWORD STDCALL NtGdiDdGetDriverState( PDD_GETDRIVERSTATEDATA pdata @@ -600,14 +594,5 @@ DWORD STDCALL NtGdiDdUpdateOverlay( return 0; } -DWORD STDCALL NtGdiDdWaitForVerticalBlank( - HANDLE hDirectDraw, - PDD_WAITFORVERTICALBLANKDATA puWaitForVerticalBlankData -) -{ - UNIMPLEMENTED - - return 0; -} /* EOF */ diff --git a/reactos/subsys/win32k/ntuser/class.c b/reactos/subsys/win32k/ntuser/class.c index fd959354c33..1952c2fce75 100644 --- a/reactos/subsys/win32k/ntuser/class.c +++ b/reactos/subsys/win32k/ntuser/class.c @@ -534,6 +534,8 @@ NtUserGetClassLong(HWND hWnd, DWORD Offset, BOOL Ansi) void FASTCALL IntSetClassLong(PWINDOW_OBJECT WindowObject, ULONG Offset, LONG dwNewLong, BOOL Ansi) { + PWINDOW_OBJECT Parent, Owner; + if ((int)Offset >= 0) { DPRINT("SetClassLong(%x, %d, %x)\n", WindowObject->Self, Offset, dwNewLong); @@ -562,6 +564,25 @@ IntSetClassLong(PWINDOW_OBJECT WindowObject, ULONG Offset, LONG dwNewLong, BOOL break; case GCL_HICON: WindowObject->Class->hIcon = (HICON)dwNewLong; + Owner = IntGetOwner(WindowObject); + Parent = IntGetParent(WindowObject); + + if ((!Owner) && (!Parent)) + { + IntShellHookNotify(HSHELL_REDRAW, (LPARAM) WindowObject->Self); + } + + if (Parent) + { + IntReleaseWindowObject(Parent); + } + + if (Owner) + { + IntReleaseWindowObject(Owner); + } + + break; case GCL_HICONSM: WindowObject->Class->hIconSm = (HICON)dwNewLong; diff --git a/reactos/subsys/win32k/ntuser/desktop.c b/reactos/subsys/win32k/ntuser/desktop.c index 55e4fa2f103..5574d9c82dc 100644 --- a/reactos/subsys/win32k/ntuser/desktop.c +++ b/reactos/subsys/win32k/ntuser/desktop.c @@ -66,6 +66,61 @@ CleanupDesktopImpl(VOID) return STATUS_SUCCESS; } +/* OBJECT CALLBACKS **********************************************************/ + +NTSTATUS STDCALL +IntDesktopObjectCreate(PVOID ObjectBody, + PVOID Parent, + PWSTR RemainingPath, + struct _OBJECT_ATTRIBUTES* ObjectAttributes) +{ + PDESKTOP_OBJECT Desktop = (PDESKTOP_OBJECT)ObjectBody; + UNICODE_STRING UnicodeString; + + if (RemainingPath == NULL) + { + return STATUS_SUCCESS; + } + + if (wcschr((RemainingPath + 1), '\\') != NULL) + { + return STATUS_UNSUCCESSFUL; + } + + RtlInitUnicodeString(&UnicodeString, (RemainingPath + 1)); + + DPRINT("Creating desktop (0x%X) Name (%wZ)\n", Desktop, &UnicodeString); + + KeInitializeSpinLock(&Desktop->Lock); + InitializeListHead(&Desktop->ShellHookWindows); + + Desktop->WindowStation = (PWINSTATION_OBJECT)Parent; + + /* Put the desktop on the window station's list of associcated desktops */ + ExInterlockedInsertTailList( + &Desktop->WindowStation->DesktopListHead, + &Desktop->ListEntry, + &Desktop->WindowStation->Lock); + + return RtlCreateUnicodeString(&Desktop->Name, UnicodeString.Buffer); +} + +VOID STDCALL +IntDesktopObjectDelete(PVOID DeletedObject) +{ + PDESKTOP_OBJECT Desktop = (PDESKTOP_OBJECT)DeletedObject; + KIRQL OldIrql; + + DPRINT("Deleting desktop (0x%X)\n", Desktop); + + /* Remove the desktop from the window station's list of associcated desktops */ + KeAcquireSpinLock(&Desktop->WindowStation->Lock, &OldIrql); + RemoveEntryList(&Desktop->ListEntry); + KeReleaseSpinLock(&Desktop->WindowStation->Lock, OldIrql); + + RtlFreeUnicodeString(&Desktop->Name); +} + /* PRIVATE FUNCTIONS **********************************************************/ NTSTATUS FASTCALL @@ -486,6 +541,147 @@ IntHideDesktop(PDESKTOP_OBJECT Desktop) #endif } +/* + * Send the Message to the windows registered for ShellHook + * notifications. The lParam contents depend on the Message. See + * MSDN for more details (RegisterShellHookWindow) + */ +VOID IntShellHookNotify(WPARAM Message, LPARAM lParam) +{ + PDESKTOP_OBJECT Desktop = IntGetActiveDesktop(); + PLIST_ENTRY Entry, Entry2; + PSHELL_HOOK_WINDOW Current; + KIRQL OldLevel; + + static UINT MsgType = 0; + + if (!MsgType) { + + /* Too bad, this doesn't work.*/ +#if 0 + UNICODE_STRING Str; + RtlInitUnicodeString(&Str, L"SHELLHOOK"); + MsgType = NtUserRegisterWindowMessage(&Str); +#endif + MsgType = IntAddAtom(L"SHELLHOOK"); + + DPRINT("MsgType = %x\n", MsgType); + if (!MsgType) + DPRINT1("LastError: %x\n", GetLastNtError()); + } + + if (!Desktop) { + DPRINT1("IntShellHookNotify: No desktop!\n"); + return; + } + + /* We have to do some tricks because the list could change + * between calls, and we can't keep the lock during the call + */ + + KeAcquireSpinLock(&Desktop->Lock, &OldLevel); + Entry = Desktop->ShellHookWindows.Flink; + while (Entry != &Desktop->ShellHookWindows) { + Current = CONTAINING_RECORD(Entry, SHELL_HOOK_WINDOW, ListEntry); + KeReleaseSpinLock(&Desktop->Lock, OldLevel); + + DPRINT("Sending notify\n"); + IntPostOrSendMessage(Current->hWnd, + MsgType, + Message, + lParam); + + /* Loop again to find the window we were sending to. If it doesn't + * exist anymore, we just stop. This could leave an infinite loop + * if a window is removed and readded to the list. That's quite + * unlikely though. + */ + + KeAcquireSpinLock(&Desktop->Lock, &OldLevel); + Entry2 = Desktop->ShellHookWindows.Flink; + while (Entry2 != Entry && + Entry2 != &Desktop->ShellHookWindows) { + Entry2 = Entry2->Flink; + } + + if (Entry2 == Entry) + Entry = Entry->Flink; + else + break; + } + KeReleaseSpinLock(&Desktop->Lock, OldLevel); +} + +/* + * Add the window to the ShellHookWindows list. The windows + * on that list get notifications that are important to shell + * type applications. + * + * TODO: Validate the window? I'm not sure if sending these messages to + * an unsuspecting application that is not your own is a nice thing to do. + */ +BOOL IntRegisterShellHookWindow(HWND hWnd) +{ + PDESKTOP_OBJECT Desktop = PsGetWin32Thread()->Desktop; + PSHELL_HOOK_WINDOW Entry; + KIRQL OldLevel; + + DPRINT("IntRegisterShellHookWindow\n"); + + /* First deregister the window, so we can be sure it's never twice in the + * list. + */ + IntDeRegisterShellHookWindow(hWnd); + + Entry = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SHELL_HOOK_WINDOW), + TAG_WINSTA); + /* We have to walk this structure with while holding a spinlock, so we + * need NonPagedPool */ + + if (!Entry) + return FALSE; + + Entry->hWnd = hWnd; + + KeAcquireSpinLock(&Desktop->Lock, &OldLevel); + InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry); + KeReleaseSpinLock(&Desktop->Lock, OldLevel); + + return TRUE; +} + +/* + * Remove the window from the ShellHookWindows list. The windows + * on that list get notifications that are important to shell + * type applications. + */ +BOOL IntDeRegisterShellHookWindow(HWND hWnd) +{ + PDESKTOP_OBJECT Desktop = PsGetWin32Thread()->Desktop; + PLIST_ENTRY Entry; + PSHELL_HOOK_WINDOW Current; + KIRQL OldLevel; + + KeAcquireSpinLock(&Desktop->Lock, &OldLevel); + + Entry = Desktop->ShellHookWindows.Flink; + while (Entry != &Desktop->ShellHookWindows) { + Current = CONTAINING_RECORD(Entry, SHELL_HOOK_WINDOW, ListEntry); + if (Current->hWnd == hWnd) { + RemoveEntryList(Entry); + KeReleaseSpinLock(&Desktop->Lock, OldLevel); + ExFreePool(Entry); + return TRUE; + } + Entry = Entry->Flink; + } + + KeReleaseSpinLock(&Desktop->Lock, OldLevel); + + return FALSE; +} + /* * NtUserCreateDesktop * diff --git a/reactos/subsys/win32k/ntuser/focus.c b/reactos/subsys/win32k/ntuser/focus.c index 0e34f9f8002..3746b33ede2 100644 --- a/reactos/subsys/win32k/ntuser/focus.c +++ b/reactos/subsys/win32k/ntuser/focus.c @@ -62,6 +62,8 @@ IntSendDeactivateMessages(HWND hWndPrev, HWND hWnd) VOID FASTCALL IntSendActivateMessages(HWND hWndPrev, HWND hWnd, BOOL MouseActivate) { + PWINDOW_OBJECT Window, Owner, Parent; + if (hWnd) { /* Send palette messages */ @@ -75,6 +77,22 @@ IntSendActivateMessages(HWND hWndPrev, HWND hWnd, BOOL MouseActivate) WinPosSetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSENDCHANGING); + Window = IntGetWindowObject(hWnd); + if (Window) { + Owner = IntGetOwner(Window); + if (!Owner) { + Parent = IntGetParent(Window); + if (!Parent) + IntShellHookNotify(HSHELL_WINDOWACTIVATED, (LPARAM) hWnd); + else + IntReleaseWindowObject(Parent); + } else { + IntReleaseWindowObject(Owner); + } + + IntReleaseWindowObject(Window); + } + /* FIXME: IntIsWindow */ IntPostOrSendMessage(hWnd, WM_NCACTIVATE, (WPARAM)(hWnd == NtUserGetForegroundWindow()), 0); diff --git a/reactos/subsys/win32k/ntuser/input.c b/reactos/subsys/win32k/ntuser/input.c index 735f789e994..15374e54ced 100644 --- a/reactos/subsys/win32k/ntuser/input.c +++ b/reactos/subsys/win32k/ntuser/input.c @@ -611,8 +611,8 @@ IntMouseInput(MOUSEINPUT *mi) MousePos.x = DesktopWindow->ClientRect.right - 1; if(MousePos.y >= DesktopWindow->ClientRect.bottom) MousePos.y = DesktopWindow->ClientRect.bottom - 1; + ObmDereferenceObject(DesktopWindow); } - ObmDereferenceObject(DesktopWindow); if(MousePos.x < 0) MousePos.x = 0; diff --git a/reactos/subsys/win32k/ntuser/keyboard.c b/reactos/subsys/win32k/ntuser/keyboard.c index 835a5b88697..d5c77f5b416 100644 --- a/reactos/subsys/win32k/ntuser/keyboard.c +++ b/reactos/subsys/win32k/ntuser/keyboard.c @@ -216,7 +216,7 @@ static BOOL TryToTranslateChar(WORD wVirtKey, PVK_TO_WCHAR_TABLE vtwTbl; PVK_TO_WCHARS10 vkPtr; size_t size_this_entry; - int nMod, shift; + int nMod; DWORD CapsMod = 0, CapsState = 0; CapsState = ModBits & ~MOD_BITS_MASK; @@ -228,8 +228,6 @@ static BOOL TryToTranslateChar(WORD wVirtKey, { return FALSE; } - shift = keyLayout->pCharModifiers->ModNumber[ModBits]; - for (nMod = 0; keyLayout->pVkToWcharTable[nMod].nModifications; nMod++) { vtwTbl = &keyLayout->pVkToWcharTable[nMod]; @@ -239,9 +237,10 @@ static BOOL TryToTranslateChar(WORD wVirtKey, { if( wVirtKey == (vkPtr->VirtualKey & 0xff) ) { - CapsMod = - shift | ((CapsState & CAPITAL_BIT) ? vkPtr->Attributes : 0); - + CapsMod = keyLayout->pCharModifiers->ModNumber + [ModBits ^ + ((CapsState & CAPITAL_BIT) ? vkPtr->Attributes : 0)]; + if( CapsMod > keyLayout->pVkToWcharTable[nMod].nModifications ) { DWORD MaxBit = 1; while( MaxBit < @@ -256,9 +255,9 @@ static BOOL TryToTranslateChar(WORD wVirtKey, *pbLigature = vkPtr->wch[CapsMod] == WCH_LGTR; *pwcTranslatedChar = vkPtr->wch[CapsMod]; - DPRINT("%d %04x: CapsMod %08x CapsState %08x shift %08x Char %04x\n", + DPRINT("%d %04x: CapsMod %08x CapsState %08x Char %04x\n", nMod, wVirtKey, - CapsMod, CapsState, shift, *pwcTranslatedChar); + CapsMod, CapsState, *pwcTranslatedChar); if( *pbDead ) { @@ -269,7 +268,7 @@ static BOOL TryToTranslateChar(WORD wVirtKey, DPRINT( "VK: %04x, ADDR: %08x\n", wVirtKey, (int)vkPtr ); return FALSE; } - *pwcTranslatedChar = vkPtr->wch[shift]; + *pwcTranslatedChar = vkPtr->wch[CapsMod]; } return TRUE; } diff --git a/reactos/subsys/win32k/ntuser/message.c b/reactos/subsys/win32k/ntuser/message.c index 3b8e33a9493..522404f8274 100644 --- a/reactos/subsys/win32k/ntuser/message.c +++ b/reactos/subsys/win32k/ntuser/message.c @@ -30,6 +30,7 @@ /* INCLUDES ******************************************************************/ #include +#include #define NDEBUG #include @@ -113,6 +114,7 @@ MsgMemorySize(PMSGMEMORY MsgMemoryEntry, WPARAM wParam, LPARAM lParam) PUNICODE_STRING ClassName; UINT Size; + _SEH_TRY { if (MMS_SIZE_WPARAM == MsgMemoryEntry->Size) { return (UINT) wParam; @@ -163,6 +165,11 @@ MsgMemorySize(PMSGMEMORY MsgMemoryEntry, WPARAM wParam, LPARAM lParam) { return MsgMemoryEntry->Size; } + } _SEH_HANDLE { + + DPRINT1("Exception caught in MsgMemorySize()! Status: 0x%x\n", _SEH_GetExceptionCode()); + } _SEH_END; + return 0; } static FASTCALL NTSTATUS diff --git a/reactos/subsys/win32k/ntuser/metric.c b/reactos/subsys/win32k/ntuser/metric.c index 3046d3c6b77..760f6e44f34 100644 --- a/reactos/subsys/win32k/ntuser/metric.c +++ b/reactos/subsys/win32k/ntuser/metric.c @@ -42,7 +42,6 @@ NtUserGetSystemMetrics(ULONG Index) { NTSTATUS Status; PWINSTATION_OBJECT WinStaObject; - PWINDOW_OBJECT DesktopWindow; ULONG Width, Height, Result; Result = 0; @@ -156,19 +155,26 @@ NtUserGetSystemMetrics(ULONG Index) return(27); case SM_CXSCREEN: case SM_CYSCREEN: - DesktopWindow = IntGetWindowObject(IntGetDesktopWindow()); - if (NULL != DesktopWindow) - { - Width = DesktopWindow->WindowRect.right; - Height = DesktopWindow->WindowRect.bottom; - } - else - { - Width = 640; - Height = 480; - } - IntReleaseWindowObject(DesktopWindow); + { + HDC ScreenDCHandle; + PDC ScreenDC; + + Width = 640; + Height = 480; + ScreenDCHandle = IntGdiCreateDC(NULL, NULL, NULL, NULL, TRUE); + if (NULL != ScreenDCHandle) + { + ScreenDC = DC_LockDc(ScreenDCHandle); + if (NULL != ScreenDC) + { + Width = ScreenDC->GDIInfo->ulHorzRes; + Height = ScreenDC->GDIInfo->ulVertRes; + DC_UnlockDc(ScreenDCHandle); + } + NtGdiDeleteDC(ScreenDCHandle); + } return SM_CXSCREEN == Index ? Width : Height; + } case SM_CXSIZE: case SM_CYSIZE: return(18); diff --git a/reactos/subsys/win32k/ntuser/misc.c b/reactos/subsys/win32k/ntuser/misc.c index 7135b288fa4..27c68e43cca 100644 --- a/reactos/subsys/win32k/ntuser/misc.c +++ b/reactos/subsys/win32k/ntuser/misc.c @@ -17,13 +17,15 @@ /* registered Logon process */ PW32PROCESS LogonProcess = NULL; -void W32kRegisterPrimitiveMessageQueue() { +VOID W32kRegisterPrimitiveMessageQueue(VOID) +{ extern PUSER_MESSAGE_QUEUE pmPrimitiveMessageQueue; if( !pmPrimitiveMessageQueue ) { PW32THREAD pThread; pThread = PsGetWin32Thread(); if( pThread && pThread->MessageQueue ) { pmPrimitiveMessageQueue = pThread->MessageQueue; + IntReferenceMessageQueue(pmPrimitiveMessageQueue); DPRINT( "Installed primitive input queue.\n" ); } } else { @@ -31,7 +33,15 @@ void W32kRegisterPrimitiveMessageQueue() { } } -PUSER_MESSAGE_QUEUE W32kGetPrimitiveMessageQueue() { +VOID W32kUnregisterPrimitiveMessageQueue(VOID) +{ + extern PUSER_MESSAGE_QUEUE pmPrimitiveMessageQueue; + IntDereferenceMessageQueue(pmPrimitiveMessageQueue); + pmPrimitiveMessageQueue = NULL; +} + +PUSER_MESSAGE_QUEUE W32kGetPrimitiveMessageQueue() +{ extern PUSER_MESSAGE_QUEUE pmPrimitiveMessageQueue; return pmPrimitiveMessageQueue; } diff --git a/reactos/subsys/win32k/ntuser/msgqueue.c b/reactos/subsys/win32k/ntuser/msgqueue.c index 900f3b9b6b3..b1c00482c93 100644 --- a/reactos/subsys/win32k/ntuser/msgqueue.c +++ b/reactos/subsys/win32k/ntuser/msgqueue.c @@ -1413,15 +1413,21 @@ VOID FASTCALL MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue) { PDESKTOP_OBJECT desk; + /* remove the message queue from any desktops */ - if((desk = (PDESKTOP_OBJECT)InterlockedExchange((LONG*)&MessageQueue->Desktop, 0))) - { - InterlockedExchange((LONG*)&desk->ActiveMessageQueue, 0); - IntDereferenceMessageQueue(MessageQueue); - } + if ((desk = (PDESKTOP_OBJECT)InterlockedExchange((LONG*)&MessageQueue->Desktop, 0))) + { + InterlockedExchange((LONG*)&desk->ActiveMessageQueue, 0); + IntDereferenceMessageQueue(MessageQueue); + } + /* if this is the primitive message queue, deregister it */ + if (MessageQueue == W32kGetPrimitiveMessageQueue()) + W32kUnregisterPrimitiveMessageQueue(); + /* clean it up */ MsqCleanupMessageQueue(MessageQueue); + /* decrease the reference counter, if it hits zero, the queue will be freed */ IntDereferenceMessageQueue(MessageQueue); } diff --git a/reactos/subsys/win32k/ntuser/painting.c b/reactos/subsys/win32k/ntuser/painting.c index dece285b320..2c603127650 100644 --- a/reactos/subsys/win32k/ntuser/painting.c +++ b/reactos/subsys/win32k/ntuser/painting.c @@ -144,16 +144,6 @@ IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags) Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) { IntSendMessage(hWnd, WM_PAINT, 0, 0); - IntLockWindowUpdate(Window); - if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) - { - Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT; - if (Window->UpdateRegion == NULL) - { - MsqDecPaintCountQueue(Window->MessageQueue); - } - } - IntUnLockWindowUpdate(Window); } } } @@ -614,6 +604,10 @@ IntGetPaintMessage(HWND hWnd, UINT MsgFilterMin, UINT MsgFilterMax, if (!MessageQueue->PaintPosted) return FALSE; + if ((MsgFilterMin != 0 || MsgFilterMax != 0) && + (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT)) + return FALSE; + if (hWnd) Message->hwnd = IntFindWindowToRepaint(hWnd, PsGetWin32Thread()); else @@ -635,25 +629,10 @@ IntGetPaintMessage(HWND hWnd, UINT MsgFilterMin, UINT MsgFilterMax, Window = IntGetWindowObject(Message->hwnd); if (Window != NULL) { - IntLockWindowUpdate(Window); - - if ((MsgFilterMin == 0 && MsgFilterMax == 0) || - (MsgFilterMin <= WM_PAINT && WM_PAINT <= MsgFilterMax)) - { - Message->message = WM_PAINT; - Message->wParam = Message->lParam = 0; - if (Remove && Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) - { - Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT; - if (Window->UpdateRegion == NULL) - { - MsqDecPaintCountQueue(Window->MessageQueue); - } - } - } - IntUnLockWindowUpdate(Window); - + Message->message = WM_PAINT; + Message->wParam = Message->lParam = 0; IntReleaseWindowObject(Window); + return TRUE; } @@ -779,8 +758,11 @@ NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs) } else { + if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) + MsqDecPaintCountQueue(Window->MessageQueue); IntGetClientRect(Window, &Ps.rcPaint); } + Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT; IntUnLockWindowUpdate(Window); if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND) @@ -892,6 +874,7 @@ NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase) return ERROR; } + IntLockWindowUpdate(Window); if (Window->UpdateRegion == NULL) { RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR); @@ -904,6 +887,7 @@ NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase) Window->WindowRect.left - Window->ClientRect.left, Window->WindowRect.top - Window->ClientRect.top); } + IntUnLockWindowUpdate(Window); IntReleaseWindowObject(Window); @@ -923,49 +907,56 @@ NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase) */ BOOL STDCALL -NtUserGetUpdateRect(HWND Wnd, LPRECT UnsafeRect, BOOL Erase) +NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase) { - RECT Rect; - HRGN Rgn; - PROSRGNDATA RgnData; - NTSTATUS Status; + PWINDOW_OBJECT Window; + RECT Rect; + INT RegionType; + PROSRGNDATA RgnData; + BOOL AlwaysPaint; + NTSTATUS Status; - Rgn = NtGdiCreateRectRgn(0, 0, 0, 0); - if (NULL == Rgn) - { - NtGdiDeleteObject(Rgn); - SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES); - return FALSE; - } - NtUserGetUpdateRgn(Wnd, Rgn, Erase); - RgnData = RGNDATA_LockRgn(Rgn); - if (NULL == RgnData) - { - NtGdiDeleteObject(Rgn); - SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES); - return FALSE; - } - if (ERROR == UnsafeIntGetRgnBox(RgnData, &Rect)) - { - RGNDATA_UnlockRgn(Rgn); - NtGdiDeleteObject(Rgn); - SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES); - return FALSE; - } - RGNDATA_UnlockRgn(Rgn); - NtGdiDeleteObject(Rgn); + if (!(Window = IntGetWindowObject(hWnd))) + { + SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE); + return ERROR; + } + + IntLockWindowUpdate(Window); + if (Window->UpdateRegion == NULL) + { + Rect.left = Rect.top = Rect.right = Rect.bottom = 0; + } + else + { + RgnData = RGNDATA_LockRgn(Window->UpdateRegion); + ASSERT(RgnData != NULL); + RegionType = UnsafeIntGetRgnBox(RgnData, &Rect); + ASSERT(RegionType != ERROR); + RGNDATA_UnlockRgn(Window->UpdateRegion); + } + AlwaysPaint = (Window->Flags & WINDOWOBJECT_NEED_NCPAINT) || + (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT); + IntUnLockWindowUpdate(Window); - if (UnsafeRect != NULL) - { + IntReleaseWindowObject(Window); + + if (bErase && Rect.left < Rect.right && Rect.top < Rect.bottom) + { + NtUserRedrawWindow(hWnd, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN); + } + + if (UnsafeRect != NULL) + { Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECT)); - if (! NT_SUCCESS(Status)) - { - SetLastWin32Error(ERROR_INVALID_PARAMETER); - return FALSE; - } - } + if (!NT_SUCCESS(Status)) + { + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return FALSE; + } + } - return Rect.left < Rect.right && Rect.top < Rect.bottom; + return (Rect.left < Rect.right && Rect.top < Rect.bottom) || AlwaysPaint; } /* diff --git a/reactos/subsys/win32k/ntuser/stubs.c b/reactos/subsys/win32k/ntuser/stubs.c index b8091f98e82..77c88aceb58 100644 --- a/reactos/subsys/win32k/ntuser/stubs.c +++ b/reactos/subsys/win32k/ntuser/stubs.c @@ -59,6 +59,17 @@ NtUserCallHwnd( DWORD Unknown0, DWORD Unknown1) { + switch (Unknown0) { + case HWND_ROUTINE_REGISTERSHELLHOOKWINDOW: + if (IntIsWindow((HWND) Unknown1)) + return IntRegisterShellHookWindow((HWND) Unknown1); + return FALSE; + break; + case HWND_ROUTINE_DEREGISTERSHELLHOOKWINDOW: + if (IntIsWindow((HWND) Unknown1)) + return IntDeRegisterShellHookWindow((HWND) Unknown1); + return FALSE; + } UNIMPLEMENTED return 0; @@ -99,20 +110,6 @@ NtUserCallMsgFilter( return 0; } -LONG -STDCALL -NtUserChangeDisplaySettings( - PUNICODE_STRING lpszDeviceName, - LPDEVMODEW lpDevMode, - HWND hwnd, - DWORD dwflags, - LPVOID lParam) -{ - // UNIMPLEMENTED - DbgPrint("(%s:%i) WIN32K: %s UNIMPLEMENTED\n", __FILE__, __LINE__, __FUNCTION__ ); - return DISP_CHANGE_BADMODE; -} - DWORD STDCALL NtUserConvertMemHandle( @@ -240,9 +237,24 @@ NtUserEnumDisplayDevices ( PDISPLAY_DEVICE lpDisplayDevice, /* device information */ DWORD dwFlags ) /* reserved */ { - UNIMPLEMENTED + DPRINT1("NtUserEnumDisplayDevices() is UNIMPLEMENTED!\n"); + if (lpDevice->Length == 0 && iDevNum > 0) + { + /* Only one display device present */ + return FALSE; + } + if (lpDisplayDevice->cb < sizeof(DISPLAY_DEVICE)) + return FALSE; - return 0; + swprintf(lpDisplayDevice->DeviceName, L"\\\\.\\DISPLAY1"); + swprintf(lpDisplayDevice->DeviceString, L""); + lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ATTACHED_TO_DESKTOP + | DISPLAY_DEVICE_MODESPRUNED + | DISPLAY_DEVICE_PRIMARY_DEVICE + | DISPLAY_DEVICE_VGA_COMPATIBLE; + lpDisplayDevice->DeviceID[0] = L'0'; + lpDisplayDevice->DeviceKey[0] = L'0'; + return TRUE; } DWORD diff --git a/reactos/subsys/win32k/ntuser/timer.c b/reactos/subsys/win32k/ntuser/timer.c index 75e9f844b1d..e79a3cc5cd1 100644 --- a/reactos/subsys/win32k/ntuser/timer.c +++ b/reactos/subsys/win32k/ntuser/timer.c @@ -92,11 +92,11 @@ IntSetTimer(HWND Wnd, UINT_PTR IDEvent, UINT Elapse, TIMERPROC TimerFunc, BOOL S SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE); return 0; } - - if (WindowObject->OwnerThread != PsGetCurrentThread()) + + if (WindowObject->OwnerThread->ThreadsProcess != PsGetCurrentProcess()) { IntReleaseWindowObject(WindowObject); - DPRINT1("Trying to set timer for window in another thread (shatter attack?)\n"); + DPRINT1("Trying to set timer for window in another process (shatter attack?)\n"); SetLastWin32Error(ERROR_ACCESS_DENIED); return 0; } diff --git a/reactos/subsys/win32k/ntuser/windc.c b/reactos/subsys/win32k/ntuser/windc.c index 7463bce6d03..ab5911fba00 100644 --- a/reactos/subsys/win32k/ntuser/windc.c +++ b/reactos/subsys/win32k/ntuser/windc.c @@ -132,7 +132,7 @@ DceAllocDCE(HWND hWnd, DCE_TYPE Type) /* No real locking, just get the pointer */ DCEOBJ_UnlockDCE(DceHandle); Dce->Self = DceHandle; - Dce->hDC = IntGdiCreateDC(&DriverName, NULL, NULL, NULL); + Dce->hDC = IntGdiCreateDC(&DriverName, NULL, NULL, NULL, FALSE); if (NULL == defaultDCstate) { defaultDCstate = NtGdiGetDCState(Dce->hDC); @@ -354,6 +354,11 @@ noparent: Dce->DCXFlags &= ~DCX_DCEDIRTY; NtGdiSelectVisRgn(Dce->hDC, hRgnVisible); + if (Window != NULL) + { + IntEngWindowChanged(Window, WOC_RGN_CLIENT); + } + if (hRgnVisible != NULL) { NtGdiDeleteObject(hRgnVisible); @@ -856,6 +861,7 @@ DceResetActiveDCEs(PWINDOW_OBJECT Window) if (Window->Self != pDCE->hwndCurrent) { +// IntEngWindowChanged(CurrentWindow, WOC_RGN_CLIENT); IntReleaseWindowObject(CurrentWindow); } } @@ -866,13 +872,6 @@ DceResetActiveDCEs(PWINDOW_OBJECT Window) DCE_UnlockList(); } -/* FIXME: find header file for this prototype. */ -extern BOOL FASTCALL -IntEnumDisplaySettings( - PUNICODE_STRING lpszDeviceName, - DWORD iModeNum, - LPDEVMODEW lpDevMode, - DWORD dwFlags); #define COPY_DEVMODE_VALUE_TO_CALLER(dst, src, member) \ Status = MmCopyToCaller(&(dst)->member, &(src)->member, sizeof ((src)->member)); \ @@ -893,15 +892,18 @@ NtUserEnumDisplaySettings( { NTSTATUS Status; LPDEVMODEW pSafeDevMode; - DWORD Size = 0, ExtraSize = 0; + PUNICODE_STRING pSafeDeviceName = NULL; + UNICODE_STRING SafeDeviceName; + USHORT Size = 0, ExtraSize = 0; - Status = MmCopyFromCaller(&Size, &lpDevMode->dmSize, sizeof (lpDevMode->dmSize)); + /* Copy the devmode */ + Status = MmCopyFromCaller(&Size, &lpDevMode->dmSize, sizeof (Size)); if (!NT_SUCCESS(Status)) { SetLastNtError(Status); return FALSE; } - Status = MmCopyFromCaller(&ExtraSize, &lpDevMode->dmDriverExtra, sizeof (lpDevMode->dmDriverExtra)); + Status = MmCopyFromCaller(&ExtraSize, &lpDevMode->dmDriverExtra, sizeof (ExtraSize)); if (!NT_SUCCESS(Status)) { SetLastNtError(Status); @@ -911,17 +913,36 @@ NtUserEnumDisplaySettings( if (pSafeDevMode == NULL) { SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - return DISP_CHANGE_FAILED; + return FALSE; } pSafeDevMode->dmSize = Size; pSafeDevMode->dmDriverExtra = ExtraSize; - if (!IntEnumDisplaySettings(lpszDeviceName, iModeNum, pSafeDevMode, dwFlags)) + /* Copy the device name */ + if (lpszDeviceName != NULL) { + Status = IntSafeCopyUnicodeString(&SafeDeviceName, lpszDeviceName); + if (!NT_SUCCESS(Status)) + { + ExFreePool(pSafeDevMode); + SetLastNtError(Status); + return FALSE; + } + pSafeDeviceName = &SafeDeviceName; + } + + /* Call internal function */ + if (!IntEnumDisplaySettings(pSafeDeviceName, iModeNum, pSafeDevMode, dwFlags)) + { + if (pSafeDeviceName != NULL) + RtlFreeUnicodeString(pSafeDeviceName); ExFreePool(pSafeDevMode); return FALSE; } + if (pSafeDeviceName != NULL) + RtlFreeUnicodeString(pSafeDeviceName); + /* Copy some information back */ COPY_DEVMODE_VALUE_TO_CALLER(lpDevMode, pSafeDevMode, dmPelsWidth); COPY_DEVMODE_VALUE_TO_CALLER(lpDevMode, pSafeDevMode, dmPelsHeight); COPY_DEVMODE_VALUE_TO_CALLER(lpDevMode, pSafeDevMode, dmBitsPerPel); @@ -940,9 +961,84 @@ NtUserEnumDisplaySettings( } } + ExFreePool(pSafeDevMode); return TRUE; } #undef COPY_DEVMODE_VALUE_TO_CALLER + +LONG +STDCALL +NtUserChangeDisplaySettings( + PUNICODE_STRING lpszDeviceName, + LPDEVMODEW lpDevMode, + HWND hwnd, + DWORD dwflags, + LPVOID lParam) +{ + NTSTATUS Status; + DEVMODEW DevMode; + PUNICODE_STRING pSafeDeviceName = NULL; + UNICODE_STRING SafeDeviceName; + LONG Ret; + + /* Check arguments */ +#ifdef CDS_VIDEOPARAMETERS + if (dwflags != CDS_VIDEOPARAMETERS && lParam != NULL) +#else + if (lParam != NULL) +#endif + { + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return DISP_CHANGE_BADPARAM; + } + if (hwnd != NULL) + { + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return DISP_CHANGE_BADPARAM; + } + + /* Copy devmode */ + Status = MmCopyFromCaller(&DevMode.dmSize, &lpDevMode->dmSize, sizeof (DevMode.dmSize)); + if (!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return DISP_CHANGE_BADPARAM; + } + DevMode.dmSize = min(sizeof (DevMode), DevMode.dmSize); + Status = MmCopyFromCaller(&DevMode, lpDevMode, DevMode.dmSize); + if (!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return DISP_CHANGE_BADPARAM; + } + if (DevMode.dmDriverExtra > 0) + { + DbgPrint("(%s:%i) WIN32K: %s lpDevMode->dmDriverExtra is IGNORED!\n", __FILE__, __LINE__, __FUNCTION__); + DevMode.dmDriverExtra = 0; + } + + /* Copy the device name */ + if (lpszDeviceName != NULL) + { + Status = IntSafeCopyUnicodeString(&SafeDeviceName, lpszDeviceName); + if (!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return DISP_CHANGE_BADPARAM; + } + pSafeDeviceName = &SafeDeviceName; + } + + /* Call internal function */ + Ret = IntChangeDisplaySettings(pSafeDeviceName, &DevMode, dwflags, lParam); + + if (pSafeDeviceName != NULL) + RtlFreeUnicodeString(pSafeDeviceName); + + return Ret; +} + + /* EOF */ diff --git a/reactos/subsys/win32k/ntuser/window.c b/reactos/subsys/win32k/ntuser/window.c index 6489cdef7b8..3dab0ce0d4c 100644 --- a/reactos/subsys/win32k/ntuser/window.c +++ b/reactos/subsys/win32k/ntuser/window.c @@ -147,6 +147,17 @@ IntGetParent(PWINDOW_OBJECT Wnd) return NULL; } +PWINDOW_OBJECT FASTCALL +IntGetOwner(PWINDOW_OBJECT Wnd) +{ + HWND hWnd; + + IntLockRelatives(Wnd); + hWnd = Wnd->Owner; + IntUnLockRelatives(Wnd); + + return IntGetWindowObject(hWnd); +} PWINDOW_OBJECT FASTCALL IntGetParentObject(PWINDOW_OBJECT Wnd) @@ -205,6 +216,8 @@ IntWinListChildren(PWINDOW_OBJECT Window) */ static void IntSendDestroyMsg(HWND Wnd) { + + PWINDOW_OBJECT Window, Owner, Parent; #if 0 /* FIXME */ GUITHREADINFO info; @@ -217,9 +230,28 @@ static void IntSendDestroyMsg(HWND Wnd) } #endif + Window = IntGetWindowObject(Wnd); + if (Window) { + Owner = IntGetOwner(Window); + if (!Owner) { + Parent = IntGetParent(Window); + if (!Parent) + IntShellHookNotify(HSHELL_WINDOWDESTROYED, (LPARAM) Wnd); + else + IntReleaseWindowObject(Parent); + } else { + IntReleaseWindowObject(Owner); + } + + IntReleaseWindowObject(Window); + } + + /* The window could already be destroyed here */ + /* * Send the WM_DESTROY to the window. */ + IntSendMessage(Wnd, WM_DESTROY, 0, 0); /* @@ -284,6 +316,8 @@ static LRESULT IntDestroyWindow(PWINDOW_OBJECT Window, IntUnLockThreadWindows(Window->OwnerThread->Tcb.Win32Thread); BelongsToThreadData = IntWndBelongsToThread(Window, ThreadData); + + IntDeRegisterShellHookWindow(Window->Self); if(SendMessages) { @@ -1417,6 +1451,8 @@ IntCreateWindowEx(DWORD dwExStyle, BOOL MenuChanged; BOOL ClassFound; + BOOL HasOwner; + ParentWindowHandle = PsGetWin32Thread()->Desktop->DesktopWindow; OwnerWindowHandle = NULL; @@ -1542,9 +1578,11 @@ IntCreateWindowEx(DWORD dwExStyle, { WindowObject->Owner = OwnerWindowHandle; IntReleaseWindowObject(OwnerWindow); - } - else + HasOwner = TRUE; + } else { WindowObject->Owner = NULL; + HasOwner = FALSE; + } WindowObject->UserData = 0; if ((((DWORD)ClassObject->lpfnWndProcA & 0xFFFF0000) != 0xFFFF0000) && (((DWORD)ClassObject->lpfnWndProcW & 0xFFFF0000) != 0xFFFF0000)) @@ -1580,6 +1618,8 @@ IntCreateWindowEx(DWORD dwExStyle, ExInitializeFastMutex(&WindowObject->PropListLock); ExInitializeFastMutex(&WindowObject->RelativesLock); ExInitializeFastMutex(&WindowObject->UpdateLock); + InitializeListHead(&WindowObject->WndObjListHead); + ExInitializeFastMutex(&WindowObject->WndObjListLock); if (NULL != WindowName->Buffer) { @@ -1937,6 +1977,9 @@ IntCreateWindowEx(DWORD dwExStyle, WindowObject->ClientRect.top); } IntSendMessage(WindowObject->Self, WM_MOVE, 0, lParam); + + /* Call WNDOBJ change procs */ + IntEngWindowChanged(WindowObject, WOC_RGN_CLIENT); } /* Show or maybe minimize or maximize the window. */ @@ -1969,6 +2012,13 @@ IntCreateWindowEx(DWORD dwExStyle, (LPARAM)WindowObject->Self); } + if ((!hWndParent) && (!HasOwner)) { + DPRINT("Sending CREATED notify\n"); + IntShellHookNotify(HSHELL_WINDOWCREATED, (LPARAM)Handle); + } else { + DPRINT("Not sending CREATED notify, %x %d\n", ParentWindow, HasOwner); + } + if (NULL != ParentWindow) { IntReleaseWindowObject(ParentWindow); @@ -2135,6 +2185,7 @@ NtUserDestroyWindow(HWND Wnd) } #endif + IntEngWindowChanged(Window, WOC_DELETE); isChild = (0 != (Window->Style & WS_CHILD)); #if 0 /* FIXME */ @@ -4060,7 +4111,7 @@ NtUserWindowFromPoint(LONG X, LONG Y) BOOL STDCALL NtUserDefSetText(HWND WindowHandle, PUNICODE_STRING WindowText) { - PWINDOW_OBJECT WindowObject; + PWINDOW_OBJECT WindowObject, Parent, Owner; UNICODE_STRING SafeText; NTSTATUS Status; @@ -4090,6 +4141,26 @@ NtUserDefSetText(HWND WindowHandle, PUNICODE_STRING WindowText) RtlFreeUnicodeString(&WindowObject->WindowName); WindowObject->WindowName = SafeText; + + /* Send shell notifications */ + + Owner = IntGetOwner(WindowObject); + Parent = IntGetParent(WindowObject); + + if ((!Owner) && (!Parent)) + { + IntShellHookNotify(HSHELL_REDRAW, (LPARAM) WindowHandle); + } + + if (Owner) + { + IntReleaseWindowObject(Owner); + } + + if (Parent) + { + IntReleaseWindowObject(Parent); + } IntReleaseWindowObject(WindowObject); return TRUE; diff --git a/reactos/subsys/win32k/ntuser/winpos.c b/reactos/subsys/win32k/ntuser/winpos.c index 1d31e8528e4..f432429fc40 100644 --- a/reactos/subsys/win32k/ntuser/winpos.c +++ b/reactos/subsys/win32k/ntuser/winpos.c @@ -1341,6 +1341,7 @@ WinPosShowWindow(HWND Wnd, INT Cmd) IntSendMessage(Wnd, WM_MOVE, 0, MAKELONG(Window->ClientRect.left, Window->ClientRect.top)); + IntEngWindowChanged(Window, WOC_RGN_CLIENT); } /* Activate the window if activation is not requested and the window is not minimized */ diff --git a/reactos/subsys/win32k/ntuser/winsta.c b/reactos/subsys/win32k/ntuser/winsta.c index 3f6f2413e38..afa60e2dfa9 100644 --- a/reactos/subsys/win32k/ntuser/winsta.c +++ b/reactos/subsys/win32k/ntuser/winsta.c @@ -80,6 +80,163 @@ CleanupWindowStationImpl(VOID) return STATUS_SUCCESS; } +/* OBJECT CALLBACKS **********************************************************/ + +NTSTATUS +STDCALL +IntWinStaObjectCreate(PVOID ObjectBody, + PVOID Parent, + PWSTR RemainingPath, + struct _OBJECT_ATTRIBUTES* ObjectAttributes) +{ + PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)ObjectBody; + UNICODE_STRING UnicodeString; + NTSTATUS Status; + + if (RemainingPath == NULL) + { + return STATUS_SUCCESS; + } + + if (wcschr((RemainingPath + 1), '\\') != NULL) + { + return STATUS_UNSUCCESSFUL; + } + + RtlInitUnicodeString(&UnicodeString, (RemainingPath + 1)); + + DPRINT("Creating window station (0x%X) Name (%wZ)\n", WinSta, &UnicodeString); + + Status = RtlCreateUnicodeString(&WinSta->Name, UnicodeString.Buffer); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + KeInitializeSpinLock(&WinSta->Lock); + + InitializeListHead(&WinSta->DesktopListHead); + + WinSta->AtomTable = NULL; + + Status = RtlCreateAtomTable(37, &WinSta->AtomTable); + if (!NT_SUCCESS(Status)) + { + RtlFreeUnicodeString(&WinSta->Name); + return Status; + } + + WinSta->SystemMenuTemplate = (HANDLE)0; + + DPRINT("Window station successfully created. Name (%wZ)\n", &WinSta->Name); + + return STATUS_SUCCESS; +} + +VOID STDCALL +IntWinStaObjectDelete(PVOID DeletedObject) +{ + PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)DeletedObject; + + DPRINT("Deleting window station (0x%X)\n", WinSta); + + RtlDestroyAtomTable(WinSta->AtomTable); + + RtlFreeUnicodeString(&WinSta->Name); +} + +PVOID STDCALL +IntWinStaObjectFind(PVOID Object, + PWSTR Name, + ULONG Attributes) +{ + PLIST_ENTRY Current; + PDESKTOP_OBJECT CurrentObject; + PWINSTATION_OBJECT WinStaObject = (PWINSTATION_OBJECT)Object; + + DPRINT("WinStaObject (0x%X) Name (%wS)\n", WinStaObject, Name); + + if (Name[0] == 0) + { + return NULL; + } + + Current = WinStaObject->DesktopListHead.Flink; + while (Current != &WinStaObject->DesktopListHead) + { + CurrentObject = CONTAINING_RECORD(Current, DESKTOP_OBJECT, ListEntry); + DPRINT("Scanning %wZ for %wS\n", &CurrentObject->Name, Name); + if (Attributes & OBJ_CASE_INSENSITIVE) + { + if (_wcsicmp(CurrentObject->Name.Buffer, Name) == 0) + { + DPRINT("Found desktop at (0x%X)\n", CurrentObject); + return CurrentObject; + } + } + else + { + if (wcscmp(CurrentObject->Name.Buffer, Name) == 0) + { + DPRINT("Found desktop at (0x%X)\n", CurrentObject); + return CurrentObject; + } + } + Current = Current->Flink; + } + + DPRINT("Returning NULL\n"); + + return NULL; +} + +NTSTATUS +STDCALL +IntWinStaObjectParse(PVOID Object, + PVOID *NextObject, + PUNICODE_STRING FullPath, + PWSTR *Path, + ULONG Attributes) +{ + PVOID FoundObject; + NTSTATUS Status; + PWSTR End; + + DPRINT("Object (0x%X) Path (0x%X) *Path (%wS)\n", Object, Path, *Path); + + *NextObject = NULL; + + if ((Path == NULL) || ((*Path) == NULL)) + { + return STATUS_SUCCESS; + } + + End = wcschr((*Path) + 1, '\\'); + if (End != NULL) + { + DPRINT("Name contains illegal characters\n"); + return STATUS_UNSUCCESSFUL; + } + + FoundObject = IntWinStaObjectFind(Object, (*Path) + 1, Attributes); + if (FoundObject == NULL) + { + DPRINT("Name was not found\n"); + return STATUS_UNSUCCESSFUL; + } + + Status = ObReferenceObjectByPointer( + FoundObject, + STANDARD_RIGHTS_REQUIRED, + NULL, + UserMode); + + *NextObject = FoundObject; + *Path = NULL; + + return Status; +} + /* PRIVATE FUNCTIONS **********************************************************/ /* @@ -152,6 +309,13 @@ IntValidateWindowStationHandle( { NTSTATUS Status; + if (WindowStation == NULL) + { +// DPRINT1("Invalid window station handle\n"); + SetLastWin32Error(ERROR_INVALID_HANDLE); + return STATUS_INVALID_HANDLE; + } + Status = ObReferenceObjectByHandle( WindowStation, DesiredAccess, @@ -189,7 +353,7 @@ IntInitializeDesktopGraphics(VOID) return FALSE; } RtlInitUnicodeString(&DriverName, L"DISPLAY"); - ScreenDeviceContext = IntGdiCreateDC(&DriverName, NULL, NULL, NULL); + ScreenDeviceContext = IntGdiCreateDC(&DriverName, NULL, NULL, NULL, FALSE); if (NULL == ScreenDeviceContext) { IntDestroyPrimarySurface(); diff --git a/reactos/subsys/win32k/objects/bitmaps.c b/reactos/subsys/win32k/objects/bitmaps.c index 31b7ff80f36..92f193f35b5 100644 --- a/reactos/subsys/win32k/objects/bitmaps.c +++ b/reactos/subsys/win32k/objects/bitmaps.c @@ -59,6 +59,12 @@ NtGdiBitBlt( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (DCDest->IsIC) + { + DC_UnlockDc(hDCDest); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if (UsesSource) { @@ -72,6 +78,13 @@ NtGdiBitBlt( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (DCSrc->IsIC) + { + DC_UnlockDc(hDCSrc); + DC_UnlockDc(hDCDest); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } } else { @@ -258,6 +271,12 @@ NtGdiTransparentBlt( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (DCDest->IsIC) + { + DC_UnlockDc(hdcDst); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if((hdcDst != hdcSrc) && !(DCSrc = DC_LockDc(hdcSrc))) { @@ -270,6 +289,16 @@ NtGdiTransparentBlt( { DCSrc = DCDest; } + if (DCSrc->IsIC) + { + DC_UnlockDc(hdcSrc); + if(hdcDst != hdcSrc) + { + DC_UnlockDc(hdcDst); + } + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } /* Offset positions */ xDst += DCDest->w.DCOrgX; @@ -434,6 +463,10 @@ BITMAP_Cleanup(PVOID ObjectBody) { EngFreeUserMem(pBmp->SurfObj.pvBits); } + if (pBmp->hDIBPalette) + { + NtGdiDeleteObject(pBmp->hDIBPalette); + } } return TRUE; @@ -523,8 +556,10 @@ NtGdiExtFloodFill( COLORREF Color, UINT FillType) { - UNIMPLEMENTED; - return FALSE; + DPRINT1("FIXME: NtGdiExtFloodFill is UNIMPLEMENTED\n"); + + /* lie and say we succeded */ + return TRUE; } BOOL STDCALL @@ -575,6 +610,11 @@ NtGdiGetPixel(HDC hDC, INT XPos, INT YPos) SetLastWin32Error(ERROR_INVALID_HANDLE); return Result; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + return Result; + } XPos += dc->w.DCOrgX; YPos += dc->w.DCOrgY; if ( IN_RECT(dc->CombinedClip->rclBounds,XPos,YPos) ) @@ -1039,6 +1079,12 @@ NtGdiStretchBlt( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (DCDest->IsIC) + { + DC_UnlockDc(hDCDest); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if (UsesSource) { @@ -1052,6 +1098,13 @@ NtGdiStretchBlt( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (DCSrc->IsIC) + { + DC_UnlockDc(hDCSrc); + DC_UnlockDc(hDCDest); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } } else { diff --git a/reactos/subsys/win32k/objects/brush.c b/reactos/subsys/win32k/objects/brush.c index a14fb5a1efa..9dee1e9e884 100644 --- a/reactos/subsys/win32k/objects/brush.c +++ b/reactos/subsys/win32k/objects/brush.c @@ -72,6 +72,10 @@ IntGdiCreateBrushXlate(PDC Dc, GDIBRUSHOBJ *BrushObj, BOOLEAN *Failed) if (Dc->w.bitsPerPixel != 1) Result = IntEngCreateSrcMonoXlate(Dc->w.hPalette, Dc->w.textColor, Dc->w.backgroundColor); } + else if (BrushObj->flAttrs & GDIBRUSH_IS_DIB) + { + Result = IntEngCreateXlate(0, 0, Dc->w.hPalette, Pattern->hDIBPalette); + } BITMAPOBJ_UnlockBitmap(BrushObj->hbmPattern); *Failed = FALSE; @@ -97,33 +101,291 @@ IntGdiInitBrushInstance(GDIBRUSHINST *BrushInst, PGDIBRUSHOBJ BrushObj, XLATEOBJ BrushInst->XlateObject = XlateObj; } -HBRUSH FASTCALL -IntGdiCreateBrushIndirect(PLOGBRUSH LogBrush) +/** + * @name CalculateColorTableSize + * + * Internal routine to calculate the number of color table entries. + * + * @param BitmapInfoHeader + * Input bitmap information header, can be any version of + * BITMAPINFOHEADER or BITMAPCOREHEADER. + * + * @param ColorSpec + * Pointer to variable which specifiing the color mode (DIB_RGB_COLORS + * or DIB_RGB_COLORS). On successful return this value is normalized + * according to the bitmap info. + * + * @param ColorTableSize + * On successful return this variable is filled with number of + * entries in color table for the image with specified parameters. + * + * @return + * TRUE if the input values together form a valid image, FALSE otherwise. + */ + +BOOL STDCALL +CalculateColorTableSize( + CONST BITMAPINFOHEADER *BitmapInfoHeader, + UINT *ColorSpec, + UINT *ColorTableSize) { - PGDIBRUSHOBJ BrushObject; - HBRUSH hBrush; - HBITMAP hPattern = 0; - - switch (LogBrush->lbStyle) + WORD BitCount; + DWORD ClrUsed; + DWORD Compression; + + /* + * At first get some basic parameters from the passed BitmapInfoHeader + * structure. It can have one of the following formats: + * - BITMAPCOREHEADER (the oldest one with totally different layout + * from the others) + * - BITMAPINFOHEADER (the standard and most common header) + * - BITMAPV4HEADER (extension of BITMAPINFOHEADER) + * - BITMAPV5HEADER (extension of BITMAPV4HEADER) + */ + + if (BitmapInfoHeader->biSize == sizeof(BITMAPCOREHEADER)) { - case BS_HATCHED: - hPattern = NtGdiCreateBitmap(8, 8, 1, 1, HatchBrushes[LogBrush->lbHatch]); - if (hPattern == NULL) - { - SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - break; - - case BS_PATTERN: - hPattern = BITMAPOBJ_CopyBitmap((HBITMAP)LogBrush->lbHatch); - if (hPattern == NULL) - { - SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - break; + BitCount = ((LPBITMAPCOREHEADER)BitmapInfoHeader)->bcBitCount; + ClrUsed = 0; + Compression = BI_RGB; } + else + { + BitCount = BitmapInfoHeader->biBitCount; + ClrUsed = BitmapInfoHeader->biClrUsed; + Compression = BitmapInfoHeader->biCompression; + } + + switch (Compression) + { + case BI_BITFIELDS: + if (*ColorSpec == DIB_PAL_COLORS) + *ColorSpec = DIB_RGB_COLORS; + + if (BitCount != 16 && BitCount != 32) + return FALSE; + + /* + * For BITMAPV4HEADER/BITMAPV5HEADER the masks are included in + * the structure itself (bV4RedMask, bV4GreenMask, and bV4BlueMask). + * For BITMAPINFOHEADER the color masks are stored in the palette. + */ + + if (BitmapInfoHeader->biSize > sizeof(BITMAPINFOHEADER)) + *ColorTableSize = 0; + else + *ColorTableSize = 3; + + return TRUE; + + case BI_RGB: + switch (BitCount) + { + case 1: + *ColorTableSize = ClrUsed ? min(ClrUsed, 2) : 2; + return TRUE; + + case 4: + *ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16; + return TRUE; + + case 8: + *ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256; + return TRUE; + + default: + if (*ColorSpec == DIB_PAL_COLORS) + *ColorSpec = DIB_RGB_COLORS; + if (BitCount != 16 && BitCount != 24 && BitCount != 32) + return FALSE; + *ColorTableSize = ClrUsed; + return TRUE; + } + + case BI_RLE4: + if (BitCount == 4) + { + *ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16; + return TRUE; + } + return FALSE; + + case BI_RLE8: + if (BitCount == 8) + { + *ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256; + return TRUE; + } + return FALSE; + + case BI_JPEG: + case BI_PNG: + *ColorTableSize = ClrUsed; + return TRUE; + + default: + return FALSE; + } +} + +HBRUSH STDCALL +IntGdiCreateDIBBrush( + CONST BITMAPINFO *BitmapInfo, + UINT ColorSpec, + UINT BitmapInfoSize, + CONST VOID *PackedDIB) +{ + HBRUSH hBrush; + PGDIBRUSHOBJ BrushObject; + HBITMAP hPattern; + ULONG_PTR DataPtr; + UINT PaletteEntryCount; + PBITMAPOBJ BitmapObject; + UINT PaletteType; + + if (BitmapInfo->bmiHeader.biSize < sizeof(BITMAPINFOHEADER)) + { + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return NULL; + } + + if (!CalculateColorTableSize(&BitmapInfo->bmiHeader, &ColorSpec, + &PaletteEntryCount)) + { + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return NULL; + } + + DataPtr = (ULONG_PTR)BitmapInfo + BitmapInfo->bmiHeader.biSize; + if (ColorSpec == DIB_RGB_COLORS) + DataPtr += PaletteEntryCount * sizeof(RGBQUAD); + else + DataPtr += PaletteEntryCount * sizeof(USHORT); + + hPattern = NtGdiCreateBitmap(BitmapInfo->bmiHeader.biWidth, + BitmapInfo->bmiHeader.biHeight, + BitmapInfo->bmiHeader.biPlanes, + BitmapInfo->bmiHeader.biBitCount, + (PVOID)DataPtr); + if (hPattern == NULL) + { + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + BitmapObject = BITMAPOBJ_LockBitmap(hPattern); + ASSERT(BitmapObject != NULL); + BitmapObject->hDIBPalette = BuildDIBPalette(BitmapInfo, &PaletteType); + BITMAPOBJ_UnlockBitmap(hPattern); + + hBrush = BRUSHOBJ_AllocBrush(); + if (hBrush == NULL) + { + NtGdiDeleteObject(hPattern); + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + BrushObject = BRUSHOBJ_LockBrush(hBrush); + ASSERT(BrushObject != NULL); + + BrushObject->flAttrs |= GDIBRUSH_IS_BITMAP | GDIBRUSH_IS_DIB; + BrushObject->hbmPattern = hPattern; + /* FIXME: Fill in the rest of fields!!! */ + + GDIOBJ_SetOwnership(hPattern, NULL); + + BRUSHOBJ_UnlockBrush(hBrush); + + return hBrush; +} + +HBRUSH STDCALL +IntGdiCreateHatchBrush( + INT Style, + COLORREF Color) +{ + HBRUSH hBrush; + PGDIBRUSHOBJ BrushObject; + HBITMAP hPattern; + + if (Style < 0 || Style >= NB_HATCH_STYLES) + { + return 0; + } + + hPattern = NtGdiCreateBitmap(8, 8, 1, 1, HatchBrushes[Style]); + if (hPattern == NULL) + { + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + hBrush = BRUSHOBJ_AllocBrush(); + if (hBrush == NULL) + { + NtGdiDeleteObject(hPattern); + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + BrushObject = BRUSHOBJ_LockBrush(hBrush); + ASSERT(BrushObject != NULL); + + BrushObject->flAttrs |= GDIBRUSH_IS_HATCH; + BrushObject->hbmPattern = hPattern; + BrushObject->BrushAttr.lbColor = Color & 0xFFFFFF; + + GDIOBJ_SetOwnership(hPattern, NULL); + + BRUSHOBJ_UnlockBrush(hBrush); + + return hBrush; +} + +HBRUSH STDCALL +IntGdiCreatePatternBrush( + HBITMAP hBitmap) +{ + HBRUSH hBrush; + PGDIBRUSHOBJ BrushObject; + HBITMAP hPattern; + + hPattern = BITMAPOBJ_CopyBitmap(hBitmap); + if (hPattern == NULL) + { + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + hBrush = BRUSHOBJ_AllocBrush(); + if (hBrush == NULL) + { + NtGdiDeleteObject(hPattern); + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + BrushObject = BRUSHOBJ_LockBrush(hBrush); + ASSERT(BrushObject != NULL); + + BrushObject->flAttrs |= GDIBRUSH_IS_BITMAP; + BrushObject->hbmPattern = hPattern; + /* FIXME: Fill in the rest of fields!!! */ + + GDIOBJ_SetOwnership(hPattern, NULL); + + BRUSHOBJ_UnlockBrush(hBrush); + + return hBrush; +} + +HBRUSH STDCALL +IntGdiCreateSolidBrush( + COLORREF Color) +{ + HBRUSH hBrush; + PGDIBRUSHOBJ BrushObject; hBrush = BRUSHOBJ_AllocBrush(); if (hBrush == NULL) @@ -133,43 +395,34 @@ IntGdiCreateBrushIndirect(PLOGBRUSH LogBrush) } BrushObject = BRUSHOBJ_LockBrush(hBrush); - if(BrushObject != NULL) + ASSERT(BrushObject != NULL); + + BrushObject->flAttrs |= GDIBRUSH_IS_SOLID; + BrushObject->BrushAttr.lbColor = Color & 0xFFFFFF; + /* FIXME: Fill in the rest of fields!!! */ + + BRUSHOBJ_UnlockBrush(hBrush); + + return hBrush; +} + +HBRUSH STDCALL +IntGdiCreateNullBrush(VOID) +{ + HBRUSH hBrush; + PGDIBRUSHOBJ BrushObject; + + hBrush = BRUSHOBJ_AllocBrush(); + if (hBrush == NULL) { - switch (LogBrush->lbStyle) - { - case BS_NULL: - BrushObject->flAttrs |= GDIBRUSH_IS_NULL; - break; - - case BS_SOLID: - BrushObject->flAttrs |= GDIBRUSH_IS_SOLID; - BrushObject->BrushAttr.lbColor = LogBrush->lbColor & 0xFFFFFF; - /* FIXME: Fill in the rest of fields!!! */ - break; - - case BS_HATCHED: - BrushObject->flAttrs |= GDIBRUSH_IS_HATCH; - BrushObject->hbmPattern = hPattern; - BrushObject->BrushAttr.lbColor = LogBrush->lbColor & 0xFFFFFF; - break; - - case BS_PATTERN: - BrushObject->flAttrs |= GDIBRUSH_IS_BITMAP; - BrushObject->hbmPattern = hPattern; - /* FIXME: Fill in the rest of fields!!! */ - break; - - default: - DPRINT1("Brush Style: %d\n", LogBrush->lbStyle); - UNIMPLEMENTED; - break; - } - - BRUSHOBJ_UnlockBrush(hBrush); + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return NULL; } - if (hPattern != 0) - GDIOBJ_SetOwnership(hPattern, NULL); + BrushObject = BRUSHOBJ_LockBrush(hBrush); + ASSERT(BrushObject != NULL); + BrushObject->flAttrs |= GDIBRUSH_IS_NULL; + BRUSHOBJ_UnlockBrush(hBrush); return hBrush; } @@ -266,6 +519,12 @@ IntGdiPolyPatBlt( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } for (r = pRects, i = 0; i < cRects; i++) { @@ -293,80 +552,58 @@ IntGdiPolyPatBlt( /* PUBLIC FUNCTIONS ***********************************************************/ HBRUSH STDCALL -NtGdiCreateBrushIndirect(CONST LOGBRUSH *LogBrush) +NtGdiCreateDIBBrush( + CONST BITMAPINFO *BitmapInfoAndData, + UINT ColorSpec, + UINT BitmapInfoSize, + CONST VOID *PackedDIB) { - LOGBRUSH SafeLogBrush; + BITMAPINFO *SafeBitmapInfoAndData; NTSTATUS Status; - - Status = MmCopyFromCaller(&SafeLogBrush, LogBrush, sizeof(LOGBRUSH)); + HBRUSH hBrush; + + SafeBitmapInfoAndData = EngAllocMem(0, BitmapInfoSize, 0); + if (SafeBitmapInfoAndData == NULL) + { + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + Status = MmCopyFromCaller(SafeBitmapInfoAndData, BitmapInfoAndData, + BitmapInfoSize); if (!NT_SUCCESS(Status)) { SetLastNtError(Status); return 0; } - - return IntGdiCreateBrushIndirect(&SafeLogBrush); + + hBrush = IntGdiCreateDIBBrush(SafeBitmapInfoAndData, ColorSpec, + BitmapInfoSize, PackedDIB); + + EngFreeMem(SafeBitmapInfoAndData); + + return hBrush; } HBRUSH STDCALL -NtGdiCreateDIBPatternBrush(HGLOBAL hDIBPacked, UINT ColorSpec) +NtGdiCreateHatchBrush( + INT Style, + COLORREF Color) { - UNIMPLEMENTED; - return 0; + return IntGdiCreateHatchBrush(Style, Color); } HBRUSH STDCALL -NtGdiCreateDIBPatternBrushPt(CONST VOID *PackedDIB, UINT Usage) +NtGdiCreatePatternBrush( + HBITMAP hBitmap) { - UNIMPLEMENTED; - return 0; -} - -HBRUSH STDCALL -NtGdiCreateHatchBrush(INT Style, COLORREF Color) -{ - LOGBRUSH LogBrush; - - if (Style < 0 || Style >= NB_HATCH_STYLES) - { - return 0; - } - - LogBrush.lbStyle = BS_HATCHED; - LogBrush.lbColor = Color; - LogBrush.lbHatch = Style; - - return IntGdiCreateBrushIndirect(&LogBrush); -} - -HBRUSH STDCALL -NtGdiCreatePatternBrush(HBITMAP hBitmap) -{ - LOGBRUSH LogBrush; - - LogBrush.lbStyle = BS_PATTERN; - LogBrush.lbColor = 0; - LogBrush.lbHatch = (ULONG)hBitmap; - - return IntGdiCreateBrushIndirect(&LogBrush); + return IntGdiCreatePatternBrush(hBitmap); } HBRUSH STDCALL NtGdiCreateSolidBrush(COLORREF Color) { - LOGBRUSH LogBrush; - - LogBrush.lbStyle = BS_SOLID; - LogBrush.lbColor = Color; - LogBrush.lbHatch = 0; - - return IntGdiCreateBrushIndirect(&LogBrush); -} - -BOOL STDCALL -NtGdiFixBrushOrgEx(VOID) -{ - return FALSE; + return IntGdiCreateSolidBrush(Color); } /* @@ -466,6 +703,12 @@ NtGdiPatBlt( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } BrushObj = BRUSHOBJ_LockBrush(dc->w.hBrush); if (BrushObj == NULL) diff --git a/reactos/subsys/win32k/objects/color.c b/reactos/subsys/win32k/objects/color.c index 919238add2a..cbeaf8967a2 100644 --- a/reactos/subsys/win32k/objects/color.c +++ b/reactos/subsys/win32k/objects/color.c @@ -287,7 +287,7 @@ UINT STDCALL NtGdiGetSystemPaletteEntries(HDC hDC, UINT STDCALL NtGdiGetSystemPaletteUse(HDC hDC) { - UNIMPLEMENTED; + DPRINT1("NtGdiGetSystemPaletteUse is unimplemented\n"); return 0; } diff --git a/reactos/subsys/win32k/objects/dc.c b/reactos/subsys/win32k/objects/dc.c index 5ef2813bf1e..dfa539cf668 100644 --- a/reactos/subsys/win32k/objects/dc.c +++ b/reactos/subsys/win32k/objects/dc.c @@ -136,7 +136,7 @@ NtGdiCreateCompatableDC(HDC hDC) if (hDC == NULL) { RtlInitUnicodeString(&DriverName, L"DISPLAY"); - DisplayDC = IntGdiCreateDC(&DriverName, NULL, NULL, NULL); + DisplayDC = IntGdiCreateDC(&DriverName, NULL, NULL, NULL, TRUE); if (NULL == DisplayDC) { return NULL; @@ -169,9 +169,9 @@ NtGdiCreateCompatableDC(HDC hDC) /* Copy information from original DC to new DC */ NewDC->hSelf = hNewDC; + NewDC->IsIC = FALSE; NewDC->PDev = OrigDC->PDev; - NewDC->DMW = OrigDC->DMW; memcpy(NewDC->FillPatternSurfaces, OrigDC->FillPatternSurfaces, sizeof OrigDC->FillPatternSurfaces); @@ -454,19 +454,16 @@ SetupDevMode(PDEVMODEW DevMode, ULONG DisplayNumber) return Valid; } -BOOL FASTCALL -IntCreatePrimarySurface() +static BOOL FASTCALL +IntPrepareDriver() { PGD_ENABLEDRIVER GDEnableDriver; DRVENABLEDATA DED; - SURFOBJ *SurfObj; - SIZEL SurfSize; UNICODE_STRING DriverFileNames; PWSTR CurrentName; BOOL GotDriver; BOOL DoDefault; ULONG DisplayNumber; - RECTL SurfaceRect; for (DisplayNumber = 0; ; DisplayNumber++) { @@ -488,7 +485,6 @@ IntCreatePrimarySurface() if (!FindDriverFileNames(&DriverFileNames, DisplayNumber)) { DPRINT1("FindDriverFileNames failed\n"); - /* return FALSE; */ continue; } @@ -543,7 +539,6 @@ IntCreatePrimarySurface() { ObDereferenceObject(PrimarySurface.VideoFileObject); DPRINT1("No suitable DDI driver found\n"); - /* return FALSE; */ continue; } @@ -608,7 +603,6 @@ IntCreatePrimarySurface() ObDereferenceObject(PrimarySurface.VideoFileObject); DPRINT1("DrvEnablePDEV with default parameters failed\n"); DPRINT1("Perhaps DDI driver doesn't match miniport driver?\n"); - /* return FALSE; */ continue; } } @@ -623,7 +617,7 @@ IntCreatePrimarySurface() DPRINT("Adjusting GDIInfo.ulLogPixelsY\n"); PrimarySurface.GDIInfo.ulLogPixelsY = 96; } - + PrimarySurface.Pointer.Exclude.right = -1; DPRINT("calling completePDev\n"); @@ -637,43 +631,67 @@ IntCreatePrimarySurface() DRIVER_ReferenceDriver(L"DISPLAY"); - DPRINT("calling EnableSurface\n"); + PrimarySurface.PreparedDriver = TRUE; + PrimarySurface.DisplayNumber = DisplayNumber; - /* Enable the drawing surface */ - PrimarySurface.Handle = - PrimarySurface.DriverFunctions.EnableSurface(PrimarySurface.PDev); - if (NULL == PrimarySurface.Handle) - { -/* PrimarySurface.DriverFunctions.AssertMode(PrimarySurface.PDev, FALSE);*/ - PrimarySurface.DriverFunctions.DisablePDEV(PrimarySurface.PDev); - ObDereferenceObject(PrimarySurface.VideoFileObject); - DPRINT1("DrvEnableSurface failed\n"); - /* return FALSE; */ - continue; - } - - /* attach monitor */ - IntAttachMonitor(&PrimarySurface, DisplayNumber); - - SurfObj = EngLockSurface((HSURF)PrimarySurface.Handle); - SurfObj->dhpdev = PrimarySurface.PDev; - SurfSize = SurfObj->sizlBitmap; - SurfSize = SurfObj->sizlBitmap; - SurfaceRect.left = SurfaceRect.top = 0; - SurfaceRect.right = SurfObj->sizlBitmap.cx; - SurfaceRect.bottom = SurfObj->sizlBitmap.cy; - /* FIXME - why does EngEraseSurface() sometimes crash? - EngEraseSurface(SurfObj, &SurfaceRect, 0); */ - - /* Put the pointer in the center of the screen */ - GDIDEV(SurfObj)->Pointer.Pos.x = (SurfaceRect.right - SurfaceRect.left) / 2; - GDIDEV(SurfObj)->Pointer.Pos.y = (SurfaceRect.bottom - SurfaceRect.top) / 2; - - EngUnlockSurface(SurfObj); - IntShowDesktop(IntGetActiveDesktop(), SurfSize.cx, SurfSize.cy); - break; + return TRUE; } + return FALSE; +} + +static BOOL FASTCALL +IntPrepareDriverIfNeeded() +{ + return (PrimarySurface.PreparedDriver ? TRUE : IntPrepareDriver()); +} + +BOOL FASTCALL +IntCreatePrimarySurface() +{ + SIZEL SurfSize; + RECTL SurfaceRect; + SURFOBJ *SurfObj; + + if (! IntPrepareDriverIfNeeded()) + { + return FALSE; + } + + DPRINT("calling EnableSurface\n"); + /* Enable the drawing surface */ + PrimarySurface.Handle = + PrimarySurface.DriverFunctions.EnableSurface(PrimarySurface.PDev); + if (NULL == PrimarySurface.Handle) + { +/* PrimarySurface.DriverFunctions.AssertMode(PrimarySurface.PDev, FALSE);*/ + PrimarySurface.DriverFunctions.DisablePDEV(PrimarySurface.PDev); + ObDereferenceObject(PrimarySurface.VideoFileObject); + DPRINT1("DrvEnableSurface failed\n"); + return FALSE; + } + + PrimarySurface.DriverFunctions.AssertMode(PrimarySurface.PDev, TRUE); + + /* attach monitor */ + IntAttachMonitor(&PrimarySurface, PrimarySurface.DisplayNumber); + + SurfObj = EngLockSurface((HSURF)PrimarySurface.Handle); + SurfObj->dhpdev = PrimarySurface.PDev; + SurfSize = SurfObj->sizlBitmap; + SurfaceRect.left = SurfaceRect.top = 0; + SurfaceRect.right = SurfObj->sizlBitmap.cx; + SurfaceRect.bottom = SurfObj->sizlBitmap.cy; + /* FIXME - why does EngEraseSurface() sometimes crash? + EngEraseSurface(SurfObj, &SurfaceRect, 0); */ + + /* Put the pointer in the center of the screen */ + GDIDEV(SurfObj)->Pointer.Pos.x = (SurfaceRect.right - SurfaceRect.left) / 2; + GDIDEV(SurfObj)->Pointer.Pos.y = (SurfaceRect.bottom - SurfaceRect.top) / 2; + + EngUnlockSurface(SurfObj); + IntShowDesktop(IntGetActiveDesktop(), SurfSize.cx, SurfSize.cy); + return TRUE; } @@ -694,6 +712,7 @@ IntDestroyPrimarySurface() PrimarySurface.DriverFunctions.AssertMode(PrimarySurface.PDev, FALSE); PrimarySurface.DriverFunctions.DisableSurface(PrimarySurface.PDev); PrimarySurface.DriverFunctions.DisablePDEV(PrimarySurface.PDev); + PrimarySurface.PreparedDriver = FALSE; DceEmptyCache(); @@ -704,7 +723,8 @@ HDC FASTCALL IntGdiCreateDC(PUNICODE_STRING Driver, PUNICODE_STRING Device, PUNICODE_STRING Output, - CONST PDEVMODEW InitData) + CONST PDEVMODEW InitData, + BOOL CreateAsIC) { HDC hNewDC; PDC NewDC; @@ -714,10 +734,18 @@ IntGdiCreateDC(PUNICODE_STRING Driver, UNICODE_STRING StdDriver; RtlInitUnicodeString(&StdDriver, L"DISPLAY"); - + if (NULL == Driver || 0 == RtlCompareUnicodeString(Driver, &StdDriver, TRUE)) { - if (! IntGraphicsCheck(TRUE)) + if (CreateAsIC) + { + if (! IntPrepareDriverIfNeeded()) + { + DPRINT1("Unable to prepare graphics driver, returning NULL ic\n"); + return NULL; + } + } + else if (! IntGraphicsCheck(TRUE)) { DPRINT1("Unable to initialize graphics, returning NULL dc\n"); return NULL; @@ -733,7 +761,7 @@ IntGdiCreateDC(PUNICODE_STRING Driver, if (Driver != NULL && Driver->Buffer != NULL) { - DPRINT("NAME: %ws\n", Driver); // FIXME: Should not crash if NULL + DPRINT("NAME: %wZ\n", Driver); // FIXME: Should not crash if NULL } /* Allocate a DC object */ @@ -750,7 +778,7 @@ IntGdiCreateDC(PUNICODE_STRING Driver, return NULL; } - NewDC->DMW = PrimarySurface.DMW; + NewDC->IsIC = CreateAsIC; NewDC->DevInfo = &PrimarySurface.DevInfo; NewDC->GDIInfo = &PrimarySurface.GDIInfo; memcpy(NewDC->FillPatternSurfaces, PrimarySurface.FillPatterns, @@ -760,46 +788,45 @@ IntGdiCreateDC(PUNICODE_STRING Driver, NewDC->DriverFunctions = PrimarySurface.DriverFunctions; NewDC->w.hBitmap = PrimarySurface.Handle; - NewDC->DMW.dmSize = sizeof(NewDC->DMW); - NewDC->DMW.dmFields = 0x000fc000; - - /* FIXME: get mode selection information from somewhere */ - - NewDC->DMW.dmLogPixels = 96; - SurfObj = EngLockSurface((HSURF)PrimarySurface.Handle); - if ( !SurfObj ) - { - DC_UnlockDc ( hNewDC ); - DC_FreeDC ( hNewDC) ; - return NULL; - } - NewDC->DMW.dmBitsPerPel = BitsPerFormat(SurfObj->iBitmapFormat); - NewDC->DMW.dmPelsWidth = SurfObj->sizlBitmap.cx; - NewDC->DMW.dmPelsHeight = SurfObj->sizlBitmap.cy; - NewDC->DMW.dmDisplayFlags = 0; - NewDC->DMW.dmDisplayFrequency = 0; - - NewDC->w.bitsPerPixel = NewDC->DMW.dmBitsPerPel; // FIXME: set this here?? - NewDC->w.hPalette = NewDC->DevInfo->hpalDefault; - NewDC->w.ROPmode = R2_COPYPEN; - + NewDC->w.bitsPerPixel = NewDC->GDIInfo->cBitsPixel * NewDC->GDIInfo->cPlanes; DPRINT("Bits per pel: %u\n", NewDC->w.bitsPerPixel); + + if (! CreateAsIC) + { + SurfObj = EngLockSurface((HSURF)PrimarySurface.Handle); + if ( !SurfObj ) + { + DC_UnlockDc ( hNewDC ); + DC_FreeDC ( hNewDC) ; + return NULL; + } + ASSERT(NewDC->GDIInfo->cBitsPixel * NewDC->GDIInfo->cPlanes == BitsPerFormat(SurfObj->iBitmapFormat)); + ASSERT(NewDC->GDIInfo->ulHorzRes == SurfObj->sizlBitmap.cx); + ASSERT(NewDC->GDIInfo->ulVertRes == SurfObj->sizlBitmap.cy); + + NewDC->w.hPalette = NewDC->DevInfo->hpalDefault; + NewDC->w.ROPmode = R2_COPYPEN; - DC_UnlockDc( hNewDC ); + DC_UnlockDc( hNewDC ); - hVisRgn = NtGdiCreateRectRgn(0, 0, SurfObj->sizlBitmap.cx, - SurfObj->sizlBitmap.cy); - NtGdiSelectVisRgn(hNewDC, hVisRgn); - NtGdiDeleteObject(hVisRgn); + hVisRgn = NtGdiCreateRectRgn(0, 0, SurfObj->sizlBitmap.cx, + SurfObj->sizlBitmap.cy); + NtGdiSelectVisRgn(hNewDC, hVisRgn); + NtGdiDeleteObject(hVisRgn); - /* Initialize the DC state */ - DC_InitDC(hNewDC); - NtGdiSetTextColor(hNewDC, RGB(0, 0, 0)); - NtGdiSetTextAlign(hNewDC, TA_TOP); - NtGdiSetBkColor(hNewDC, RGB(255, 255, 255)); - NtGdiSetBkMode(hNewDC, OPAQUE); + /* Initialize the DC state */ + DC_InitDC(hNewDC); + NtGdiSetTextColor(hNewDC, RGB(0, 0, 0)); + NtGdiSetTextAlign(hNewDC, TA_TOP); + NtGdiSetBkColor(hNewDC, RGB(255, 255, 255)); + NtGdiSetBkMode(hNewDC, OPAQUE); - EngUnlockSurface(SurfObj); + EngUnlockSurface(SurfObj); + } + else + { + DC_UnlockDc( hNewDC ); + } return hNewDC; } @@ -847,7 +874,7 @@ NtGdiCreateDC(PUNICODE_STRING Driver, } } - Ret = IntGdiCreateDC(&SafeDriver, &SafeDevice, NULL, &SafeInitData); + Ret = IntGdiCreateDC(&SafeDriver, &SafeDevice, NULL, &SafeInitData, FALSE); return Ret; } @@ -856,10 +883,50 @@ HDC STDCALL NtGdiCreateIC(PUNICODE_STRING Driver, PUNICODE_STRING Device, PUNICODE_STRING Output, - CONST PDEVMODEW DevMode) + CONST PDEVMODEW InitData) { - /* FIXME: this should probably do something else... */ - return NtGdiCreateDC(Driver, Device, Output, DevMode); + UNICODE_STRING SafeDriver, SafeDevice; + DEVMODEW SafeInitData; + HDC Ret; + NTSTATUS Status; + + if(InitData) + { + Status = MmCopyFromCaller(&SafeInitData, InitData, sizeof(DEVMODEW)); + if(!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return NULL; + } + /* FIXME - InitData can have some more bytes! */ + } + + if(Driver) + { + Status = IntSafeCopyUnicodeString(&SafeDriver, Driver); + if(!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return NULL; + } + } + + if(Device) + { + Status = IntSafeCopyUnicodeString(&SafeDevice, Device); + if(!NT_SUCCESS(Status)) + { + RtlFreeUnicodeString(&SafeDriver); + SetLastNtError(Status); + return NULL; + } + } + + Ret = IntGdiCreateDC(NULL == Driver ? NULL : &SafeDriver, + NULL == Device ? NULL : &SafeDevice, NULL, + NULL == InitData ? NULL : &SafeInitData, TRUE); + + return Ret; } BOOL STDCALL @@ -1144,6 +1211,7 @@ NtGdiGetDCState(HDC hDC) newdc->hSelf = hnewdc; newdc->saveLevel = 0; + newdc->IsIC = dc->IsIC; #if 0 PATH_InitGdiPath( &newdc->w.path ); @@ -1800,7 +1868,6 @@ NtGdiSelectObject(HDC hDC, HGDIOBJ hGDIObj) PGDIBRUSHOBJ brush; XLATEOBJ *XlateObj; DWORD objectType; - ULONG NumColors = 0; HRGN hVisRgn; BOOLEAN Failed; @@ -1894,21 +1961,7 @@ NtGdiSelectObject(HDC hDC, HGDIOBJ hGDIObj) if(pb->dib) { dc->w.bitsPerPixel = pb->dib->dsBmih.biBitCount; - - if(pb->dib->dsBmih.biBitCount <= 8) - { - if(pb->dib->dsBmih.biBitCount == 1) { NumColors = 2; } else - if(pb->dib->dsBmih.biBitCount == 4) { NumColors = 16; } else - if(pb->dib->dsBmih.biBitCount == 8) { NumColors = 256; } - dc->w.hPalette = PALETTE_AllocPaletteIndexedRGB(NumColors, pb->ColorMap); - } - else - { - dc->w.hPalette = PALETTE_AllocPalette(PAL_BITFIELDS, 0, NULL, - pb->dib->dsBitfields[0], - pb->dib->dsBitfields[1], - pb->dib->dsBitfields[2]); - } + dc->w.hPalette = pb->hDIBPalette; } else { @@ -2228,26 +2281,28 @@ IntSetDCColor(HDC hDC, ULONG Object, COLORREF Color) /*! \brief Enumerate possible display settings for the given display... * * \todo Make thread safe!? - * \todo Don't ignore lpszDeviceName + * \todo Don't ignore pDeviceName * \todo Implement non-raw mode (only return settings valid for driver and monitor) */ BOOL FASTCALL IntEnumDisplaySettings( - PUNICODE_STRING lpszDeviceName, - DWORD iModeNum, - LPDEVMODEW lpDevMode, - DWORD dwFlags) + IN PUNICODE_STRING pDeviceName OPTIONAL, + IN DWORD iModeNum, + IN OUT LPDEVMODEW pDevMode, + IN DWORD dwFlags) { static DEVMODEW *CachedDevModes = NULL, *CachedDevModesEnd = NULL; static DWORD SizeOfCachedDevModes = 0; - LPDEVMODEW CachedMode = NULL; + PDEVMODEW CachedMode = NULL; DEVMODEW DevMode; INT Size, OldSize; ULONG DisplayNumber = 0; /* only default display supported */ - if (lpDevMode->dmSize != SIZEOF_DEVMODEW_300 && - lpDevMode->dmSize != SIZEOF_DEVMODEW_400 && - lpDevMode->dmSize != SIZEOF_DEVMODEW_500) + DPRINT1("DevMode->dmSize = %d\n", pDevMode->dmSize); + DPRINT1("DevMode->dmExtraSize = %d\n", pDevMode->dmDriverExtra); + if (pDevMode->dmSize != SIZEOF_DEVMODEW_300 && + pDevMode->dmSize != SIZEOF_DEVMODEW_400 && + pDevMode->dmSize != SIZEOF_DEVMODEW_500) { SetLastWin32Error(STATUS_INVALID_PARAMETER); return FALSE; @@ -2256,7 +2311,7 @@ IntEnumDisplaySettings( if (iModeNum == ENUM_CURRENT_SETTINGS) { CachedMode = &PrimarySurface.DMW; - assert(CachedMode->dmSize > 0); + ASSERT(CachedMode->dmSize > 0); } else if (iModeNum == ENUM_REGISTRY_SETTINGS) { @@ -2420,24 +2475,193 @@ IntEnumDisplaySettings( } } - assert(CachedMode != NULL); + ASSERT(CachedMode != NULL); - Size = OldSize = lpDevMode->dmSize; + Size = OldSize = pDevMode->dmSize; if (Size > CachedMode->dmSize) Size = CachedMode->dmSize; - RtlCopyMemory(lpDevMode, CachedMode, Size); - RtlZeroMemory((PCHAR)lpDevMode + Size, OldSize - Size); - lpDevMode->dmSize = OldSize; + RtlCopyMemory(pDevMode, CachedMode, Size); + RtlZeroMemory((PCHAR)pDevMode + Size, OldSize - Size); + pDevMode->dmSize = OldSize; - Size = OldSize = lpDevMode->dmDriverExtra; + Size = OldSize = pDevMode->dmDriverExtra; if (Size > CachedMode->dmDriverExtra) Size = CachedMode->dmDriverExtra; - RtlCopyMemory((PCHAR)lpDevMode + lpDevMode->dmSize, + RtlCopyMemory((PCHAR)pDevMode + pDevMode->dmSize, (PCHAR)CachedMode + CachedMode->dmSize, Size); - RtlZeroMemory((PCHAR)lpDevMode + lpDevMode->dmSize + Size, OldSize - Size); - lpDevMode->dmDriverExtra = OldSize; + RtlZeroMemory((PCHAR)pDevMode + pDevMode->dmSize + Size, OldSize - Size); + pDevMode->dmDriverExtra = OldSize; return TRUE; } +LONG +FASTCALL +IntChangeDisplaySettings( + IN PUNICODE_STRING pDeviceName OPTIONAL, + IN LPDEVMODEW DevMode, + IN DWORD dwflags, + IN PVOID lParam OPTIONAL) +{ + BOOLEAN Global = FALSE; + BOOLEAN NoReset = FALSE; + BOOLEAN Reset = FALSE; + BOOLEAN SetPrimary = FALSE; + LONG Ret; + + if ((dwflags & CDS_UPDATEREGISTRY) == CDS_UPDATEREGISTRY) + { + /* Check global, reset and noreset flags */ + if ((dwflags & CDS_GLOBAL) == CDS_GLOBAL) + Global = TRUE; + if ((dwflags & CDS_NORESET) == CDS_NORESET) + NoReset = TRUE; + dwflags &= ~(CDS_GLOBAL | CDS_NORESET); + } + if ((dwflags & CDS_RESET) == CDS_RESET) + Reset = TRUE; + if ((dwflags & CDS_SET_PRIMARY) == CDS_SET_PRIMARY) + SetPrimary = TRUE; + dwflags &= ~(CDS_RESET | CDS_SET_PRIMARY); + + if (Reset && NoReset) + return DISP_CHANGE_BADFLAGS; + + switch (dwflags) + { + case 0: /* Dynamically change graphics mode */ + Ret = DISP_CHANGE_FAILED; + break; + + case CDS_FULLSCREEN: /* Given mode is temporary */ + Ret = DISP_CHANGE_FAILED; + break; + + case CDS_UPDATEREGISTRY: + { + UNICODE_STRING ObjectName; + UNICODE_STRING KernelModeName; + WCHAR KernelModeNameBuffer[256]; + UNICODE_STRING RegistryKey; + WCHAR RegistryKeyBuffer[512]; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + ULONG LastSlash; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE DevInstRegKey; + ULONG NewValue; + + /* Get device name (pDeviceName is "\.\xxx") */ + for (LastSlash = pDeviceName->Length / sizeof(WCHAR); LastSlash > 0; LastSlash--) + { + if (pDeviceName->Buffer[LastSlash - 1] == L'\\') + break; + } + if (LastSlash == 0) return DISP_CHANGE_FAILED; + ObjectName = *pDeviceName; + ObjectName.Length -= LastSlash * sizeof(WCHAR); + ObjectName.MaximumLength -= LastSlash * sizeof(WCHAR); + ObjectName.Buffer += LastSlash; + + KernelModeName.Length = 0; + KernelModeName.MaximumLength = sizeof(KernelModeNameBuffer); + KernelModeName.Buffer = KernelModeNameBuffer; + + /* Open \??\xxx (ex: "\??\DISPLAY1") */ + Status = RtlAppendUnicodeToString(&KernelModeName, L"\\??\\"); + if (!NT_SUCCESS(Status)) return DISP_CHANGE_FAILED; + Status = RtlAppendUnicodeStringToString(&KernelModeName, &ObjectName); + if (!NT_SUCCESS(Status)) return DISP_CHANGE_FAILED; + Status = ObReferenceObjectByName( + &KernelModeName, + OBJ_CASE_INSENSITIVE, + NULL, + 0, + IoDeviceObjectType, + KernelMode, + NULL, + (PVOID*)&DeviceObject); + if (!NT_SUCCESS(Status)) return DISP_CHANGE_FAILED; + /* Get associated driver name (ex: "VBE") */ + for (LastSlash = DeviceObject->DriverObject->DriverName.Length / sizeof(WCHAR); LastSlash > 0; LastSlash--) + { + if (DeviceObject->DriverObject->DriverName.Buffer[LastSlash - 1] == L'\\') + break; + } + if (LastSlash == 0) { ObDereferenceObject(DeviceObject); return DISP_CHANGE_FAILED; } + ObjectName = DeviceObject->DriverObject->DriverName; + ObjectName.Length -= LastSlash * sizeof(WCHAR); + ObjectName.MaximumLength -= LastSlash * sizeof(WCHAR); + ObjectName.Buffer += LastSlash; + + RegistryKey.Length = 0; + RegistryKey.MaximumLength = sizeof(RegistryKeyBuffer); + RegistryKey.Buffer = RegistryKeyBuffer; + + /* Open registry key */ + Status = RtlAppendUnicodeToString(&RegistryKey, + L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Hardware Profiles\\Current\\System\\CurrentControlSet\\Services\\"); + if (!NT_SUCCESS(Status)) { ObDereferenceObject(DeviceObject); return DISP_CHANGE_FAILED; } + Status = RtlAppendUnicodeStringToString(&RegistryKey, &ObjectName); + if (!NT_SUCCESS(Status)) { ObDereferenceObject(DeviceObject); return DISP_CHANGE_FAILED; } + Status = RtlAppendUnicodeToString(&RegistryKey, + L"\\Device0"); + if (!NT_SUCCESS(Status)) { ObDereferenceObject(DeviceObject); return DISP_CHANGE_FAILED; } + + InitializeObjectAttributes(&ObjectAttributes, &RegistryKey, + OBJ_CASE_INSENSITIVE, NULL, NULL); + Status = ZwOpenKey(&DevInstRegKey, GENERIC_READ | GENERIC_WRITE, &ObjectAttributes); + ObDereferenceObject(DeviceObject); + if (!NT_SUCCESS(Status)) return DISP_CHANGE_FAILED; + + /* Update needed fields */ + if (NT_SUCCESS(Status) && DevMode->dmFields & DM_BITSPERPEL) + { + RtlInitUnicodeString(&RegistryKey, L"DefaultSettings.BitsPerPel"); + NewValue = DevMode->dmBitsPerPel; + Status = ZwSetValueKey(DevInstRegKey, &RegistryKey, 0, REG_DWORD, &NewValue, sizeof(NewValue)); + } + + if (NT_SUCCESS(Status) && DevMode->dmFields & DM_PELSWIDTH) + { + RtlInitUnicodeString(&RegistryKey, L"DefaultSettings.XResolution"); + NewValue = DevMode->dmPelsWidth; + Status = ZwSetValueKey(DevInstRegKey, &RegistryKey, 0, REG_DWORD, &NewValue, sizeof(NewValue)); + } + + if (NT_SUCCESS(Status) && DevMode->dmFields & DM_PELSHEIGHT) + { + RtlInitUnicodeString(&RegistryKey, L"DefaultSettings.YResolution"); + NewValue = DevMode->dmPelsHeight; + Status = ZwSetValueKey(DevInstRegKey, &RegistryKey, 0, REG_DWORD, &NewValue, sizeof(NewValue)); + } + + ZwClose(DevInstRegKey); + if (NT_SUCCESS(Status)) + Ret = DISP_CHANGE_RESTART; + else + Ret = DISP_CHANGE_FAILED; + break; + } + + case CDS_TEST: /* Test if the mode could be set */ + Ret = DISP_CHANGE_FAILED; + break; + +#ifdef CDS_VIDEOPARAMETERS + case CDS_VIDEOPARAMETERS: + if (lParam == NULL) + return DISP_CHANGE_BADPARAM; + Ret = DISP_CHANGE_FAILED; + break; +#endif + + default: + Ret = DISP_CHANGE_BADFLAGS; + break; + } + + return Ret; +} + /* EOF */ diff --git a/reactos/subsys/win32k/objects/dib.c b/reactos/subsys/win32k/objects/dib.c index 831b4353eb5..f203cb212d3 100644 --- a/reactos/subsys/win32k/objects/dib.c +++ b/reactos/subsys/win32k/objects/dib.c @@ -19,14 +19,22 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include +#include UINT STDCALL NtGdiSetDIBColorTable(HDC hDC, UINT StartIndex, UINT Entries, CONST RGBQUAD *Colors) { PDC dc; PBITMAPOBJ BitmapObj; + PPALGDI PalGDI; + UINT Index; if (!(dc = DC_LockDc(hDC))) return 0; + if (dc->IsIC) + { + DC_UnlockDc(hDC); + return 0; + } BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap); if (BitmapObj == NULL) @@ -50,12 +58,24 @@ NtGdiSetDIBColorTable(HDC hDC, UINT StartIndex, UINT Entries, CONST RGBQUAD *Col if (StartIndex + Entries > (1 << BitmapObj->dib->dsBmih.biBitCount)) Entries = (1 << BitmapObj->dib->dsBmih.biBitCount) - StartIndex; - MmCopyFromCaller(BitmapObj->ColorMap + StartIndex, Colors, Entries * sizeof(RGBQUAD)); - - /* Rebuild the palette. */ - NtGdiDeleteObject(dc->w.hPalette); - dc->w.hPalette = PALETTE_AllocPaletteIndexedRGB(1 << BitmapObj->dib->dsBmih.biBitCount, - BitmapObj->ColorMap); + PalGDI = PALETTE_LockPalette(BitmapObj->hDIBPalette); + _SEH_TRY + { + for (Index = StartIndex; + Index < StartIndex + Entries && Index < PalGDI->NumColors; + Index++) + { + PalGDI->IndexedColors[Index].peRed = Colors[Index - StartIndex].rgbRed; + PalGDI->IndexedColors[Index].peGreen = Colors[Index - StartIndex].rgbGreen; + PalGDI->IndexedColors[Index].peBlue = Colors[Index - StartIndex].rgbBlue; + } + } + _SEH_HANDLE + { + Entries = 0; + } + _SEH_END + PALETTE_UnlockPalette(BitmapObj->hDIBPalette); } else Entries = 0; @@ -71,8 +91,15 @@ NtGdiGetDIBColorTable(HDC hDC, UINT StartIndex, UINT Entries, RGBQUAD *Colors) { PDC dc; PBITMAPOBJ BitmapObj; + PPALGDI PalGDI; + UINT Index; if (!(dc = DC_LockDc(hDC))) return 0; + if (dc->IsIC) + { + DC_UnlockDc(hDC); + return 0; + } BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap); if (BitmapObj == NULL) @@ -96,7 +123,24 @@ NtGdiGetDIBColorTable(HDC hDC, UINT StartIndex, UINT Entries, RGBQUAD *Colors) if (StartIndex + Entries > (1 << BitmapObj->dib->dsBmih.biBitCount)) Entries = (1 << BitmapObj->dib->dsBmih.biBitCount) - StartIndex; - MmCopyToCaller(Colors, BitmapObj->ColorMap + StartIndex, Entries * sizeof(RGBQUAD)); + PalGDI = PALETTE_LockPalette(BitmapObj->hDIBPalette); + _SEH_TRY + { + for (Index = StartIndex; + Index < StartIndex + Entries && Index < PalGDI->NumColors; + Index++) + { + Colors[Index - StartIndex].rgbRed = PalGDI->IndexedColors[Index].peRed; + Colors[Index - StartIndex].rgbGreen = PalGDI->IndexedColors[Index].peGreen; + Colors[Index - StartIndex].rgbBlue = PalGDI->IndexedColors[Index].peBlue; + } + } + _SEH_HANDLE + { + Entries = 0; + } + _SEH_END + PALETTE_UnlockPalette(BitmapObj->hDIBPalette); } else Entries = 0; @@ -264,6 +308,11 @@ NtGdiSetDIBits( SetLastWin32Error(ERROR_INVALID_HANDLE); return 0; } + if (Dc->IsIC) + { + DC_UnlockDc(hDC); + return 0; + } Ret = IntSetDIBits(Dc, hBitmap, StartScan, ScanLines, Bits, bmi, ColorUse); @@ -325,6 +374,11 @@ NtGdiGetDIBits(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return 0; } + if (Dc->IsIC) + { + DC_UnlockDc(hDC); + return 0; + } hSourcePalette = Dc->w.hPalette; /* FIXME: This is incorrect. hDestPalette should be something other. */ hDestPalette = Dc->DevInfo->hpalDefault; @@ -599,7 +653,7 @@ LONG STDCALL NtGdiGetBitmapBits(HBITMAP hBitmap, return ret; } -static HBITMAP FASTCALL +HBITMAP FASTCALL IntCreateDIBitmap(PDC Dc, const BITMAPINFOHEADER *header, DWORD init, LPCVOID bits, const BITMAPINFO *data, UINT coloruse) @@ -863,8 +917,13 @@ DIB_CreateDIBSection( if(bi->biBitCount == 4) { Entries = 16; } else if(bi->biBitCount == 8) { Entries = 256; } - bmp->ColorMap = ExAllocatePoolWithTag(PagedPool, sizeof(RGBQUAD)*Entries, TAG_COLORMAP); - RtlCopyMemory(bmp->ColorMap, bmi->bmiColors, sizeof(RGBQUAD)*Entries); + if (Entries) + bmp->hDIBPalette = PALETTE_AllocPaletteIndexedRGB(Entries, bmi->bmiColors); + else + bmp->hDIBPalette = PALETTE_AllocPalette(PAL_BITFIELDS, 0, NULL, + dib->dsBitfields[0], + dib->dsBitfields[1], + dib->dsBitfields[2]); } // Clean up in case of errors @@ -1058,7 +1117,7 @@ DIB_MapPaletteColors(PDC dc, CONST BITMAPINFO* lpbmi) } HPALETTE FASTCALL -BuildDIBPalette (PBITMAPINFO bmi, PINT paletteType) +BuildDIBPalette (CONST BITMAPINFO *bmi, PINT paletteType) { BYTE bits; ULONG ColorCount; diff --git a/reactos/subsys/win32k/objects/fillshap.c b/reactos/subsys/win32k/objects/fillshap.c index 277e855e5aa..ae848136e12 100644 --- a/reactos/subsys/win32k/objects/fillshap.c +++ b/reactos/subsys/win32k/objects/fillshap.c @@ -237,6 +237,12 @@ NtGdiEllipse( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } FillBrush = BRUSHOBJ_LockBrush(dc->w.hBrush); if (NULL == FillBrush) @@ -664,6 +670,12 @@ NtGdiPie(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } FillBrushObj = BRUSHOBJ_LockBrush(dc->w.hBrush); if (NULL == FillBrushObj) @@ -844,6 +856,12 @@ NtGdiPolygon(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); else { + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } Safept = ExAllocatePoolWithTag(PagedPool, sizeof(POINT) * Count, TAG_SHAPE); if(!Safept) SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); @@ -883,6 +901,12 @@ NtGdiPolyPolygon(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if(Count > 0) { @@ -1061,6 +1085,12 @@ NtGdiRectangle(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } ret = IntRectangle ( dc, LeftRect, TopRect, RightRect, BottomRect ); DC_UnlockDc ( hDC ); @@ -1358,6 +1388,12 @@ NtGdiRoundRect( DPRINT1("NtGdiRoundRect() - hDC is invalid\n"); SetLastWin32Error(ERROR_INVALID_HANDLE); } + else if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + ret = TRUE; + } else { ret = IntRoundRect ( dc, LeftRect, TopRect, RightRect, BottomRect, Width, Height ); @@ -1490,6 +1526,12 @@ NtGdiGradientFill( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hdc); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if(!pVertex || !uVertex || !pMesh || !uMesh) { DC_UnlockDc(hdc); diff --git a/reactos/subsys/win32k/objects/gdiobj.c b/reactos/subsys/win32k/objects/gdiobj.c index 70190b918cb..bfb78166199 100644 --- a/reactos/subsys/win32k/objects/gdiobj.c +++ b/reactos/subsys/win32k/objects/gdiobj.c @@ -1216,15 +1216,18 @@ LockHandle: /* dereference the process' object counter */ /* FIXME */ - Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess); - if(NT_SUCCESS(Status)) + if((ULONG_PTR)PrevProcId & ~0x1) { - W32Process = OldProcess->Win32Process; - if(W32Process != NULL) + Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess); + if(NT_SUCCESS(Status)) { - InterlockedDecrement(&W32Process->GDIObjects); + W32Process = OldProcess->Win32Process; + if(W32Process != NULL) + { + InterlockedDecrement(&W32Process->GDIObjects); + } + ObDereferenceObject(OldProcess); } - ObDereferenceObject(OldProcess); } if(NewOwner != NULL) diff --git a/reactos/subsys/win32k/objects/line.c b/reactos/subsys/win32k/objects/line.c index 866e04ff0cb..64ba46b8d8d 100644 --- a/reactos/subsys/win32k/objects/line.c +++ b/reactos/subsys/win32k/objects/line.c @@ -368,6 +368,12 @@ NtGdiArc(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } Ret = IntGdiArc(dc, LeftRect, @@ -404,6 +410,12 @@ NtGdiArcTo(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } // Line from current position to starting point of arc if ( !IntGdiLineTo(dc, XRadial1, YRadial1) ) @@ -466,6 +478,12 @@ NtGdiLineTo(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } Ret = IntGdiLineTo(dc, XEnd, YEnd); @@ -491,6 +509,12 @@ NtGdiMoveToEx(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if(Point) { @@ -526,6 +550,12 @@ NtGdiPolyBezier(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if(Count > 0) { @@ -577,6 +607,12 @@ NtGdiPolyBezierTo(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if(Count > 0) { @@ -639,6 +675,12 @@ NtGdiPolyline(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if(Count >= 2) { @@ -690,6 +732,12 @@ NtGdiPolylineTo(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if(Count > 0) { @@ -743,6 +791,12 @@ NtGdiPolyPolyline(HDC hDC, SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if(Count > 0) { diff --git a/reactos/subsys/win32k/objects/pen.c b/reactos/subsys/win32k/objects/pen.c index d3c4d2aae15..f13451f59fd 100644 --- a/reactos/subsys/win32k/objects/pen.c +++ b/reactos/subsys/win32k/objects/pen.c @@ -63,7 +63,7 @@ IntGdiCreatePenIndirect(PLOGPEN LogPen) break; default: - UNIMPLEMENTED; + DPRINT1("FIXME: IntGdiCreatePenIndirect is UNIMPLEMENTED\n"); } PENOBJ_UnlockPen(hPen); diff --git a/reactos/subsys/win32k/objects/print.c b/reactos/subsys/win32k/objects/print.c index 29dd8beca4c..784a859e593 100644 --- a/reactos/subsys/win32k/objects/print.c +++ b/reactos/subsys/win32k/objects/print.c @@ -158,6 +158,11 @@ NtGdiExtEscape( SetLastWin32Error(ERROR_INVALID_HANDLE); return -1; } + if ( pDC->IsIC ) + { + DC_UnlockDc(hDC); + return 0; + } if ( InSize && UnsafeInData ) { diff --git a/reactos/subsys/win32k/objects/stockobj.c b/reactos/subsys/win32k/objects/stockobj.c index c36e414ce87..59a8a3e42b5 100644 --- a/reactos/subsys/win32k/objects/stockobj.c +++ b/reactos/subsys/win32k/objects/stockobj.c @@ -68,28 +68,6 @@ static HBRUSH SysColorBrushes[NUM_SYSCOLORS]; /* GDI stock objects */ -static LOGBRUSH WhiteBrush = -{ BS_SOLID, RGB(255,255,255), 0 }; - -static LOGBRUSH LtGrayBrush = -/* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */ -{ BS_SOLID, RGB(192,192,192), 0 }; - -static LOGBRUSH GrayBrush = -/* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */ -{ BS_SOLID, RGB(128,128,128), 0 }; - -static LOGBRUSH DkGrayBrush = -/* This is BS_HATCHED, for 1 bitperpixel. This makes the spray work in pbrush */ -/* NB_HATCH_STYLES is an index into HatchBrushes */ -{ BS_HATCHED, RGB(0,0,0), NB_HATCH_STYLES }; - -static LOGBRUSH BlackBrush = -{ BS_SOLID, RGB(0,0,0), 0 }; - -static LOGBRUSH NullBrush = -{ BS_NULL, 0, 0 }; - static LOGPEN WhitePen = { PS_SOLID, { 0, 0 }, RGB(255,255,255) }; @@ -144,12 +122,12 @@ CreateStockObjects(void) /* Create GDI Stock Objects from the logical structures we've defined */ - StockObjects[WHITE_BRUSH] = IntGdiCreateBrushIndirect(&WhiteBrush); - StockObjects[LTGRAY_BRUSH] = IntGdiCreateBrushIndirect(&LtGrayBrush); - StockObjects[GRAY_BRUSH] = IntGdiCreateBrushIndirect(&GrayBrush); - StockObjects[DKGRAY_BRUSH] = IntGdiCreateBrushIndirect(&DkGrayBrush); - StockObjects[BLACK_BRUSH] = IntGdiCreateBrushIndirect(&BlackBrush); - StockObjects[NULL_BRUSH] = IntGdiCreateBrushIndirect(&NullBrush); + StockObjects[WHITE_BRUSH] = IntGdiCreateSolidBrush(RGB(255,255,255)); + StockObjects[LTGRAY_BRUSH] = IntGdiCreateSolidBrush(RGB(192,192,192)); + StockObjects[GRAY_BRUSH] = IntGdiCreateSolidBrush(RGB(128,128,128)); + StockObjects[DKGRAY_BRUSH] = IntGdiCreateSolidBrush(RGB(64,64,64)); + StockObjects[BLACK_BRUSH] = IntGdiCreateSolidBrush(RGB(0,0,0)); + StockObjects[NULL_BRUSH] = IntGdiCreateNullBrush(); StockObjects[WHITE_PEN] = IntGdiCreatePenIndirect(&WhitePen); StockObjects[BLACK_PEN] = IntGdiCreatePenIndirect(&BlackPen); @@ -279,18 +257,14 @@ VOID FASTCALL CreateSysColorObjects(VOID) { UINT i; - LOGBRUSH Brush; LOGPEN Pen; /* Create the syscolor brushes */ - Brush.lbStyle = BS_SOLID; - Brush.lbHatch = 0; for(i = 0; i < NUM_SYSCOLORS; i++) { if(SysColorBrushes[i] == NULL) { - Brush.lbColor = SysColors[i]; - SysColorBrushes[i] = IntGdiCreateBrushIndirect(&Brush); + SysColorBrushes[i] = IntGdiCreateSolidBrush(SysColors[i]); if(SysColorBrushes[i] != NULL) { GDIOBJ_ConvertToStockObj((HGDIOBJ*)&SysColorBrushes[i]); diff --git a/reactos/subsys/win32k/objects/text.c b/reactos/subsys/win32k/objects/text.c index 02f7c13cb45..7d10025c9e4 100644 --- a/reactos/subsys/win32k/objects/text.c +++ b/reactos/subsys/win32k/objects/text.c @@ -427,18 +427,14 @@ IntGetFontRenderMode(LOGFONTW *logfont) { switch(logfont->lfQuality) { - //case ANTIALIASED_QUALITY: - case DEFAULT_QUALITY: - return FT_RENDER_MODE_NORMAL; + case NONANTIALIASED_QUALITY: + return FT_RENDER_MODE_MONO; case DRAFT_QUALITY: return FT_RENDER_MODE_LIGHT; - //case NONANTIALIASED_QUALITY: - case PROOF_QUALITY: - return FT_RENDER_MODE_MONO; - //case CLEARTYPE_QUALITY: - // return FT_RENDER_MODE_LCD; +/* case CLEARTYPE_QUALITY: + return FT_RENDER_MODE_LCD; */ } - return FT_RENDER_MODE_MONO; + return FT_RENDER_MODE_NORMAL; } int @@ -1538,6 +1534,12 @@ NtGdiExtTextOut( SetLastWin32Error(ERROR_INVALID_HANDLE); return FALSE; } + if (dc->IsIC) + { + DC_UnlockDc(hDC); + /* Yes, Windows really returns TRUE in this case */ + return TRUE; + } if (NULL != UnsafeDx && Count > 0) { @@ -2969,6 +2971,12 @@ TextIntRealizeFont(HFONT FontHandle) return STATUS_INVALID_HANDLE; } + if (TextObj->Initialized) + { + TEXTOBJ_UnlockText(FontHandle); + return STATUS_SUCCESS; + } + if (! RtlCreateUnicodeString(&FaceName, TextObj->logfont.lfFaceName)) { TEXTOBJ_UnlockText(FontHandle); @@ -3001,6 +3009,7 @@ TextIntRealizeFont(HFONT FontHandle) } else { + TextObj->Initialized = TRUE; Status = STATUS_SUCCESS; } diff --git a/reactos/tools/hack-coff.c b/reactos/tools/hack-coff.c new file mode 100644 index 00000000000..3617a94de0a --- /dev/null +++ b/reactos/tools/hack-coff.c @@ -0,0 +1,83 @@ +/* + * hack-coff.c - hack the header of an xcoff file to fill in + * a few fields needed by the Open Firmware xcoff loader on + * Power Macs but not initialized by objcopy. + * + * Copyright (C) Paul Mackerras 1997. + * + * 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 +#include +#include +#include +#include +#include "rs6000.h" + +#define AOUT_MAGIC 0x010b + +#define get_16be(x) ((((unsigned char *)(x))[0] << 8) \ + + ((unsigned char *)(x))[1]) +#define put_16be(x, v) (((unsigned char *)(x))[0] = (v) >> 8, \ + ((unsigned char *)(x))[1] = (v) & 0xff) +#define get_32be(x) ((((unsigned char *)(x))[0] << 24) \ + + (((unsigned char *)(x))[1] << 16) \ + + (((unsigned char *)(x))[2] << 8) \ + + ((unsigned char *)(x))[3]) + +int main(int ac, char **av) +{ + int fd; + int i, nsect; + int aoutsz; + struct external_filehdr fhdr; + AOUTHDR aout; + struct external_scnhdr shdr; + + if (ac != 2) { + fprintf(stderr, "Usage: hack-coff coff-file\n"); + exit(1); + } + if ((fd = open(av[1], 2)) == -1) { + perror(av[2]); + exit(1); + } + if (read(fd, &fhdr, sizeof(fhdr)) != sizeof(fhdr)) + goto readerr; + i = get_16be(fhdr.f_magic); + if (i != U802TOCMAGIC && i != U802WRMAGIC && i != U802ROMAGIC) { + fprintf(stderr, "%s: not an xcoff file\n", av[1]); + exit(1); + } + aoutsz = get_16be(fhdr.f_opthdr); + if (read(fd, &aout, aoutsz) != aoutsz) + goto readerr; + nsect = get_16be(fhdr.f_nscns); + for (i = 0; i < nsect; ++i) { + if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr)) + goto readerr; + if (strcmp(shdr.s_name, ".text") == 0) { + put_16be(aout.o_snentry, i+1); + put_16be(aout.o_sntext, i+1); + } else if (strcmp(shdr.s_name, ".data") == 0) { + put_16be(aout.o_sndata, i+1); + } else if (strcmp(shdr.s_name, ".bss") == 0) { + put_16be(aout.o_snbss, i+1); + } + } + put_16be(aout.magic, AOUT_MAGIC); + if (lseek(fd, (long) sizeof(struct external_filehdr), 0) == -1 + || write(fd, &aout, aoutsz) != aoutsz) { + fprintf(stderr, "%s: write error\n", av[1]); + exit(1); + } + close(fd); + exit(0); + +readerr: + fprintf(stderr, "%s: read error or file too short\n", av[1]); + exit(1); +} diff --git a/reactos/tools/make_ctests.c b/reactos/tools/make_ctests.c new file mode 100644 index 00000000000..c57d8af558e --- /dev/null +++ b/reactos/tools/make_ctests.c @@ -0,0 +1,132 @@ +/* make_ctests.c + + * This program is a port of wine project's make_ctests script + +# Script to generate a C file containing a list of tests +# +# Copyright 2002 Alexandre Julliard +# Copyright 2002 Dimitrie O. Paun +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# ***** Keep in sync with tools/winapi/msvcmaker:_generate_testlist_c ***** +*/ + +#include +#include + +const char* header = + "/* Automatically generated file; DO NOT EDIT!! */\n" + "\n" + "/* stdarg.h is needed for Winelib */\n" + "#include \n" + "#include \n" + "#include \n" + "#include \"windef.h\"\n" + "#include \"winbase.h\"\n" + "\n"; + +const char* middle = + "\n" + "struct test\n" + "{\n" + " const char *name;\n" + " void (*func)(void);\n" + "};\n" + "\n" + "static const struct test winetest_testlist[] =\n" + "{\n"; + +const char* end = + " { 0, 0 }\n" + "};\n" + "\n" + "#define WINETEST_WANT_MAIN\n" + "#include \"wine/test.h\"\n" + "\n"; + +char* +basename ( const char* filename ) +{ + const char *p, *p2; + char *out; + size_t out_len; + + if ( filename == NULL ) + { + fprintf ( stderr, "basename() called with null filename\n" ); + return NULL; + } + p = strrchr ( filename, '/' ); + if ( !p ) + p = filename; + else + ++p; + + /* look for backslashes, too... */ + p2 = strrchr ( filename, '\\' ); + if ( p2 > p ) + p = p2 + 1; + + /* find extension... */ + p2 = strrchr ( filename, '.' ); + if ( !p2 ) + p2 = p + strlen(p); + + /* malloc a copy */ + out_len = p2-p; + out = malloc ( out_len+1 ); + if ( out == NULL ) + { + fprintf ( stderr, "malloc() failed\n" ); + return NULL; + } + memmove ( out, p, out_len ); + out[out_len] = '\0'; + return out; +} + +int +main ( int argc, const char** argv ) +{ + size_t i; + + printf ( "%s", header ); + + for ( i = 1; i < argc; i++ ) + { + char* test = basename(argv[i]); + if ( test == NULL ) + return 255; + printf ( "extern void func_%s(void);\n", test ); + free ( test ); + } + + printf ( "%s", middle ); + + for ( i = 1; i < argc; i++ ) + { + char* test = basename(argv[i]); + if ( test == NULL ) + return 255; + printf ( " { \"%s\", func_%s },\n", test, test ); + free ( test ); + } + + printf ( "%s", end ); + + return 0; +} diff --git a/reactos/tools/nci/sysfuncs.lst b/reactos/tools/nci/sysfuncs.lst index f52329eb239..7b76c796e9e 100644 --- a/reactos/tools/nci/sysfuncs.lst +++ b/reactos/tools/nci/sysfuncs.lst @@ -180,7 +180,6 @@ NtSetEaFile 4 NtSetEvent 2 NtSetHighEventPair 1 NtSetHighWaitLowEventPair 1 -NtSetHighWaitLowThread 0 NtSetInformationFile 5 NtSetInformationKey 4 NtSetInformationJobObject 4 @@ -192,7 +191,6 @@ NtSetIntervalProfile 2 NtSetLdtEntries 6 NtSetLowEventPair 1 NtSetLowWaitHighEventPair 1 -NtSetLowWaitHighThread 0 NtSetQuotaInformationFile 4 NtSetSecurityObject 3 NtSetSystemEnvironmentValue 2 diff --git a/reactos/tools/nci/w32ksvc.db b/reactos/tools/nci/w32ksvc.db index f821cb711bf..85212cee508 100644 --- a/reactos/tools/nci/w32ksvc.db +++ b/reactos/tools/nci/w32ksvc.db @@ -22,14 +22,12 @@ NtGdiCopyEnhMetaFile 2 NtGdiCopyMetaFile 2 NtGdiCreateBitmap 5 NtGdiCreateBitmapIndirect 1 -NtGdiCreateBrushIndirect 1 NtGdiCreateColorSpace 1 NtGdiCreateCompatibleBitmap 3 NtGdiCreateCompatableDC 1 NtGdiCreateDC 4 NtGdiCreateDIBitmap 6 -NtGdiCreateDIBPatternBrush 2 -NtGdiCreateDIBPatternBrushPt 2 +NtGdiCreateDIBBrush 4 NtGdiCreateDIBSection 6 NtGdiCreateDiscardableBitmap 3 NtGdiCreateEllipticRgn 4 @@ -81,7 +79,6 @@ NtGdiExtSelectClipRgn 3 NtGdiExtTextOut 8 NtGdiFillPath 1 NtGdiFillRgn 3 -NtGdiFixBrushOrgEx 0 NtGdiFlattenPath 1 NtGdiFloodFill 4 NtGdiFrameRgn 5 diff --git a/reactos/tools/ppc-le2be.c b/reactos/tools/ppc-le2be.c new file mode 100644 index 00000000000..3db03a441ed --- /dev/null +++ b/reactos/tools/ppc-le2be.c @@ -0,0 +1,25 @@ +#include + +int main( int argc, char **argv ) { + char buf[8]; + int rlen, i; + FILE *fin = NULL, *fout = NULL; + + if( argc < 3 ) return 1; + + fin = fopen( argv[1], "rb" ); + + if( fin ) + fout = fopen( argv[2], "wb" ); + if( !fout ) return 1; + + do { + rlen = fread( buf, 1, 8, fin ); + for( i = 7; rlen > 0 && i >= 0; i-- ) fputc( buf[i], fout ); + } + while( rlen == 8 ); + + fclose( fout ); + + return 0; +} diff --git a/reactos/tools/raddr2line.c b/reactos/tools/raddr2line.c new file mode 100644 index 00000000000..fa0bdec2851 --- /dev/null +++ b/reactos/tools/raddr2line.c @@ -0,0 +1,188 @@ +/* + * Usage: raddr2line input-file address/offset + * + * This is a tool and is compiled using the host compiler, + * i.e. on Linux gcc and not mingw-gcc (cross-compiler). + * Therefore we can't include SDK headers and we have to + * duplicate some definitions here. + * Also note that the internal functions are "old C-style", + * returning an int, where a return of 0 means success and + * non-zero is failure. + */ + +#include +#include +#include + +#include "rsym.h" + +size_t fixup_offset ( size_t ImageBase, size_t offset ) +{ + if ( offset >= ImageBase ) + offset -= ImageBase; + return offset; +} + +long +my_atoi ( const char* a ) +{ + int i = 0; + const char* fmt = "%x"; + + if ( *a == '0' ) + { + switch ( *++a ) + { + case 'x': + fmt = "%x"; + ++a; + break; + case 'd': + fmt = "%d"; + ++a; + break; + default: + fmt = "%o"; + break; + } + } + sscanf ( a, fmt, &i ); + return i; +} + +PIMAGE_SECTION_HEADER +find_rossym_section ( PIMAGE_FILE_HEADER PEFileHeader, + PIMAGE_SECTION_HEADER PESectionHeaders ) +{ + size_t i; + for ( i = 0; i < PEFileHeader->NumberOfSections; i++ ) + { + if ( 0 == strcmp ( PESectionHeaders[i].Name, ".rossym" ) ) + return &PESectionHeaders[i]; + } + return NULL; +} + +int +find_and_print_offset ( + void* data, + size_t offset ) +{ + PSYMBOLFILE_HEADER RosSymHeader = (PSYMBOLFILE_HEADER)data; + PROSSYM_ENTRY Entries = (PROSSYM_ENTRY)((char*)data + RosSymHeader->SymbolsOffset); + char* Strings = (char*)data + RosSymHeader->StringsOffset; + size_t symbols = RosSymHeader->SymbolsLength / sizeof(ROSSYM_ENTRY); + size_t i; + + //if ( RosSymHeader->SymbolsOffset ) + + for ( i = 0; i < symbols; i++ ) + { + if ( Entries[i].Address > offset ) + { + if ( !i-- ) + return 1; + else + { + PROSSYM_ENTRY e = &Entries[i]; + printf ( "%s:%lu (%s)", + &Strings[e->FileOffset], + e->SourceLine, + &Strings[e->FunctionOffset] ); + return 0; + } + } + } + return 1; +} + +int +process_data ( const void* FileData, size_t FileSize, size_t offset ) +{ + PIMAGE_DOS_HEADER PEDosHeader; + PIMAGE_FILE_HEADER PEFileHeader; + PIMAGE_OPTIONAL_HEADER PEOptHeader; + PIMAGE_SECTION_HEADER PESectionHeaders; + PIMAGE_SECTION_HEADER PERosSymSectionHeader; + size_t ImageBase; + int res; + + /* Check if MZ header exists */ + PEDosHeader = (PIMAGE_DOS_HEADER)FileData; + if (PEDosHeader->e_magic != IMAGE_DOS_MAGIC || PEDosHeader->e_lfanew == 0L) + { + perror("Input file is not a PE image.\n"); + return 1; + } + + /* Locate PE file header */ + /* sizeof(ULONG) = sizeof(MAGIC) */ + PEFileHeader = (PIMAGE_FILE_HEADER)((char *)FileData + PEDosHeader->e_lfanew + sizeof(ULONG)); + + /* Locate optional header */ + PEOptHeader = (PIMAGE_OPTIONAL_HEADER)(PEFileHeader + 1); + ImageBase = PEOptHeader->ImageBase; + + /* Locate PE section headers */ + PESectionHeaders = (PIMAGE_SECTION_HEADER)((char *) PEOptHeader + PEFileHeader->SizeOfOptionalHeader); + + /* make sure offset is what we want */ + offset = fixup_offset ( ImageBase, offset ); + + /* find rossym section */ + PERosSymSectionHeader = find_rossym_section ( + PEFileHeader, PESectionHeaders ); + if ( !PERosSymSectionHeader ) + { + fprintf ( stderr, "Couldn't find rossym section in executable\n" ); + return 1; + } + res = find_and_print_offset ( (char*)FileData + PERosSymSectionHeader->PointerToRawData, + offset ); + if ( res ) + printf ( "??:0\n" ); + return res; +} + +int +process_file ( const char* file_name, size_t offset ) +{ + void* FileData; + size_t FileSize; + int res = 1; + + FileData = load_file ( file_name, &FileSize ); + if ( !FileData ) + { + fprintf ( stderr, "An error occured loading '%s'\n", file_name ); + } + else + { + res = process_data ( FileData, FileSize, offset ); + free ( FileData ); + } + + return res; +} + +int main ( int argc, const char** argv ) +{ + char* path; + size_t offset; + int res; + + if ( argc != 3 ) + { + fprintf(stderr, "Usage: raddr2line \n"); + exit(1); + } + + path = convert_path ( argv[1] ); + offset = my_atoi ( argv[2] ); + + res = process_file ( path, offset ); + + free ( path ); + + return res; +} diff --git a/reactos/tools/rbuild/backend/mingw/mingw.cpp b/reactos/tools/rbuild/backend/mingw/mingw.cpp index 320e5e19ef9..4e349851d9d 100644 --- a/reactos/tools/rbuild/backend/mingw/mingw.cpp +++ b/reactos/tools/rbuild/backend/mingw/mingw.cpp @@ -418,6 +418,7 @@ MingwBackend::GenerateGlobalVariables () const "=", ProjectNode.non_if_data ); fprintf ( fMakefile, "PROJECT_RCFLAGS = $(PROJECT_CFLAGS)\n" ); + fprintf ( fMakefile, "PROJECT_WIDLFLAGS = $(PROJECT_CFLAGS)\n" ); fprintf ( fMakefile, "PROJECT_LFLAGS = %s\n", GenerateProjectLFLAGS ().c_str () ); fprintf ( fMakefile, "\n" ); @@ -426,12 +427,14 @@ MingwBackend::GenerateGlobalVariables () const bool MingwBackend::IncludeInAllTarget ( const Module& module ) const { - if ( module.type == ObjectLibrary ) + if ( MingwModuleHandler::ReferenceObjects ( module ) ) return false; if ( module.type == BootSector ) return false; if ( module.type == Iso ) return false; + if ( module.type == Test ) + return false; return true; } diff --git a/reactos/tools/rbuild/backend/mingw/modulehandler.cpp b/reactos/tools/rbuild/backend/mingw/modulehandler.cpp index 09e71f7d2d6..03e25149740 100644 --- a/reactos/tools/rbuild/backend/mingw/modulehandler.cpp +++ b/reactos/tools/rbuild/backend/mingw/modulehandler.cpp @@ -198,6 +198,12 @@ MingwModuleHandler::InstanciateHandler ( case Test: handler = new MingwTestModuleHandler ( module ); break; + case RpcServer: + handler = new MingwRpcServerModuleHandler ( module ); + break; + case RpcClient: + handler = new MingwRpcClientModuleHandler ( module ); + break; default: throw UnknownModuleTypeException ( module.node.location, @@ -233,6 +239,17 @@ MingwModuleHandler::GetActualSourceFilename ( return PassThruCacheDirectory ( NormalizeFilename ( basename + ".stubs.c" ), backend->intermediateDirectory ); } + else if ( extension == ".idl" || extension == ".IDL" ) + { + string basename = GetBasename ( filename ); + string newname; + if ( module.type == RpcServer ) + newname = basename + "_s.c"; + else + newname = basename + "_c.c"; + return PassThruCacheDirectory ( NormalizeFilename ( newname ), + backend->intermediateDirectory ); + } else return filename; } @@ -255,26 +272,53 @@ MingwModuleHandler::IsGeneratedFile ( const File& file ) const return ( extension == ".spec" || extension == ".SPEC" ); } +/*static*/ bool +MingwModuleHandler::ReferenceObjects ( + const Module& module ) +{ + if ( module.type == ObjectLibrary ) + return true; + if ( module.type == RpcServer ) + return true; + if ( module.type == RpcClient ) + return true; + return false; +} + string MingwModuleHandler::GetImportLibraryDependency ( const Module& importedModule ) { string dep; - if ( importedModule.type == ObjectLibrary ) + if ( ReferenceObjects ( importedModule ) ) dep = GetTargetMacro ( importedModule ); else dep = GetImportLibraryFilename ( importedModule, NULL ); return dep; } +void +MingwModuleHandler::GetTargets ( const Module& dependencyModule, + string_list& targets ) +{ + if ( dependencyModule.invocations.size () > 0 ) + { + for ( size_t i = 0; i < dependencyModule.invocations.size (); i++ ) + { + Invoke& invoke = *dependencyModule.invocations[i]; + invoke.GetTargets ( targets ); + } + } + else + targets.push_back ( GetImportLibraryDependency ( dependencyModule ) ); +} + void MingwModuleHandler::GetModuleDependencies ( string_list& dependencies ) { size_t iend = module.dependencies.size (); - // TODO FIXME - do we *really* not want to call - // GetDefinitionDependencies() if dependencies.size() == 0 ??? if ( iend == 0 ) return; @@ -282,7 +326,8 @@ MingwModuleHandler::GetModuleDependencies ( { const Dependency& dependency = *module.dependencies[i]; const Module& dependencyModule = *dependency.dependencyModule; - dependencyModule.GetTargets ( dependencies ); + GetTargets ( dependencyModule, + dependencies ); } GetDefinitionDependencies ( dependencies ); } @@ -347,6 +392,13 @@ MingwModuleHandler::GetObjectFilename ( newExtension = ".coff"; else if ( extension == ".spec" || extension == ".SPEC" ) newExtension = ".stubs.o"; + else if ( extension == ".idl" || extension == ".IDL" ) + { + if ( module.type == RpcServer ) + newExtension = "_s.o"; + else + newExtension = "_c.o"; + } else newExtension = ".o"; @@ -737,6 +789,10 @@ MingwModuleHandler::GenerateGccCommand ( string deps = sourceFilename; if ( module.pch && use_pch ) deps += " " + module.pch->header + ".gch"; + + /* WIDL generated headers may be used */ + deps += " " + GetLinkingDependenciesMacro (); + string objectFilename = GetObjectFilename ( sourceFilename, &clean_files ); fprintf ( fMakefile, @@ -801,31 +857,47 @@ MingwModuleHandler::GenerateWindresCommand ( ReplaceExtension ( sourceFilename, ".rci" ); string resFilename = ros_temp + ReplaceExtension ( sourceFilename, ".res" ); - fprintf ( fMakefile, - "%s: %s $(WRC_TARGET) | %s\n", - objectFilename.c_str (), - sourceFilename.c_str (), - GetDirectory ( objectFilename ).c_str () ); - fprintf ( fMakefile, "\t$(ECHO_WRC)\n" ); - fprintf ( fMakefile, - "\t${gcc} -xc -E -DRC_INVOKED ${%s} %s > %s\n", - windresflagsMacro.c_str (), - sourceFilename.c_str (), - rciFilename.c_str () ); - fprintf ( fMakefile, - "\t$(Q)$(WRC_TARGET) ${%s} %s %s\n", - windresflagsMacro.c_str (), - rciFilename.c_str (), - resFilename.c_str () ); - fprintf ( fMakefile, - "\t-@${rm} %s 2>$(NUL)\n", - rciFilename.c_str () ); - fprintf ( fMakefile, - "\t${windres} %s -o $@\n", - resFilename.c_str () ); - fprintf ( fMakefile, - "\t-@${rm} %s 2>$(NUL)\n", - resFilename.c_str () ); + if ( module.useWRC ) + { + fprintf ( fMakefile, + "%s: %s $(WRC_TARGET) | %s\n", + objectFilename.c_str (), + sourceFilename.c_str (), + GetDirectory ( objectFilename ).c_str () ); + fprintf ( fMakefile, "\t$(ECHO_WRC)\n" ); + fprintf ( fMakefile, + "\t${gcc} -xc -E -DRC_INVOKED ${%s} %s > %s\n", + windresflagsMacro.c_str (), + sourceFilename.c_str (), + rciFilename.c_str () ); + fprintf ( fMakefile, + "\t$(Q)$(WRC_TARGET) ${%s} %s %s\n", + windresflagsMacro.c_str (), + rciFilename.c_str (), + resFilename.c_str () ); + fprintf ( fMakefile, + "\t-@${rm} %s 2>$(NUL)\n", + rciFilename.c_str () ); + fprintf ( fMakefile, + "\t${windres} %s -o $@\n", + resFilename.c_str () ); + fprintf ( fMakefile, + "\t-@${rm} %s 2>$(NUL)\n", + resFilename.c_str () ); + } + else + { + fprintf ( fMakefile, + "%s: %s $(WRC_TARGET) | %s\n", + objectFilename.c_str (), + sourceFilename.c_str (), + GetDirectory ( objectFilename ).c_str () ); + fprintf ( fMakefile, "\t$(ECHO_WRC)\n" ); + fprintf ( fMakefile, + "\t${windres} $(%s) %s -o $@\n", + windresflagsMacro.c_str (), + sourceFilename.c_str () ); + } } void @@ -867,6 +939,91 @@ MingwModuleHandler::GenerateWinebuildCommands ( stub_file.c_str () ); } +void +MingwModuleHandler::GenerateWidlCommandsServer ( + const string& sourceFilename, + const string& widlflagsMacro ) +{ + string basename = GetBasename ( sourceFilename ); + + /*string generatedHeaderFilename = PassThruCacheDirectory ( + basename + ".h", + backend->intermediateDirectory ); + CLEAN_FILE(generatedHeaderFilename); + */ + string generatedHeaderFilename = basename + "_s.h"; + CLEAN_FILE(generatedHeaderFilename); + + string generatedServerFilename = PassThruCacheDirectory ( + basename + "_s.c", + backend->intermediateDirectory ); + CLEAN_FILE(generatedServerFilename); + + fprintf ( fMakefile, + "%s %s: %s $(WIDL_TARGET) | %s\n", + generatedServerFilename.c_str (), + generatedHeaderFilename.c_str (), + sourceFilename.c_str (), + GetDirectory ( generatedServerFilename ).c_str () ); + fprintf ( fMakefile, "\t$(ECHO_WIDL)\n" ); + fprintf ( fMakefile, + "\t%s %s -h -H %s -s -S %s %s\n", + "$(Q)$(WIDL_TARGET)", + widlflagsMacro.c_str (), + generatedHeaderFilename.c_str (), + generatedServerFilename.c_str (), + sourceFilename.c_str () ); +} + +void +MingwModuleHandler::GenerateWidlCommandsClient ( + const string& sourceFilename, + const string& widlflagsMacro ) +{ + string basename = GetBasename ( sourceFilename ); + + /*string generatedHeaderFilename = PassThruCacheDirectory ( + basename + ".h", + backend->intermediateDirectory ); + CLEAN_FILE(generatedHeaderFilename); + */ + string generatedHeaderFilename = basename + "_c.h"; + CLEAN_FILE(generatedHeaderFilename); + + string generatedClientFilename = PassThruCacheDirectory ( + basename + "_c.c", + backend->intermediateDirectory ); + CLEAN_FILE(generatedClientFilename); + + fprintf ( fMakefile, + "%s %s: %s $(WIDL_TARGET) | %s\n", + generatedClientFilename.c_str (), + generatedHeaderFilename.c_str (), + sourceFilename.c_str (), + GetDirectory ( generatedClientFilename ).c_str () ); + fprintf ( fMakefile, "\t$(ECHO_WIDL)\n" ); + fprintf ( fMakefile, + "\t%s %s -h -H %s -c -C %s %s\n", + "$(Q)$(WIDL_TARGET)", + widlflagsMacro.c_str (), + generatedHeaderFilename.c_str (), + generatedClientFilename.c_str (), + sourceFilename.c_str () ); +} + +void +MingwModuleHandler::GenerateWidlCommands ( + const string& sourceFilename, + const string& widlflagsMacro ) +{ + if ( module.type == RpcServer ) + GenerateWidlCommandsServer ( sourceFilename, + widlflagsMacro ); + else + GenerateWidlCommandsClient ( sourceFilename, + widlflagsMacro ); +} + void MingwModuleHandler::GenerateCommands ( const string& sourceFilename, @@ -874,7 +1031,8 @@ MingwModuleHandler::GenerateCommands ( const string& cppc, const string& cflagsMacro, const string& nasmflagsMacro, - const string& windresflagsMacro ) + const string& windresflagsMacro, + const string& widlflagsMacro ) { string extension = GetExtension ( sourceFilename ); if ( extension == ".c" || extension == ".C" ) @@ -920,6 +1078,15 @@ MingwModuleHandler::GenerateCommands ( cflagsMacro ); return; } + else if ( extension == ".idl" || extension == ".IDL" ) + { + GenerateWidlCommands ( sourceFilename, + widlflagsMacro ); + GenerateGccCommand ( GetActualSourceFilename ( sourceFilename ), + cc, + cflagsMacro ); + return; + } throw InvalidOperationException ( __FILE__, __LINE__, @@ -1070,7 +1237,8 @@ MingwModuleHandler::GenerateObjectFileTargets ( const string& cppc, const string& cflagsMacro, const string& nasmflagsMacro, - const string& windresflagsMacro ) + const string& windresflagsMacro, + const string& widlflagsMacro ) { size_t i; @@ -1083,7 +1251,8 @@ MingwModuleHandler::GenerateObjectFileTargets ( cppc, cflagsMacro, nasmflagsMacro, - windresflagsMacro ); + windresflagsMacro, + widlflagsMacro ); fprintf ( fMakefile, "\n" ); } @@ -1096,7 +1265,8 @@ MingwModuleHandler::GenerateObjectFileTargets ( cppc, cflagsMacro, nasmflagsMacro, - windresflagsMacro ); + windresflagsMacro, + widlflagsMacro ); } } @@ -1106,7 +1276,8 @@ MingwModuleHandler::GenerateObjectFileTargets ( const string& cppc, const string& cflagsMacro, const string& nasmflagsMacro, - const string& windresflagsMacro ) + const string& windresflagsMacro, + const string& widlflagsMacro ) { if ( module.pch ) { @@ -1136,7 +1307,8 @@ MingwModuleHandler::GenerateObjectFileTargets ( cppc, cflagsMacro, nasmflagsMacro, - windresflagsMacro ); + windresflagsMacro, + widlflagsMacro ); fprintf ( fMakefile, "\n" ); } @@ -1198,7 +1370,7 @@ MingwModuleHandler::GetLinkerMacro () const string MingwModuleHandler::GetModuleTargets ( const Module& module ) { - if ( module.type == ObjectLibrary ) + if ( ReferenceObjects ( module ) ) return GetObjectsMacro ( module ); else return GetTargetFilename ( module, NULL ); @@ -1234,6 +1406,7 @@ MingwModuleHandler::GenerateOtherMacros () cflagsMacro = ssprintf ("%s_CFLAGS", module.name.c_str ()); nasmflagsMacro = ssprintf ("%s_NASMFLAGS", module.name.c_str ()); windresflagsMacro = ssprintf ("%s_RCFLAGS", module.name.c_str ()); + widlflagsMacro = ssprintf ("%s_WIDLFLAGS", module.name.c_str ()); linkerflagsMacro = ssprintf ("%s_LFLAGS", module.name.c_str ()); libsMacro = ssprintf("%s_LIBS", module.name.c_str ()); linkDepsMacro = ssprintf ("%s_LINKDEPS", module.name.c_str ()); @@ -1284,6 +1457,11 @@ MingwModuleHandler::GenerateOtherMacros () "%s += $(PROJECT_RCFLAGS)\n", windresflagsMacro.c_str () ); + fprintf ( + fMakefile, + "%s += $(PROJECT_WIDLFLAGS)\n", + widlflagsMacro.c_str () ); + fprintf ( fMakefile, "%s_LFLAGS += $(PROJECT_LFLAGS) -g\n", @@ -1318,6 +1496,7 @@ MingwModuleHandler::GenerateOtherMacros () // future references to the macros will be to get their values cflagsMacro = ssprintf ("$(%s)", cflagsMacro.c_str ()); nasmflagsMacro = ssprintf ("$(%s)", nasmflagsMacro.c_str ()); + widlflagsMacro = ssprintf ("$(%s)", widlflagsMacro.c_str ()); } void @@ -1338,7 +1517,7 @@ MingwModuleHandler::GenerateRules () module.name.c_str (), GetTargetMacro ( module ).c_str () ); - if ( module.type != ObjectLibrary ) + if ( !ReferenceObjects ( module ) ) { string ar_target ( GenerateArchiveTarget ( ar, objectsMacro ) ); if ( targetMacro != ar_target ) @@ -1351,7 +1530,8 @@ MingwModuleHandler::GenerateRules () cppc, cflagsMacro, nasmflagsMacro, - windresflagsMacro ); + windresflagsMacro, + widlflagsMacro ); } void @@ -1544,6 +1724,18 @@ MingwModuleHandler::GetSpecObjectDependencies ( dependencies.push_back ( stubsDependency ); } +void +MingwModuleHandler::GetWidlObjectDependencies ( + string_list& dependencies, + const string& filename ) const +{ + string basename = GetBasename ( filename ); + string serverDependency = PassThruCacheDirectory ( + NormalizeFilename ( basename + "_s.c" ), + backend->intermediateDirectory ); + dependencies.push_back ( serverDependency ); +} + void MingwModuleHandler::GetDefinitionDependencies ( string_list& dependencies ) const @@ -1558,6 +1750,10 @@ MingwModuleHandler::GetDefinitionDependencies ( { GetSpecObjectDependencies ( dependencies, file.name ); } + if ( extension == ".idl" || extension == ".IDL" ) + { + GetWidlObjectDependencies ( dependencies, file.name ); + } } } @@ -1621,7 +1817,7 @@ void MingwKernelModuleHandler::GenerateKernelModuleTarget () { string targetName ( module.GetTargetName () ); // i.e. "ntoskrnl.exe" - string targetMacro ( GetTargetMacro (module) ); // i.e. "$(NTOSKRNL_TARGET)" + string targetMacro ( GetTargetMacro ( module ) ); // i.e. "$(NTOSKRNL_TARGET)" string workingDirectory = GetWorkingDirectory (); string objectsMacro = GetObjectsMacro ( module ); string linkDepsMacro = GetLinkingDependenciesMacro (); @@ -1740,7 +1936,7 @@ MingwKernelModeDLLModuleHandler::Process () void MingwKernelModeDLLModuleHandler::GenerateKernelModeDLLModuleTarget () { - string targetMacro ( GetTargetMacro (module) ); + string targetMacro ( GetTargetMacro ( module ) ); string workingDirectory = GetWorkingDirectory ( ); string objectsMacro = GetObjectsMacro ( module ); string linkDepsMacro = GetLinkingDependenciesMacro (); @@ -2421,3 +2617,30 @@ MingwTestModuleHandler::GenerateTestModuleTarget () GeneratePhonyTarget(); } } + + +MingwRpcServerModuleHandler::MingwRpcServerModuleHandler ( + const Module& module_ ) + + : MingwModuleHandler ( module_ ) +{ +} + +void +MingwRpcServerModuleHandler::Process () +{ + GenerateRules (); +} + +MingwRpcClientModuleHandler::MingwRpcClientModuleHandler ( + const Module& module_ ) + + : MingwModuleHandler ( module_ ) +{ +} + +void +MingwRpcClientModuleHandler::Process () +{ + GenerateRules (); +} diff --git a/reactos/tools/rbuild/backend/mingw/modulehandler.h b/reactos/tools/rbuild/backend/mingw/modulehandler.h index 69333acdacf..6cb6748b430 100644 --- a/reactos/tools/rbuild/backend/mingw/modulehandler.h +++ b/reactos/tools/rbuild/backend/mingw/modulehandler.h @@ -51,6 +51,7 @@ public: virtual std::string TypeSpecificNasmFlags() { return ""; } void GenerateInvocations () const; void GenerateCleanTarget () const; + static bool ReferenceObjects ( const Module& module ); protected: std::string GetWorkingDirectory () const; std::string GetBasename ( const std::string& filename ) const; @@ -58,6 +59,8 @@ protected: std::string GetModuleArchiveFilename () const; bool IsGeneratedFile ( const File& file ) const; std::string GetImportLibraryDependency ( const Module& importedModule ); + void GetTargets ( const Module& dependencyModule, + string_list& targets ); void GetModuleDependencies ( string_list& dependencies ); std::string GetAllDependencies () const; void GetSourceFilenames ( string_list& list, bool includeGeneratedFiles = true ) const; @@ -122,27 +125,40 @@ private: void GenerateWindresCommand ( const std::string& sourceFilename, const std::string& windresflagsMacro ); void GenerateWinebuildCommands ( const std::string& sourceFilename ); + void GenerateWidlCommandsServer ( + const std::string& sourceFilename, + const std::string& widlflagsMacro ); + void GenerateWidlCommandsClient ( + const std::string& sourceFilename, + const std::string& widlflagsMacro ); + void GenerateWidlCommands ( const std::string& sourceFilename, + const std::string& widlflagsMacro ); void GenerateCommands ( const std::string& sourceFilename, const std::string& cc, const std::string& cppc, const std::string& cflagsMacro, const std::string& nasmflagsMacro, - const std::string& windresflagsMacro ); + const std::string& windresflagsMacro, + const std::string& widlflagsMacro ); void GenerateObjectFileTargets ( const IfableData& data, const std::string& cc, const std::string& cppc, const std::string& cflagsMacro, const std::string& nasmflagsMacro, - const std::string& windresflagsMacro ); + const std::string& windresflagsMacro, + const std::string& widlflagsMacro ); void GenerateObjectFileTargets ( const std::string& cc, const std::string& cppc, const std::string& cflagsMacro, const std::string& nasmflagsMacro, - const std::string& windresflagsMacro ); + const std::string& windresflagsMacro, + const std::string& widlflagsMacro ); std::string GenerateArchiveTarget ( const std::string& ar, const std::string& objs_macro ) const; void GetSpecObjectDependencies ( string_list& dependencies, const std::string& filename ) const; + void GetWidlObjectDependencies ( string_list& dependencies, + const std::string& filename ) const; void GetDefaultDependencies ( string_list& dependencies ) const; void GetInvocationDependencies ( const Module& module, string_list& dependencies ); bool IsWineModule () const; @@ -152,8 +168,14 @@ private: public: const Module& module; string_list clean_files; - std::string cflagsMacro, nasmflagsMacro, windresflagsMacro, - linkerflagsMacro, objectsMacro, libsMacro, linkDepsMacro; + std::string cflagsMacro; + std::string nasmflagsMacro; + std::string windresflagsMacro; + std::string widlflagsMacro; + std::string linkerflagsMacro; + std::string objectsMacro; + std::string libsMacro; + std::string linkDepsMacro; }; @@ -322,6 +344,7 @@ private: void OutputCdfileCopyCommands ( const std::string& bootcdDirectory ); }; + class MingwTestModuleHandler : public MingwModuleHandler { public: @@ -332,4 +355,22 @@ private: void GenerateTestModuleTarget (); }; + +class MingwRpcServerModuleHandler : public MingwModuleHandler +{ +public: + MingwRpcServerModuleHandler ( const Module& module ); + virtual HostType DefaultHost() { return HostFalse; } + virtual void Process (); +}; + + +class MingwRpcClientModuleHandler : public MingwModuleHandler +{ +public: + MingwRpcClientModuleHandler ( const Module& module ); + virtual HostType DefaultHost() { return HostFalse; } + virtual void Process (); +}; + #endif /* MINGW_MODULEHANDLER_H */ diff --git a/reactos/tools/rbuild/bootstrap.cpp b/reactos/tools/rbuild/bootstrap.cpp index c4d984206e2..cf3639fad2a 100644 --- a/reactos/tools/rbuild/bootstrap.cpp +++ b/reactos/tools/rbuild/bootstrap.cpp @@ -40,6 +40,8 @@ Bootstrap::IsSupportedModuleType ( ModuleType type ) case ObjectLibrary: case Iso: case Test: + case RpcServer: + case RpcClient: return false; } throw InvalidOperationException ( __FILE__, diff --git a/reactos/tools/rbuild/module.cpp b/reactos/tools/rbuild/module.cpp index 8567a760916..8bfd0353437 100644 --- a/reactos/tools/rbuild/module.cpp +++ b/reactos/tools/rbuild/module.cpp @@ -216,6 +216,12 @@ Module::Module ( const Project& project, installName = att->value; else installName = ""; + + att = moduleNode.GetAttribute ( "usewrc", false ); + if ( att != NULL ) + useWRC = att->value == "true"; + else + useWRC = true; } Module::~Module () @@ -450,6 +456,10 @@ Module::GetModuleType ( const string& location, const XMLAttribute& attribute ) return Iso; if ( attribute.value == "test" ) return Test; + if ( attribute.value == "rpcserver" ) + return RpcServer; + if ( attribute.value == "rpcclient" ) + return RpcClient; throw InvalidAttributeValueException ( location, attribute.name, attribute.value ); @@ -484,6 +494,10 @@ Module::GetDefaultModuleExtension () const return ".iso"; case Test: return ".exe"; + case RpcServer: + return ".o"; + case RpcClient: + return ".o"; } throw InvalidOperationException ( __FILE__, __LINE__ ); @@ -517,6 +531,8 @@ Module::GetDefaultModuleEntrypoint () const case BootLoader: case BootSector: case Iso: + case RpcServer: + case RpcClient: return ""; } throw InvalidOperationException ( __FILE__, @@ -551,6 +567,8 @@ Module::GetDefaultModuleBaseaddress () const case BootLoader: case BootSector: case Iso: + case RpcServer: + case RpcClient: return ""; } throw InvalidOperationException ( __FILE__, @@ -601,21 +619,6 @@ Module::GetPathWithPrefix ( const string& prefix ) const return path + CSEP + prefix + GetTargetName (); } -void -Module::GetTargets ( string_list& targets ) const -{ - if ( invocations.size () > 0 ) - { - for ( size_t i = 0; i < invocations.size (); i++ ) - { - Invoke& invoke = *invocations[i]; - invoke.GetTargets ( targets ); - } - } - else - targets.push_back ( GetPath () ); -} - string Module::GetInvocationTarget ( const int index ) const { diff --git a/reactos/tools/rbuild/rbuild.h b/reactos/tools/rbuild/rbuild.h index aa3f185e175..b52ff471c6e 100644 --- a/reactos/tools/rbuild/rbuild.h +++ b/reactos/tools/rbuild/rbuild.h @@ -137,7 +137,9 @@ enum ModuleType BootLoader, BootSector, Iso, - Test + Test, + RpcServer, + RpcClient }; enum HostType @@ -173,6 +175,7 @@ public: HostType host; std::string installBase; std::string installName; + bool useWRC; Module ( const Project& project, const XMLElement& moduleNode, diff --git a/reactos/tools/rbuild/rbuild.txt b/reactos/tools/rbuild/rbuild.txt index 9bd140c5b7c..e707ac08f26 100644 --- a/reactos/tools/rbuild/rbuild.txt +++ b/reactos/tools/rbuild/rbuild.txt @@ -107,7 +107,7 @@ Module element There can be zero or more modules per xml build file. Syntax: - + ... @@ -120,6 +120,7 @@ Attributes: mangledsymbols - Controls wether or not to pass --kill-at to dlltool. If this attribute has the value false then --kill-at is passed to dlltool. If the value is true, then --kill-at is not passed to dlltool. If the generated file exports C++ classes then this need to be true. installbase - Base directory of the generated file in the installation directory. This attribute is optional. installname - Name of generated file in the installation directory. This attribute is optional, but if not specified, the generated file is not copied to the installation directory. + usewrc - Use WRC to compile resources if true. If false, windres is used. This attribute is optional. If not specified, WRC will be used. Value: None. @@ -145,6 +146,8 @@ The module type determines the actions that is to be carried out to process the bootsector - Builds one or more bootsector binaries. The entrypoint, baseaddress, and mangledsymbols module attributes are not applicable for this module type. iso - Builds a bootable CD. The entrypoint, baseaddress, and mangledsymbols module attributes are not applicable for this module type. test - Builds a testsuite. Default extension is .exe. Default entrypoint is _mainCRTStartup. The baseaddress module attribute is not applicable for this module type. + rpcserver - Generates and builds server code for an RPC interface. Default extension is .o. The entrypoint, baseaddress, and mangledsymbols module attributes are not applicable for this module type. + rpcclient - Generates and builds client code for an RPC interface. Default extension is .o. The entrypoint, baseaddress, and mangledsymbols module attributes are not applicable for this module type. Bootstrap element diff --git a/reactos/tools/rs6000.h b/reactos/tools/rs6000.h new file mode 100644 index 00000000000..433f45084e4 --- /dev/null +++ b/reactos/tools/rs6000.h @@ -0,0 +1,243 @@ +/* IBM RS/6000 "XCOFF" file definitions for BFD. + Copyright (C) 1990, 1991 Free Software Foundation, Inc. + FIXME: Can someone provide a transliteration of this name into ASCII? + Using the following chars caused a compiler warning on HIUX (so I replaced + them with octal escapes), and isn't useful without an understanding of what + character set it is. + Written by Mimi Ph\373\364ng-Th\345o V\365 of IBM + and John Gilmore of Cygnus Support. */ + +/********************** FILE HEADER **********************/ + +struct external_filehdr { + char f_magic[2]; /* magic number */ + char f_nscns[2]; /* number of sections */ + char f_timdat[4]; /* time & date stamp */ + char f_symptr[4]; /* file pointer to symtab */ + char f_nsyms[4]; /* number of symtab entries */ + char f_opthdr[2]; /* sizeof(optional hdr) */ + char f_flags[2]; /* flags */ +}; + + /* IBM RS/6000 */ +#define U802WRMAGIC 0730 /* writeable text segments **chh** */ +#define U802ROMAGIC 0735 /* readonly sharable text segments */ +#define U802TOCMAGIC 0737 /* readonly text segments and TOC */ + +#define BADMAG(x) \ + ((x).f_magic != U802ROMAGIC && (x).f_magic != U802WRMAGIC && \ + (x).f_magic != U802TOCMAGIC) + +#define FILHDR struct external_filehdr +#define FILHSZ 20 + + +/********************** AOUT "OPTIONAL HEADER" **********************/ + + +typedef struct +{ + unsigned char magic[2]; /* type of file */ + unsigned char vstamp[2]; /* version stamp */ + unsigned char tsize[4]; /* text size in bytes, padded to FW bdry */ + unsigned char dsize[4]; /* initialized data " " */ + unsigned char bsize[4]; /* uninitialized data " " */ + unsigned char entry[4]; /* entry pt. */ + unsigned char text_start[4]; /* base of text used for this file */ + unsigned char data_start[4]; /* base of data used for this file */ + unsigned char o_toc[4]; /* address of TOC */ + unsigned char o_snentry[2]; /* section number of entry point */ + unsigned char o_sntext[2]; /* section number of .text section */ + unsigned char o_sndata[2]; /* section number of .data section */ + unsigned char o_sntoc[2]; /* section number of TOC */ + unsigned char o_snloader[2]; /* section number of .loader section */ + unsigned char o_snbss[2]; /* section number of .bss section */ + unsigned char o_algntext[2]; /* .text alignment */ + unsigned char o_algndata[2]; /* .data alignment */ + unsigned char o_modtype[2]; /* module type (??) */ + unsigned char o_cputype[2]; /* cpu type */ + unsigned char o_maxstack[4]; /* max stack size (??) */ + unsigned char o_maxdata[4]; /* max data size (??) */ + unsigned char o_resv2[12]; /* reserved */ +} +AOUTHDR; + +#define AOUTSZ 72 +#define SMALL_AOUTSZ (28) +#define AOUTHDRSZ 72 + +#define RS6K_AOUTHDR_OMAGIC 0x0107 /* old: text & data writeable */ +#define RS6K_AOUTHDR_NMAGIC 0x0108 /* new: text r/o, data r/w */ +#define RS6K_AOUTHDR_ZMAGIC 0x010B /* paged: text r/o, both page-aligned */ + + +/********************** SECTION HEADER **********************/ + + +struct external_scnhdr { + char s_name[8]; /* section name */ + char s_paddr[4]; /* physical address, aliased s_nlib */ + char s_vaddr[4]; /* virtual address */ + char s_size[4]; /* section size */ + char s_scnptr[4]; /* file ptr to raw data for section */ + char s_relptr[4]; /* file ptr to relocation */ + char s_lnnoptr[4]; /* file ptr to line numbers */ + char s_nreloc[2]; /* number of relocation entries */ + char s_nlnno[2]; /* number of line number entries*/ + char s_flags[4]; /* flags */ +}; + +/* + * names of "special" sections + */ +#define _TEXT ".text" +#define _DATA ".data" +#define _BSS ".bss" +#define _PAD ".pad" +#define _LOADER ".loader" + +#define SCNHDR struct external_scnhdr +#define SCNHSZ 40 + +/* XCOFF uses a special .loader section with type STYP_LOADER. */ +#define STYP_LOADER 0x1000 + +/* XCOFF uses a special .debug section with type STYP_DEBUG. */ +#define STYP_DEBUG 0x2000 + +/* XCOFF handles line number or relocation overflow by creating + another section header with STYP_OVRFLO set. */ +#define STYP_OVRFLO 0x8000 + +/********************** LINE NUMBERS **********************/ + +/* 1 line number entry for every "breakpointable" source line in a section. + * Line numbers are grouped on a per function basis; first entry in a function + * grouping will have l_lnno = 0 and in place of physical address will be the + * symbol table index of the function name. + */ +struct external_lineno { + union { + char l_symndx[4]; /* function name symbol index, iff l_lnno == 0*/ + char l_paddr[4]; /* (physical) address of line number */ + } l_addr; + char l_lnno[2]; /* line number */ +}; + + +#define LINENO struct external_lineno +#define LINESZ 6 + + +/********************** SYMBOLS **********************/ + +#define E_SYMNMLEN 8 /* # characters in a symbol name */ +#define E_FILNMLEN 14 /* # characters in a file name */ +#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ + +struct external_syment +{ + union { + char e_name[E_SYMNMLEN]; + struct { + char e_zeroes[4]; + char e_offset[4]; + } e; + } e; + char e_value[4]; + char e_scnum[2]; + char e_type[2]; + char e_sclass[1]; + char e_numaux[1]; +}; + + + +#define N_BTMASK (017) +#define N_TMASK (060) +#define N_BTSHFT (4) +#define N_TSHIFT (2) + + +union external_auxent { + struct { + char x_tagndx[4]; /* str, un, or enum tag indx */ + union { + struct { + char x_lnno[2]; /* declaration line number */ + char x_size[2]; /* str/union/array size */ + } x_lnsz; + char x_fsize[4]; /* size of function */ + } x_misc; + union { + struct { /* if ISFCN, tag, or .bb */ + char x_lnnoptr[4]; /* ptr to fcn line # */ + char x_endndx[4]; /* entry ndx past block end */ + } x_fcn; + struct { /* if ISARY, up to 4 dimen. */ + char x_dimen[E_DIMNUM][2]; + } x_ary; + } x_fcnary; + char x_tvndx[2]; /* tv index */ + } x_sym; + + union { + char x_fname[E_FILNMLEN]; + struct { + char x_zeroes[4]; + char x_offset[4]; + } x_n; + } x_file; + + struct { + char x_scnlen[4]; /* section length */ + char x_nreloc[2]; /* # relocation entries */ + char x_nlinno[2]; /* # line numbers */ + } x_scn; + + struct { + char x_tvfill[4]; /* tv fill value */ + char x_tvlen[2]; /* length of .tv */ + char x_tvran[2][2]; /* tv range */ + } x_tv; /* info about .tv section (in auxent of symbol .tv)) */ + + struct { + unsigned char x_scnlen[4]; + unsigned char x_parmhash[4]; + unsigned char x_snhash[2]; + unsigned char x_smtyp[1]; + unsigned char x_smclas[1]; + unsigned char x_stab[4]; + unsigned char x_snstab[2]; + } x_csect; + +}; + +#define SYMENT struct external_syment +#define SYMESZ 18 +#define AUXENT union external_auxent +#define AUXESZ 18 +#define DBXMASK 0x80 /* for dbx storage mask */ +#define SYMNAME_IN_DEBUG(symptr) ((symptr)->n_sclass & DBXMASK) + + + +/********************** RELOCATION DIRECTIVES **********************/ + + +struct external_reloc { + char r_vaddr[4]; + char r_symndx[4]; + char r_size[1]; + char r_type[1]; +}; + + +#define RELOC struct external_reloc +#define RELSZ 10 + +#define DEFAULT_DATA_SECTION_ALIGNMENT 4 +#define DEFAULT_BSS_SECTION_ALIGNMENT 4 +#define DEFAULT_TEXT_SECTION_ALIGNMENT 4 +/* For new sections we havn't heard of before */ +#define DEFAULT_SECTION_ALIGNMENT 4 diff --git a/reactos/tools/rsym.c b/reactos/tools/rsym.c index 21885b4e985..46405bf0921 100644 --- a/reactos/tools/rsym.c +++ b/reactos/tools/rsym.c @@ -24,287 +24,7 @@ #include #include -#define IMAGE_DOS_MAGIC 0x5a4d -#define IMAGE_PE_MAGIC 0x00004550 - -#define IMAGE_SIZEOF_SHORT_NAME 8 - -#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 - -typedef unsigned char BYTE; -typedef unsigned short WORD; -typedef unsigned long DWORD; -typedef signed long LONG; -typedef unsigned long ULONG; -#if defined(_WIN64) -typedef unsigned __int64 ULONG_PTR; -#else -typedef unsigned long ULONG_PTR; -#endif - -#pragma pack(push,2) -typedef struct _IMAGE_DOS_HEADER { - WORD e_magic; - WORD e_cblp; - WORD e_cp; - WORD e_crlc; - WORD e_cparhdr; - WORD e_minalloc; - WORD e_maxalloc; - WORD e_ss; - WORD e_sp; - WORD e_csum; - WORD e_ip; - WORD e_cs; - WORD e_lfarlc; - WORD e_ovno; - WORD e_res[4]; - WORD e_oemid; - WORD e_oeminfo; - WORD e_res2[10]; - LONG e_lfanew; -} IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER; -#pragma pack(pop) - -#define IMAGE_FILE_LINE_NUMS_STRIPPED 4 -#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 8 -#define IMAGE_FILE_DEBUG_STRIPPED 512 - -#pragma pack(push,4) -typedef struct _IMAGE_FILE_HEADER { - WORD Machine; - WORD NumberOfSections; - DWORD TimeDateStamp; - DWORD PointerToSymbolTable; - DWORD NumberOfSymbols; - WORD SizeOfOptionalHeader; - WORD Characteristics; -} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; - -typedef struct _IMAGE_DATA_DIRECTORY { - DWORD VirtualAddress; - DWORD Size; -} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY; - -#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 - -typedef struct _IMAGE_OPTIONAL_HEADER { - WORD Magic; - BYTE MajorLinkerVersion; - BYTE MinorLinkerVersion; - DWORD SizeOfCode; - DWORD SizeOfInitializedData; - DWORD SizeOfUninitializedData; - DWORD AddressOfEntryPoint; - DWORD BaseOfCode; - DWORD BaseOfData; - DWORD ImageBase; - DWORD SectionAlignment; - DWORD FileAlignment; - WORD MajorOperatingSystemVersion; - WORD MinorOperatingSystemVersion; - WORD MajorImageVersion; - WORD MinorImageVersion; - WORD MajorSubsystemVersion; - WORD MinorSubsystemVersion; - DWORD Reserved1; - DWORD SizeOfImage; - DWORD SizeOfHeaders; - DWORD CheckSum; - WORD Subsystem; - WORD DllCharacteristics; - DWORD SizeOfStackReserve; - DWORD SizeOfStackCommit; - DWORD SizeOfHeapReserve; - DWORD SizeOfHeapCommit; - DWORD LoaderFlags; - DWORD NumberOfRvaAndSizes; - IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; -} IMAGE_OPTIONAL_HEADER,*PIMAGE_OPTIONAL_HEADER; - -#define IMAGE_SCN_TYPE_NOLOAD 0x00000002 -#define IMAGE_SCN_LNK_REMOVE 0x00000800 -#define IMAGE_SCN_MEM_READ 0x40000000 -#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 - -typedef struct _IMAGE_SECTION_HEADER { - BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; - union { - DWORD PhysicalAddress; - DWORD VirtualSize; - } Misc; - DWORD VirtualAddress; - DWORD SizeOfRawData; - DWORD PointerToRawData; - DWORD PointerToRelocations; - DWORD PointerToLinenumbers; - WORD NumberOfRelocations; - WORD NumberOfLinenumbers; - DWORD Characteristics; -} IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER; - -typedef struct _IMAGE_BASE_RELOCATION { - DWORD VirtualAddress; - DWORD SizeOfBlock; -} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION; - - -typedef struct { - unsigned short f_magic; /* magic number */ - unsigned short f_nscns; /* number of sections */ - unsigned long f_timdat; /* time & date stamp */ - unsigned long f_symptr; /* file pointer to symtab */ - unsigned long f_nsyms; /* number of symtab entries */ - unsigned short f_opthdr; /* sizeof(optional hdr) */ - unsigned short f_flags; /* flags */ -} FILHDR; - -typedef struct { - char s_name[8]; /* section name */ - unsigned long s_paddr; /* physical address, aliased s_nlib */ - unsigned long s_vaddr; /* virtual address */ - unsigned long s_size; /* section size */ - unsigned long s_scnptr; /* file ptr to raw data for section */ - unsigned long s_relptr; /* file ptr to relocation */ - unsigned long s_lnnoptr; /* file ptr to line numbers */ - unsigned short s_nreloc; /* number of relocation entries */ - unsigned short s_nlnno; /* number of line number entries */ - unsigned long s_flags; /* flags */ -} SCNHDR; -#pragma pack(pop) - -typedef struct _SYMBOLFILE_HEADER { - unsigned long SymbolsOffset; - unsigned long SymbolsLength; - unsigned long StringsOffset; - unsigned long StringsLength; -} SYMBOLFILE_HEADER, *PSYMBOLFILE_HEADER; - -typedef struct _STAB_ENTRY { - unsigned long n_strx; /* index into string table of name */ - unsigned char n_type; /* type of symbol */ - unsigned char n_other; /* misc info (usually empty) */ - unsigned short n_desc; /* description field */ - unsigned long n_value; /* value of symbol */ -} STAB_ENTRY, *PSTAB_ENTRY; - -#define N_FUN 0x24 -#define N_SLINE 0x44 -#define N_SO 0x64 - -/* COFF symbol table */ - -#define E_SYMNMLEN 8 /* # characters in a symbol name */ -#define E_FILNMLEN 14 /* # characters in a file name */ -#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ - -#define N_BTMASK (0xf) -#define N_TMASK (0x30) -#define N_BTSHFT (4) -#define N_TSHIFT (2) - -/* derived types, in e_type */ -#define DT_NON (0) /* no derived type */ -#define DT_PTR (1) /* pointer */ -#define DT_FCN (2) /* function */ -#define DT_ARY (3) /* array */ - -#define BTYPE(x) ((x) & N_BTMASK) - -#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT)) -#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT)) -#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT)) -#define ISTAG(x) ((x)==C_STRTAG||(x)==C_UNTAG||(x)==C_ENTAG) -#define DECREF(x) ((((x)>>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK)) - -#define C_EFCN 0xff /* physical end of function */ -#define C_NULL 0 -#define C_AUTO 1 /* automatic variable */ -#define C_EXT 2 /* external symbol */ -#define C_STAT 3 /* static */ -#define C_REG 4 /* register variable */ -#define C_EXTDEF 5 /* external definition */ -#define C_LABEL 6 /* label */ -#define C_ULABEL 7 /* undefined label */ -#define C_MOS 8 /* member of structure */ -#define C_ARG 9 /* function argument */ -#define C_STRTAG 10 /* structure tag */ -#define C_MOU 11 /* member of union */ -#define C_UNTAG 12 /* union tag */ -#define C_TPDEF 13 /* type definition */ -#define C_USTATIC 14 /* undefined static */ -#define C_ENTAG 15 /* enumeration tag */ -#define C_MOE 16 /* member of enumeration */ -#define C_REGPARM 17 /* register parameter */ -#define C_FIELD 18 /* bit field */ -#define C_AUTOARG 19 /* auto argument */ -#define C_LASTENT 20 /* dummy entry (end of block) */ -#define C_BLOCK 100 /* ".bb" or ".eb" */ -#define C_FCN 101 /* ".bf" or ".ef" */ -#define C_EOS 102 /* end of structure */ -#define C_FILE 103 /* file name */ -#define C_LINE 104 /* line # reformatted as symbol table entry */ -#define C_ALIAS 105 /* duplicate tag */ -#define C_HIDDEN 106 /* ext symbol in dmert public lib */ - -#pragma pack(push,1) -typedef struct _COFF_SYMENT -{ - union - { - char e_name[E_SYMNMLEN]; - struct - { - unsigned long e_zeroes; - unsigned long e_offset; - } - e; - } - e; - unsigned long e_value; - short e_scnum; - unsigned short e_type; - unsigned char e_sclass; - unsigned char e_numaux; -} COFF_SYMENT, *PCOFF_SYMENT; -#pragma pack(pop) - -typedef struct _ROSSYM_ENTRY { - ULONG_PTR Address; - ULONG FunctionOffset; - ULONG FileOffset; - ULONG SourceLine; -} ROSSYM_ENTRY, *PROSSYM_ENTRY; - -#define ROUND_UP(N, S) (((N) + (S) - 1) & ~((S) - 1)) - -char* convert_path(char* origpath) -{ - char* newpath; - int i; - - newpath = strdup(origpath); - - i = 0; - while (newpath[i] != 0) - { -#ifdef UNIX_PATHS - if (newpath[i] == '\\') - { - newpath[i] = '/'; - } -#else -#ifdef DOS_PATHS - if (newpath[i] == '/') - { - newpath[i] = '\\'; - } -#endif -#endif - i++; - } - return(newpath); -} +#include "rsym.h" static int CompareSymEntry(const PROSSYM_ENTRY SymEntry1, const PROSSYM_ENTRY SymEntry2) @@ -342,7 +62,7 @@ GetStabInfo(void *FileData, PIMAGE_FILE_HEADER PEFileHeader, if ((strncmp((char*)PESectionHeaders[Idx].Name, ".stab", 5) == 0) && (PESectionHeaders[Idx].Name[5] == 0)) { - /* printf(".stab section found. Size %d\n", + /* printf(".stab section found. Size %d\n", PESectionHeaders[Idx].SizeOfRawData); */ *StabSymbolsLength = PESectionHeaders[Idx].SizeOfRawData; @@ -351,7 +71,7 @@ GetStabInfo(void *FileData, PIMAGE_FILE_HEADER PEFileHeader, if (strncmp((char*)PESectionHeaders[Idx].Name, ".stabstr", 8) == 0) { - /* printf(".stabstr section found. Size %d\n", + /* printf(".stabstr section found. Size %d\n", PESectionHeaders[Idx].SizeOfRawData); */ *StabStringsLength = PESectionHeaders[Idx].SizeOfRawData; @@ -462,9 +182,9 @@ ConvertStabs(ULONG *SymbolsCount, PROSSYM_ENTRY *SymbolsBase, (*SymbolsBase)[*SymbolsCount].FunctionOffset = 0; (*SymbolsBase)[*SymbolsCount].SourceLine = 0; LastFunctionAddress = 0; - break; + break; case N_FUN: - if (0 == StabEntry[i].n_desc || StabEntry[i].n_value < ImageBase) /* line # 0 isn't valid */ + if (0 == StabEntry[i].n_desc || StabEntry[i].n_value < ImageBase) /* line # 0 isn't valid */ { continue; } @@ -492,7 +212,7 @@ ConvertStabs(ULONG *SymbolsCount, PROSSYM_ENTRY *SymbolsBase, StringsBase); (*SymbolsBase)[*SymbolsCount].SourceLine = 0; LastFunctionAddress = Address; - break; + break; case N_SLINE: if (0 == LastFunctionAddress) { @@ -512,7 +232,7 @@ ConvertStabs(ULONG *SymbolsCount, PROSSYM_ENTRY *SymbolsBase, (*SymbolsBase)[*SymbolsCount].SourceLine = StabEntry[i].n_desc; break; default: - continue; + continue; } First = 0; } @@ -1022,9 +742,7 @@ int main(int argc, char* argv[]) ULONG CoffStringsLength; char* path1; char* path2; - FILE* in; FILE* out; - int n_in; void *StringBase; ULONG StringsLength; ULONG StabSymbolsCount; @@ -1033,7 +751,7 @@ int main(int argc, char* argv[]) PROSSYM_ENTRY CoffSymbols; ULONG MergedSymbolsCount; PROSSYM_ENTRY MergedSymbols; - long FileSize; + size_t FileSize; void *FileData; ULONG RosSymLength; void *RosSymSection; @@ -1047,31 +765,12 @@ int main(int argc, char* argv[]) path1 = convert_path(argv[1]); path2 = convert_path(argv[2]); - in = fopen(path1, "rb"); - if (in == NULL) - { - perror("Cannot open input file"); - exit(1); - } - fseek(in, 0L, SEEK_END); - FileSize = ftell(in); - fseek(in, 0L, SEEK_SET); - FileData = malloc(FileSize); - if (NULL == FileData) - { - fclose(in); - fprintf(stderr, "Can't allocate %ld bytes to read input file\n", FileSize); - exit(1); - } - n_in = fread(FileData, 1, FileSize, in); - if (n_in != FileSize) - { - perror("Error reading from input file"); - free(FileData); - fclose(in); - exit(1); - } - fclose(in); + FileData = load_file ( path1, &FileSize ); + if ( !FileData ) + { + fprintf ( stderr, "An error occured loading '%s'\n", path1 ); + exit(1); + } /* Check if MZ header exists */ PEDosHeader = (PIMAGE_DOS_HEADER) FileData; diff --git a/reactos/tools/rsym.h b/reactos/tools/rsym.h new file mode 100644 index 00000000000..a282c2b22b9 --- /dev/null +++ b/reactos/tools/rsym.h @@ -0,0 +1,266 @@ +/* rsym.h */ + +#ifndef RSYM_H +#define RSYM_H + +#define IMAGE_DOS_MAGIC 0x5a4d +#define IMAGE_PE_MAGIC 0x00004550 + +#define IMAGE_SIZEOF_SHORT_NAME 8 + +#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef signed long LONG; +typedef unsigned long ULONG; +#if defined(_WIN64) +typedef unsigned __int64 ULONG_PTR; +#else +typedef unsigned long ULONG_PTR; +#endif + +#pragma pack(push,2) +typedef struct _IMAGE_DOS_HEADER { + WORD e_magic; + WORD e_cblp; + WORD e_cp; + WORD e_crlc; + WORD e_cparhdr; + WORD e_minalloc; + WORD e_maxalloc; + WORD e_ss; + WORD e_sp; + WORD e_csum; + WORD e_ip; + WORD e_cs; + WORD e_lfarlc; + WORD e_ovno; + WORD e_res[4]; + WORD e_oemid; + WORD e_oeminfo; + WORD e_res2[10]; + LONG e_lfanew; +} IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER; +#pragma pack(pop) + +#define IMAGE_FILE_LINE_NUMS_STRIPPED 4 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 8 +#define IMAGE_FILE_DEBUG_STRIPPED 512 + +#pragma pack(push,4) +typedef struct _IMAGE_FILE_HEADER { + WORD Machine; + WORD NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + WORD SizeOfOptionalHeader; + WORD Characteristics; +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + +typedef struct _IMAGE_DATA_DIRECTORY { + DWORD VirtualAddress; + DWORD Size; +} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY; + +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 + +typedef struct _IMAGE_OPTIONAL_HEADER { + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; + DWORD BaseOfData; + DWORD ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + WORD MajorOperatingSystemVersion; + WORD MinorOperatingSystemVersion; + WORD MajorImageVersion; + WORD MinorImageVersion; + WORD MajorSubsystemVersion; + WORD MinorSubsystemVersion; + DWORD Reserved1; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + WORD Subsystem; + WORD DllCharacteristics; + DWORD SizeOfStackReserve; + DWORD SizeOfStackCommit; + DWORD SizeOfHeapReserve; + DWORD SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; +} IMAGE_OPTIONAL_HEADER,*PIMAGE_OPTIONAL_HEADER; + +#define IMAGE_SCN_TYPE_NOLOAD 0x00000002 +#define IMAGE_SCN_LNK_REMOVE 0x00000800 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 + +typedef struct _IMAGE_SECTION_HEADER { + BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; + union { + DWORD PhysicalAddress; + DWORD VirtualSize; + } Misc; + DWORD VirtualAddress; + DWORD SizeOfRawData; + DWORD PointerToRawData; + DWORD PointerToRelocations; + DWORD PointerToLinenumbers; + WORD NumberOfRelocations; + WORD NumberOfLinenumbers; + DWORD Characteristics; +} IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER; + +typedef struct _IMAGE_BASE_RELOCATION { + DWORD VirtualAddress; + DWORD SizeOfBlock; +} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION; + + +typedef struct { + unsigned short f_magic; /* magic number */ + unsigned short f_nscns; /* number of sections */ + unsigned long f_timdat; /* time & date stamp */ + unsigned long f_symptr; /* file pointer to symtab */ + unsigned long f_nsyms; /* number of symtab entries */ + unsigned short f_opthdr; /* sizeof(optional hdr) */ + unsigned short f_flags; /* flags */ +} FILHDR; + +typedef struct { + char s_name[8]; /* section name */ + unsigned long s_paddr; /* physical address, aliased s_nlib */ + unsigned long s_vaddr; /* virtual address */ + unsigned long s_size; /* section size */ + unsigned long s_scnptr; /* file ptr to raw data for section */ + unsigned long s_relptr; /* file ptr to relocation */ + unsigned long s_lnnoptr; /* file ptr to line numbers */ + unsigned short s_nreloc; /* number of relocation entries */ + unsigned short s_nlnno; /* number of line number entries */ + unsigned long s_flags; /* flags */ +} SCNHDR; +#pragma pack(pop) + +typedef struct _SYMBOLFILE_HEADER { + unsigned long SymbolsOffset; + unsigned long SymbolsLength; + unsigned long StringsOffset; + unsigned long StringsLength; +} SYMBOLFILE_HEADER, *PSYMBOLFILE_HEADER; + +typedef struct _STAB_ENTRY { + unsigned long n_strx; /* index into string table of name */ + unsigned char n_type; /* type of symbol */ + unsigned char n_other; /* misc info (usually empty) */ + unsigned short n_desc; /* description field */ + unsigned long n_value; /* value of symbol */ +} STAB_ENTRY, *PSTAB_ENTRY; + +#define N_FUN 0x24 +#define N_SLINE 0x44 +#define N_SO 0x64 + +/* COFF symbol table */ + +#define E_SYMNMLEN 8 /* # characters in a symbol name */ +#define E_FILNMLEN 14 /* # characters in a file name */ +#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ + +#define N_BTMASK (0xf) +#define N_TMASK (0x30) +#define N_BTSHFT (4) +#define N_TSHIFT (2) + +/* derived types, in e_type */ +#define DT_NON (0) /* no derived type */ +#define DT_PTR (1) /* pointer */ +#define DT_FCN (2) /* function */ +#define DT_ARY (3) /* array */ + +#define BTYPE(x) ((x) & N_BTMASK) + +#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT)) +#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT)) +#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT)) +#define ISTAG(x) ((x)==C_STRTAG||(x)==C_UNTAG||(x)==C_ENTAG) +#define DECREF(x) ((((x)>>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK)) + +#define C_EFCN 0xff /* physical end of function */ +#define C_NULL 0 +#define C_AUTO 1 /* automatic variable */ +#define C_EXT 2 /* external symbol */ +#define C_STAT 3 /* static */ +#define C_REG 4 /* register variable */ +#define C_EXTDEF 5 /* external definition */ +#define C_LABEL 6 /* label */ +#define C_ULABEL 7 /* undefined label */ +#define C_MOS 8 /* member of structure */ +#define C_ARG 9 /* function argument */ +#define C_STRTAG 10 /* structure tag */ +#define C_MOU 11 /* member of union */ +#define C_UNTAG 12 /* union tag */ +#define C_TPDEF 13 /* type definition */ +#define C_USTATIC 14 /* undefined static */ +#define C_ENTAG 15 /* enumeration tag */ +#define C_MOE 16 /* member of enumeration */ +#define C_REGPARM 17 /* register parameter */ +#define C_FIELD 18 /* bit field */ +#define C_AUTOARG 19 /* auto argument */ +#define C_LASTENT 20 /* dummy entry (end of block) */ +#define C_BLOCK 100 /* ".bb" or ".eb" */ +#define C_FCN 101 /* ".bf" or ".ef" */ +#define C_EOS 102 /* end of structure */ +#define C_FILE 103 /* file name */ +#define C_LINE 104 /* line # reformatted as symbol table entry */ +#define C_ALIAS 105 /* duplicate tag */ +#define C_HIDDEN 106 /* ext symbol in dmert public lib */ + +#pragma pack(push,1) +typedef struct _COFF_SYMENT +{ + union + { + char e_name[E_SYMNMLEN]; + struct + { + unsigned long e_zeroes; + unsigned long e_offset; + } + e; + } + e; + unsigned long e_value; + short e_scnum; + unsigned short e_type; + unsigned char e_sclass; + unsigned char e_numaux; +} COFF_SYMENT, *PCOFF_SYMENT; +#pragma pack(pop) + +typedef struct _ROSSYM_ENTRY { + ULONG_PTR Address; + ULONG FunctionOffset; + ULONG FileOffset; + ULONG SourceLine; +} ROSSYM_ENTRY, *PROSSYM_ENTRY; + +#define ROUND_UP(N, S) (((N) + (S) - 1) & ~((S) - 1)) + +extern char* +convert_path(const char* origpath); + +extern void* +load_file ( const char* file_name, size_t* file_size ); + +#endif/*RSYM_H*/ diff --git a/reactos/tools/rsym_common.c b/reactos/tools/rsym_common.c new file mode 100644 index 00000000000..875463fe920 --- /dev/null +++ b/reactos/tools/rsym_common.c @@ -0,0 +1,62 @@ +/* rsym_common.c */ + +#include +#include +#include + +#include "rsym.h" + +char* +convert_path ( const char* origpath ) +{ + char* newpath; + int i; + + newpath = strdup(origpath); + + i = 0; + while (newpath[i] != 0) + { +#ifdef UNIX_PATHS + if (newpath[i] == '\\') + { + newpath[i] = '/'; + } +#else +#ifdef DOS_PATHS + if (newpath[i] == '/') + { + newpath[i] = '\\'; + } +#endif +#endif + i++; + } + return(newpath); +} + +void* +load_file ( const char* file_name, size_t* file_size ) +{ + FILE* f; + void* FileData = NULL; + + f = fopen ( file_name, "rb" ); + if (f != NULL) + { + fseek(f, 0L, SEEK_END); + *file_size = ftell(f); + fseek(f, 0L, SEEK_SET); + FileData = malloc(*file_size); + if (FileData != NULL) + { + if ( *file_size != fread(FileData, 1, *file_size, f) ) + { + free(FileData); + FileData = NULL; + } + } + fclose(f); + } + return FileData; +} diff --git a/reactos/tools/tools-check.c b/reactos/tools/tools-check.c new file mode 100644 index 00000000000..8452a2632c5 --- /dev/null +++ b/reactos/tools/tools-check.c @@ -0,0 +1,32 @@ +#include "tools-check.h" + +/* + * - Binutils older than 2003/10/01 have broken windres which can't handle + * icons with alpha channel. + * - Binutils between 2004/09/02 and 2004/10/08 have broken handling of + * forward exports in dlltool. + */ + +#if (BINUTILS_VERSION_DATE >= 20040902 && BINUTILS_VERSION_DATE <= 20041008) || \ + (BINUTILS_VERSION_DATE < 20031001) +#error "Due to technical reasons your binutils version can't be used to" \ + "build ReactOS. Please consider upgrading to newer version. See" \ + "www.mingw.org for details." +#endif + +/* + * GCC 3.3.1 is lowest allowed version. Older versions have various problems + * with C++ code. + */ + +#if (__GNUC__ == 3 && __GNUC_MINOR__ < 3) || \ + (__GNUC__ < 3) +#error "Due to technical reasons your GCC version can't be used to" \ + "build ReactOS. Please consider upgrading to newer version. See" \ + "www.mingw.org for details." +#endif + +/* + * FIXME: GCC 3.4.1 has broken headers which cause Explorer to not build. + * We should warn in this case...maybe add check for the broken headers? + */ diff --git a/reactos/tools/tools-check.mak b/reactos/tools/tools-check.mak new file mode 100644 index 00000000000..89e3b3a1c86 --- /dev/null +++ b/reactos/tools/tools-check.mak @@ -0,0 +1,23 @@ +PATH_TO_TOP = .. +include $(PATH_TO_TOP)/rules.mak + +# +# Get the binutils version +# +# The "ld -v" output can be in either of these two formats: +# "GNU ld version 050113 20050113" (nightly build) +# "GNU ld version 2.15.94 20050118" (official release) +# + +BINUTILS_VERSION_DATE=$(word 5,$(shell $(PREFIX)ld -v)) + +all: +ifeq ($(HOST),mingw32-linux) + @echo "#define BINUTILS_VERSION_DATE $(BINUTILS_VERSION_DATE)" > tools-check.h +endif +ifeq ($(HOST),mingw32-windows) + @echo #define BINUTILS_VERSION_DATE $(BINUTILS_VERSION_DATE) > tools-check.h +endif + $(HOST_CC) -c tools-check.c -o tools-check.temp + $(RM) tools-check.temp + $(RM) tools-check.h diff --git a/reactos/tools/tools.mak b/reactos/tools/tools.mak index 725a2610ec3..27b6557041e 100644 --- a/reactos/tools/tools.mak +++ b/reactos/tools/tools.mak @@ -27,7 +27,8 @@ RSYM_TARGET = \ $(EXEPREFIX)$(RSYM_OUT_)rsym$(EXEPOSTFIX) RSYM_SOURCES = \ - $(RSYM_BASE_)rsym.c + $(RSYM_BASE_)rsym.c \ + $(RSYM_BASE_)rsym_common.c RSYM_OBJECTS = \ $(addprefix $(INTERMEDIATE_), $(RSYM_SOURCES:.c=.o)) @@ -47,6 +48,10 @@ $(RSYM_INT_)rsym.o: $(RSYM_BASE_)rsym.c | $(RSYM_INT) $(ECHO_CC) ${host_gcc} $(RSYM_HOST_CFLAGS) -c $< -o $@ +$(RSYM_INT_)rsym_common.o: $(RSYM_BASE_)rsym_common.c | $(RSYM_INT) + $(ECHO_CC) + ${host_gcc} $(RSYM_HOST_CFLAGS) -c $< -o $@ + .PHONY: rsym_clean rsym_clean: -@$(rm) $(RSYM_TARGET) $(RSYM_OBJECTS) 2>$(NUL) @@ -61,6 +66,7 @@ include tools/mkhive/mkhive.mak include tools/nci/nci.mak include tools/rbuild/rbuild.mak include tools/unicode/unicode.mak +include tools/widl/widl.mak include tools/winebuild/winebuild.mak include tools/wmc/wmc.mak include tools/wpp/wpp.mak diff --git a/reactos/tools/unicode/mbtowc.c b/reactos/tools/unicode/mbtowc.c index dd3c9242d37..1c85b2429d8 100644 --- a/reactos/tools/unicode/mbtowc.c +++ b/reactos/tools/unicode/mbtowc.c @@ -22,6 +22,8 @@ #include "wine/unicode.h" +typedef unsigned char uchar; + /* get the decomposition of a Unicode char */ int get_decomposition( WCHAR src, WCHAR *dst, unsigned int dstlen ) { @@ -256,25 +258,25 @@ int wine_cp_mbstowcs( const union cptable *table, int flags, { if (flags & MB_ERR_INVALID_CHARS) { - if (check_invalid_chars_sbcs( &table->sbcs, src, srclen )) return -2; + if (check_invalid_chars_sbcs( &table->sbcs, (const uchar*)src, srclen )) return -2; } if (!(flags & MB_COMPOSITE)) { if (!dstlen) return srclen; - return mbstowcs_sbcs( &table->sbcs, src, srclen, dst, dstlen ); + return mbstowcs_sbcs( &table->sbcs, (const uchar*)src, srclen, dst, dstlen ); } - return mbstowcs_sbcs_decompose( &table->sbcs, src, srclen, dst, dstlen ); + return mbstowcs_sbcs_decompose( &table->sbcs, (const uchar*)src, srclen, dst, dstlen ); } else /* mbcs */ { if (flags & MB_ERR_INVALID_CHARS) { - if (check_invalid_chars_dbcs( &table->dbcs, src, srclen )) return -2; + if (check_invalid_chars_dbcs( &table->dbcs, (const uchar*)src, srclen )) return -2; } if (!(flags & MB_COMPOSITE)) - return mbstowcs_dbcs( &table->dbcs, src, srclen, dst, dstlen ); + return mbstowcs_dbcs( &table->dbcs, (const uchar*)src, srclen, dst, dstlen ); else - return mbstowcs_dbcs_decompose( &table->dbcs, src, srclen, dst, dstlen ); + return mbstowcs_dbcs_decompose( &table->dbcs, (const uchar*)src, srclen, dst, dstlen ); } } diff --git a/reactos/tools/unicode/string.c b/reactos/tools/unicode/string.c index 9f94aa19280..6d3926f9659 100644 --- a/reactos/tools/unicode/string.c +++ b/reactos/tools/unicode/string.c @@ -20,6 +20,7 @@ #include #include +#include #include "wine/unicode.h" diff --git a/reactos/tools/unicode/wctomb.c b/reactos/tools/unicode/wctomb.c index 68be134e7ae..aed43031f67 100644 --- a/reactos/tools/unicode/wctomb.c +++ b/reactos/tools/unicode/wctomb.c @@ -196,7 +196,7 @@ static int wcstombs_sbcs_slow( const struct sbcs_table *table, int flags, int tmp; WCHAR composed; - if (!defchar) defchar = &table_default; + if (!defchar) defchar = (const char*)&table_default; if (!used) used = &tmp; /* avoid checking on every char */ *used = 0; diff --git a/reactos/tools/widl/ChangeLog b/reactos/tools/widl/ChangeLog index 28d675151fb..3b14c38e2e3 100644 --- a/reactos/tools/widl/ChangeLog +++ b/reactos/tools/widl/ChangeLog @@ -1,44 +1,152 @@ -ChangeLog -2005-02-27 sedwards - - tools/widl/y.tab.c - tools/widl/parser.y - tools/widl/widl.c - tools/widl/widl.h - tools/widl/server.c - tools/widl/typelib.c - -Merge in Winehq changes to reduce noise. - - tools/widl/server.h - tools/widl/client.h - tools/widl/proxy.h - -Deleted unneeded headers. - -2005-02-26 ekohl - tools/widl/server.c - -Server stubs are void functions. -Fix a warning in the server stub descriptor. - - -2005-02-25 ekohl - tools/widl/client.c - tools/widl/header.c - tools/widl/header.h - tools/widl/server.c - -Support multiple interfaces per idl file. -Support explicit binding handles. - - -2005-02-24 ekohl - include/wine/rpcfc.h - tools/widl/client.c - tools/widl/header.c - tools/widl/parser.l - tools/widl/parser.y - tools/widl/server.c - -Support remaining basic types (float, double, small, wchar_t and handle_t). +ChangeLog + +2005-04-03 ekohl + + tools/widl/client.c + tools/widl/parser.l + tools/widl/parser.y + tools/widl/server.c + tools/widl/widltypes.h + +Support 'ref' and 'unique' attributes for pointers. + +2005-03-27 Jacek Caban (from WINE) + + tools/widl/parser.y + tools/widl/y.tab.c + +Added handling of unsigned type + +2005-03-25 ekohl + + tools/widl/client.c + tools/widl/server.c + +Generate code without L-value casts. Fix remaining issues. + +2005-03-21 navaraf + + tools/widl/client.c + +Fix a typo in write_function_stubs. + +2005-03-20 navaraf + + tools/widl/client.c + tools/widl/server.c + +Generate code without L-value casts. + +2005-03-20 ekohl + + tools/widl/client.c + tools/widl/server.c + +Improve error messages. +Don't start counting the type_offset for each new fuction. It is a global offset. +Use only basic types in type- and proc-strings (e.g. unsigned long -> long). + + +2005-03-13 ekohl + + tools/widl/server.c + +Remove debug printf(). + + +2005-03-13 jimtabor + + tools/widl/write_msft.c + +Fixup *nix port, added include fcntl.h and unistd.h. + + +2005-03-13 royce3 + + tools/widl/hash.c + tools/widl/Makefile + tools/widl/write_msft.c + +-Wall -Werror and fix warnings + + +2005-03-13 ekohl + + tools/widl/client.c + tools/widl/server.c + +Implement [string] attribute for pointers to char and wchar_t. + + +2005-03-12 ekohl + + tools/widl/client.c + tools/widl/server.c + +Implement [in], [out] and [in, out] support for pointers to basic types. + + +2005-03-10 ekohl + + tools/widl/client.c + tools/widl/header.c + tools/widl/server.c + tools/widl/widl.c + tools/widl/widl.h + +Implement '-o' option. This is equivalent to MIDLs '/oldnames' option. + + +2005-03-06 ekohl + + tools/widl/client.c + tools/widl/header.c + tools/widl/header.h + tools/widl/server.c + +Support pointers to simple types as input arguments. + + +2005-02-27 sedwards + + tools/widl/y.tab.c + tools/widl/parser.y + tools/widl/widl.c + tools/widl/widl.h + tools/widl/server.c + tools/widl/typelib.c + +Merge in Winehq changes to reduce noise. + + tools/widl/server.h + tools/widl/client.h + tools/widl/proxy.h + +Deleted unneeded headers. + +2005-02-26 ekohl + tools/widl/server.c + +Server stubs are void functions. +Fix a warning in the server stub descriptor. + + +2005-02-25 ekohl + tools/widl/client.c + tools/widl/header.c + tools/widl/header.h + tools/widl/server.c + +Support multiple interfaces per idl file. +Support explicit binding handles. + + +2005-02-24 ekohl + include/wine/rpcfc.h + tools/widl/client.c + tools/widl/header.c + tools/widl/parser.l + tools/widl/parser.y + tools/widl/server.c + +Support remaining basic types (float, double, small, wchar_t and handle_t). diff --git a/reactos/tools/widl/Makefile b/reactos/tools/widl/Makefile index 2716d5384c9..b70896aaa0e 100644 --- a/reactos/tools/widl/Makefile +++ b/reactos/tools/widl/Makefile @@ -36,7 +36,8 @@ CLEAN_FILES = *.o $(TARGET) HOST_CFLAGS = -DYYDEBUG=1 -DINT16=SHORT -D__USE_W32API \ -I$(LIB_WPP_DIR) -I$(PATH_TO_TOP)/include/wine \ - -I$(PATH_TO_TOP)/include -I$(PATH_TO_TOP)/w32api/include + -I$(PATH_TO_TOP)/include -I$(PATH_TO_TOP)/w32api/include \ + -Werror -Wall %.o: %.c $(HOST_CC) $(HOST_CFLAGS) -c $< -o $@ diff --git a/reactos/tools/widl/client.c b/reactos/tools/widl/client.c index fc48625e372..134652997c0 100644 --- a/reactos/tools/widl/client.c +++ b/reactos/tools/widl/client.c @@ -56,10 +56,31 @@ static int print_client( const char *format, ... ) } +static unsigned char +get_base_type(unsigned char type) +{ + + switch (type) + { + case RPC_FC_USHORT: + type = RPC_FC_SHORT; + break; + + case RPC_FC_ULONG: + type = RPC_FC_LONG; + break; + } + + return type; +} + + static void write_procformatstring(type_t *iface) { func_t *func = iface->funcs; var_t *var; + unsigned int type_offset = 2; + int in_attr, out_attr; print_client("static const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString =\n"); print_client("{\n"); @@ -78,53 +99,51 @@ static void write_procformatstring(type_t *iface) while (NEXT_LINK(var)) var = NEXT_LINK(var); while (var) { - switch(var->type->type) + out_attr = is_attr(var->attrs, ATTR_OUT); + in_attr = is_attr(var->attrs, ATTR_IN); + + /* set 'in' attribute if neither 'in' nor 'out' is set */ + if (!out_attr && !in_attr) + in_attr = 1; + + if (var->ptr_level == 0) { - case RPC_FC_BYTE: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_BYTE */\n", RPC_FC_BYTE); - break; - case RPC_FC_CHAR: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_CHAR */\n", RPC_FC_CHAR); - break; - case RPC_FC_WCHAR: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_WCHAR */\n", RPC_FC_WCHAR); - break; - case RPC_FC_USHORT: - case RPC_FC_SHORT: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_USHORT */\n", RPC_FC_SHORT); - break; - case RPC_FC_ULONG: - case RPC_FC_LONG: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_LONG */\n", RPC_FC_LONG); - break; - case RPC_FC_HYPER: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_HYPER */\n", RPC_FC_HYPER); - break; - case RPC_FC_IGNORE: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_IGNORE */\n", RPC_FC_IGNORE); - break; - case RPC_FC_SMALL: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_SMALL */\n", RPC_FC_SMALL); - break; - case RPC_FC_FLOAT: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_FLOAT */\n", RPC_FC_FLOAT); - break; - case RPC_FC_DOUBLE: - print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_DOUBLE */\n", RPC_FC_DOUBLE); - break; - default: - error("Unknown/unsupported type\n"); - return; + if (is_base_type(var->type)) + { + print_client("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); + print_client("0x%02x, /* FC_ */\n", get_base_type(var->type->type)); + } + else + { + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + } + else if (var->ptr_level == 1) + { + if (is_base_type(var->type)) + { + if (in_attr & !out_attr) + print_client("0x4d, /* FC_IN_PARAM */\n"); + else if (!in_attr & out_attr) + print_client("0x51, /* FC_OUT_PARAM */\n"); + else if (in_attr & out_attr) + print_client("0x50, /* FC_IN_OUT_PARAM */\n"); + fprintf(client, "#ifndef _ALPHA_\n"); + print_client("0x01,\n"); + fprintf(client, "#else\n"); + print_client("0x02,\n"); + fprintf(client, "#endif\n"); + print_client("NdrFcShort(0x%x),\n", type_offset); + type_offset += 4; + } + else + { + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } } var = PREV_LINK(var); @@ -138,58 +157,16 @@ static void write_procformatstring(type_t *iface) print_client("0x5b, /* FC_END */\n"); print_client("0x5c, /* FC_PAD */\n"); } + else if (is_base_type(var->type)) + { + print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); + print_client("0x%02x, /* FC_ */\n", get_base_type(var->type->type)); + } else { - switch(var->type->type) - { - case RPC_FC_BYTE: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_BYTE */\n", var->type->type); - break; - case RPC_FC_CHAR: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_CHAR */\n", var->type->type); - break; - case RPC_FC_WCHAR: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_WCHAR */\n", var->type->type); - break; - case RPC_FC_USHORT: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_USHORT */\n", var->type->type); - break; - case RPC_FC_SHORT: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_SHORT */\n", var->type->type); - break; - case RPC_FC_ULONG: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_ULONG */\n", var->type->type); - break; - case RPC_FC_LONG: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_LONG */\n", var->type->type); - break; - case RPC_FC_HYPER: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_HYPER */\n", var->type->type); - break; - case RPC_FC_SMALL: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_SMALL */\n", RPC_FC_SMALL); - break; - case RPC_FC_FLOAT: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_FLOAT */\n", RPC_FC_FLOAT); - break; - case RPC_FC_DOUBLE: - print_client("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_client("0x%02x, /* FC_DOUBLE */\n", RPC_FC_DOUBLE); - break; - default: - error("Unknown/unsupported type\n"); - return; - } + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; } func = PREV_LINK(func); @@ -204,8 +181,14 @@ static void write_procformatstring(type_t *iface) } -static void write_typeformatstring(void) +static void write_typeformatstring(type_t *iface) { + func_t *func = iface->funcs; + var_t *var; + int out_attr; + int string_attr; + int ptr_attr, ref_attr, unique_attr; + print_client("static const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString =\n"); print_client("{\n"); indent++; @@ -213,6 +196,76 @@ static void write_typeformatstring(void) print_client("{\n"); indent++; print_client("NdrFcShort(0x0),\n"); + + while (NEXT_LINK(func)) func = NEXT_LINK(func); + while (func) + { + if (func->args) + { + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + while (var) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + string_attr = is_attr(var->attrs, ATTR_STRING); + + if (var->ptr_level > 1) + { + error("Function '%s' argument '%s': Pointer level %d not supported!\n", + func->def->name, var->name, var->ptr_level); + return; + } + + if (var->ptr_level == 1) + { + ptr_attr = is_attr(var->attrs, ATTR_PTR); + ref_attr = is_attr(var->attrs, ATTR_REF); + unique_attr = is_attr(var->attrs, ATTR_UNIQUE); + + if (ptr_attr + ref_attr + unique_attr == 0) + ref_attr = 1; + + if (is_base_type(var->type)) + { + if (out_attr) + print_client("0x11, 0x0c, /* FC_RP [allocated_on_stack] [simple_pointer] */\n"); + else + { + if (ptr_attr) + print_client("0x14, 0x08, /* FC_FP [simple_pointer] */\n"); + else if (ref_attr) + print_client("0x11, 0x08, /* FC_RP [simple_pointer] */\n"); + else if (unique_attr) + print_client("0x12, 0x08, /* FC_UP [simple_pointer] */\n"); + } + + if (string_attr) + { + if (var->type->type == RPC_FC_CHAR) + print_client("0x%02x, /* FC_C_CSTRING */\n", RPC_FC_C_CSTRING); + else if (var->type->type == RPC_FC_WCHAR) + print_client("0x%02x, /* FC_C_WSTRING */\n", RPC_FC_C_WSTRING); + else + { + error("%s: Invalid type!\n", __FUNCTION__); + return; + } + } + else + print_client("0x%02x, /* FC_ */\n", get_base_type(var->type->type)); + print_client("0x5c, /* FC_PAD */\n"); + } + } + + var = PREV_LINK(var); + } + } + + + + func = PREV_LINK(func); + } + print_client("0x0\n"); indent--; print_client("}\n"); @@ -222,147 +275,704 @@ static void write_typeformatstring(void) } -static void print_message_buffer_size(func_t *func) +static void print_message_buffer_size(func_t *func, unsigned int *type_offset) { unsigned int alignment; int size; int last_size = -1; + int in_attr; + int out_attr; + int string_attr; + int nothing_printed = 1; + int ptr_attr, ref_attr, unique_attr; + var_t *var; + unsigned int local_type_offset = *type_offset; + + print_client("_StubMsg.BufferLength ="); + if (func->args) + { + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + for (; var; var = PREV_LINK(var)) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + in_attr = is_attr(var->attrs, ATTR_IN); + string_attr = is_attr(var->attrs, ATTR_STRING); + + /* set 'in' attribute if neither 'in' nor 'out' is found */ + if (!out_attr && !in_attr) + in_attr = 1; + + ptr_attr = is_attr(var->attrs, ATTR_PTR); + ref_attr = is_attr(var->attrs, ATTR_REF); + unique_attr = is_attr(var->attrs, ATTR_UNIQUE); + + /* default to 'ref' attribute */ + if (ptr_attr + ref_attr + unique_attr == 0) + ref_attr = 1; + + if (!in_attr) + continue; + + if (var->ptr_level == 1) + { + if (unique_attr) + { + if (string_attr && + (var->type->type == RPC_FC_CHAR || var->type->type == RPC_FC_WCHAR)) + { + size = 16; + alignment = 0; + if (last_size != -1) + fprintf(client, " +"); + fprintf(client, " %dU", (size == 0) ? 0 : size + alignment); + nothing_printed = 0; + } + else + { + size = 8; + alignment = 0; + if (last_size != -1) + fprintf(client, " +"); + fprintf(client, " %dU", (size == 0) ? 0 : size + alignment); + nothing_printed = 0; + } + } + else if (ref_attr) + { + if (string_attr && + (var->type->type == RPC_FC_CHAR || var->type->type == RPC_FC_WCHAR)) + { + size = 12; + alignment = 0; + if (last_size != -1) + fprintf(client, " +"); + fprintf(client, " %dU", (size == 0) ? 0 : size + alignment); + nothing_printed = 0; + } + else + { + alignment = 0; + switch (var->type->type) + { + case RPC_FC_BYTE: + case RPC_FC_CHAR: + case RPC_FC_SMALL: + size = 1; + alignment = 0; + break; + + case RPC_FC_WCHAR: + case RPC_FC_USHORT: + case RPC_FC_SHORT: + size = 2; + if (last_size > 0 && last_size < 2) + alignment += (2 - last_size); + break; + + case RPC_FC_ULONG: + case RPC_FC_LONG: + case RPC_FC_FLOAT: + size = 4; + if (last_size > 0 && last_size < 4) + alignment += (4 - last_size); + break; + + case RPC_FC_HYPER: + case RPC_FC_DOUBLE: + size = 8; + if (last_size > 0 && last_size < 4) + alignment += (4 - last_size); + break; + + case RPC_FC_IGNORE: + size = 0; + break; + + default: + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + + if (last_size != -1) + fprintf(client, " +"); + fprintf(client, " %dU", (size == 0) ? 0 : size + alignment); + nothing_printed = 0; + } + } + } + else + { + alignment = 0; + switch (var->type->type) + { + case RPC_FC_BYTE: + case RPC_FC_CHAR: + case RPC_FC_SMALL: + size = 1; + alignment = 0; + break; + + case RPC_FC_WCHAR: + case RPC_FC_USHORT: + case RPC_FC_SHORT: + size = 2; + if (last_size > 0 && last_size < 2) + alignment += (2 - last_size); + break; + + case RPC_FC_ULONG: + case RPC_FC_LONG: + case RPC_FC_FLOAT: + size = 4; + if (last_size > 0 && last_size < 4) + alignment += (4 - last_size); + break; + + case RPC_FC_HYPER: + case RPC_FC_DOUBLE: + size = 8; + if (last_size > 0 && last_size < 4) + alignment += (4 - last_size); + break; + + case RPC_FC_IGNORE: + size = 0; + break; + + default: + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + + if (last_size != -1) + fprintf(client, " +"); + fprintf(client, " %dU", (size == 0) ? 0 : size + alignment); + nothing_printed = 0; + } + + last_size = size; + } + } + + if (nothing_printed) + { + fprintf(client, " 0U"); + } + fprintf(client, ";\n"); + + /* get string size */ + if (func->args) + { + nothing_printed = 0; + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + for (; var; var = PREV_LINK(var)) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + in_attr = is_attr(var->attrs, ATTR_IN); + string_attr = is_attr(var->attrs, ATTR_STRING); + + /* set 'in' attribute if neither 'in' nor 'out' is found */ + if (!out_attr && !in_attr) + in_attr = 1; + + ptr_attr = is_attr(var->attrs, ATTR_PTR); + ref_attr = is_attr(var->attrs, ATTR_REF); + unique_attr = is_attr(var->attrs, ATTR_UNIQUE); + + /* default to 'ref' attribute */ + if (ptr_attr + ref_attr + unique_attr == 0) + ref_attr = 1; + + if (in_attr) + { + if (var->ptr_level == 1 && + string_attr && + (var->type->type == RPC_FC_CHAR || var->type->type == RPC_FC_WCHAR)) + { + if (ptr_attr) + { + /* FIXME: not supported yet */ + } + if (ref_attr) + { + print_client("NdrConformantStringBufferSize(\n"); + indent++; + print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); + print_client("(unsigned char __RPC_FAR *)%s,\n", var->name); + print_client("(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%u]);\n", + local_type_offset + 2); + nothing_printed = 1; + indent--; + } + else if (unique_attr) + { + print_client("NdrPointerBufferSize(\n"); + indent++; + print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); + print_client("(unsigned char __RPC_FAR *)%s,\n", var->name); + print_client("(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%u]);\n", + local_type_offset); + nothing_printed = 1; + indent--; + } + } + } + + /* calculate the next type offset */ + if (var->ptr_level == 1) + { + local_type_offset += 4; + } + } + + if (nothing_printed) + fprintf(client, "\n"); + } +} + + +static void marshall_in_arguments(func_t *func, unsigned int *type_offset) +{ + unsigned int alignment; + unsigned int size; + unsigned int last_size = 0; + int in_attr; + int out_attr; + int string_attr; + int ptr_attr, ref_attr, unique_attr; var_t *var; if (!func->args) - { - fprintf(client, " 0U"); return; - } var = func->args; while (NEXT_LINK(var)) var = NEXT_LINK(var); - while (var) + for (; var; var = PREV_LINK(var)) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + in_attr = is_attr(var->attrs, ATTR_IN); + string_attr = is_attr(var->attrs, ATTR_STRING); + + /* set 'in' attribute if neither 'in' nor 'out' is set */ + if (!out_attr && !in_attr) + in_attr = 1; + + if (in_attr) + { + if (var->ptr_level > 1) + { + error("Function '%s' argument '%s': Pointer level %d not supported!\n", + func->def->name, var->name, var->ptr_level); + return; + } + + if (var->ptr_level == 1) + { + ptr_attr = is_attr(var->attrs, ATTR_PTR); + ref_attr = is_attr(var->attrs, ATTR_REF); + unique_attr = is_attr(var->attrs, ATTR_UNIQUE); + if (ptr_attr + ref_attr + unique_attr == 0) + ref_attr = 1; + + if (ref_attr) + { + if (string_attr && + (var->type->type == RPC_FC_CHAR || var->type->type == RPC_FC_WCHAR)) + { + print_client("NdrConformantStringMarshall(\n"); + indent++; + print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); + print_client("(unsigned char __RPC_FAR *)%s,\n", var->name); + print_client("(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%u]);\n", *type_offset + 2); + indent--; + fprintf(client, "\n"); + } + else + { + alignment = 0; + switch (var->type->type) + { + case RPC_FC_BYTE: + case RPC_FC_CHAR: + case RPC_FC_SMALL: + size = 1; + alignment = 0; + break; + + case RPC_FC_WCHAR: + case RPC_FC_USHORT: + case RPC_FC_SHORT: + size = 2; + if (last_size > 0 && last_size < 2) + alignment = (2 - last_size); + break; + + case RPC_FC_ULONG: + case RPC_FC_LONG: + case RPC_FC_FLOAT: + size = 4; + if (last_size > 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_HYPER: + case RPC_FC_DOUBLE: + size = 8; + if (last_size > 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_IGNORE: + size = 0; + break; + + default: + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + + if (size != 0) + { + if (alignment != 0) + print_client("_StubMsg.Buffer += %u;\n", alignment); + + print_client("*(("); + write_type(client, var->type, NULL, var->tname); + fprintf(client, " __RPC_FAR*)_StubMsg.Buffer) = "); + if (var->ptr_level == 1) + fprintf(client, "*"); + write_name(client, var); + fprintf(client, ";\n"); + print_client("_StubMsg.Buffer += sizeof("); + write_type(client, var->type, NULL, var->tname); + fprintf(client, ");\n"); + fprintf(client, "\n"); + + last_size = size; + } + } + } + else if (unique_attr) + { + print_client("NdrPointerMarshall(\n"); + indent++; + print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); + print_client("(unsigned char __RPC_FAR *)%s,\n", var->name); + print_client("(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%u]);\n", *type_offset); + indent--; + fprintf(client, "\n"); + } + } + else + { + alignment = 0; + switch (var->type->type) + { + case RPC_FC_BYTE: + case RPC_FC_CHAR: + case RPC_FC_SMALL: + size = 1; + alignment = 0; + break; + + case RPC_FC_WCHAR: + case RPC_FC_USHORT: + case RPC_FC_SHORT: + size = 2; + if (last_size > 0 && last_size < 2) + alignment = (2 - last_size); + break; + + case RPC_FC_ULONG: + case RPC_FC_LONG: + case RPC_FC_FLOAT: + size = 4; + if (last_size > 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_HYPER: + case RPC_FC_DOUBLE: + size = 8; + if (last_size > 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_IGNORE: + size = 0; + break; + + default: + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + + if (size != 0) + { + if (alignment != 0) + print_client("_StubMsg.Buffer += %u;\n", alignment); + + print_client("*(("); + write_type(client, var->type, NULL, var->tname); + fprintf(client, " __RPC_FAR*)_StubMsg.Buffer) = "); + if (var->ptr_level == 1) + fprintf(client, "*"); + write_name(client, var); + fprintf(client, ";\n"); + print_client("_StubMsg.Buffer += sizeof("); + write_type(client, var->type, NULL, var->tname); + fprintf(client, ");\n"); + fprintf(client, "\n"); + + last_size = size; + } + } + } + + /* calculate the next type offset */ + if (var->ptr_level == 1) + { + *type_offset += 4; + } + } +} + + +static void unmarshall_out_arguments(func_t *func) +{ + unsigned int alignment; + unsigned int size; + unsigned int last_size = 0; + int out_attr; + var_t *var; + var_t *def; + + def = func->def; + + /* unmarshall the out arguments */ + if (func->args) + { + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + for (; var; var = PREV_LINK(var)) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + if (!out_attr) + continue; + + if (var->ptr_level > 1) + { + error("Function '%s' argument '%s': Pointer level %d not supported!\n", + func->def->name, var->name, var->ptr_level); + return; + } + + alignment = 0; + switch (var->type->type) + { + case RPC_FC_BYTE: + case RPC_FC_CHAR: + case RPC_FC_SMALL: + size = 1; + alignment = 0; + break; + + case RPC_FC_WCHAR: + case RPC_FC_USHORT: + case RPC_FC_SHORT: + size = 2; + if (last_size > 0 && last_size < 2) + alignment = (2 - last_size); + break; + + case RPC_FC_ULONG: + case RPC_FC_LONG: + case RPC_FC_FLOAT: + size = 4; + if (last_size > 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_HYPER: + case RPC_FC_DOUBLE: + size = 8; + if (last_size > 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_IGNORE: + size = 0; + break; + + default: + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + + if (size != 0) + { + if (var->ptr_level == 1) + { + fprintf(client, "\n"); + if (alignment != 0) + print_client("_StubMsg.Buffer += %u;\n", alignment); + + print_client("*"); + write_name(client, var); + fprintf(client, " = *(("); + write_type(client, var->type, NULL, var->tname); + fprintf(client, " __RPC_FAR *)_StubMsg.Buffer);\n"); + + print_client("_StubMsg.Buffer += sizeof("); + write_type(client, var->type, NULL, var->tname); + fprintf(client, ");\n"); + } + + last_size = size; + } + } + } + + /* unmarshall return value */ + if (!is_void(def->type, NULL)) { alignment = 0; - switch (var->type->type) + switch (def->type->type) { case RPC_FC_BYTE: case RPC_FC_CHAR: case RPC_FC_SMALL: - size = 1; - alignment = 0; - break; - case RPC_FC_WCHAR: case RPC_FC_USHORT: case RPC_FC_SHORT: - size = 2; - if (last_size != -1 && last_size < 2) - alignment += (2 - last_size); - break; - case RPC_FC_ULONG: case RPC_FC_LONG: case RPC_FC_FLOAT: size = 4; - if (last_size != -1 && last_size < 4) - alignment += (4 - last_size); + if (last_size > 0 && last_size < 4) + alignment = (4 - last_size); break; case RPC_FC_HYPER: case RPC_FC_DOUBLE: size = 8; - if (last_size != -1 && last_size < 4) - alignment += (4 - last_size); - break; - - case RPC_FC_IGNORE: - size = 0; + if (last_size > 0 && last_size < 4) + alignment = (4 - last_size); break; default: - error("Unknown/unsupported type!"); + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; } - if (last_size != -1) - fprintf(client, " +"); - fprintf(client, " %dU", (size == 0) ? 0 : size + alignment); + fprintf(client, "\n"); + if (alignment != 0) + print_client("_StubMsg.Buffer += %u;\n", alignment); + print_client("_RetVal = *(("); + write_type(client, def->type, def, def->tname); + fprintf(client, " __RPC_FAR *)_StubMsg.Buffer);\n"); - last_size = size; + print_client("_StubMsg.Buffer += sizeof("); + write_type(client, def->type, def, def->tname); + fprintf(client, ");\n"); + } +} + + +static void check_pointers(func_t *func) +{ + var_t *var; + int ptr_attr; + int ref_attr; + int unique_attr; + + if (!func->args) + return; + + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + while (var) + { + ptr_attr = is_attr(var->attrs, ATTR_PTR); + ref_attr = is_attr(var->attrs, ATTR_REF); + unique_attr = is_attr(var->attrs, ATTR_UNIQUE); + + if (var->ptr_level == 0) + { + if (ptr_attr + ref_attr + unique_attr != 0) + { + error("The attributes [ptr], [ref] and [unique] can only be used for pointers!\n"); + return; + } + } + else + { + /* default to 'ref' attribute */ + if (ptr_attr + ref_attr + unique_attr == 0) + { + ref_attr = 1; + } + + if (ptr_attr + ref_attr + unique_attr > 1) + { + error("The attributes [ptr], [ref] and [unique] are mutually exclusive!\n"); + return; + } + + if (var->ptr_level == 1) + { + if (ref_attr) + { + print_client("if (!%s)\n", var->name); + print_client("{\n"); + indent++; + print_client("RpcRaiseException(RPC_X_NULL_REF_POINTER);\n"); + indent--; + print_client("}\n"); + fprintf(client, "\n"); + } + } + else if (var->ptr_level > 1) + { + error("Pointer level %d not supported!\n", var->ptr_level); + return; + } + } var = PREV_LINK(var); } } -static void marshall_arguments(func_t *func) +static int use_return_buffer(func_t *func) { - unsigned int alignment; - unsigned int size; - unsigned int last_size = 0; var_t *var; + if (!is_void(func->def->type, NULL)) + return 1; + if (!func->args) - return; + return 0; var = func->args; while (NEXT_LINK(var)) var = NEXT_LINK(var); while (var) { - alignment = 0; - switch (var->type->type) - { - case RPC_FC_BYTE: - case RPC_FC_CHAR: - case RPC_FC_SMALL: - size = 1; - alignment = 0; - break; - - case RPC_FC_WCHAR: - case RPC_FC_USHORT: - case RPC_FC_SHORT: - size = 2; - if (last_size != 0 && last_size < 2) - alignment = (2 - last_size); - break; - - case RPC_FC_ULONG: - case RPC_FC_LONG: - case RPC_FC_FLOAT: - size = 4; - if (last_size != 0 && last_size < 4) - alignment = (4 - last_size); - break; - - case RPC_FC_HYPER: - case RPC_FC_DOUBLE: - size = 8; - if (last_size != 0 && last_size < 4) - alignment = (4 - last_size); - break; - - case RPC_FC_IGNORE: - size = 0; - break; - - default: - error("Unknown/unsupported type!"); - } - - if (size != 0) - { - if (alignment != 0) - print_client("_StubMsg.Buffer += %u;\n", alignment); - - print_client("*(("); - write_type(client, var->type, var, var->tname); - fprintf(client, " __RPC_FAR*)_StubMsg.Buffer)++ = "); - write_name(client, var); - fprintf(client, ";\n"); - fprintf(client, "\n"); - - last_size = size; - } + if (is_attr(var->attrs, ATTR_OUT)) + return 1; var = PREV_LINK(var); } + + return 0; } @@ -375,6 +985,7 @@ static void write_function_stubs(type_t *iface) var_t* explicit_handle_var; int method_count = 0; unsigned int proc_offset = 0; + unsigned int type_offset = 2; while (NEXT_LINK(func)) func = NEXT_LINK(func); while (func) @@ -426,6 +1037,10 @@ static void write_function_stubs(type_t *iface) print_client("RPC_MESSAGE _RpcMessage;\n"); print_client("MIDL_STUB_MESSAGE _StubMsg;\n"); fprintf(client, "\n"); + + /* check pointers */ + check_pointers(func); + print_client("RpcTryFinally\n"); print_client("{\n"); indent++; @@ -451,24 +1066,21 @@ static void write_function_stubs(type_t *iface) } /* emit the message buffer size */ - print_client("_StubMsg.BufferLength ="); - print_message_buffer_size(func); - fprintf(client, ";\n"); - + print_message_buffer_size(func, &type_offset); print_client("NdrGetBuffer(\n"); indent++; print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); print_client("_StubMsg.BufferLength,\n"); if (implicit_handle || explicit_handle) - print_client("%_Handle);\n"); + print_client("_Handle);\n"); else print_client("%s__MIDL_AutoBindHandle);\n", iface->name); indent--; fprintf(client, "\n"); - /* marshal arguments */ - marshall_arguments(func); + /* marshal in arguments */ + marshall_in_arguments(func, &type_offset); /* send/recieve message */ print_client("NdrSendReceive(\n"); @@ -477,11 +1089,10 @@ static void write_function_stubs(type_t *iface) print_client("(unsigned char __RPC_FAR *)_StubMsg.Buffer);\n"); indent--; - /* unmarshal return value */ - if (!is_void(def->type, NULL)) + if (use_return_buffer(func)) { + /* convert data representation */ fprintf(client, "\n"); - print_client("if ((_RpcMessage.DataRepresentation & 0x0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION)\n"); indent++; print_client("NdrConvert(\n"); @@ -489,11 +1100,9 @@ static void write_function_stubs(type_t *iface) print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); print_client("(PFORMAT_STRING)&__MIDL_ProcFormatString.Format[%u]);\n", proc_offset); indent -= 2; - fprintf(client, "\n"); - print_client("_RetVal = *(("); - write_type(client, def->type, def, def->tname); - fprintf(client, " __RPC_FAR *)_StubMsg.Buffer)++;\n"); + /* unmarshal out arguments */ + unmarshall_out_arguments(func); } /* update proc_offset */ @@ -503,7 +1112,19 @@ static void write_function_stubs(type_t *iface) while (NEXT_LINK(var)) var = NEXT_LINK(var); while (var) { - proc_offset += 2; /* FIXME */ + switch (var->ptr_level) + { + case 0: + if (is_base_type(var->type)) + proc_offset += 2; + break; + + case 1: + if (is_base_type(var->type)) + proc_offset += 4; + break; + } + var = PREV_LINK(var); } } @@ -544,8 +1165,11 @@ static void write_function_stubs(type_t *iface) static void write_bindinghandledecl(type_t *iface) { - print_client("static RPC_BINDING_HANDLE %s__MIDL_AutoBindHandle;\n", iface->name); - fprintf(client, "\n"); + if (!get_attrp(iface->attrs, ATTR_IMPLICIT_HANDLE)) + { + print_client("static RPC_BINDING_HANDLE %s__MIDL_AutoBindHandle;\n", iface->name); + fprintf(client, "\n"); + } } @@ -567,9 +1191,9 @@ static void write_stubdescriptor(type_t *iface) print_client("MIDL_user_allocate,\n"); print_client("MIDL_user_free,\n"); if (implicit_handle) - print_client("&%s,\n", implicit_handle); + print_client("{&%s},\n", implicit_handle); else - print_client("&%s__MIDL_AutoBindHandle,\n", iface->name); + print_client("{&%s__MIDL_AutoBindHandle},\n", iface->name); print_client("0,\n"); print_client("0,\n"); print_client("0,\n"); @@ -614,8 +1238,12 @@ static void write_clientinterfacedecl(type_t *iface) print_client("0,\n"); indent--; print_client("};\n"); - print_client("RPC_IF_HANDLE %s_v%d_%d_c_ifspec = (RPC_IF_HANDLE)& %s___RpcClientInterface;\n", - iface->name, LOWORD(ver), HIWORD(ver), iface->name); + if (old_names) + print_client("RPC_IF_HANDLE %s_ClientIfHandle = (RPC_IF_HANDLE)& %s___RpcClientInterface;\n", + iface->name, iface->name); + else + print_client("RPC_IF_HANDLE %s_v%d_%d_c_ifspec = (RPC_IF_HANDLE)& %s___RpcClientInterface;\n", + iface->name, LOWORD(ver), HIWORD(ver), iface->name); fprintf(client, "\n"); } @@ -633,14 +1261,12 @@ static void write_formatdesc( const char *str ) } -static void write_formatstringsdecl(type_t *iface) +static int get_type_format_string_size(type_t *iface) { - int byte_count = 1; + int size = 3; func_t *func; var_t *var; - print_client("#define TYPE_FORMAT_STRING_SIZE %d\n", 3); /* FIXME */ - /* determine the proc format string size */ func = iface->funcs; while (NEXT_LINK(func)) func = NEXT_LINK(func); @@ -653,17 +1279,74 @@ static void write_formatstringsdecl(type_t *iface) while (NEXT_LINK(var)) var = NEXT_LINK(var); while (var) { - byte_count += 2; /* FIXME: determine real size */ + if (var->ptr_level == 1) + { + if (is_base_type(var->type)) + size += 4; + } + + var = PREV_LINK(var); + } + } + + func = PREV_LINK(func); + } + + return size; +} + + +static int get_proc_format_string_size(type_t *iface) +{ + int size = 1; + func_t *func; + var_t *var; + + /* determine the proc format string size */ + func = iface->funcs; + while (NEXT_LINK(func)) func = NEXT_LINK(func); + while (func) + { + /* argument list size */ + if (func->args) + { + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + while (var) + { + switch (var->ptr_level) + { + case 0: + if (is_base_type(var->type)) + size += 2; + break; + + case 1: + if (is_base_type(var->type)) + size += 4; + break; + } + var = PREV_LINK(var); } } /* return value size */ - byte_count += 2; /* FIXME: determine real size */ + size += 2; func = PREV_LINK(func); } - print_client("#define PROC_FORMAT_STRING_SIZE %d\n", byte_count); + return size; +} + + +static void write_formatstringsdecl(type_t *iface) +{ + print_client("#define TYPE_FORMAT_STRING_SIZE %d\n", + get_type_format_string_size(iface)); + + print_client("#define PROC_FORMAT_STRING_SIZE %d\n", + get_proc_format_string_size(iface)); fprintf(client, "\n"); write_formatdesc("TYPE"); @@ -741,7 +1424,7 @@ void write_client(ifref_t *ifaces) fprintf(client, "\n"); write_procformatstring(iface->iface); - write_typeformatstring(); + write_typeformatstring(iface->iface); fprintf(client, "\n"); diff --git a/reactos/tools/widl/hash.c b/reactos/tools/widl/hash.c index 89822cc5388..8ee30982248 100644 --- a/reactos/tools/widl/hash.c +++ b/reactos/tools/widl/hash.c @@ -511,7 +511,7 @@ unsigned long lhash_val_of_name_sys( syskind_t skind, LCID lcid, LPCSTR lpStr) switch (PRIMARYLANGID(LANGIDFROMLCID(lcid))) { default: - fprintf(stderr, "Unknown lcid %lx, treating as latin-based, please report\n", lcid); + fprintf(stderr, "Unknown lcid %lx, treating as latin-based, please report\n", (long)lcid); /* .. Fall Through .. */ case LANG_AFRIKAANS: case LANG_ALBANIAN: case LANG_ARMENIAN: case LANG_ASSAMESE: case LANG_AZERI: case LANG_BASQUE: diff --git a/reactos/tools/widl/header.c b/reactos/tools/widl/header.c index 3a3345b074b..ec5c8c7493e 100644 --- a/reactos/tools/widl/header.c +++ b/reactos/tools/widl/header.c @@ -45,6 +45,18 @@ static void indent(int delta) if (delta > 0) indentation += delta; } +int is_base_type(type_t *t) +{ + return (t->type == RPC_FC_BYTE || t->type == RPC_FC_CHAR || + t->type == RPC_FC_SMALL || t->type == RPC_FC_USMALL || + t->type == RPC_FC_WCHAR || t->type == RPC_FC_SHORT || + t->type == RPC_FC_USHORT || t->type == RPC_FC_LONG || + t->type == RPC_FC_ULONG || t->type == RPC_FC_FLOAT || + t->type == RPC_FC_HYPER || t->type == RPC_FC_DOUBLE || + t->type == RPC_FC_ENUM16 || t->type == RPC_FC_ENUM32 || + t->type == RPC_FC_IGNORE); +} + int is_attr(attr_t *a, enum attr_type t) { while (a) { @@ -839,8 +851,16 @@ void write_rpc_interface(type_t *iface) { fprintf(header, "extern handle_t %s;\n", var); } - fprintf(header, "extern RPC_IF_HANDLE %s_v%d_%d_c_ifspec;\n", iface->name, LOWORD(ver), HIWORD(ver)); - fprintf(header, "extern RPC_IF_HANDLE %s_v%d_%d_s_ifspec;\n", iface->name, LOWORD(ver), HIWORD(ver)); + if (old_names) + { + fprintf(header, "extern RPC_IF_HANDLE %s_ClientIfHandle;\n", iface->name); + fprintf(header, "extern RPC_IF_HANDLE %s_ServerIfHandle;\n", iface->name); + } + else + { + fprintf(header, "extern RPC_IF_HANDLE %s_v%d_%d_c_ifspec;\n", iface->name, LOWORD(ver), HIWORD(ver)); + fprintf(header, "extern RPC_IF_HANDLE %s_v%d_%d_s_ifspec;\n", iface->name, LOWORD(ver), HIWORD(ver)); + } write_function_proto(iface); fprintf(header, "\n"); diff --git a/reactos/tools/widl/header.h b/reactos/tools/widl/header.h index 6d276c3d6ac..42243ff9c48 100644 --- a/reactos/tools/widl/header.h +++ b/reactos/tools/widl/header.h @@ -21,6 +21,7 @@ #ifndef __WIDL_HEADER_H #define __WIDL_HEADER_H +extern int is_base_type(type_t *t); extern int is_attr(attr_t *a, enum attr_type t); extern void *get_attrp(attr_t *a, enum attr_type t); extern unsigned long get_attrv(attr_t *a, enum attr_type t); diff --git a/reactos/tools/widl/lex.yy.c b/reactos/tools/widl/lex.yy.c index 419d7576c26..6083b88b7c2 100644 --- a/reactos/tools/widl/lex.yy.c +++ b/reactos/tools/widl/lex.yy.c @@ -4555,6 +4555,7 @@ static struct keyword { {"propput", tPROPPUT}, {"propputref", tPROPPUTREF}, /* ... */ + {"ptr", tPTR}, {"public", tPUBLIC}, /* ... */ {"readonly", tREADONLY}, @@ -4579,8 +4580,8 @@ static struct keyword { /* ... */ {"transmit_as", tTRANSMITAS}, {"typedef", tTYPEDEF}, - {"union", tUNION}, /* ... */ + {"union", tUNION}, {"unique", tUNIQUE}, {"unsigned", tUNSIGNED}, /* ... */ diff --git a/reactos/tools/widl/parser.l b/reactos/tools/widl/parser.l index f41af6bff25..72716b70e0d 100644 --- a/reactos/tools/widl/parser.l +++ b/reactos/tools/widl/parser.l @@ -275,6 +275,7 @@ static struct keyword { {"propput", tPROPPUT}, {"propputref", tPROPPUTREF}, /* ... */ + {"ptr", tPTR}, {"public", tPUBLIC}, /* ... */ {"readonly", tREADONLY}, @@ -299,8 +300,8 @@ static struct keyword { /* ... */ {"transmit_as", tTRANSMITAS}, {"typedef", tTYPEDEF}, - {"union", tUNION}, /* ... */ + {"union", tUNION}, {"unique", tUNIQUE}, {"unsigned", tUNSIGNED}, /* ... */ diff --git a/reactos/tools/widl/parser.y b/reactos/tools/widl/parser.y index 4d27f2f2e6a..a4b7edf05c6 100644 --- a/reactos/tools/widl/parser.y +++ b/reactos/tools/widl/parser.y @@ -162,6 +162,7 @@ static type_t std_uhyper = { "MIDL_uhyper" }; %token tPOINTERDEFAULT %token tPROPERTIES %token tPROPGET tPROPPUT tPROPPUTREF +%token tPTR %token tPUBLIC %token tREADONLY tREF %token tRESTRICTED @@ -373,8 +374,10 @@ attribute: | tPROPGET { $$ = make_attr(ATTR_PROPGET); } | tPROPPUT { $$ = make_attr(ATTR_PROPPUT); } | tPROPPUTREF { $$ = make_attr(ATTR_PROPPUTREF); } + | tPTR { $$ = make_attr(ATTR_PTR); } | tPUBLIC { $$ = make_attr(ATTR_PUBLIC); } | tREADONLY { $$ = make_attr(ATTR_READONLY); } + | tREF { $$ = make_attr(ATTR_REF); } | tRESTRICTED { $$ = make_attr(ATTR_RESTRICTED); } | tRETVAL { $$ = make_attr(ATTR_RETVAL); } | tSIZEIS '(' m_exprs ')' { $$ = make_attrp(ATTR_SIZEIS, $3); } @@ -383,6 +386,7 @@ attribute: | tSWITCHIS '(' expr ')' { $$ = make_attrp(ATTR_SWITCHIS, $3); } | tSWITCHTYPE '(' type ')' { $$ = make_attrp(ATTR_SWITCHTYPE, type_ref($3)); } | tTRANSMITAS '(' type ')' { $$ = make_attrp(ATTR_TRANSMITAS, type_ref($3)); } + | tUNIQUE { $$ = make_attr(ATTR_UNIQUE); } | tUUID '(' aUUID ')' { $$ = make_attrp(ATTR_UUID, $3); } | tV1ENUM { $$ = make_attr(ATTR_V1ENUM); } | tVARARG { $$ = make_attr(ATTR_VARARG); } @@ -561,6 +565,8 @@ base_type: tBYTE { $$ = make_type(RPC_FC_BYTE, NULL); } default: break; } } + | tUNSIGNED { $$ = make_type(RPC_FC_ULONG, &std_int); + $$->sign = -1; } | tFLOAT { $$ = make_type(RPC_FC_FLOAT, NULL); } | tDOUBLE { $$ = make_type(RPC_FC_DOUBLE, NULL); } | tBOOLEAN { $$ = make_type(RPC_FC_SMALL, &std_bool); } diff --git a/reactos/tools/widl/server.c b/reactos/tools/widl/server.c index dca83ac9ea6..833c117951b 100644 --- a/reactos/tools/widl/server.c +++ b/reactos/tools/widl/server.c @@ -61,10 +61,31 @@ static int print_server(const char *format, ...) } +static unsigned char +get_base_type(unsigned char type) +{ + + switch (type) + { + case RPC_FC_USHORT: + type = RPC_FC_SHORT; + break; + + case RPC_FC_ULONG: + type = RPC_FC_LONG; + break; + } + + return type; +} + + static void write_procformatstring(type_t *iface) { func_t *func = iface->funcs; var_t *var; + unsigned int type_offset = 2; + int in_attr, out_attr; print_server("static const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString =\n"); print_server("{\n"); @@ -83,58 +104,51 @@ static void write_procformatstring(type_t *iface) while (NEXT_LINK(var)) var = NEXT_LINK(var); while (var) { - switch(var->type->type) + out_attr = is_attr(var->attrs, ATTR_OUT); + in_attr = is_attr(var->attrs, ATTR_IN); + + /* set 'in' attribute if neither 'in' nor 'out' is set */ + if (!out_attr && !in_attr) + in_attr = 1; + + if (var->ptr_level == 0) { - case RPC_FC_BYTE: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_BYTE */\n", var->type->type); - break; - case RPC_FC_CHAR: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_CHAR */\n", var->type->type); - break; - case RPC_FC_WCHAR: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_WCHAR */\n", var->type->type); - break; - case RPC_FC_USHORT: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_USHORT */\n", var->type->type); - break; - case RPC_FC_SHORT: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_SHORT */\n", var->type->type); - break; - case RPC_FC_ULONG: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_ULONG */\n", var->type->type); - break; - case RPC_FC_LONG: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_LONG */\n", var->type->type); - break; - case RPC_FC_HYPER: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_HYPER */\n", var->type->type); - break; - case RPC_FC_IGNORE: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_IGNORE */\n", var->type->type); - break; - case RPC_FC_SMALL: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_SMALL */\n", RPC_FC_SMALL); - break; - case RPC_FC_FLOAT: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_FLOAT */\n", RPC_FC_FLOAT); - break; - case RPC_FC_DOUBLE: - print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_DOUBLE */\n", RPC_FC_DOUBLE); - break; - default: - error("Unknown/unsupported type\n"); + if (is_base_type(var->type)) + { + print_server("0x4e, /* FC_IN_PARAM_BASETYPE */\n"); + print_server("0x%02x, /* FC_ */\n", get_base_type(var->type->type)); + } + else + { + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + } + else if (var->ptr_level == 1) + { + if (is_base_type(var->type)) + { + if (in_attr & !out_attr) + print_server("0x4d, /* FC_IN_PARAM */\n"); + else if (!in_attr & out_attr) + print_server("0x51, /* FC_OUT_PARAM */\n"); + else if (in_attr & out_attr) + print_server("0x50, /* FC_IN_OUT_PARAM */\n"); + fprintf(server, "#ifndef _ALPHA_\n"); + print_server("0x01,\n"); + fprintf(server, "#else\n"); + print_server("0x02,\n"); + fprintf(server, "#endif\n"); + print_server("NdrFcShort(0x%x),\n", type_offset); + type_offset += 4; + } + else + { + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } } var = PREV_LINK(var); @@ -148,57 +162,16 @@ static void write_procformatstring(type_t *iface) print_server("0x5b, /* FC_END */\n"); print_server("0x5c, /* FC_PAD */\n"); } + else if (is_base_type(var->type)) + { + print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); + print_server("0x%02x, /* FC_ */\n", get_base_type(var->type->type)); + } else { - switch(var->type->type) - { - case RPC_FC_BYTE: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_BYTE */\n", var->type->type); - break; - case RPC_FC_CHAR: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_CHAR */\n", var->type->type); - break; - case RPC_FC_WCHAR: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_WCHAR */\n", var->type->type); - break; - case RPC_FC_USHORT: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_USHORT */\n", var->type->type); - break; - case RPC_FC_SHORT: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_SHORT */\n", var->type->type); - break; - case RPC_FC_ULONG: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_ULONG */\n", var->type->type); - break; - case RPC_FC_LONG: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_LONG */\n", var->type->type); - break; - case RPC_FC_HYPER: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_HYPER */\n", var->type->type); - break; - case RPC_FC_SMALL: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_SMALL */\n", RPC_FC_SMALL); - break; - case RPC_FC_FLOAT: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_FLOAT */\n", RPC_FC_FLOAT); - break; - case RPC_FC_DOUBLE: - print_server("0x53, /* FC_RETURN_PARAM_BASETYPE */\n"); - print_server("0x%02x, /* FC_DOUBLE */\n", RPC_FC_DOUBLE); - break; - default: - error("Unknown/unsupported type\n"); - } + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; } func = PREV_LINK(func); @@ -213,8 +186,14 @@ static void write_procformatstring(type_t *iface) } -static void write_typeformatstring(void) +static void write_typeformatstring(type_t *iface) { + func_t *func = iface->funcs; + var_t *var; + int out_attr; + int string_attr; + int ptr_attr, ref_attr, unique_attr; + print_server("static const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString =\n"); print_server("{\n"); indent++; @@ -222,6 +201,76 @@ static void write_typeformatstring(void) print_server("{\n"); indent++; print_server("NdrFcShort(0x0),\n"); + + while (NEXT_LINK(func)) func = NEXT_LINK(func); + while (func) + { + if (func->args) + { + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + while (var) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + string_attr = is_attr(var->attrs, ATTR_STRING); + + if (var->ptr_level > 1) + { + error("Function '%s' argument '%s': Pointer level %d not supported!\n", + func->def->name, var->name, var->ptr_level); + return; + } + + if (var->ptr_level == 1) + { + ptr_attr = is_attr(var->attrs, ATTR_PTR); + ref_attr = is_attr(var->attrs, ATTR_REF); + unique_attr = is_attr(var->attrs, ATTR_UNIQUE); + + if (ptr_attr + ref_attr + unique_attr == 0) + ref_attr = 1; + + if (is_base_type(var->type)) + { + if (out_attr) + print_server("0x11, 0x0c, /* FC_RP [allocated_on_stack] [simple_pointer] */\n"); + else + { + if (ptr_attr) + print_server("0x14, 0x08, /* FC_FP [simple_pointer] */\n"); + else if (ref_attr) + print_server("0x11, 0x08, /* FC_RP [simple_pointer] */\n"); + else if (unique_attr) + print_server("0x12, 0x08, /* FC_UP [simple_pointer] */\n"); + } + + if (string_attr) + { + if (var->type->type == RPC_FC_CHAR) + print_server("0x%02x, /* FC_C_CSTRING */\n", RPC_FC_C_CSTRING); + else if (var->type->type == RPC_FC_WCHAR) + print_server("0x%02x, /* FC_C_WSTRING */\n", RPC_FC_C_WSTRING); + else + { + error("%s: Invalid type!\n", __FUNCTION__); + return; + } + } + else + print_server("0x%02x, /* FC_ */\n", get_base_type(var->type->type)); + print_server("0x5c, /* FC_PAD */\n"); + } + } + + var = PREV_LINK(var); + } + } + + + + func = PREV_LINK(func); + } + print_server("0x0\n"); indent--; print_server("}\n"); @@ -231,10 +280,81 @@ static void write_typeformatstring(void) } -unsigned int get_required_buffer_size(type_t *type) +static void print_message_buffer_size(func_t *func) { - switch(type->type) + unsigned int alignment = 0; + int size = 0; + int last_size = -1; + int in_attr; + int out_attr; + int string_attr; + int empty_line; + var_t *var; + + fprintf(server, "\n"); + print_server("_StubMsg.BufferLength ="); + if (func->args) { + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + for (; var; var = PREV_LINK(var)) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + if (!out_attr) + continue; + + alignment = 0; + switch (var->type->type) + { + case RPC_FC_BYTE: + case RPC_FC_CHAR: + case RPC_FC_SMALL: + size = 1; + alignment = 0; + break; + + case RPC_FC_WCHAR: + case RPC_FC_USHORT: + case RPC_FC_SHORT: + size = 2; + if (last_size > 0 && last_size < 2) + alignment += (2 - last_size); + break; + + case RPC_FC_ULONG: + case RPC_FC_LONG: + case RPC_FC_FLOAT: + size = 4; + if (last_size > 0 && last_size < 4) + alignment += (4 - last_size); + break; + + case RPC_FC_HYPER: + case RPC_FC_DOUBLE: + size = 8; + if (last_size > 0 && last_size < 4) + alignment += (4 - last_size); + break; + + default: + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + + if (last_size != -1) + fprintf(server, " +"); + fprintf(server, " %dU", (size == 0) ? 0 : size + alignment); + + last_size = size; + } + } + + /* return value size */ + if (!is_void(func->def->type, NULL)) + { + switch(func->def->type->type) + { case RPC_FC_BYTE: case RPC_FC_SMALL: case RPC_FC_CHAR: @@ -244,27 +364,64 @@ unsigned int get_required_buffer_size(type_t *type) case RPC_FC_ULONG: case RPC_FC_LONG: case RPC_FC_FLOAT: - return 4; + size = 4; + if (last_size > 0 && last_size < 4) + alignment += (4 - last_size); + break; case RPC_FC_HYPER: case RPC_FC_DOUBLE: - return 8; - - case RPC_FC_IGNORE: - return 0; + size = 8; + if (last_size > 0 && last_size < 4) + alignment += (4 - last_size); + break; default: - error("Unknown/unsupported type: %s\n", type->name); - return 0; + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, func->def->type->type); + return; + } + + if (last_size != -1) + fprintf(server, " +"); + + fprintf(server, " %dU", (size == 0) ? 0 : size + alignment); + } + fprintf(server, ";\n"); + + /* get string size */ + if (func->args) + { + empty_line = 0; + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + for (; var; var = PREV_LINK(var)) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + in_attr = is_attr(var->attrs, ATTR_IN); + string_attr = is_attr(var->attrs, ATTR_STRING); + + /* set 'in' attribute if neither 'in' nor 'out' is found */ + if (!out_attr && !in_attr) + in_attr = 1; + + if (var->ptr_level == 1 && + string_attr && + (var->type->type == RPC_FC_CHAR || var->type->type == RPC_FC_WCHAR)) + { + print_server("_StubMsg.BufferLength += 16;\n"); + empty_line = 1; + } + } + + if (empty_line) + fprintf(server, "\n"); } } -static void unmarshall_arguments(func_t *func) +static void init_pointers (func_t *func) { - unsigned int alignment; - unsigned int size; - unsigned int last_size = 0; var_t *var; if (!func->args) @@ -273,25 +430,331 @@ static void unmarshall_arguments(func_t *func) var = func->args; while (NEXT_LINK(var)) var = NEXT_LINK(var); while (var) + { + if (var->ptr_level == 1) + { + print_server("("); + write_type(server, var->type, NULL, var->tname); + fprintf(server, " __RPC_FAR *)%s = 0;\n", var->name); + } + else if (var->ptr_level > 1) + { + error("Pointer level %d not supported!\n", var->ptr_level); + return; + } + + var = PREV_LINK(var); + } + fprintf(server, "\n"); +} + + +static void unmarshall_in_arguments(func_t *func, unsigned int *type_offset) +{ + unsigned int alignment; + unsigned int size; + unsigned int last_size = 0; + var_t *var; + int in_attr, out_attr; + int string_attr; + int ptr_attr, ref_attr, unique_attr; + + if (!func->args) + return; + + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + for (; var; var = PREV_LINK(var)) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + in_attr = is_attr(var->attrs, ATTR_IN); + string_attr = is_attr(var->attrs, ATTR_STRING); + + /* set 'in' attribute if neither 'in' nor 'out' is set */ + if (!out_attr && !in_attr) + in_attr = 1; + + if (in_attr) + { + if (var->ptr_level == 1) + { + ptr_attr = is_attr(var->attrs, ATTR_PTR); + ref_attr = is_attr(var->attrs, ATTR_REF); + unique_attr = is_attr(var->attrs, ATTR_UNIQUE); + if (ptr_attr + ref_attr + unique_attr == 0) + ref_attr = 1; + + if (ref_attr) + { + if (string_attr && + (var->type->type == RPC_FC_CHAR || var->type->type == RPC_FC_WCHAR)) + { + print_server("NdrConformantStringUnmarshall(\n"); + indent++; + print_server("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); + print_server("(unsigned char __RPC_FAR * __RPC_FAR *)&%s,\n", var->name); + print_server("(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%u],\n", *type_offset + 2); + print_server("(unsigned char)0);\n"); + indent--; + fprintf(server, "\n"); + } + else + { + alignment = 0; + switch (var->type->type) + { + case RPC_FC_BYTE: + case RPC_FC_CHAR: + case RPC_FC_SMALL: + size = 1; + alignment = 0; + break; + + case RPC_FC_WCHAR: + case RPC_FC_USHORT: + case RPC_FC_SHORT: + size = 2; + if (last_size != 0 && last_size < 2) + alignment = (2 - last_size); + break; + + case RPC_FC_ULONG: + case RPC_FC_LONG: + case RPC_FC_FLOAT: + size = 4; + if (last_size != 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_HYPER: + case RPC_FC_DOUBLE: + size = 8; + if (last_size != 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_IGNORE: + size = 0; + break; + + default: + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + + if (size != 0) + { + if (alignment != 0) + print_server("_StubMsg.Buffer += %u;\n", alignment); + + print_server(""); + write_name(server, var); + fprintf(server, " = ("); + write_type(server, var->type, NULL, var->tname); + fprintf(server, " __RPC_FAR*)_StubMsg.Buffer;\n"); + print_server("_StubMsg.Buffer += sizeof("); + write_type(server, var->type, NULL, var->tname); + fprintf(server, ");\n"); + fprintf(server, "\n"); + + last_size = size; + } + } + } + else if (unique_attr) + { + print_server("NdrPointerUnmarshall(\n"); + indent++; + print_server("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); + print_server("(unsigned char __RPC_FAR * __RPC_FAR *)&%s,\n", var->name); + print_server("(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%u],\n", *type_offset); + print_server("(unsigned char)0);\n"); + indent--; + fprintf(server, "\n"); + } + + } + else + { + alignment = 0; + switch (var->type->type) + { + case RPC_FC_BYTE: + case RPC_FC_CHAR: + case RPC_FC_SMALL: + size = 1; + alignment = 0; + break; + + case RPC_FC_WCHAR: + case RPC_FC_USHORT: + case RPC_FC_SHORT: + size = 2; + if (last_size != 0 && last_size < 2) + alignment = (2 - last_size); + break; + + case RPC_FC_ULONG: + case RPC_FC_LONG: + case RPC_FC_FLOAT: + size = 4; + if (last_size != 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_HYPER: + case RPC_FC_DOUBLE: + size = 8; + if (last_size != 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_IGNORE: + size = 0; + break; + + default: + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + + if (size != 0) + { + if (alignment != 0) + print_server("_StubMsg.Buffer += %u;\n", alignment); + + print_server(""); + write_name(server, var); + fprintf(server, " = *(("); + write_type(server, var->type, NULL, var->tname); + fprintf(server, " __RPC_FAR*)_StubMsg.Buffer);\n"); + print_server("_StubMsg.Buffer += sizeof("); + write_type(server, var->type, NULL, var->tname); + fprintf(server, ");\n"); + fprintf(server, "\n"); + + last_size = size; + } + } + } + + /* calculate the next type offset */ + if (var->ptr_level == 1) + { + *type_offset += 4; + } + } +} + + +static void marshall_out_arguments(func_t *func) +{ + unsigned int alignment = 0; + unsigned int size = 0; + unsigned int last_size = 0; + var_t *var; + var_t *def; + int out_attr; + + def = func->def; + + /* marshall the out arguments */ + if (func->args) + { + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + for (; var; var = PREV_LINK(var)) + { + out_attr = is_attr(var->attrs, ATTR_OUT); + if (!out_attr) + continue; + + alignment = 0; + switch (var->type->type) + { + case RPC_FC_BYTE: + case RPC_FC_CHAR: + case RPC_FC_SMALL: + size = 1; + alignment = 0; + break; + + case RPC_FC_WCHAR: + case RPC_FC_USHORT: + case RPC_FC_SHORT: + size = 2; + if (last_size != 0 && last_size < 2) + alignment = (2 - last_size); + break; + + case RPC_FC_ULONG: + case RPC_FC_LONG: + case RPC_FC_FLOAT: + size = 4; + if (last_size != 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_HYPER: + case RPC_FC_DOUBLE: + size = 8; + if (last_size != 0 && last_size < 4) + alignment = (4 - last_size); + break; + + case RPC_FC_IGNORE: + size = 0; + break; + + default: + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; + } + + if (size != 0) + { + if (alignment != 0) + print_server("_StubMsg.Buffer += %u;\n", alignment); + + if (var->ptr_level == 1) + { + fprintf(server, "\n"); + print_server("*(("); + write_type(server, var->type, NULL, var->tname); + fprintf(server, " __RPC_FAR *)_StubMsg.Buffer) = *"); + write_name(server, var); + fprintf(server, ";\n"); + + print_server("_StubMsg.Buffer += sizeof("); + write_type(server, var->type, NULL, var->tname); + fprintf(server, ");"); + } + else + { + error("Pointer level %d is not supported!\n", var->ptr_level); + return; + } + + last_size = size; + } + } + } + + /* marshall the return value */ + if (!is_void(def->type, NULL)) { alignment = 0; - switch (var->type->type) + switch (def->type->type) { case RPC_FC_BYTE: case RPC_FC_CHAR: case RPC_FC_SMALL: - size = 1; - alignment = 0; - break; - case RPC_FC_WCHAR: case RPC_FC_USHORT: case RPC_FC_SHORT: - size = 2; - if (last_size != 0 && last_size < 2) - alignment = (2 - last_size); - break; - case RPC_FC_ULONG: case RPC_FC_LONG: case RPC_FC_FLOAT: @@ -307,31 +770,46 @@ static void unmarshall_arguments(func_t *func) alignment = (4 - last_size); break; - case RPC_FC_IGNORE: - size = 0; - break; - default: - error("Unknown/unsupported type!"); + error("%s:%d Unknown/unsupported type 0x%x\n", + __FUNCTION__,__LINE__, var->type->type); + return; } - if (size != 0) - { - if (alignment != 0) - print_server("_StubMsg.Buffer += %u;\n", alignment); + fprintf(server, "\n"); + if (alignment != 0) + print_server("_StubMsg.Buffer += %u;\n", alignment); + print_server("*(("); + write_type(server, def->type, def, def->tname); + fprintf(server, " __RPC_FAR *)_StubMsg.Buffer) = _RetVal;\n"); + print_server("_StubMsg.Buffer += sizeof("); + write_type(server, def->type, def, def->tname); + fprintf(server, ");\n"); + } +} - print_server(""); - write_name(server, var); - fprintf(server, " = *(("); - write_type(server, var->type, var, var->tname); - fprintf(server, " __RPC_FAR*)_StubMsg.Buffer)++;\n"); - fprintf(server, "\n"); - last_size = size; - } +static int use_return_buffer(func_t *func) +{ + var_t *var; + + if (!is_void(func->def->type, NULL)) + return 1; + + if (!func->args) + return 0; + + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + while (var) + { + if (is_attr(var->attrs, ATTR_OUT)) + return 1; var = PREV_LINK(var); } + + return 0; } @@ -342,6 +820,10 @@ static void write_function_stubs(type_t *iface) var_t *var; var_t* explicit_handle_var; unsigned int proc_offset = 0; + unsigned int type_offset = 2; + unsigned int i; + int in_attr; + int out_attr; while (NEXT_LINK(func)) func = NEXT_LINK(func); while (func) @@ -390,10 +872,23 @@ static void write_function_stubs(type_t *iface) /* declare arguments */ if (func->args) { + i = 0; var = func->args; while (NEXT_LINK(var)) var = NEXT_LINK(var); while (var) { + in_attr = is_attr(var->attrs, ATTR_IN); + out_attr = is_attr(var->attrs, ATTR_OUT); + if (!out_attr && !in_attr) + in_attr = 1; + if (!in_attr) + { + print_server(""); + write_type(server, var->type, NULL, var->tname); + fprintf(server, " _W%u;\n", i); + i++; + } + print_server(""); write_type(server, var->type, var, var->tname); fprintf(server, " "); @@ -424,6 +919,8 @@ static void write_function_stubs(type_t *iface) fprintf(server, "\n"); } + init_pointers(func); + print_server("RpcTryFinally\n"); print_server("{\n"); indent++; @@ -442,7 +939,7 @@ static void write_function_stubs(type_t *iface) indent -= 2; fprintf(server, "\n"); - unmarshall_arguments(func); + unmarshall_in_arguments(func, &type_offset); } print_server("if (_StubMsg.Buffer > _StubMsg.BufferEnd)\n"); @@ -462,6 +959,32 @@ static void write_function_stubs(type_t *iface) print_server("RpcEndExcept\n"); fprintf(server, "\n"); + /* assign out arguments */ + if (func->args) + { + i = 0; + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + while (var) + { + in_attr = is_attr(var->attrs, ATTR_IN); + out_attr = is_attr(var->attrs, ATTR_OUT); + if (!out_attr && !in_attr) + in_attr = 1; + if (!in_attr) + { + print_server(""); + write_name(server, var); + fprintf(server, " = &_W%u;\n", i); + i++; + } + + var = PREV_LINK(var); + } + + if (i) + fprintf(server, "\n"); + } /* Call the real server function */ if (!is_void(def->type, NULL)) @@ -496,11 +1019,10 @@ static void write_function_stubs(type_t *iface) fprintf(server, "();\n"); } - /* marshall the return value */ - if (!is_void(def->type, NULL)) + /* allocate and fill the return message buffer */ + if (use_return_buffer(func)) { - fprintf(server, "\n"); - print_server("_StubMsg.BufferLength = %uU;\n", get_required_buffer_size(def->type)); + print_message_buffer_size(func); print_server("_pRpcMessage->BufferLength = _StubMsg.BufferLength;\n"); fprintf(server, "\n"); print_server("_Status = I_RpcGetBuffer(_pRpcMessage);\n"); @@ -510,11 +1032,9 @@ static void write_function_stubs(type_t *iface) indent--; fprintf(server, "\n"); print_server("_StubMsg.Buffer = (unsigned char __RPC_FAR *)_pRpcMessage->Buffer;\n"); - fprintf(server, "\n"); - print_server("*(("); - write_type(server, def->type, def, def->tname); - fprintf(server, " __RPC_FAR *)_StubMsg.Buffer)++ = _RetVal;\n"); + /* marshall the out arguments */ + marshall_out_arguments(func); } indent--; @@ -649,8 +1169,12 @@ static void write_serverinterfacedecl(type_t *iface) print_server("0,\n"); indent--; print_server("};\n"); - print_server("RPC_IF_HANDLE %s_v%d_%d_s_ifspec = (RPC_IF_HANDLE)& %s___RpcServerInterface;\n", - iface->name, LOWORD(ver), HIWORD(ver), iface->name); + if (old_names) + print_server("RPC_IF_HANDLE %s_ServerIfHandle = (RPC_IF_HANDLE)& %s___RpcServerInterface;\n", + iface->name, iface->name); + else + print_server("RPC_IF_HANDLE %s_v%d_%d_s_ifspec = (RPC_IF_HANDLE)& %s___RpcServerInterface;\n", + iface->name, LOWORD(ver), HIWORD(ver), iface->name); fprintf(server, "\n"); } @@ -666,14 +1190,11 @@ static void write_formatdesc( const char *str ) print_server("\n"); } - -static void write_formatstringsdecl(type_t *iface) +static int get_type_format_string_size(type_t *iface) { + int size = 3; func_t *func; var_t *var; - int byte_count = 1; - - print_server("#define TYPE_FORMAT_STRING_SIZE %d\n", 3); /* FIXME */ /* determine the proc format string size */ func = iface->funcs; @@ -687,16 +1208,74 @@ static void write_formatstringsdecl(type_t *iface) while (NEXT_LINK(var)) var = NEXT_LINK(var); while (var) { - byte_count += 2; /* FIXME: determine real size */ + if (var->ptr_level == 1) + { + if (is_base_type(var->type)) + size += 4; + } + + var = PREV_LINK(var); + } + } + + func = PREV_LINK(func); + } + + return size; +} + + +static int get_proc_format_string_size(type_t *iface) +{ + int size = 1; + func_t *func; + var_t *var; + + /* determine the proc format string size */ + func = iface->funcs; + while (NEXT_LINK(func)) func = NEXT_LINK(func); + while (func) + { + /* argument list size */ + if (func->args) + { + var = func->args; + while (NEXT_LINK(var)) var = NEXT_LINK(var); + while (var) + { + switch (var->ptr_level) + { + case 0: + if (is_base_type(var->type)) + size += 2; + break; + + case 1: + if (is_base_type(var->type)) + size += 4; + break; + } + var = PREV_LINK(var); } } /* return value size */ - byte_count += 2; /* FIXME: determine real size */ + size += 2; func = PREV_LINK(func); } - print_server("#define PROC_FORMAT_STRING_SIZE %d\n", byte_count); + + return size; +} + + +static void write_formatstringsdecl(type_t *iface) +{ + print_server("#define TYPE_FORMAT_STRING_SIZE %d\n", + get_type_format_string_size(iface)); + + print_server("#define PROC_FORMAT_STRING_SIZE %d\n", + get_proc_format_string_size(iface)); fprintf(server, "\n"); write_formatdesc("TYPE"); @@ -759,7 +1338,7 @@ void write_server(ifref_t *ifaces) fprintf(server, "\n"); write_procformatstring(iface->iface); - write_typeformatstring(); + write_typeformatstring(iface->iface); fprintf(server, "\n"); diff --git a/reactos/tools/widl/widl.c b/reactos/tools/widl/widl.c index b952b5a4e40..a5f17570a94 100644 --- a/reactos/tools/widl/widl.c +++ b/reactos/tools/widl/widl.c @@ -58,6 +58,7 @@ static char usage[] = " -H file Name of header file (default is infile.h)\n" " -I path Set include search dir to path (multiple -I allowed)\n" " -N Do not preprocess input\n" +" -o Use old naming conventions\n" " -p Generate proxy\n" " -P file Name of proxy file (default is infile_p.c)\n" " -s Generate server stub\n" @@ -90,6 +91,7 @@ int do_proxies = 0; int do_client = 0; int do_server = 0; int no_preprocess = 0; +int old_names = 0; char *input_name; char *header_name; @@ -142,8 +144,8 @@ int main(int argc,char *argv[]) now = time(NULL); - while((optc = getopt(argc, argv, "cC:d:D:EhH:I:NpP:sS:tT:VW")) != EOF) { - switch(optc) { + while((optc = getopt(argc, argv, "cC:d:D:EhH:I:NopP:sS:tT:VW")) != EOF) { + switch (optc) { case 'c': do_everything = 0; do_client = 1; @@ -174,6 +176,9 @@ int main(int argc,char *argv[]) case 'N': no_preprocess = 1; break; + case 'o': + old_names = 1; + break; case 'p': do_everything = 0; do_proxies = 1; @@ -207,10 +212,10 @@ int main(int argc,char *argv[]) } } - if(do_everything) { + if (do_everything) { do_header = do_typelib = do_proxies = do_client = do_server = 1; } - if(optind < argc) { + if (optind < argc) { input_name = xstrdup(argv[optind]); } else { @@ -218,7 +223,7 @@ int main(int argc,char *argv[]) return 1; } - if(debuglevel) + if (debuglevel) { setbuf(stdout,0); setbuf(stderr,0); diff --git a/reactos/tools/widl/widl.h b/reactos/tools/widl/widl.h index 5b303c35369..db8b95cc396 100644 --- a/reactos/tools/widl/widl.h +++ b/reactos/tools/widl/widl.h @@ -43,6 +43,7 @@ extern int do_typelib; extern int do_proxies; extern int do_client; extern int do_server; +extern int old_names; extern char *input_name; extern char *header_name; diff --git a/reactos/tools/widl/widl.mak b/reactos/tools/widl/widl.mak new file mode 100644 index 00000000000..d552051036e --- /dev/null +++ b/reactos/tools/widl/widl.mak @@ -0,0 +1,123 @@ +WIDL_BASE = $(TOOLS_BASE)$(SEP)widl +WIDL_BASE_ = $(WIDL_BASE)$(SEP) +WIDL_INT = $(INTERMEDIATE_)$(WIDL_BASE) +WIDL_INT_ = $(WIDL_INT)$(SEP) +WIDL_OUT = $(OUTPUT_)$(WIDL_BASE) +WIDL_OUT_ = $(WIDL_OUT)$(SEP) + +$(WIDL_INT): | $(TOOLS_INT) + $(ECHO_MKDIR) + ${mkdir} $@ + +ifneq ($(INTERMEDIATE),$(OUTPUT)) +$(WIDL_OUT): | $(TOOLS_OUT) + $(ECHO_MKDIR) + ${mkdir} $@ +endif + +WIDL_PORT_BASE = $(WIDL_BASE)$(SEP)port +WIDL_PORT_BASE_ = $(WIDL_PORT_BASE)$(SEP) +WIDL_PORT_INT = $(INTERMEDIATE_)$(WIDL_PORT_BASE) +WIDL_PORT_INT_ = $(WIDL_PORT_INT)$(SEP) +WIDL_PORT_OUT = $(OUTPUT_)$(WIDL_PORT_BASE) +WIDL_PORT_OUT_ = $(WIDL_PORT_OUT)$(SEP) + +$(WIDL_PORT_INT): | $(WIDL_INT) + $(ECHO_MKDIR) + ${mkdir} $@ + +ifneq ($(INTERMEDIATE),$(OUTPUT)) +$(WIDL_PORT_OUT): | $(WIDL_OUT) + $(ECHO_MKDIR) + ${mkdir} $@ +endif + +WIDL_TARGET = \ + $(EXEPREFIX)$(WIDL_OUT_)widl$(EXEPOSTFIX) + +WIDL_SOURCES = $(addprefix $(WIDL_BASE_), \ + client.c \ + hash.c \ + header.c \ + lex.yy.c \ + proxy.c \ + server.c \ + typelib.c \ + utils.c \ + widl.c \ + write_msft.c \ + y.tab.c \ + port$(SEP)mkstemps.c \ + ) + +WIDL_OBJECTS = \ + $(addprefix $(INTERMEDIATE_), $(WIDL_SOURCES:.c=.o)) + +WIDL_HOST_CFLAGS = -g -Werror -Wall \ + -DINT16=SHORT -D__USE_W32API -DYYDEBUG=1 \ + -I$(WIDL_BASE) -I$(WPP_BASE) \ + -Iinclude/wine -Iinclude -Iw32api/include + +WIDL_HOST_LFLAGS = -g + +WIDL_LIBS = $(WPP_TARGET) + +.PHONY: widl +widl: $(WIDL_TARGET) + +$(WIDL_TARGET): $(WIDL_OBJECTS) $(WIDL_LIBS) | $(WIDL_OUT) + $(ECHO_LD) + ${host_gcc} $(WIDL_OBJECTS) $(WIDL_LIBS) $(WIDL_HOST_LFLAGS) -o $@ + +$(WIDL_INT_)client.o: $(WIDL_BASE_)client.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)hash.o: $(WIDL_BASE_)hash.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)header.o: $(WIDL_BASE_)header.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)lex.yy.o: $(WIDL_BASE_)lex.yy.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)proxy.o: $(WIDL_BASE_)proxy.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)server.o: $(WIDL_BASE_)server.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)typelib.o: $(WIDL_BASE_)typelib.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)utils.o: $(WIDL_BASE_)utils.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)widl.o: $(WIDL_BASE_)widl.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)write_msft.o: $(WIDL_BASE_)write_msft.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_INT_)y.tab.o: $(WIDL_BASE_)y.tab.c | $(WIDL_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +$(WIDL_PORT_INT_)mkstemps.o: $(WIDL_PORT_BASE_)mkstemps.c | $(WIDL_PORT_INT) + $(ECHO_CC) + ${host_gcc} $(WIDL_HOST_CFLAGS) -c $< -o $@ + +.PHONY: widl_clean +widl_clean: + -@$(rm) $(WIDL_TARGET) $(WIDL_OBJECTS) 2>$(NUL) +clean: widl_clean diff --git a/reactos/tools/widl/widltypes.h b/reactos/tools/widl/widltypes.h index 6ea50d429ee..5802099589d 100644 --- a/reactos/tools/widl/widltypes.h +++ b/reactos/tools/widl/widltypes.h @@ -98,8 +98,10 @@ enum attr_type ATTR_PROPGET, ATTR_PROPPUT, ATTR_PROPPUTREF, + ATTR_PTR, ATTR_PUBLIC, ATTR_READONLY, + ATTR_REF, ATTR_RESTRICTED, ATTR_RETVAL, ATTR_SIZEIS, @@ -108,6 +110,7 @@ enum attr_type ATTR_SWITCHIS, ATTR_SWITCHTYPE, ATTR_TRANSMITAS, + ATTR_UNIQUE, ATTR_UUID, ATTR_V1ENUM, ATTR_VARARG, diff --git a/reactos/tools/widl/write_msft.c b/reactos/tools/widl/write_msft.c index 2d36081a3e8..c9ae86ab24e 100644 --- a/reactos/tools/widl/write_msft.c +++ b/reactos/tools/widl/write_msft.c @@ -33,6 +33,12 @@ #include #include #include +#ifdef WIN32 +#include /* write() */ +#else +#include /* create */ +#include /* write and close */ +#endif /* WIN32 */ #define NONAMELESSUNION #define NONAMELESSSTRUCT diff --git a/reactos/tools/widl/y.tab.c b/reactos/tools/widl/y.tab.c index 082d19fe7bd..ca14f77f36f 100644 --- a/reactos/tools/widl/y.tab.c +++ b/reactos/tools/widl/y.tab.c @@ -91,40 +91,41 @@ #define tPROPGET 341 #define tPROPPUT 342 #define tPROPPUTREF 343 -#define tPUBLIC 344 -#define tREADONLY 345 -#define tREF 346 -#define tRESTRICTED 347 -#define tRETVAL 348 -#define tSHORT 349 -#define tSIGNED 350 -#define tSIZEIS 351 -#define tSIZEOF 352 -#define tSMALL 353 -#define tSOURCE 354 -#define tSTDCALL 355 -#define tSTRING 356 -#define tSTRUCT 357 -#define tSWITCH 358 -#define tSWITCHIS 359 -#define tSWITCHTYPE 360 -#define tTRANSMITAS 361 -#define tTYPEDEF 362 -#define tUNION 363 -#define tUNIQUE 364 -#define tUNSIGNED 365 -#define tUUID 366 -#define tV1ENUM 367 -#define tVARARG 368 -#define tVERSION 369 -#define tVOID 370 -#define tWCHAR 371 -#define tWIREMARSHAL 372 -#define tPOINTERTYPE 373 -#define COND 374 -#define CAST 375 -#define PPTR 376 -#define NEG 377 +#define tPTR 344 +#define tPUBLIC 345 +#define tREADONLY 346 +#define tREF 347 +#define tRESTRICTED 348 +#define tRETVAL 349 +#define tSHORT 350 +#define tSIGNED 351 +#define tSIZEIS 352 +#define tSIZEOF 353 +#define tSMALL 354 +#define tSOURCE 355 +#define tSTDCALL 356 +#define tSTRING 357 +#define tSTRUCT 358 +#define tSWITCH 359 +#define tSWITCHIS 360 +#define tSWITCHTYPE 361 +#define tTRANSMITAS 362 +#define tTYPEDEF 363 +#define tUNION 364 +#define tUNIQUE 365 +#define tUNSIGNED 366 +#define tUUID 367 +#define tV1ENUM 368 +#define tVARARG 369 +#define tVERSION 370 +#define tVOID 371 +#define tWCHAR 372 +#define tWIREMARSHAL 373 +#define tPOINTERTYPE 374 +#define COND 375 +#define CAST 376 +#define PPTR 377 +#define NEG 378 #line 1 "parser.y" @@ -232,7 +233,7 @@ static type_t std_int64 = { "__int64" }; static type_t std_uhyper = { "MIDL_uhyper" }; -#line 109 "parser.y" +#line 106 "parser.y" typedef union { attr_t *attr; expr_t *expr; @@ -256,26 +257,26 @@ typedef union { -#define YYFINAL 459 +#define YYFINAL 462 #define YYFLAG -32768 -#define YYNTBASE 143 +#define YYNTBASE 144 -#define YYTRANSLATE(x) ((unsigned)(x) <= 377 ? yytranslate[x] : 215) +#define YYTRANSLATE(x) ((unsigned)(x) <= 378 ? yytranslate[x] : 216) static const short yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 123, 2, 133, - 134, 126, 125, 120, 124, 142, 127, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 139, 132, 2, - 140, 2, 141, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 124, 2, 134, + 135, 127, 126, 121, 125, 143, 128, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 140, 133, 2, + 141, 2, 142, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 137, 2, 138, 2, 2, 2, 2, 2, 2, 2, + 138, 2, 139, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 135, 122, 136, 128, 2, 2, 2, 2, + 2, 2, 136, 123, 137, 129, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -300,7 +301,7 @@ static const short yytranslate[] = { 0, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, - 117, 118, 119, 121, 129, 130, 131 + 117, 118, 119, 120, 122, 130, 131, 132 }; #if YYDEBUG != 0 @@ -315,132 +316,133 @@ static const short yyprhs[] = { 0, 222, 224, 226, 231, 236, 241, 246, 251, 253, 258, 260, 265, 271, 273, 275, 280, 282, 284, 286, 288, 290, 292, 294, 299, 301, 303, 305, 307, 309, 311, - 313, 318, 320, 322, 327, 332, 337, 342, 344, 346, - 351, 356, 358, 359, 361, 362, 365, 370, 374, 380, - 381, 384, 386, 388, 392, 396, 398, 404, 406, 410, - 411, 413, 415, 417, 419, 425, 429, 433, 437, 441, - 445, 449, 453, 457, 460, 463, 466, 471, 476, 480, - 482, 486, 488, 493, 494, 497, 500, 504, 507, 509, - 514, 522, 523, 525, 526, 528, 530, 532, 534, 536, - 538, 540, 542, 544, 546, 549, 552, 554, 556, 558, - 560, 562, 563, 565, 567, 570, 573, 576, 579, 581, - 583, 586, 589, 592, 597, 598, 601, 604, 607, 610, - 613, 616, 620, 623, 627, 633, 634, 637, 640, 643, - 646, 652, 660, 662, 665, 668, 671, 674, 677, 682, - 685, 688, 690, 692, 696, 698, 702, 704, 706, 712, - 714, 716, 718, 721, 723, 726, 728, 731, 733, 736, - 741, 747, 758, 760 + 313, 315, 317, 322, 324, 326, 331, 336, 341, 343, + 348, 350, 352, 357, 362, 364, 365, 367, 368, 371, + 376, 380, 386, 387, 390, 392, 394, 398, 402, 404, + 410, 412, 416, 417, 419, 421, 423, 425, 431, 435, + 439, 443, 447, 451, 455, 459, 463, 466, 469, 472, + 477, 482, 486, 488, 492, 494, 499, 500, 503, 506, + 510, 513, 515, 520, 528, 529, 531, 532, 534, 536, + 538, 540, 542, 544, 546, 548, 550, 552, 555, 558, + 560, 562, 564, 566, 568, 570, 571, 573, 575, 578, + 581, 584, 587, 589, 591, 594, 597, 600, 605, 606, + 609, 612, 615, 618, 621, 624, 628, 631, 635, 641, + 642, 645, 648, 651, 654, 660, 668, 670, 673, 676, + 679, 682, 685, 690, 693, 696, 698, 700, 704, 706, + 710, 712, 714, 720, 722, 724, 726, 729, 731, 734, + 736, 739, 741, 744, 749, 755, 766, 768 }; -static const short yyrhs[] = { 144, - 0, 0, 144, 202, 0, 144, 201, 0, 144, 190, - 0, 144, 205, 0, 144, 153, 0, 144, 147, 0, - 0, 145, 202, 0, 145, 201, 0, 145, 190, 0, - 145, 205, 0, 145, 147, 0, 0, 146, 181, 132, - 0, 146, 147, 0, 132, 0, 167, 132, 0, 148, - 0, 171, 132, 0, 177, 132, 0, 150, 0, 210, - 132, 0, 212, 132, 0, 213, 132, 0, 37, 133, - 7, 134, 0, 64, 7, 132, 0, 149, 145, 9, - 0, 74, 3, 0, 161, 151, 135, 0, 152, 145, - 136, 0, 0, 156, 0, 116, 0, 157, 0, 156, - 120, 157, 0, 155, 0, 161, 211, 207, 158, 0, - 211, 207, 158, 0, 161, 211, 207, 133, 154, 134, - 0, 211, 207, 133, 154, 134, 0, 0, 137, 159, - 138, 0, 137, 126, 138, 0, 173, 0, 159, 120, - 174, 0, 159, 138, 137, 174, 0, 0, 161, 0, - 137, 162, 138, 0, 163, 0, 162, 120, 163, 0, - 162, 138, 137, 163, 0, 16, 0, 18, 0, 24, - 133, 184, 134, 0, 26, 133, 175, 134, 0, 33, +static const short yyrhs[] = { 145, + 0, 0, 145, 203, 0, 145, 202, 0, 145, 191, + 0, 145, 206, 0, 145, 154, 0, 145, 148, 0, + 0, 146, 203, 0, 146, 202, 0, 146, 191, 0, + 146, 206, 0, 146, 148, 0, 0, 147, 182, 133, + 0, 147, 148, 0, 133, 0, 168, 133, 0, 149, + 0, 172, 133, 0, 178, 133, 0, 151, 0, 211, + 133, 0, 213, 133, 0, 214, 133, 0, 37, 134, + 7, 135, 0, 64, 7, 133, 0, 150, 146, 9, + 0, 74, 3, 0, 162, 152, 136, 0, 153, 146, + 137, 0, 0, 157, 0, 117, 0, 158, 0, 157, + 121, 158, 0, 156, 0, 162, 212, 208, 159, 0, + 212, 208, 159, 0, 162, 212, 208, 134, 155, 135, + 0, 212, 208, 134, 155, 135, 0, 0, 138, 160, + 139, 0, 138, 127, 139, 0, 174, 0, 160, 121, + 175, 0, 160, 139, 138, 175, 0, 0, 162, 0, + 138, 163, 139, 0, 164, 0, 163, 121, 164, 0, + 163, 139, 138, 164, 0, 16, 0, 18, 0, 24, + 134, 185, 135, 0, 26, 134, 176, 135, 0, 33, 0, 34, 0, 35, 0, 36, 0, 38, 0, 39, - 133, 176, 134, 0, 39, 133, 7, 134, 0, 41, - 133, 7, 134, 0, 43, 0, 44, 133, 7, 134, - 0, 45, 133, 7, 134, 0, 45, 133, 176, 134, - 0, 48, 0, 51, 0, 53, 133, 176, 134, 0, - 54, 133, 7, 134, 0, 55, 133, 7, 134, 0, - 56, 133, 176, 134, 0, 57, 133, 7, 134, 0, - 58, 0, 60, 133, 176, 134, 0, 61, 0, 62, - 133, 184, 134, 0, 63, 133, 52, 3, 134, 0, - 66, 0, 69, 0, 73, 133, 172, 134, 0, 75, + 134, 177, 135, 0, 39, 134, 7, 135, 0, 41, + 134, 7, 135, 0, 43, 0, 44, 134, 7, 135, + 0, 45, 134, 7, 135, 0, 45, 134, 177, 135, + 0, 48, 0, 51, 0, 53, 134, 177, 135, 0, + 54, 134, 7, 135, 0, 55, 134, 7, 135, 0, + 56, 134, 177, 135, 0, 57, 134, 7, 135, 0, + 58, 0, 60, 134, 177, 135, 0, 61, 0, 62, + 134, 185, 135, 0, 63, 134, 52, 3, 135, 0, + 66, 0, 69, 0, 73, 134, 173, 135, 0, 75, 0, 79, 0, 80, 0, 81, 0, 82, 0, 83, - 0, 84, 0, 85, 133, 209, 134, 0, 87, 0, - 88, 0, 89, 0, 90, 0, 91, 0, 93, 0, - 94, 0, 97, 133, 172, 134, 0, 100, 0, 102, - 0, 105, 133, 174, 134, 0, 106, 133, 211, 134, - 0, 107, 133, 211, 134, 0, 112, 133, 8, 134, - 0, 113, 0, 114, 0, 115, 133, 214, 134, 0, - 118, 133, 211, 134, 0, 209, 0, 0, 101, 0, - 0, 165, 166, 0, 26, 174, 139, 179, 0, 38, - 139, 179, 0, 32, 211, 184, 140, 176, 0, 0, - 169, 120, 0, 169, 0, 170, 0, 169, 120, 170, - 0, 184, 140, 176, 0, 184, 0, 46, 183, 135, - 168, 136, 0, 173, 0, 172, 120, 173, 0, 0, - 174, 0, 5, 0, 6, 0, 3, 0, 174, 141, - 174, 139, 174, 0, 174, 122, 174, 0, 174, 123, - 174, 0, 174, 125, 174, 0, 174, 124, 174, 0, - 174, 126, 174, 0, 174, 127, 174, 0, 174, 10, - 174, 0, 174, 11, 174, 0, 128, 174, 0, 124, - 174, 0, 126, 174, 0, 133, 211, 134, 174, 0, - 98, 133, 211, 134, 0, 133, 174, 134, 0, 176, - 0, 175, 120, 176, 0, 174, 0, 49, 32, 211, - 184, 0, 0, 178, 179, 0, 180, 132, 0, 160, - 213, 132, 0, 161, 132, 0, 132, 0, 160, 211, - 207, 158, 0, 160, 211, 164, 207, 133, 154, 134, - 0, 0, 184, 0, 0, 3, 0, 4, 0, 3, - 0, 4, 0, 60, 0, 94, 0, 115, 0, 22, - 0, 117, 0, 187, 0, 96, 187, 0, 111, 187, - 0, 50, 0, 42, 0, 20, 0, 47, 0, 52, - 0, 0, 70, 0, 70, 0, 99, 186, 0, 95, - 186, 0, 76, 186, 0, 59, 186, 0, 71, 0, - 28, 0, 29, 3, 0, 29, 4, 0, 161, 188, - 0, 189, 135, 191, 136, 0, 0, 191, 192, 0, - 160, 202, 0, 40, 3, 0, 40, 4, 0, 161, - 193, 0, 86, 139, 0, 195, 180, 132, 0, 77, - 139, 0, 196, 181, 132, 0, 194, 135, 195, 196, - 136, 0, 0, 139, 4, 0, 72, 3, 0, 72, - 4, 0, 161, 199, 0, 200, 198, 135, 146, 136, - 0, 200, 139, 3, 135, 150, 146, 136, 0, 197, - 0, 199, 132, 0, 193, 132, 0, 78, 3, 0, - 78, 4, 0, 161, 203, 0, 204, 135, 146, 136, - 0, 126, 207, 0, 32, 206, 0, 184, 0, 206, - 0, 133, 207, 134, 0, 207, 0, 208, 120, 207, - 0, 92, 0, 110, 0, 103, 183, 135, 178, 136, - 0, 116, 0, 4, 0, 185, 0, 32, 211, 0, - 171, 0, 46, 3, 0, 210, 0, 103, 3, 0, - 213, 0, 109, 3, 0, 108, 160, 211, 208, 0, - 109, 183, 135, 178, 136, 0, 109, 183, 104, 133, - 180, 134, 182, 135, 165, 136, 0, 5, 0, 5, - 142, 5, 0 + 0, 84, 0, 85, 134, 210, 135, 0, 87, 0, + 88, 0, 89, 0, 90, 0, 91, 0, 92, 0, + 93, 0, 94, 0, 95, 0, 98, 134, 173, 135, + 0, 101, 0, 103, 0, 106, 134, 175, 135, 0, + 107, 134, 212, 135, 0, 108, 134, 212, 135, 0, + 111, 0, 113, 134, 8, 135, 0, 114, 0, 115, + 0, 116, 134, 215, 135, 0, 119, 134, 212, 135, + 0, 210, 0, 0, 102, 0, 0, 166, 167, 0, + 26, 175, 140, 180, 0, 38, 140, 180, 0, 32, + 212, 185, 141, 177, 0, 0, 170, 121, 0, 170, + 0, 171, 0, 170, 121, 171, 0, 185, 141, 177, + 0, 185, 0, 46, 184, 136, 169, 137, 0, 174, + 0, 173, 121, 174, 0, 0, 175, 0, 5, 0, + 6, 0, 3, 0, 175, 142, 175, 140, 175, 0, + 175, 123, 175, 0, 175, 124, 175, 0, 175, 126, + 175, 0, 175, 125, 175, 0, 175, 127, 175, 0, + 175, 128, 175, 0, 175, 10, 175, 0, 175, 11, + 175, 0, 129, 175, 0, 125, 175, 0, 127, 175, + 0, 134, 212, 135, 175, 0, 99, 134, 212, 135, + 0, 134, 175, 135, 0, 177, 0, 176, 121, 177, + 0, 175, 0, 49, 32, 212, 185, 0, 0, 179, + 180, 0, 181, 133, 0, 161, 214, 133, 0, 162, + 133, 0, 133, 0, 161, 212, 208, 159, 0, 161, + 212, 165, 208, 134, 155, 135, 0, 0, 185, 0, + 0, 3, 0, 4, 0, 3, 0, 4, 0, 60, + 0, 95, 0, 116, 0, 22, 0, 118, 0, 188, + 0, 97, 188, 0, 112, 188, 0, 112, 0, 50, + 0, 42, 0, 20, 0, 47, 0, 52, 0, 0, + 70, 0, 70, 0, 100, 187, 0, 96, 187, 0, + 76, 187, 0, 59, 187, 0, 71, 0, 28, 0, + 29, 3, 0, 29, 4, 0, 162, 189, 0, 190, + 136, 192, 137, 0, 0, 192, 193, 0, 161, 203, + 0, 40, 3, 0, 40, 4, 0, 162, 194, 0, + 86, 140, 0, 196, 181, 133, 0, 77, 140, 0, + 197, 182, 133, 0, 195, 136, 196, 197, 137, 0, + 0, 140, 4, 0, 72, 3, 0, 72, 4, 0, + 162, 200, 0, 201, 199, 136, 147, 137, 0, 201, + 140, 3, 136, 151, 147, 137, 0, 198, 0, 200, + 133, 0, 194, 133, 0, 78, 3, 0, 78, 4, + 0, 162, 204, 0, 205, 136, 147, 137, 0, 127, + 208, 0, 32, 207, 0, 185, 0, 207, 0, 134, + 208, 135, 0, 208, 0, 209, 121, 208, 0, 93, + 0, 111, 0, 104, 184, 136, 179, 137, 0, 117, + 0, 4, 0, 186, 0, 32, 212, 0, 172, 0, + 46, 3, 0, 211, 0, 104, 3, 0, 214, 0, + 110, 3, 0, 109, 161, 212, 209, 0, 110, 184, + 136, 179, 137, 0, 110, 184, 105, 134, 181, 135, + 183, 136, 166, 137, 0, 5, 0, 5, 143, 5, + 0 }; #endif #if YYDEBUG != 0 static const short yyrline[] = { 0, - 229, 232, 233, 234, 235, 236, 237, 238, 241, 242, - 243, 244, 245, 246, 249, 250, 251, 254, 255, 256, - 257, 258, 259, 260, 261, 262, 265, 267, 270, 273, - 275, 277, 280, 281, 284, 287, 288, 289, 293, 297, - 300, 306, 313, 314, 315, 318, 319, 320, 323, 324, - 327, 331, 332, 333, 336, 338, 339, 340, 341, 342, - 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, - 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, - 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, - 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, - 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, - 393, 394, 397, 398, 401, 402, 407, 411, 417, 424, - 425, 426, 429, 430, 436, 441, 447, 455, 456, 469, - 470, 473, 474, 475, 476, 477, 478, 479, 480, 481, - 482, 483, 484, 485, 486, 487, 488, 489, 490, 493, - 494, 497, 503, 508, 509, 514, 515, 516, 517, 520, - 523, 534, 535, 538, 539, 540, 543, 545, 546, 547, - 548, 551, 552, 553, 554, 555, 567, 568, 569, 570, - 571, 574, 575, 578, 579, 580, 581, 582, 583, 584, - 587, 588, 591, 597, 602, 603, 606, 610, 611, 614, - 626, 627, 630, 631, 634, 649, 650, 653, 654, 657, - 665, 673, 680, 683, 685, 688, 689, 692, 697, 703, - 704, 707, 708, 709, 712, 714, 717, 719, 722, 732, - 733, 734, 735, 736, 737, 738, 739, 740, 741, 744, - 756, 760, 773, 775 + 227, 230, 231, 232, 233, 234, 235, 236, 239, 240, + 241, 242, 243, 244, 247, 248, 249, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 263, 265, 268, 271, + 273, 275, 278, 279, 282, 285, 286, 287, 291, 295, + 298, 304, 311, 312, 313, 316, 317, 318, 321, 322, + 325, 329, 330, 331, 334, 336, 337, 338, 339, 340, + 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, + 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, + 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, + 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 394, 395, 398, 399, 402, 403, 408, + 412, 418, 425, 426, 427, 430, 431, 437, 442, 448, + 456, 457, 470, 471, 474, 475, 476, 477, 478, 479, + 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, + 490, 491, 494, 495, 498, 504, 509, 510, 515, 516, + 517, 518, 521, 524, 535, 536, 539, 540, 541, 544, + 546, 547, 548, 549, 552, 553, 554, 555, 556, 568, + 570, 571, 572, 573, 574, 577, 578, 581, 582, 583, + 584, 585, 586, 587, 590, 591, 594, 600, 605, 606, + 609, 613, 614, 617, 629, 630, 633, 634, 637, 652, + 653, 656, 657, 660, 668, 676, 683, 686, 688, 691, + 692, 695, 700, 706, 707, 710, 711, 712, 715, 717, + 720, 722, 725, 735, 736, 737, 738, 739, 740, 741, + 742, 743, 744, 747, 759, 763, 776, 778 }; #endif @@ -460,10 +462,10 @@ static const char * const yytname[] = { "$","error","$undefined.","aIDENTIFIER "tINLINE","tINPUTSYNC","tINT","tINT64","tINTERFACE","tLENGTHIS","tLIBRARY","tLOCAL", "tLONG","tMETHODS","tMODULE","tNONCREATABLE","tOBJECT","tODL","tOLEAUTOMATION", "tOPTIONAL","tOUT","tPOINTERDEFAULT","tPROPERTIES","tPROPGET","tPROPPUT","tPROPPUTREF", -"tPUBLIC","tREADONLY","tREF","tRESTRICTED","tRETVAL","tSHORT","tSIGNED","tSIZEIS", -"tSIZEOF","tSMALL","tSOURCE","tSTDCALL","tSTRING","tSTRUCT","tSWITCH","tSWITCHIS", -"tSWITCHTYPE","tTRANSMITAS","tTYPEDEF","tUNION","tUNIQUE","tUNSIGNED","tUUID", -"tV1ENUM","tVARARG","tVERSION","tVOID","tWCHAR","tWIREMARSHAL","tPOINTERTYPE", +"tPTR","tPUBLIC","tREADONLY","tREF","tRESTRICTED","tRETVAL","tSHORT","tSIGNED", +"tSIZEIS","tSIZEOF","tSMALL","tSOURCE","tSTDCALL","tSTRING","tSTRUCT","tSWITCH", +"tSWITCHIS","tSWITCHTYPE","tTRANSMITAS","tTYPEDEF","tUNION","tUNIQUE","tUNSIGNED", +"tUUID","tV1ENUM","tVARARG","tVERSION","tVOID","tWCHAR","tWIREMARSHAL","tPOINTERTYPE", "','","COND","'|'","'&'","'-'","'+'","'*'","'/'","'~'","CAST","PPTR","NEG","';'", "'('","')'","'{'","'}'","'['","']'","':'","'='","'?'","'.'","input","gbl_statements", "imp_statements","int_statements","statement","cppquote","import_start","import", @@ -480,31 +482,31 @@ static const char * const yytname[] = { "$","error","$undefined.","aIDENTIFIER #endif static const short yyr1[] = { 0, - 143, 144, 144, 144, 144, 144, 144, 144, 145, 145, - 145, 145, 145, 145, 146, 146, 146, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 148, 149, 150, 151, - 152, 153, 154, 154, 155, 156, 156, 156, 157, 157, - 157, 157, 158, 158, 158, 159, 159, 159, 160, 160, - 161, 162, 162, 162, 163, 163, 163, 163, 163, 163, - 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, - 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, - 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, - 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, - 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, - 163, 163, 164, 164, 165, 165, 166, 166, 167, 168, - 168, 168, 169, 169, 170, 170, 171, 172, 172, 173, - 173, 174, 174, 174, 174, 174, 174, 174, 174, 174, - 174, 174, 174, 174, 174, 174, 174, 174, 174, 175, - 175, 176, 177, 178, 178, 179, 179, 179, 179, 180, - 181, 182, 182, 183, 183, 183, 184, 184, 184, 184, - 184, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 186, 186, 187, 187, 187, 187, 187, 187, 187, - 188, 188, 189, 190, 191, 191, 192, 193, 193, 194, - 195, 195, 196, 196, 197, 198, 198, 199, 199, 200, - 201, 201, 201, 202, 202, 203, 203, 204, 205, 206, - 206, 207, 207, 207, 208, 208, 209, 209, 210, 211, - 211, 211, 211, 211, 211, 211, 211, 211, 211, 212, - 213, 213, 214, 214 + 144, 145, 145, 145, 145, 145, 145, 145, 146, 146, + 146, 146, 146, 146, 147, 147, 147, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 149, 150, 151, 152, + 153, 154, 155, 155, 156, 157, 157, 157, 158, 158, + 158, 158, 159, 159, 159, 160, 160, 160, 161, 161, + 162, 163, 163, 163, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 165, 165, 166, 166, 167, + 167, 168, 169, 169, 169, 170, 170, 171, 171, 172, + 173, 173, 174, 174, 175, 175, 175, 175, 175, 175, + 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, + 175, 175, 176, 176, 177, 178, 179, 179, 180, 180, + 180, 180, 181, 182, 183, 183, 184, 184, 184, 185, + 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, + 186, 186, 186, 186, 186, 187, 187, 188, 188, 188, + 188, 188, 188, 188, 189, 189, 190, 191, 192, 192, + 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, + 199, 200, 200, 201, 202, 202, 202, 203, 203, 204, + 204, 205, 206, 207, 207, 208, 208, 208, 209, 209, + 210, 210, 211, 212, 212, 212, 212, 212, 212, 212, + 212, 212, 212, 213, 214, 214, 215, 215 }; static const short yyr2[] = { 0, @@ -518,333 +520,327 @@ static const short yyr2[] = { 0, 1, 1, 4, 4, 4, 4, 4, 1, 4, 1, 4, 5, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, - 4, 1, 1, 4, 4, 4, 4, 1, 1, 4, - 4, 1, 0, 1, 0, 2, 4, 3, 5, 0, - 2, 1, 1, 3, 3, 1, 5, 1, 3, 0, - 1, 1, 1, 1, 5, 3, 3, 3, 3, 3, - 3, 3, 3, 2, 2, 2, 4, 4, 3, 1, - 3, 1, 4, 0, 2, 2, 3, 2, 1, 4, - 7, 0, 1, 0, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, - 1, 0, 1, 1, 2, 2, 2, 2, 1, 1, - 2, 2, 2, 4, 0, 2, 2, 2, 2, 2, - 2, 3, 2, 3, 5, 0, 2, 2, 2, 2, - 5, 7, 1, 2, 2, 2, 2, 2, 4, 2, - 2, 1, 1, 3, 1, 3, 1, 1, 5, 1, - 1, 1, 2, 1, 2, 1, 2, 1, 2, 4, - 5, 10, 1, 3 + 1, 1, 4, 1, 1, 4, 4, 4, 1, 4, + 1, 1, 4, 4, 1, 0, 1, 0, 2, 4, + 3, 5, 0, 2, 1, 1, 3, 3, 1, 5, + 1, 3, 0, 1, 1, 1, 1, 5, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 2, 2, 4, + 4, 3, 1, 3, 1, 4, 0, 2, 2, 3, + 2, 1, 4, 7, 0, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, + 1, 1, 1, 1, 1, 0, 1, 1, 2, 2, + 2, 2, 1, 1, 2, 2, 2, 4, 0, 2, + 2, 2, 2, 2, 2, 3, 2, 3, 5, 0, + 2, 2, 2, 2, 5, 7, 1, 2, 2, 2, + 2, 2, 4, 2, 2, 1, 1, 3, 1, 3, + 1, 1, 5, 1, 1, 1, 2, 1, 2, 1, + 2, 1, 2, 4, 5, 10, 1, 3 }; static const short yydefact[] = { 2, - 1, 0, 0, 0, 164, 0, 0, 0, 164, 49, - 164, 18, 0, 8, 20, 9, 23, 9, 7, 0, - 0, 0, 0, 0, 5, 0, 0, 213, 0, 206, - 4, 3, 0, 6, 0, 0, 0, 231, 179, 172, - 190, 0, 178, 164, 180, 177, 181, 182, 184, 189, - 182, 182, 0, 182, 164, 164, 0, 230, 173, 234, - 232, 174, 236, 0, 238, 0, 198, 199, 165, 166, - 0, 0, 0, 208, 209, 0, 0, 50, 0, 55, + 1, 0, 0, 0, 167, 0, 0, 0, 167, 49, + 167, 18, 0, 8, 20, 9, 23, 9, 7, 0, + 0, 0, 0, 0, 5, 0, 0, 217, 0, 210, + 4, 3, 0, 6, 0, 0, 0, 235, 183, 175, + 194, 0, 182, 167, 184, 181, 185, 186, 188, 193, + 186, 186, 0, 186, 167, 167, 180, 234, 176, 238, + 236, 177, 240, 0, 242, 0, 202, 203, 168, 169, + 0, 0, 0, 212, 213, 0, 0, 50, 0, 55, 56, 0, 0, 59, 60, 61, 62, 63, 0, 0, 67, 0, 0, 71, 72, 0, 0, 0, 0, 0, 78, 0, 80, 0, 0, 83, 84, 0, 86, 87, 88, 89, 90, 91, 92, 0, 94, 95, 96, 97, - 98, 227, 99, 100, 0, 102, 103, 0, 0, 0, - 228, 0, 108, 109, 0, 0, 0, 52, 112, 0, - 0, 0, 0, 0, 0, 193, 200, 210, 218, 19, - 21, 22, 195, 215, 0, 214, 0, 0, 15, 24, - 25, 26, 233, 235, 183, 188, 187, 186, 175, 185, - 237, 239, 176, 167, 168, 169, 170, 171, 0, 0, - 120, 0, 28, 154, 0, 0, 154, 0, 0, 0, + 98, 99, 100, 101, 102, 0, 104, 105, 0, 0, + 0, 109, 0, 111, 112, 0, 0, 0, 52, 115, + 0, 0, 0, 0, 0, 0, 197, 204, 214, 222, + 19, 21, 22, 199, 219, 0, 218, 0, 0, 15, + 24, 25, 26, 237, 239, 187, 192, 191, 190, 178, + 189, 241, 243, 179, 170, 171, 172, 173, 174, 0, + 0, 123, 0, 28, 157, 0, 0, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 130, 0, 130, 0, 0, 0, 0, 0, 0, - 0, 51, 29, 14, 0, 12, 11, 10, 13, 32, - 191, 192, 30, 216, 217, 31, 49, 0, 49, 0, - 207, 15, 49, 0, 27, 0, 122, 123, 126, 153, - 49, 0, 0, 0, 222, 223, 225, 240, 49, 49, - 0, 134, 132, 133, 0, 0, 0, 0, 0, 152, - 0, 150, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 128, 131, 0, - 0, 0, 0, 0, 0, 243, 0, 0, 53, 0, - 194, 0, 196, 201, 0, 0, 0, 49, 0, 49, - 219, 17, 0, 0, 119, 127, 121, 0, 159, 229, - 0, 50, 155, 0, 221, 220, 0, 0, 0, 241, - 57, 0, 145, 146, 144, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 58, 65, 64, - 66, 68, 69, 70, 73, 74, 75, 76, 77, 79, - 81, 0, 130, 85, 93, 101, 104, 105, 106, 107, - 0, 110, 111, 54, 197, 203, 0, 202, 205, 0, - 15, 211, 113, 16, 124, 125, 238, 158, 156, 224, - 226, 162, 0, 149, 0, 142, 143, 136, 137, 139, - 138, 140, 141, 0, 151, 82, 129, 244, 43, 204, - 49, 114, 0, 157, 0, 163, 148, 147, 0, 130, - 160, 212, 0, 115, 135, 0, 0, 46, 33, 0, - 45, 0, 44, 230, 0, 38, 34, 36, 0, 0, - 0, 0, 242, 116, 47, 0, 161, 0, 0, 43, - 0, 49, 48, 37, 43, 33, 40, 49, 118, 33, - 39, 0, 117, 0, 42, 41, 0, 0, 0 + 0, 0, 133, 0, 133, 0, 0, 0, 0, 0, + 0, 0, 51, 29, 14, 0, 12, 11, 10, 13, + 32, 195, 196, 30, 220, 221, 31, 49, 0, 49, + 0, 211, 15, 49, 0, 27, 0, 125, 126, 129, + 156, 49, 0, 0, 0, 226, 227, 229, 244, 49, + 49, 0, 137, 135, 136, 0, 0, 0, 0, 0, + 155, 0, 153, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 131, 134, + 231, 232, 0, 0, 0, 0, 0, 0, 247, 0, + 0, 53, 0, 198, 0, 200, 205, 0, 0, 0, + 49, 0, 49, 223, 17, 0, 0, 122, 130, 124, + 0, 162, 233, 0, 50, 158, 0, 225, 224, 0, + 0, 0, 245, 57, 0, 148, 149, 147, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 58, 65, 64, 66, 68, 69, 70, 73, 74, 75, + 76, 77, 79, 81, 0, 133, 85, 93, 103, 106, + 107, 108, 110, 0, 113, 114, 54, 201, 207, 0, + 206, 209, 0, 15, 215, 116, 16, 127, 128, 242, + 161, 159, 228, 230, 165, 0, 152, 0, 145, 146, + 139, 140, 142, 141, 143, 144, 0, 154, 82, 132, + 248, 43, 208, 49, 117, 0, 160, 0, 166, 151, + 150, 0, 133, 163, 216, 0, 118, 138, 0, 0, + 46, 33, 0, 45, 0, 44, 234, 0, 38, 34, + 36, 0, 0, 0, 0, 246, 119, 47, 0, 164, + 0, 0, 43, 0, 49, 48, 37, 43, 33, 40, + 49, 121, 33, 39, 0, 120, 0, 42, 41, 0, + 0, 0 }; -static const short yydefgoto[] = { 457, - 1, 140, 233, 302, 15, 16, 17, 145, 18, 19, - 425, 426, 427, 428, 411, 417, 303, 78, 137, 138, - 403, 420, 434, 21, 236, 237, 238, 60, 277, 278, - 260, 261, 262, 23, 241, 313, 314, 304, 405, 71, - 245, 61, 166, 62, 146, 24, 216, 227, 293, 26, - 27, 229, 298, 28, 158, 29, 30, 217, 218, 149, - 33, 219, 246, 247, 248, 139, 63, 430, 36, 65, - 287 +static const short yydefgoto[] = { 460, + 1, 141, 234, 305, 15, 16, 17, 146, 18, 19, + 428, 429, 430, 431, 414, 420, 306, 78, 138, 139, + 406, 423, 437, 21, 237, 238, 239, 60, 278, 279, + 261, 262, 263, 23, 242, 316, 317, 307, 408, 71, + 246, 61, 167, 62, 147, 24, 217, 228, 296, 26, + 27, 230, 301, 28, 159, 29, 30, 218, 219, 150, + 33, 220, 247, 248, 249, 140, 63, 433, 36, 65, + 290 }; static const short yypact[] = {-32768, - 609, 598, -98, 138, 144, 22, 30, 172, 144, -61, - 144,-32768, 791,-32768,-32768,-32768,-32768,-32768,-32768, 35, - -49, -42, -32, -14,-32768, -24, -5,-32768, 32, -3, --32768,-32768, 44,-32768, 49, 59, 68,-32768,-32768,-32768, --32768, 598,-32768, 185,-32768,-32768,-32768, 63,-32768,-32768, - 63, 63, 27, 63, 220, 260, 27,-32768,-32768,-32768, --32768,-32768,-32768, 17,-32768, 176,-32768,-32768,-32768,-32768, - 69, 598, 74,-32768,-32768, 85, 598,-32768, -73,-32768, --32768, 53, 76,-32768,-32768,-32768,-32768,-32768, 102, 106, --32768, 115, 118,-32768,-32768, 121, 124, 134, 136, 148, --32768, 157,-32768, 158, 159,-32768,-32768, 160,-32768,-32768, --32768,-32768,-32768,-32768,-32768, 161,-32768,-32768,-32768,-32768, --32768,-32768,-32768,-32768, 162,-32768,-32768, 165, 169, 178, --32768, 180,-32768,-32768, 182, 183, -87,-32768,-32768, 505, - 587, 271, 234, 275, 174,-32768,-32768,-32768,-32768,-32768, --32768,-32768,-32768,-32768, 231,-32768, 280, 186,-32768,-32768, --32768,-32768,-32768, 187,-32768,-32768,-32768,-32768,-32768,-32768, - 187, -72,-32768,-32768,-32768,-32768,-32768,-32768, 184, 194, - 17, 17,-32768,-32768, 267, 197,-32768, 17, 389, 329, - 311, 313, 337, 389, 324, 330, 389, 331, 389, 17, - 293, 389, -58, 389, 389, 598, 598, 340, 344, 598, - 791, 213,-32768,-32768, 51,-32768,-32768,-32768,-32768,-32768, --32768,-32768,-32768,-32768,-32768,-32768, 149, 212, -50, 217, --32768,-32768, 634, 389,-32768, 218, 233,-32768, 215,-32768, - -20, -6, 267, 267,-32768,-32768,-32768, 236, -61, 129, - 223,-32768,-32768,-32768, 225, 389, 389, 389, 477, 104, - -90,-32768, 226, 232, 239, 240, 242, 244, 246, 247, - 250, 251, 254, 255, 256, 361, -64,-32768, 104, 257, - -62, 71, 262, 264, 265, 237, 269, 274,-32768, 791, --32768, -11,-32768,-32768, 270, 598, 278, 151, 301, 676, --32768,-32768, 598, 281,-32768,-32768, 17, 389,-32768,-32768, - 598, 283,-32768, 284,-32768,-32768, 294, 267, 295,-32768, --32768, 598, 276, 276, 276, 91, 298, 389, 389, 389, - 389, 389, 389, 389, 389, 389, 389,-32768,-32768,-32768, + 514, 533, -109, 177, 188, 15, 30, 207, 188, -79, + 188,-32768, 742,-32768,-32768,-32768,-32768,-32768,-32768, 24, + -83, -54, -36, -20,-32768, -27, -5,-32768, -11, 2, +-32768,-32768, 17,-32768, 6, 42, 60,-32768,-32768,-32768, +-32768, 533,-32768, 215,-32768,-32768,-32768, 54,-32768,-32768, + 54, 54, 29, 54, 265, 270, 29,-32768,-32768,-32768, +-32768,-32768,-32768, 299,-32768, 175,-32768,-32768,-32768,-32768, + 66, 533, 83,-32768,-32768, 91, 533,-32768, -74,-32768, +-32768, 96, 98,-32768,-32768,-32768,-32768,-32768, 102, 117, +-32768, 119, 126,-32768,-32768, 129, 131, 141, 145, 151, +-32768, 160,-32768, 161, 162,-32768,-32768, 163,-32768,-32768, +-32768,-32768,-32768,-32768,-32768, 164,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768, 167,-32768,-32768, 178, 180, + 182,-32768, 184,-32768,-32768, 186, 187, -88,-32768,-32768, + 498, 595, 274, 306, 279, 189,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768, 225,-32768, 284, 191,-32768, +-32768,-32768,-32768,-32768, 192,-32768,-32768,-32768,-32768,-32768, +-32768, 192, -71,-32768,-32768,-32768,-32768,-32768,-32768, 183, + 194, 299, 299,-32768,-32768, 394, 198,-32768, 299, 220, + 106, 316, 326, 130, 220, 328, 329, 220, 330, 220, + 299, 287, 220, -45, 220, 220, 533, 533, 332, 336, + 533, 742, 204,-32768,-32768, 32,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768,-32768,-32768, 152, 203, -57, + 212,-32768,-32768, 616, 220,-32768, 213, 237,-32768, 214, +-32768, -30, -9, 394, 394,-32768,-32768,-32768, 239, -79, + -10, 235,-32768,-32768,-32768, 240, 220, 220, 220, 472, + 357, -61,-32768, 241, 243, 244, 247, 254, 255, 257, + 258, 268, 269, 271, 276, 281, 372, -50,-32768, 357, +-32768,-32768, 282, -46, 72, 283, 288, 289, 262, 292, + 293,-32768, 742,-32768, -14,-32768,-32768, 290, 533, 296, + 154, 358, 626,-32768,-32768, 533, 298,-32768,-32768, 299, + 220,-32768,-32768, 533, 301,-32768, 302,-32768,-32768, 297, + 394, 303,-32768,-32768, 533, 294, 294, 294, 238, 304, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, -32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, --32768, 299, 389,-32768,-32768,-32768,-32768,-32768,-32768,-32768, - 407,-32768,-32768,-32768,-32768,-32768, 267,-32768,-32768, 307, --32768,-32768, 341,-32768,-32768,-32768, 312,-32768,-32768,-32768, --32768, 17, 309,-32768, 389, 276, 276, 245, 36, 58, - 58, 12, 12, 297,-32768,-32768,-32768,-32768, 315,-32768, - 684,-32768, 267,-32768, 321,-32768,-32768, 276, 389, 483, --32768,-32768, 326,-32768, 104, 54, -71,-32768, 230, -2, --32768, 389, 323, -55, 314,-32768, 347,-32768, 598, 267, - 389, 334,-32768,-32768, 104, 389,-32768, 355, 267, -45, - 352, -66, 104,-32768, -27, 230,-32768, -66,-32768, 230, --32768, 335,-32768, 350,-32768,-32768, 446, 468,-32768 +-32768,-32768,-32768,-32768, 307, 220,-32768,-32768,-32768,-32768, +-32768,-32768,-32768, 436,-32768,-32768,-32768,-32768,-32768, 394, +-32768,-32768, 314,-32768,-32768, 353,-32768,-32768,-32768, 318, +-32768,-32768,-32768,-32768, 299, 321,-32768, 220, 294, 294, + 31, 59, 35, 35, 18, 18, 260,-32768,-32768,-32768, +-32768, 319,-32768, 636,-32768, 394,-32768, 322,-32768,-32768, + 294, 220, 407,-32768,-32768, 331,-32768, 357, 49, -72, +-32768, 234, -16,-32768, 220, 324, -34, 325,-32768, 342, +-32768, 533, 394, 220, 339,-32768,-32768, 357, 220,-32768, + 349, 394, -4, 346, -98, 357,-32768, 56, 234,-32768, + -98,-32768, 234,-32768, 333,-32768, 355,-32768,-32768, 464, + 491,-32768 }; static const short yypgoto[] = {-32768, --32768, 467, -222, 18,-32768,-32768, 191,-32768,-32768,-32768, - -322,-32768,-32768, 56, -395,-32768, -8, -1,-32768, -186, --32768,-32768,-32768,-32768,-32768,-32768, 188, 5, 292, -336, - -163,-32768, -181,-32768, 316, -343, -201, 200,-32768, 29, - -63,-32768, 103, 78,-32768,-32768, 499,-32768,-32768, -13, --32768,-32768,-32768,-32768,-32768, -12,-32768, 500, 4,-32768, --32768, 501, 266, -229,-32768, 303, 10, 1,-32768, 3, +-32768, 475, -220, 8,-32768,-32768, 193,-32768,-32768,-32768, + -236,-32768,-32768, 55, -380,-32768, -8, -1,-32768, -198, +-32768,-32768,-32768,-32768,-32768,-32768, 195, 5, 308, -329, + -167,-32768, -179,-32768, 309, -365, -206, 197,-32768, 21, + -63,-32768, 63, 171,-32768,-32768, 500,-32768,-32768, -13, +-32768,-32768,-32768,-32768,-32768, -12,-32768, 501, 4,-32768, +-32768, 502, 266, -227,-32768, 311, 10, 1,-32768, 3, -32768 }; -#define YYLAST 909 +#define YYLAST 861 static const short yytable[] = { 20, - 179, 77, 64, 37, 32, 22, 147, 148, 264, 300, - 35, 268, 269, 316, 317, 272, 397, 274, 14, 174, - 175, 328, 329, 431, 289, 242, 295, 297, 4, 337, - 186, -165, 211, 122, 66, 432, 73, 76, 279, 79, - 279, 282, 163, 338, 447, 328, 329, 319, 422, 451, - 212, 131, 305, 72, 41, 353, 252, 353, 253, 254, - 8, 187, -165, 142, -35, 309, 423, 328, 329, 354, - 13, 356, 182, 418, 4, 13, 176, 185, -35, 142, - 328, 329, 150, 76, 79, 48, 13, 446, 381, 151, - 4, 410, 323, 324, 325, 326, 49, 50, 449, 152, - 328, 329, 51, 364, 453, 450, 8, 154, 143, 410, - 177, 309, 144, 328, 329, 310, 13, 239, 240, 243, - 153, 52, 8, 452, 251, 54, 376, 454, 144, 155, - 169, 178, 165, 433, 173, 157, 275, 399, 215, 215, - 67, 68, 37, 37, 22, 22, 69, 70, 401, 35, - 35, 255, 336, 167, 168, 395, 170, 214, 214, 332, - 333, 334, 335, 156, 386, 387, 388, 389, 390, 391, - 392, 393, 394, 413, 74, 75, 336, 256, 159, 257, - 160, 258, 180, 334, 335, 188, 259, 164, 70, 279, - 161, 421, 330, 331, 332, 333, 334, 335, 336, 162, - 440, 147, 148, 181, 357, 183, 283, 284, 189, 445, - 288, 336, 330, 331, 332, 333, 334, 335, 292, 184, - 296, 408, 171, 70, 384, 330, 331, 332, 333, 334, - 335, 336, 311, 38, 190, 37, 223, 22, 191, 312, - 296, 311, 35, 239, 336, 415, 279, 192, 312, 39, - 193, 40, 324, 194, 328, 329, 195, 41, 435, 327, - 309, 42, 172, 70, 320, 13, 196, 441, 197, 174, - 175, 43, 443, 221, 222, 44, 45, 224, 225, 46, - 198, 47, 230, 231, 291, 13, 369, 13, 48, 199, - 200, 201, 202, 203, 204, 365, 367, 205, 242, 49, - 50, 206, 37, 373, 22, 51, 328, 329, 226, 35, - 207, 367, 208, 377, 209, 210, 228, 265, 406, 266, - 232, -165, 383, 234, 52, 53, 176, 235, 54, 249, - 270, 252, 55, 253, 254, 263, 271, 273, 56, 252, - 57, 253, 254, 267, 276, 424, 59, 285, 286, 290, - 294, 299, 307, 306, 308, 318, 321, 322, 38, 339, - 177, 328, 329, 352, 7, 340, 13, 331, 332, 333, - 334, 335, 341, 342, 39, 343, 40, 344, 361, 345, - 346, 178, 41, 347, 348, 336, 42, 349, 350, 351, - 355, 252, 243, 253, 254, 358, 43, 359, 360, 244, - 44, 45, 362, 37, 46, 22, 47, 363, 366, 368, - 35, 398, 374, 48, 378, 379, 336, 429, 330, 331, - 332, 333, 334, 335, 49, 50, 255, 380, 382, 439, - 51, 385, 396, 311, 255, 409, 429, 336, 400, 311, - 312, 402, 407, 404, 429, 458, 312, 437, 429, 52, - 53, 410, 256, 54, 257, 414, 258, 55, 419, 436, - 256, 259, 257, 56, 258, 57, 438, 459, 455, 259, - 58, 59, 442, 330, 331, 332, 333, 334, 335, 252, - 38, 253, 254, 456, 141, 252, 255, 253, 254, 371, - 448, 13, 336, 444, 375, 281, 39, 370, 40, 25, - 31, 34, 250, 0, 41, 280, 0, 315, 42, 0, - 0, 0, 256, 213, 257, 0, 258, 0, 43, 0, - 0, 259, 44, 45, 0, 0, 46, 0, 47, 0, - 0, 0, 0, 0, 0, 48, 2, 0, 0, 0, - 0, 3, 0, 0, 4, 0, 49, 50, 0, 0, - 5, 0, 51, 6, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, - 0, 52, 53, 0, 255, 54, 8, 0, 0, 55, - 255, 0, 0, 0, 0, 56, 0, 57, 0, 0, - 0, 0, 58, 59, 0, 0, 0, 0, 0, 0, - 256, 38, 257, 0, 258, 0, 256, 9, 416, 259, - 258, 0, 10, 11, 0, 259, 0, 39, 2, 40, - 0, 0, 0, 3, 0, 41, 4, 0, 0, 42, - 0, 0, 5, 0, 0, 6, 12, 0, 0, 43, - 2, 13, 0, 44, 45, 3, 0, 46, 4, 47, - 7, 0, 0, 0, 5, 0, 48, 6, 8, 0, - 0, 0, 0, 0, 0, 2, 0, 49, 50, 0, - 3, 0, 7, 51, 0, 0, 0, 0, 0, 5, - 8, 0, 6, 0, 0, 0, 0, 0, 0, 9, - 0, 0, 52, 53, 10, 11, 54, 7, 0, 0, - 55, 0, 0, 0, 0, 0, 56, 2, 57, 0, - 0, 9, 3, 58, 59, 2, 10, 11, 12, 0, - 3, 5, 220, 13, 6, 0, 0, 0, 0, 5, - 0, 0, 6, 0, 0, 0, 9, 0, 0, 7, - 12, 10, 11, 0, 0, 13, 0, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 12, 0, 0, 0, 301, - 13, 0, 0, 0, 0, 0, 0, 0, 9, 0, - 0, 0, 0, 10, 11, 0, 9, 0, 0, 0, - 0, 10, 11, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 80, 12, 81, 0, - 0, 372, 13, 0, 82, 12, 83, 0, 0, 412, - 13, 0, 0, 84, 85, 86, 87, 0, 88, 89, - 0, 90, 0, 91, 92, 93, 0, 0, 94, 0, - 0, 95, 0, 96, 97, 98, 99, 100, 101, 0, - 102, 103, 104, 105, 0, 0, 106, 0, 0, 107, - 0, 0, 0, 108, 0, 109, 0, 0, 0, 110, - 111, 112, 113, 114, 115, 116, 0, 117, 118, 119, - 120, 121, 122, 123, 124, 0, 0, 125, 0, 0, - 126, 0, 127, 0, 0, 128, 129, 130, 0, 0, - 131, 0, 132, 133, 134, 135, 0, 0, 136 + 180, 77, 64, 37, 32, 22, 148, 149, 14, 434, + 35, 265, 303, 292, 269, 270, 319, 320, 273, 298, + 275, 435, 243, 300, 66, 4, 400, 331, 332, 76, + 187, 79, 212, -168, 312, 280, 73, 280, 285, 13, + 331, 332, 164, 322, 331, 332, 72, 281, 425, 151, + 213, 253, 143, 254, 255, 308, 41, 8, 13, 340, + 143, 188, 450, 4, -168, 282, 426, 454, 331, 332, + 356, 4, 183, 341, 356, 76, 79, 186, 152, 452, + 13, 331, 332, 421, 357, 456, -35, 48, 359, 326, + 327, 328, 329, 384, 367, 8, 153, 144, 49, 50, + -35, 145, 312, 8, 51, 155, 313, 13, 253, 145, + 254, 255, 264, 168, 169, 154, 171, 244, 240, 241, + 436, 157, 312, 166, 52, 252, 323, 13, 54, 449, + 156, 379, 253, 413, 254, 255, 268, 276, 161, 216, + 216, 158, 402, 37, 37, 22, 22, 256, 215, 215, + 35, 35, 160, 404, 334, 335, 336, 337, 338, 339, + 398, 337, 338, 389, 390, 391, 392, 393, 394, 395, + 396, 397, 339, 257, 162, 258, 339, 259, 416, 67, + 68, 181, 260, 335, 336, 337, 338, 424, 280, 453, + 69, 70, 163, 413, 333, 334, 335, 336, 337, 338, + 339, 182, 148, 149, 256, 443, 360, 286, 287, 74, + 75, 291, 455, 339, 448, 184, 457, 165, 70, 295, + 411, 299, 253, 170, 254, 255, 185, 174, 256, 189, + 257, 190, 258, 314, 259, 191, 37, 38, 22, 260, + 315, 299, 314, 35, 418, 280, 240, 331, 332, 315, + 192, 327, 193, 39, 257, 40, 258, 438, 259, 194, + 330, 41, 195, 260, 196, 42, 444, 172, 70, 331, + 332, 446, 173, 70, 197, 43, 222, 223, 198, 44, + 45, 225, 226, 46, 199, 47, 231, 232, 294, 13, + 372, 13, 48, 200, 201, 202, 203, 204, 368, 370, + 205, 175, 176, 49, 50, 37, 376, 22, 224, 51, + 229, 206, 35, 207, 370, 208, 380, 209, 256, 210, + 211, 409, 266, 235, 227, 386, 233, -168, 236, 52, + 53, 250, 267, 54, 271, 272, 274, 55, 277, 288, + 289, 293, 297, 56, 257, 57, 258, 302, 259, 309, + 427, 59, 38, 260, 311, 331, 332, 310, 177, 321, + 333, 334, 335, 336, 337, 338, 331, 332, 39, 324, + 40, 13, 387, 325, 355, 342, 41, 343, 344, 339, + 42, 345, 333, 334, 335, 336, 337, 338, 346, 347, + 43, 348, 349, 178, 44, 45, 175, 176, 46, 412, + 47, 339, 350, 351, 364, 352, 37, 48, 22, 253, + 353, 254, 255, 35, 179, 354, 358, 361, 49, 50, + 432, 7, 362, 363, 51, 243, 365, 366, 371, 369, + 377, 383, 442, 381, 382, 339, 314, 385, 388, 432, + 401, 399, 314, 315, 52, 53, 403, 432, 54, 315, + 407, 432, 55, 177, 405, 410, 413, 417, 56, 440, + 57, 439, 441, 461, 422, 58, 59, 458, 333, 334, + 335, 336, 337, 338, 253, 38, 254, 255, 445, 333, + 334, 335, 336, 337, 338, 451, 13, 339, 178, 459, + 462, 39, 142, 40, 374, 447, 251, 373, 339, 41, + 25, 31, 34, 42, 378, 256, 214, 0, 318, 179, + 0, 0, 284, 43, 283, 0, 0, 44, 45, 0, + 244, 46, 0, 47, 0, 0, 0, 245, 0, 2, + 48, 257, 0, 419, 3, 259, 38, 4, 0, 0, + 260, 49, 50, 5, 0, 2, 6, 51, 0, 0, + 3, 0, 39, 4, 40, 0, 0, 0, 0, 5, + 41, 7, 6, 0, 42, 0, 0, 52, 53, 8, + 256, 54, 0, 0, 43, 55, 0, 7, 44, 45, + 0, 56, 46, 57, 47, 8, 0, 0, 58, 59, + 0, 48, 0, 0, 0, 0, 257, 0, 258, 0, + 259, 9, 49, 50, 0, 260, 10, 11, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, + 0, 0, 10, 11, 0, 0, 2, 0, 52, 53, + 12, 3, 54, 0, 4, 13, 55, 0, 0, 0, + 5, 0, 56, 6, 57, 0, 12, 2, 0, 58, + 59, 13, 3, 0, 0, 0, 0, 2, 7, 0, + 0, 5, 3, 0, 6, 0, 8, 2, 0, 0, + 0, 5, 3, 0, 6, 0, 0, 0, 0, 7, + 0, 5, 0, 0, 6, 0, 0, 0, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 7, + 0, 0, 0, 10, 11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 0, 0, 0, 0, 10, 11, 0, 12, 0, 9, + 0, 221, 13, 0, 10, 11, 0, 0, 0, 9, + 0, 0, 0, 0, 10, 11, 0, 0, 12, 0, + 0, 0, 304, 13, 0, 0, 0, 80, 12, 81, + 0, 0, 375, 13, 0, 82, 0, 83, 12, 0, + 0, 0, 415, 13, 84, 85, 86, 87, 0, 88, + 89, 0, 90, 0, 91, 92, 93, 0, 0, 94, + 0, 0, 95, 0, 96, 97, 98, 99, 100, 101, + 0, 102, 103, 104, 105, 0, 0, 106, 0, 0, + 107, 0, 0, 0, 108, 0, 109, 0, 0, 0, + 110, 111, 112, 113, 114, 115, 116, 0, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 0, 0, 126, + 0, 0, 127, 0, 128, 0, 0, 129, 130, 131, + 0, 0, 132, 0, 133, 134, 135, 136, 0, 0, + 137 }; static const short yycheck[] = { 1, - 64, 10, 2, 1, 1, 1, 20, 20, 190, 232, - 1, 193, 194, 243, 244, 197, 353, 199, 1, 3, - 4, 10, 11, 26, 211, 32, 77, 229, 40, 120, - 104, 104, 120, 92, 133, 38, 7, 9, 202, 11, - 204, 205, 42, 134, 440, 10, 11, 249, 120, 445, - 138, 110, 234, 32, 28, 120, 3, 120, 5, 6, - 72, 135, 135, 29, 120, 132, 138, 10, 11, 134, - 137, 134, 72, 410, 40, 137, 60, 77, 134, 29, - 10, 11, 132, 55, 56, 59, 137, 133, 318, 132, - 40, 137, 256, 257, 258, 259, 70, 71, 442, 132, - 10, 11, 76, 290, 448, 133, 72, 132, 74, 137, - 94, 132, 78, 10, 11, 136, 137, 181, 182, 126, - 135, 95, 72, 446, 188, 99, 308, 450, 78, 135, - 53, 115, 70, 136, 57, 139, 200, 367, 140, 141, - 3, 4, 140, 141, 140, 141, 3, 4, 371, 140, - 141, 98, 141, 51, 52, 337, 54, 140, 141, 124, - 125, 126, 127, 132, 328, 329, 330, 331, 332, 333, - 334, 335, 336, 403, 3, 4, 141, 124, 135, 126, - 132, 128, 7, 126, 127, 133, 133, 3, 4, 353, - 132, 138, 122, 123, 124, 125, 126, 127, 141, 132, - 430, 215, 215, 135, 134, 132, 206, 207, 133, 439, - 210, 141, 122, 123, 124, 125, 126, 127, 227, 135, - 229, 385, 3, 4, 134, 122, 123, 124, 125, 126, - 127, 141, 241, 4, 133, 233, 3, 233, 133, 241, - 249, 250, 233, 307, 141, 409, 410, 133, 250, 20, - 133, 22, 416, 133, 10, 11, 133, 28, 422, 259, - 132, 32, 3, 4, 136, 137, 133, 431, 133, 3, - 4, 42, 436, 3, 4, 46, 47, 3, 4, 50, - 133, 52, 3, 4, 136, 137, 136, 137, 59, 133, - 133, 133, 133, 133, 133, 292, 296, 133, 32, 70, - 71, 133, 300, 303, 300, 76, 10, 11, 135, 300, - 133, 311, 133, 311, 133, 133, 86, 7, 382, 7, - 135, 135, 322, 140, 95, 96, 60, 134, 99, 133, - 7, 3, 103, 5, 6, 7, 7, 7, 109, 3, - 111, 5, 6, 7, 52, 116, 117, 8, 5, 137, - 139, 135, 120, 136, 140, 120, 134, 133, 4, 134, - 94, 10, 11, 3, 64, 134, 137, 123, 124, 125, - 126, 127, 134, 134, 20, 134, 22, 134, 142, 134, - 134, 115, 28, 134, 134, 141, 32, 134, 134, 134, - 134, 3, 126, 5, 6, 134, 42, 134, 134, 133, - 46, 47, 134, 401, 50, 401, 52, 134, 139, 132, - 401, 5, 132, 59, 132, 132, 141, 419, 122, 123, - 124, 125, 126, 127, 70, 71, 98, 134, 134, 429, - 76, 134, 134, 442, 98, 139, 438, 141, 132, 448, - 442, 101, 134, 132, 446, 0, 448, 134, 450, 95, - 96, 137, 124, 99, 126, 135, 128, 103, 133, 137, - 124, 133, 126, 109, 128, 111, 120, 0, 134, 133, - 116, 117, 139, 122, 123, 124, 125, 126, 127, 3, - 4, 5, 6, 134, 18, 3, 98, 5, 6, 299, - 139, 137, 141, 438, 307, 204, 20, 298, 22, 1, - 1, 1, 187, -1, 28, 203, -1, 242, 32, -1, - -1, -1, 124, 9, 126, -1, 128, -1, 42, -1, - -1, 133, 46, 47, -1, -1, 50, -1, 52, -1, - -1, -1, -1, -1, -1, 59, 32, -1, -1, -1, - -1, 37, -1, -1, 40, -1, 70, 71, -1, -1, - 46, -1, 76, 49, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 64, -1, - -1, 95, 96, -1, 98, 99, 72, -1, -1, 103, - 98, -1, -1, -1, -1, 109, -1, 111, -1, -1, - -1, -1, 116, 117, -1, -1, -1, -1, -1, -1, - 124, 4, 126, -1, 128, -1, 124, 103, 126, 133, - 128, -1, 108, 109, -1, 133, -1, 20, 32, 22, - -1, -1, -1, 37, -1, 28, 40, -1, -1, 32, - -1, -1, 46, -1, -1, 49, 132, -1, -1, 42, - 32, 137, -1, 46, 47, 37, -1, 50, 40, 52, - 64, -1, -1, -1, 46, -1, 59, 49, 72, -1, - -1, -1, -1, -1, -1, 32, -1, 70, 71, -1, - 37, -1, 64, 76, -1, -1, -1, -1, -1, 46, - 72, -1, 49, -1, -1, -1, -1, -1, -1, 103, - -1, -1, 95, 96, 108, 109, 99, 64, -1, -1, - 103, -1, -1, -1, -1, -1, 109, 32, 111, -1, - -1, 103, 37, 116, 117, 32, 108, 109, 132, -1, - 37, 46, 136, 137, 49, -1, -1, -1, -1, 46, - -1, -1, 49, -1, -1, -1, 103, -1, -1, 64, - 132, 108, 109, -1, -1, 137, -1, 64, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 132, -1, -1, -1, 136, - 137, -1, -1, -1, -1, -1, -1, -1, 103, -1, - -1, -1, -1, 108, 109, -1, 103, -1, -1, -1, - -1, 108, 109, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 16, 132, 18, -1, - -1, 136, 137, -1, 24, 132, 26, -1, -1, 136, - 137, -1, -1, 33, 34, 35, 36, -1, 38, 39, - -1, 41, -1, 43, 44, 45, -1, -1, 48, -1, - -1, 51, -1, 53, 54, 55, 56, 57, 58, -1, - 60, 61, 62, 63, -1, -1, 66, -1, -1, 69, - -1, -1, -1, 73, -1, 75, -1, -1, -1, 79, - 80, 81, 82, 83, 84, 85, -1, 87, 88, 89, - 90, 91, 92, 93, 94, -1, -1, 97, -1, -1, - 100, -1, 102, -1, -1, 105, 106, 107, -1, -1, - 110, -1, 112, 113, 114, 115, -1, -1, 118 + 64, 10, 2, 1, 1, 1, 20, 20, 1, 26, + 1, 191, 233, 212, 194, 195, 244, 245, 198, 77, + 200, 38, 32, 230, 134, 40, 356, 10, 11, 9, + 105, 11, 121, 105, 133, 203, 7, 205, 206, 138, + 10, 11, 42, 250, 10, 11, 32, 93, 121, 133, + 139, 3, 29, 5, 6, 235, 28, 72, 138, 121, + 29, 136, 443, 40, 136, 111, 139, 448, 10, 11, + 121, 40, 72, 135, 121, 55, 56, 77, 133, 445, + 138, 10, 11, 413, 135, 451, 121, 59, 135, 257, + 258, 259, 260, 321, 293, 72, 133, 74, 70, 71, + 135, 78, 133, 72, 76, 133, 137, 138, 3, 78, + 5, 6, 7, 51, 52, 136, 54, 127, 182, 183, + 137, 133, 133, 70, 96, 189, 137, 138, 100, 134, + 136, 311, 3, 138, 5, 6, 7, 201, 133, 141, + 142, 140, 370, 141, 142, 141, 142, 99, 141, 142, + 141, 142, 136, 374, 124, 125, 126, 127, 128, 142, + 340, 127, 128, 331, 332, 333, 334, 335, 336, 337, + 338, 339, 142, 125, 133, 127, 142, 129, 406, 3, + 4, 7, 134, 125, 126, 127, 128, 139, 356, 134, + 3, 4, 133, 138, 123, 124, 125, 126, 127, 128, + 142, 136, 216, 216, 99, 433, 135, 207, 208, 3, + 4, 211, 449, 142, 442, 133, 453, 3, 4, 228, + 388, 230, 3, 53, 5, 6, 136, 57, 99, 134, + 125, 134, 127, 242, 129, 134, 234, 4, 234, 134, + 242, 250, 251, 234, 412, 413, 310, 10, 11, 251, + 134, 419, 134, 20, 125, 22, 127, 425, 129, 134, + 260, 28, 134, 134, 134, 32, 434, 3, 4, 10, + 11, 439, 3, 4, 134, 42, 3, 4, 134, 46, + 47, 3, 4, 50, 134, 52, 3, 4, 137, 138, + 137, 138, 59, 134, 134, 134, 134, 134, 295, 299, + 134, 3, 4, 70, 71, 303, 306, 303, 3, 76, + 86, 134, 303, 134, 314, 134, 314, 134, 99, 134, + 134, 385, 7, 141, 136, 325, 136, 136, 135, 96, + 97, 134, 7, 100, 7, 7, 7, 104, 52, 8, + 5, 138, 140, 110, 125, 112, 127, 136, 129, 137, + 117, 118, 4, 134, 141, 10, 11, 121, 60, 121, + 123, 124, 125, 126, 127, 128, 10, 11, 20, 135, + 22, 138, 135, 134, 3, 135, 28, 135, 135, 142, + 32, 135, 123, 124, 125, 126, 127, 128, 135, 135, + 42, 135, 135, 95, 46, 47, 3, 4, 50, 140, + 52, 142, 135, 135, 143, 135, 404, 59, 404, 3, + 135, 5, 6, 404, 116, 135, 135, 135, 70, 71, + 422, 64, 135, 135, 76, 32, 135, 135, 133, 140, + 133, 135, 432, 133, 133, 142, 445, 135, 135, 441, + 5, 135, 451, 445, 96, 97, 133, 449, 100, 451, + 133, 453, 104, 60, 102, 135, 138, 136, 110, 135, + 112, 138, 121, 0, 134, 117, 118, 135, 123, 124, + 125, 126, 127, 128, 3, 4, 5, 6, 140, 123, + 124, 125, 126, 127, 128, 140, 138, 142, 95, 135, + 0, 20, 18, 22, 302, 441, 188, 301, 142, 28, + 1, 1, 1, 32, 310, 99, 9, -1, 243, 116, + -1, -1, 205, 42, 204, -1, -1, 46, 47, -1, + 127, 50, -1, 52, -1, -1, -1, 134, -1, 32, + 59, 125, -1, 127, 37, 129, 4, 40, -1, -1, + 134, 70, 71, 46, -1, 32, 49, 76, -1, -1, + 37, -1, 20, 40, 22, -1, -1, -1, -1, 46, + 28, 64, 49, -1, 32, -1, -1, 96, 97, 72, + 99, 100, -1, -1, 42, 104, -1, 64, 46, 47, + -1, 110, 50, 112, 52, 72, -1, -1, 117, 118, + -1, 59, -1, -1, -1, -1, 125, -1, 127, -1, + 129, 104, 70, 71, -1, 134, 109, 110, 76, -1, + -1, -1, -1, -1, -1, -1, -1, 104, -1, -1, + -1, -1, 109, 110, -1, -1, 32, -1, 96, 97, + 133, 37, 100, -1, 40, 138, 104, -1, -1, -1, + 46, -1, 110, 49, 112, -1, 133, 32, -1, 117, + 118, 138, 37, -1, -1, -1, -1, 32, 64, -1, + -1, 46, 37, -1, 49, -1, 72, 32, -1, -1, + -1, 46, 37, -1, 49, -1, -1, -1, -1, 64, + -1, 46, -1, -1, 49, -1, -1, -1, -1, 64, + -1, -1, -1, -1, -1, -1, -1, -1, 104, 64, + -1, -1, -1, 109, 110, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 104, + -1, -1, -1, -1, 109, 110, -1, 133, -1, 104, + -1, 137, 138, -1, 109, 110, -1, -1, -1, 104, + -1, -1, -1, -1, 109, 110, -1, -1, 133, -1, + -1, -1, 137, 138, -1, -1, -1, 16, 133, 18, + -1, -1, 137, 138, -1, 24, -1, 26, 133, -1, + -1, -1, 137, 138, 33, 34, 35, 36, -1, 38, + 39, -1, 41, -1, 43, 44, 45, -1, -1, 48, + -1, -1, 51, -1, 53, 54, 55, 56, 57, 58, + -1, 60, 61, 62, 63, -1, -1, 66, -1, -1, + 69, -1, -1, -1, 73, -1, 75, -1, -1, -1, + 79, 80, 81, 82, 83, 84, 85, -1, 87, 88, + 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, + -1, -1, 101, -1, 103, -1, -1, 106, 107, 108, + -1, -1, 111, -1, 113, 114, 115, 116, -1, -1, + 119 }; /* -*-C-*- Note some compilers choke on comments on `#line' lines. */ #line 3 "/usr/share/bison.simple" @@ -1390,161 +1386,161 @@ yyreduce: switch (yyn) { case 1: -#line 229 "parser.y" +#line 227 "parser.y" { write_proxies(yyvsp[0].ifref); write_client(yyvsp[0].ifref); write_server(yyvsp[0].ifref); ; break;} case 2: -#line 232 "parser.y" +#line 230 "parser.y" { yyval.ifref = NULL; ; break;} case 3: -#line 233 "parser.y" +#line 231 "parser.y" { yyval.ifref = yyvsp[-1].ifref; ; break;} case 4: -#line 234 "parser.y" +#line 232 "parser.y" { yyval.ifref = make_ifref(yyvsp[0].type); LINK(yyval.ifref, yyvsp[-1].ifref); ; break;} case 5: -#line 235 "parser.y" +#line 233 "parser.y" { yyval.ifref = yyvsp[-1].ifref; add_coclass(yyvsp[0].clas); ; break;} case 6: -#line 236 "parser.y" +#line 234 "parser.y" { yyval.ifref = yyvsp[-1].ifref; add_module(yyvsp[0].type); ; break;} case 7: -#line 237 "parser.y" +#line 235 "parser.y" { yyval.ifref = yyvsp[-1].ifref; ; break;} case 8: -#line 238 "parser.y" +#line 236 "parser.y" { yyval.ifref = yyvsp[-1].ifref; ; break;} case 9: -#line 241 "parser.y" +#line 239 "parser.y" {; break;} case 10: -#line 242 "parser.y" +#line 240 "parser.y" { if (!parse_only) add_interface(yyvsp[0].type); ; break;} case 11: -#line 243 "parser.y" +#line 241 "parser.y" { if (!parse_only) add_interface(yyvsp[0].type); ; break;} case 12: -#line 244 "parser.y" +#line 242 "parser.y" { if (!parse_only) add_coclass(yyvsp[0].clas); ; break;} case 13: -#line 245 "parser.y" +#line 243 "parser.y" { if (!parse_only) add_module(yyvsp[0].type); ; break;} case 14: -#line 246 "parser.y" +#line 244 "parser.y" {; break;} case 15: -#line 249 "parser.y" +#line 247 "parser.y" { yyval.func = NULL; ; break;} case 16: -#line 250 "parser.y" +#line 248 "parser.y" { yyval.func = yyvsp[-1].func; LINK(yyval.func, yyvsp[-2].func); ; break;} case 17: -#line 251 "parser.y" +#line 249 "parser.y" { yyval.func = yyvsp[-1].func; ; break;} case 18: -#line 254 "parser.y" +#line 252 "parser.y" {; break;} case 19: -#line 255 "parser.y" +#line 253 "parser.y" { if (!parse_only && do_header) { write_constdef(yyvsp[-1].var); } ; break;} case 20: -#line 256 "parser.y" +#line 254 "parser.y" {; break;} case 21: -#line 257 "parser.y" +#line 255 "parser.y" { if (!parse_only && do_header) { write_type(header, yyvsp[-1].type, NULL, NULL); fprintf(header, ";\n\n"); } ; break;} case 22: -#line 258 "parser.y" +#line 256 "parser.y" { if (!parse_only && do_header) { write_externdef(yyvsp[-1].var); } ; break;} case 23: -#line 259 "parser.y" +#line 257 "parser.y" {; break;} case 24: -#line 260 "parser.y" +#line 258 "parser.y" { if (!parse_only && do_header) { write_type(header, yyvsp[-1].type, NULL, NULL); fprintf(header, ";\n\n"); } ; break;} case 25: -#line 261 "parser.y" +#line 259 "parser.y" {; break;} case 26: -#line 262 "parser.y" +#line 260 "parser.y" { if (!parse_only && do_header) { write_type(header, yyvsp[-1].type, NULL, NULL); fprintf(header, ";\n\n"); } ; break;} case 27: -#line 265 "parser.y" +#line 263 "parser.y" { if (!parse_only && do_header) fprintf(header, "%s\n", yyvsp[-1].str); ; break;} case 28: -#line 267 "parser.y" +#line 265 "parser.y" { assert(yychar == YYEMPTY); if (!do_import(yyvsp[-1].str)) yychar = aEOF; ; break;} case 29: -#line 270 "parser.y" +#line 268 "parser.y" {; break;} case 30: -#line 273 "parser.y" +#line 271 "parser.y" { yyval.str = yyvsp[0].str; ; break;} case 31: -#line 275 "parser.y" +#line 273 "parser.y" { start_typelib(yyvsp[-1].str, yyvsp[-2].attr); ; break;} case 32: -#line 277 "parser.y" +#line 275 "parser.y" { end_typelib(); ; break;} case 33: -#line 280 "parser.y" +#line 278 "parser.y" { yyval.var = NULL; ; break;} case 35: -#line 284 "parser.y" +#line 282 "parser.y" { yyval.var = NULL; ; break;} case 37: -#line 288 "parser.y" +#line 286 "parser.y" { LINK(yyvsp[0].var, yyvsp[-2].var); yyval.var = yyvsp[0].var; ; break;} case 39: -#line 293 "parser.y" +#line 291 "parser.y" { yyval.var = yyvsp[-1].var; set_type(yyval.var, yyvsp[-2].tref, yyvsp[0].expr); yyval.var->attrs = yyvsp[-3].attr; ; break;} case 40: -#line 297 "parser.y" +#line 295 "parser.y" { yyval.var = yyvsp[-1].var; set_type(yyval.var, yyvsp[-2].tref, yyvsp[0].expr); ; break;} case 41: -#line 300 "parser.y" +#line 298 "parser.y" { yyval.var = yyvsp[-3].var; yyval.var->ptr_level--; set_type(yyval.var, yyvsp[-4].tref, NULL); @@ -1553,7 +1549,7 @@ case 41: ; break;} case 42: -#line 306 "parser.y" +#line 304 "parser.y" { yyval.var = yyvsp[-3].var; yyval.var->ptr_level--; set_type(yyval.var, yyvsp[-4].tref, NULL); @@ -1561,337 +1557,349 @@ case 42: ; break;} case 43: -#line 313 "parser.y" +#line 311 "parser.y" { yyval.expr = NULL; ; break;} case 44: -#line 314 "parser.y" +#line 312 "parser.y" { yyval.expr = yyvsp[-1].expr; ; break;} case 45: -#line 315 "parser.y" +#line 313 "parser.y" { yyval.expr = make_expr(EXPR_VOID); ; break;} case 47: -#line 319 "parser.y" +#line 317 "parser.y" { LINK(yyvsp[0].expr, yyvsp[-2].expr); yyval.expr = yyvsp[0].expr; ; break;} case 48: -#line 320 "parser.y" +#line 318 "parser.y" { LINK(yyvsp[0].expr, yyvsp[-3].expr); yyval.expr = yyvsp[0].expr; ; break;} case 49: -#line 323 "parser.y" +#line 321 "parser.y" { yyval.attr = NULL; ; break;} case 51: -#line 328 "parser.y" +#line 326 "parser.y" { yyval.attr = yyvsp[-1].attr; ; break;} case 53: -#line 332 "parser.y" +#line 330 "parser.y" { LINK(yyvsp[0].attr, yyvsp[-2].attr); yyval.attr = yyvsp[0].attr; ; break;} case 54: -#line 333 "parser.y" +#line 331 "parser.y" { LINK(yyvsp[0].attr, yyvsp[-3].attr); yyval.attr = yyvsp[0].attr; ; break;} case 55: -#line 337 "parser.y" +#line 335 "parser.y" { yyval.attr = make_attr(ATTR_ASYNC); ; break;} case 56: -#line 338 "parser.y" +#line 336 "parser.y" { yyval.attr = make_attr(ATTR_AUTO_HANDLE); ; break;} case 57: -#line 339 "parser.y" +#line 337 "parser.y" { yyval.attr = make_attrp(ATTR_CALLAS, yyvsp[-1].var); ; break;} case 58: -#line 340 "parser.y" +#line 338 "parser.y" { yyval.attr = make_attrp(ATTR_CASE, yyvsp[-1].expr); ; break;} case 59: -#line 341 "parser.y" +#line 339 "parser.y" { yyval.attr = make_attrv(ATTR_CONTEXTHANDLE, 0); ; break;} case 60: -#line 342 "parser.y" +#line 340 "parser.y" { yyval.attr = make_attrv(ATTR_CONTEXTHANDLE, 0); /* RPC_CONTEXT_HANDLE_DONT_SERIALIZE */ ; break;} case 61: -#line 343 "parser.y" +#line 341 "parser.y" { yyval.attr = make_attrv(ATTR_CONTEXTHANDLE, 0); /* RPC_CONTEXT_HANDLE_SERIALIZE */ ; break;} case 62: -#line 344 "parser.y" +#line 342 "parser.y" { yyval.attr = make_attr(ATTR_CONTROL); ; break;} case 63: -#line 345 "parser.y" +#line 343 "parser.y" { yyval.attr = make_attr(ATTR_DEFAULT); ; break;} case 64: -#line 346 "parser.y" +#line 344 "parser.y" { yyval.attr = make_attrp(ATTR_DEFAULTVALUE_EXPR, yyvsp[-1].expr); ; break;} case 65: -#line 347 "parser.y" +#line 345 "parser.y" { yyval.attr = make_attrp(ATTR_DEFAULTVALUE_STRING, yyvsp[-1].str); ; break;} case 66: -#line 348 "parser.y" +#line 346 "parser.y" { yyval.attr = make_attrp(ATTR_DLLNAME, yyvsp[-1].str); ; break;} case 67: -#line 349 "parser.y" +#line 347 "parser.y" { yyval.attr = make_attr(ATTR_DUAL); ; break;} case 68: -#line 350 "parser.y" +#line 348 "parser.y" { yyval.attr = make_attrp(ATTR_ENDPOINT, yyvsp[-1].str); ; break;} case 69: -#line 351 "parser.y" +#line 349 "parser.y" { yyval.attr = make_attrp(ATTR_ENTRY_STRING, yyvsp[-1].str); ; break;} case 70: -#line 352 "parser.y" +#line 350 "parser.y" { yyval.attr = make_attrp(ATTR_ENTRY_ORDINAL, yyvsp[-1].expr); ; break;} case 71: -#line 353 "parser.y" +#line 351 "parser.y" { yyval.attr = make_attr(ATTR_EXPLICIT_HANDLE); ; break;} case 72: -#line 354 "parser.y" +#line 352 "parser.y" { yyval.attr = make_attr(ATTR_HANDLE); ; break;} case 73: -#line 355 "parser.y" +#line 353 "parser.y" { yyval.attr = make_attrp(ATTR_HELPCONTEXT, yyvsp[-1].expr); ; break;} case 74: -#line 356 "parser.y" +#line 354 "parser.y" { yyval.attr = make_attrp(ATTR_HELPFILE, yyvsp[-1].str); ; break;} case 75: -#line 357 "parser.y" +#line 355 "parser.y" { yyval.attr = make_attrp(ATTR_HELPSTRING, yyvsp[-1].str); ; break;} case 76: -#line 358 "parser.y" +#line 356 "parser.y" { yyval.attr = make_attrp(ATTR_HELPSTRINGCONTEXT, yyvsp[-1].expr); ; break;} case 77: -#line 359 "parser.y" +#line 357 "parser.y" { yyval.attr = make_attrp(ATTR_HELPSTRINGDLL, yyvsp[-1].str); ; break;} case 78: -#line 360 "parser.y" +#line 358 "parser.y" { yyval.attr = make_attr(ATTR_HIDDEN); ; break;} case 79: -#line 361 "parser.y" +#line 359 "parser.y" { yyval.attr = make_attrp(ATTR_ID, yyvsp[-1].expr); ; break;} case 80: -#line 362 "parser.y" +#line 360 "parser.y" { yyval.attr = make_attr(ATTR_IDEMPOTENT); ; break;} case 81: -#line 363 "parser.y" +#line 361 "parser.y" { yyval.attr = make_attrp(ATTR_IIDIS, yyvsp[-1].var); ; break;} case 82: -#line 364 "parser.y" +#line 362 "parser.y" { yyval.attr = make_attrp(ATTR_IMPLICIT_HANDLE, yyvsp[-1].str); ; break;} case 83: -#line 365 "parser.y" +#line 363 "parser.y" { yyval.attr = make_attr(ATTR_IN); ; break;} case 84: -#line 366 "parser.y" +#line 364 "parser.y" { yyval.attr = make_attr(ATTR_INPUTSYNC); ; break;} case 85: -#line 367 "parser.y" +#line 365 "parser.y" { yyval.attr = make_attrp(ATTR_LENGTHIS, yyvsp[-1].expr); ; break;} case 86: -#line 368 "parser.y" +#line 366 "parser.y" { yyval.attr = make_attr(ATTR_LOCAL); ; break;} case 87: -#line 369 "parser.y" +#line 367 "parser.y" { yyval.attr = make_attr(ATTR_NONCREATABLE); ; break;} case 88: -#line 370 "parser.y" +#line 368 "parser.y" { yyval.attr = make_attr(ATTR_OBJECT); ; break;} case 89: -#line 371 "parser.y" +#line 369 "parser.y" { yyval.attr = make_attr(ATTR_ODL); ; break;} case 90: -#line 372 "parser.y" +#line 370 "parser.y" { yyval.attr = make_attr(ATTR_OLEAUTOMATION); ; break;} case 91: -#line 373 "parser.y" +#line 371 "parser.y" { yyval.attr = make_attr(ATTR_OPTIONAL); ; break;} case 92: -#line 374 "parser.y" +#line 372 "parser.y" { yyval.attr = make_attr(ATTR_OUT); ; break;} case 93: -#line 375 "parser.y" +#line 373 "parser.y" { yyval.attr = make_attrv(ATTR_POINTERDEFAULT, yyvsp[-1].num); ; break;} case 94: -#line 376 "parser.y" +#line 374 "parser.y" { yyval.attr = make_attr(ATTR_PROPGET); ; break;} case 95: -#line 377 "parser.y" +#line 375 "parser.y" { yyval.attr = make_attr(ATTR_PROPPUT); ; break;} case 96: -#line 378 "parser.y" +#line 376 "parser.y" { yyval.attr = make_attr(ATTR_PROPPUTREF); ; break;} case 97: -#line 379 "parser.y" -{ yyval.attr = make_attr(ATTR_PUBLIC); ; +#line 377 "parser.y" +{ yyval.attr = make_attr(ATTR_PTR); ; break;} case 98: -#line 380 "parser.y" -{ yyval.attr = make_attr(ATTR_READONLY); ; +#line 378 "parser.y" +{ yyval.attr = make_attr(ATTR_PUBLIC); ; break;} case 99: +#line 379 "parser.y" +{ yyval.attr = make_attr(ATTR_READONLY); ; + break;} +case 100: +#line 380 "parser.y" +{ yyval.attr = make_attr(ATTR_REF); ; + break;} +case 101: #line 381 "parser.y" { yyval.attr = make_attr(ATTR_RESTRICTED); ; break;} -case 100: +case 102: #line 382 "parser.y" { yyval.attr = make_attr(ATTR_RETVAL); ; break;} -case 101: +case 103: #line 383 "parser.y" { yyval.attr = make_attrp(ATTR_SIZEIS, yyvsp[-1].expr); ; break;} -case 102: +case 104: #line 384 "parser.y" { yyval.attr = make_attr(ATTR_SOURCE); ; break;} -case 103: +case 105: #line 385 "parser.y" { yyval.attr = make_attr(ATTR_STRING); ; break;} -case 104: +case 106: #line 386 "parser.y" { yyval.attr = make_attrp(ATTR_SWITCHIS, yyvsp[-1].expr); ; break;} -case 105: +case 107: #line 387 "parser.y" { yyval.attr = make_attrp(ATTR_SWITCHTYPE, type_ref(yyvsp[-1].tref)); ; break;} -case 106: +case 108: #line 388 "parser.y" { yyval.attr = make_attrp(ATTR_TRANSMITAS, type_ref(yyvsp[-1].tref)); ; break;} -case 107: -#line 389 "parser.y" -{ yyval.attr = make_attrp(ATTR_UUID, yyvsp[-1].uuid); ; - break;} -case 108: -#line 390 "parser.y" -{ yyval.attr = make_attr(ATTR_V1ENUM); ; - break;} case 109: -#line 391 "parser.y" -{ yyval.attr = make_attr(ATTR_VARARG); ; +#line 389 "parser.y" +{ yyval.attr = make_attr(ATTR_UNIQUE); ; break;} case 110: -#line 392 "parser.y" -{ yyval.attr = make_attrv(ATTR_VERSION, yyvsp[-1].num); ; +#line 390 "parser.y" +{ yyval.attr = make_attrp(ATTR_UUID, yyvsp[-1].uuid); ; break;} case 111: -#line 393 "parser.y" -{ yyval.attr = make_attrp(ATTR_WIREMARSHAL, type_ref(yyvsp[-1].tref)); ; +#line 391 "parser.y" +{ yyval.attr = make_attr(ATTR_V1ENUM); ; break;} case 112: +#line 392 "parser.y" +{ yyval.attr = make_attr(ATTR_VARARG); ; + break;} +case 113: +#line 393 "parser.y" +{ yyval.attr = make_attrv(ATTR_VERSION, yyvsp[-1].num); ; + break;} +case 114: #line 394 "parser.y" -{ yyval.attr = make_attrv(ATTR_POINTERTYPE, yyvsp[0].num); ; +{ yyval.attr = make_attrp(ATTR_WIREMARSHAL, type_ref(yyvsp[-1].tref)); ; break;} case 115: -#line 401 "parser.y" +#line 395 "parser.y" +{ yyval.attr = make_attrv(ATTR_POINTERTYPE, yyvsp[0].num); ; + break;} +case 118: +#line 402 "parser.y" { yyval.var = NULL; ; break;} -case 116: -#line 402 "parser.y" +case 119: +#line 403 "parser.y" { if (yyvsp[0].var) { LINK(yyvsp[0].var, yyvsp[-1].var); yyval.var = yyvsp[0].var; } else { yyval.var = yyvsp[-1].var; } ; break;} -case 117: -#line 407 "parser.y" +case 120: +#line 408 "parser.y" { attr_t *a = make_attrp(ATTR_CASE, yyvsp[-2].expr); yyval.var = yyvsp[0].var; if (!yyval.var) yyval.var = make_var(NULL); LINK(a, yyval.var->attrs); yyval.var->attrs = a; ; break;} -case 118: -#line 411 "parser.y" +case 121: +#line 412 "parser.y" { attr_t *a = make_attr(ATTR_DEFAULT); yyval.var = yyvsp[0].var; if (!yyval.var) yyval.var = make_var(NULL); LINK(a, yyval.var->attrs); yyval.var->attrs = a; ; break;} -case 119: -#line 417 "parser.y" +case 122: +#line 418 "parser.y" { yyval.var = reg_const(yyvsp[-2].var); set_type(yyval.var, yyvsp[-3].tref, NULL); yyval.var->eval = yyvsp[0].expr; yyval.var->lval = yyvsp[0].expr->cval; ; break;} -case 120: -#line 424 "parser.y" +case 123: +#line 425 "parser.y" { yyval.var = NULL; ; break;} -case 121: -#line 425 "parser.y" +case 124: +#line 426 "parser.y" { yyval.var = yyvsp[-1].var; ; break;} -case 124: -#line 430 "parser.y" +case 127: +#line 431 "parser.y" { LINK(yyvsp[0].var, yyvsp[-2].var); yyval.var = yyvsp[0].var; if (yyvsp[-2].var && !yyvsp[0].var->eval) yyvsp[0].var->lval = yyvsp[-2].var->lval + 1; ; break;} -case 125: -#line 436 "parser.y" +case 128: +#line 437 "parser.y" { yyval.var = reg_const(yyvsp[-2].var); yyval.var->eval = yyvsp[0].expr; yyval.var->lval = yyvsp[0].expr->cval; yyval.var->type = make_type(RPC_FC_LONG, &std_int); ; break;} -case 126: -#line 441 "parser.y" +case 129: +#line 442 "parser.y" { yyval.var = reg_const(yyvsp[0].var); yyval.var->lval = 0; /* default for first enum entry */ yyval.var->type = make_type(RPC_FC_LONG, &std_int); ; break;} -case 127: -#line 447 "parser.y" +case 130: +#line 448 "parser.y" { yyval.type = get_typev(RPC_FC_ENUM16, yyvsp[-3].var, tsENUM); yyval.type->fields = yyvsp[-1].var; yyval.type->defined = TRUE; @@ -1899,135 +1907,135 @@ case 127: add_enum(yyval.type); ; break;} -case 129: -#line 456 "parser.y" -{ LINK(yyvsp[0].expr, yyvsp[-2].expr); yyval.expr = yyvsp[0].expr; ; - break;} -case 130: -#line 469 "parser.y" -{ yyval.expr = make_expr(EXPR_VOID); ; - break;} case 132: -#line 473 "parser.y" -{ yyval.expr = make_exprl(EXPR_NUM, yyvsp[0].num); ; +#line 457 "parser.y" +{ LINK(yyvsp[0].expr, yyvsp[-2].expr); yyval.expr = yyvsp[0].expr; ; break;} case 133: -#line 474 "parser.y" -{ yyval.expr = make_exprl(EXPR_HEXNUM, yyvsp[0].num); ; - break;} -case 134: -#line 475 "parser.y" -{ yyval.expr = make_exprs(EXPR_IDENTIFIER, yyvsp[0].str); ; +#line 470 "parser.y" +{ yyval.expr = make_expr(EXPR_VOID); ; break;} case 135: -#line 476 "parser.y" -{ yyval.expr = make_expr3(EXPR_COND, yyvsp[-4].expr, yyvsp[-2].expr, yyvsp[0].expr); ; +#line 474 "parser.y" +{ yyval.expr = make_exprl(EXPR_NUM, yyvsp[0].num); ; break;} case 136: -#line 477 "parser.y" -{ yyval.expr = make_expr2(EXPR_OR , yyvsp[-2].expr, yyvsp[0].expr); ; +#line 475 "parser.y" +{ yyval.expr = make_exprl(EXPR_HEXNUM, yyvsp[0].num); ; break;} case 137: -#line 478 "parser.y" -{ yyval.expr = make_expr2(EXPR_AND, yyvsp[-2].expr, yyvsp[0].expr); ; +#line 476 "parser.y" +{ yyval.expr = make_exprs(EXPR_IDENTIFIER, yyvsp[0].str); ; break;} case 138: -#line 479 "parser.y" -{ yyval.expr = make_expr2(EXPR_ADD, yyvsp[-2].expr, yyvsp[0].expr); ; +#line 477 "parser.y" +{ yyval.expr = make_expr3(EXPR_COND, yyvsp[-4].expr, yyvsp[-2].expr, yyvsp[0].expr); ; break;} case 139: -#line 480 "parser.y" -{ yyval.expr = make_expr2(EXPR_SUB, yyvsp[-2].expr, yyvsp[0].expr); ; +#line 478 "parser.y" +{ yyval.expr = make_expr2(EXPR_OR , yyvsp[-2].expr, yyvsp[0].expr); ; break;} case 140: -#line 481 "parser.y" -{ yyval.expr = make_expr2(EXPR_MUL, yyvsp[-2].expr, yyvsp[0].expr); ; +#line 479 "parser.y" +{ yyval.expr = make_expr2(EXPR_AND, yyvsp[-2].expr, yyvsp[0].expr); ; break;} case 141: -#line 482 "parser.y" -{ yyval.expr = make_expr2(EXPR_DIV, yyvsp[-2].expr, yyvsp[0].expr); ; +#line 480 "parser.y" +{ yyval.expr = make_expr2(EXPR_ADD, yyvsp[-2].expr, yyvsp[0].expr); ; break;} case 142: -#line 483 "parser.y" -{ yyval.expr = make_expr2(EXPR_SHL, yyvsp[-2].expr, yyvsp[0].expr); ; +#line 481 "parser.y" +{ yyval.expr = make_expr2(EXPR_SUB, yyvsp[-2].expr, yyvsp[0].expr); ; break;} case 143: -#line 484 "parser.y" -{ yyval.expr = make_expr2(EXPR_SHR, yyvsp[-2].expr, yyvsp[0].expr); ; +#line 482 "parser.y" +{ yyval.expr = make_expr2(EXPR_MUL, yyvsp[-2].expr, yyvsp[0].expr); ; break;} case 144: -#line 485 "parser.y" -{ yyval.expr = make_expr1(EXPR_NOT, yyvsp[0].expr); ; +#line 483 "parser.y" +{ yyval.expr = make_expr2(EXPR_DIV, yyvsp[-2].expr, yyvsp[0].expr); ; break;} case 145: -#line 486 "parser.y" -{ yyval.expr = make_expr1(EXPR_NEG, yyvsp[0].expr); ; +#line 484 "parser.y" +{ yyval.expr = make_expr2(EXPR_SHL, yyvsp[-2].expr, yyvsp[0].expr); ; break;} case 146: -#line 487 "parser.y" -{ yyval.expr = make_expr1(EXPR_PPTR, yyvsp[0].expr); ; +#line 485 "parser.y" +{ yyval.expr = make_expr2(EXPR_SHR, yyvsp[-2].expr, yyvsp[0].expr); ; break;} case 147: -#line 488 "parser.y" -{ yyval.expr = make_exprt(EXPR_CAST, yyvsp[-2].tref, yyvsp[0].expr); ; +#line 486 "parser.y" +{ yyval.expr = make_expr1(EXPR_NOT, yyvsp[0].expr); ; break;} case 148: -#line 489 "parser.y" -{ yyval.expr = make_exprt(EXPR_SIZEOF, yyvsp[-1].tref, NULL); ; +#line 487 "parser.y" +{ yyval.expr = make_expr1(EXPR_NEG, yyvsp[0].expr); ; break;} case 149: -#line 490 "parser.y" -{ yyval.expr = yyvsp[-1].expr; ; +#line 488 "parser.y" +{ yyval.expr = make_expr1(EXPR_PPTR, yyvsp[0].expr); ; + break;} +case 150: +#line 489 "parser.y" +{ yyval.expr = make_exprt(EXPR_CAST, yyvsp[-2].tref, yyvsp[0].expr); ; break;} case 151: -#line 494 "parser.y" -{ LINK(yyvsp[0].expr, yyvsp[-2].expr); yyval.expr = yyvsp[0].expr; ; +#line 490 "parser.y" +{ yyval.expr = make_exprt(EXPR_SIZEOF, yyvsp[-1].tref, NULL); ; break;} case 152: -#line 497 "parser.y" +#line 491 "parser.y" +{ yyval.expr = yyvsp[-1].expr; ; + break;} +case 154: +#line 495 "parser.y" +{ LINK(yyvsp[0].expr, yyvsp[-2].expr); yyval.expr = yyvsp[0].expr; ; + break;} +case 155: +#line 498 "parser.y" { yyval.expr = yyvsp[0].expr; if (!yyval.expr->is_const) yyerror("expression is not constant\n"); ; break;} -case 153: -#line 503 "parser.y" +case 156: +#line 504 "parser.y" { yyval.var = yyvsp[0].var; set_type(yyval.var, yyvsp[-1].tref, NULL); ; break;} -case 154: -#line 508 "parser.y" +case 157: +#line 509 "parser.y" { yyval.var = NULL; ; break;} -case 155: -#line 509 "parser.y" +case 158: +#line 510 "parser.y" { if (yyvsp[0].var) { LINK(yyvsp[0].var, yyvsp[-1].var); yyval.var = yyvsp[0].var; } else { yyval.var = yyvsp[-1].var; } ; break;} -case 156: -#line 514 "parser.y" +case 159: +#line 515 "parser.y" { yyval.var = yyvsp[-1].var; ; break;} -case 157: -#line 515 "parser.y" +case 160: +#line 516 "parser.y" { yyval.var = make_var(NULL); yyval.var->type = yyvsp[-1].type; yyval.var->attrs = yyvsp[-2].attr; ; break;} -case 158: -#line 516 "parser.y" +case 161: +#line 517 "parser.y" { yyval.var = make_var(NULL); yyval.var->attrs = yyvsp[-1].attr; ; break;} -case 159: -#line 517 "parser.y" +case 162: +#line 518 "parser.y" { yyval.var = NULL; ; break;} -case 160: -#line 520 "parser.y" +case 163: +#line 521 "parser.y" { yyval.var = yyvsp[-1].var; set_type(yyval.var, yyvsp[-2].tref, yyvsp[0].expr); yyval.var->attrs = yyvsp[-3].attr; ; break;} -case 161: -#line 525 "parser.y" +case 164: +#line 526 "parser.y" { set_type(yyvsp[-3].var, yyvsp[-5].tref, NULL); yyvsp[-3].var->attrs = yyvsp[-6].attr; yyval.func = make_func(yyvsp[-3].var, yyvsp[-1].var); @@ -2036,56 +2044,56 @@ case 161: } ; break;} -case 162: -#line 534 "parser.y" -{ yyval.var = NULL; ; - break;} -case 164: -#line 538 "parser.y" -{ yyval.var = NULL; ; - break;} case 165: -#line 539 "parser.y" -{ yyval.var = make_var(yyvsp[0].str); ; +#line 535 "parser.y" +{ yyval.var = NULL; ; break;} -case 166: +case 167: +#line 539 "parser.y" +{ yyval.var = NULL; ; + break;} +case 168: #line 540 "parser.y" -{ yyval.var = make_var(yyvsp[0].str); ; - break;} -case 167: -#line 543 "parser.y" -{ yyval.var = make_var(yyvsp[0].str); ; - break;} -case 168: -#line 545 "parser.y" { yyval.var = make_var(yyvsp[0].str); ; break;} case 169: -#line 546 "parser.y" +#line 541 "parser.y" { yyval.var = make_var(yyvsp[0].str); ; break;} case 170: -#line 547 "parser.y" +#line 544 "parser.y" { yyval.var = make_var(yyvsp[0].str); ; break;} case 171: -#line 548 "parser.y" +#line 546 "parser.y" { yyval.var = make_var(yyvsp[0].str); ; break;} case 172: -#line 551 "parser.y" -{ yyval.type = make_type(RPC_FC_BYTE, NULL); ; +#line 547 "parser.y" +{ yyval.var = make_var(yyvsp[0].str); ; break;} case 173: -#line 552 "parser.y" -{ yyval.type = make_type(RPC_FC_WCHAR, NULL); ; +#line 548 "parser.y" +{ yyval.var = make_var(yyvsp[0].str); ; + break;} +case 174: +#line 549 "parser.y" +{ yyval.var = make_var(yyvsp[0].str); ; break;} case 175: -#line 554 "parser.y" -{ yyval.type = yyvsp[0].type; yyval.type->sign = 1; ; +#line 552 "parser.y" +{ yyval.type = make_type(RPC_FC_BYTE, NULL); ; break;} case 176: +#line 553 "parser.y" +{ yyval.type = make_type(RPC_FC_WCHAR, NULL); ; + break;} +case 178: #line 555 "parser.y" +{ yyval.type = yyvsp[0].type; yyval.type->sign = 1; ; + break;} +case 179: +#line 556 "parser.y" { yyval.type = yyvsp[0].type; yyval.type->sign = -1; switch (yyval.type->type) { case RPC_FC_CHAR: break; @@ -2099,97 +2107,102 @@ case 176: } ; break;} -case 177: -#line 567 "parser.y" -{ yyval.type = make_type(RPC_FC_FLOAT, NULL); ; - break;} -case 178: -#line 568 "parser.y" -{ yyval.type = make_type(RPC_FC_DOUBLE, NULL); ; - break;} -case 179: -#line 569 "parser.y" -{ yyval.type = make_type(RPC_FC_SMALL, &std_bool); ; - break;} case 180: -#line 570 "parser.y" -{ yyval.type = make_type(RPC_FC_ERROR_STATUS_T, NULL); ; +#line 568 "parser.y" +{ yyval.type = make_type(RPC_FC_ULONG, &std_int); + yyval.type->sign = -1; ; break;} case 181: +#line 570 "parser.y" +{ yyval.type = make_type(RPC_FC_FLOAT, NULL); ; + break;} +case 182: #line 571 "parser.y" -{ yyval.type = make_type(RPC_FC_IGNORE, NULL); ; +{ yyval.type = make_type(RPC_FC_DOUBLE, NULL); ; + break;} +case 183: +#line 572 "parser.y" +{ yyval.type = make_type(RPC_FC_SMALL, &std_bool); ; break;} case 184: -#line 578 "parser.y" -{ yyval.type = make_type(RPC_FC_LONG, &std_int); ; +#line 573 "parser.y" +{ yyval.type = make_type(RPC_FC_ERROR_STATUS_T, NULL); ; break;} case 185: -#line 579 "parser.y" -{ yyval.type = make_type(RPC_FC_SMALL, NULL); ; - break;} -case 186: -#line 580 "parser.y" -{ yyval.type = make_type(RPC_FC_SHORT, NULL); ; - break;} -case 187: -#line 581 "parser.y" -{ yyval.type = make_type(RPC_FC_LONG, NULL); ; +#line 574 "parser.y" +{ yyval.type = make_type(RPC_FC_IGNORE, NULL); ; break;} case 188: -#line 582 "parser.y" -{ yyval.type = make_type(RPC_FC_HYPER, NULL); ; +#line 581 "parser.y" +{ yyval.type = make_type(RPC_FC_LONG, &std_int); ; break;} case 189: -#line 583 "parser.y" -{ yyval.type = make_type(RPC_FC_HYPER, &std_int64); ; +#line 582 "parser.y" +{ yyval.type = make_type(RPC_FC_SMALL, NULL); ; break;} case 190: -#line 584 "parser.y" -{ yyval.type = make_type(RPC_FC_CHAR, NULL); ; +#line 583 "parser.y" +{ yyval.type = make_type(RPC_FC_SHORT, NULL); ; break;} case 191: -#line 587 "parser.y" -{ yyval.clas = make_class(yyvsp[0].str); ; +#line 584 "parser.y" +{ yyval.type = make_type(RPC_FC_LONG, NULL); ; break;} case 192: -#line 588 "parser.y" -{ yyval.clas = make_class(yyvsp[0].str); ; +#line 585 "parser.y" +{ yyval.type = make_type(RPC_FC_HYPER, NULL); ; break;} case 193: +#line 586 "parser.y" +{ yyval.type = make_type(RPC_FC_HYPER, &std_int64); ; + break;} +case 194: +#line 587 "parser.y" +{ yyval.type = make_type(RPC_FC_CHAR, NULL); ; + break;} +case 195: +#line 590 "parser.y" +{ yyval.clas = make_class(yyvsp[0].str); ; + break;} +case 196: #line 591 "parser.y" +{ yyval.clas = make_class(yyvsp[0].str); ; + break;} +case 197: +#line 594 "parser.y" { yyval.clas = yyvsp[0].clas; yyval.clas->attrs = yyvsp[-1].attr; if (!parse_only && do_header) write_coclass(yyval.clas); ; break;} -case 194: -#line 597 "parser.y" +case 198: +#line 600 "parser.y" { yyval.clas = yyvsp[-3].clas; yyval.clas->ifaces = yyvsp[-1].ifref; ; break;} -case 195: -#line 602 "parser.y" +case 199: +#line 605 "parser.y" { yyval.ifref = NULL; ; break;} -case 196: -#line 603 "parser.y" +case 200: +#line 606 "parser.y" { LINK(yyvsp[0].ifref, yyvsp[-1].ifref); yyval.ifref = yyvsp[0].ifref; ; break;} -case 197: -#line 607 "parser.y" +case 201: +#line 610 "parser.y" { yyval.ifref = make_ifref(yyvsp[0].type); yyval.ifref->attrs = yyvsp[-1].attr; ; break;} -case 198: -#line 610 "parser.y" +case 202: +#line 613 "parser.y" { yyval.type = get_type(0, yyvsp[0].str, 0); ; break;} -case 199: -#line 611 "parser.y" -{ yyval.type = get_type(0, yyvsp[0].str, 0); ; - break;} -case 200: +case 203: #line 614 "parser.y" +{ yyval.type = get_type(0, yyvsp[0].str, 0); ; + break;} +case 204: +#line 617 "parser.y" { yyval.type = yyvsp[0].type; if (yyval.type->defined) yyerror("multiple definition error\n"); yyval.type->attrs = yyvsp[-1].attr; @@ -2201,48 +2214,48 @@ case 200: if (!parse_only && do_header) write_forward(yyval.type); ; break;} -case 201: -#line 626 "parser.y" +case 205: +#line 629 "parser.y" { yyval.var = NULL; ; break;} -case 202: -#line 627 "parser.y" +case 206: +#line 630 "parser.y" { LINK(yyvsp[-1].var, yyvsp[-2].var); yyval.var = yyvsp[-1].var; ; break;} -case 203: -#line 630 "parser.y" +case 207: +#line 633 "parser.y" { yyval.func = NULL; ; break;} -case 204: -#line 631 "parser.y" +case 208: +#line 634 "parser.y" { LINK(yyvsp[-1].func, yyvsp[-2].func); yyval.func = yyvsp[-1].func; ; break;} -case 205: -#line 637 "parser.y" +case 209: +#line 640 "parser.y" { yyval.type = yyvsp[-4].type; yyval.type->fields = yyvsp[-2].var; yyval.type->funcs = yyvsp[-1].func; if (!parse_only && do_header) write_dispinterface(yyval.type); ; break;} -case 206: -#line 649 "parser.y" +case 210: +#line 652 "parser.y" { yyval.type = NULL; ; break;} -case 207: -#line 650 "parser.y" +case 211: +#line 653 "parser.y" { yyval.type = find_type2(yyvsp[0].str, 0); ; break;} -case 208: -#line 653 "parser.y" +case 212: +#line 656 "parser.y" { yyval.type = get_type(RPC_FC_IP, yyvsp[0].str, 0); ; break;} -case 209: -#line 654 "parser.y" -{ yyval.type = get_type(RPC_FC_IP, yyvsp[0].str, 0); ; - break;} -case 210: +case 213: #line 657 "parser.y" +{ yyval.type = get_type(RPC_FC_IP, yyvsp[0].str, 0); ; + break;} +case 214: +#line 660 "parser.y" { yyval.type = yyvsp[0].type; if (yyval.type->defined) yyerror("multiple definition error\n"); yyval.type->attrs = yyvsp[-1].attr; @@ -2250,16 +2263,16 @@ case 210: if (!parse_only && do_header) write_forward(yyval.type); ; break;} -case 211: -#line 666 "parser.y" +case 215: +#line 669 "parser.y" { yyval.type = yyvsp[-4].type; yyval.type->ref = yyvsp[-3].type; yyval.type->funcs = yyvsp[-1].func; if (!parse_only && do_header) write_interface(yyval.type); ; break;} -case 212: -#line 674 "parser.y" +case 216: +#line 677 "parser.y" { yyval.type = yyvsp[-6].type; yyval.type->ref = find_type2(yyvsp[-4].str, 0); if (!yyval.type->ref) yyerror("base class %s not found in import\n", yyvsp[-4].str); @@ -2267,65 +2280,65 @@ case 212: if (!parse_only && do_header) write_interface(yyval.type); ; break;} -case 213: -#line 680 "parser.y" +case 217: +#line 683 "parser.y" { yyval.type = yyvsp[0].type; ; break;} -case 214: -#line 684 "parser.y" -{ yyval.type = yyvsp[-1].type; if (!parse_only && do_header) write_forward(yyval.type); ; - break;} -case 215: -#line 685 "parser.y" -{ yyval.type = yyvsp[-1].type; if (!parse_only && do_header) write_forward(yyval.type); ; - break;} -case 216: -#line 688 "parser.y" -{ yyval.type = make_type(0, NULL); yyval.type->name = yyvsp[0].str; ; - break;} -case 217: -#line 689 "parser.y" -{ yyval.type = make_type(0, NULL); yyval.type->name = yyvsp[0].str; ; - break;} case 218: +#line 687 "parser.y" +{ yyval.type = yyvsp[-1].type; if (!parse_only && do_header) write_forward(yyval.type); ; + break;} +case 219: +#line 688 "parser.y" +{ yyval.type = yyvsp[-1].type; if (!parse_only && do_header) write_forward(yyval.type); ; + break;} +case 220: +#line 691 "parser.y" +{ yyval.type = make_type(0, NULL); yyval.type->name = yyvsp[0].str; ; + break;} +case 221: #line 692 "parser.y" +{ yyval.type = make_type(0, NULL); yyval.type->name = yyvsp[0].str; ; + break;} +case 222: +#line 695 "parser.y" { yyval.type = yyvsp[0].type; yyval.type->attrs = yyvsp[-1].attr; ; break;} -case 219: -#line 697 "parser.y" +case 223: +#line 700 "parser.y" { yyval.type = yyvsp[-3].type; yyval.type->funcs = yyvsp[-1].func; /* FIXME: if (!parse_only && do_header) write_module($$); */ ; break;} -case 220: -#line 703 "parser.y" +case 224: +#line 706 "parser.y" { yyval.var = yyvsp[0].var; yyval.var->ptr_level++; ; break;} -case 221: -#line 704 "parser.y" +case 225: +#line 707 "parser.y" { yyval.var = yyvsp[0].var; /* FIXME */ ; break;} -case 224: -#line 709 "parser.y" +case 228: +#line 712 "parser.y" { yyval.var = yyvsp[-1].var; ; break;} -case 226: -#line 714 "parser.y" +case 230: +#line 717 "parser.y" { LINK(yyvsp[0].var, yyvsp[-2].var); yyval.var = yyvsp[0].var; ; break;} -case 227: -#line 718 "parser.y" +case 231: +#line 721 "parser.y" { yyval.num = RPC_FC_RP; ; break;} -case 228: -#line 719 "parser.y" +case 232: +#line 722 "parser.y" { yyval.num = RPC_FC_UP; ; break;} -case 229: -#line 722 "parser.y" +case 233: +#line 725 "parser.y" { yyval.type = get_typev(RPC_FC_STRUCT, yyvsp[-3].var, tsSTRUCT); /* overwrite RPC_FC_STRUCT with a more exact type */ yyval.type->type = get_struct_type( yyvsp[-1].var ); @@ -2335,48 +2348,48 @@ case 229: add_struct(yyval.type); ; break;} -case 230: -#line 732 "parser.y" +case 234: +#line 735 "parser.y" { yyval.tref = make_tref(NULL, make_type(0, NULL)); ; break;} -case 231: -#line 733 "parser.y" +case 235: +#line 736 "parser.y" { yyval.tref = make_tref(yyvsp[0].str, find_type(yyvsp[0].str, 0)); ; break;} -case 232: -#line 734 "parser.y" -{ yyval.tref = make_tref(NULL, yyvsp[0].type); ; - break;} -case 233: -#line 735 "parser.y" -{ yyval.tref = uniq_tref(yyvsp[0].tref); yyval.tref->ref->is_const = TRUE; ; - break;} -case 234: -#line 736 "parser.y" -{ yyval.tref = make_tref(NULL, yyvsp[0].type); ; - break;} -case 235: -#line 737 "parser.y" -{ yyval.tref = make_tref(NULL, find_type2(yyvsp[0].str, tsENUM)); ; - break;} case 236: -#line 738 "parser.y" +#line 737 "parser.y" { yyval.tref = make_tref(NULL, yyvsp[0].type); ; break;} case 237: -#line 739 "parser.y" -{ yyval.tref = make_tref(NULL, get_type(RPC_FC_STRUCT, yyvsp[0].str, tsSTRUCT)); ; +#line 738 "parser.y" +{ yyval.tref = uniq_tref(yyvsp[0].tref); yyval.tref->ref->is_const = TRUE; ; break;} case 238: -#line 740 "parser.y" +#line 739 "parser.y" { yyval.tref = make_tref(NULL, yyvsp[0].type); ; break;} case 239: -#line 741 "parser.y" -{ yyval.tref = make_tref(NULL, find_type2(yyvsp[0].str, tsUNION)); ; +#line 740 "parser.y" +{ yyval.tref = make_tref(NULL, find_type2(yyvsp[0].str, tsENUM)); ; break;} case 240: +#line 741 "parser.y" +{ yyval.tref = make_tref(NULL, yyvsp[0].type); ; + break;} +case 241: +#line 742 "parser.y" +{ yyval.tref = make_tref(NULL, get_type(RPC_FC_STRUCT, yyvsp[0].str, tsSTRUCT)); ; + break;} +case 242: +#line 743 "parser.y" +{ yyval.tref = make_tref(NULL, yyvsp[0].type); ; + break;} +case 243: #line 744 "parser.y" +{ yyval.tref = make_tref(NULL, find_type2(yyvsp[0].str, tsUNION)); ; + break;} +case 244: +#line 747 "parser.y" { typeref_t *tref = uniq_tref(yyvsp[-1].tref); yyvsp[0].var->tname = tref->name; tref->name = NULL; @@ -2388,15 +2401,15 @@ case 240: reg_types(yyval.type, yyvsp[0].var, 0); ; break;} -case 241: -#line 756 "parser.y" +case 245: +#line 759 "parser.y" { yyval.type = get_typev(RPC_FC_NON_ENCAPSULATED_UNION, yyvsp[-3].var, tsUNION); yyval.type->fields = yyvsp[-1].var; yyval.type->defined = TRUE; ; break;} -case 242: -#line 762 "parser.y" +case 246: +#line 765 "parser.y" { var_t *u = yyvsp[-3].var; yyval.type = get_typev(RPC_FC_ENCAPSULATED_UNION, yyvsp[-8].var, tsUNION); if (!u) u = make_var("tagged_union"); @@ -2407,12 +2420,12 @@ case 242: yyval.type->defined = TRUE; ; break;} -case 243: -#line 774 "parser.y" +case 247: +#line 777 "parser.y" { yyval.num = MAKELONG(yyvsp[0].num, 0); ; break;} -case 244: -#line 775 "parser.y" +case 248: +#line 778 "parser.y" { yyval.num = MAKELONG(yyvsp[-2].num, yyvsp[0].num); ; break;} } @@ -2637,7 +2650,7 @@ yyerrhandle: } return 1; } -#line 778 "parser.y" +#line 781 "parser.y" static attr_t *make_attr(enum attr_type type) diff --git a/reactos/tools/widl/y.tab.h b/reactos/tools/widl/y.tab.h index 4ae1eb30003..1e3f2af1dd4 100644 --- a/reactos/tools/widl/y.tab.h +++ b/reactos/tools/widl/y.tab.h @@ -98,40 +98,41 @@ typedef union { #define tPROPGET 341 #define tPROPPUT 342 #define tPROPPUTREF 343 -#define tPUBLIC 344 -#define tREADONLY 345 -#define tREF 346 -#define tRESTRICTED 347 -#define tRETVAL 348 -#define tSHORT 349 -#define tSIGNED 350 -#define tSIZEIS 351 -#define tSIZEOF 352 -#define tSMALL 353 -#define tSOURCE 354 -#define tSTDCALL 355 -#define tSTRING 356 -#define tSTRUCT 357 -#define tSWITCH 358 -#define tSWITCHIS 359 -#define tSWITCHTYPE 360 -#define tTRANSMITAS 361 -#define tTYPEDEF 362 -#define tUNION 363 -#define tUNIQUE 364 -#define tUNSIGNED 365 -#define tUUID 366 -#define tV1ENUM 367 -#define tVARARG 368 -#define tVERSION 369 -#define tVOID 370 -#define tWCHAR 371 -#define tWIREMARSHAL 372 -#define tPOINTERTYPE 373 -#define COND 374 -#define CAST 375 -#define PPTR 376 -#define NEG 377 +#define tPTR 344 +#define tPUBLIC 345 +#define tREADONLY 346 +#define tREF 347 +#define tRESTRICTED 348 +#define tRETVAL 349 +#define tSHORT 350 +#define tSIGNED 351 +#define tSIZEIS 352 +#define tSIZEOF 353 +#define tSMALL 354 +#define tSOURCE 355 +#define tSTDCALL 356 +#define tSTRING 357 +#define tSTRUCT 358 +#define tSWITCH 359 +#define tSWITCHIS 360 +#define tSWITCHTYPE 361 +#define tTRANSMITAS 362 +#define tTYPEDEF 363 +#define tUNION 364 +#define tUNIQUE 365 +#define tUNSIGNED 366 +#define tUUID 367 +#define tV1ENUM 368 +#define tVARARG 369 +#define tVERSION 370 +#define tVOID 371 +#define tWCHAR 372 +#define tWIREMARSHAL 373 +#define tPOINTERTYPE 374 +#define COND 375 +#define CAST 376 +#define PPTR 377 +#define NEG 378 extern YYSTYPE yylval; diff --git a/reactos/tools/wpp/lex.yy.c b/reactos/tools/wpp/lex.yy.c index 779d55e2ee9..d5d6a82cc5e 100644 --- a/reactos/tools/wpp/lex.yy.c +++ b/reactos/tools/wpp/lex.yy.c @@ -16,7 +16,7 @@ #define yytext pptext #define yywrap ppwrap -#line 20 "wpp/lex.yy.c" +#line 20 "lex.yy.c" /* A lexical scanner generated by flex */ /* Scanner skeleton version: @@ -13707,9 +13707,9 @@ static yyconst short int yy_rule_linenum[144] = 470, 471, 472, 483, 484, 488, 489, 501, 505, 514, 520, 521, 522, 523, 524, 525, 530, 531, 532, 533, - 538, 546, 547, 548, 549, 572, 573, 587, 588, 594, - 618, 619, 628, 673, 674, 675, 676, 677, 683, 685, - 691, 693, 701 + 538, 546, 547, 548, 549, 571, 572, 586, 587, 593, + 617, 618, 627, 672, 673, 674, 675, 676, 682, 684, + 690, 692, 700 } ; /* The intent behind this definition is that it'll catch @@ -13720,7 +13720,7 @@ static yyconst short int yy_rule_linenum[144] = #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *yytext; -#line 1 "wpp/ppl.l" +#line 1 "ppl.l" #define INITIAL 0 /* * Wrc preprocessor lexical analysis @@ -13893,7 +13893,7 @@ char *yytext; #define RCINCL 21 -#line 159 "wpp/ppl.l" +#line 159 "ppl.l" #include #include #include @@ -14031,7 +14031,7 @@ includelogicentry_t *pp_includelogiclist = NULL; * The scanner starts here ************************************************************************** */ -#line 14035 "wpp/lex.yy.c" +#line 14035 "lex.yy.c" /* Macros after this point can all be overridden by user definitions in * section 1. @@ -14196,7 +14196,7 @@ YY_DECL register char *yy_cp, *yy_bp; register int yy_act; -#line 299 "wpp/ppl.l" +#line 299 "ppl.l" /* * Catch line-continuations. @@ -14210,7 +14210,7 @@ YY_DECL /* * Detect the leading # of a preprocessor directive. */ -#line 14214 "wpp/lex.yy.c" +#line 14214 "lex.yy.c" if ( yy_init ) { @@ -14299,7 +14299,7 @@ do_action: /* This label is used only to access EOF actions. */ case 1: YY_RULE_SETUP -#line 312 "wpp/ppl.l" +#line 312 "ppl.l" pp_incl_state.seen_junk++; yy_push_state(pp_pp); YY_BREAK /* @@ -14307,107 +14307,107 @@ pp_incl_state.seen_junk++; yy_push_state(pp_pp); */ case 2: YY_RULE_SETUP -#line 317 "wpp/ppl.l" +#line 317 "ppl.l" if(yy_top_state() != pp_ignore) {yy_pp_state(pp_inc); return tINCLUDE;} else {yy_pp_state(pp_eol);} YY_BREAK case 3: YY_RULE_SETUP -#line 318 "wpp/ppl.l" +#line 318 "ppl.l" if(yy_top_state() != pp_ignore) {yy_pp_state(pp_inc); return tINCLUDE_NEXT;} else {yy_pp_state(pp_eol);} YY_BREAK case 4: YY_RULE_SETUP -#line 319 "wpp/ppl.l" +#line 319 "ppl.l" yy_pp_state(yy_current_state() != pp_ignore ? pp_def : pp_eol); YY_BREAK case 5: YY_RULE_SETUP -#line 320 "wpp/ppl.l" +#line 320 "ppl.l" yy_pp_state(pp_eol); if(yy_top_state() != pp_ignore) return tERROR; YY_BREAK case 6: YY_RULE_SETUP -#line 321 "wpp/ppl.l" +#line 321 "ppl.l" yy_pp_state(pp_eol); if(yy_top_state() != pp_ignore) return tWARNING; YY_BREAK case 7: YY_RULE_SETUP -#line 322 "wpp/ppl.l" +#line 322 "ppl.l" yy_pp_state(pp_eol); if(yy_top_state() != pp_ignore) return tPRAGMA; YY_BREAK case 8: YY_RULE_SETUP -#line 323 "wpp/ppl.l" +#line 323 "ppl.l" yy_pp_state(pp_eol); if(yy_top_state() != pp_ignore) return tPPIDENT; YY_BREAK case 9: YY_RULE_SETUP -#line 324 "wpp/ppl.l" +#line 324 "ppl.l" if(yy_top_state() != pp_ignore) {yy_pp_state(pp_ifd); return tUNDEF;} else {yy_pp_state(pp_eol);} YY_BREAK case 10: YY_RULE_SETUP -#line 325 "wpp/ppl.l" +#line 325 "ppl.l" yy_pp_state(pp_ifd); return tIFDEF; YY_BREAK case 11: YY_RULE_SETUP -#line 326 "wpp/ppl.l" +#line 326 "ppl.l" pp_incl_state.seen_junk--; yy_pp_state(pp_ifd); return tIFNDEF; YY_BREAK case 12: YY_RULE_SETUP -#line 327 "wpp/ppl.l" +#line 327 "ppl.l" yy_pp_state(pp_if); return tIF; YY_BREAK case 13: YY_RULE_SETUP -#line 328 "wpp/ppl.l" +#line 328 "ppl.l" yy_pp_state(pp_if); return tELIF; YY_BREAK case 14: YY_RULE_SETUP -#line 329 "wpp/ppl.l" +#line 329 "ppl.l" yy_pp_state(pp_endif); return tELSE; YY_BREAK case 15: YY_RULE_SETUP -#line 330 "wpp/ppl.l" +#line 330 "ppl.l" yy_pp_state(pp_endif); return tENDIF; YY_BREAK case 16: YY_RULE_SETUP -#line 331 "wpp/ppl.l" +#line 331 "ppl.l" if(yy_top_state() != pp_ignore) {yy_pp_state(pp_line); return tLINE;} else {yy_pp_state(pp_eol);} YY_BREAK case 17: YY_RULE_SETUP -#line 332 "wpp/ppl.l" +#line 332 "ppl.l" if(yy_top_state() != pp_ignore) {yy_pp_state(pp_line); return tGCCLINE;} else {yy_pp_state(pp_eol);} YY_BREAK case 18: YY_RULE_SETUP -#line 333 "wpp/ppl.l" +#line 333 "ppl.l" pperror("Invalid preprocessor token '%s'", pptext); YY_BREAK case 19: YY_RULE_SETUP -#line 334 "wpp/ppl.l" +#line 334 "ppl.l" newline(1); yy_pop_state(); return tNL; /* This could be the null-token */ YY_BREAK case 20: YY_RULE_SETUP -#line 335 "wpp/ppl.l" +#line 335 "ppl.l" newline(0); YY_BREAK case 21: YY_RULE_SETUP -#line 336 "wpp/ppl.l" +#line 336 "ppl.l" pperror("Preprocessor junk '%s'", pptext); YY_BREAK case 22: YY_RULE_SETUP -#line 337 "wpp/ppl.l" +#line 337 "ppl.l" return *pptext; YY_BREAK /* @@ -14415,37 +14415,37 @@ return *pptext; */ case 23: YY_RULE_SETUP -#line 342 "wpp/ppl.l" +#line 342 "ppl.l" return make_number(10, &pplval, pptext, ppleng); YY_BREAK case 24: YY_RULE_SETUP -#line 343 "wpp/ppl.l" +#line 343 "ppl.l" new_string(); add_string(pptext, ppleng); yy_push_state(pp_iqs); YY_BREAK case 25: YY_RULE_SETUP -#line 344 "wpp/ppl.l" +#line 344 "ppl.l" new_string(); add_string(pptext, ppleng); yy_push_state(pp_dqs); YY_BREAK case 26: YY_RULE_SETUP -#line 345 "wpp/ppl.l" +#line 345 "ppl.l" ; YY_BREAK case 27: YY_RULE_SETUP -#line 346 "wpp/ppl.l" +#line 346 "ppl.l" newline(1); yy_pop_state(); return tNL; YY_BREAK case 28: YY_RULE_SETUP -#line 347 "wpp/ppl.l" +#line 347 "ppl.l" newline(0); YY_BREAK case 29: YY_RULE_SETUP -#line 348 "wpp/ppl.l" +#line 348 "ppl.l" pperror(yy_current_state() == pp_inc ? "Trailing junk in #include" : "Trailing junk in #line"); YY_BREAK /* @@ -14453,22 +14453,22 @@ pperror(yy_current_state() == pp_inc ? "Trailing junk in #include" : "Trailing j */ case 30: YY_RULE_SETUP -#line 353 "wpp/ppl.l" +#line 353 "ppl.l" ; YY_BREAK case 31: YY_RULE_SETUP -#line 354 "wpp/ppl.l" +#line 354 "ppl.l" newline(1); YY_BREAK case 32: YY_RULE_SETUP -#line 355 "wpp/ppl.l" +#line 355 "ppl.l" newline(0); YY_BREAK case 33: YY_RULE_SETUP -#line 356 "wpp/ppl.l" +#line 356 "ppl.l" ; YY_BREAK /* @@ -14479,107 +14479,107 @@ YY_RULE_SETUP */ case 34: YY_RULE_SETUP -#line 365 "wpp/ppl.l" +#line 365 "ppl.l" return make_number(8, &pplval, pptext, ppleng); YY_BREAK case 35: YY_RULE_SETUP -#line 366 "wpp/ppl.l" +#line 366 "ppl.l" pperror("Invalid octal digit"); YY_BREAK case 36: YY_RULE_SETUP -#line 367 "wpp/ppl.l" +#line 367 "ppl.l" return make_number(10, &pplval, pptext, ppleng); YY_BREAK case 37: YY_RULE_SETUP -#line 368 "wpp/ppl.l" +#line 368 "ppl.l" return make_number(16, &pplval, pptext, ppleng); YY_BREAK case 38: YY_RULE_SETUP -#line 369 "wpp/ppl.l" +#line 369 "ppl.l" pperror("Invalid hex number"); YY_BREAK case 39: YY_RULE_SETUP -#line 370 "wpp/ppl.l" +#line 370 "ppl.l" yy_push_state(pp_defined); return tDEFINED; YY_BREAK case 40: YY_RULE_SETUP -#line 371 "wpp/ppl.l" +#line 371 "ppl.l" return tLSHIFT; YY_BREAK case 41: YY_RULE_SETUP -#line 372 "wpp/ppl.l" +#line 372 "ppl.l" return tRSHIFT; YY_BREAK case 42: YY_RULE_SETUP -#line 373 "wpp/ppl.l" +#line 373 "ppl.l" return tLOGAND; YY_BREAK case 43: YY_RULE_SETUP -#line 374 "wpp/ppl.l" +#line 374 "ppl.l" return tLOGOR; YY_BREAK case 44: YY_RULE_SETUP -#line 375 "wpp/ppl.l" +#line 375 "ppl.l" return tEQ; YY_BREAK case 45: YY_RULE_SETUP -#line 376 "wpp/ppl.l" +#line 376 "ppl.l" return tNE; YY_BREAK case 46: YY_RULE_SETUP -#line 377 "wpp/ppl.l" +#line 377 "ppl.l" return tLTE; YY_BREAK case 47: YY_RULE_SETUP -#line 378 "wpp/ppl.l" +#line 378 "ppl.l" return tGTE; YY_BREAK case 48: YY_RULE_SETUP -#line 379 "wpp/ppl.l" +#line 379 "ppl.l" newline(1); yy_pop_state(); return tNL; YY_BREAK case 49: YY_RULE_SETUP -#line 380 "wpp/ppl.l" +#line 380 "ppl.l" newline(0); YY_BREAK case 50: YY_RULE_SETUP -#line 381 "wpp/ppl.l" +#line 381 "ppl.l" pperror("Junk in conditional expression"); YY_BREAK case 51: YY_RULE_SETUP -#line 382 "wpp/ppl.l" +#line 382 "ppl.l" ; YY_BREAK case 52: YY_RULE_SETUP -#line 383 "wpp/ppl.l" +#line 383 "ppl.l" new_string(); add_string(pptext, ppleng); yy_push_state(pp_sqs); YY_BREAK case 53: YY_RULE_SETUP -#line 384 "wpp/ppl.l" +#line 384 "ppl.l" pperror("String constants not allowed in conditionals"); YY_BREAK case 54: YY_RULE_SETUP -#line 385 "wpp/ppl.l" +#line 385 "ppl.l" return *pptext; YY_BREAK /* @@ -14588,27 +14588,27 @@ return *pptext; */ case 55: YY_RULE_SETUP -#line 391 "wpp/ppl.l" +#line 391 "ppl.l" pplval.cptr = pp_xstrdup(pptext); return tIDENT; YY_BREAK case 56: YY_RULE_SETUP -#line 392 "wpp/ppl.l" +#line 392 "ppl.l" ; YY_BREAK case 57: YY_RULE_SETUP -#line 393 "wpp/ppl.l" +#line 393 "ppl.l" newline(1); yy_pop_state(); return tNL; YY_BREAK case 58: YY_RULE_SETUP -#line 394 "wpp/ppl.l" +#line 394 "ppl.l" newline(0); YY_BREAK case 59: YY_RULE_SETUP -#line 395 "wpp/ppl.l" +#line 395 "ppl.l" pperror("Identifier expected"); YY_BREAK /* @@ -14616,22 +14616,22 @@ pperror("Identifier expected"); */ case 60: YY_RULE_SETUP -#line 400 "wpp/ppl.l" +#line 400 "ppl.l" ; YY_BREAK case 61: YY_RULE_SETUP -#line 401 "wpp/ppl.l" +#line 401 "ppl.l" newline(1); yy_pop_state(); return tNL; YY_BREAK case 62: YY_RULE_SETUP -#line 402 "wpp/ppl.l" +#line 402 "ppl.l" newline(0); YY_BREAK case 63: YY_RULE_SETUP -#line 403 "wpp/ppl.l" +#line 403 "ppl.l" pperror("Garbage after #else or #endif."); YY_BREAK /* @@ -14641,27 +14641,27 @@ pperror("Garbage after #else or #endif."); */ case 64: YY_RULE_SETUP -#line 410 "wpp/ppl.l" +#line 410 "ppl.l" yy_pop_state(); pplval.cptr = pp_xstrdup(pptext); return tIDENT; YY_BREAK case 65: YY_RULE_SETUP -#line 411 "wpp/ppl.l" +#line 411 "ppl.l" ; YY_BREAK case 66: YY_RULE_SETUP -#line 412 "wpp/ppl.l" +#line 412 "ppl.l" return *pptext; YY_BREAK case 67: YY_RULE_SETUP -#line 413 "wpp/ppl.l" +#line 413 "ppl.l" newline(0); YY_BREAK case 68: YY_RULE_SETUP -#line 414 "wpp/ppl.l" +#line 414 "ppl.l" pperror("Identifier expected"); YY_BREAK /* @@ -14672,27 +14672,27 @@ pperror("Identifier expected"); */ case 69: YY_RULE_SETUP -#line 422 "wpp/ppl.l" +#line 422 "ppl.l" if(yy_top_state() != pp_ignore) { pplval.cptr = pp_xstrdup(pptext); return tLITERAL; } YY_BREAK case 70: YY_RULE_SETUP -#line 423 "wpp/ppl.l" +#line 423 "ppl.l" if(yy_top_state() != pp_ignore) { pplval.cptr = pp_xstrdup(pptext); return tLITERAL; } YY_BREAK case 71: YY_RULE_SETUP -#line 424 "wpp/ppl.l" +#line 424 "ppl.l" if(yy_top_state() != pp_ignore) { pplval.cptr = pp_xstrdup(pptext); return tLITERAL; } YY_BREAK case 72: YY_RULE_SETUP -#line 425 "wpp/ppl.l" +#line 425 "ppl.l" newline(1); yy_pop_state(); if(yy_current_state() != pp_ignore) { return tNL; } YY_BREAK case 73: YY_RULE_SETUP -#line 426 "wpp/ppl.l" +#line 426 "ppl.l" newline(0); YY_BREAK /* @@ -14700,27 +14700,27 @@ newline(0); */ case 74: YY_RULE_SETUP -#line 431 "wpp/ppl.l" +#line 431 "ppl.l" pplval.cptr = pp_xstrdup(pptext); pplval.cptr[ppleng-1] = '\0'; yy_pp_state(pp_macro); return tMACRO; YY_BREAK case 75: YY_RULE_SETUP -#line 432 "wpp/ppl.l" +#line 432 "ppl.l" pplval.cptr = pp_xstrdup(pptext); yy_pp_state(pp_define); return tDEFINE; YY_BREAK case 76: YY_RULE_SETUP -#line 433 "wpp/ppl.l" +#line 433 "ppl.l" ; YY_BREAK case 77: YY_RULE_SETUP -#line 434 "wpp/ppl.l" +#line 434 "ppl.l" newline(0); YY_BREAK case 78: YY_RULE_SETUP -#line 435 "wpp/ppl.l" +#line 435 "ppl.l" perror("Identifier expected"); YY_BREAK /* @@ -14728,37 +14728,37 @@ perror("Identifier expected"); */ case 79: YY_RULE_SETUP -#line 440 "wpp/ppl.l" +#line 440 "ppl.l" pplval.cptr = pp_xstrdup(pptext); return tLITERAL; YY_BREAK case 80: YY_RULE_SETUP -#line 441 "wpp/ppl.l" +#line 441 "ppl.l" pplval.cptr = pp_xstrdup(pptext); return tLITERAL; YY_BREAK case 81: YY_RULE_SETUP -#line 442 "wpp/ppl.l" +#line 442 "ppl.l" newline(0); pplval.cptr = pp_xstrdup(" "); return tLITERAL; YY_BREAK case 82: YY_RULE_SETUP -#line 443 "wpp/ppl.l" +#line 443 "ppl.l" newline(0); YY_BREAK case 83: YY_RULE_SETUP -#line 444 "wpp/ppl.l" +#line 444 "ppl.l" newline(1); yy_pop_state(); return tNL; YY_BREAK case 84: YY_RULE_SETUP -#line 445 "wpp/ppl.l" +#line 445 "ppl.l" new_string(); add_string(pptext, ppleng); yy_push_state(pp_sqs); YY_BREAK case 85: YY_RULE_SETUP -#line 446 "wpp/ppl.l" +#line 446 "ppl.l" new_string(); add_string(pptext, ppleng); yy_push_state(pp_dqs); YY_BREAK /* @@ -14766,37 +14766,37 @@ new_string(); add_string(pptext, ppleng); yy_push_state(pp_dqs); */ case 86: YY_RULE_SETUP -#line 451 "wpp/ppl.l" +#line 451 "ppl.l" yy_pp_state(pp_mbody); return tMACROEND; YY_BREAK case 87: YY_RULE_SETUP -#line 452 "wpp/ppl.l" +#line 452 "ppl.l" ; YY_BREAK case 88: YY_RULE_SETUP -#line 453 "wpp/ppl.l" +#line 453 "ppl.l" pplval.cptr = pp_xstrdup(pptext); return tIDENT; YY_BREAK case 89: YY_RULE_SETUP -#line 454 "wpp/ppl.l" +#line 454 "ppl.l" return ','; YY_BREAK case 90: YY_RULE_SETUP -#line 455 "wpp/ppl.l" +#line 455 "ppl.l" return tELIPSIS; YY_BREAK case 91: YY_RULE_SETUP -#line 456 "wpp/ppl.l" +#line 456 "ppl.l" pperror("Argument identifier expected"); YY_BREAK case 92: YY_RULE_SETUP -#line 457 "wpp/ppl.l" +#line 457 "ppl.l" newline(0); YY_BREAK /* @@ -14804,57 +14804,57 @@ newline(0); */ case 93: YY_RULE_SETUP -#line 462 "wpp/ppl.l" +#line 462 "ppl.l" pplval.cptr = pp_xstrdup(pptext); return tLITERAL; YY_BREAK case 94: YY_RULE_SETUP -#line 463 "wpp/ppl.l" +#line 463 "ppl.l" pplval.cptr = pp_xstrdup(pptext); return tIDENT; YY_BREAK case 95: YY_RULE_SETUP -#line 464 "wpp/ppl.l" +#line 464 "ppl.l" return tCONCAT; YY_BREAK case 96: YY_RULE_SETUP -#line 465 "wpp/ppl.l" +#line 465 "ppl.l" return tSTRINGIZE; YY_BREAK case 97: YY_RULE_SETUP -#line 466 "wpp/ppl.l" +#line 466 "ppl.l" pplval.cptr = pp_xstrdup(pptext); return tLITERAL; YY_BREAK case 98: YY_RULE_SETUP -#line 467 "wpp/ppl.l" +#line 467 "ppl.l" pplval.cptr = pp_xstrdup(pptext); return tLITERAL; YY_BREAK case 99: YY_RULE_SETUP -#line 468 "wpp/ppl.l" +#line 468 "ppl.l" newline(0); pplval.cptr = pp_xstrdup(" "); return tLITERAL; YY_BREAK case 100: YY_RULE_SETUP -#line 469 "wpp/ppl.l" +#line 469 "ppl.l" newline(0); YY_BREAK case 101: YY_RULE_SETUP -#line 470 "wpp/ppl.l" +#line 470 "ppl.l" newline(1); yy_pop_state(); return tNL; YY_BREAK case 102: YY_RULE_SETUP -#line 471 "wpp/ppl.l" +#line 471 "ppl.l" new_string(); add_string(pptext, ppleng); yy_push_state(pp_sqs); YY_BREAK case 103: YY_RULE_SETUP -#line 472 "wpp/ppl.l" +#line 472 "ppl.l" new_string(); add_string(pptext, ppleng); yy_push_state(pp_dqs); YY_BREAK /* @@ -14871,12 +14871,12 @@ case 104: yy_c_buf_p = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 483 "wpp/ppl.l" +#line 483 "ppl.l" yy_pp_state(pp_macscan); YY_BREAK case 105: YY_RULE_SETUP -#line 484 "wpp/ppl.l" +#line 484 "ppl.l" { if(yy_top_state() != pp_macscan) newline(0); @@ -14884,12 +14884,12 @@ YY_RULE_SETUP YY_BREAK case 106: YY_RULE_SETUP -#line 488 "wpp/ppl.l" +#line 488 "ppl.l" newline(0); YY_BREAK case 107: YY_RULE_SETUP -#line 489 "wpp/ppl.l" +#line 489 "ppl.l" { macexpstackentry_t *mac = pop_macro(); yy_pop_state(); @@ -14904,7 +14904,7 @@ YY_RULE_SETUP */ case 108: YY_RULE_SETUP -#line 501 "wpp/ppl.l" +#line 501 "ppl.l" { if(++MACROPARENTHESES() > 1) add_text_to_macro(pptext, ppleng); @@ -14912,7 +14912,7 @@ YY_RULE_SETUP YY_BREAK case 109: YY_RULE_SETUP -#line 505 "wpp/ppl.l" +#line 505 "ppl.l" { if(--MACROPARENTHESES() == 0) { @@ -14925,7 +14925,7 @@ YY_RULE_SETUP YY_BREAK case 110: YY_RULE_SETUP -#line 514 "wpp/ppl.l" +#line 514 "ppl.l" { if(MACROPARENTHESES() > 1) add_text_to_macro(pptext, ppleng); @@ -14935,32 +14935,32 @@ YY_RULE_SETUP YY_BREAK case 111: YY_RULE_SETUP -#line 520 "wpp/ppl.l" +#line 520 "ppl.l" new_string(); add_string(pptext, ppleng); yy_push_state(pp_dqs); YY_BREAK case 112: YY_RULE_SETUP -#line 521 "wpp/ppl.l" +#line 521 "ppl.l" new_string(); add_string(pptext, ppleng); yy_push_state(pp_sqs); YY_BREAK case 113: YY_RULE_SETUP -#line 522 "wpp/ppl.l" +#line 522 "ppl.l" yy_push_state(pp_comment); add_text_to_macro(" ", 1); YY_BREAK case 114: YY_RULE_SETUP -#line 523 "wpp/ppl.l" +#line 523 "ppl.l" pp_status.line_number++; pp_status.char_number = 1; add_text_to_macro(pptext, ppleng); YY_BREAK case 115: YY_RULE_SETUP -#line 524 "wpp/ppl.l" +#line 524 "ppl.l" add_text_to_macro(pptext, ppleng); YY_BREAK case 116: YY_RULE_SETUP -#line 525 "wpp/ppl.l" +#line 525 "ppl.l" newline(0); YY_BREAK /* @@ -14968,22 +14968,22 @@ newline(0); */ case 117: YY_RULE_SETUP -#line 530 "wpp/ppl.l" +#line 530 "ppl.l" yy_push_state(pp_comment); YY_BREAK case 118: YY_RULE_SETUP -#line 531 "wpp/ppl.l" +#line 531 "ppl.l" ; YY_BREAK case 119: YY_RULE_SETUP -#line 532 "wpp/ppl.l" +#line 532 "ppl.l" newline(0); YY_BREAK case 120: YY_RULE_SETUP -#line 533 "wpp/ppl.l" +#line 533 "ppl.l" yy_pop_state(); YY_BREAK /* @@ -14991,7 +14991,7 @@ yy_pop_state(); */ case 121: YY_RULE_SETUP -#line 538 "wpp/ppl.l" +#line 538 "ppl.l" { if(pptext[ppleng-1] == '\\') ppwarning("C++ style comment ends with an escaped newline (escape ignored)"); @@ -15002,22 +15002,22 @@ YY_RULE_SETUP */ case 122: YY_RULE_SETUP -#line 546 "wpp/ppl.l" +#line 546 "ppl.l" pp_incl_state.seen_junk++; new_string(); add_string(pptext, ppleng); yy_push_state(pp_dqs); YY_BREAK case 123: YY_RULE_SETUP -#line 547 "wpp/ppl.l" +#line 547 "ppl.l" pp_incl_state.seen_junk++; new_string(); add_string(pptext, ppleng); yy_push_state(pp_sqs); YY_BREAK case 124: YY_RULE_SETUP -#line 548 "wpp/ppl.l" +#line 548 "ppl.l" add_string(pptext, ppleng); YY_BREAK case 125: YY_RULE_SETUP -#line 549 "wpp/ppl.l" +#line 549 "ppl.l" { add_string(pptext, ppleng); yy_pop_state(); @@ -15035,7 +15035,6 @@ YY_RULE_SETUP pplval.cptr = get_string(); if (is_c_h_include(pplval.cptr, 1)) pass_data=0; else pass_data=1; -//fprintf(stderr, "%s pass_data %d\n", pplval.cptr, pass_data); return tDQSTRING; default: put_string(); @@ -15044,12 +15043,12 @@ YY_RULE_SETUP YY_BREAK case 126: YY_RULE_SETUP -#line 572 "wpp/ppl.l" +#line 571 "ppl.l" add_string(pptext, ppleng); YY_BREAK case 127: YY_RULE_SETUP -#line 573 "wpp/ppl.l" +#line 572 "ppl.l" { add_string(pptext, ppleng); yy_pop_state(); @@ -15067,12 +15066,12 @@ YY_RULE_SETUP YY_BREAK case 128: YY_RULE_SETUP -#line 587 "wpp/ppl.l" +#line 586 "ppl.l" add_string(pptext, ppleng); YY_BREAK case 129: YY_RULE_SETUP -#line 588 "wpp/ppl.l" +#line 587 "ppl.l" { add_string(pptext, ppleng); yy_pop_state(); @@ -15082,7 +15081,7 @@ YY_RULE_SETUP YY_BREAK case 130: YY_RULE_SETUP -#line 594 "wpp/ppl.l" +#line 593 "ppl.l" { /* * This is tricky; we need to remove the line-continuation @@ -15110,12 +15109,12 @@ YY_RULE_SETUP YY_BREAK case 131: YY_RULE_SETUP -#line 618 "wpp/ppl.l" +#line 617 "ppl.l" add_string(pptext, ppleng); YY_BREAK case 132: YY_RULE_SETUP -#line 619 "wpp/ppl.l" +#line 618 "ppl.l" { newline(1); add_string(pptext, ppleng); @@ -15127,7 +15126,7 @@ YY_RULE_SETUP */ case 133: YY_RULE_SETUP -#line 628 "wpp/ppl.l" +#line 627 "ppl.l" { pp_entry_t *ppp; pp_incl_state.seen_junk++; @@ -15175,27 +15174,27 @@ YY_RULE_SETUP */ case 134: YY_RULE_SETUP -#line 673 "wpp/ppl.l" +#line 672 "ppl.l" pp_incl_state.seen_junk++; put_buffer(pptext, ppleng); YY_BREAK case 135: YY_RULE_SETUP -#line 674 "wpp/ppl.l" +#line 673 "ppl.l" put_buffer(pptext, ppleng); YY_BREAK case 136: YY_RULE_SETUP -#line 675 "wpp/ppl.l" +#line 674 "ppl.l" newline(1); YY_BREAK case 137: YY_RULE_SETUP -#line 676 "wpp/ppl.l" +#line 675 "ppl.l" newline(0); YY_BREAK case 138: YY_RULE_SETUP -#line 677 "wpp/ppl.l" +#line 676 "ppl.l" pp_incl_state.seen_junk++; put_buffer(pptext, ppleng); YY_BREAK /* @@ -15204,12 +15203,12 @@ pp_incl_state.seen_junk++; put_buffer(pptext, ppleng); */ case 139: YY_RULE_SETUP -#line 683 "wpp/ppl.l" +#line 682 "ppl.l" put_buffer(pptext, ppleng); YY_BREAK case 140: YY_RULE_SETUP -#line 685 "wpp/ppl.l" +#line 684 "ppl.l" { pplval.cptr=pp_xstrdup(pptext); yy_pop_state(); @@ -15218,12 +15217,12 @@ YY_RULE_SETUP YY_BREAK case 141: YY_RULE_SETUP -#line 691 "wpp/ppl.l" +#line 690 "ppl.l" ; YY_BREAK case 142: YY_RULE_SETUP -#line 693 "wpp/ppl.l" +#line 692 "ppl.l" { new_string(); add_string(pptext,ppleng);yy_push_state(pp_dqs); } @@ -15234,7 +15233,7 @@ YY_RULE_SETUP */ case 143: YY_RULE_SETUP -#line 701 "wpp/ppl.l" +#line 700 "ppl.l" pp_incl_state.seen_junk++; ppwarning("Unmatched text '%c' (0x%02x); please report\n", isprint(*pptext & 0xff) ? *pptext : ' ', *pptext); YY_BREAK case YY_STATE_EOF(INITIAL): @@ -15259,7 +15258,7 @@ case YY_STATE_EOF(pp_line): case YY_STATE_EOF(pp_defined): case YY_STATE_EOF(pp_ignore): case YY_STATE_EOF(RCINCL): -#line 703 "wpp/ppl.l" +#line 702 "ppl.l" { YY_BUFFER_STATE b = YY_CURRENT_BUFFER; bufferstackentry_t *bep = pop_buffer(); @@ -15284,10 +15283,10 @@ case YY_STATE_EOF(RCINCL): YY_BREAK case 144: YY_RULE_SETUP -#line 725 "wpp/ppl.l" +#line 724 "ppl.l" ECHO; YY_BREAK -#line 15291 "wpp/lex.yy.c" +#line 15290 "lex.yy.c" case YY_END_OF_BUFFER: { @@ -16172,7 +16171,7 @@ int main() return 0; } #endif -#line 725 "wpp/ppl.l" +#line 724 "ppl.l" /* ************************************************************************** @@ -16869,7 +16868,7 @@ static int is_c_h_include(char *fname, int quoted) int sl=strlen(fname); if (sl < 2 + 2 * quoted) return 0; if ((toupper(fname[sl-1-quoted])!='H') && (toupper(fname[sl-1-quoted])!='C')) return 0; - if (fname[sl-2-quoted]!='.') return 0; + if (fname[sl-2-quoted]!='.') return 0; return 1; } diff --git a/reactos/tools/wpp/ppl.l b/reactos/tools/wpp/ppl.l index 6a021ee5084..f4f8bc1bbe2 100644 --- a/reactos/tools/wpp/ppl.l +++ b/reactos/tools/wpp/ppl.l @@ -563,7 +563,6 @@ includelogicentry_t *pp_includelogiclist = NULL; pplval.cptr = get_string(); if (is_c_h_include(pplval.cptr, 1)) pass_data=0; else pass_data=1; -//fprintf(stderr, "%s pass_data %d\n", pplval.cptr, pass_data); return tDQSTRING; default: put_string(); @@ -1418,7 +1417,7 @@ static int is_c_h_include(char *fname, int quoted) int sl=strlen(fname); if (sl < 2 + 2 * quoted) return 0; if ((toupper(fname[sl-1-quoted])!='H') && (toupper(fname[sl-1-quoted])!='C')) return 0; - if (fname[sl-2-quoted]!='.') return 0; + if (fname[sl-2-quoted]!='.') return 0; return 1; } diff --git a/reactos/tools/wrc/newstruc.c b/reactos/tools/wrc/newstruc.c index 499844a9f05..c9c056082f1 100644 --- a/reactos/tools/wrc/newstruc.c +++ b/reactos/tools/wrc/newstruc.c @@ -679,8 +679,8 @@ cursor_group_t *new_cursor_group(raw_data_t *rd, int *memopt) * The "LIST" tag may occur several times and may encapsulate different * tags. The `steps' is the number of "icon" tags found (actually the * number of steps specified in the aniheader_t structure). The "seq "uence - * tag can be ommitted, in which case the sequence is equal to the sequence - * of "icon"s found in the file. Also "rate" may be ommitted, in which case + * tag can be omitted, in which case the sequence is equal to the sequence + * of "icon"s found in the file. Also "rate" may be omitted, in which case * the default from the aniheader_t structure is used. * * An animated cursor puts `.cur' formatted files into each "icon" tag, diff --git a/reactos/tools/wrc/parser.y b/reactos/tools/wrc/parser.y index f9d3da8b655..2e2c40c2a2b 100644 --- a/reactos/tools/wrc/parser.y +++ b/reactos/tools/wrc/parser.y @@ -352,7 +352,6 @@ static int rsrcid_to_token(int lookahead); %type dlginit %type optional_style_pair %type any_num -%type