/* PstCache.java
* Component: ProperJavaRDP
*
* Revision: $Revision: 1.4 $
* Author: $Author: telliott $
* Date: $Date: 2005/09/27 14:15:39 $
*
* Copyright (c) 2005 Propero Limited
*
* Purpose: Handle persistent caching
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* (See gpl.txt for details of the GNU General Public License.)
*
*/
package org.jopenray.rdp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.log4j.Logger;
public class PstCache {
protected static Logger logger = Logger.getLogger(Rdp.class);
public static final int MAX_CELL_SIZE = 0x1000; /* pixels */
protected static boolean IS_PERSISTENT(int id){
return (id < 8 && g_pstcache_fd[id] != null);
}
static int g_stamp;
static File[] g_pstcache_fd = new File[8];
static int g_pstcache_Bpp;
static boolean g_pstcache_enumerated = false;
/* Update usage info for a bitmap */
protected static void touchBitmap(int cache_id, int cache_idx, int stamp)
{
logger.info("PstCache.touchBitmap");
FileOutputStream fd;
if (!IS_PERSISTENT(cache_id) || cache_idx >= Rdp.BMPCACHE2_NUM_PSTCELLS)
return;
try {
fd = new FileOutputStream(g_pstcache_fd[cache_id]);
fd.write(toBigEndian32(stamp), 12 + cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + CELLHEADER.size()),4);
//rd_lseek_file(fd, 12 + cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); // this seems to do nothing (return 0) in rdesktop
//rd_write_file(fd, &stamp, sizeof(stamp)); // same with this one???
} catch (IOException e) {
return;
}
}
private static byte[] toBigEndian32(int value){
byte[] out = new byte[4];
out[0] = (byte) (value & 0xFF);
out[1] = (byte) (value & 0xFF00);
out[2] = (byte) (value & 0xFF0000);
out[3] = (byte) (value & 0xFF000000);
return out;
}
/* Load a bitmap from the persistent cache */
static boolean pstcache_load_bitmap(int cache_id, int cache_idx) throws IOException, RdesktopException
{
logger.info("PstCache.pstcache_load_bitmap");
byte[] celldata = null;
FileInputStream fd;
//CELLHEADER cellhdr;
Bitmap bitmap;
byte[] cellHead = null;
if (!Options.persistent_bitmap_caching)
return false;
if (!IS_PERSISTENT(cache_id) || cache_idx >= Rdp.BMPCACHE2_NUM_PSTCELLS)
return false;
fd = new FileInputStream(g_pstcache_fd[cache_id]);
int offset = cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + CELLHEADER.size());
fd.read(cellHead, offset, CELLHEADER.size());
CELLHEADER c = new CELLHEADER(cellHead);
//rd_lseek_file(fd, cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
//rd_read_file(fd, &cellhdr, sizeof(CELLHEADER));
//celldata = (uint8 *) xmalloc(cellhdr.length);
//rd_read_file(fd, celldata, cellhdr.length);
celldata = new byte[c.length];
fd.read(celldata);
logger.debug("Loading bitmap from disk (" + cache_id + ":" + cache_idx + ")\n");
bitmap = new Bitmap(celldata,c.width,c.height,0,0,Options.Bpp);
//bitmap = ui_create_bitmap(cellhdr.width, cellhdr.height, celldata);
Orders.cache.putBitmap(cache_id,cache_idx,bitmap,c.stamp);
//xfree(celldata);
return true;
}
/* Store a bitmap in the persistent cache */
static boolean pstcache_put_bitmap(int cache_id, int cache_idx, byte[] bitmap_id, int width, int height, int length, byte[] data) throws IOException
{
logger.info("PstCache.pstcache_put_bitmap");
FileOutputStream fd;
CELLHEADER cellhdr = new CELLHEADER();
if (!IS_PERSISTENT(cache_id) || cache_idx >= Rdp.BMPCACHE2_NUM_PSTCELLS)
return false;
cellhdr.bitmap_id = bitmap_id;
//memcpy(cellhdr.bitmap_id, bitmap_id, 8/* sizeof(BITMAP_ID) */);
cellhdr.width = width;
cellhdr.height = height;
cellhdr.length = length;
cellhdr.stamp = 0;
fd = new FileOutputStream(g_pstcache_fd[cache_id]);
int offset = cache_idx * (Options.Bpp * MAX_CELL_SIZE + CELLHEADER.size());
fd.write(cellhdr.toBytes(),offset,CELLHEADER.size());
fd.write(data);
//rd_lseek_file(fd, cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER)));
//rd_write_file(fd, &cellhdr, sizeof(CELLHEADER));
//rd_write_file(fd, data, length);
return true;
}
/* list the bitmaps from the persistent cache file */
static int pstcache_enumerate(int cache_id, int[] idlist) throws IOException, RdesktopException
{
logger.info("PstCache.pstcache_enumerate");
FileInputStream fd;
int n, c = 0;
CELLHEADER cellhdr = null;
if (!(Options.bitmap_caching && Options.persistent_bitmap_caching && IS_PERSISTENT(cache_id)))
return 0;
/* The server disconnects if the bitmap cache content is sent more than once */
if (g_pstcache_enumerated)
return 0;
logger.debug("pstcache enumeration... ");
for (n = 0; n < Rdp.BMPCACHE2_NUM_PSTCELLS; n++)
{
fd = new FileInputStream(g_pstcache_fd[cache_id]);
byte[] cellhead_data = new byte[CELLHEADER.size()];
if (fd.read(cellhead_data,n * (g_pstcache_Bpp * MAX_CELL_SIZE + CELLHEADER.size()), CELLHEADER.size()) <= 0)
break;
cellhdr = new CELLHEADER(cellhead_data);
int result = 0;
for(int i = 0; i < cellhdr.bitmap_id.length; i++){
result += cellhdr.bitmap_id[i];
}
if (result != 0)
{
for(int i = 0; i < 8; i++){
idlist[(n * 8) + i] = cellhdr.bitmap_id[i];
}
if (cellhdr.stamp != 0)
{
/* Pre-caching is not possible with 8bpp because a colourmap
* is needed to load them */
if (Options.precache_bitmaps && (Options.server_bpp > 8))
{
if (pstcache_load_bitmap(cache_id, n))
c++;
}
g_stamp = Math.max(g_stamp, cellhdr.stamp);
}
}
else
{
break;
}
}
logger.info(n + " bitmaps in persistent cache, " + c + " bitmaps loaded in memory\n");
g_pstcache_enumerated = true;
return n;
}
/* initialise the persistent bitmap cache */
static boolean pstcache_init(int cache_id)
{
//int fd;
String filename;
if (g_pstcache_enumerated)
return true;
g_pstcache_fd[cache_id] = null;
if (!(Options.bitmap_caching && Options.persistent_bitmap_caching))
return false;
g_pstcache_Bpp = Options.Bpp;
filename = "./cache/pstcache_" + cache_id + "_" + g_pstcache_Bpp;
logger.debug("persistent bitmap cache file: " + filename);
File cacheDir = new File("./cache/");
if(!cacheDir.exists() && !cacheDir.mkdir()){
logger.warn("failed to get/make cache directory");
return false;
}
File f = new File(filename);
try {
if(!f.exists() && !f.createNewFile()){
logger.warn("Could not create cache file");
return false;
}
} catch (IOException e) {
return false;
}
/* if (!rd_lock_file(fd, 0, 0))
{
logger.warn("Persistent bitmap caching is disabled. (The file is already in use)\n");
rd_close_file(fd);
return false;
}*/
g_pstcache_fd[cache_id] = f;
return true;
}
}
/* Header for an entry in the persistent bitmap cache file */
class CELLHEADER
{
byte[] bitmap_id = new byte[8]; // int8 *
int width, height; // int8
int length; // int16
int stamp; // int32
static int size(){
return 8*8 + 8*2 + 16 + 32;
}
public CELLHEADER(){
}
public CELLHEADER(byte[] data){
for(int i = 0; i < bitmap_id.length; i++) bitmap_id[i] = data[i];
width = data[bitmap_id.length];
height = data[bitmap_id.length + 1];
length = (data[bitmap_id.length + 2] >> 8) + data[bitmap_id.length + 3];
stamp = (data[bitmap_id.length + 6] >> 24) + (data[bitmap_id.length + 6] >> 16) +(data[bitmap_id.length + 6] >> 8) + data[bitmap_id.length + 7];
}
public byte[] toBytes(){
return null;
}
}