嗯,这个也是快一年前写的了。当时在澄空看到有人想要解手机少女的文件,就随便写了个程序来处理这个script.arc与string.arc。另外几个arc文件用下面的程序处理不了,因为文件头里的信息没有被正确的处理。嘛,后来[url=http://plaza.rakuten.co.jp/asmodean]asmodean[/url]也放出了处理这个游戏的工具而且是用C++写的,我就没必要继续把这个程序做完。
也是由于使用了[url=http://www.hotpixel.net/software.html]BlowfishJ[/url]的BinConverter.java,所以以下代码以[url=http://www.gnu.org/copyleft/lesser.html]LGPL[/url]许可证发布。
Archiver.java:
IndexEntry.java:
LZSS.java:
Binconverter.java:
也是由于使用了[url=http://www.hotpixel.net/software.html]BlowfishJ[/url]的BinConverter.java,所以以下代码以[url=http://www.gnu.org/copyleft/lesser.html]LGPL[/url]许可证发布。
Archiver.java:
/*
* @(#)Archiver.java 2007/05/15
* Written by rednaxela / FX
*/
import java.io.*;
import java.util.*;
/*
Data structures:
//--------------------------------
typedef struct tagHEADER {
DWORD id; // IDENTIFIER, BE AD CA A8, little-endian ("ARCW" xor 0xFF)
DWORD ver; // VERSION, locked to 0x00
DWORD fileSize; // total size of current archive
DWORD data_offset; // begin offset of file data
} HEADER; // 16 bytes
typedef struct tagSECINFO {
DWORD sectionLength; // sectionLength = len(SECINFO)(=0x0C) + len(SECDATA)
DWORD flags; // flags: NOT_MSK = 1, LZSS_MSK = 2
DWORD origSize; // original size of SECDATA after inflation
} SECINFO; // 12 bytes
typedef struct tagSECDATA {
BYTE data[SECINFO.sectionLength - 12];
// when SECINFO.flags = NOT_MSK | LZSS_MSK, the contents is both encrypted by NOT
// and compressed by LZSS.
} SECDATA;
struct INDEXBLOCK1 {
struct INDEXENTRY {
DWORD offset; // actualOfs = offset + totalHeaderLength - 0x0C
DWORD length; // length in archive
DWORD flags; // NOT_MSK = 1, LZSS_MSK = 2
DWORD origLen; // original file length
};
DWORD reserved1; // some sort of flag I guess
DWORD reserved2; // seems to be always zero
DWORD entryCount; // number of entries in this block
DWORD blockLength; // length of the remaining part of this block
DWORD nameOffset[entryCount]; // offset relative to nameSecData after decode and decompression
INDEXENTRY entry[entryCount]; // TOC entrys
};
struct INDEXBLOCK2 {
}; // structure is unknown
//--------------------------------
arc file:
HEADER header; // 1 header per archive
SECINFO nameSecInfo; // 1st of SECINFO in archive
SECDATA nameSecData; // 1st of SECDATA in archive, contents of which are filenames in UNICODE
SECINFO indexSecInfo; // 2nd of SECINFO in archive
SECDATA indexSecData; // 2nd of SECDATA in archive, contents of which indicates offset, length, flag info of file data.
bytes data[]; // remaining part of archive, which is file data
//--------------------------------
indexSecData after decode and decompression:
DWORD blockCount;
INDEXBLOCK1 block1;
// INDEXBLOCK2 block2; // this is where I've got trouble figuring out.
*/
/**
* Demonstrating archive operations on *.arc files as seen in Ketai-s.
*/
public class Archiver {
static final int IDENTIFIER = 0xA8BCADBE;
static final int VERSION = 0x00;
static final int NOT_MSK = 1;
static final int LZSS_MSK = 2;
static final String USAGE = "Usage: java Archiver [option] srcPath dstPath\n"
+ "options:\n"
+ "[l]ist\n"
+ "[e]xtract\n"
+ "[a]rchive";
static ArrayList<IndexEntry> index = null;
/**
* the application entry point
* @param args (command line) parameters
*/
public static void main(String[] args) throws Exception {
// check command line arguments
if (args.length != 3) error(USAGE);
if ("l".equals(args[0].trim())) { // extract files from archive
String srcPath = args[1].trim();
String dstPath = args[2].trim();
if (srcPath.length() == 0) error("2nd argument not exist.");
if (dstPath.length() == 0) error("3rd argument not exist.");
listFiles(srcPath, dstPath);
} else if ("e".equals(args[0].trim())) { // extract files from archive
String srcPath = args[1].trim();
String dstPath = args[2].trim();
if (srcPath.length() == 0) error("2nd argument not exist.");
if (dstPath.length() == 0) error("3rd argument not exist.");
extractFiles(srcPath, dstPath);
} else if ("a".equals(args[0].trim())) { // pack files into archive
String srcPath = args[1].trim();
String dstPath = args[2].trim();
if (srcPath.length() == 0) error("2nd argument not exist.");
if (dstPath.length() == 0) error("3rd argument not exist.");
packFiles(srcPath, dstPath);
} else error(USAGE);
}
private static void listFiles(String srcFile, String listFile) throws Exception {
// open source archive
File arc = new File(srcFile);
if (!arc.exists()) error("Archive " + srcFile + " doesn't exist");
long contentOfs = -1L;
FileInputStream fis = new FileInputStream(arc);
DataInputStream dis = new DataInputStream(fis);
index = new ArrayList<IndexEntry>();
// match archive IDENTIFIER
int id = flipEndian(dis.readInt());
if (IDENTIFIER != id) error("Archive file not supported. Unexpected identifier 0x" + Integer.toHexString(id).toUpperCase());
// match archive version
int ver = flipEndian(dis.readInt());
if (VERSION != ver) error("Archive file not supported. Unexpected version.");
// match file size
int fsize = flipEndian(dis.readInt());
if ((int)(arc.length()) != fsize) error("Archive file not supported. Unexpected file size.");
// probable checksum
int checksum = flipEndian(dis.readInt());
// if (checksum(arc) != checksum)) error("Archive file not supported. Unexpected checksum.");
System.err.println("CHECKSUM: 0x" + Integer.toHexString(checksum).toUpperCase());
// read index entries
byte[] nameBuf = readIndexBlock(fis);
byte[] infoBuf = readIndexBlock(fis);
parseIndex(nameBuf, infoBuf);
// extract files
int baseOfs = (int)(fis.getChannel().position()); // check this, might not be correct
PrintStream out = new PrintStream(new FileOutputStream(listFile));
System.setOut(out); // stdout redirection
for (IndexEntry entry : index) {
// print file info
printFileInfo(entry, baseOfs);
}
}
private static void extractFiles(String srcFile, String dstDir) throws Exception {
// open source archive
File arc = new File(srcFile);
if (!arc.exists()) error("Archive " + srcFile + " doesn't exist");
long contentOfs = -1L;
FileInputStream fis = new FileInputStream(arc);
DataInputStream dis = new DataInputStream(fis);
index = new ArrayList<IndexEntry>();
// match archive IDENTIFIER
int id = flipEndian(dis.readInt());
if (IDENTIFIER != id) error("Archive file not supported. Unexpected identifier 0x" + Integer.toHexString(id).toUpperCase());
// match archive version
int ver = flipEndian(dis.readInt());
if (VERSION != ver) error("Archive file not supported. Unexpected version.");
// match file size
int fsize = flipEndian(dis.readInt());
if ((int)(arc.length()) != fsize) error("Archive file not supported. Unexpected file size.");
// probable checksum
int checksum = flipEndian(dis.readInt());
// if (checksum(arc) != checksum)) error("Archive file not supported. Unexpected checksum.");
System.err.println("CHECKSUM: 0x" + Integer.toHexString(checksum).toUpperCase());
// read index entries
byte[] nameBuf = readIndexBlock(fis);
byte[] infoBuf = readIndexBlock(fis);
parseIndex(nameBuf, infoBuf);
// extract files
int baseOfs = (int)(fis.getChannel().position()); // check this, might not be correct
for (IndexEntry entry : index) {
// print file info
printFileInfo(entry, baseOfs);
// data correctness check - this support ordered file archive only
// to support out-of-order archives, use RandomAccessFile instead.
if ((int)(fis.getChannel().position()) != entry.getOffset() + baseOfs) {
// error("Bad file content order "
// + entry.getFilename() + " @ 0x"
// + Integer.toHexString((int)(fis.getChannel().position())));
System.err.println("Out-of-order. " + entry.getFilename() + " @ 0x"
+ Integer.toHexString((int)(fis.getChannel().position())));
fis.getChannel().position(entry.getOffset() + baseOfs);
}
// get buffer and read file
byte[] writeBuf = readBlock(fis, entry.getLength(), entry.getFlags(), entry.getOrigLength());
// write file
writeFile(writeBuf, entry.getFilename(), dstDir);
}
fis.close();
}
private static void packFiles(String srcDir, String dstFile) throws Exception {
// TODO
// not implemented yet
}
private static void parseIndex(byte[] nameBuf, byte[] infoBuf) throws IOException {
DataInputStream dis = new DataInputStream(
new ByteArrayInputStream(infoBuf));
int blockCount = flipEndian(dis.readInt());
// PROBLEM 1
// This is where my program doesn't go compatible with the image.arc
// and sound.arc files. Their blockCount is 2. The 1st block seem
// to be the same as string.arc and script.arc, but the 2nd block is severely
// obsfucated that I don't know what the program is doing.
// if (blockCount != 1) error("Archive not supported. BlockCount is " + blockCount);
for (int i = 0; i < blockCount; ++i) {
int reserved1 = flipEndian(dis.readInt()); // unknown usage
int reserved2 = flipEndian(dis.readInt()); // not used
int entryCount = flipEndian(dis.readInt()); // entry count in current block
int blockSize = flipEndian(dis.readInt()); // current block size
// System.err.println(Arrays.toString(nameBuf));
System.err.println("reserved1: " + reserved1);
System.err.println("reserved2: " + reserved2);
System.err.println("entry count: " + entryCount);
System.err.println("block size: " + blockSize);
// PROBLEM 2
// if (4 * 5 * entryCount != blockSize) error("Index error. Wrong block size.\n"
// + "Count=" + entryCount + " BlockSize=" + blockSize);
int[] nameOfs = new int[entryCount];
for (int j = 0; j < entryCount; ++j) {
nameOfs[j] = flipEndian(dis.readInt());
}
for (int j = 0; j < entryCount; ++j) {
IndexEntry entry = new IndexEntry();
entry.setFilename(readWString(nameBuf, nameOfs[j]));
entry.setOffset(flipEndian(dis.readInt()));
entry.setLength(flipEndian(dis.readInt()) - 12);
entry.setFlags(flipEndian(dis.readInt()));
entry.setOrigLength(flipEndian(dis.readInt()));
index.add(entry);
}
}
}
private static int flipEndian(int i) {
byte[] bytes = new byte[4];
bytes[0] = (byte)(i >>> 24);
bytes[1] = (byte)(i >>> 16);
bytes[2] = (byte)(i >>> 8);
bytes[3] = (byte) i;
i = bytes[3] << 24;
i |= (bytes[2] << 16) & 0x0ff0000;
i |= (bytes[1] << 8) & 0x000ff00;
i |= bytes[0] & 0x00000ff;
return i;
}
private static void invertBitsArray(byte[] arr) {
for (int i = 0; i < arr.length; ++i) {
arr[i] = (byte)~arr[i];
}
}
private static void error(String cause) {
System.err.println("Error " + cause);
System.exit(1);
}
private static void printFileInfo(IndexEntry entry, int baseOfs) {
System.out.print("File " + entry.getFilename());
System.out.print(" @ 0x" + Integer.toHexString(entry.getOffset() + baseOfs).toUpperCase());
System.out.print(" size: 0x" + Integer.toHexString(entry.getLength()).toUpperCase());
System.out.println(" origSize: 0x" + Integer.toHexString(entry.getOrigLength()).toUpperCase());
System.out.print("Masks: ");
boolean doPrint = (entry.getFlags() | NOT_MSK) != 0;
if (doPrint) System.out.print("NOT_MSK");
if ((entry.getFlags() | LZSS_MSK) != 0 && doPrint) System.out.print(" | ");
else System.out.println();
if ((entry.getFlags() | LZSS_MSK) != 0) System.out.println("LZSS_MSK");
}
private static byte[] readIndexBlock(InputStream in) throws IOException {
DataInputStream dis = new DataInputStream(in);
int len = flipEndian(dis.readInt()) - 12;
int flags = flipEndian(dis.readInt());
int origLen = flipEndian(dis.readInt());
return readBlock(in, len, flags, origLen);
}
private static byte[] readBlock(InputStream in, int len, int flags, int origLen) throws IOException {
byte[] buf = new byte[len];
in.read(buf);
if ((flags | NOT_MSK) != 0) invertBitsArray(buf);
if (((flags | LZSS_MSK) != 0) && (len < origLen)) buf = LZSS.decompress(buf, len, null, origLen);
// TODO - other flag status are not supported yet
return buf;
}
private static String readWString(byte[] buf, int pos) throws IOException {
int len = 0;
int i = pos;
while ((buf[i] | buf[i+1]) != 0) {
len += 2;
i += 2;
}
return new String(buf, pos, len, "UTF-16LE");
}
private static void writeFile(byte[] buf, String name, String dstDir) throws IOException {
if (dstDir == null) dstDir = "./output";
File outfile = new File(dstDir + "/" + name);
File parentDir = outfile.getParentFile();
if (!parentDir.exists()) parentDir.mkdirs();
FileOutputStream fos = new FileOutputStream(outfile);
BufferedOutputStream bos = new BufferedOutputStream(fos, 0x100000);
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
int remainder = buf.length; // keep track of the amount of remaining bytes
byte[] bytes16 = new byte[16];
while (bais.available() != 0 && remainder / 16 != 0) {
bais.read(bytes16);
bos.write(bytes16);
remainder -= 16;
}
if (remainder != 0) {
byte[] remains = new byte[remainder];
bais.read(remains);
bos.write(remains);
}
bos.flush();
bos.close();
}
}
IndexEntry.java:
/*
* @(#)IndexEntry.java 2007/05/15
* Written by rednaxela / FX
*/
public class IndexEntry {
static final int ENTRY_LENGTH = 16;
static final int MAX_FILENAME_LENGTH = 256;
static final int OFFSET_OFS = 0;
static final int LENGTH_OFS = 4;
static final int FLAGS_OFS = 8;
static final int ORIGLENGTH_OFS = 12;
private String filename;
private int length;
private int offset;
private int flags; // NOT_MSK = 1, LZSS_MSK = 2
private int origLength;
/**
* @return the filename
*/
public String getFilename() {
return filename;
}
/**
* @param filename the filename to set
*/
public void setFilename(String filename) {
this.filename = filename;
}
/**
* @return the length
*/
public int getLength() {
return length;
}
/**
* @param length the length to set
*/
public void setLength(int length) {
this.length = length;
}
/**
* @return the offset
*/
public int getOffset() {
return offset;
}
/**
* @param offset the offset to set
*/
public void setOffset(int offset) {
this.offset = offset;
}
/**
* @return the offset
*/
public int getFlags() {
return offset;
}
/**
* @param offset the offset to set
*/
public void setFlags(int flags) {
this.flags = flags;
}
/**
* @return the compressedLength
*/
public int getOrigLength() {
return origLength;
}
/**
* @param offset the compressedLength to set
*/
public void setOrigLength(int origLength) {
this.origLength = origLength;
}
}
LZSS.java:
/*
* @(#)LZSS.java 2007/05/15
* Written by rednaxela / FX
*/
public class LZSS {
static final int WINDOW_LENGTH = 4096;
public static byte[] decompress(byte[] from, int compLen, byte[] to, int origLen) {
return decompress(from, compLen, to, 0, origLen);
}
public static byte[] decompress(byte[] from, int compLen, byte[] to, int pos, int origLen) {
if (to == null) to = new byte[origLen];
byte[] window = new byte[WINDOW_LENGTH];
int readOffset = 0;
int writeOffset = pos;
int marker = 0; // read marker, 8-bits, 1 for raw byte, 0 for back ref
int windowWriteOffset = 0x0FEE;
int windowReadOffset = 0;
int backRefLength = 0;
int current = 0;
while (readOffset != from.length) {
marker >>= 1;
if ((marker & 0x0100) == 0) {
current = from[readOffset++] & 0x0FF;
marker = 0x0FF00 | current;
}
if(readOffset == from.length) break;
if ((marker & 0x01) == 1) { // copy raw bytes
current = from[readOffset++] & 0x0FF;
to[writeOffset++] = (byte)current;
window[windowWriteOffset++] = (byte)current;
windowWriteOffset &= 0x0FFF;
} else { // copy from slide window
windowReadOffset = from[readOffset++] & 0x0FF;
if(readOffset == from.length) break;
current = from[readOffset++] & 0x0FF;
windowReadOffset |= (current & 0x0F0) << 4;
backRefLength = (current & 0x0F) + 2;
if (backRefLength < 0) continue;
int addOffset = 0;
while (addOffset <= backRefLength) {
int curOfs = (windowReadOffset + addOffset++) & 0x0FFF;
current = window[curOfs] & 0x0FF;
windowReadOffset &= 0x0FFF;
to[writeOffset++] = (byte)current;
window[windowWriteOffset++] = (byte)current;
windowWriteOffset &= 0x0FFF;
} // while
} // if-else
} // while
return to;
}
}
Binconverter.java:
/*
* @(#)BinConverter.java
*/
/**
* Some helper routines for data conversion, all data is treated in network
* byte order.
*/
public class BinConverter
{
/**
* Gets bytes from an array into an integer, in big-endian.
* @param buf where to get the bytes
* @param ofs index from where to read the data
* @return the 32bit integer
*/
public final static int byteArrayToIntBE(
byte[] buf,
int ofs)
{
return (buf[ofs ] << 24)
| ((buf[ofs + 1] & 0x0ff) << 16)
| ((buf[ofs + 2] & 0x0ff) << 8)
| ( buf[ofs + 3] & 0x0ff);
}
/**
* Gets bytes from an array into an integer, in little-endian.
* @param buf where to get the bytes
* @param ofs index from where to read the data
* @return the 32bit integer
*/
public final static int byteArrayToIntLE(
byte[] buf,
int ofs)
{
return (buf[ofs + 3] << 24)
| ((buf[ofs + 2] & 0x0ff) << 16)
| ((buf[ofs + 1] & 0x0ff) << 8)
| ( buf[ofs ] & 0x0ff);
}
///////////////////////////////////////////////////////////////////////////
/**
* Converts an integer to bytes in big-endian, which are put into an array.
* @param value the 32bit integer to convert
* @param buf the target buf
* @param ofs where to place the bytes in the buf
*/
public final static void intToByteArrayBE(
int value,
byte[] buf,
int ofs)
{
buf[ofs ] = (byte)((value >>> 24) & 0x0ff);
buf[ofs + 1] = (byte)((value >>> 16) & 0x0ff);
buf[ofs + 2] = (byte)((value >>> 8) & 0x0ff);
buf[ofs + 3] = (byte) value;
}
/**
* Converts an integer to bytes in little-endian, which are put into an array.
* @param value the 32bit integer to convert
* @param buf the target buf
* @param ofs where to place the bytes in the buf
*/
public final static void intToByteArrayLE(
int value,
byte[] buf,
int ofs)
{
buf[ofs + 3] = (byte)((value >>> 24) & 0x0ff);
buf[ofs + 2] = (byte)((value >>> 16) & 0x0ff);
buf[ofs + 1] = (byte)((value >>> 8) & 0x0ff);
buf[ofs ] = (byte) value;
}
///////////////////////////////////////////////////////////////////////////
/**
* Gets bytes from an array into a long.
* @param buf where to get the bytes
* @param ofs index from where to read the data
* @return the 64bit integer
*/
public final static long byteArrayToLong(
byte[] buf,
int ofs)
{
// Looks more complex - but it is faster (at least on 32bit platforms).
return
((long)(( buf[ofs ] << 24) |
((buf[ofs + 1] & 0x0ff) << 16) |
((buf[ofs + 2] & 0x0ff) << 8) |
( buf[ofs + 3] & 0x0ff )) << 32) |
((long)(( buf[ofs + 4] << 24) |
((buf[ofs + 5] & 0x0ff) << 16) |
((buf[ofs + 6] & 0x0ff) << 8) |
( buf[ofs + 7] & 0x0ff )) & 0x0ffffffffL);
}
///////////////////////////////////////////////////////////////////////////
/**
* Converts a long to bytes, which are put into an array.
* @param value the 64bit integer to convert
* @param buf the target buf
* @param ofs where to place the bytes in the buf
*/
public final static void longToByteArray(
long value,
byte[] buf,
int ofs)
{
int tmp = (int)(value >>> 32);
buf[ofs ] = (byte) (tmp >>> 24);
buf[ofs + 1] = (byte)((tmp >>> 16) & 0x0ff);
buf[ofs + 2] = (byte)((tmp >>> 8) & 0x0ff);
buf[ofs + 3] = (byte) tmp;
tmp = (int)value;
buf[ofs + 4] = (byte) (tmp >>> 24);
buf[ofs + 5] = (byte)((tmp >>> 16) & 0x0ff);
buf[ofs + 6] = (byte)((tmp >>> 8) & 0x0ff);
buf[ofs + 7] = (byte) tmp;
}
///////////////////////////////////////////////////////////////////////////
/**
* Converts values from an integer array to a long.
* @param buf where to get the bytes
* @param ofs index from where to read the data
* @return the 64bit integer
*/
public final static long intArrayToLong(
int[] buf,
int ofs)
{
return (((long) buf[ofs ]) << 32) |
(((long) buf[ofs + 1]) & 0x0ffffffffL);
}
///////////////////////////////////////////////////////////////////////////
/**
* Converts a long to integers which are put into an array.
* @param value the 64bit integer to convert
* @param buf the target buf
* @param ofs where to place the bytes in the buf
*/
public final static void longToIntArray(
long value,
int[] buf,
int ofs)
{
buf[ofs ] = (int)(value >>> 32);
buf[ofs + 1] = (int) value;
}
///////////////////////////////////////////////////////////////////////////
/**
* Makes a long from two integers (treated unsigned).
* @param lo lower 32bits
* @param hi higher 32bits
* @return the built long
*/
public final static long makeLong(
int lo,
int hi)
{
return (((long) hi << 32) |
((long) lo & 0x00000000ffffffffL));
}
///////////////////////////////////////////////////////////////////////////
/**
* Gets the lower 32 bits of a long.
* @param val the long integer
* @return lower 32 bits
*/
public final static int longLo32(
long val)
{
return (int)val;
}
///////////////////////////////////////////////////////////////////////////
/**
* Gets the higher 32 bits of a long.
* @param val the long integer
* @return higher 32 bits
*/
public final static int longHi32(
long val)
{
return (int)(val >>> 32);
}
///////////////////////////////////////////////////////////////////////////
// our table for hex conversion
final static char[] HEXTAB =
{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
/**
* Converts a byte array to a hex string.
* @param data the byte array
* @return the hex string
*/
public final static String bytesToHexStr(
byte[] data)
{
return bytesToHexStr(data, 0, data.length);
}
///////////////////////////////////////////////////////////////////////////
/**
* Converts a byte array to a hex string.
* @param data the byte array
* @param ofs start index where to get the bytes
* @param len number of bytes to convert
* @return the hex string
*/
public final static String bytesToHexStr(
byte[] data,
int ofs,
int len)
{
int pos, c;
StringBuffer sbuf;
sbuf = new StringBuffer();
sbuf.setLength(len << 1);
pos = 0;
c = ofs + len;
while (ofs < c)
{
sbuf.setCharAt(pos++, HEXTAB[(data[ofs ] >> 4) & 0x0f]);
sbuf.setCharAt(pos++, HEXTAB[ data[ofs++] & 0x0f]);
}
return sbuf.toString();
}
///////////////////////////////////////////////////////////////////////////
/**
* Converts a hex string back into a byte array (invalid codes will be
* skipped).
* @param hex hex string
* @param data the target array
* @param srcofs from which character in the string the conversion should
* begin, remember that (nSrcPos modulo 2) should equals 0 normally
* @param dstofs to store the bytes from which position in the array
* @param len number of bytes to extract
* @return number of extracted bytes
*/
public final static int hexStrToBytes(
String hex,
byte[] data,
int srcofs,
int dstofs,
int len)
{
int i, j, strlen, avail_bytes, dstofs_bak;
byte abyte;
boolean convertOK;
// check for correct ranges
strlen = hex.length();
avail_bytes = (strlen - srcofs) >> 1;
if (avail_bytes < len)
{
len = avail_bytes;
}
int nOutputCapacity = data.length - dstofs;
if (len > nOutputCapacity)
{
len = nOutputCapacity;
}
// convert now
dstofs_bak = dstofs;
for (i = 0; i < len; i++)
{
abyte = 0;
convertOK = true;
for (j = 0; j < 2; j++)
{
abyte <<= 4;
char cActChar = hex.charAt(srcofs++);
if ((cActChar >= 'a') && (cActChar <= 'f'))
{
abyte |= (byte) (cActChar - 'a') + 10;
}
else
{
if ((cActChar >= '0') && (cActChar <= '9'))
{
abyte |= (byte) (cActChar - '0');
}
else
{
convertOK = false;
}
}
}
if (convertOK)
{
data[dstofs++] = abyte;
}
}
return (dstofs - dstofs_bak);
}
///////////////////////////////////////////////////////////////////////////
/**
* Converts a byte array into a Unicode string.
* @param data the byte array
* @param ofs where to begin the conversion
* @param len number of bytes to handle
* @return the string
*/
public final static String byteArrayToStr(
byte[] data,
int ofs,
int len)
{
int avail_capacity, sbuf_pos;
StringBuffer sbuf;
// we need two bytes for every character
len &= ~1;
// enough bytes in the buf?
avail_capacity = data.length - ofs;
if (avail_capacity < len)
{
len = avail_capacity;
}
sbuf = new StringBuffer();
sbuf.setLength(len >> 1);
sbuf_pos = 0;
while (0 < len)
{
sbuf.setCharAt(
sbuf_pos++,
(char)((data[ofs ] << 8)
| (data[ofs + 1] & 0x0ff)));
ofs += 2;
len -= 2;
}
return sbuf.toString();
}
}