-
Notifications
You must be signed in to change notification settings - Fork 1
/
cdb_command_impl.c
322 lines (289 loc) · 8.39 KB
/
cdb_command_impl.c
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/* Copyright 2015 James Laird-Wah
This file is part of Saturn Satisfier and is not licensed under the GPL.
This file may be freely distributed and used as part of Yabause.
Incorporation of any part of this file in any other software or firmware
without the written permission of the author is not permitted.
*/
// file handles {{{
#define MAX_FILES 16
static FIL files[MAX_FILES];
static uint16_t files_open = 0;
static FIL* file_for_handle(int8_t handle) {
if (handle < 0)
return NULL;
handle &= 0xf;
if (!(files_open & (1<<handle)))
return NULL;
return &files[handle];
}
static int8_t alloc_handle(void) {
int8_t handle = 0;
for (handle=0; handle<16; handle++) {
if (!(files_open & (1<<handle))) {
files_open |= (1<<handle);
return handle;
}
}
return -1;
}
static void free_handle(int8_t handle) {
if (handle < 0)
return;
handle &= 0xf;
files_open &= ~(1<<handle);
}
// ensure any open files are flushed to disk && close them
void cdb_unmount(void) {
int i;
for (i=0; i<MAX_FILES; i++) {
FIL *f = file_for_handle(i);
if (f)
f_close(f);
free_handle(i);
}
}
// }}}
typedef struct {
uint32_t size;
uint16_t date, time;
uint8_t attr;
char name[];
} __attribute__((packed)) s_stat_t;
static uint8_t buffer[2049];
uint16_t cdb_buf[5];
FRESULT reader_desc_load(const char *filename);
static uint32_t fat_time = 0;
DWORD get_fattime(void) {
if (fat_time)
return fat_time;
else
return (35<<25) | // 2015
(3<<21) | // March
(1<<16) | // the first
0; // midnight
}
static inline void set_status(uint8_t result) {
cdb_buf[0] = result << 8;
}
static inline void set_length(uint32_t length) {
cdb_buf[2] = length>>16;
cdb_buf[3] = length;
}
static inline uint32_t get_length(void) {
return (cdb_buf[2] << 16) | cdb_buf[3];
}
static inline uint8_t get_handle(void) {
return cdb_buf[0] & 0xf;
}
static inline FIL *get_file(void) {
return file_for_handle(get_handle());
}
#define read_filename() do { \
if (length > 256) \
goto fail; \
read_buf(length); \
buffer[length] = '\0'; \
} while(0)
int cdb_command(void) {
static FIL *f;
static FRESULT result;
static uint8_t handle;
static uint32_t length;
static UINT nread, nwritten;
static int bios_len;
static FIL biosfp;
static FILINFO stat;
static uint8_t lfname[256];
static DIR dir;
static int diropen = 0;
static s_stat_t *sst;
static int32_t offset;
int cmd;
setup_cmd();
switch (cmd) {
case 0xE2:
if (f_open(&biosfp, "menu.bin", FA_READ) != FR_OK) {
warn("Failed to open menu.bin!");
goto fail;
}
f_lseek(&biosfp, cdb_buf[1] * 2048);
if (f_tell(&biosfp) != cdb_buf[1] * 2048)
goto fail;
bios_len = cdb_buf[3];
while (bios_len--) {
f_read(&biosfp, buffer, 2048, &nread);
write_bios_sector();
}
f_close(&biosfp);
break;
case c_close:
if (!f)
goto fail;
result = f_close(f);
set_status(result);
free_handle(handle);
break;
case c_read:
if (!f)
goto fail;
if (length > sizeof(buffer))
goto fail;
result = f_read(f, buffer, length, &nread);
set_length(nread);
set_status(result);
if (result == FR_OK)
write_buf(nread);
break;
case c_seek:
if (!f)
goto fail;
f_sync(f);
offset = get_length();
switch (cdb_buf[1]) {
case C_SEEK_SET:
result = f_lseek(f, offset);
break;
case C_SEEK_CUR:
result = f_lseek(f, offset + f_tell(f));
break;
case C_SEEK_END:
result = f_lseek(f, offset + f_size(f));
break;
default:
result = FR_INVALID_PARAMETER;
}
set_length(f_tell(f));
set_status(result);
break;
case c_open:
read_filename();
handle = alloc_handle();
if (handle < 0) {
set_status(FR_TOO_MANY_OPEN_FILES);
goto fail;
}
f = file_for_handle(handle);
result = f_open(f, buffer, cdb_buf[1] & 0xff);
if (result != FR_OK) {
free_handle(handle);
cdb_buf[3] = 0xffff;
} else {
cdb_buf[3] = handle;
}
set_status(result);
break;
case c_write:
if (!f) {
set_status(FR_INVALID_OBJECT);
break;
}
read_buf(length);
result = f_write(f, buffer, length, &nwritten);
set_length(nwritten);
set_status(result);
break;
case c_truncate:
if (!f) {
set_status(FR_INVALID_OBJECT);
break;
}
result = f_truncate(f);
set_length(f_tell(f));
set_status(result);
break;
case c_readdir:
stat.lfname = lfname;
stat.lfsize = sizeof(lfname);
result = f_readdir(&dir, &stat);
set_status(result);
if (result)
break;
goto return_stat;
case c_stat:
read_filename();
stat.lfname = lfname;
stat.lfsize = sizeof(lfname);
result = f_stat(buffer, &stat);
set_status(result);
if (result)
break;
return_stat:
// 9 bytes as per FILINFO, but with endianness fixed
sst = (void*)buffer;
sst->size = htonl(stat.fsize);
sst->date = htons(stat.fdate);
sst->time = htons(stat.ftime);
sst->attr = stat.fattrib;
// then the filename
if (*stat.lfname) {
strcpy(sst->name, stat.lfname);
} else {
strcpy(sst->name, stat.fname);
}
length = 9 + strlen(sst->name);
set_length(length);
write_buf(length);
break;
case c_rename:
// two filenames, NUL separated
read_filename();
result = f_rename(buffer, buffer+strlen(buffer)+1);
set_status(result);
break;
case c_unlink:
read_filename();
result = f_unlink(buffer);
set_status(result);
break;
case c_mkdir:
read_filename();
result = f_mkdir(buffer);
set_status(result);
break;
case c_opendir:
if (diropen) {
f_closedir(&dir);
diropen = 0;
}
read_filename();
result = f_opendir(&dir, buffer);
set_status(result);
if (!result)
diropen = 1;
break;
case c_chdir:
read_filename();
result = f_chdir(buffer);
set_status(result);
f_getcwd(buffer, sizeof(buffer));
length = strlen(buffer);
set_length(length);
write_buf(length);
break;
#if _USE_MKFS
case c_mkfs:
if (cdb_buf[1] != 0xfeed ||
cdb_buf[2] != 0xdead ||
cdb_buf[3] != 0xbeef)
goto fail;
result = f_mkfs("", 0, 0);
set_status(result);
break;
#endif
case c_settime:
fat_time = length;
break;
case c_emulate:
read_filename();
result = reader_desc_load(buffer);
set_status(result);
break;
default: // unknown cmd
goto fail;
}
if (0) {
fail:
cdb_buf[0] = cdb_buf[1] = cdb_buf[2] = cdb_buf[3] = 0xffff;
}
finish_cmd();
}