1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    Persistent Bitmap Cache routines
4    Copyright (C) Jeroen Meijer 2004-2005
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 2 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 along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "rdesktop.h"
22 
23 #define MAX_CELL_SIZE		0x1000	/* pixels */
24 
25 #define IS_PERSISTENT(id) (id < 8 && This->pstcache_fd[id] > 0)
26 
27 const uint8 zero_key[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
28 
29 
30 /* Update mru stamp/index for a bitmap */
31 void
32 pstcache_touch_bitmap(RDPCLIENT * This, uint8 cache_id, uint16 cache_idx, uint32 stamp)
33 {
34 	int fd;
35 
36 	if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS)
37 		return;
38 
39 	fd = This->pstcache_fd[cache_id];
40 	rd_lseek_file(fd, 12 + cache_idx * (This->pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
41 	rd_write_file(fd, &stamp, sizeof(stamp));
42 }
43 
44 /* Load a bitmap from the persistent cache */
45 BOOL
46 pstcache_load_bitmap(RDPCLIENT * This, uint8 cache_id, uint16 cache_idx)
47 {
48 	uint8 *celldata;
49 	int fd;
50 	CELLHEADER cellhdr;
51 	HBITMAP bitmap;
52 
53 	if (!This->bitmap_cache_persist_enable)
54 		return False;
55 
56 	if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS)
57 		return False;
58 
59 	fd = This->pstcache_fd[cache_id];
60 	rd_lseek_file(fd, cache_idx * (This->pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
61 	rd_read_file(fd, &cellhdr, sizeof(CELLHEADER));
62 	celldata = (uint8 *) malloc(cellhdr.length);
63 
64 	if(celldata == NULL)
65 		return False;
66 
67 	rd_read_file(fd, celldata, cellhdr.length);
68 
69 	bitmap = ui_create_bitmap(This, cellhdr.width, cellhdr.height, celldata);
70 	DEBUG(("Load bitmap from disk: id=%d, idx=%d, bmp=0x%x)\n", cache_id, cache_idx, bitmap));
71 	cache_put_bitmap(This, cache_id, cache_idx, bitmap);
72 
73 	free(celldata);
74 	return True;
75 }
76 
77 /* Store a bitmap in the persistent cache */
78 BOOL
79 pstcache_save_bitmap(RDPCLIENT * This, uint8 cache_id, uint16 cache_idx, uint8 * key,
80 		     uint8 width, uint8 height, uint16 length, uint8 * data)
81 {
82 	int fd;
83 	CELLHEADER cellhdr;
84 
85 	if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS)
86 		return False;
87 
88 	memcpy(cellhdr.key, key, sizeof(HASH_KEY));
89 	cellhdr.width = width;
90 	cellhdr.height = height;
91 	cellhdr.length = length;
92 	cellhdr.stamp = 0;
93 
94 	fd = This->pstcache_fd[cache_id];
95 	rd_lseek_file(fd, cache_idx * (This->pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
96 	rd_write_file(fd, &cellhdr, sizeof(CELLHEADER));
97 	rd_write_file(fd, data, length);
98 
99 	return True;
100 }
101 
102 /* List the bitmap keys from the persistent cache file */
103 int
104 pstcache_enumerate(RDPCLIENT * This, uint8 id, HASH_KEY * keylist)
105 {
106 	int fd, n;
107 	uint16 idx;
108 	sint16 mru_idx[0xa00];
109 	uint32 mru_stamp[0xa00];
110 	CELLHEADER cellhdr;
111 
112 	if (!(This->bitmap_cache && This->bitmap_cache_persist_enable && IS_PERSISTENT(id)))
113 		return 0;
114 
115 	/* The server disconnects if the bitmap cache content is sent more than once */
116 	if (This->pstcache_enumerated)
117 		return 0;
118 
119 	DEBUG_RDP5(("Persistent bitmap cache enumeration... "));
120 	for (idx = 0; idx < BMPCACHE2_NUM_PSTCELLS; idx++)
121 	{
122 		fd = This->pstcache_fd[id];
123 		rd_lseek_file(fd, idx * (This->pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
124 		if (rd_read_file(fd, &cellhdr, sizeof(CELLHEADER)) <= 0)
125 			break;
126 
127 		if (memcmp(cellhdr.key, zero_key, sizeof(HASH_KEY)) != 0)
128 		{
129 			memcpy(keylist[idx], cellhdr.key, sizeof(HASH_KEY));
130 
131 			/* Pre-cache (not possible for 8 bit colour depth cause it needs a colourmap) */
132 			if (This->bitmap_cache_precache && cellhdr.stamp && This->server_depth > 8)
133 				pstcache_load_bitmap(This, id, idx);
134 
135 			/* Sort by stamp */
136 			for (n = idx; n > 0 && cellhdr.stamp < mru_stamp[n - 1]; n--)
137 			{
138 				mru_idx[n] = mru_idx[n - 1];
139 				mru_stamp[n] = mru_stamp[n - 1];
140 			}
141 
142 			mru_idx[n] = idx;
143 			mru_stamp[n] = cellhdr.stamp;
144 		}
145 		else
146 		{
147 			break;
148 		}
149 	}
150 
151 	DEBUG_RDP5(("%d cached bitmaps.\n", idx));
152 
153 	cache_rebuild_bmpcache_linked_list(This, id, mru_idx, idx);
154 	This->pstcache_enumerated = True;
155 	return idx;
156 }
157 
158 /* initialise the persistent bitmap cache */
159 BOOL
160 pstcache_init(RDPCLIENT * This, uint8 cache_id)
161 {
162 	int fd;
163 	char filename[256];
164 
165 	if (This->pstcache_enumerated)
166 		return True;
167 
168 	This->pstcache_fd[cache_id] = 0;
169 
170 	if (!(This->bitmap_cache && This->bitmap_cache_persist_enable))
171 		return False;
172 
173 	if (!rd_pstcache_mkdir())
174 	{
175 		DEBUG(("failed to get/make cache directory!\n"));
176 		return False;
177 	}
178 
179 	This->pstcache_Bpp = (This->server_depth + 7) / 8;
180 	sprintf(filename, "cache/pstcache_%d_%d", cache_id, This->pstcache_Bpp);
181 	DEBUG(("persistent bitmap cache file: %s\n", filename));
182 
183 	fd = rd_open_file(filename);
184 	if (fd == -1)
185 		return False;
186 
187 	if (!rd_lock_file(fd, 0, 0))
188 	{
189 		warning("Persistent bitmap caching is disabled. (The file is already in use)\n");
190 		rd_close_file(fd);
191 		return False;
192 	}
193 
194 	This->pstcache_fd[cache_id] = fd;
195 	return True;
196 }
197