1 /*---------------------------------------------------------------------------*
2 
3   Copyright 2010-2012 Nintendo.  All rights reserved.
4 
5   These coded instructions, statements, and computer programs contain
6   proprietary information of Nintendo of America Inc. and/or Nintendo
7   Company Ltd., and are protected by Federal copyright law.  They may
8   not be disclosed to third parties or copied or duplicated in any form,
9   in whole or in part, without the prior written consent of Nintendo.
10 
11  *---------------------------------------------------------------------------*/
12 ////===========================================================================
13 ///  demoFWB.c
14 ///
15 ///     This is file write buffer code for the DEMO library.
16 ///
17 ////===========================================================================
18 
19 #include <cafe/demo.h>
20 
21 #include <stdio.h>
22 
DEMOFWBOpenFile(const char * path,DEMOFWBFileInfo * fwbFileInfo,const char * mode,u32 writeBufferSize)23 s32 DEMOFWBOpenFile(const char* path, DEMOFWBFileInfo* fwbFileInfo, const char* mode, u32 writeBufferSize)
24 {
25     if((NULL == fwbFileInfo) ||
26        (NULL == mode)        ||
27 	   (0 == writeBufferSize))
28     {
29         return DEMO_FS_RESULT_FATAL_ERROR;
30     }
31 
32     memset(fwbFileInfo, 0, sizeof(DEMOFWBFileInfo));
33 
34     s32 openResult = DEMOFSOpenFileMode(path, &(fwbFileInfo->fileInfo), mode);
35     if(openResult != DEMO_FS_RESULT_OK)
36     {
37         return openResult;
38     }
39 
40     fwbFileInfo->writeBufferSize = writeBufferSize;
41     fwbFileInfo->writeBuffer = DEMOAllocEx(writeBufferSize, PPC_IO_BUFFER_ALIGN);
42 
43     if(NULL == fwbFileInfo->writeBuffer)
44     {
45         return DEMO_FS_RESULT_FATAL_ERROR;
46     }
47 
48     return DEMO_FS_RESULT_OK;
49 }
50 
DEMOFWBCloseFile(DEMOFWBFileInfo * fwbFileInfo)51 s32 DEMOFWBCloseFile(DEMOFWBFileInfo* fwbFileInfo)
52 {
53     if(fwbFileInfo == NULL)
54     {
55         return DEMO_FS_RESULT_FATAL_ERROR;
56     }
57 
58     s32 result = DEMOFWBFlush(fwbFileInfo);
59 
60     if(fwbFileInfo->writeBuffer)
61     {
62         DEMOFree(fwbFileInfo->writeBuffer);
63         fwbFileInfo->writeBuffer = 0;
64     }
65 
66     if(result != DEMO_FS_RESULT_OK)
67     {
68         return result;
69     }
70 
71     s32 result2 = DEMOFSCloseFile(&(fwbFileInfo->fileInfo));
72 
73     fwbFileInfo->fileInfo = 0;
74 
75     return result2;
76 }
77 
78 
79 
DEMOFWBFlush(DEMOFWBFileInfo * fwbFileInfo)80 s32 DEMOFWBFlush(DEMOFWBFileInfo* fwbFileInfo)
81 {
82     s32 retVal = DEMO_FS_RESULT_OK;
83     if(fwbFileInfo == NULL)
84     {
85         return DEMO_FS_RESULT_FATAL_ERROR;
86     }
87 
88 
89     if(fwbFileInfo->writeBufferWriteLocation > 0)
90     {
91         ASSERT(fwbFileInfo->writeBufferWriteLocation <= fwbFileInfo->writeBufferSize);
92 
93         //We have data to be written.
94         retVal =  DEMOFSWrite(&(fwbFileInfo->fileInfo),
95                               fwbFileInfo->writeBuffer,
96                               fwbFileInfo->writeBufferWriteLocation);
97 
98         //Reset the writeBuffer to "empty"
99         fwbFileInfo->writeBufferWriteLocation = 0;
100     }
101 
102     return retVal;
103 }
104 
105 
DEMOFWBWrite(DEMOFWBFileInfo * fwbFileInfo,void * bufferAddress,s32 length)106 s32 DEMOFWBWrite(DEMOFWBFileInfo* fwbFileInfo, void* bufferAddress, s32 length)
107 {
108     if((fwbFileInfo == NULL) ||
109        (bufferAddress == NULL))
110     {
111         return DEMO_FS_RESULT_FATAL_ERROR;
112     }
113 
114     ASSERT(fwbFileInfo->writeBufferWriteLocation <= fwbFileInfo->writeBufferSize);
115 
116     //Is the write larger than writeBufferSize and cannot be buffered?  This will not be efficient\fast.
117     if(length > fwbFileInfo->writeBufferSize)
118     {
119         s32 retVal;
120         DEMOPrintf("DEMOFWB Error: DEMOFWBWrite is taking a slow path since length > writeBufferSize!\n");
121 
122         //The write will not fit in the buffer, so flush buffered writes first so the writes are correctly
123         //ordered.
124         DEMOFWBFlush(fwbFileInfo);
125         ASSERT(fwbFileInfo->writeBufferWriteLocation == 0);
126 
127         //We can't buffer this write, but instead of failing, just write directly to FSA.
128         void *tempPtr;
129         if(((u32)bufferAddress % PPC_IO_BUFFER_ALIGN) == 0)
130         {
131             //address is already 64 byte alligned
132             tempPtr = bufferAddress;
133         }
134         else
135         {
136             //Move the data so the buffer is 64 byte aligned.
137             tempPtr = DEMOAllocEx(length+1, PPC_IO_BUFFER_ALIGN);
138             memcpy(tempPtr, bufferAddress, length);
139         }
140 
141         retVal =  DEMOFSWrite(&(fwbFileInfo->fileInfo),
142                               tempPtr,
143                               length);
144 
145         if(tempPtr != bufferAddress)
146         {
147             DEMOFree(tempPtr);
148         }
149 
150         return retVal;
151     }
152 
153     //Can the write fit in the buffer space remaining?
154     if((fwbFileInfo->writeBufferWriteLocation > 0) &&
155        (length > (fwbFileInfo->writeBufferSize - fwbFileInfo->writeBufferWriteLocation)))
156     {
157         //The write, will not fit in the buffer, so flush it first.
158         DEMOFWBFlush(fwbFileInfo);
159         ASSERT(fwbFileInfo->writeBufferWriteLocation == 0);
160     }
161 
162     //There should be space in the buffer if we get here.
163     if(length > (fwbFileInfo->writeBufferSize - fwbFileInfo->writeBufferWriteLocation))
164     {
165         ASSERT(0); //Something is very broken;
166         return DEMO_FS_RESULT_FATAL_ERROR;
167     }
168 
169     void* writeAddr = (void*)((u32)fwbFileInfo->writeBuffer + fwbFileInfo->writeBufferWriteLocation);
170     memcpy(writeAddr, bufferAddress, length);
171     fwbFileInfo->writeBufferWriteLocation += length;
172 
173     ASSERT(fwbFileInfo->writeBufferWriteLocation <= fwbFileInfo->writeBufferSize);
174 
175     return DEMO_FS_RESULT_OK;
176 }
177 
DEMOFWBvsnprintfHelper(s32 vsnprintfReturnVal,s32 maxPrintSize,s32 * returnCode,u32 * printedChars)178 inline void DEMOFWBvsnprintfHelper(s32 vsnprintfReturnVal, s32 maxPrintSize, s32* returnCode, u32* printedChars)
179 {
180     if(vsnprintfReturnVal < 0)
181     {
182         DEMOPrintf("DEMOFWB Error: DEMOFWBfprintf returned %d!\n", vsnprintfReturnVal);
183         *returnCode = DEMO_FS_RESULT_FATAL_ERROR;
184     }
185     if(vsnprintfReturnVal > maxPrintSize)
186     {
187         //We must have been truncated!
188         //Subtract off the NULL terminator.
189         *printedChars = maxPrintSize - 1;
190     }
191     else
192     {
193         *printedChars = vsnprintfReturnVal;
194     }
195 }
196 
copyChars(char * destination,char * source,u32 length)197 void copyChars(char* destination, char* source, u32 length)
198 {
199     for(u32 i = 0; i < length; i++)
200     {
201         destination[i] = source[i];
202     }
203 }
204 
DEMOFWBfprintf(DEMOFWBFileInfo * fwbFileInfo,const char * format,...)205 s32 DEMOFWBfprintf(DEMOFWBFileInfo* fwbFileInfo, const char * format, ...)
206 {
207     s32 retVal = DEMO_FS_RESULT_OK;
208     va_list valist;
209 
210     if(fwbFileInfo == NULL)
211     {
212         return DEMO_FS_RESULT_FATAL_ERROR;
213     }
214 
215     if(fwbFileInfo->writeBufferWriteLocation == fwbFileInfo->writeBufferSize)
216     {
217         //the buffer is full, so flush it to disk.
218         DEMOFWBFlush(fwbFileInfo);
219     }
220 
221     u32 printedChars = 0;
222     void* writeAddr = (void*)((u32)fwbFileInfo->writeBuffer + fwbFileInfo->writeBufferWriteLocation);
223     u32 freeSpace = fwbFileInfo->writeBufferSize - fwbFileInfo->writeBufferWriteLocation;
224 
225     ASSERT(freeSpace > 0);
226 
227     //We don't really know if the print will fit in the buffer. Try to print "freeSpace" characters of it.
228     va_start(valist, format);
229     s32 vsnprintfResult = vsnprintf(writeAddr, freeSpace, format, valist);
230     va_end(valist);
231     DEMOFWBvsnprintfHelper(vsnprintfResult, freeSpace, &retVal, &printedChars);
232     fwbFileInfo->writeBufferWriteLocation += printedChars;
233 
234     if(vsnprintfResult > fwbFileInfo->writeBufferSize)
235     {
236         DEMOPrintf("DEMOFWB Error: DEMOFWBfprintf print length of %d was greater than writeBufferSize(%d) and data was truncated!", vsnprintfResult, fwbFileInfo->writeBufferSize);
237         retVal = DEMO_FS_RESULT_AREA_FULL;
238     }
239 
240     //Check if the full print fit in the free space or was truncated.
241     if((DEMO_FS_RESULT_OK == retVal) &&
242        (vsnprintfResult > freeSpace))
243     {
244         //The print was truncated, so we need to flush, retry, and pack that data.
245         //The write buffer should be almost full now.  Don't print the null terminator.
246         ASSERT(fwbFileInfo->writeBufferWriteLocation + 1 == fwbFileInfo->writeBufferSize);
247         //Flush the full buffer.
248         DEMOFWBFlush(fwbFileInfo);
249         ASSERT(fwbFileInfo->writeBufferWriteLocation == 0);
250 
251         //print again into the empy buffer
252         u32 printedChars2 = 0;
253         va_start(valist, format);
254         s32 vsnprintfResult2 = vsnprintf(fwbFileInfo->writeBuffer, fwbFileInfo->writeBufferSize, format, valist);
255         va_end(valist);
256         DEMOFWBvsnprintfHelper(vsnprintfResult2, fwbFileInfo->writeBufferSize, &retVal, &printedChars2);
257 
258         if(DEMO_FS_RESULT_OK == retVal)
259         {
260             ASSERT(vsnprintfResult2 <= fwbFileInfo->writeBufferSize); //This shouldn't happen.
261             ASSERT(vsnprintfResult == vsnprintfResult2); //I assume these should match.
262             ASSERT(printedChars2 == vsnprintfResult2);
263 
264             char* newDataStart = (char*)((u32)fwbFileInfo->writeBuffer + printedChars);
265             copyChars((char*)fwbFileInfo->writeBuffer, newDataStart, printedChars2 - printedChars);
266             fwbFileInfo->writeBufferWriteLocation = printedChars2 - printedChars;
267         }
268     }
269 
270     ASSERT(fwbFileInfo->writeBufferWriteLocation <= fwbFileInfo->writeBufferSize);
271 
272     if(fwbFileInfo->writeBufferWriteLocation == fwbFileInfo->writeBufferSize)
273     {
274         //the buffer is full, so flush it to disk.
275         DEMOFWBFlush(fwbFileInfo);
276     }
277 
278     return retVal;
279 }
280 
281 // --------------------------------------------------------
282