1 #ifndef NALL_FILE_HPP
2 #define NALL_FILE_HPP
3 
4 #include <stdio.h>
5 #include <string.h>
6 
7 #if !defined(_WIN32)
8   #include <unistd.h>
9 #else
10   #include <io.h>
11 #endif
12 
13 #include <nall/stdint.hpp>
14 #include <nall/utf8.hpp>
15 #include <nall/utility.hpp>
16 
17 namespace nall {
fopen_utf8(const char * utf8_filename,const char * mode)18   inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) {
19     #if !defined(_WIN32)
20     return fopen(utf8_filename, mode);
21     #else
22     return _wfopen(utf16_t(utf8_filename), utf16_t(mode));
23     #endif
24   }
25 
26   class file : noncopyable {
27   public:
28     enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread };
29     enum SeekMode { seek_absolute, seek_relative };
30 
read()31     uint8_t read() {
32       if(!fp) return 0xff;                       //file not open
33       if(file_mode == mode_write) return 0xff;   //reads not permitted
34       if(file_offset >= file_size) return 0xff;  //cannot read past end of file
35       buffer_sync();
36       return buffer[(file_offset++) & buffer_mask];
37     }
38 
readl(unsigned length=1)39     uintmax_t readl(unsigned length = 1) {
40       uintmax_t data = 0;
41       for(int i = 0; i < length; i++) {
42         data |= (uintmax_t)read() << (i << 3);
43       }
44       return data;
45     }
46 
readm(unsigned length=1)47     uintmax_t readm(unsigned length = 1) {
48       uintmax_t data = 0;
49       while(length--) {
50         data <<= 8;
51         data |= read();
52       }
53       return data;
54     }
55 
read(uint8_t * buffer,unsigned length)56     void read(uint8_t *buffer, unsigned length) {
57       while(length--) *buffer++ = read();
58     }
59 
write(uint8_t data)60     void write(uint8_t data) {
61       if(!fp) return;                     //file not open
62       if(file_mode == mode_read) return;  //writes not permitted
63       buffer_sync();
64       buffer[(file_offset++) & buffer_mask] = data;
65       buffer_dirty = true;
66       if(file_offset > file_size) file_size = file_offset;
67     }
68 
writel(uintmax_t data,unsigned length=1)69     void writel(uintmax_t data, unsigned length = 1) {
70       while(length--) {
71         write(data);
72         data >>= 8;
73       }
74     }
75 
writem(uintmax_t data,unsigned length=1)76     void writem(uintmax_t data, unsigned length = 1) {
77       for(int i = length - 1; i >= 0; i--) {
78         write(data >> (i << 3));
79       }
80     }
81 
write(const uint8_t * buffer,unsigned length)82     void write(const uint8_t *buffer, unsigned length) {
83       while(length--) write(*buffer++);
84     }
85 
print(const char * string)86     void print(const char *string) {
87       if(!string) return;
88       while(*string) write(*string++);
89     }
90 
flush()91     void flush() {
92       buffer_flush();
93       fflush(fp);
94     }
95 
seek(int offset,SeekMode mode=seek_absolute)96     void seek(int offset, SeekMode mode = seek_absolute) {
97       if(!fp) return;  //file not open
98       buffer_flush();
99 
100       intmax_t req_offset = file_offset;
101       switch(mode) {
102         case seek_absolute: req_offset  = offset; break;
103         case seek_relative: req_offset += offset; break;
104       }
105 
106       if(req_offset < 0) req_offset = 0;  //cannot seek before start of file
107       if(req_offset > file_size) {
108         if(file_mode == mode_read) {      //cannot seek past end of file
109           req_offset = file_size;
110         } else {                          //pad file to requested location
111           file_offset = file_size;
112           while(file_size < req_offset) write(0x00);
113         }
114       }
115 
116       file_offset = req_offset;
117     }
118 
offset()119     int offset() {
120       if(!fp) return -1;  //file not open
121       return file_offset;
122     }
123 
size()124     int size() {
125       if(!fp) return -1;  //file not open
126       return file_size;
127     }
128 
truncate(unsigned size)129     bool truncate(unsigned size) {
130       if(!fp) return false;  //file not open
131       #if !defined(_WIN32)
132       return ftruncate(fileno(fp), size) == 0;
133       #else
134       return _chsize(fileno(fp), size) == 0;
135       #endif
136     }
137 
end()138     bool end() {
139       if(!fp) return true;  //file not open
140       return file_offset >= file_size;
141     }
142 
exists(const char * fn)143     static bool exists(const char *fn) {
144       #if !defined(_WIN32)
145       FILE *fp = fopen(fn, "rb");
146       #else
147       FILE *fp = _wfopen(utf16_t(fn), L"rb");
148       #endif
149       if(fp) {
150         fclose(fp);
151         return true;
152       }
153       return false;
154     }
155 
size(const char * fn)156     static unsigned size(const char *fn) {
157       #if !defined(_WIN32)
158       FILE *fp = fopen(fn, "rb");
159       #else
160       FILE *fp = _wfopen(utf16_t(fn), L"rb");
161       #endif
162       unsigned filesize = 0;
163       if(fp) {
164         fseek(fp, 0, SEEK_END);
165         filesize = ftell(fp);
166         fclose(fp);
167       }
168       return filesize;
169     }
170 
open()171     bool open() {
172       return fp;
173     }
174 
open(const char * fn,FileMode mode)175     bool open(const char *fn, FileMode mode) {
176       if(fp) return false;
177 
178       switch(file_mode = mode) {
179         #if !defined(_WIN32)
180         case mode_read:      fp = fopen(fn, "rb");  break;
181         case mode_write:     fp = fopen(fn, "wb+"); break;  //need read permission for buffering
182         case mode_readwrite: fp = fopen(fn, "rb+"); break;
183         case mode_writeread: fp = fopen(fn, "wb+"); break;
184         #else
185         case mode_read:      fp = _wfopen(utf16_t(fn), L"rb");  break;
186         case mode_write:     fp = _wfopen(utf16_t(fn), L"wb+"); break;
187         case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break;
188         case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break;
189         #endif
190       }
191       if(!fp) return false;
192       buffer_offset = -1;  //invalidate buffer
193       file_offset = 0;
194       fseek(fp, 0, SEEK_END);
195       file_size = ftell(fp);
196       fseek(fp, 0, SEEK_SET);
197       return true;
198     }
199 
close()200     void close() {
201       if(!fp) return;
202       buffer_flush();
203       fclose(fp);
204       fp = 0;
205     }
206 
file()207     file() {
208       memset(buffer, 0, sizeof buffer);
209       buffer_offset = -1;
210       buffer_dirty = false;
211       fp = 0;
212       file_offset = 0;
213       file_size = 0;
214       file_mode = mode_read;
215     }
216 
~file()217     ~file() {
218       close();
219     }
220 
221   private:
222     enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 };
223     char buffer[buffer_size];
224     int buffer_offset;
225     bool buffer_dirty;
226     FILE *fp;
227     unsigned file_offset;
228     unsigned file_size;
229     FileMode file_mode;
230 
buffer_sync()231     void buffer_sync() {
232       if(!fp) return;  //file not open
233       if(buffer_offset != (file_offset & ~buffer_mask)) {
234         buffer_flush();
235         buffer_offset = file_offset & ~buffer_mask;
236         fseek(fp, buffer_offset, SEEK_SET);
237         unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
238         if(length) unsigned unused = fread(buffer, 1, length, fp);
239       }
240     }
241 
buffer_flush()242     void buffer_flush() {
243       if(!fp) return;                     //file not open
244       if(file_mode == mode_read) return;  //buffer cannot be written to
245       if(buffer_offset < 0) return;       //buffer unused
246       if(buffer_dirty == false) return;   //buffer unmodified since read
247       fseek(fp, buffer_offset, SEEK_SET);
248       unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
249       if(length) unsigned unused = fwrite(buffer, 1, length, fp);
250       buffer_offset = -1;                 //invalidate buffer
251       buffer_dirty = false;
252     }
253   };
254 }
255 
256 #endif
257