2015-06-01 09:35:51 +00:00
|
|
|
package com.bytezone.diskbrowser.infocom;
|
|
|
|
|
|
|
|
class ZString
|
|
|
|
{
|
2019-04-22 04:35:50 +00:00
|
|
|
private static String[] letters =
|
|
|
|
{ " abcdefghijklmnopqrstuvwxyz", " ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
|
|
" 0123456789.,!?_#\'\"/\\-:()" };
|
|
|
|
String value;
|
|
|
|
Header header;
|
|
|
|
int startPtr;
|
|
|
|
int length;
|
|
|
|
|
|
|
|
public ZString (Header header, int offset)
|
|
|
|
{
|
|
|
|
ZStringBuilder text = new ZStringBuilder ();
|
|
|
|
this.header = header;
|
|
|
|
this.startPtr = offset;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (offset >= header.buffer.length - 1)
|
|
|
|
{
|
|
|
|
System.out.println ("********" + text.toString ());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the next two bytes
|
|
|
|
int val = header.getWord (offset);
|
|
|
|
|
|
|
|
// process each zChar as a 5-bit value
|
|
|
|
text.processZChar ((byte) ((val >>> 10) & 0x1F));
|
|
|
|
text.processZChar ((byte) ((val >>> 5) & 0x1F));
|
|
|
|
text.processZChar ((byte) (val & 0x1F));
|
|
|
|
|
|
|
|
if ((val & 0x8000) != 0) // bit 15 = finished flag
|
|
|
|
{
|
|
|
|
length = offset - startPtr + 2;
|
|
|
|
value = text.toString ();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
offset += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString ()
|
|
|
|
{
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
private class ZStringBuilder
|
|
|
|
{
|
|
|
|
int alphabet;
|
|
|
|
boolean shift;
|
|
|
|
int shiftAlphabet;
|
|
|
|
int synonym;
|
|
|
|
int buildingLevel;
|
|
|
|
int builtLetter;
|
|
|
|
StringBuilder text = new StringBuilder ();
|
|
|
|
|
|
|
|
private void processZChar (byte zchar)
|
|
|
|
{
|
|
|
|
// A flag to indicate that we are building a character not in the alphabet. The
|
|
|
|
// value indicates which half of the character the current zchar goes into. Once
|
|
|
|
// both halves are full, we use the ascii value in builtLetter.
|
|
|
|
if (buildingLevel > 0)
|
|
|
|
{
|
|
|
|
builtLetter = (short) ((builtLetter << 5) | zchar);
|
|
|
|
if (++buildingLevel == 3)
|
|
|
|
{
|
|
|
|
text.append ((char) builtLetter);
|
|
|
|
buildingLevel = 0;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A flag to indicate that we need to insert an abbreviation. The synonym value
|
|
|
|
// (1-3) indicates which abbreviation block to use, and the current zchar is the
|
|
|
|
// offset within that block.
|
|
|
|
if (synonym > 0)
|
|
|
|
{
|
|
|
|
text.append (header.getAbbreviation ((synonym - 1) * 32 + zchar));
|
|
|
|
synonym = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((shift && shiftAlphabet == 2) || (!shift && alphabet == 2))
|
|
|
|
{
|
|
|
|
if (zchar == 6)
|
|
|
|
{
|
|
|
|
buildingLevel = 1;
|
|
|
|
builtLetter = 0;
|
|
|
|
shift = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (zchar == 7)
|
|
|
|
{
|
|
|
|
text.append ("\n");
|
|
|
|
shift = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// zChar values 0-5 have special meanings, and 6-7 are special only in alphabet #2.
|
|
|
|
// Otherwise it's just a straight lookup into the current alphabet.
|
|
|
|
switch (zchar)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
text.append (" ");
|
|
|
|
shift = false;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
synonym = zchar;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
if (header.version >= 3)
|
|
|
|
{
|
|
|
|
synonym = zchar;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// version 1 or 2
|
|
|
|
shiftAlphabet = (alphabet + zchar - 1) % 3;
|
|
|
|
shift = true;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
case 5:
|
|
|
|
if (header.version >= 3) // shift key
|
|
|
|
{
|
|
|
|
shiftAlphabet = zchar - 3;
|
|
|
|
shift = true;
|
|
|
|
}
|
|
|
|
else // shift lock key
|
|
|
|
alphabet = (alphabet + zchar - 3) % 3;
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (shift)
|
|
|
|
{
|
|
|
|
text.append (letters[shiftAlphabet].charAt (zchar));
|
|
|
|
shift = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
text.append (letters[alphabet].charAt (zchar));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString ()
|
|
|
|
{
|
|
|
|
return text.toString ();
|
|
|
|
}
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|