xref: /reactos/base/applications/mstsc/pstcache.c (revision 84ccccab)
1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    Persistent Bitmap Cache routines
4    Copyright (C) Jeroen Meijer <jeroen@oldambt7.com> 2004-2008
5 
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, either version 3 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "precomp.h"
21 
22 #define MAX_CELL_SIZE		0x1000	/* pixels */
23 
24 #define IS_PERSISTENT(id) (id < 8 && g_pstcache_fd[id] > 0)
25 
26 extern int g_server_depth;
27 extern RD_BOOL g_bitmap_cache;
28 extern RD_BOOL g_bitmap_cache_persist_enable;
29 extern RD_BOOL g_bitmap_cache_precache;
30 
31 int g_pstcache_fd[8];
32 int g_pstcache_Bpp;
33 RD_BOOL g_pstcache_enumerated = False;
34 uint8 zero_key[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
35 
36 
37 /* Update mru stamp/index for a bitmap */
38 void
39 pstcache_touch_bitmap(uint8 cache_id, uint16 cache_idx, uint32 stamp)
40 {
41 	int fd;
42 
43 	if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS)
44 		return;
45 
46 	fd = g_pstcache_fd[cache_id];
47 	rd_lseek_file(fd, 12 + cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
48 	rd_write_file(fd, &stamp, sizeof(stamp));
49 }
50 
51 /* Load a bitmap from the persistent cache */
52 RD_BOOL
53 pstcache_load_bitmap(uint8 cache_id, uint16 cache_idx)
54 {
55 	uint8 *celldata;
56 	int fd;
57 	CELLHEADER cellhdr;
58 	RD_HBITMAP bitmap;
59 
60 	if (!g_bitmap_cache_persist_enable)
61 		return False;
62 
63 	if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS)
64 		return False;
65 
66 	fd = g_pstcache_fd[cache_id];
67 	rd_lseek_file(fd, cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
68 	rd_read_file(fd, &cellhdr, sizeof(CELLHEADER));
69 	celldata = (uint8 *) xmalloc(cellhdr.length);
70 	rd_read_file(fd, celldata, cellhdr.length);
71 
72 	bitmap = ui_create_bitmap(cellhdr.width, cellhdr.height, celldata);
73 	DEBUG(("Load bitmap from disk: id=%d, idx=%d, bmp=%p)\n", cache_id, cache_idx, bitmap));
74 	cache_put_bitmap(cache_id, cache_idx, bitmap);
75 
76 	xfree(celldata);
77 	return True;
78 }
79 
80 /* Store a bitmap in the persistent cache */
81 RD_BOOL
82 pstcache_save_bitmap(uint8 cache_id, uint16 cache_idx, uint8 * key,
83 		     uint8 width, uint8 height, uint16 length, uint8 * data)
84 {
85 	int fd;
86 	CELLHEADER cellhdr;
87 
88 	if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS)
89 		return False;
90 
91 	memcpy(cellhdr.key, key, sizeof(HASH_KEY));
92 	cellhdr.width = width;
93 	cellhdr.height = height;
94 	cellhdr.length = length;
95 	cellhdr.stamp = 0;
96 
97 	fd = g_pstcache_fd[cache_id];
98 	rd_lseek_file(fd, cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
99 	rd_write_file(fd, &cellhdr, sizeof(CELLHEADER));
100 	rd_write_file(fd, data, length);
101 
102 	return True;
103 }
104 
105 /* List the bitmap keys from the persistent cache file */
106 int
107 pstcache_enumerate(uint8 id, HASH_KEY * keylist)
108 {
109 	int fd, n;
110 	uint16 idx;
111 	sint16 mru_idx[0xa00];
112 	uint32 mru_stamp[0xa00];
113 	CELLHEADER cellhdr;
114 
115 	if (!(g_bitmap_cache && g_bitmap_cache_persist_enable && IS_PERSISTENT(id)))
116 		return 0;
117 
118 	/* The server disconnects if the bitmap cache content is sent more than once */
119 	if (g_pstcache_enumerated)
120 		return 0;
121 
122 	DEBUG_RDP5(("Persistent bitmap cache enumeration... "));
123 	for (idx = 0; idx < BMPCACHE2_NUM_PSTCELLS; idx++)
124 	{
125 		fd = g_pstcache_fd[id];
126 		rd_lseek_file(fd, idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
127 		if (rd_read_file(fd, &cellhdr, sizeof(CELLHEADER)) <= 0)
128 			break;
129 
130 		if (memcmp(cellhdr.key, zero_key, sizeof(HASH_KEY)) != 0)
131 		{
132 			memcpy(keylist[idx], cellhdr.key, sizeof(HASH_KEY));
133 
134 			/* Pre-cache (not possible for 8 bit colour depth cause it needs a colourmap) */
135 			if (g_bitmap_cache_precache && cellhdr.stamp && g_server_depth > 8)
136 				pstcache_load_bitmap(id, idx);
137 
138 			/* Sort by stamp */
139 			for (n = idx; n > 0 && cellhdr.stamp < mru_stamp[n - 1]; n--)
140 			{
141 				mru_idx[n] = mru_idx[n - 1];
142 				mru_stamp[n] = mru_stamp[n - 1];
143 			}
144 
145 			mru_idx[n] = idx;
146 			mru_stamp[n] = cellhdr.stamp;
147 		}
148 		else
149 		{
150 			break;
151 		}
152 	}
153 
154 	DEBUG_RDP5(("%d cached bitmaps.\n", idx));
155 
156 	cache_rebuild_bmpcache_linked_list(id, mru_idx, idx);
157 	g_pstcache_enumerated = True;
158 	return idx;
159 }
160 
161 /* initialise the persistent bitmap cache */
162 RD_BOOL
163 pstcache_init(uint8 cache_id)
164 {
165 	int fd;
166 	char filename[256];
167 
168 	if (g_pstcache_enumerated)
169 		return True;
170 
171 	g_pstcache_fd[cache_id] = 0;
172 
173 	if (!(g_bitmap_cache && g_bitmap_cache_persist_enable))
174 		return False;
175 
176 	if (!rd_pstcache_mkdir())
177 	{
178 		DEBUG(("failed to get/make cache directory!\n"));
179 		return False;
180 	}
181 
182 	g_pstcache_Bpp = (g_server_depth + 7) / 8;
183 	sprintf(filename, "cache/pstcache_%d_%d", cache_id, g_pstcache_Bpp);
184 	DEBUG(("persistent bitmap cache file: %s\n", filename));
185 
186 	fd = rd_open_file(filename);
187 	if (fd == -1)
188 		return False;
189 
190 	if (!rd_lock_file(fd, 0, 0))
191 	{
192 		warning("Persistent bitmap caching is disabled. (The file is already in use)\n");
193 		rd_close_file(fd);
194 		return False;
195 	}
196 
197 	g_pstcache_fd[cache_id] = fd;
198 	return True;
199 }
200