aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascFindFile.cpp
blob: 1016dffdb4528386adbab1fc4c7f824e1b47c761 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*****************************************************************************/
/* CascFindFile.cpp                       Copyright (c) Ladislav Zezula 2014 */
/*---------------------------------------------------------------------------*/
/* System-dependent directory functions for CascLib                          */
/*---------------------------------------------------------------------------*/
/*   Date    Ver   Who  Comment                                              */
/* --------  ----  ---  -------                                              */
/* 10.05.14  1.00  Lad  The first version of CascFindFile.cpp                */
/*****************************************************************************/

#define __CASCLIB_SELF__
#include "CascLib.h"
#include "CascCommon.h"

//-----------------------------------------------------------------------------
// Local functions

static TCascSearch * IsValidSearchHandle(HANDLE hFind)
{
    TCascSearch * pSearch = (TCascSearch *)hFind;

    return (pSearch != NULL && pSearch->szClassName != NULL && !strcmp(pSearch->szClassName, "TCascSearch") && pSearch->szMask != NULL) ? pSearch : NULL;
}

static void FreeSearchHandle(TCascSearch * pSearch)
{
    // Only if the storage handle is valid
    assert(pSearch != NULL);

    // Close (dereference) the archive handle
    if(pSearch->hs != NULL)
    {
        // Give root handler chance to free their stuff
        RootHandler_EndSearch(pSearch->hs->pRootHandler, pSearch);

        // Dereference the storage handle
        CascCloseStorage((HANDLE)pSearch->hs);
        pSearch->hs = NULL;
    }

    // Free the file cache and frame array
    if(pSearch->szMask != NULL)
        CASC_FREE(pSearch->szMask);
    if(pSearch->szListFile != NULL)
        CASC_FREE(pSearch->szListFile);
//  if(pSearch->pStruct1C != NULL)
//      delete pSearch->pStruct1C;
    if(pSearch->pCache != NULL)
        ListFile_Free(pSearch->pCache);

    // Free the structure itself
    pSearch->szClassName = NULL;
    CASC_FREE(pSearch);
}

static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szListFile, const char * szMask)
{
    TCascSearch * pSearch;
    size_t cbToAllocate;

    // When using the MNDX info, do not allocate the extra bit array
    cbToAllocate = sizeof(TCascSearch) + ((hs->pEncodingMap->TableSize + 7) / 8);
    pSearch = (TCascSearch *)CASC_ALLOC(BYTE, cbToAllocate);
    if(pSearch != NULL)
    {
        // Initialize the structure
        memset(pSearch, 0, cbToAllocate);
        pSearch->szClassName = "TCascSearch";

        // Save the search handle
        pSearch->hs = hs;
        hs->dwRefCount++;

        // If the mask was not given, use default
        if(szMask == NULL)
            szMask = "*";

        // Save the other variables
        if(szListFile != NULL)
        {
            pSearch->szListFile = CascNewStr(szListFile, 0);
            if(pSearch->szListFile == NULL)
            {
                FreeSearchHandle(pSearch);
                return NULL;
            }
        }

        // Allocate the search mask
        pSearch->szMask = CascNewStr(szMask, 0);
        if(pSearch->szMask == NULL)
        {
            FreeSearchHandle(pSearch);
            return NULL;
        }
    }

    return pSearch;
}

// Perform searching using root-specific provider.
// The provider may need the listfile
static bool DoStorageSearch_RootFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
    PCASC_ENCODING_ENTRY pEncodingEntry;
    PCASC_INDEX_ENTRY pIndexEntry;
    QUERY_KEY EncodingKey;
    QUERY_KEY IndexKey;
    LPBYTE pbEncodingKey;
    DWORD EncodingIndex = 0;
    DWORD LocaleFlags = 0;
    DWORD FileSize = CASC_INVALID_SIZE;
    DWORD ByteIndex;
    DWORD BitMask;

    for(;;)
    {
        // Attempt to find (the next) file from the root entry
        pbEncodingKey = RootHandler_Search(pSearch->hs->pRootHandler, pSearch, &FileSize, &LocaleFlags);
        if(pbEncodingKey == NULL)
            return false;

        // Verify whether the encoding key exists in the encoding table
        EncodingKey.pbData = pbEncodingKey;
        EncodingKey.cbData = MD5_HASH_SIZE;
        pEncodingEntry = FindEncodingEntry(pSearch->hs, &EncodingKey, &EncodingIndex);
        if(pEncodingEntry != NULL)
        {
            // Mark the item as already found
            // Note: Duplicate items are allowed while we are searching using file names
            // Do not exclude items from search if they were found before
            ByteIndex = (DWORD)(EncodingIndex / 8);
            BitMask   = 1 << (EncodingIndex & 0x07);
            pSearch->BitArray[ByteIndex] |= BitMask;

            // Locate the index entry
            IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry);
            IndexKey.cbData = MD5_HASH_SIZE;
            pIndexEntry = FindIndexEntry(pSearch->hs, &IndexKey);
            if(pIndexEntry == NULL)
                continue;

            // If we retrieved the file size directly from the root provider, use it
            // Otherwise, we need to retrieve it from the encoding entry
            if(FileSize == CASC_INVALID_SIZE)
                FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);

            // Fill-in the found file
            strcpy(pFindData->szFileName, pSearch->szFileName);
            memcpy(pFindData->EncodingKey, pEncodingEntry->EncodingKey, MD5_HASH_SIZE);
            pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
            pFindData->dwLocaleFlags = LocaleFlags;
            pFindData->dwFileSize = FileSize;
            return true;
        }
    }
}

