import java.io.*; import java.util.Arrays; /* 18/01/2020 Geert De Prins Convert the turbo Chameleon help file to PETSCII and rebuild the file from PETSCII Crude UTF8<->Petscii conversion added by Tobias Korbmacher */ public class ChamHelp { private final static int MAXPAGES = 9999; private static final String utf8BOM = "\uFEFF"; private static long getOffset(byte[] buf) { long result = ((long)buf[1] & 0xFF) + (((long)buf[2] & 0XFF) << 8) + (((long)buf[3] & 0XFF) << 16); return result; } private static int getPageNum(byte[] buf) { int result = ((int)buf[1] & 0xFF) + (((int)buf[2] & 0XFF) << 8); return result; } private static int getLink(long[] posList, long offSet, int iPages) { int begin,end,mid; begin=0;end=iPages-1; do { mid = (begin + end) >> 1; long elem = posList[mid]; if (elem < offSet) { begin = mid+1; } else if (elem > offSet) { end = mid-1; } else { return mid; } } while (begin <= end); return 0; } private static int outputPetsciiByte(BufferedWriter bufferedWriter, int value) { // String colText = String.format("",value & 0xff); String[] petStr = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", // "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", // "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", // "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "@", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", // "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "[", "", "]", "^", "_", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", // "", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", // "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "~", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", // "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", // "{SHIFT-*>", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "\u2014", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", // "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", // "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "{SHIFT-+>", "", "{SHIFT-->", "", "", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\u2020", "", "\u007c", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }; try { bufferedWriter.write(petStr[value & 0xff]); } catch (IOException ex) { ex.printStackTrace(); } return 0; } private static int outputHelpChar(RandomAccessFile outputStream, char thischar) { int charValue = (int)thischar; try { if((charValue > 64) && (charValue < 91)) { charValue += 0x80; } else if((charValue > 96) && (charValue < 123)) { charValue -= 0x20; } // else if((charValue > 129) && (charValue < 219)) { // charValue -= 0xa0; // } if(charValue == 8212) { outputStream.writeByte(0xc0); // horizontal line } else if(charValue == 8224) { // outputStream.writeByte(123); // line cross outputStream.writeByte(219); // line cross } else if(charValue == 0x7c) { outputStream.writeByte(221); // vertical line } else if(charValue == 0x7e) { // outputStream.writeByte(222); // checkerboard / pi // outputStream.writeByte(255); // checkerboard / pi outputStream.writeByte(166); // checkerboard } else if(charValue > 255) { System.out.println("WARNING: untranslated UTF8 char: "+thischar+ " "+(int)thischar); outputStream.writeByte(thischar); } else { outputStream.writeByte(charValue); } } catch (IOException ex) { ex.printStackTrace(); } return 0; } public static void toText(String inputFile, String outputFile) { long[] posList = new long[MAXPAGES]; try ( RandomAccessFile inputStream = new RandomAccessFile(new File(inputFile),"r"); FileOutputStream outStream = new FileOutputStream(outputFile); OutputStreamWriter writer = new OutputStreamWriter(outStream, "UTF-8"); BufferedWriter outputStream = new BufferedWriter(writer); ) { byte[] shortCutBlock = new byte[5]; int iPagNum = 0; try { do { long pos = inputStream.getFilePointer(); posList[iPagNum/10] = pos;iPagNum+=10; while (inputStream.read(shortCutBlock) >= 0) { if (shortCutBlock[0] == 0) break; } while (inputStream.readByte() != 0) {}; } while(true); } catch (EOFException ex) { } inputStream.seek(0); int iPagNum2 = 0; long filesize = inputStream.length(); try { do { String headText = String.format("\r\r",iPagNum2);iPagNum2+=10; outputStream.write(headText); while (inputStream.read(shortCutBlock) >= 0) { byte shortCut = shortCutBlock[0]; if (shortCut == 0) break; long offSet = getOffset(shortCutBlock); int pagNumLink = getLink(posList,offSet,iPagNum/10) * 10; outputStream.write("<"); if (shortCut>=65 && shortCut<=90) { outputPetsciiByte(outputStream, shortCutBlock[0]); } else { String shortText = String.format("$%02X",((int)shortCut) & 0xFF); outputStream.write(shortText); } String linkText = String.format(" %04d",pagNumLink); outputStream.write(linkText); outputStream.write(">"); } outputStream.write("\n"); byte inByte; int iCharCount = 0; while ((inByte = inputStream.readByte()) != 0) { int inInt = ((int)inByte) & 0xFF; if ((inInt != 13)) { outputPetsciiByte(outputStream, inByte); } else { outputStream.write("\n"); } if (inInt == 13) { iCharCount = 0; } else if (inInt > 9) { iCharCount++; } // When we have a full screen line add a CR if (iCharCount == 40) { iCharCount = 0; outputStream.write("\n"); } } } while(inputStream.getFilePointer()= 0) { String pagNumStr = line.substring(pageIndex+6,pageIndex+10); iPagNum = Integer.parseInt(pagNumStr); if (posList[iPagNum] != 0) { System.out.println("Double page number defined: "+iPagNum); System.exit(-1); } posList[iPagNum] = outputStream.getFilePointer(); iMode = 1; } break; case 1: int linkIndex = line.indexOf("<"); if (linkIndex >= 0) { do { byte hotKey = 0; if (line.charAt(linkIndex+1) == '$') { int hexNum = Integer.parseInt(line.substring(linkIndex+2,linkIndex+4),16); hotKey = (byte)hexNum; linkIndex+=5; } else { hotKey = (byte)line.charAt(linkIndex+1); // convert ascii->petscii if((hotKey > 64) && (hotKey < 91)) { hotKey += 0x80; } else if((hotKey > 96) && (hotKey < 123)) { hotKey -= 0x20; } linkIndex+=3; } int linkPage = Integer.parseInt(line.substring(linkIndex,linkIndex+4),10); shortCutBlock[0] = hotKey; shortCutBlock[1] = (byte)(linkPage & 0xff); shortCutBlock[2] = (byte)((linkPage >> 8) & 0xff); shortCutBlock[3] = 0; shortCutBlock[4] = 0; outputStream.write(shortCutBlock); linkIndex = line.indexOf("<",linkIndex+4); } while (linkIndex > 0); Arrays.fill( shortCutBlock, (byte)0); outputStream.write(shortCutBlock); iMode = 2; } break; case 2: pageIndex = line.indexOf("= 0) { // Remove CR at the end of the previous page if (!lastLineFull) { outputStream.seek(outputStream.getFilePointer()-1); } outputStream.writeByte(0); String pagNumStr = line.substring(pageIndex+6,pageIndex+10); iPagNum = Integer.parseInt(pagNumStr); posList[iPagNum] = outputStream.getFilePointer(); iMode = 1; } else { while ((pageIndex = line.indexOf("= 0) { int color = Integer.parseInt(line.substring(pageIndex + 3,pageIndex + 5),16); char[] chArray = new char[1]; chArray[0]=(char)color; line = line.substring(0,pageIndex) + new String(chArray) + line.substring(pageIndex+6); } for(int i=0;i 9) { lineLength++; } } // Add CR only when screen line not full if (lineLength != 40) { outputStream.writeByte(13); lastLineFull = false; } else { lastLineFull = true; } } break; } } outputStream.writeByte(0); outputStream.seek(0); try { do { while (outputStream.read(shortCutBlock) >= 0) { if (shortCutBlock[0] == 0) break; int iPage = getPageNum(shortCutBlock); long lOffset = posList[iPage]; shortCutBlock[1] = (byte)(lOffset & 0xff); shortCutBlock[2] = (byte)((lOffset >> 8) & 0xff); shortCutBlock[3] = (byte)((lOffset >> 16) & 0xff); outputStream.seek(outputStream.getFilePointer()-5); outputStream.write(shortCutBlock); } while (outputStream.readByte() != 0) {}; } while(true); } catch (EOFException ex) { } reader.close(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { System.out.println("SYNTAX ERROR on File page:"+iPagNum); } } public static void main(String[] args) { if (args.length < 3) { System.out.println("Usage: ChamHelp -d/e infile outfile (encode/decode)\n"); System.exit(0); } String command = args[0]; if (command.equals("-d")) { toText(args[1],args[2]); } else if (command.equals("-e")) { toHelp(args[1],args[2]); } } }