Branch data Line data Source code
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 : 4277 : string dir(char const *name)
24 : : {
25 [ + - ]: 49121 : for (signed i = (int)strlen(name); i >= 0; i--)
26 : : {
27 [ + + - + ]: 49121 : if (name[i] == '/' || name[i] == '\\')
28 : : {
29 : 4277 : return string(name, i+1);
30 : : }
31 : : }
32 : 0 : return "";
33 : : }
34 : :
35 : 3553 : string create_combined_path(const char* path1, const char* path2)
36 : : {
37 : 3553 : string combined = path1;
38 : :
39 : 2882 : int length = combined.length();
40 : :
41 [ + - + - : 3553 : if (combined.length() > 0 && path1[length - 1] != '\\' && path1[length - 1] != '/')
+ + + + +
+ ]
42 : : {
43 : 32 : combined += get_native_path_separator();
44 : : }
45 : :
46 : 3553 : combined += path2;
47 : :
48 : 7106 : return normalize_path(combined);
49 : 3553 : }
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 : 9062 : string normalize_path(const char* path)
80 : : {
81 : 9062 : 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 [ + + + + ]: 363236 : for (int i = 0; i < normalized.length(); ++i)
90 : : {
91 [ + + ]: 274749 : if (normalized[i] == '\\')
92 : : {
93 : 2404 : 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 : 6742 : char* previous_dir_start_pos = nullptr;
107 : 6742 : char* current_dir_start_pos = normalized.temp_raw();
108 [ + + ]: 10740 : while (*current_dir_start_pos == '/')
109 : : {
110 : 1678 : ++current_dir_start_pos;
111 : : }
112 : 6742 : char* current_dir_end_pos = current_dir_start_pos;
113 : :
114 : :
115 [ + + ]: 48498 : while (*current_dir_start_pos != '\0')
116 : : {
117 [ + + + + ]: 282151 : while (*current_dir_end_pos != '/' && *current_dir_end_pos != '\0')
118 : : {
119 : 242715 : ++current_dir_end_pos;
120 : : }
121 : :
122 : 39436 : size_t length = (size_t)(current_dir_end_pos - current_dir_start_pos);
123 [ + + + + ]: 39436 : if (length > 0 && strncmp(current_dir_start_pos, ".", length) == 0)
124 : : {
125 : : // Found a . path component - remove it.
126 [ + + ]: 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 [ + - ]: 72 : if (*current_dir_start_pos == '/')
133 : : {
134 : 72 : *current_dir_start_pos = '\x01';
135 : 72 : ++current_dir_end_pos;
136 : : }
137 : : }
138 [ + + + + ]: 39355 : 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 [ + + ]: 96 : 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 [ + + + - ]: 432 : while (*previous_dir_start_pos != '/' && *previous_dir_start_pos != '\0')
149 : : {
150 : 360 : *previous_dir_start_pos = '\x01';
151 : 360 : ++previous_dir_start_pos;
152 : : }
153 : :
154 [ + - ]: 72 : if (*previous_dir_start_pos == '/')
155 : : {
156 : 72 : *previous_dir_start_pos = '\x01';
157 : : }
158 : :
159 [ + + ]: 216 : while (current_dir_start_pos != current_dir_end_pos)
160 : : {
161 : 144 : *current_dir_start_pos = '\x01';
162 : 144 : ++current_dir_start_pos;
163 : : }
164 : :
165 [ + + ]: 72 : if (*current_dir_start_pos == '/')
166 : : {
167 : 66 : *current_dir_start_pos = '\x01';
168 : 66 : ++current_dir_end_pos;
169 : : }
170 : : }
171 : :
172 : 48 : previous_dir_start_pos = nullptr;
173 : : }
174 : : else
175 : : {
176 [ + + ]: 39268 : if (*current_dir_end_pos == '/')
177 : : {
178 : 30218 : ++current_dir_end_pos;
179 : : }
180 : :
181 [ + + ]: 39268 : if (length > 0)
182 : : {
183 : 29308 : previous_dir_start_pos = current_dir_start_pos;
184 : : }
185 : : }
186 : :
187 : 29401 : 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 : 9062 : string copy;
193 : :
194 [ + + + + ]: 363236 : for (int i = 0; i < normalized.length(); ++i)
195 : : {
196 [ + + ]: 274749 : if (normalized[i] != '\x01')
197 : : {
198 : 273963 : copy += normalized[i];
199 : : }
200 : : }
201 : :
202 : 6742 : 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 : 6742 : 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 : 15804 : return normalized;
220 : 9062 : }
221 : :
222 : 0 : string get_base_name(char const *name)
223 : : {
224 [ # # ]: 0 : for(int i = (int)strlen(name); i >= 0; --i)
225 : : {
226 [ # # # # ]: 0 : if(name[i] == '/' || name[i] == '\\')
227 : : {
228 : : //file has no extension
229 : : break;
230 : : }
231 [ # # ]: 0 : if(name[i] == '.')
232 : : {
233 : 0 : return string(name, i);
234 : : }
235 : : }
236 : 0 : return string(name);
237 : : }
|