added automatic memory usage limitation to engine snapshot management
[rocksndiamonds.git] / src / libgame / snapshot.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // snapshot.c
10 // ============================================================================
11
12 #include "snapshot.h"
13
14
15 #ifdef DEBUG
16 #define DEBUG_SNAPSHOTS                 0
17 #endif
18
19 #if DEBUG_SNAPSHOTS
20 #define MAX_SNAPSHOT_BYTES              (50 * 1024 * 1024)
21 #else
22 #define MAX_SNAPSHOT_BYTES              (500 * 1024 * 1024)
23 #endif
24
25 static ListNode *snapshot_single = NULL;
26 static ListNode *snapshot_list = NULL;
27 static ListNode *snapshot_current = NULL;
28
29 static int num_snapshots = 0;
30 static int num_snapshot_buffers = 0;
31 static int num_snapshot_bytes = 0;
32 static int next_snapshot_key = 0;
33
34
35 // -----------------------------------------------------------------------------
36 // functions for handling buffers for a single snapshot
37 // -----------------------------------------------------------------------------
38
39 void SaveSnapshotBuffer(ListNode **snapshot_buffers, void *buffer, int size)
40 {
41   struct SnapshotNodeInfo *bi =
42     checked_calloc(sizeof(struct SnapshotNodeInfo));
43
44   bi->buffer_orig = buffer;
45   bi->buffer_copy = checked_malloc(size);
46   bi->size = size;
47
48   memcpy(bi->buffer_copy, buffer, size);
49
50   addNodeToList(snapshot_buffers, NULL, bi);
51
52   num_snapshot_buffers++;
53   num_snapshot_bytes += size;
54 }
55
56 static void LoadSnapshotBuffer(struct SnapshotNodeInfo *bi)
57 {
58   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
59 }
60
61 void LoadSnapshotBuffers(ListNode *snapshot_buffers)
62 {
63   while (snapshot_buffers != NULL)
64   {
65     LoadSnapshotBuffer((struct SnapshotNodeInfo *)snapshot_buffers->content);
66
67     snapshot_buffers = snapshot_buffers->next;
68   }
69 }
70
71 static void FreeSnapshotBuffer(void *bi_raw)
72 {
73   struct SnapshotNodeInfo *bi = (struct SnapshotNodeInfo *)bi_raw;
74
75   num_snapshot_buffers--;
76   num_snapshot_bytes -= bi->size;
77
78   checked_free(bi->buffer_copy);
79   checked_free(bi);
80 }
81
82 void FreeSnapshotBuffers(ListNode *snapshot_buffers)
83 {
84   while (snapshot_buffers != NULL)
85     deleteNodeFromList(&snapshot_buffers, NULL, FreeSnapshotBuffer);
86 }
87
88 // -----------------------------------------------------------------------------
89 // functions for handling single shapshot or list of snapshots
90 // -----------------------------------------------------------------------------
91
92 static void FreeSnapshot(void *snapshot_buffers_ptr)
93 {
94   FreeSnapshotBuffers(snapshot_buffers_ptr);
95 }
96
97 void FreeSnapshotSingle()
98 {
99   FreeSnapshotBuffers(snapshot_single);
100
101   snapshot_single = NULL;
102 }
103
104 static void FreeSnapshotList_UpToNode(ListNode *node)
105 {
106   while (snapshot_list != node)
107   {
108 #if DEBUG_SNAPSHOTS
109     printf("::: FreeSnapshotList_*() [%s, %d, %d]\n",
110            snapshot_list->key, num_snapshot_buffers, num_snapshot_bytes);
111 #endif
112
113     deleteNodeFromList(&snapshot_list, snapshot_list->key, FreeSnapshot);
114
115     num_snapshots--;
116     next_snapshot_key = (snapshot_list ? atoi(snapshot_list->key) + 1 : 0);
117   }
118 }
119
120 void FreeSnapshotList()
121 {
122 #if DEBUG_SNAPSHOTS
123   printf("::: FreeSnapshotList()\n");
124 #endif
125
126   FreeSnapshotList_UpToNode(NULL);
127
128   num_snapshots = 0;
129   num_snapshot_buffers = 0;
130   num_snapshot_bytes = 0;
131   next_snapshot_key = 0;
132
133   snapshot_current = NULL;
134 }
135
136 void ReduceSnapshotList()
137 {
138 #if DEBUG_SNAPSHOTS
139   printf("::: (Reducing number of snapshots from %d ",
140          num_snapshots);
141 #endif
142
143   // maximum number of snapshots exceeded -- thin out list of snapshots
144   ListNode *node = snapshot_list;
145   int num_snapshots_to_skip = num_snapshots / 10;
146
147   // do not remove the newest snapshots from the list
148   while (node && num_snapshots_to_skip--)
149     node = node->next;
150
151   // remove every second snapshot from the remaining list
152   while (node)
153   {
154     // never delete the first list node (snapshot at game start)
155     if (node->next == NULL)
156       break;
157
158     // in alternation, delete one node from the list ...
159     deleteNodeFromList(&node, node->key, FreeSnapshot);
160     num_snapshots--;
161
162     // ... and keep one node (which always exists here)
163     node = node->next;
164   }
165
166 #if DEBUG_SNAPSHOTS
167   printf("to %d.)\n", num_snapshots);
168
169 #if 0
170   node = snapshot_list;
171   while (node)
172   {
173     printf("::: key: %s\n", node->key);
174     node = node->next;
175   }
176 #endif
177 #endif
178 }
179
180 void SaveSnapshotSingle(ListNode *snapshot_buffers)
181 {
182   if (snapshot_single)
183     FreeSnapshotSingle();
184
185   snapshot_single = snapshot_buffers;
186 }
187
188 void SaveSnapshotToList(ListNode *snapshot_buffers)
189 {
190   if (snapshot_current != snapshot_list)
191     FreeSnapshotList_UpToNode(snapshot_current);
192
193 #if DEBUG_SNAPSHOTS
194   printf("::: SaveSnapshotToList() [%d] [%d snapshots, %d buffers, %d bytes]\n",
195          next_snapshot_key, num_snapshots,
196          num_snapshot_buffers, num_snapshot_bytes);
197 #endif
198
199   addNodeToList(&snapshot_list, i_to_a(next_snapshot_key),
200                 snapshot_buffers);
201
202   snapshot_current = snapshot_list;
203
204   num_snapshots++;
205   next_snapshot_key++;
206
207   if (num_snapshot_bytes > MAX_SNAPSHOT_BYTES)
208     ReduceSnapshotList();
209 }
210
211 boolean LoadSnapshotSingle()
212 {
213   if (snapshot_single)
214   {
215     LoadSnapshotBuffers(snapshot_single);
216
217     return TRUE;
218   }
219
220   return FALSE;
221 }
222
223 boolean LoadSnapshotFromList_Older(int steps)
224 {
225   if (snapshot_current && snapshot_current->next)
226   {
227     while (snapshot_current->next && steps--)
228       snapshot_current = snapshot_current->next;
229
230     LoadSnapshotBuffers(snapshot_current->content);
231
232 #if DEBUG_SNAPSHOTS
233     printf("::: LoadSnapshotFromList_Older() [%s]\n", snapshot_current->key);
234 #endif
235
236     return TRUE;
237   }
238
239   return FALSE;
240 }
241
242 boolean LoadSnapshotFromList_Newer(int steps)
243 {
244   if (snapshot_current && snapshot_current->prev)
245   {
246     while (snapshot_current->prev && steps--)
247       snapshot_current = snapshot_current->prev;
248
249     LoadSnapshotBuffers(snapshot_current->content);
250
251 #if DEBUG_SNAPSHOTS
252     printf("::: LoadSnapshotFromList_Newer() [%s]\n", snapshot_current->key);
253 #endif
254
255     return TRUE;
256   }
257
258   return FALSE;
259 }
260
261 boolean CheckSnapshotList()
262 {
263   return (snapshot_list ? TRUE : FALSE);
264 }