asar coverage - build #234


src/asar/
File: src/asar/platform/file-helpers.cpp
Date: 2025-02-20 01:21:10
Lines:
61/67
91.0%
Functions:
3/4
75.0%
Branches:
60/82
73.2%

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 51817 string dir(char const *name)
24 {
25
2/2
✓ Branch 0 taken 703440 times.
✓ Branch 1 taken 1 times.
703441 for (signed i = (int)strlen(name); i >= 0; i--)
26 {
27
3/4
✓ Branch 0 taken 651624 times.
✓ Branch 1 taken 51816 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 651624 times.
703440 if (name[i] == '/' || name[i] == '\\')
28 {
29 51816 return string(name, i+1);
30 }
31 }
32 1 return "";
33 }
34
35 25765 string create_combined_path(const char* path1, const char* path2)
36 {
37
1/2
✓ Branch 0 taken 10256 times.
✗ Branch 1 not taken.
10256 string combined = path1;
38
39 10256 int length = combined.length();
40
41
6/8
✓ Branch 0 taken 25765 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25765 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 109 times.
✓ Branch 5 taken 25656 times.
✓ Branch 6 taken 71 times.
✓ Branch 7 taken 10185 times.
25765 if (combined.length() > 0 && path1[length - 1] != '\\' && path1[length - 1] != '/')
42 {
43
2/4
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 109 times.
✗ Branch 3 not taken.
109 combined += get_native_path_separator();
44 }
45
46
1/2
✓ Branch 0 taken 25765 times.
✗ Branch 1 not taken.
25765 combined += path2;
47
48
1/2
✓ Branch 0 taken 25765 times.
✗ Branch 1 not taken.
51530 return normalize_path(combined);
49 25765 }
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 58061 string normalize_path(const char* path)
80 {
81
1/2
✓ Branch 0 taken 23900 times.
✗ Branch 1 not taken.
23900 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 1681698 times.
✓ Branch 1 taken 58061 times.
1739759 for (int i = 0; i < normalized.length(); ++i)
90 {
91
2/2
✓ Branch 0 taken 5276 times.
✓ Branch 1 taken 1676422 times.
1681698 if (normalized[i] == '\\')
92 {
93 5276 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 23900 char* previous_dir_start_pos = nullptr;
107 23900 char* current_dir_start_pos = normalized.temp_raw();
108
2/2
✓ Branch 0 taken 13637 times.
✓ Branch 1 taken 58061 times.
71698 while (*current_dir_start_pos == '/')
109 {
110 13637 ++current_dir_start_pos;
111 }
112 23900 char* current_dir_end_pos = current_dir_start_pos;
113
114
115
2/2
✓ Branch 0 taken 246365 times.
✓ Branch 1 taken 58061 times.
304426 while (*current_dir_start_pos != '\0')
116 {
117
4/4
✓ Branch 0 taken 1540319 times.
✓ Branch 1 taken 188305 times.
✓ Branch 2 taken 1482259 times.
✓ Branch 3 taken 58060 times.
1728624 while (*current_dir_end_pos != '/' && *current_dir_end_pos != '\0')
118 {
119 1482259 ++current_dir_end_pos;
120 }
121
122 246365 size_t length = (size_t)(current_dir_end_pos - current_dir_start_pos);
123
4/4
✓ Branch 0 taken 243862 times.
✓ Branch 1 taken 2503 times.
✓ Branch 2 taken 216 times.
✓ Branch 3 taken 243646 times.
246365 if (length > 0 && strncmp(current_dir_start_pos, ".", length) == 0)
124 {
125 // Found a . path component - remove it.
126
2/2
✓ Branch 0 taken 216 times.
✓ Branch 1 taken 216 times.
432 while (current_dir_start_pos != current_dir_end_pos)
127 {
128 216 *current_dir_start_pos = '\x01';
129 216 ++current_dir_start_pos;
130 }
131
132
1/2
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
216 if (*current_dir_start_pos == '/')
133 {
134 216 *current_dir_start_pos = '\x01';
135 216 ++current_dir_end_pos;
136 }
137 }
138
4/4
✓ Branch 0 taken 100219 times.
✓ Branch 1 taken 145903 times.
✓ Branch 2 taken 5995 times.
✓ Branch 3 taken 94080 times.
246122 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 3618 times.
✓ Branch 1 taken 2521 times.
6139 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
3/4
✓ Branch 0 taken 28429 times.
✓ Branch 1 taken 3618 times.
✓ Branch 2 taken 28429 times.
✗ Branch 3 not taken.
32047 while (*previous_dir_start_pos != '/' && *previous_dir_start_pos != '\0')
149 {
150 28429 *previous_dir_start_pos = '\x01';
151 28429 ++previous_dir_start_pos;
152 }
153
154
1/2
✓ Branch 0 taken 3618 times.
✗ Branch 1 not taken.
3618 if (*previous_dir_start_pos == '/')
155 {
156 3618 *previous_dir_start_pos = '\x01';
157 }
158
159
2/2
✓ Branch 0 taken 7236 times.
✓ Branch 1 taken 3618 times.
10854 while (current_dir_start_pos != current_dir_end_pos)
160 {
161 7236 *current_dir_start_pos = '\x01';
162 7236 ++current_dir_start_pos;
163 }
164
165
2/2
✓ Branch 0 taken 3600 times.
✓ Branch 1 taken 18 times.
3618 if (*current_dir_start_pos == '/')
166 {
167 3600 *current_dir_start_pos = '\x01';
168 3600 ++current_dir_end_pos;
169 }
170 }
171
172 5995 previous_dir_start_pos = nullptr;
173 }
174 else
175 {
176
2/2
✓ Branch 0 taken 181986 times.
✓ Branch 1 taken 58024 times.
240010 if (*current_dir_end_pos == '/')
177 {
178 181986 ++current_dir_end_pos;
179 }
180
181
2/2
✓ Branch 0 taken 237507 times.
✓ Branch 1 taken 2503 times.
240010 if (length > 0)
182 {
183 94080 previous_dir_start_pos = current_dir_start_pos;
184 }
185 }
186
187 102659 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 23900 string copy;
193
194
2/2
✓ Branch 0 taken 1681698 times.
✓ Branch 1 taken 58061 times.
1739759 for (int i = 0; i < normalized.length(); ++i)
195 {
196
2/2
✓ Branch 0 taken 1638383 times.
✓ Branch 1 taken 43315 times.
1681698 if (normalized[i] != '\x01')
197 {
198
1/2
✓ Branch 0 taken 1638383 times.
✗ Branch 1 not taken.
1638383 copy += normalized[i];
199 }
200 }
201
202
1/2
✓ Branch 0 taken 23900 times.
✗ Branch 1 not taken.
23900 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 34161 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 81961 return normalized;
220 58061 }
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