/* * File : Clic3.java * Created : 30-oct-2000 20:45 * By : fbusquets * * JClic - Authoring and playing system for educational activities * * Copyright (C) 2000 - 2005 Francesc Busquets & Departament * d'Educacio de la Generalitat de Catalunya * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details (see the LICENSE file). */ package edu.xtec.jclic.clic3; import edu.xtec.jclic.Activity; import edu.xtec.jclic.bags.ActivityBag; import edu.xtec.jclic.bags.ActivitySequenceElement; import edu.xtec.jclic.bags.ActivitySequenceJump; import edu.xtec.jclic.bags.ConditionalJumpInfo; import edu.xtec.jclic.bags.JumpInfo; import edu.xtec.jclic.bags.MediaBagElement; import edu.xtec.jclic.fileSystem.FileSystem; import edu.xtec.jclic.fileSystem.PCCFileSystem; import edu.xtec.jclic.project.JClicProject; import java.awt.Color; import java.util.ArrayList; /** * Constants and miscellaneous functions useful to read Clic 3.0 files and import * it into JClic projects. * @author Francesc Busquets (fbusquets@xtec.cat) * @version 13.08.08 */ public abstract class Clic3 { // CONSTANTS OF CLIC 3.0 // public static final int CLICVER = 120; public static final String CLICSTR = "CLIC120"; protected static final String BLANK=""; protected static final String COMMA=","; protected static final Color DEFAULT_BACK_COLOR=new Color(0xC0, 0xC0, 0xC0); protected static final Color DEFAULT_COLOR_USUARI_0=Color.blue; protected static final Color DEFAULT_COLOR_USUARI_1=DEFAULT_BACK_COLOR; protected static final Color DEFAULT_COLOR_ERROR_0=Color.white; protected static final Color DEFAULT_COLOR_ERROR_1=Color.red; protected static final TripleColor DEFAULT_TRIPLE_COLOR=new TripleColor(Color.white, Color.black, Color.gray, false); //protected static final Font DEFAULT_FONT = new Font("Helvetica", 0, 12); //Extensions // better a hastable? public static final String extName[]={"*.BMP", "*.GIF", "*.TXT", "*.PUZ", "*.SOP", "*.ASS", "*.CRW", "*.TXA", "*.PAC", "*.PCC", "*.P?C", "*.HLP", "*.MVB", "*.EXE", "*.MID", "*.WAV", "*.JPG", "*.PNG", BLANK}; public static final int EXT_BMP=0, EXT_GIF=1, EXT_TXT=2, EXT_PUZ=3, EXT_SOP=4, EXT_ASS=5, EXT_CRW=6, EXT_TXA=7, EXT_PAC=8, EXT_PCC=9, EXT_PCG=10, EXT_HLP=11, EXT_MVB=12, EXT_EXE=13, EXT_MID=14, EXT_WAV=15, EXT_JPG=16, EXT_PNG=17, UNKNOWN_EXT=18, NO_EXT=-1; //public static final int MAX_MESS_LEN = 200; //public static final int MAX_FULL_FILENAME = 100, MAX_DESC = 200; protected static final int MAXCW = 20, MAXCH = 20; protected static final int MAXGW = 21, MAXGH = 10; // PlayModes public static final int NONE=0, PUZZLE=1, SOPA=2, ASSOCIA=3, CREUATS=4, TEXTACT=5; //PuzModes public static final int INTERC=0, FORAT=1, DOBLE=2, MEMORY=3; //AssModes public static final int NORMAL=0, ESPECIAL=1, EXPLORA=2, INFO=3, IDENTIFICA=4, ESCRIU=5; //TxActModes public static final int FORATS=0, COMPTEXT=1, IDLLETRES=2, IDPARAULES=3, BPARAULES=4, BPARAGRAFS=5; //GraPositions public static final int AB=0, BA=1, AUB=2, BUA=3; public static final int DEF_TXTCASW = 140, DEF_TXTCASH = 100, DEF_CICLES = 30; public static final int NEGRE = 160; public static final char COMODI = '*'; public static final char SEPARADOR = ';'; public static final byte ENDDESC_BYTE = -121; //0x87 //public static final char ENDDESC = '\u0000'; public static final char ENDDESC = '\u2021'; public static final char CHBLOCK = '\u008E'; public static final char CHINC = '\u008F'; public static final char SEP = '\u0090'; /* * Constants used in Clic 3.0 * not needed in JClic * * WM_USERIDS * WM_PLAYW=WM_USER, WM_HELPW, WM_CBNEXT, WM_CBPREV, WM_CBGETFOCUS, * WM_NOUINTENT}; * * enum USERMESSAGES {PLAYW_RESIZE, PLAYW_ENDGAME, PLAYW_INTENT, * PLAYW_ENCERTS, PLAYW_WAITMEIN, * PLAYW_SETMESS, PLAYW_SETMESSPLAY, HELPW_ITEMSEL, HELPW_MOUSE}; * * public static final int WDIGIT = 13, HDIGIT = 23; * public static final int MARGE = 6; * public static final int BOTOW = 63, BOTOW2 = 30, BOTOH = 39; * * public static final int DIGITSW = 39, DIGITSH = 23; * * public static final int CTRLH = 39; // era 41 * public static final int CTRLW = 450; * * public static final int CLW = (3*MARGE+BOTOW+CTRLW); * public static final int CLH = (6*BOTOH+7*MARGE); * * public static final int PLWW = CTRLW; * public static final int PLWH = (CLH-CTRLH-3*MARGE); * * public static final int LMAXRFTXT = 512; * * String resources (to be revised!) * public static final int IDS_ERRFMT = 1; * * Common variables * * public static int passos, passos0; * public static int gAjVis, encertsNow; * public static boolean quick, quick0, arrossega, arrossega0, sonor0, * autoCursor, cursorGran; * public static boolean doReportFile, drf0, exitDirect; * public static boolean helponhelp, menus; * public static boolean allowNewUsr, allowNewGrp, showUserReports; * public static int caretVis, caretX, caretY, caretW, caretH, autoCursorDelay; * public static final Font logFont1 = new Font("Arial", 0, 12); * public static int gruixfil; * * public static String clicPath = ""; * public static String ediText = ""; * public static String ediGraf = ""; * public static String originalPath = ""; * public static String playerId = ""; * public static String iniFile = ""; * public static String dbFile = ""; * public static String originalDbFile = ""; * public static String fileName0 = ""; * * extern char cdUnit; * public static String lastExt = " "; */ public static int getExt(String fileName){ int begExt; String ext; int i; //lastExt=BLANK; if(fileName==null || fileName.length()==0) return UNKNOWN_EXT; if((begExt=fileName.indexOf(' '))<0) begExt=fileName.length()-1; for(;begExt>0; begExt--) if(fileName.charAt(begExt)=='.') break; if(begExt==0 || fileName.length()-begExt<4) return UNKNOWN_EXT; ext = "*" + fileName.substring(begExt, begExt+4).toUpperCase(); for(i=0; i<UNKNOWN_EXT; i++) if(ext.equals(extName[i])) break; //lastExt=ext.substring(2,4); if(i==EXT_JPG || i==EXT_PNG) i=EXT_GIF; return i; } public static boolean isClic3Extension(int ext){ return ext==EXT_PUZ || ext==EXT_ASS || ext==EXT_SOP || ext==EXT_CRW || ext==EXT_TXA; } public static int parseIntX(String s){ return parseIntX(s, 10); } public static int parseIntX(String s, int radix){ int v; try { v=Integer.parseInt(s, radix); } catch(NumberFormatException e){ v=0; } return v; } protected static int correctColor(int color){ return (color & 0xFF)<<16 | (color & 0xFF00) | (color & 0xFF0000)>>16; } private static final String[] strArray={"FFFFFF", "000000", "808080", "C0C0C0", "0000FF", "00FF00", "FF0000"}; private static final Color[] colorArray={Color.white, Color.black, Color.gray, DEFAULT_BACK_COLOR, Color.red, Color.green, Color.blue}; protected static Color strToColor(String st){ if(st==null) return Color.black; if(st.length()>6) st=st.substring(st.length()-6); for(int i=0; i<strArray.length; i++) if(st.equals(strArray[i])) return colorArray[i]; return new Color(correctColor(parseIntX(st, 16))); } public static boolean copyArray(String[] dest, int indest, String[] source, int insource, int e, boolean upper) { int i, d, s; for(i=0, d=indest, s=insource; i<e; i++, d++, s++) { //if(dest[d]!=null) delete dest[d]; //if(source[s]==null || (dest[d]=new String(source[s]))==null) break; if((dest[d]=source[s])==null) break; if(upper) dest[d]=dest[d].toUpperCase(); } return (i==e); } public static void readPccFile(JClicProject project) throws Exception { String name; byte [] bytes; MediaBagElement mbe; int i, j; if(!(project.getFileSystem() instanceof PCCFileSystem)) throw new Exception("FileSystem is not PCCFileSystem!"); PCCFileSystem pccfs=(PCCFileSystem)project.getFileSystem(); //if(!pccfs.loaded) return; for(i=0; i<pccfs.numFiles; i++){ name=pccfs.getEntryName(i); switch(getExt(name)){ case EXT_PAC: addPacToSequence(project, name); break; case EXT_ASS: case EXT_PUZ: case EXT_SOP: case EXT_CRW: case EXT_TXA: addActivityToBag(project, name/*, pccfs*/); break; default: //do nothing; } } } private static int gcCounter=0; public static void addActivityToBag(JClicProject project, String name) throws Exception { ActivityBag ab=project.activityBag; String normalizedName=FileSystem.stdFn(name); if(ab.getElementByName(normalizedName)!=null) return; Clic3Activity c3a=new Clic3Activity(project); try{ c3a.load(normalizedName, project.getFileSystem().getBytes(normalizedName)); } catch(Exception e){ System.err.println("Error loading " + normalizedName + "\n"+e); throw e; } switch(c3a.actMode){ case ASSOCIA: switch(c3a.puzMode){ case INFO: c3a.className="edu.xtec.jclic.activities.panels.InformationScreen"; break; case NORMAL: c3a.className="edu.xtec.jclic.activities.associations.SimpleAssociation"; break; case ESPECIAL: c3a.className="edu.xtec.jclic.activities.associations.ComplexAssociation"; break; case IDENTIFICA: c3a.className="edu.xtec.jclic.activities.panels.Identify"; break; case EXPLORA: c3a.className="edu.xtec.jclic.activities.panels.Explore"; break; case ESCRIU: c3a.className="edu.xtec.jclic.activities.text.WrittenAnswer"; break; } break; case PUZZLE: switch(c3a.puzMode){ case INTERC: c3a.className="edu.xtec.jclic.activities.puzzles.ExchangePuzzle"; break; case DOBLE: c3a.className="edu.xtec.jclic.activities.puzzles.DoublePuzzle"; break; case MEMORY: c3a.className="edu.xtec.jclic.activities.memory.MemoryGame"; break; case FORAT: c3a.className="edu.xtec.jclic.activities.puzzles.HolePuzzle"; break; } break; case SOPA: c3a.className="edu.xtec.jclic.activities.textGrid.WordSearch"; break; case CREUATS: c3a.className="edu.xtec.jclic.activities.textGrid.CrossWord"; break; case TEXTACT: switch(c3a.puzMode){ case FORATS: c3a.className="edu.xtec.jclic.activities.text.FillInBlanks"; break; case COMPTEXT: c3a.className="edu.xtec.jclic.activities.text.Complete"; break; case IDLLETRES: case IDPARAULES: c3a.className="edu.xtec.jclic.activities.text.Identify"; break; case BPARAULES: case BPARAGRAFS: c3a.className="edu.xtec.jclic.activities.text.Order"; break; } break; default: throw new IllegalArgumentException("Unknown Clic3 activity!"); } // WARNING: ab.addActivity(Activity.getActivity(c3a, project)); // clear memory project.mediaBag.clearData(); // force a gc every 50 activities if((++gcCounter%50)==0) System.gc(); } public static void addPacToSequence(JClicProject project, String name) throws Exception { String normalizedName=FileSystem.stdFn(name); if(project.activitySequence.getElementByTag(normalizedName, false)!=null) return; FileSystem fs=project.getFileSystem(); Clic3Pac c3p=new Clic3Pac(); try{ c3p.load(normalizedName, fs.getBytes(normalizedName)); } catch(Exception e){ System.err.println("Error loading " + normalizedName + "\n"+e); throw e; } for(int i=0; i<c3p.nActs; i++){ ActivitySequenceElement ase=new ActivitySequenceElement(c3p.acts[i], c3p.lapToPass, c3p.noPassButtons ? ActivitySequenceElement.NAV_NONE : ActivitySequenceElement.NAV_BOTH); if(i==0){ ase.backJump=new ActivitySequenceJump(JumpInfo.STOP); //if(as.getCurrentActNum()<0) // as.setCurrentAct(as.size()); ase.setTag(normalizedName); ase.setDescription(c3p.fileDesc!=null && c3p.fileDesc.length()>0 ? c3p.fileDesc : null); } if(i==c3p.nActs-1){ ActivitySequenceJump asj=new ActivitySequenceJump(JumpInfo.STOP); if(c3p.def==true && c3p.chDef!=null && getExt(c3p.chDef)==EXT_PAC){ //s=fs.getRelativePathOf(relativePath+c3p.chDef)+FileSystem.getFileNameOf(c3p.chDef); asj.action=JumpInfo.JUMP; asj.sequence=fs.getCanonicalNameOf(c3p.chDef); if(c3p.sup){ //s=fs.getRelativePathOf(relativePath+c3p.chSup)+FileSystem.getFileNameOf(c3p.chSup); asj.setConditionalJump( new ConditionalJumpInfo(JumpInfo.JUMP, fs.getCanonicalNameOf(c3p.chSup), c3p.supP, c3p.supTFlag ? c3p.supT : -1), true); } if(c3p.inf){ //s=fs.getRelativePathOf(relativePath+c3p.chInf)+FileSystem.getFileNameOf(c3p.chInf); asj.setConditionalJump( new ConditionalJumpInfo(JumpInfo.JUMP, fs.getCanonicalNameOf(c3p.chInf), c3p.infP, c3p.infTFlag ? c3p.infT : -1), false); } } ase.fwdJump=asj; } project.activitySequence.add(ase); } } public static final String ansiChars= "\u20ac\u0081\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0160\u2039\u0152"+ "\u008d\u008e\u008f\u0090\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122"+ "\u0161\u203a\u0153\u009d\u009e\u0178"; public static final String convertFileChars= "e__f______s_c_z__________ts_o_zy_icl_y_s_ca___r_o_23__p__10_423_aaaaaaa"+ "ceeeeiiiidnooooox0uuuuy_baaaaaaaceeeeiiiionooooo_0uuuuy_y"; // 0x80 is Euro: 20ac // bytes used: //ENDDESC 0x87 ('\u2021') //CHBLOCK 0x8E (not used) //CHINC 0x8F (not used) //SEP ? 0x90 (not used) public static String bufferToString(byte[] buffer){ if(buffer==null || buffer.length==0) return BLANK; char[] result=new char[buffer.length]; int v; for(int i=0; i<buffer.length; i++){ v=buffer[i]&0x00FF; if(v<128 || v>=160) // warning: direct map from int to char result[i]=(char)v; //result[i]=(char)(v==45 ? 95 : v); else result[i]=ansiChars.charAt(v-128); } return new String(result); } public static String[] dataToArray(byte[] data) { ArrayList<String> v=new ArrayList<String>(); int i, j, k, l, l2; byte[] buffer; String[] array; int nLines=0; l=data.length; for(i=0, j=0; i<l;){ for(; j<l; j++) if(data[j]==0x0D || data[j]==0x0A) break; l2=j-i; if(l2>0){ buffer = new byte[l2]; for(k=0;k<l2;k++) buffer[k]=data[i+k]; v.add(bufferToString(buffer)); //v.add(new String(buffer)); //try{ // v.add(new String(buffer, "Cp1252")); //} catch(Exception ex){ // v.add(new String(buffer)); //} } else v.add(BLANK); nLines++; if(j<l && data[++j]==0x0A) j++; i=j; } if(nLines>0){ array=new String[nLines]; try { array=v.toArray(array); } catch(ArrayStoreException e){ array=null; } } else array=null; return array; } //public static final String validFileChars=".:-_!~\\0123456789abcdefghijklmnopqrstuvwxyz"; public static final String validFileChars=".:\\_!~0123456789abcdefghijklmnopqrstuvwxyz"; public static String validFileName(String name, boolean strict, boolean remember){ if(name==null) return null; StringBuilder sb=new StringBuilder(); for(char ch : name.toCharArray()){ if(validFileChars.indexOf(ch, strict ? 3 : 0)<0){ if(ch>='A' && ch<='Z') ch=Character.toLowerCase(ch); else if(ch=='/') ch='\\'; else{ int k=ansiChars.indexOf(ch); if(k>=0) ch=convertFileChars.charAt(k); else ch='_'; } } sb.append(ch); } String result=sb.substring(0); if(remember && !result.equals(name)){ FileSystem.altFileNames.put(result, name); } return result; } public static String validFileName(String name){ return validFileName(name, false, true); } public static String pacNameToLowerCase(String s){ String result=s; if(s!=null){ String l=s.toLowerCase(); if(l.endsWith(".pac")||l.endsWith(".pcc")) result=l; } return result; } }