-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathfiles.inc
179 lines (154 loc) · 5.66 KB
/
files.inc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* Utility stocks for files.
*/
#if defined __stocksoup_file_included
#endinput
#endif
#define __stocksoup_file_included
#include <stocksoup/string>
/**
* Copies the contents of the file located at `source` to the path specified by `dest`. No
* other information (e.g. metadata) is copied.
*
* @param source Path to source file.
* @param dest Path to destination. Any file present is overwritten.
* @error Source / destination file(s) could not be opened for reading / writing.
*/
stock void CopyFile(const char[] source, const char[] dest) {
if (!FileExists(source)) {
ThrowError("Source file \"%s\" does not exist", source);
}
File srcFile = OpenFile(source, "rb");
File dstFile = OpenFile(dest, "wb");
if (!srcFile) {
ThrowError("Failed to open source file \"%s\"", source);
} else if (!dstFile) {
ThrowError("Failed to open destination file \"%s\"", dest);
}
int buffer[4096];
int numRead;
do {
numRead = srcFile.Read(buffer, sizeof(buffer), 1);
dstFile.Write(buffer, numRead, 1);
} while (numRead > 0);
delete srcFile;
delete dstFile;
}
/**
* Creates a directory, creating parent directories if they don't exist.
*
* @param path Path to create. Parent directories that do not exist are also created.
* On POSIX systems, a backslash will be treated as part of the folder name
* (this may not work -- for simplicity's sake stick to '/' no matter the
* platform).
* @param mode Permissions (default is o=rx,g=rx,u=rwx). Note that folders must have
* the execute bit set on Linux. On Windows, the mode is ignored.
* Nonexisting directories will be created with these permissions; existing
* directories' permissions will not be modified.
* @param use_valve_fs If true, the Valve file stem will be used instead. This can be used to
* create folders in the game's Valve search paths, rather than directly in
* the gamedir.
* @param valve_path_id If use_valve_fs, a search path from gameinfo or NULL_STRING for default.
* In this case, mode is ignored.
*/
stock bool CreateDirectories(const char[] path, int mode, bool use_valve_fs = false,
const char[] valve_path_id = "DEFAULT_WRITE_PATH") {
char normalizedPath[PLATFORM_MAX_PATH];
strcopy(normalizedPath, sizeof(normalizedPath), path);
NormalizePathToPOSIX(normalizedPath);
char partialPathBuffer[PLATFORM_MAX_PATH];
int p = -1;
while ( (p = FindNextCharInString(p, normalizedPath, '/')) != -1 ) {
// exclude trailing forward slash
strcopy(partialPathBuffer, p, normalizedPath);
if (!DirExists(partialPathBuffer, use_valve_fs, valve_path_id)
&& !CreateDirectory(partialPathBuffer, mode, use_valve_fs, valve_path_id)) {
return false;
}
}
// try to create the last directory in the path
return DirExists(path, use_valve_fs, valve_path_id) ||
CreateDirectory(path, mode, use_valve_fs, valve_path_id);
}
/**
* Returns an ArrayList containing the paths of all files in the given directory and its
* subdirectories. Each entry contains the input path as the prefix, and directory separators
* will use the system's native form ('\' on Windows, '/' on POSIX).
*
* This performs a breadth-first iteration.
*
* @param path Starting path to search files.
* @param use_valve_fs If true, the Valve file stem will be used instead. This can be used to
* find files in the game's Valve search paths, rather than solely files
* existing in the gamedir.
* @param valve_path_id If use_valve_fs, a search path from gameinfo or NULL_STRING for all
* search paths.
*/
stock ArrayList GetFilesInDirectoryRecursive(const char[] path, bool use_valve_fs = false,
const char[] valve_path_id = "GAME") {
ArrayList files = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH));
ArrayList paths = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH));
paths.PushString(path);
/**
* Check paths.Length on every iteration since we store tracked paths, just in case we
* somehow get caught in a loop.
*/
for (int i; i < paths.Length; i++) {
char currentPath[PLATFORM_MAX_PATH], currentEntry[PLATFORM_MAX_PATH];
FileType type;
paths.GetString(i, currentPath, sizeof(currentPath));
DirectoryListing listing = OpenDirectory(currentPath, use_valve_fs, valve_path_id);
if (!listing) {
continue;
}
while (listing.GetNext(currentEntry, sizeof(currentEntry), type)) {
char fullpath[PLATFORM_MAX_PATH];
// SourceMod doesn't do native path separators without BuildPath...
// ... forward slashes it is.
FormatEx(fullpath, sizeof(fullpath), "%s%c%s",
currentPath, DumbHackTo_GetPathComponentSeparator(), currentEntry);
switch (type) {
case FileType_File: {
files.PushString(fullpath);
}
case FileType_Directory: {
if (StrEqual(currentEntry, ".") || StrEqual(currentEntry, "..")) {
continue;
}
if (paths.FindString(fullpath) == -1) {
paths.PushString(fullpath);
}
}
}
}
delete listing;
}
delete paths;
return files;
}
/**
* Normalizes the input path to use POSIX path component separators only on non-POSIX systems.
*/
stock void NormalizePathToPOSIX(char[] path) {
char sep = DumbHackTo_GetPathComponentSeparator();
if (sep == '/') {
return;
}
for (int i; path[i]; i++) {
if (path[i] == sep) {
path[i] = '/';
}
}
}
/**
* Returns the path component separator. This is '/' for POSIX and '\\' for Windows.
*/
static stock char DumbHackTo_GetPathComponentSeparator() {
static char result;
if (!result) {
char dir[PLATFORM_MAX_PATH];
BuildPath(Path_SM, dir, sizeof(dir), "%s", "/");
result = dir[strlen(dir) - 1];
}
return result;
}