aboutsummaryrefslogtreecommitdiffstats
path: root/src/filesystem.c
blob: 5732e4037409827b4dff7f550e67bf71250bfe71 (plain)
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
// We need 64 * 32 bytes (2K) of SRAM for file indexes. To avoid corruption
// issues we ignore the first file (32 bytes).
// Note that the filename should include the null terminator if we want to use
// strcmp.
#define FILE_NAME_SIZE     27
#define FILE_CAPACITY      4
#define FILE_HEADER_OFFSET 2
#define FILE_INDEX_OFFSET  32
#define FILE_DATA_OFFSET   KB(2)
#define FILE_MAX_SIZE      KB(16)
#define SRAM               ((vu8*)(MEM_CART))

typedef struct File {
    char name[FILE_NAME_SIZE + 1];
    u16 size;
    u16 mem_offset; // NOTE: Unused...
} File;

// The filesystem header.
typedef struct FileSystem {
    u16 num_files;
    u16 data_size; // NOTE: Unused...
    u16 data_capacity; // NOTE: Unused...
    File files[FILE_CAPACITY];
} FileSystem;

static FileSystem filesystem;

void
_fs_read(u8 *dst, size_t pos, size_t n_bytes) {
    for (size_t i = 0; i < n_bytes; ++i) {
        dst[i] = SRAM[pos + i];
    }
}

void
_fs_write(u8 *src, size_t pos, size_t n_bytes) {
    for (size_t i = 0; i < n_bytes; ++i) {
        SRAM[pos + i] = src[i];
    }
}

void
fs_init() {
    // Load header if existing.
    _fs_read(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files));
    if (filesystem.num_files == 0xFFFF
            && filesystem.data_capacity == 0xFFFF
            && filesystem.data_size == 0xFFFF) {
        // Clear SRAM.
        for (size_t i = 0; i < KB(64); ++i) {
            SRAM[i] = 0;
        }

        // Initialize filesystem.
        filesystem.num_files = 0;
        filesystem.data_size = 0;
        filesystem.data_capacity = 27 * FILE_MAX_SIZE;
        memset(&filesystem.files, 0, FILE_CAPACITY * sizeof(File));
        _fs_write(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files));
    } else {
        _fs_read(&filesystem.files, FILE_INDEX_OFFSET, sizeof(File) * filesystem.num_files);
    }
}

typedef enum {
    OPEN_READ,
    OPEN_WRITE,
} OpenMode;

int
fs_open_file(char *name, OpenMode mode) {
    // Try to find an existing file.
    for (size_t i = 0; i < filesystem.num_files; ++i) {
        // TODO: Replace strcmp with vectorized fixed size char comparison.
        if (strcmp(name, filesystem.files[i].name) == 0) {
            return i;
        }
    }

    if (mode == OPEN_READ) {
        return -1;
    }

    // Create a new file if there is space.
    if (filesystem.num_files < FILE_CAPACITY) {
        size_t index = filesystem.num_files++;
        size_t k = 0;
        while(*name) {
            filesystem.files[index].name[k++] = *name++;
        }
        filesystem.files[index].size = 0;
        filesystem.files[index].mem_offset = 0;

        // Update file index.
        _fs_write(&filesystem.files[index],
                FILE_INDEX_OFFSET + index * sizeof(File),
                sizeof(File));

        // Update header.
        _fs_write(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files));

        return index;
    }
    return -1;
}

size_t
fs_write(u8 *src, size_t n_bytes, u16 file_index, u16 offset, bool append) {
    File *file = &filesystem.files[file_index];

    // Check if there is enough capacity for this write operation.
    if (offset + n_bytes >= FILE_MAX_SIZE) {
        return 0;
    }

    // Write data to file block.
    _fs_write(src, FILE_DATA_OFFSET + FILE_MAX_SIZE * file_index + offset, n_bytes);

    // Update file index.
    if (append) {
        if (offset + n_bytes > file->size) {
            file->size = offset + n_bytes;
        }
    } else {
        file->size = offset + n_bytes;
    }
    _fs_write(file, FILE_INDEX_OFFSET + file_index * sizeof(File), sizeof(File));

    // Update header.
    _fs_write(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files));

    return n_bytes;
}

size_t
fs_read(u8 *dst, size_t n_bytes, u16 file_index, u16 offset) {
    File *file = &filesystem.files[file_index];

    // Check if the offset is within limits.
    if (file->size == 0 || offset >= file->size - 1) {
        return 0;
    }

    // Read as much as we can.
    if (offset + n_bytes > file->size) {
        n_bytes = file->size - offset;
    }

    // Copy n_bytes to destination.
    _fs_read(dst, FILE_DATA_OFFSET + FILE_MAX_SIZE * file_index + offset, n_bytes);

    return n_bytes;
}