Use local directory files in preference to files from .jar
[jpcrr.git] / org / jpc / Misc.java
blob09bf2cf9d06edee0b494346c20ee1a2b5c0dd5ad
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009-2010 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
30 package org.jpc;
32 import java.io.*;
33 import java.nio.*;
34 import java.nio.charset.*;
35 import java.util.*;
36 import javax.swing.*;
38 import org.jpc.diskimages.ImageLibrary;
39 import org.jpc.jrsr.UTFInputLineStream;
41 import static org.jpc.Exceptions.classes;
42 import static org.jpc.emulator.memory.codeblock.optimised.MicrocodeSet.*;
43 import static org.jpc.Revision.getRevision;
45 public class Misc
47 private static boolean renameOverSupported;
49 public static String randomHexes(int bytes)
51 java.security.SecureRandom prng = new java.security.SecureRandom();
52 byte[] rnd = new byte[bytes];
53 prng.nextBytes(rnd);
54 StringBuffer buf = new StringBuffer(2 * rnd.length);
55 for(int i = 0; i < rnd.length; i++) {
56 int b = (int)rnd[i] & 0xFF;
57 buf.append(Character.forDigit(b / 16, 16));
58 buf.append(Character.forDigit(b % 16, 16));
60 return buf.toString();
63 public static String tempname(String prefix)
65 //As we don't create files atomically, we need to be unpredictable.
66 return prefix + "." + randomHexes(12);
69 public static String arrayToString(byte[] array) throws IOException
71 if(array == null)
72 return null;
73 return (new ImageLibrary.ByteArray(array)).toString();
76 public static byte[] stringToArray(String name) throws IOException
78 if(name == null)
79 return null;
81 if((name.length() % 2) != 0)
82 throw new IOException("Trying to transform odd-length string into byte array");
83 int l = name.length() / 2;
84 byte[] parsed = new byte[l];
85 for(int i = 0; i < l; i++)
86 parsed[i] = (byte)(Character.digit(name.charAt(2 * i), 16) * 16 +
87 Character.digit(name.charAt(2 * i + 1), 16));
89 return parsed;
92 public static boolean isspace(char ch)
94 if(ch == 12)
95 return true;
96 if(ch == 32)
97 return true;
98 if(ch == 9)
99 return true;
100 if(ch == 0x1680)
101 return true;
102 if(ch == 0x180E)
103 return true;
104 if(ch >= 0x2000 && ch <= 0x200A)
105 return true;
106 if(ch == 0x2028)
107 return true;
108 if(ch == 0x205F)
109 return true;
110 if(ch == 0x3000)
111 return true;
112 return false;
115 public static String componentEscape(String in)
117 boolean needEscape = false;
118 boolean needParens = false;
119 Stack<Integer> parens = new Stack<Integer>();
120 Stack<Integer> parens2 = new Stack<Integer>();
122 int strlen = in.length();
123 for(int i = 0; i < strlen; i++) {
124 char ch = in.charAt(i);
125 if(isspace(ch))
126 needParens = true;
127 if(ch == '\\') {
128 needEscape = true;
129 } if(ch == '(') {
130 needParens = true;
131 parens.push(new Integer(i));
132 } else if(ch == ')') {
133 if(!parens.empty())
134 parens.pop();
135 else
136 needEscape = true;
140 if(!parens.empty())
141 needEscape = true;
143 //Copy the paren stack to another to reverse it.
144 while(!parens.empty())
145 parens2.push(parens.pop());
147 if(!needEscape && !needParens)
148 return in;
150 StringBuilder out = new StringBuilder();
151 if(needParens)
152 out.append('(');
154 if(needEscape) {
155 int parenDepth = 0;
156 for(int i = 0; i < strlen; i++) {
157 char ch = in.charAt(i);
158 if(ch == '\\') {
159 out.append("\\\\");
160 } else if(!parens2.empty() && parens2.peek().intValue() == i) {
161 out.append("\\(");
162 parens2.pop();
163 } else if(ch == '(') {
164 out.append("(");
165 parenDepth++;
166 } else if(ch == ')') {
167 if(parenDepth > 0) {
168 out.append(")");
169 parenDepth--;
170 } else
171 out.append("\\)");
172 } else
173 out.append(ch);
175 } else
176 out.append(in);
178 if(needParens)
179 out.append(')');
181 return out.toString();
184 public static String componentUnescape(String in) throws IOException
186 if(in.indexOf('\\') < 0)
187 return in; //No escapes.
188 StringBuilder out = new StringBuilder();
189 boolean escapeActive = false;
190 int strlen = in.length();
191 for(int i = 0; i < strlen; i++) {
192 char ch = in.charAt(i);
193 if(escapeActive) {
194 out.append(ch);
195 escapeActive = false;
196 } else if(ch == '\\') {
197 escapeActive = true;
198 } else
199 out.append(ch);
201 if(escapeActive)
202 throw new IOException("Invalid escaped string: unexpected end of string after \\");
203 return out.toString();
206 public static String[] nextParseLine(UTFInputLineStream in) throws IOException
208 String[] ret = null;
210 String parseLine = "";
211 while(parseLine != null && "".equals(parseLine))
212 parseLine = in.readLine();
213 if(parseLine == null)
214 return null;
216 ret = parseString(parseLine);
218 if(ret == null)
219 return nextParseLine(in);
221 return ret;
224 public static String[] parseString(String parseLine) throws IOException
226 ArrayList<String> ret = new ArrayList<String>();
227 boolean escapeActive = false;
229 if(parseLine == null)
230 return null;
232 //System.err.println("Line: \"" + parseLine + "\".");
233 int parenDepth = 0;
234 int lastSplitStart = 0;
235 int strlen = parseLine.length();
237 for(int i = 0; i < strlen; i++) {
238 String component = null;
239 char ch = parseLine.charAt(i);
240 if(escapeActive) {
241 escapeActive = false;
242 } else if(ch == '\\') {
243 escapeActive = true;
244 } else if(ch == '(') {
245 if(parenDepth > 0)
246 parenDepth++;
247 else if(parenDepth == 0) {
248 //Split here.
249 component = parseLine.substring(lastSplitStart, i);
250 lastSplitStart = i + 1;
251 parenDepth++;
253 } else if(ch == ')') {
254 if(parenDepth == 0)
255 throw new IOException("Unbalanced ) in component line \"" + parseLine + "\".");
256 else if(parenDepth == 1) {
257 //Split here.
258 component = parseLine.substring(lastSplitStart, i);
259 lastSplitStart = i + 1;
260 parenDepth--;
261 } else
262 parenDepth--;
263 } else if(parenDepth == 0 && isspace(ch)) {
264 //Split here.
265 //System.err.println("Splitting at point " + i + ".");
266 component = componentUnescape(parseLine.substring(lastSplitStart, i));
267 lastSplitStart = i + 1;
270 if(component != null && !component.equals(""))
271 ret.add(component);
273 if(parenDepth > 0)
274 throw new IOException("Unbalanced ( in component line \"" + parseLine + "\".");
275 String component = componentUnescape(parseLine.substring(lastSplitStart));
276 if(component != null && !component.equals(""))
277 ret.add(component);
279 if(!ret.isEmpty())
280 return (String[])ret.toArray(new String[ret.size()]);
281 else
282 return null;
285 public static boolean hasParensInserted(String in)
287 return (in.charAt(0) == '(');
290 public static String encodeLine(String[] components)
292 String s = "";
293 boolean lastParen = true; //Hack to supress initial space.
294 for(int i = 0; i < components.length; i++) {
295 String escaped = componentEscape(components[i]);
296 boolean thisParen = hasParensInserted(escaped);
297 if(!lastParen && !thisParen)
298 s = s + " ";
299 s = s + escaped;
300 lastParen = thisParen;
302 return s;
305 public static int callShowOptionDialog(java.awt.Component parent, Object msg, String title, int oType,
306 int mType, Icon icon, Object[] buttons, Object deflt)
308 try {
309 return JOptionPane.showOptionDialog(parent, msg, title, oType, mType, icon, buttons, deflt);
310 } catch(Throwable e) { //Catch errors too!
311 //No GUI available.
312 System.err.println("MESSAGE: *** " + title + " ***: " + msg.toString());
313 for(int i = 0; i < buttons.length; i++)
314 if(buttons[i] == deflt)
315 return i;
316 return 0;
321 public static String messageForException(Throwable e, boolean chase)
323 boolean supressClass = false;
324 while(chase && e.getCause() != null)
325 e = e.getCause();
326 String message = e.getMessage();
327 Class<?> eClass = e.getClass();
328 while(eClass != null) {
329 if(classes.containsKey(eClass.getName())) {
330 if(message != null && !message.equals("") && !message.equals("null"))
331 message = classes.get(eClass.getName()) + " (" + message + ")";
332 else
333 message = classes.get(eClass.getName());
334 if(eClass == e.getClass())
335 supressClass = true;
336 break;
338 eClass = eClass.getSuperclass();
341 if(!supressClass)
342 if(message != null && !message.equals("") && !message.equals("null"))
343 message = message + " [" + e.getClass().getName() + "]";
344 else
345 message = message + "<no description available> [" + e.getClass().getName() + "]";
346 return message;
349 public static void errorDialog(Throwable e, String title, java.awt.Component component, String text)
351 String message = messageForException(e, true);
352 int i = callShowOptionDialog(null, message, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[]{text, "Save stack trace"}, "Save stack Trace");
353 if(i > 0) {
354 saveStackTrace(e, null, text);
358 public static void saveStackTrace(Throwable e, java.awt.Component component, String text)
360 StringBuffer sb = new StringBuffer();
361 sb.append("Exception trace generated on '" + (new Date()).toString() + "' by version '" + getRevision() + "'.\n\n");
363 while(true) {
364 StackTraceElement[] traceback = e.getStackTrace();
365 sb.append(messageForException(e, false) + "\n");
366 for(int i = 0; i < traceback.length; i++) {
367 StackTraceElement el = traceback[i];
368 if(el.getClassName().startsWith("sun.reflect."))
369 continue; //Clean up the trace a bit.
370 if(el.isNativeMethod())
371 sb.append(el.getMethodName() + " of " + el.getClassName() + " <native>\n");
372 else
373 sb.append(el.getMethodName() + " of " + el.getClassName() + " <" + el.getFileName() + ":" +
374 el.getLineNumber() + ">\n");
376 if(e.getCause() != null) {
377 e = e.getCause();
378 sb.append("\nCaused By:\n\n");
379 } else
380 break;
382 String exceptionMessage = sb.toString();
384 try {
385 ByteBuffer buf;
386 buf = Charset.forName("UTF-8").newEncoder().encode(CharBuffer.wrap(exceptionMessage));
387 byte[] buf2 = new byte[buf.remaining()];
388 buf.get(buf2);
390 String traceFileName = "StackTrace-" + System.currentTimeMillis() + ".text";
391 OutputStream stream = new FileOutputStream(traceFileName);
392 stream.write(buf2);
393 stream.close();
394 callShowOptionDialog(component, "Stack trace saved to " + traceFileName + ".", "Stack trace saved", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[]{text}, text);
395 } catch(Exception e2) {
396 callShowOptionDialog(component, e.getMessage(), "Saving stack trace failed", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[]{text}, text);
400 public static Map<String, String> parseStringToComponents(String string) throws IOException
402 Map<String,String> ret = new HashMap<String, String>();
403 while(!string.equals("")) {
404 int i = string.indexOf(',');
405 String element;
406 if(i < 0) {
407 element = string;
408 string = "";
409 } else {
410 element = string.substring(0, i);
411 string = string.substring(i + 1);
413 int j = element.indexOf('=');
414 if(j < 0)
415 throw new IOException("Bad string element: \"" + element + "\"");
416 String key = element.substring(0, j);
417 String value = element.substring(j + 1);
418 ret.put(key, value);
420 return ret;
423 public static InputStream openStream(String name, String defaultName)
425 InputStream ret = null;
426 if(name != null) {
427 try {
428 ret = new FileInputStream(name);
429 } catch(Exception e) {
430 ret = ClassLoader.getSystemResourceAsStream(name);
432 if(ret != null)
433 return ret;
435 if(defaultName != null) {
436 System.err.println("Error: Can't open '" + name + "' falling back to default of '" + defaultName + "'.");
437 ret = ClassLoader.getSystemResourceAsStream(defaultName);
439 if(ret == null)
440 System.err.println("Error: Can't open '" + name + "' nor default fallback.");
441 return ret;
444 public static void renameFile(File src, File dest) throws IOException
446 if(!src.exists())
447 return;
448 if(renameOverSupported) {
449 System.err.println("Informational: Renaming file...");
450 if(!src.renameTo(dest))
451 throw new IOException("Failed to rename '" + src.getAbsolutePath() + "' to '" + dest.getAbsolutePath() + "'.");
452 } else {
453 System.err.println("Informational: Copying & deleting file...");
454 FileInputStream srch = new FileInputStream(src);
455 FileOutputStream desth = new FileOutputStream(dest);
456 byte[] copyBuffer = new byte[1024];
457 int r = 0;
458 while((r = srch.read(copyBuffer)) >= 0)
459 desth.write(copyBuffer, 0, r);
460 srch.close();
461 desth.close();
462 src.delete();
466 public static void probeRenameOver(boolean forceFalse)
468 File file1 = null;
469 File file2 = null;
470 try {
471 if(forceFalse)
472 throw new IOException("Rename-over forced off");
473 String name1 = randomHexes(24);
474 String name2 = randomHexes(24);
475 RandomAccessFile fh1 = new RandomAccessFile(name1, "rw");
476 RandomAccessFile fh2 = new RandomAccessFile(name2, "rw");
477 fh1.close();
478 fh2.close();
479 file1 = new File(name1);
480 file2 = new File(name2);
481 if(!file1.renameTo(file2))
482 throw new IOException("Rename-over test failed");
483 file1.delete();
484 file2.delete();
485 } catch(IOException e) {
486 System.err.println("Informational: Probing if rename-over works...no: " + e.getMessage());
487 System.err.println("Notice: Using copy & delete for file overwrites.");
488 if(file1 != null)
489 file1.delete();
490 if(file2 != null)
491 file2.delete();
492 return;
494 System.err.println("Informational: Probing if rename-over works...yes.");
495 System.err.println("Notice: Using rename-over for file overwrites.");
496 renameOverSupported = true;
499 public static void moveWindow(JFrame window, int x, int y, int w, int h)
501 final int x2 = x;
502 final int y2 = y;
503 final int w2 = w;
504 final int h2 = h;
505 final JFrame window2 = window;
507 if(!SwingUtilities.isEventDispatchThread())
508 try {
509 SwingUtilities.invokeAndWait(new Thread() { public void run() {
510 window2.setBounds(x2, y2, w2, h2); }});
511 } catch(Exception e) {
513 else
514 window2.setBounds(x2, y2, w2, h2);
517 public static boolean isFPUOp(int op)
519 switch(op) {
520 case FWAIT:
521 case FLOAD0_ST0:
522 case FLOAD0_STN:
523 case FLOAD0_MEM_SINGLE:
524 case FLOAD0_MEM_DOUBLE:
525 case FLOAD0_MEM_EXTENDED:
526 case FLOAD0_REG0:
527 case FLOAD0_REG0L:
528 case FLOAD0_1:
529 case FLOAD0_L2TEN:
530 case FLOAD0_L2E:
531 case FLOAD0_PI:
532 case FLOAD0_LOG2:
533 case FLOAD0_LN2:
534 case FLOAD0_POS0:
535 case FLOAD1_ST0:
536 case FLOAD1_STN:
537 case FLOAD1_MEM_SINGLE:
538 case FLOAD1_MEM_DOUBLE:
539 case FLOAD1_MEM_EXTENDED:
540 case FLOAD1_REG0:
541 case FLOAD1_REG0L:
542 case FLOAD1_POS0:
543 case FSTORE0_ST0:
544 case FSTORE0_STN:
545 case FSTORE0_MEM_SINGLE:
546 case FSTORE0_MEM_DOUBLE:
547 case FSTORE0_MEM_EXTENDED:
548 case FSTORE0_REG0:
549 case FSTORE1_ST0:
550 case FSTORE1_STN:
551 case FSTORE1_MEM_SINGLE:
552 case FSTORE1_MEM_DOUBLE:
553 case FSTORE1_MEM_EXTENDED:
554 case FSTORE1_REG0:
555 case LOAD0_FPUCW:
556 case STORE0_FPUCW:
557 case LOAD0_FPUSW:
558 case STORE0_FPUSW:
559 case FPOP:
560 case FPUSH:
561 case FADD:
562 case FMUL:
563 case FCOM:
564 case FUCOM:
565 case FCOMI:
566 case FUCOMI:
567 case FSUB:
568 case FDIV:
569 case FCHS:
570 case FABS:
571 case FXAM:
572 case F2XM1:
573 case FYL2X:
574 case FPTAN:
575 case FPATAN:
576 case FXTRACT:
577 case FPREM1:
578 case FDECSTP:
579 case FINCSTP:
580 case FPREM:
581 case FYL2XP1:
582 case FSQRT:
583 case FSINCOS:
584 case FRNDINT:
585 case FSCALE:
586 case FSIN:
587 case FCOS:
588 case FRSTOR_94:
589 case FRSTOR_108:
590 case FSAVE_94:
591 case FSAVE_108:
592 case FFREE:
593 case FBCD2F:
594 case FF2BCD:
595 case FLDENV_14:
596 case FLDENV_28:
597 case FSTENV_14:
598 case FSTENV_28:
599 case FCMOVB:
600 case FCMOVE:
601 case FCMOVBE:
602 case FCMOVU:
603 case FCMOVNB:
604 case FCMOVNE:
605 case FCMOVNBE:
606 case FCMOVNU:
607 case FCHOP:
608 case FCLEX:
609 case FINIT:
610 case FCHECK0:
611 case FCHECK1:
612 case FXSAVE:
613 return true;
614 default:
615 return false;