git subrepo pull --force deps/lightrec
[pcsx_rearmed.git] / deps / libretro-common / lists / file_list.c
CommitLineData
3719602c
PC
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
32static 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
58bool 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
77bool 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
131bool 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
161void 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
182void file_list_free(file_list_t *list)
183{
184 if (!list)
185 return;
186 file_list_deinitialize_internal(list);
187 free(list);
188}
189
190bool 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
201void 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
226static 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
237void 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
251static 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
260static 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
272void 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
277void 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
282void *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
289void *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
296void 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
305void 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
314bool 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}