1 /*****************************************************************************
2 ** $Source: /cvsroot/bluemsx/blueMSX/Src/Utils/SaveState.c,v $
3 **
4 ** $Revision: 1.5 $
5 **
6 ** $Date: 2008/06/25 22:26:17 $
7 **
8 ** More info: http://www.bluemsx.com
9 **
10 ** Copyright (C) 2003-2006 Daniel Vik
11 **
12 ** This program is free software; you can redistribute it and/or modify
13 ** it under the terms of the GNU General Public License as published by
14 ** the Free Software Foundation; either version 2 of the License, or
15 ** (at your option) any later version.
16 **
17 ** This program is distributed in the hope that it will be useful,
18 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ** GNU General Public License for more details.
21 **
22 ** You should have received a copy of the GNU General Public License
23 ** along with this program; if not, write to the Free Software
24 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 **
26 ******************************************************************************
27 */
28 #include "SaveState.h"
29 #include "ziphelper.h"
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
33 
34 // Must be power of 2
35 #define ALLOC_BLOCK_SIZE 256
36 
37 struct SaveState {
38     UInt32 allocSize;
39     UInt32 size;
40     UInt32 offset;
41     UInt32 *buffer;
42     char   fileName[64];
43 };
44 
45 static char stateFile[512];
46 
tagFromName(const char * tagName)47 static UInt32 tagFromName(const char* tagName)
48 {
49     UInt32 tag = 0;
50     UInt32 mod = 1;
51 
52     while (*tagName) {
53         mod *= 19219;
54         tag += mod * *tagName++;
55     }
56 
57     return tag;
58 }
59 
60 #if 0
61 static void checkTag(SaveState* state, const char* tagName)
62 {
63     UInt32 tag = tagFromName(tagName);
64     UInt32 offset = 0;
65 
66     while (offset < state->offset) {
67         UInt32 elemTag = state->buffer[offset++];
68         UInt32 elemLen = state->buffer[offset++];
69         if (elemTag == tag) {
70             printf("Tag duplicated: %s : %d\n", state->fileName, tagName);
71         }
72         offset += (elemLen + sizeof(UInt32) - 1) / sizeof(UInt32);
73     }
74 }
75 #else
76 #define checkTag(state, tagName)
77 #endif
78 
79 static struct {
80     char fileName[64];
81     int  count;
82 } saveFileTable[128];
83 
84 static int tableCount;
85 
getIndexedFilename(const char * fileName)86 static char* getIndexedFilename(const char* fileName)
87 {
88     static char indexedFileName[64];
89     int i;
90 
91     for (i = 0; i < tableCount; i++) {
92         if (0 == strcmp(fileName, saveFileTable[i].fileName)) {
93             sprintf(indexedFileName, "%s_%.2d", fileName, ++saveFileTable[i].count);
94             return indexedFileName;
95         }
96     }
97     strcpy(saveFileTable[tableCount].fileName, fileName);
98     saveFileTable[tableCount++].count = 0;
99 
100     strcpy(indexedFileName, fileName);
101     strcat(indexedFileName, "_00");
102 
103     return indexedFileName;
104 }
105 
saveStateCreateForRead(const char * fileName)106 void saveStateCreateForRead(const char* fileName)
107 {
108     tableCount = 0;
109     strcpy(stateFile, fileName);
110     zipCacheReadOnlyZip(fileName);
111 }
112 
saveStateCreateForWrite(const char * fileName)113 void saveStateCreateForWrite(const char* fileName)
114 {
115     tableCount = 0;
116     strcpy(stateFile, fileName);
117 }
118 
saveStateDestroy(void)119 void saveStateDestroy(void)
120 {
121     zipCacheReadOnlyZip(NULL);
122 }
123 
saveStateOpenForRead(const char * fileName)124 SaveState* saveStateOpenForRead(const char* fileName) {
125     SaveState* state = (SaveState*)malloc(sizeof(SaveState));
126     Int32 size = 0;
127     void* buffer = zipLoadFile(stateFile, getIndexedFilename(fileName), &size);
128 
129     state->allocSize = size;
130     state->buffer = buffer;
131     state->size = size / sizeof(UInt32);
132     state->offset = 0;
133     state->fileName[0] = 0;
134 
135     return state;
136 }
137 
saveStateOpenForWrite(const char * fileName)138 SaveState* saveStateOpenForWrite(const char* fileName) {
139     SaveState* state = (SaveState*)malloc(sizeof(SaveState));
140 
141     state->size      = 0;
142     state->offset    = 0;
143     state->buffer    = NULL;
144     state->allocSize = 0;
145 
146     strcpy(state->fileName, getIndexedFilename(fileName));
147 
148     return state;
149 }
150 
saveStateClose(SaveState * state)151 void saveStateClose(SaveState* state) {
152     if (state->fileName[0]) {
153         zipSaveFile(stateFile, state->fileName, 1, state->buffer, state->offset * sizeof(UInt32));
154     }
155     if (state->buffer != NULL) {
156         free(state->buffer);
157     }
158     state->allocSize = 0;
159     free(state);
160 }
161 
stateExtendBuffer(SaveState * state,UInt32 extend)162 static void stateExtendBuffer(SaveState* state, UInt32 extend) {
163     state->size += extend;
164     if (state->size > state->allocSize)
165     {
166         state->allocSize = (state->size + ALLOC_BLOCK_SIZE - 1) & ~(ALLOC_BLOCK_SIZE - 1);
167         state->buffer = realloc(state->buffer, state->allocSize * sizeof(UInt32));
168     }
169 }
170 
saveStateSet(SaveState * state,const char * tagName,UInt32 value)171 void saveStateSet(SaveState* state, const char* tagName, UInt32 value)
172 {
173     checkTag(state, tagName);
174 
175     stateExtendBuffer(state, 3);
176     state->buffer[state->offset++] = tagFromName(tagName);
177     state->buffer[state->offset++] = sizeof(UInt32);
178     state->buffer[state->offset++] = value;
179 }
180 
saveStateSetBuffer(SaveState * state,const char * tagName,void * buffer,UInt32 length)181 void saveStateSetBuffer(SaveState* state, const char* tagName, void* buffer, UInt32 length)
182 {
183     checkTag(state, tagName);
184 
185     stateExtendBuffer(state, 2 + (length + sizeof(UInt32) - 1) / sizeof(UInt32));
186     state->buffer[state->offset++] = tagFromName(tagName);
187     state->buffer[state->offset++] = length;
188     memcpy(state->buffer + state->offset, buffer, length);
189     state->offset += (length + sizeof(UInt32) - 1) / sizeof(UInt32);
190 }
191 
saveStateGet(SaveState * state,const char * tagName,UInt32 defValue)192 UInt32 saveStateGet(SaveState* state, const char* tagName, UInt32 defValue)
193 {
194     UInt32 tag = tagFromName(tagName);
195     UInt32 startOffset = state->offset;
196     UInt32 offset = state->offset;
197     UInt32 value = defValue;
198     UInt32 wrapAround = 0;
199     UInt32 elemTag;
200     UInt32 elemLen;
201 
202     if (state->size == 0) {
203         return value;
204     }
205 
206     do {
207         elemTag = state->buffer[offset++];
208         elemLen = state->buffer[offset++];
209         if (elemTag == tag) {
210             value = state->buffer[offset];
211         }
212         offset += (elemLen + sizeof(UInt32) - 1) / sizeof(UInt32);
213         if (offset >= state->size) {
214             if (++wrapAround > 1) {
215                 break;
216             }
217             offset = 0;
218         }
219     } while (offset != startOffset && elemTag != tag);
220 
221     return value;
222 }
223 
saveStateGetBuffer(SaveState * state,const char * tagName,void * buffer,UInt32 length)224 void saveStateGetBuffer(SaveState* state, const char* tagName, void* buffer, UInt32 length)
225 {
226     UInt32 tag = tagFromName(tagName);
227     UInt32 startOffset = state->offset;
228     UInt32 offset = state->offset;
229     UInt32 wrapAround = 0;
230     UInt32 elemTag;
231     UInt32 elemLen;
232 
233     if (state->size == 0) {
234         return;
235     }
236 
237     do {
238         elemTag = state->buffer[offset++];
239         elemLen = state->buffer[offset++];
240         if (elemTag == tag) {
241             memcpy(buffer, state->buffer + offset, length < elemLen ? length : elemLen);
242         }
243         offset += (elemLen + sizeof(UInt32) - 1) / sizeof(UInt32);
244         if (offset >= state->size) {
245             if (++wrapAround > 1) {
246                 break;
247             }
248             offset = 0;
249         }
250     } while (offset != startOffset && elemTag != tag);
251 
252     state->offset = offset;
253 }
254