1 /*
2 Copyright 2010 Jared Krinke.
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <physfs.h>
31
32 #include "r_defs.h"
33 #include "r_assert.h"
34 #include "r_string.h"
35 #include "r_file_system.h"
36
37 const char *r_platform_newline = "\n";
38
r_platform_create_directory(r_state_t * rs,const char * dir)39 r_status_t r_platform_create_directory(r_state_t *rs, const char *dir)
40 {
41 r_status_t status = (rs != NULL && dir != NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
42 R_ASSERT(R_SUCCEEDED(status));
43
44 if (R_SUCCEEDED(status))
45 {
46 int result = mkdir(dir, 0744);
47
48 status = (result == 0) ? R_SUCCESS : R_FAILURE;
49
50 if (R_FAILED(status) && errno == EEXIST)
51 {
52 status = R_S_ALREADY_EXISTS;
53 }
54 }
55
56 return status;
57 }
58
r_platform_setup_output(r_state_t * rs,const char * user_dir)59 r_status_t r_platform_setup_output(r_state_t *rs, const char *user_dir)
60 {
61 /* Just use stdout/stderr normally on UNIX */
62 return R_SUCCESS;
63 }
64
r_platform_application_allocate_user_dir(r_state_t * rs,const char * application,char ** user_dir)65 r_status_t r_platform_application_allocate_user_dir(r_state_t *rs, const char *application, char **user_dir)
66 {
67 r_status_t status = (rs != NULL && application != NULL && user_dir!= NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
68 R_ASSERT(R_SUCCEEDED(status));
69
70 if (R_SUCCEEDED(status))
71 {
72 char *home = getenv("HOME");
73
74 status = (home != NULL) ? R_SUCCESS : R_FAILURE;
75
76 if (R_SUCCEEDED(status))
77 {
78 status = r_string_format_allocate(rs, user_dir, R_FILE_SYSTEM_MAX_PATH_LENGTH, R_FILE_SYSTEM_MAX_PATH_LENGTH, "%s/.%s", home, application);
79 }
80 }
81
82 return status;
83 }
84
r_platform_application_allocate_data_dirs(r_state_t * rs,const char * application_name,const char * data_dir_override,char *** data_dirs)85 r_status_t r_platform_application_allocate_data_dirs(r_state_t *rs, const char *application_name, const char *data_dir_override, char ***data_dirs)
86 {
87 /* Convert application name to lower case */
88 size_t application_name_length = strlen(application_name);
89 char *application_name_lower = (char*)malloc(application_name_length + 1);
90 r_status_t status = (application_name_lower != NULL) ? R_SUCCESS : R_F_OUT_OF_MEMORY;
91
92 if (R_SUCCEEDED(status))
93 {
94 int c;
95
96 for (c = 0; c < application_name_length; ++c)
97 {
98 application_name_lower[c] = tolower(application_name[c]);
99 }
100
101 application_name_lower[c] = '\0';
102
103 {
104 /* These are encoded with the lower-cased application name */
105 static const char *default_dirs[] = {
106 "/usr/share/%s",
107 "/usr/local/share/%s",
108 "/usr/share/games/%s",
109 "/usr/local/share/games/%s",
110 };
111
112 /* Data directory search path:
113 *
114 * 1) Hard-coded override (user-supplied at compile time)
115 * 2) Default installation paths (these are just educated guesses)
116 * 3) PhysicsFS "base" dir (e.g. if the user runs from the build directory)
117 * (Followed by a NULL) */
118
119 char **data_dirs_internal = (char**)calloc(R_ARRAY_SIZE(default_dirs) + 3, sizeof(char*));
120 r_status_t status = (data_dirs_internal != NULL) ? R_SUCCESS : R_F_OUT_OF_MEMORY;
121
122 if (R_SUCCEEDED(status))
123 {
124 /* First, use the hard-coded override */
125 int j = 0;
126
127 status = r_string_format_allocate(rs, &data_dirs_internal[j++], R_FILE_SYSTEM_MAX_PATH_LENGTH, R_FILE_SYSTEM_MAX_PATH_LENGTH, "%s", data_dir_override);
128
129 /* Next, use the default paths */
130 if (R_SUCCEEDED(status))
131 {
132 int i;
133
134 for (i = 0; R_SUCCEEDED(status) && i < R_ARRAY_SIZE(default_dirs); ++i)
135 {
136 status = r_string_format_allocate(rs, &data_dirs_internal[j++], R_FILE_SYSTEM_MAX_PATH_LENGTH, R_FILE_SYSTEM_MAX_PATH_LENGTH, default_dirs[i], application_name_lower);
137 }
138 }
139
140 /* As a last resort, use "<base>/Data" */
141 if (R_SUCCEEDED(status))
142 {
143 status = r_string_format_allocate(rs, &data_dirs_internal[j++], R_FILE_SYSTEM_MAX_PATH_LENGTH, R_FILE_SYSTEM_MAX_PATH_LENGTH, "%sData", PHYSFS_getBaseDir());
144 }
145
146 if (R_FAILED(status))
147 {
148 /* Free already-allocated paths */
149 for (--j; j >= 0; --j)
150 {
151 free(data_dirs_internal[j]);
152 }
153
154 free(data_dirs_internal);
155 }
156 }
157
158 if (R_SUCCEEDED(status))
159 {
160 *data_dirs = data_dirs_internal;
161 }
162 }
163
164 free(application_name_lower);
165 }
166
167 return status;
168 }
169
170