1 /**
2 Copyright (c) 2018 Clipsey (clipseypone@gmail.com)
3 
4 Permission is hereby granted, free of charge, to any person or organization
5 obtaining a copy of the software and accompanying documentation covered by
6 this license (the "Software") to use, reproduce, display, distribute,
7 execute, and transmit the Software, and to prepare derivative works of the
8 Software, and to permit third-parties to whom the Software is furnished to
9 do so, all subject to the following:
10 
11 The copyright notices in the Software and this entire statement, including
12 the above license grant, this restriction and the following disclaimer,
13 must be included in all copies of the Software, in whole or in part, and
14 all derivative works of the Software, unless such copies or derivative
15 works are solely in the form of machine-executable object code generated by
16 a source language processor.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
21 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
22 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
23 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 DEALINGS IN THE SOFTWARE.
25 */
26 module ppc.backend.cfile;
27 import ppc.backend;
28 import ppc.exceptions;
29 
30 import core.stdc.stdlib;
31 import core.stdc..string;
32 import core.stdc.stdio;
33 
34 // Handling for bit arch
35 // Windows acts like it's 32 bit even if it's 64 bit, ugly hack to make windows work.
36 version(Windows) version = BIT_32;
37 else version(D_X32) version = BIT_32;
38 else version(D_LP64) version = BIT_64;
39 
40 /// Seek based of the start of the MemFile
41 /// Alias of SEEK_SET
42 enum SeekStart = SEEK_SET;
43 
44 /// Seek based of the current position of the readhead of the MemFile
45 /// Alias of SEEK_CUR
46 enum SeekCurrent = SEEK_CUR;
47 
48 /// Seek based of the end of the MemFile backwards.
49 /// Alias of SEEK_END
50 enum SeekEnd = SEEK_END;
51 
52 /// Loads a raw file as a MemFile usable by the loaders.
53 MemFile loadFile(string filePath) {
54     import std.file : read;
55     auto data = cast(ubyte[])read(filePath);
56     MemFile file;
57     file.arrayptr = data.ptr;
58     file.readhead = file.arrayptr;
59     file.length = data.length;
60     return file;
61 }
62 
63 /// Loads a raw file as a MemFile usable by the loaders.
64 RefMemFile loadFileRef(string filePath) {
65     import std.file : read;
66     auto data = cast(ubyte[])read(filePath);
67     return RefMemFile(data);
68 }
69 
70 /// A memfile wrapper to make sure that the garbage collector 100% doesn't remove it.
71 struct RefMemFile {
72 private:
73     ubyte[] data;
74 
75 public:
76     /// The MemFile object that the references the internally stored data of this struct.
77     MemFile file;
78 
79     /// Creates a new RefMemFile
80     this(ubyte[] data) {
81         this.data = data;
82         file.arrayptr = cast(ubyte*)&this.data;
83         file.readhead = file.arrayptr;
84         file.length = data.length;
85     }
86 }
87 
88 /// A C File struct which reads from memory instead of disk.
89 struct MemFile {
90 public:
91     /// Pointer to data
92     ubyte* arrayptr;
93 
94     /// Pointer to read head.
95     ubyte* readhead;
96 
97     /// Length of data.
98     size_t length;
99 
100     this(ubyte* array, size_t length) {
101         this.arrayptr = array;
102         this.readhead = this.arrayptr;
103         this.length = length;
104     }
105 
106     /// MemFile implementation of C fseek.
107     static extern (C) int seek(void* fileStream, int64_t offset, int whence) nothrow {
108         MemFile* mf = cast(MemFile*)fileStream;
109         switch (whence) {
110             case SEEK_CUR:
111                 mf.readhead += offset;
112                 break;
113             case SEEK_SET:
114                 mf.readhead = mf.arrayptr + offset;
115                 break;
116             case SEEK_END:
117                 mf.readhead = mf.arrayptr + mf.length-offset;
118                 break;
119             default:
120                 return -1;
121         }
122 
123         if (mf.readhead < mf.arrayptr) {
124             mf.readhead = mf.arrayptr;
125             return -1;
126         }
127 
128         if (mf.readhead > mf.arrayptr + mf.length) {
129             mf.readhead = mf.arrayptr + mf.length;
130         }
131 
132         return 0;
133     }
134 
135     /// MemFile implementation of C fread.
136     static extern (C) size_t read(void* data, size_t bytes, size_t to_read, void* fileStream) nothrow {
137         MemFile* mf = cast(MemFile*)fileStream;
138         
139         size_t len = bytes*to_read;
140         if (mf.readhead + len > mf.arrayptr+mf.length) {
141             len = mf.arrayptr+mf.length-mf.readhead;
142         }
143         memcpy(data, mf.readhead, len);
144         mf.readhead += len;
145         return len;
146     }
147 
148     // TODO: Improve the implementation of this.
149     /// MemFile implementation of C fwrite
150     /// Returns 0 if index is out of range
151     static extern (C) size_t write(void* data, size_t bytes, size_t to_write, void* fileStream) {
152         MemFile* mf = cast(MemFile*)fileStream;
153 
154         // Create buffer if doesn't exist.
155         if (mf.arrayptr is null) {
156             mf.arrayptr = (new ubyte[1]).ptr;
157             mf.readhead = mf.arrayptr;
158         }
159 
160         ubyte[] newArr;
161         size_t len = bytes*to_write;
162         if (mf.readhead + len > mf.arrayptr+mf.length) {
163             
164             // Offset from last iteration
165             size_t offset = (cast(size_t)mf.readhead-cast(size_t)mf.arrayptr);
166 
167             // Allocation
168             newArr = new ubyte[mf.length+(len)];
169             
170             // Resize/copy operation
171             memoryCopy(mf.arrayptr, newArr.ptr, mf.length);
172             mf.arrayptr = newArr.ptr;
173             mf.readhead = mf.arrayptr+offset;
174             mf.length = newArr.length;
175 
176         }
177         memoryCopy(data, mf.readhead, len);
178         mf.readhead += len;
179         return len;
180     }
181 
182     /// MemFile implementation of C fclose.
183     static extern (C) int close(void* fileStream) nothrow {
184         return 0;
185     }
186 
187     /// MemFile implementation of C ftell.
188     static extern (C) clong tell(void* fileStream) nothrow {
189         MemFile* mf = cast(MemFile*)fileStream;
190 
191         // Screw C file handling, make sure that the right type of long is used on each platform...
192         version(BIT_32) {
193 
194             // Pray that stuff doesn't break with this cast.
195             return cast(clong)mf.readhead-cast(clong)mf.arrayptr;
196 
197         } else version(BIT_64) {
198 
199             // How it is intended to work, geez.
200             return mf.readhead-mf.arrayptr;
201 
202         } 
203     }
204 
205     ubyte[] toArray() nothrow {
206         return cast(ubyte[])arrayptr[0..length];
207     }
208     
209 }
210 
211 /// C-style memory copy.
212 void memoryCopy(void* input, void* output, size_t length) nothrow {
213     memcpy(output, input, length);
214 }