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