View Javadoc

1   /*
2    * $Id: ZsciiString.java,v 1.4 2005/10/25 16:27:32 weiju Exp $
3    * 
4    * Created on 08.10.2005
5    * Copyright 2005 by Wei-ju Wu
6    *
7    * This file is part of The Z-machine Preservation Project (ZMPP).
8    *
9    * ZMPP is free software; you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as published by
11   * the Free Software Foundation; either version 2 of the License, or
12   * (at your option) any later version.
13   *
14   * ZMPP is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public License
20   * along with ZMPP; if not, write to the Free Software
21   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
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; // 1 00000 00101 00101
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; // the current character 
128     int zwpos = 0; // the character position within a zword
129     int zword = 0; // the current zword
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         // put character in correct position
138         zword |= (zchar << ((2 - zwpos) * 5));
139         zwpos++;
140       }
141       
142       // new zword needed
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       // end of string ? mark the current zword as last then
155       if (i == str.length() - 1) {
156         
157         zword |= 0x8000;
158       }
159       
160       // pad with 0x05's if necessary
161       if ((zword & 0x8000) > 0) {
162         
163         for (; zwpos < 3; zwpos++) {
164           
165           zword |= (0x05 << ((2 - zwpos) * 5));
166         }
167       }
168       
169       // new zword needed
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 }