git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / lists / file_list.c
1 /* Copyright  (C) 2010-2020 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (file_list.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <retro_common.h>
28 #include <lists/file_list.h>
29 #include <string/stdstring.h>
30 #include <compat/strcasestr.h>
31
32 static bool file_list_deinitialize_internal(file_list_t *list)
33 {
34    size_t i;
35    for (i = 0; i < list->size; i++)
36    {
37       file_list_free_userdata(list, i);
38       file_list_free_actiondata(list, i);
39
40       if (list->list[i].path)
41          free(list->list[i].path);
42       list->list[i].path = NULL;
43
44       if (list->list[i].label)
45          free(list->list[i].label);
46       list->list[i].label = NULL;
47
48       if (list->list[i].alt)
49          free(list->list[i].alt);
50       list->list[i].alt = NULL;
51    }
52    if (list->list)
53       free(list->list);
54    list->list = NULL;
55    return true;
56 }
57
58 bool file_list_reserve(file_list_t *list, size_t nitems)
59 {
60    const size_t item_size = sizeof(struct item_file);
61    struct item_file *new_data;
62
63    if (nitems < list->capacity || nitems > (size_t)-1/item_size)
64       return false;
65
66    if (!(new_data = (struct item_file*)realloc(list->list, nitems * item_size)))
67       return false;
68
69    memset(&new_data[list->capacity], 0, item_size * (nitems - list->capacity));
70
71    list->list     = new_data;
72    list->capacity = nitems;
73
74    return true;
75 }
76
77 bool file_list_insert(file_list_t *list,
78       const char *path, const char *label,
79       unsigned type, size_t directory_ptr,
80       size_t entry_idx,
81       size_t idx)
82 {
83    int i;
84
85    /* Expand file list if needed */
86    if (list->size >= list->capacity)
87       if (!file_list_reserve(list, list->capacity * 2 + 1))
88          return false;
89
90    for (i = (unsigned)list->size; i > (int)idx; i--)
91    {
92       struct item_file *copy = (struct item_file*)
93          malloc(sizeof(struct item_file));
94
95       copy->path             = NULL;
96       copy->label            = NULL;
97       copy->alt              = NULL;
98       copy->type             = 0;
99       copy->directory_ptr    = 0;
100       copy->entry_idx        = 0;
101       copy->userdata         = NULL;
102       copy->actiondata       = NULL;
103
104       memcpy(copy, &list->list[i-1], sizeof(struct item_file));
105
106       memcpy(&list->list[i-1], &list->list[i], sizeof(struct item_file));
107       memcpy(&list->list[i],             copy, sizeof(struct item_file));
108
109       free(copy);
110    }
111
112    list->list[idx].path          = NULL;
113    list->list[idx].label         = NULL;
114    list->list[idx].alt           = NULL;
115    list->list[idx].type          = type;
116    list->list[idx].directory_ptr = directory_ptr;
117    list->list[idx].entry_idx     = entry_idx;
118    list->list[idx].userdata      = NULL;
119    list->list[idx].actiondata    = NULL;
120
121    if (label)
122       list->list[idx].label      = strdup(label);
123    if (path)
124       list->list[idx].path       = strdup(path);
125
126    list->size++;
127
128    return true;
129 }
130
131 bool file_list_append(file_list_t *list,
132       const char *path, const char *label,
133       unsigned type, size_t directory_ptr,
134       size_t entry_idx)
135 {
136    unsigned idx = (unsigned)list->size;
137    /* Expand file list if needed */
138    if (idx >= list->capacity)
139       if (!file_list_reserve(list, list->capacity * 2 + 1))
140          return false;
141
142    list->list[idx].path          = NULL;
143    list->list[idx].label         = NULL;
144    list->list[idx].alt           = NULL;
145    list->list[idx].type          = type;
146    list->list[idx].directory_ptr = directory_ptr;
147    list->list[idx].entry_idx     = entry_idx;
148    list->list[idx].userdata      = NULL;
149    list->list[idx].actiondata    = NULL;
150
151    if (label)
152       list->list[idx].label      = strdup(label);
153    if (path)
154       list->list[idx].path       = strdup(path);
155
156    list->size++;
157
158    return true;
159 }
160
161 void file_list_pop(file_list_t *list, size_t *directory_ptr)
162 {
163    if (!list)
164       return;
165
166    if (list->size != 0)
167    {
168       --list->size;
169       if (list->list[list->size].path)
170          free(list->list[list->size].path);
171       list->list[list->size].path = NULL;
172
173       if (list->list[list->size].label)
174          free(list->list[list->size].label);
175       list->list[list->size].label = NULL;
176    }
177
178    if (directory_ptr)
179       *directory_ptr = list->list[list->size].directory_ptr;
180 }
181
182 void file_list_free(file_list_t *list)
183 {
184    if (!list)
185       return;
186    file_list_deinitialize_internal(list);
187    free(list);
188 }
189
190 bool file_list_deinitialize(file_list_t *list)
191 {
192    if (!list)
193       return false;
194    if (!file_list_deinitialize_internal(list))
195       return false;
196    list->capacity = 0;
197    list->size     = 0;
198    return true;
199 }
200
201 void file_list_clear(file_list_t *list)
202 {
203    size_t i;
204
205    if (!list)
206       return;
207
208    for (i = 0; i < list->size; i++)
209    {
210       if (list->list[i].path)
211          free(list->list[i].path);
212       list->list[i].path = NULL;
213
214       if (list->list[i].label)
215          free(list->list[i].label);
216       list->list[i].label = NULL;
217
218       if (list->list[i].alt)
219          free(list->list[i].alt);
220       list->list[i].alt = NULL;
221    }
222
223    list->size = 0;
224 }
225
226 static void file_list_get_label_at_offset(const file_list_t *list, size_t idx,
227       const char **label)
228 {
229    if (!label || !list)
230       return;
231
232    *label = list->list[idx].path;
233    if (list->list[idx].label)
234       *label = list->list[idx].label;
235 }
236
237 void file_list_set_alt_at_offset(file_list_t *list, size_t idx,
238       const char *alt)
239 {
240    if (!list || !alt)
241       return;
242
243    if (list->list[idx].alt)
244       free(list->list[idx].alt);
245    list->list[idx].alt      = NULL;
246
247    if (alt)
248       list->list[idx].alt   = strdup(alt);
249 }
250
251 static int file_list_alt_cmp(const void *a_, const void *b_)
252 {
253    const struct item_file *a = (const struct item_file*)a_;
254    const struct item_file *b = (const struct item_file*)b_;
255    const char *cmp_a         = a->alt ? a->alt : a->path;
256    const char *cmp_b         = b->alt ? b->alt : b->path;
257    return strcasecmp(cmp_a, cmp_b);
258 }
259
260 static int file_list_type_cmp(const void *a_, const void *b_)
261 {
262    const struct item_file *a = (const struct item_file*)a_;
263    const struct item_file *b = (const struct item_file*)b_;
264    if (a->type < b->type)
265       return -1;
266    if (a->type == b->type)
267       return 0;
268
269    return 1;
270 }
271
272 void file_list_sort_on_alt(file_list_t *list)
273 {
274    qsort(list->list, list->size, sizeof(list->list[0]), file_list_alt_cmp);
275 }
276
277 void file_list_sort_on_type(file_list_t *list)
278 {
279    qsort(list->list, list->size, sizeof(list->list[0]), file_list_type_cmp);
280 }
281
282 void *file_list_get_userdata_at_offset(const file_list_t *list, size_t idx)
283 {
284    if (!list)
285       return NULL;
286    return list->list[idx].userdata;
287 }
288
289 void *file_list_get_actiondata_at_offset(const file_list_t *list, size_t idx)
290 {
291    if (!list)
292       return NULL;
293    return list->list[idx].actiondata;
294 }
295
296 void file_list_free_actiondata(const file_list_t *list, size_t idx)
297 {
298    if (!list)
299       return;
300    if (list->list[idx].actiondata)
301        free(list->list[idx].actiondata);
302    list->list[idx].actiondata = NULL;
303 }
304
305 void file_list_free_userdata(const file_list_t *list, size_t idx)
306 {
307    if (!list)
308       return;
309    if (list->list[idx].userdata)
310        free(list->list[idx].userdata);
311    list->list[idx].userdata = NULL;
312 }
313
314 bool file_list_search(const file_list_t *list, const char *needle, size_t *idx)
315 {
316    size_t i;
317    bool ret        = false;
318
319    if (!list)
320       return false;
321
322    for (i = 0; i < list->size; i++)
323    {
324       const char *str = NULL;
325       const char *alt = list->list[i].alt 
326             ? list->list[i].alt 
327             : list->list[i].path;
328
329       if (!alt)
330       {
331          file_list_get_label_at_offset(list, i, &alt);
332          if (!alt)
333             continue;
334       }
335
336       if ((str = (const char *)strcasestr(alt, needle)) == alt)
337       {
338          /* Found match with first chars, best possible match. */
339          *idx = i;
340          ret  = true;
341          break;
342       }
343       else if (str && !ret)
344       {
345          /* Found mid-string match, but try to find a match with
346           * first characters before we settle. */
347          *idx = i;
348          ret  = true;
349       }
350    }
351
352    return ret;
353 }