static bool DoStorageSearch_EncodingKey(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
    PCASC_ENCODING_ENTRY pEncodingEntry;
    PCASC_INDEX_ENTRY pIndexEntry;
    TCascStorage * hs = pSearch->hs;
    QUERY_KEY IndexKey;
    DWORD ByteIndex;
    DWORD BitMask;

    // Check for encoding keys that haven't been found yet
    while(pSearch->IndexLevel1 < hs->pEncodingMap->TableSize)
    {
        // Check if that entry has been reported before
        ByteIndex = (DWORD)(pSearch->IndexLevel1 / 8);
        BitMask = 1 << (pSearch->IndexLevel1 & 0x07);
        if((pSearch->BitArray[ByteIndex] & BitMask) == 0)
        {
            // Locate the index entry
            pEncodingEntry  = (PCASC_ENCODING_ENTRY)hs->pEncodingMap->HashTable[pSearch->IndexLevel1];
            if(pEncodingEntry != NULL)
            {
                IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry);
                IndexKey.cbData = MD5_HASH_SIZE;
                pIndexEntry = FindIndexEntry(pSearch->hs, &IndexKey);
                if(pIndexEntry != NULL)
                {
                    // Fill-in the found file
                    memcpy(pFindData->EncodingKey, pEncodingEntry->EncodingKey, MD5_HASH_SIZE);
                    pFindData->szFileName[0] = 0;
                    pFindData->szPlainName = NULL;
                    pFindData->dwLocaleFlags = CASC_LOCALE_NONE;
                    pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);

                    // Mark the entry as already-found
                    pSearch->BitArray[ByteIndex] |= BitMask;
                    return true;
                }
            }
        }

        // Go to the next encoding entry
        pSearch->IndexLevel1++;
    }

    // Nameless search ended
    return false;
}

static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
    // State 0: No search done yet
    if(pSearch->dwState == 0)
    {
        // Does the search specify listfile?
        if(pSearch->szListFile != NULL)
            pSearch->pCache = ListFile_OpenExternal(pSearch->szListFile);

        // Move the search phase to the listfile searching
        pSearch->IndexLevel1 = 0;
        pSearch->dwState++;
    }

    // State 1: Searching the list file
    if(pSearch->dwState == 1)
    {
        if(DoStorageSearch_RootFile(pSearch, pFindData))
            return true;

        // Move to the nameless search state
        pSearch->IndexLevel1 = 0;
        pSearch->dwState++;
    }

    // State 2: Searching the remaining entries
    if(pSearch->dwState == 2)
    {
        if(DoStorageSearch_EncodingKey(pSearch, pFindData))
            return true;

        // Move to the final search state
        pSearch->dwState++;
    }

    return false;
}

//-----------------------------------------------------------------------------
// Public functions

HANDLE WINAPI CascFindFirstFile(
    HANDLE hStorage,
    const char * szMask,
    PCASC_FIND_DATA pFindData,
    const TCHAR * szListFile)
{
    TCascStorage * hs;
    TCascSearch * pSearch = NULL;
    int nError = ERROR_SUCCESS;

    // Check parameters
    if((hs = IsValidStorageHandle(hStorage)) == NULL)
        nError = ERROR_INVALID_HANDLE;
    if(szMask == NULL || pFindData == NULL)
        nError = ERROR_INVALID_PARAMETER;

    // Init the search structure and search handle
    if(nError == ERROR_SUCCESS)
    {
        // Clear the entire search structure
        memset(pFindData, 0, sizeof(CASC_FIND_DATA));

        // Allocate the search handle
        pSearch = AllocateSearchHandle(hs, szListFile, szMask);
        if(pSearch == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Perform search
    if(nError == ERROR_SUCCESS)
    {
        if(!DoStorageSearch(pSearch, pFindData))
            nError = ERROR_NO_MORE_FILES;
    }

    if(nError != ERROR_SUCCESS)
    {
        if(pSearch != NULL)
            FreeSearchHandle(pSearch);
        pSearch = NULL;
    }

    return (HANDLE)pSearch;
}

bool WINAPI CascFindNextFile(
    HANDLE hFind,
    PCASC_FIND_DATA pFindData)
{
    TCascSearch * pSearch;

    pSearch = IsValidSearchHandle(hFind);
    if(pSearch == NULL || pFindData == NULL)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return false;
    }

    // Perform search
    return DoStorageSearch(pSearch, pFindData);
}

bool WINAPI CascFindClose(HANDLE hFind)
{
    TCascSearch * pSearch;

    pSearch = IsValidSearchHandle(hFind);
    if(pSearch == NULL)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return false;
    }

    FreeSearchHandle(pSearch);
    return true;
}