dbghelp: Improve the symbol loader.

When looking for a PDB file, no longer use SymFindFileInPath as it
doesn't actually check the signatures, but use an internal function
instead.
diff --git a/dlls/dbghelp/path.c b/dlls/dbghelp/path.c
index 7057e26..8f06062 100644
--- a/dlls/dbghelp/path.c
+++ b/dlls/dbghelp/path.c
@@ -1,7 +1,7 @@
 /*
  * File path.c - managing path in debugging environments
  *
- * Copyright (C) 2004, Eric Pouech
+ * Copyright (C) 2004,2008, Eric Pouech
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -555,3 +555,206 @@
         WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
     return ret;
 }
+
+struct module_find
+{
+    enum module_type            kind;
+    /* pe:  dw1         DWORD:timestamp
+     *      dw2         size of image (from PE header)
+     * pdb: guid        PDB guid (if DS PDB file)
+     *      or dw1      PDB timestamp (if JG PDB file)
+     *      dw2         PDB age
+     * elf: dw1         DWORD:CRC 32 of ELF image (Wine only)
+     */
+    const GUID*                 guid;
+    DWORD                       dw1;
+    DWORD                       dw2;
+    WCHAR                       filename[MAX_PATH];
+    unsigned                    matched;
+};
+
+/* checks that buffer (as found by matching the name) matches the info
+ * (information is based on file type)
+ * returns TRUE when file is found, FALSE to continue searching
+ * (NB this is the opposite convention of SymFindFileInPathProc)
+ */
+static BOOL CALLBACK module_find_cb(PCWSTR buffer, PVOID user)
+{
+    struct module_find* mf = (struct module_find*)user;
+    DWORD               size, checksum;
+    unsigned            matched = 0;
+
+    /* the matching weights:
+     * +1 if a file with same name is found and is a decent file of expected type
+     * +1 if first parameter and second parameter match
+     */
+
+    /* FIXME: should check that id/two match the file pointed
+     * by buffer
+     */
+    switch (mf->kind)
+    {
+    case DMT_PE:
+        {
+            HANDLE  hFile, hMap;
+            void*   mapping;
+            DWORD   timestamp;
+
+            timestamp = ~mf->dw1;
+            size = ~mf->dw2;
+            hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL,
+                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+            if (hFile == INVALID_HANDLE_VALUE) return FALSE;
+            if ((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
+            {
+                if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
+                {
+                    IMAGE_NT_HEADERS*   nth = RtlImageNtHeader(mapping);
+
+                    matched++;
+                    timestamp = nth->FileHeader.TimeDateStamp;
+                    size = nth->OptionalHeader.SizeOfImage;
+                    UnmapViewOfFile(mapping);
+                }
+                CloseHandle(hMap);
+            }
+            CloseHandle(hFile);
+            if (timestamp != mf->dw1)
+                WARN("Found %s, but wrong timestamp\n", debugstr_w(buffer));
+            if (size != mf->dw2)
+                WARN("Found %s, but wrong size\n", debugstr_w(buffer));
+            if (timestamp == mf->dw1 && size == mf->dw2) matched++;
+        }
+        break;
+    case DMT_ELF:
+        if (elf_fetch_file_info(buffer, 0, &size, &checksum))
+        {
+            matched++;
+            if (checksum == mf->dw1) matched++;
+            else
+                WARN("Found %s, but wrong checksums: %08x %08x\n",
+                     debugstr_w(buffer), checksum, mf->dw1);
+        }
+        else
+        {
+            WARN("Couldn't read %s\n", debugstr_w(buffer));
+            return FALSE;
+        }
+        break;
+    case DMT_PDB:
+        {
+            struct pdb_lookup   pdb_lookup;
+            char                fn[MAX_PATH];
+
+            WideCharToMultiByte(CP_ACP, 0, buffer, -1, fn, MAX_PATH, NULL, NULL);
+            pdb_lookup.filename = fn;
+
+            if (!pdb_fetch_file_info(&pdb_lookup)) return FALSE;
+            matched++;
+            switch (pdb_lookup.kind)
+            {
+            case PDB_JG:
+                if (mf->guid)
+                {
+                    WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer));
+                }
+                else if (pdb_lookup.u.jg.timestamp == mf->dw1)
+                    matched++;
+                else
+                    WARN("Found %s, but wrong signature: %08x %08x\n",
+                         debugstr_w(buffer), pdb_lookup.u.jg.timestamp, mf->dw1);
+                break;
+            case PDB_DS:
+                if (!mf->guid)
+                {
+                    WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer));
+                }
+                else if (!memcmp(&pdb_lookup.u.ds.guid, mf->guid, sizeof(GUID)))
+                    matched++;
+                else
+                    WARN("Found %s, but wrong GUID: %s %s\n",
+                         debugstr_w(buffer), debugstr_guid(&pdb_lookup.u.ds.guid),
+                         debugstr_guid(mf->guid));
+                break;
+            }
+            if (pdb_lookup.age != mf->dw2)
+            {
+                matched--;
+                WARN("Found %s, but wrong age: %08x %08x\n",
+                     debugstr_w(buffer), pdb_lookup.age, mf->dw2);
+            }
+        }
+        break;
+    default:
+        FIXME("What the heck??\n");
+        return FALSE;
+    }
+    if (matched > mf->matched)
+    {
+        strcpyW(mf->filename, buffer);
+        mf->matched = matched;
+    }
+    /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
+     * convention to stop/continue enumeration. sigh.
+     */
+    return mf->matched == 2;
+}
+
+BOOL path_find_symbol_file(const struct process* pcs, PCSTR full_path,
+                           const GUID* guid, DWORD dw1, DWORD dw2, PSTR buffer)
+{
+    struct module_find  mf;
+    WCHAR               full_pathW[MAX_PATH];
+    WCHAR               tmp[MAX_PATH];
+    WCHAR*              ptr;
+    const WCHAR*        filename;
+    WCHAR*              searchPath = pcs->search_path;
+
+    TRACE("(pcs = %p, full_path = %s, guid = %s, dw1 = 0x%08x, dw2 = 0x%08x, buffer = %p)\n",
+          pcs, debugstr_a(full_path), debugstr_guid(guid), dw1, dw2, buffer);
+
+    mf.guid = guid;
+    mf.dw1 = dw1;
+    mf.dw2 = dw2;
+    mf.matched = 0;
+
+    MultiByteToWideChar(CP_ACP, 0, full_path, -1, full_pathW, MAX_PATH);
+    filename = file_nameW(full_pathW);
+    mf.kind = module_get_type_by_name(filename);
+
+    /* first check full path to file */
+    if (module_find_cb(full_pathW, &mf))
+    {
+        WideCharToMultiByte(CP_ACP, 0, full_pathW, -1, buffer, MAX_PATH, NULL, NULL);
+        return TRUE;
+    }
+
+    while (searchPath)
+    {
+        ptr = strchrW(searchPath, ';');
+        if (ptr)
+        {
+            memcpy(tmp, searchPath, (ptr - searchPath) * sizeof(WCHAR));
+            tmp[ptr - searchPath] = '\0';
+            searchPath = ptr + 1;
+        }
+        else
+        {
+            strcpyW(tmp, searchPath);
+            searchPath = NULL;
+        }
+        if (do_searchW(filename, tmp, FALSE, module_find_cb, &mf))
+        {
+            /* return first fully matched file */
+            WideCharToMultiByte(CP_ACP, 0, tmp, -1, buffer, MAX_PATH, NULL, NULL);
+            return TRUE;
+        }
+    }
+    /* if no fully matching file is found, return the best matching file if any */
+    if ((dbghelp_options & SYMOPT_LOAD_ANYTHING) && mf.matched)
+    {
+        WideCharToMultiByte(CP_ACP, 0, mf.filename, -1, buffer, MAX_PATH, NULL, NULL);
+        return TRUE;
+    }
+    return FALSE;
+}