asar coverage - build #273


src/asar/
File: src/asar/platform/file-helpers.cpp
Date: 2025-03-03 20:49:12
Lines:
60/67
89.6%
Functions:
3/4
75.0%
Branches:
85/120
70.8%

Line Branch Exec Source
1 #include "platform/file-helpers.h"
2
3
4 // This function is based in part on nall, which is under the following licence.
5 // This modified version is licenced under the LGPL version 3 or later. See the LICENSE file
6 // for details.
7 //
8 // Copyright (c) 2006-2015 byuu
9 //
10 // Permission to use, copy, modify, and/or distribute this software for
11 // any purpose with or without fee is hereby granted, provided that the
12 // above copyright notice and this permission notice appear in all
13 // copies.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
16 // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
17 // WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
18 // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
19 // DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
20 // PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 // TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 // PERFORMANCE OF THIS SOFTWARE.
23 7845 string dir(char const *name)
24 {
25
4/4
✓ Branch 0 taken 68519 times.
✓ Branch 1 taken 2859 times.
✓ Branch 2 taken 39556 times.
✓ Branch 3 taken 1 times.
108076 for (signed i = (int)strlen(name); i >= 0; i--)
26 {
27
5/6
✓ Branch 0 taken 63533 times.
✓ Branch 1 taken 4986 times.
✓ Branch 2 taken 36698 times.
✓ Branch 3 taken 66391 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 36698 times.
108075 if (name[i] == '/' || name[i] == '\\')
28 {
29 7844 return string(name, i+1);
30 }
31 }
32 1 return "";
33 }
34
35 3886 string create_combined_path(const char* path1, const char* path2)
36 {
37
2/3
✓ Branch 0 taken 2455 times.
✓ Branch 1 taken 1431 times.
✗ Branch 2 not taken.
3886 string combined = path1;
38
39 3886 int length = combined.length();
40
41
9/12
✓ Branch 0 taken 3886 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2455 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1431 times.
✓ Branch 5 taken 2455 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2455 times.
✓ Branch 8 taken 33 times.
✓ Branch 9 taken 1398 times.
✓ Branch 10 taken 33 times.
✓ Branch 11 taken 1398 times.
3886 if (combined.length() > 0 && path1[length - 1] != '\\' && path1[length - 1] != '/')
42 {
43
2/4
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 combined += get_native_path_separator();
44 }
45
46
1/2
✓ Branch 0 taken 3886 times.
✗ Branch 1 not taken.
3886 combined += path2;
47
48
2/3
✓ Branch 0 taken 2455 times.
✓ Branch 1 taken 1431 times.
✗ Branch 2 not taken.
7772 return normalize_path(combined);
49 3886 }
50
51
52 // RPG Hacker: This function attempts to normalize a path.
53 // This means that it attempts to put it into a format
54 // where a simple string compare between two paths
55 // referring to the same file will hopefully always
56 // succeed.
57 // There are a few known problems and limitations:
58 // - Relative paths are not recommended, as they can't be
59 // resolved reliably. For example, in this path
60 // ../patch/main.asm
61 // we can't collapse the .. because we don't know the
62 // parent directory. A comparison will also fail when
63 // one piece of code uses a relative path while another
64 // piece of code uses an absolute path. For those
65 // reasons, it's recommended to always use absolute paths
66 // everywhere if possible.
67 // - Windows network paths like
68 // \\MY_PC\patch\main.asm
69 // Will likely break with this, since the function converts
70 // all back slashes to forward slashes. I think Windows
71 // actually requires back slashes for network paths.
72 // - Currently, the code doesn't look at drive letters
73 // and just treats them as folders, so a path like
74 // C:/../patch/main.asm
75 // would result in
76 // patch/main.asm
77 // (However, a path like that is invalid, anyways, so
78 // this hopefully doesn't matter).
79 9989 string normalize_path(const char* path)
80 {
81
1/2
✓ Branch 0 taken 9989 times.
✗ Branch 1 not taken.
9989 string normalized = path;
82
83
84 // RPG Hacker: Replace all \ in path by /
85 // (Windows should work with / in paths just fine).
86 // Note: will likely break network paths on Windows,
87 // which still expect a prefix of \\, but does anyone
88 // seriously even use them with Asar?
89
2/2
✓ Branch 0 taken 307265 times.
✓ Branch 1 taken 9989 times.
317254 for (int i = 0; i < normalized.length(); ++i)
90 {
91
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 175538 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131727 times.
307265 if (normalized[i] == '\\')
92 {
93 normalized.temp_raw()[i]= '/';
94 }
95 }
96
97 // RPG Hacker: Collapse path by removing all unnecessary
98 // . or .. path components.
99 // As a little hack, just overwrite all the stuff we're
100 // going to remove with 0x01 and remove it later. Probably
101 // easier than to always shorten the path immediately.
102 // Also using \x01 instead of \0 so that the path is still
103 // easily readable in a debugger (since it normally just
104 // assumes a string to end at \0). I don't think \x01 can
105 // be used in valid paths, so this should be okay.
106 9989 char* previous_dir_start_pos = nullptr;
107 9989 char* current_dir_start_pos = normalized.temp_raw();
108
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 8585 times.
✓ Branch 2 taken 4096 times.
12681 while (*current_dir_start_pos == '/')
109 {
110 2692 ++current_dir_start_pos;
111 }
112 9989 char* current_dir_end_pos = current_dir_start_pos;
113
114
115
3/3
✓ Branch 0 taken 25864 times.
✓ Branch 1 taken 25665 times.
✓ Branch 2 taken 4096 times.
55625 while (*current_dir_start_pos != '\0')
116 {
117
6/6
✓ Branch 0 taken 161469 times.
✓ Branch 1 taken 138170 times.
✓ Branch 2 taken 171253 times.
✓ Branch 3 taken 5893 times.
✓ Branch 4 taken 114104 times.
✓ Branch 5 taken 4095 times.
315316 while (*current_dir_end_pos != '/' && *current_dir_end_pos != '\0')
118 {
119 269680 ++current_dir_end_pos;
120 }
121
122 45636 size_t length = (size_t)(current_dir_end_pos - current_dir_start_pos);
123
6/6
✓ Branch 0 taken 44881 times.
✓ Branch 1 taken 755 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 44845 times.
✓ Branch 4 taken 36 times.
✓ Branch 5 taken 18990 times.
45636 if (length > 0 && strncmp(current_dir_start_pos, ".", length) == 0)
124 {
125 // Found a . path component - remove it.
126
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 72 times.
144 while (current_dir_start_pos != current_dir_end_pos)
127 {
128 72 *current_dir_start_pos = '\x01';
129 72 ++current_dir_start_pos;
130 }
131
132
2/3
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
72 if (*current_dir_start_pos == '/')
133 {
134 72 *current_dir_start_pos = '\x01';
135 72 ++current_dir_end_pos;
136 }
137 }
138
6/6
✓ Branch 0 taken 44809 times.
✓ Branch 1 taken 755 times.
✓ Branch 2 taken 48 times.
✓ Branch 3 taken 44761 times.
✓ Branch 4 taken 1834 times.
✓ Branch 5 taken 17156 times.
45564 else if (length > 0 && strncmp(current_dir_start_pos, "..", length) == 0)
139 {
140 // Found a .. path component - if we have any known
141 // folder before it, remove them both.
142
2/2
✓ Branch 0 taken 1121 times.
✓ Branch 1 taken 761 times.
1882 if (previous_dir_start_pos != nullptr)
143 {
144 // previous_dir_start_pos and current_dir_start_pos are immediately
145 // set to something else shortly after, so it should be
146 // okay to move them directly here and not use a
147 // temporary variable.
148
4/6
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 4837 times.
✓ Branch 2 taken 1265 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4801 times.
✗ Branch 5 not taken.
6102 while (*previous_dir_start_pos != '/' && *previous_dir_start_pos != '\0')
149 {
150 4981 *previous_dir_start_pos = '\x01';
151 4981 ++previous_dir_start_pos;
152 }
153
154
2/3
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 1085 times.
✗ Branch 2 not taken.
1121 if (*previous_dir_start_pos == '/')
155 {
156 1121 *previous_dir_start_pos = '\x01';
157 }
158
159
2/2
✓ Branch 0 taken 2242 times.
✓ Branch 1 taken 1121 times.
3363 while (current_dir_start_pos != current_dir_end_pos)
160 {
161 2242 *current_dir_start_pos = '\x01';
162 2242 ++current_dir_start_pos;
163 }
164
165
3/3
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 1085 times.
✓ Branch 2 taken 3 times.
1121 if (*current_dir_start_pos == '/')
166 {
167 1115 *current_dir_start_pos = '\x01';
168 1115 ++current_dir_end_pos;
169 }
170 }
171
172 1882 previous_dir_start_pos = nullptr;
173 }
174 else
175 {
176
3/3
✓ Branch 0 taken 19893 times.
✓ Branch 1 taken 19700 times.
✓ Branch 2 taken 4089 times.
43682 if (*current_dir_end_pos == '/')
177 {
178 33706 ++current_dir_end_pos;
179 }
180
181
2/2
✓ Branch 0 taken 42927 times.
✓ Branch 1 taken 755 times.
43682 if (length > 0)
182 {
183 42927 previous_dir_start_pos = current_dir_start_pos;
184 }
185 }
186
187 45636 current_dir_start_pos = current_dir_end_pos;
188 }
189
190
191 // Now construct our new string by copying everything but the \x01 into it.
192 9989 string copy;
193
194
2/2
✓ Branch 0 taken 307265 times.
✓ Branch 1 taken 9989 times.
317254 for (int i = 0; i < normalized.length(); ++i)
195 {
196
4/4
✓ Branch 0 taken 175145 times.
✓ Branch 1 taken 393 times.
✓ Branch 2 taken 122517 times.
✓ Branch 3 taken 9210 times.
307265 if (normalized[i] != '\x01')
197 {
198
2/4
✓ Branch 0 taken 175145 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 122517 times.
✗ Branch 3 not taken.
297662 copy += normalized[i];
199 }
200 }
201
202
1/2
✓ Branch 0 taken 9989 times.
✗ Branch 1 not taken.
9989 normalized = copy;
203
204
205 // RPG Hacker: On Windows, file paths are case-insensitive.
206 // It isn't really good style to mix different cases, especially
207 // when referring to the same file, but it's theoretically possible
208 // to do so, so we need to handle it and not have Asar fail in this case.
209 // The easiest way to handle it is to convert the entire path to lowercase.
210 #if defined(windows)
211
1/2
✓ Branch 0 taken 5893 times.
✗ Branch 1 not taken.
5893 normalized = lower(normalized);
212 #endif
213
214
215 // TODO: Maybe resolve symbolic links here?
216 // Not sure if it's a good idea and if it's needed in the first place.
217 // In theory, it could lead to some problems with files that exist
218 // only in memory.
219 19978 return normalized;
220 9989 }
221
222 string get_base_name(char const *name)
223 {
224 for(int i = (int)strlen(name); i >= 0; --i)
225 {
226 if(name[i] == '/' || name[i] == '\\')
227 {
228 //file has no extension
229 break;
230 }
231 if(name[i] == '.')
232 {
233 return string(name, i);
234 }
235 }
236 return string(name);
237 }
238