1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.zmpp.vmutil;
24
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import org.zmpp.base.DefaultMemoryAccess;
29 import org.zmpp.base.MemoryAccess;
30 import org.zmpp.base.MemoryReadAccess;
31
32 /***
33 * This class represents a string of Z characters. It also does some
34 * conversion in case a string is specified as a constructor argument.
35 *
36 * @author Wei-ju Wu
37 * @version 1.0
38 */
39 public class ZsciiString {
40
41 private MemoryReadAccess memaccess;
42 private int startAddress;
43 private byte[] buffer;
44 private static ZsciiConverter decoder;
45
46 static {
47
48 decoder = new ZsciiConverter(3, null);
49 }
50
51 /***
52 * Constructor. Creates a ZsciiString from a string.
53 *
54 * @param str a Java string object
55 */
56 public ZsciiString(String str) {
57
58 encode(str);
59 }
60
61 /***
62 * Sets the converter object used by the ZsciiString class.
63 *
64 * @param converter the converter
65 */
66 public static void setZsciiConverter(ZsciiConverter converter) {
67
68 decoder = converter;
69 }
70
71 /***
72 * Constructor. Creates a ZsciiString sharing data that lies at the
73 * specified position in memory.
74 *
75 * @param memaccess a memory access object
76 * @param startAddress the start address within the memory access object
77 */
78 public ZsciiString(MemoryReadAccess memaccess, int startAddress) {
79
80 this.memaccess = memaccess;
81 this.startAddress = startAddress;
82 }
83
84 /***
85 * Creates a ZsciiString from a single zchar.
86 *
87 * @param zchar a ZSCII char
88 */
89 public ZsciiString(short zchar) {
90
91 byte[] buffer = new byte[2];
92 MemoryAccess memaccess = new DefaultMemoryAccess(buffer);
93 int zword = 0x80a5;
94 zword |= ((zchar & 0x1f) << 10);
95 memaccess.writeUnsignedShort(0, zword);
96 this.memaccess = memaccess;
97 }
98
99 /***
100 * Returns a memory access object.
101 *
102 * @return the memory access object
103 */
104 public MemoryReadAccess getMemoryAccess() {
105
106 return memaccess;
107 }
108
109 /***
110 * Returns the start address within the memory access buffer.
111 *
112 * @return the start address in the memory access object
113 */
114 public int getStartAddress() {
115
116 return startAddress;
117 }
118
119 /***
120 * Encodes the specified String object to a ZSCII string.
121 *
122 * @param str the string
123 */
124 private void encode(String str) {
125
126 List<Integer> result = new ArrayList<Integer>();
127 char c;
128 int zwpos = 0;
129 int zword = 0;
130
131 for (int i = 0; i < str.length(); i++) {
132
133 c = str.charAt(i);
134 if (needsShift(c)) {
135
136 short zchar = getShiftFor(c);
137
138 zword |= (zchar << ((2 - zwpos) * 5));
139 zwpos++;
140 }
141
142
143 if (zwpos > 2) {
144
145 result.add(zword);
146 zword = 0;
147 zwpos = 0;
148 }
149
150 short zchar = getZchar(c);
151 zword |= (zchar << ((2 - zwpos) * 5));
152 zwpos++;
153
154
155 if (i == str.length() - 1) {
156
157 zword |= 0x8000;
158 }
159
160
161 if ((zword & 0x8000) > 0) {
162
163 for (; zwpos < 3; zwpos++) {
164
165 zword |= (0x05 << ((2 - zwpos) * 5));
166 }
167 }
168
169
170 if (zwpos > 2) {
171
172 result.add(zword);
173 zword = 0;
174 zwpos = 0;
175 }
176 }
177 int numWords = result.size();
178 buffer = new byte[numWords * 2];
179 MemoryAccess memaccess = new DefaultMemoryAccess(buffer);
180 for (int i = 0; i < numWords; i++) {
181
182 memaccess.writeUnsignedShort(i * 2, result.get(i));
183 }
184 this.memaccess = memaccess;
185 }
186
187 /***
188 * Returns the ZSCII representation of the specified character.
189 *
190 * @param c a Java character
191 * @return the ZSCII character
192 */
193 private short getZchar(char c) {
194
195 if (c >= 'A' && c <= 'Z') return (short) ((c - 'A') + 6);
196 if (c >= 'a' && c <= 'z') return (short) ((c - 'a') + 6);
197 if (c == ' ') return 0;
198 for (int i = 1; i < ZsciiConverter.A2CHARS.length(); i++) {
199
200 if (ZsciiConverter.A2CHARS.charAt(i) == c) return (short) (i + 6);
201 }
202 return (short) c;
203 }
204
205 /***
206 * Returns the total number of Zwords occupied by this ZsciiString.
207 *
208 * @return the numbe of 16-bit words
209 */
210 public int getNumWords() {
211
212 int zword = 0;
213
214 for (int i = 0; ; i++) {
215
216 zword = memaccess.readUnsignedShort(startAddress + i * 2);
217 if ((zword & 0x8000) > 0) return (i + 1);
218 }
219 }
220
221 /***
222 * {@inheritDoc}
223 */
224 public boolean equals(Object o) {
225
226 if (o == this) return true;
227 if (o != null && o instanceof ZsciiString) {
228
229 return o.toString().equals(toString());
230 }
231 return false;
232 }
233
234 /***
235 * {@inheritDoc}
236 */
237 public String toString() {
238
239 return decoder.convert(memaccess, startAddress);
240 }
241
242 /***
243 * Returns true, if the specified character needs a shift to be encoded.
244 *
245 * @param c a Java character
246 * @return true, if needs shift character, false, else
247 */
248 private boolean needsShift(char c) {
249
250 if (c >= 'A' && c <= 'Z') return true;
251
252 for (int i = 0; i < ZsciiConverter.A2CHARS.length(); i++) {
253
254 if (ZsciiConverter.A2CHARS.charAt(i) == c) return true;
255 }
256 return false;
257 }
258
259 /***
260 * Returns the shift character needed to be properly encoded.
261 *
262 * @param c the Java character
263 * @return the shift to select the proper alphabet
264 */
265 private short getShiftFor(char c) {
266
267 if (c >= 'A' && c <= 'Z') return ZsciiConverter.SHIFT_4;
268 else return ZsciiConverter.SHIFT_5;
269 }
270 }