home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 June / MacFormat 25.iso / Shareware City / Applications / ZX Loader 1.0.4 / ZX Loader source / Application.c next >
Encoding:
C/C++ Source or Header  |  1995-02-26  |  47.3 KB  |  1,512 lines  |  [TEXT/MMCC]

  1.  
  2. /* -----    ZX Spectrum Tape Converter ---------------------------------------------
  3.             
  4.     This program reads ZX Spectrum program and data tapes and converts 
  5.     them into 'Tape' files for many ZX Spectrum emulators
  6.             
  7.     Final Release - Version 1.0.3
  8.             
  9. ***    Copyright (C) 1994-1995 Günter Woigk alias KIO !
  10.     snail mail:    Günter Woigk; Nürnberger Str. 79; 91052 Erlangen; Germany
  11.     e-mail:        ea215@fim.uni-erlangen.de
  12.             
  13.     01.07.94    started work                                            (KIO !)
  14.     25.11.94    killed my audio input with high voltage on protective ground
  15.                 virtually nothing is impossible!                        (KIO !)
  16.     17.12.94    last review - Bach composed masterpieces being deaf        (KIO !)
  17.     21.01.95    replaced old tape file format with .tap file format        (KIO !)
  18.     22.01.95    added code to set sound input to mono/8bit/22kHz        (KIO !)    
  19.     26.Feb.95    Metrowerk C version                                        (KIO !)
  20.  
  21. ***    This program is free software; you can redistribute it and/or modify
  22.     it under the terms of the GNU General Public License as published by
  23.     the Free Software Foundation; either version 2 of the License, or
  24.     (at your option) any later version.
  25.     
  26. ***    This program is distributed in the hope that it will be useful,
  27.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  28.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  29.     See the GNU General Public License for more details.
  30.  
  31.     You should have received a copy of the GNU General Public License
  32.     along with this program; if not, write to the Free Software
  33.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  34. */
  35.  
  36. #include    <ToolUtils.h>
  37. #include    <resources.h>
  38. #include    <soundinput.h>
  39. #include    <palettes.h>
  40. #include    <files.h>
  41. #include    <types.h>
  42. #include    <FixMath.h>
  43. #include    <quickdraw.h>
  44. #include    "kio.h"
  45. #include    "mem.h"
  46. #include    "application.h"
  47. #include    "eventloop.h"
  48. #include    "console.h"
  49.  
  50. // -----    General settings --------------------------------------------------
  51. short        verbose         = 1;    // verbosity
  52.  
  53. // -----    Settings for signal decoding -------------------------------------
  54. #define        minh            10        // min. length for 1/2 header period    (10)
  55. #define        maxh            17        // max. length for 1/2 header period    (16)
  56.  
  57. #define        mins            2        // min. length for 1/2 sync                (3)
  58. #define        maxs            9        // max. length for 1/2 sync                (9)
  59.  
  60. #define        min0            2        // min. length for 1/2 0-bit period        (3)
  61. #define        max0            9        // max. length for 1/2 0-bit period        (9)
  62.  
  63. #define        min1            6        // min. length for 1/2 1-bit periode    (6)
  64. #define        max1            16        // max. length for 1/2 1-bit periode    (14)
  65.  
  66. #define        mn                2        // minimal codable run length            (2)
  67. #define        mx                17        // mn+15
  68. #define        llsize            19        // mx+mn
  69.  
  70. // -----    The Spectrum window -----------------------------------------------
  71. short        show_spectrum     = true;
  72. WindowPtr    spectrumfenster    = nil;
  73. RgnHandle    borderRgn        = nil;
  74. RgnHandle    screenRgn        = nil;
  75. CGrafPtr    screenshot        = nil;
  76. Char*        spectrumbitmap    = nil;
  77. Boolean        flashing        = false;
  78. short        flashphase        = 0;
  79. short        borderstate        = 0;
  80. #define        spectrumID        129        // Resource ID of window
  81. short        hborder            = 32;    // 16*n; border left & right for spectrum window
  82. short        vborder            = 16;    // 16*n; border top & bottom for spectrum window
  83. long        idxp[512];                // konvertiert attr+flashing -> paper color index
  84. long        idxi[512];                // konvertiert attr+flashing -> ink color index
  85. RGBColor    rgb[16];                // rgb colors
  86.  
  87. // -----    Statistics window #1: Byte distribution -----------------------------------
  88. short        show_distribution     = 0;
  89. WindowPtr    bytedistributionwindow    = nil;
  90. #define        statistikID            130        // resource ID
  91. long         cnt[256];                     // for calculating
  92.  
  93. // -----    Statistics window #2: Run length distribution -----------------------------
  94. short        show_run_length     = 1;
  95. WindowPtr    runlengthwindow        = nil;
  96. #define        statistik2ID        131
  97. long        hi[llsize], lo[llsize];        // for calculating
  98.  
  99. // -----    Sound recording -----------------------------------------------------------
  100. short         plth_volume         = 1;    // play through volume
  101. short         auto_gain_control     = 0;    // automatic gain control ?
  102.  
  103. // -----    Used files ---------------------------------------------------------------
  104. FSSpec        soundfile;                    // AIFF File containing sound data
  105. FSSpec        rlesfile;                    // run legth encoded square wave sound
  106. FSSpec        tapefile;                    // segment header framed binary data
  107. FSSpec        logfile;
  108. short        soundID            = 0;
  109. short        tapeID            = 0;
  110. short        dataID            = 0;
  111.  
  112. short                 oDocNumTypes    = 2;        // number of file types to use for OPEN
  113. SFTypeList             oDocTypeList    = {'AIFF','RLES',0,0};    // list of file types 
  114.  
  115. // -----    "Local" Variables for LoadTape() -------------------------------------------
  116. Char        crc, *p, *d;                // used inside LoadTape()
  117. Char*         mh = nil;                    // Buffer for rle-Bytes to decode; inside LoadTape()
  118. Char*        dh = nil;                    // Buffer for decoded Bytes; inside LoadTape()
  119.  
  120. // -----    Event Loop ---------------------------------------------------------------
  121. Boolean     done            = false;    // done:=TRUE to QuitApplication()
  122. long        eventDelay        = 999;        // delay/60s until HandleNull() called
  123. RgnHandle    eventRegion        = nil;        // fence for HandleREGION() calls
  124. EventRecord    event;                        // event returned by WaitNextEvent
  125.  
  126. // ----- Menu bar -------------------------------------------------------------------
  127. short        mApple        =    128;    
  128. short            iAbout    =        1;        
  129. short            iHelp    =        2;
  130. short        mFile        =    129;
  131. short            iNew    =        1;
  132. short            iOpen    =        2;
  133. short             iSave    =        3;
  134. short            iClose    =        4;
  135. short            iPrint    =        99;
  136. short            iQuit    =        6;
  137. short        mEdit        =    130;    
  138. short            iUndo    =        1;
  139. short            iCut    =        3;
  140. short            iCopy    =        4;
  141. short            iPaste    =        5;
  142. short        mOptions    =    131;
  143.  
  144. // ----- et cetera ----------------------------------------------------------------
  145. #define        mem_magic        'KIO!'
  146.  
  147.  
  148. // ----- prototypes ---------------------------------------------------------------
  149. CloseAll();
  150. PrintNum ( long n )    ;
  151. ErrMsg ( Str255 s );
  152. long *Malloc ( Char **pp, long n );
  153. MFree ( Char **pp );
  154. AddItem ( MenuHandle m, Str255 eng, Str255 deu );
  155. InitSpectrumColors();
  156. RedrawFlashingColors();
  157. RedrawSpectrumscreen();
  158. RedrawSpectrumborder ();
  159. RefreshSpectrumfenster();
  160. CloseSpectrumWindow ();
  161. OpenSpectrumWindow();
  162. RefreshByteDistributionWindow();
  163. CloseStatistikfenster ();
  164. OpenByteDistributionWindow ( );
  165. CloseRunLengthWindow ();
  166. RefreshRunLengthWindow();
  167. OpenRunLengthWindow ( );
  168. HandleSomeMouseDown();
  169. RefreshWindows();
  170. DisplayScreenshot();
  171. Notify ( Str255 eng, Str255 ger );
  172. Short NextByte (Boolean v);
  173. ChangeBorder ( long n );
  174. LoadTape();
  175. RunLengthCompression();
  176.  
  177.  
  178. /* ********************************************************************** */
  179. /* ******************     S U B R O U T I N E S      ******************** */
  180. /* ********************************************************************** */
  181.  
  182.  
  183. // -----    Close all files -------------------------------------------------------
  184. CloseAll()
  185. {    if (soundID) { FSClose(soundID); soundID=0; };
  186.     if (tapeID) { FSClose(tapeID); tapeID=0; };
  187.     if (dataID) { FSClose(dataID); dataID=0; };
  188. }
  189.  
  190.  
  191. // -----    Print number ----------------------------------------------------------
  192. PrintNum ( long n )    
  193. {    Str255 s; 
  194.     NumToString ( n, s ); 
  195.     ConPrint(s,0); 
  196. }
  197.  
  198.  
  199. // -----    Print standard error message -------------------------------------------
  200. ErrMsg ( Str255 s )
  201. {    ConPrint("\pAn error occured: ","\pEin Fehler ist aufgetreten: ");
  202.     ConMsg(s,0);
  203. }
  204.  
  205.  
  206. // -----    Allocate n bytes and lock 'em ------------------------------------------
  207. //        pp -> Master pointer for memory block
  208. //        if *pp is not nil on entry, the old memory block will be released first
  209. long *Malloc ( Char **pp, long n )
  210. {    long **h; OSErr err;
  211.     if ( *pp != nil ) MFree(pp);
  212.     h = (long**)TempNewHandle(n+8, &err);
  213.     if ( h==nil || err ) return;            // Master pointer = *pp := nil
  214.     HLock ( (Handle)h );                    // don't move around
  215.     (*h)[0] = mem_magic;                    // private mem_hdr: magic
  216.     (*h)[1] = (long)h;                        // private mem_hdr: backpointer to handle
  217.     *pp = ((Char*)*h)+8;                    // Master pointer = *pp := address of buffer + 8
  218. }
  219.  
  220. // -----    Release memory block -------------------------------------------------
  221. //        pp -> Master pointer of the memory block
  222. MFree ( Char **pp )
  223. {    long *p;
  224.     if ( *pp == nil ) return;            // there's nothing
  225.     p = (*((long**)pp))-2;                // p -> private mem_hdr
  226.     *pp = nil;                            // clear pointer
  227.     if ( p[0] != mem_magic ) return;    // internal error !
  228.     p[0] = 0;                            // clear magic
  229.     DisposeHandle ( (Handle)p[1] );        // release memory
  230. }
  231.  
  232.  
  233. /* *************************************************************************** */
  234. /* ********************     A P P L I C A T I O N      *********************** */
  235. /* *************************************************************************** */
  236.  
  237.  
  238. AddItem ( MenuHandle m, Str255 eng, Str255 deu )
  239. {
  240.     if ( (german&&deu) || !eng ) AppendMenu ( m, deu );
  241.     else AppendMenu( m, eng );
  242. }
  243.  
  244. InitMenuBar ()
  245. {    MenuHandle am, fm, em, om;
  246.     am = NewMenu ( mApple, "\p\024" );
  247.     fm = NewMenu ( mFile, "\pFile" );
  248.     em = NewMenu ( mEdit, "\pEdit" );
  249.     om = NewMenu ( mOptions, "\pOptions" );
  250.     
  251.     AddItem         ( am, "\pAbout ZX Tape Loader ...","\pÜber den ZX Loader ..." );
  252.     AddResMenu    ( am, 'DRVR' );
  253.         
  254.     AddItem     ( fm, "\pRecord new sound/N",        "\pNeuen Sound aufnehmen/N" );
  255.     AddItem     ( fm, "\pOpen file/O",                "\pDatei öffnen/O" );
  256.     AddItem     ( fm, "\pSave file/S",                "\pDatei speichern/S" );
  257.     AddItem     ( fm, "\pClose top window/W",        "\pOberstes Fenster schließen/W" );
  258.     AppendMenu    ( fm, "\p-" );
  259.     AddItem        ( fm, "\pQuit/Q",                    "\pProgramm beenden/Q" );
  260.  
  261.     AddItem     ( om, "\pSpeaker off",                "\pLautsprecher aus" );
  262.     AddItem     ( om, "\pSpeaker on",                "\pLautsprecher ein" );
  263.     AppendMenu    ( om, "\p-" );
  264.     AddItem     ( om, "\pMaximum recording volume",    "\pMaximale Aufnahmelautstärke" );
  265.     AddItem     ( om, "\pAutomatic gain control",    "\pAutomatische Aussteuerung" );
  266.     AppendMenu    ( om, "\p-" );
  267.     AddItem     ( om, "\pShort protocol",            "\pKurzes Protokoll" );
  268.     AddItem     ( om, "\pMedium protocol",            "\pNormales Protokoll" );
  269.     AddItem     ( om, "\pLong protocol",            "\pAusführliches Protokoll" );
  270.     AppendMenu     ( om, "\p-" );
  271.     AddItem     ( om, "\pByte distribution window off",            "\pByteverteilungsstatistik aus" );
  272.     AddItem     ( om, "\pByte distribution window on",            "\pByteverteilungsstatistik ein" );
  273.     AppendMenu     ( om, "\p-" );
  274.     AddItem     ( om, "\pRun length distribution window off",    "\pPhasenlängenstatistik aus" );
  275.     AddItem     ( om, "\pRun length distribution window on",    "\pPhasenlängenstatistik ein" );
  276.     AppendMenu    ( om, "\p-" );
  277.     AddItem     ( om, "\pZX Spectrum window off",                "\pZX Spectrum Fenster aus" );
  278.     AddItem        ( om, "\pZX Spectrum window on",                "\pZX Spectrum Fenster ein" );
  279.  
  280.     InsertMenu ( am, 0 );
  281.     InsertMenu ( fm, 0 );
  282.     InsertMenu ( em, 0 );
  283.     InsertMenu ( om, 0 );
  284.     DrawMenuBar();
  285. }
  286.  
  287.  
  288. // =====    Quit program ======================================================
  289. HandleQUIT()            
  290.     done = 1; 
  291. }
  292.  
  293.  
  294. // =====    Initialize application ===================================================
  295. HandleINIT(long how)        // how = 'OAPP' or 'ODOC'
  296. {    
  297.     ConMsg("\p**** THE LONG AWAITED ZX SPECTRUM LOADER !",0);
  298.     ConMsg("\p**** Version 1.0 Copyright (c) 1994 G.Woigk",0);
  299.     ConNL();
  300.     ConMsg("\p**** Read in Your ZX Spectrum Tapes to Harddisk",        "\p**** Speichert ZX Spectrum Kassetten auf Harddisk");
  301.     ConMsg("\p**** Free for non commercial use & distribution",        "\p**** Frei für private Verwendung und Vertrieb");
  302.     ConNL();
  303.     ConMsg("\pThis program is free software; you can redistribute",0);
  304.     ConMsg("\pit and/or modify it under the terms of the GNU",0);
  305.     ConMsg("\pGeneral Public License as published by the Free",0);
  306.     ConMsg("\pSoftware Foundation; either version 2 of the",0);
  307.     ConMsg("\pLicense, or (at your option) any later version.",0);
  308.     ConNL();
  309.     ConMsg("\pThis program is distributed in the hope that it will",0);
  310.     ConMsg("\pbe useful, but WITHOUT ANY WARRANTY; without even",0);
  311.     ConMsg("\pthe implied warranty of MERCHANTABILITY or FITNESS",0);
  312.     ConMsg("\pFOR A PARTICULAR PURPOSE. See the GNU General Public",0);
  313.     ConMsg("\pLicense for more details.",0);
  314.     ConNL();
  315.     ConMsg("\pYou should have received a copy of the GNU General",0);
  316.     ConMsg("\pPublic License along with this program; if not,",0);
  317.     ConMsg("\pwrite to the Free Software Foundation, Inc.,",0);
  318.     ConMsg("\p675 Mass Ave, Cambridge, MA 02139, USA.",0);
  319.     ConNL();
  320.     ConMsg("\pGünter Woigk; Nürnberger Str. 79; D-91052 Erlangen",0);
  321.     ConMsg("\pe-mail: ea215@fim.uni-erlangen.de",0);
  322.     ConNL();
  323.     ConCrsr(true);
  324.  
  325.     HandleMENU(131,2);        // speaker := on
  326.     HandleMENU(131,4);        // volume := max
  327.     HandleMENU(131,8);        // verbosity := medium
  328.     HandleMENU(131,11);        // don't show byte distribution window
  329.     HandleMENU(131,15);        // show run length distribution window
  330.     HandleMENU(131,18);        // show Spectrum window
  331.  
  332.     InitSpectrumColors();
  333.     OpenSpectrumWindow();
  334. }
  335.  
  336.  
  337. // -----    Refresh window -----------------------------------------------
  338. HandleUPDATE(WindowPtr window)    
  339. {    
  340.     if (window==conWindow)                 return ConRfsh();
  341.     if (window==bytedistributionwindow)    return RefreshByteDistributionWindow();
  342.     if (window==runlengthwindow)         return RefreshRunLengthWindow();
  343.     if (window==spectrumfenster)        return RefreshSpectrumfenster();
  344. }
  345.  
  346.  
  347. // -----    Close top window ----------------------------------------------
  348. HandleCLOSE()    
  349.     if (FrontWindow()==conWindow)                return ConClose();
  350.     if (FrontWindow()==bytedistributionwindow)    return CloseStatistikfenster();
  351.     if (FrontWindow()==runlengthwindow)            return CloseRunLengthWindow();
  352.     if (FrontWindow()==spectrumfenster)            return CloseSpectrumWindow();
  353. }
  354.  
  355.  
  356. // -----    Null Event: Flash flashing colors ----------------------------------------
  357. HandleNULL()            
  358. {     static Long oldTick=0; Long tick;
  359.  
  360.     if ( !spectrumfenster || !spectrumbitmap || !flashing )     // nothing to do
  361.     {    eventDelay = 120;
  362.         return;
  363.     };
  364.     
  365.     tick=TickCount();
  366.     if ( tick >= oldTick+64 )
  367.     {    oldTick=tick;
  368.         RedrawFlashingColors();        
  369.         flashphase = 256 - flashphase;            // toggle  0  <->  256
  370.     };
  371.     eventDelay = 64 - (tick & 63);
  372. }
  373.  
  374. // -----    Open "Tape", "RLES" or "AIFF" file ------------------------------------
  375. HandleOPEN(FSSpec file)
  376. {     OSErr err; FInfo info; long i;
  377.  
  378.     ConPrint("\pOpen file: ","\pÖffne Datei: "); ConMsg(file.name,0);
  379.  
  380.     err = FSpGetFInfo ( &file, &info );
  381.     if (err) return ErrMsg("\pFSpGetFInfo()");
  382.  
  383.     CloseAll();
  384.     ConCrsr(false);                        // no cursor to indicate 'busy'
  385.  
  386.     if (info.fdType=='AIFF')            // Sound-File
  387.     {    soundfile = file; 
  388.         OpenByteDistributionWindow();
  389.         CloseAll();
  390.         RunLengthCompression();
  391.         CloseAll();
  392.     }
  393.     else if (info.fdType=='RLES')        // run length compressed square wave sound file
  394.     {    rlesfile = file;
  395.         CloseStatistikfenster();
  396.     }
  397.     else
  398.     {    ConMsg("\pThis cannot happen!",0);
  399.         ConCrsr(true);
  400.         return;
  401.     };
  402.         
  403.     OpenRunLengthWindow();
  404.     CloseAll();
  405.     LoadTape();
  406.     CloseAll();
  407.     ConNL();
  408.     ConCrsr(TRUE);
  409. }
  410.  
  411.  
  412. // -----    Handle menu selection -----------------
  413. HandleMENU ( long menu, long item ) 
  414. {    MenuHandle m;
  415.     if ( menu==mOptions ) 
  416.     {    m = GetMHandle(menu); if (!m) return;
  417.         switch (item)
  418.         {    case 1:    // Speaker off    
  419.             case 2:    // Speaker on    
  420.                 plth_volume = item-1; 
  421.                 CheckItem ( m, 1, !plth_volume ); 
  422.                 CheckItem ( m, 2, plth_volume ); 
  423.                 break;
  424.             case 4:    // max. volume
  425.             case 5:    // autom. gain control
  426.                 auto_gain_control = item-4;
  427.                 CheckItem ( m, 4, !auto_gain_control ); 
  428.                 CheckItem ( m, 5, auto_gain_control ); 
  429.                 break;
  430.             case 7:    /* verbose 0 */        
  431.             case 8:    /* verbose 1 */
  432.             case 9:    /* verbose 2 */        
  433.                 verbose = item-7;
  434.                 CheckItem ( m, 7, verbose==0 ); 
  435.                 CheckItem ( m, 8, verbose==1 ); 
  436.                 CheckItem ( m, 9, verbose==2 ); 
  437.                 break;
  438.             case 11:    /* hide byte distribution */
  439.                 CloseStatistikfenster();
  440.             case 12:    /* show byte distribution */    
  441.                 show_distribution = item-11;
  442.                 CheckItem ( m, 11, !show_distribution);
  443.                 CheckItem ( m, 12, show_distribution);
  444.                 break;
  445.             case 14:    /* hide run length distribution */
  446.                 CloseRunLengthWindow();
  447.             case 15:    /* show run length distribution */            
  448.                 show_run_length = item-14;
  449.                 CheckItem ( m, 14, !show_run_length);
  450.                 CheckItem ( m, 15, show_run_length);
  451.                 break;
  452.             case 17:    /* hide specci */        
  453.                 CloseSpectrumWindow();
  454.             case 18:    /* show speci */        
  455.                 show_spectrum = item-17; 
  456.                 CheckItem ( m,17,  !show_spectrum);
  457.                 CheckItem ( m,18, show_spectrum);
  458.                 break;
  459.         };
  460.         return;
  461.     };
  462. }
  463.  
  464.  
  465. // *********************************************************************
  466. //          Handling the Spectrum window
  467. // *********************************************************************
  468.  
  469.  
  470. // -----    Init Spectrum color tables ------------------------------------
  471. InitSpectrumColors()
  472. {    Short c,i;
  473.  
  474.     for ( i=0; i<16; i++ )                  // rgb tables
  475.     {    c=0xcccc; if (i>=8) c=0xffff;
  476.         rgb[i].blue      = (i&1) ? c : 0;
  477.         rgb[i].red       = (i&2) ? c : 0;
  478.         rgb[i].green = (i&4) ? c : 0;
  479.     };
  480.     
  481.     for ( i=0; i<128; i++ )
  482.     {    idxi[i]  =  idxi[i+128]  =  idxi[i+256]  =  idxp[i+384]  =  (i&7)+((i>>3)&8);    
  483.         idxp[i]  =  idxp[i+128]  =  idxp[i+256]  =  idxi[i+384]  =  (i>>3)&0xf;
  484.     };
  485. }
  486.  
  487.  
  488. // -----    Redraw flashing character positions -----------------------------------
  489. RedrawFlashingColors()
  490. {    short x,y,i,j; Char *attr; Pattern p; Rect r;
  491.     
  492.      if ( !spectrumfenster || !spectrumbitmap || !flashing ) return;        // nothing to do
  493.  
  494.     SetPort ( spectrumfenster );
  495.  
  496.     attr = spectrumbitmap+6144;
  497.     SetRect ( &r, hborder,vborder, hborder+8,vborder+8 );
  498.     for ( y=0; y<24; y++ )
  499.     {    for ( x=0; x<32; x++ )
  500.         {    if (*attr&0x80)
  501.             {    RGBForeColor ( rgb + idxi[flashphase+*attr] );        
  502.                 RGBBackColor ( rgb + idxp[flashphase+*attr] );    
  503.                 j = ((y&0x0007)<<5) + ((y&0x0018)<<8) + x;
  504.                 for ( i=0; i<8; i++ )
  505.                 {    p.pat[i] = spectrumbitmap[j];         
  506.                     j += 256;
  507.                 };
  508.                 FillRect ( &r, &p );
  509.             };
  510.             attr++; r.left+=8; r.right+=8;
  511.         };
  512.         r.left=hborder; r.right=hborder+8;
  513.         r.top+=8; r.bottom+=8;
  514.     };
  515. }
  516.  
  517.  
  518. // -----    Redraw the inner part of the Spectrum window --------------------------
  519. RedrawSpectrumscreen()
  520. {    short x,y,i,j; Char *attr; Pattern p; Rect r;
  521.         
  522.      if (!spectrumfenster) return;                // not open
  523.  
  524.     SetPort ( spectrumfenster );
  525.  
  526.     if (spectrumbitmap)                        // there's a picture to show
  527.     {    attr = spectrumbitmap+6144;
  528.         SetRect ( &r, hborder,vborder, hborder+8,vborder+8 );
  529.         for ( y=0; y<24; y++ )
  530.         {    for ( x=0; x<32; x++ )
  531.             {    RGBForeColor ( rgb + idxi[flashphase+*attr] );        
  532.                 RGBBackColor ( rgb + idxp[flashphase+*attr] );    
  533.                 j = ((y&0x0007)<<5) + ((y&0x0018)<<8) + x;
  534.                 for ( i=0; i<8; i++ )
  535.                 {    p.pat[i] = spectrumbitmap[j];         
  536.                     j += 256;
  537.                 };
  538.                 FillRect ( &r, &p );
  539.                 attr++; r.left+=8; r.right+=8;
  540.             };
  541.             r.left=hborder; r.right=hborder+8;
  542.             r.top+=8; r.bottom+=8;  
  543.         };
  544.     }
  545.     else
  546.     {
  547.         ForeColor ( whiteColor ); 
  548.         PaintRgn ( screenRgn );
  549.         ForeColor ( blackColor );  
  550.         BackColor ( whiteColor );
  551.         MoveTo ( hborder+1,vborder+190 );  
  552.         DrawString ( "\p(C) 1982 Sinclair Corporation");
  553.     };
  554. }
  555.  
  556.  
  557. // -----    Redraw Border of Spectrum window ----------------------------
  558. //        borderstate    0 = normal
  559. //                    1 = white        3 = red-white
  560. //                    2 = red            4 = blue-yellow
  561. RedrawSpectrumborder ()
  562. {    RgnHandle oldClipRgn;
  563.     Rect border, box;
  564.     short i;
  565.         
  566.     if (!spectrumfenster) return;        // window not open
  567.  
  568.     SetPort(spectrumfenster);
  569.     oldClipRgn=NewRgn();
  570.     GetClip(oldClipRgn);
  571.     SetClip(borderRgn);
  572.     border = spectrumfenster->portRect;
  573.         
  574.     switch (borderstate)
  575.     {    case 1:    // search header indicator
  576.             ForeColor ( whiteColor ); 
  577.             PaintRect ( &border ); 
  578.             break;
  579.         case 2:    // blips detect indicator
  580.             ForeColor ( redColor );
  581.             PaintRect ( &border ); 
  582.             break;
  583.         case 3:    // header indicator
  584.             ForeColor ( redColor );
  585.             PaintRect ( &border ); 
  586.             ForeColor ( whiteColor );
  587.             box=border;
  588.             for ( i=1; i<border.bottom; i+=9 )
  589.             {    box.top=i;box.bottom=i+4;
  590.                 PaintRect ( &box );
  591.             };
  592.             break;
  593.         case 4:    // data indicator
  594.             ForeColor (yellowColor );
  595.             PaintRect ( &border ); 
  596.             ForeColor ( blueColor );
  597.             box=border;
  598.             for ( i=1; i<border.bottom; i+=4 )
  599.             {    box.top=i;box.bottom=i+2;
  600.                 PaintRect ( &box );
  601.             };
  602.             break;
  603.         default:    // normal border
  604.             if (spectrumbitmap)        // fit border to picture
  605.             {    Char* attr, a,b,c,d; 
  606.                 attr = spectrumbitmap+6144;
  607.                 a = attr[0]&0x78;
  608.                 b = attr[31]&0x78;
  609.                 c = attr[672]&0x78;
  610.                 d = attr[703]&0x78;
  611.                 if ( a==b || a==c || a==d ) {}
  612.                 else    if ( b==c || b==d ) a=b;
  613.                 else    if ( c==d ) a=c;
  614.                 RGBForeColor ( rgb + (a>>3) );
  615.                 PaintRect ( &border ); 
  616.             }
  617.             else                    // default border = default paper = white
  618.             {    ForeColor ( whiteColor ); 
  619.                 PaintRect ( &border ); 
  620.             };
  621.             break;
  622.     };
  623.  
  624.     SetClip ( oldClipRgn );
  625.     DisposeRgn(oldClipRgn);
  626. }
  627.  
  628.  
  629. // -----    Update ZX Spectrum window ------------------------------------
  630. RefreshSpectrumfenster()
  631. {    
  632.     RedrawSpectrumscreen();
  633.     RedrawSpectrumborder();
  634. }
  635.  
  636.  
  637. // -----    Close window -------------------------------------------------
  638. //            if a picture is valid, it's not discarded, only the window is closed
  639. CloseSpectrumWindow ()
  640. {    
  641.     if (spectrumfenster)
  642.     {    DisposeWindow ( spectrumfenster ); 
  643.         spectrumfenster=nil;
  644.     };
  645. }
  646.  
  647.  
  648. // -----    Open ZX Spectrum window ----------------------------------------
  649. //        Opens the window only, if checked in menu (default)
  650. //        If a valid picture is present, it will be displayed
  651. //        If already open, it stays open in any case
  652. OpenSpectrumWindow()
  653. {    Rect mybox,border,screen; short i;
  654.     
  655.     borderstate=0; 
  656.  
  657.     if (!show_spectrum) return;                    // option not checked in menu
  658.  
  659.     if (!borderRgn) borderRgn = NewRgn();
  660.     if (!screenRgn) screenRgn = NewRgn();
  661.     if ( ! (screenRgn && borderRgn) ) return;    // memory low
  662.  
  663.     if (!spectrumfenster)
  664.     {    spectrumfenster = GetNewCWindow ( spectrumID, nil, (WindowPtr)-1 );
  665.         if (!spectrumfenster) return ConMsg("\pCannot open the ZX Spectrum window","\pFehler beim Öffnen des ZX Spectrumfensters");
  666.         hborder = ((spectrumfenster->portRect.right-256)/16)*8; if (hborder<=0) hborder=8;
  667.         vborder = ((spectrumfenster->portRect.bottom-192)/16)*8; if (vborder<=0) vborder=8;
  668.         SizeWindow ( spectrumfenster, 256+2*hborder, 192+2*vborder, true );
  669.         ShowWindow ( spectrumfenster );
  670.     };
  671.  
  672.     SetPort ( spectrumfenster );
  673.     GetFNum("\pMonaco",&i);
  674.     TextFont(i);
  675.     TextMode(srcCopy);
  676.     TextSize(9);
  677.     
  678.     screen=border=spectrumfenster->portRect;
  679.     InsetRect ( &screen, hborder, vborder );
  680.     RectRgn ( screenRgn, &screen );
  681.     RectRgn ( borderRgn, &border );
  682.     XorRgn ( borderRgn, screenRgn, borderRgn );
  683.  
  684.     RedrawSpectrumborder();
  685.     RedrawSpectrumscreen();
  686. }
  687.  
  688.  
  689. // =====    Handling the byte distribution window ======================================
  690.  
  691.  
  692. // -----    Refresh byte distribution window -----------------------------------------
  693. RefreshByteDistributionWindow()
  694. {    short x,y; long mxm;
  695.     
  696.     if (!bytedistributionwindow) return;
  697.     
  698.     SetPort(bytedistributionwindow);
  699.     EraseRect ( &bytedistributionwindow->portRect );
  700.  
  701.     mxm=0;
  702.     for (x=0; x<256; x++)  if (cnt[x]>mxm) mxm = cnt[x];
  703.     mxm/=100; if (mxm<=0) return;
  704.     
  705.     for (x=0; x<256; x++)
  706.     {    MoveTo(x+3,118);
  707.         LineTo(x+3,118-cnt[x]/mxm);
  708.     };
  709. }
  710.  
  711.  
  712. // -----    Close byte distribution window ---------------------------------------------
  713. //            statistical data is not discarded, only the window is closed
  714. CloseStatistikfenster ()
  715. {
  716.     if (bytedistributionwindow) 
  717.     {     DisposeWindow ( bytedistributionwindow ); 
  718.         bytedistributionwindow=nil; 
  719.     };
  720. }
  721.  
  722.  
  723. // -----    Calculate and display new byte distribution statistic ------------------------
  724. //            source is soundfile
  725. OpenByteDistributionWindow ( )
  726. {    OSErr err; long i,j, len, n;
  727.     char buffer[10000];
  728.     Rect mybox;
  729.     
  730.     if (!show_distribution) return;                // not selected
  731.     
  732.     if (!bytedistributionwindow)
  733.     {    bytedistributionwindow = GetNewWindow ( statistikID, nil, (WindowPtr)-1 );
  734.         if (!bytedistributionwindow) return ConMsg("\pCannot open the byte distribution window","\pFehler beim Öffnen des Byteverteilungsfensters");
  735.         SizeWindow(bytedistributionwindow, 262,120,true);
  736.         ShowWindow(bytedistributionwindow);
  737.     };
  738.  
  739.     ConMsg ("\pByte distribution is calculated...","\pByteverteilungsstatistik wird erzeugt.");
  740.     
  741.     for ( i=0; i<256; i++ ) cnt[i]=0;
  742.     RefreshByteDistributionWindow();                // Erase the window
  743.     
  744.     err = FSpOpenDF ( &soundfile, fsCurPerm, &soundID );
  745.     if (err) soundID=0;
  746.     if (err) return ErrMsg("\pFSpOpenDF()");
  747.  
  748.     err = GetEOF ( soundID, &len );
  749.     if (err) return ErrMsg("\pGetEOF()");
  750.  
  751.     for ( i=0; i<=len; i+=10000 )
  752.     {    n=10000;
  753.         err = FSRead ( soundID, &n, buffer );
  754.         if ( (err==eofErr) & (i+n==len) ) err=noErr;
  755.         if (err) return ErrMsg("\pFSRead()");
  756.         for ( j=0; j<n; j++ ) cnt[buffer[j]+128]++;
  757.         RefreshWindows();
  758.     }
  759.     
  760.     RefreshByteDistributionWindow();
  761. }
  762.  
  763.  
  764. // =====    Handling the run length distribution window ==============================
  765.  
  766.  
  767. // -----    Close run length distribution window -------------------------------------------------
  768. //            actual data is not discarded, only the window is closed!
  769. CloseRunLengthWindow ()
  770. {
  771.     if (runlengthwindow) 
  772.     {     DisposeWindow ( runlengthwindow ); 
  773.         runlengthwindow=nil; 
  774.     };
  775. }
  776.  
  777.  
  778. // -----    Refresh run length distribution window -----------------------------------------
  779. RefreshRunLengthWindow()
  780. {    short x,y; long mxm; Rect mybox; short hscale, vscale;
  781.     
  782.     if (!runlengthwindow) return;            // window not open
  783.     
  784.     SetPort ( runlengthwindow );
  785.     EraseRect ( &runlengthwindow->portRect );
  786.  
  787.     hscale=256/llsize; vscale=55;
  788.     
  789.     mxm=0;
  790.     for (x=0; x<llsize; x++)  { if (hi[x]>mxm) mxm = hi[x];  if (lo[x]>mxm) mxm = lo[x]; };
  791.     mxm/=vscale;  if (mxm<=0) return;
  792.         
  793.     for (x=0; x<llsize; x++)
  794.     {    SetRect ( &mybox, 3+x*hscale, 60-1-((hi[x]+mxm/2)/mxm), 2+(x+1)*hscale, 60+2+((lo[x]+mxm/2)/mxm) );
  795.         InvertRect ( &mybox );
  796.     };
  797.     SetRect ( &mybox,  3, 60-1, 2+llsize*hscale, 60 );
  798.     EraseRect ( &mybox );
  799.     SetRect ( &mybox,  3, 60+1, 2+llsize*hscale, 60+2 );
  800.     EraseRect ( &mybox );
  801. }
  802.  
  803.  
  804. // -----    Calculate and display new run length distribution statistic ------------------------
  805. //            source is rlesfile
  806. OpenRunLengthWindow ( )
  807. {    OSErr err; long i,j,k, len, n;
  808.     Char buffer[10000];
  809.     Rect mybox;
  810.     
  811.     if (!show_run_length) return;                        // not selected in menu
  812.     
  813.     if (!runlengthwindow)
  814.     {    runlengthwindow = GetNewWindow ( statistik2ID, nil, (WindowPtr)-1 );
  815.         if (!runlengthwindow) return ConMsg("\pCannot open run length distribution window","\pFehler beim Öffnen des Lauflängenfensters");
  816.         SizeWindow(runlengthwindow, 262,120,true);
  817.         ShowWindow(runlengthwindow);
  818.     };
  819.  
  820.     ConMsg ("\pRun length distribution is calculated...","\pLauflängenstatistik wird erzeugt.");
  821.     
  822.     for ( i=0; i<llsize; i++ ) hi[i]=lo[i]=0;
  823.     RefreshRunLengthWindow();                // clear window
  824.     
  825.     err = FSpOpenDF ( &rlesfile, fsCurPerm, &tapeID );
  826.     if (err) tapeID=0;
  827.     if (err) return ErrMsg("\pFSpOpenDF()");
  828.  
  829.     err = GetEOF ( tapeID, &len );
  830.     if (err) return ErrMsg("\pGetEOF()");
  831.  
  832.     for ( i=0; i<len; i+=10000 )
  833.     {    n=10000;
  834.         err = FSRead ( tapeID, &n, buffer );
  835.         if ( (err==eofErr) & (i+n==len) ) err=noErr;
  836.         if (err) return ErrMsg("\pFSRead()");
  837.         
  838.         for ( j=0; j<n; )
  839.         {    k = buffer[j++];
  840.             hi[(k>>4)+mn] += 1;
  841.             lo[(k&0x0f)+mn] +=1;
  842.         };
  843.         
  844.         RefreshWindows();
  845.     }
  846.         
  847.     RefreshRunLengthWindow();
  848. }
  849.  
  850.  
  851. // ==========================================================================================
  852.  
  853.  
  854. // -----    Handle some mouse down events --------------------------------
  855. HandleSomeMouseDown()
  856. {    WindowPtr    window;
  857.     long            where;
  858.     where = FindWindow ( event.where, &window );
  859.     switch (where)
  860.     {
  861. //    case inMenuBar:        HandleMenuSelection ( MenuSelect( event.where ) );        return;
  862.     case inMenuBar:        SysBeep(4711);                                            return;
  863.     case inDrag:        DragWindow ( window, event.where, &qd.screenBits.bounds );    return;
  864.     case inSysWindow:    SystemClick ( &event, window );                            return;
  865.     case inGoAway:        HandleCLOSE();                                            return;
  866.     case inGrow:        HandleGROW();                                            return;
  867.     case inZoomIn:        HandleZOOMIN();                                            return;
  868.     case inZoomOut:        HandleZOOMOUT();                                        return;
  869.     case inContent:        SelectWindow (window); HandleINCONTENT();                return;
  870.     case inDesk:        HandleINDESK();                                            return;
  871.     };
  872. }
  873.  
  874.  
  875. // -----    Refresh windows if neccessary ----------------------------------
  876. RefreshWindows()
  877. {    while ( WaitNextEvent(mDownMask|mUpMask|updateMask|activMask|osMask,&event,0,eventRegion) )
  878.     {    switch (event.what)
  879.         {
  880. //        case kHighLevelEvent:    AEProcessAppleEvent(&event);        break;    
  881.         case mouseDown:            HandleSomeMouseDown();                 break;
  882. //        case autoKey:            HandleAUTOKEY(event.message&0x0ff); break;
  883. /*        case keyDown:
  884.             if (event.modifiers&cmdKey)
  885.                 {    selection = MenuKey(event.message&0x0ff);
  886.                     if (HiWord(selection)) 
  887.                     {     HandleMenuSelection( selection );
  888.                         break;
  889.                     }
  890.                 }
  891.             HandleKEYDOWN (event.message&0x0ff); 
  892.             break;
  893. */
  894.         case activateEvt:    
  895.             SetPort ( (WindowPtr)event.message );
  896.             if (event.modifiers&activeFlag)    
  897.                 HandleACTIVATE   ();         // window activated  
  898.             else    HandleDEACTIVATE ();    // window deactivated
  899.             break; 
  900.  
  901.         case updateEvt:    
  902.             BeginUpdate ( (WindowPtr)event.message );
  903.             HandleUPDATE((WindowPtr)event.message);
  904.             EndUpdate   ( (WindowPtr)event.message ); 
  905.             break;
  906.             
  907.         case osEvt:                
  908.             if (event.message & 0x01000000 ) 
  909.                 if ( event.message & 0x00000001 ) 
  910.                     SetCursor(&qd.arrow),HandleRESUME();     // application re-activated 
  911.                 else    HandleSUSPEND();                     // application de-activated
  912.             if ((event.message & 0xFF000000)==0xFA000000)
  913.                 HandleREGION();                                // mouse leaves eventRegion
  914.             break;
  915.  
  916.         };
  917.     };
  918. }
  919.  
  920.  
  921. // ------ Check new data in buffer for screen$ and if so, display it
  922. DisplayScreenshot()
  923. {    Char *p,*e, *ph, *pd;
  924.     Short slen;
  925.     long len;
  926.     Char *cp,*ep; long blklen,blkadr;
  927.     
  928.     if ( !dh ) return;                // no data buffer
  929.     if ( !spectrumfenster ) return;    // window not open
  930.     
  931.     p = dh+4;                        // scan start
  932.     e = *(Char**)dh;                // scan end
  933.     ph = nil;                        // ptr -> header block
  934.     pd = nil;                        // ptr -> data block
  935.     
  936.     while (p<e)
  937.     {    ph=pd;
  938.         pd=p;
  939.         slen = *p + *(p+1)*256 -2; 
  940.         p += slen ? slen+4 : 0x10004;
  941.     };
  942.     
  943.     if (ph==nil) return;                // no header block found
  944.     
  945.     slen = *ph + *(ph+1)*256 -2;
  946.     if ( slen < 17 ) return;            // min. length for header: type+17byte+crc = 19 byte
  947.     cp = ph+2;                            // cp -> type
  948.     if ( *cp!=0 || *(cp+1)!=3 ) return;    // not a code header
  949.     
  950.     blklen = *(cp+12) + (*(cp+13)<<8);    // length of data block
  951.     if (!blklen) blklen = 0x10000;
  952.     blkadr = *(cp+14) + (*(cp+15)<<8);    // original address of data block
  953.     
  954.     if (  (blklen!=6912)  &&  ( (blkadr+blklen<0x4000) || (blkadr>=23296) ) )  return;
  955.     if (blklen==6912) blkadr=0x4000;    // sometimes they've saved it from anywhere in memory
  956.     
  957. // This is a header for data, that might contain a screen$
  958.  
  959.     slen = *pd + *(pd+1)*256 -2;    
  960.     if ( slen < blklen ) return;    // mismatch between data and header block
  961.     cp = pd+2;                        // cp -> type
  962.     if ( *cp!=255) return;            // not a data block!
  963.  
  964. // Anything seems to be all right ...
  965.  
  966.     cp = cp+1;                        // cp points to effective data
  967.     if (blkadr<0x4000)  {  blklen-=(0x4000-blkadr); cp += (0x4000-blkadr); blkadr=0x4000;  };
  968.     if (blkadr+blklen > 23296) blklen = 23296-blkadr;
  969.     if (blklen<=0) return;            // this cannot happen
  970.  
  971.     Malloc ( &spectrumbitmap, 6912 );
  972.     if (!spectrumbitmap) return;                    // out of memory
  973.     memset ( spectrumbitmap, 0, 6144 );                // default pixel
  974.     memset ( spectrumbitmap+6144, 0x38, 768);        // default attribute
  975.  
  976.     memcpy ( spectrumbitmap + blkadr - 0x4000, cp, blklen );    // and overwrite
  977.  
  978.     cp=spectrumbitmap+6144;                            // flashing required ?
  979.     ep=spectrumbitmap+6912;
  980.     flashing=false;
  981.     eventDelay=120;
  982.     while ( cp<ep )
  983.     {    if ( *cp++&0x80 ) 
  984.         {     if ( (*(cp-1)&0x07)==((*(cp-1))>>3)&0x07 )
  985.                 *(cp-1) &=0x7f;                        // don't flash same colors
  986.             else
  987.             {    flashing=true; eventDelay=60; break;     };
  988.         };
  989.     }
  990.     
  991.     RedrawSpectrumscreen();
  992. }
  993.  
  994.  
  995. Notify ( Str255 eng, Str255 ger )
  996. {    
  997.     ConNL();
  998.     PrintNum(p-mh);
  999.     ConTab(); 
  1000.     ConPrint(eng,ger);
  1001. }
  1002.  
  1003.  
  1004. Short NextByte (Boolean v)
  1005. {    register Char b1,b2; short i,c;
  1006.     c=0;
  1007.     for (i=0;i<8;i++)
  1008.     {    b1=*p++;        // get 1/2 nibble
  1009.         b2=*p++;        // get 2/2 nibble
  1010.         if (b1>max1 || b2>max1) { if (v) Notify("\p••••• Phase too long!","\p••••• Flanke zu lang!"); return(-1); };
  1011.         if (b1<min0 || b2<min0)  {  if (v) Notify("\p••••• Phase too short!","\p••••• Flanke zu kurz!"); return(-1);  };
  1012.         if (b1<=max0 && b2<=max0)  c = c+c;
  1013.         else if (b1>=min1 && b2>=min1)  c = c+c+1;
  1014.         else 
  1015.         {    if (v) Notify("\p••••• Long and short phase for same bit!","\p••••• Kurze und lange Flanke für gleiches Bit!");
  1016.             return(-1);
  1017.         };
  1018.     };
  1019.     crc ^= c;            // update checksum
  1020.     *d++=c;                // and store byte in buffer
  1021.     return(c);
  1022. }
  1023.  
  1024.  
  1025. ChangeBorder ( long n )
  1026. {
  1027.     borderstate=n;
  1028.     RedrawSpectrumborder();
  1029.     Delay ( 15, &n );            // for your eyes only ...
  1030.     RefreshWindows();            // don't get nervous !
  1031. }
  1032.  
  1033.  
  1034. // -----    Convert RLES -> Tape ----------------------------------------------
  1035. LoadTape()
  1036. {    OSErr err; long i,n,len; Char *q, c; short b, typ; Short slen;
  1037.     Short expect=0;
  1038.     Char    hdr[17];
  1039.     Char * dsp;
  1040.     
  1041.     ConMsg ("\pPrograms are loaded.","\pProgramme werden geladen.");
  1042.     MFree ( &spectrumbitmap ); 
  1043.     OpenSpectrumWindow();
  1044.  
  1045.     err = FSpOpenDF ( &rlesfile, fsCurPerm, &tapeID );
  1046.     if (err) tapeID=0;
  1047.     if (err) return ErrMsg("\pFSpOpenDF()");
  1048.  
  1049.     err = GetEOF ( tapeID, &len );
  1050.     if (err) return ErrMsg("\pGetEOF()");
  1051.  
  1052.     Malloc ( &mh, len*2 );
  1053.     if ( !mh )  return ConMsg("\pNo memory available!","\pKein Speicher mehr frei!");
  1054.     
  1055.     Malloc ( &dh, 200 + len/7 );   // benötigt werden: len/8 + gesamtgröße aller segmentköpfe
  1056.     if ( !dh ) MFree (&mh);
  1057.     if ( !dh ) return ConMsg("\pNo memory available!","\pKein Speicher mehr frei!");
  1058.     
  1059. // ----- Read source file and prepare    
  1060.     n=len;  err = FSRead ( tapeID, &n, mh+len );
  1061.     if ( (err==eofErr) & (n==len) ) err=noErr;
  1062.     if (err) 
  1063.     {     ErrMsg("\pFSRead()"); 
  1064.         MFree ( &mh ); MFree ( &dh );
  1065.         return; 
  1066.     };
  1067.  
  1068.     p = mh;
  1069.     q = p + len;
  1070.     
  1071.     for ( i=0; i<len; i++ )
  1072.     {    *p++ = ( *q >> 4 ) + 2;
  1073.         *p++ = ( *q++ & 0x0f ) + 2;
  1074.     };
  1075.  
  1076. // ----- prepare destination buffer
  1077.     dsp = dh+4;                    // data segment pointer
  1078.     *(Char**)dh=dsp;            // end of data pointer
  1079.  
  1080. // ----- let's do it! ---------------------------------------------------
  1081.     p = mh;             // read pointer
  1082.     q = p+len+len;        // end pointer
  1083.  
  1084.     while (p<q)
  1085.     {    
  1086. nh:        ChangeBorder(1);            // we are looking for a header !
  1087.         if (verbose>=2) { ConNL(); Notify("\p      Looking for header","\p      Suche Vorspann"); };
  1088. nhx:    p++;
  1089.         if (p>=q) break;
  1090.         for (i=0; i<88; i++,p++ ) if (*p>maxh || *p<minh) break; 
  1091.         if (i<88) goto nhx;
  1092.         
  1093.         ChangeBorder(2);            // blips ... check it !
  1094.         for (i=0; i<444; i++,p++ ) if (*p>maxh || *p<minh) break;
  1095.         if (i<444) goto nh;
  1096.         
  1097.         ChangeBorder(3);            // we've got it !
  1098.         if (verbose>=2) { Notify("\p      Header ... ","\p      Vorspann ... "); };
  1099.         while ( *p<=maxh && *p>=minh && p<q ) p++;  
  1100.         
  1101.         if (*p>maxh) { if (verbose) { Notify("\p      Header: Period too long",    "\p      Fangsequenz: Periode zu lang"); }; goto nh; };
  1102.         if (*p>maxs) { if (verbose) { Notify("\p      Sync 1/2 too long",        "\p      Sync 1/2 zu lang"); goto nh; };};
  1103.         if (*p<mins) { if (verbose) { Notify("\p      Sync 1/2 too short",        "\p      Sync 1/2 zu kurz"); goto nh; };};    p++;
  1104.         if (*p>maxs) { if (verbose) { Notify("\p      Sync 2/2 too long",        "\p      Sync 2/2 zu lang"); goto nh; };};
  1105.         if (*p<mins) { if (verbose) { Notify("\p      Sync 2/2 too short",        "\p      Sync 2/2 zu kurz"); goto nh; };};    p++;
  1106.         if (verbose>=2) { Notify("\p      Sync pulse ... ","\p      Syncimpuls ... "); };
  1107.         
  1108.         crc = 0;  d = dsp+2;
  1109.         typ=NextByte(true);          // read Block Type
  1110.         if (typ<0) goto nh; 
  1111.         ChangeBorder(4);        
  1112.  
  1113.         if (typ!=0) goto ld;            // load data
  1114.  
  1115. // Header
  1116.         if (expect)  { Notify("\p••••• The data block wasn't found!","\p••••• Der Datenblock wurde nicht gefunden!");};
  1117.         expect=0;
  1118.         ConNL();Notify("\p***** HEADER",0); 
  1119.         for (i=0; i<=16; i++ )  {  hdr[i]=b=NextByte(true); if (b<0) goto nl;  };
  1120.         
  1121.         typ = *hdr; *hdr=10; for (i=1;i<=10;i++) if (hdr[i]<32) hdr[i]='?';
  1122.         
  1123.         switch (typ)
  1124.         {    case 0:        Notify("\p      Basic program",            "\p      Programmdatei"); break;
  1125.             case 1:        Notify("\p      Number array",            "\p      Zahlenarray"); break;
  1126.             case 2:        Notify("\p      Text array",            "\p      Textarray"); break;
  1127.             case 3:        Notify("\p      Code or Screen$",        "\p      Code oder Screen$"); break;
  1128.             default:    Notify("\p      Illegal header ID",        "\p      Ungültiger Headertyp");
  1129.                         Notify("\p>>>>> Loading data as Long Header", "\p>>>>> Lade Daten als Long Header");
  1130.                     i=1;expect=0;goto ldx;
  1131.         };
  1132.         
  1133.         if (hdr[1]==255)    Notify("\p      unnamed", "\p      ohne Namen");
  1134.         else    {              Notify("\p      Name: ",0); ConPrint(hdr,0);  };
  1135.         
  1136.         Notify ("\p      File length: ","\p      Dateilänge: "); PrintNum(hdr[11]+((Short)hdr[12]<<8)); 
  1137.         
  1138.         if ( typ==0 && verbose )
  1139.         {    if ( hdr[14]<0x0080 )  
  1140.             {    Notify ("\p      Autostart at line: ","\p      Autostart ab Zeile: "); PrintNum(hdr[13]+((Short)hdr[14]<<8)); 
  1141.             };
  1142.             Notify ("\p      Program length without data: ","\p      Programmlänge ohne Daten: ");  
  1143.             PrintNum(hdr[15]+((Short)hdr[16]<<8)); 
  1144.         };
  1145.         
  1146.         if ( (typ==1 || typ==2) && verbose )
  1147.         {    
  1148.         };
  1149.         
  1150.         if ( typ==3 && verbose )
  1151.         {    Notify ("\p      Original address of data: ", "\p      Originallage der Daten: "); 
  1152.             PrintNum(hdr[13]+((Short)hdr[14]<<8)); 
  1153.         };
  1154.         
  1155.         b=NextByte(true); if(b<0) goto nl;
  1156.         if (crc) 
  1157.         {     Notify("\p••••• Checksum wrong","\p••••• Prüfsumme stimmt nicht"); 
  1158.             b=NextByte(false); if (b<0) goto nl;
  1159.             b=NextByte(false); if (b<0) goto nl;
  1160.             Notify("\p>>>>> Long Header ... loading proceeds !","\p>>>>> Long Header ... lade weiter !");
  1161.             i=20;        // number of bytes already read
  1162.             expect=0;    // no read end marker
  1163.             goto ldx;     // read "headerless"
  1164.         };
  1165.         Notify("\p+++++ Checksum ok","\p+++++ Prüfsumme ok");
  1166.         expect=hdr[11]+((Short)hdr[12]<<8);
  1167.         b=NextByte(false); if (b<0) goto nl;
  1168.         b=NextByte(false); if (b<0) goto nl;
  1169.         Notify("\p>>>>> Long Header ... loading proceeds !","\p>>>>> Long Header ... lade weiter !");
  1170.         i=20;            // number of bytes already read
  1171.         expect=18;        // last read end marker
  1172.         goto ldx;        // read "headerless"
  1173.         
  1174. // Data
  1175. ld:        
  1176.         if (typ!=255) { ConNL();Notify("\p$$$$$ UNDEFINED TYPE: ","\p$$$$$ UNDEFINIERTER TYP: "); PrintNum(typ); ConNL(); };
  1177.         if (expect)
  1178.         {    ConNL(); Notify("\p***** DATA","\p***** DATEN");
  1179.             for ( i=0; i<expect && p<q; i++ )
  1180.             {    b=NextByte(true); if (b<0) { expect=0; goto nl; };
  1181.             };
  1182.             b=NextByte(true); if (b<0) { expect=0; goto nl; };
  1183.             if (crc) {     Notify("\p••••• Checksum wrong","\p••••• Prüfsumme stimmt nicht"); expect=0; }
  1184.             else         Notify("\p+++++ Checksum ok","\p+++++ Prüfsumme ok");
  1185.             b=NextByte(false); if (b<0) { expect=0;goto nl;};
  1186.             b=NextByte(false); if (b<0) { expect=0;goto nl;};
  1187.             Notify("\p>>>>> Long Data ... loading proceeds !","\p>>>>> Long Data ... lade weiter !");
  1188.             i=i+2;
  1189.             goto ldx;
  1190.         }
  1191.         else
  1192.         {    ConNL(); Notify("\p***** HEADERLESS DATA","\p***** HEADERLOSE DATEN");
  1193.             for ( i=0; p<q; i++ )
  1194.             {    
  1195. ldx:                b=NextByte(!expect); if (b<0) break;
  1196.                 if (crc==0) 
  1197.                 {    if (verbose>=2) ConPrint("\p ((crc==0)) ",0);
  1198.                     expect=i;
  1199.                 };
  1200.             };
  1201.             Notify ("\p      File length: ","\p      Dateilänge: "); PrintNum(i-1);
  1202.             if (expect==0)  {  Notify("\p••••• Checksum wrong!","\p••••• Prüfsumme stimmt nicht!"); goto nl;  };
  1203.             if (i-1==expect) 
  1204.             {    Notify ("\p+++++ Checksum ok","\p+++++ Prüfsumme ok");
  1205.             }
  1206.             else
  1207.             {    Notify ("\p      Last OK for checksum at: ","\p      Letztes ok für die Prüfsumme: "); PrintNum(expect); 
  1208.                 Notify ("\p••••• Probably unusable!","\p••••• Vermutlich fehlerhaft!");
  1209.             };
  1210.             expect=0;
  1211.             goto nl;
  1212.         };
  1213.         
  1214. // ----- update segment header
  1215. nl:        len = d-(dsp+2);
  1216.         if (len<3)                        // all blocks contain at least 3 bytes: 1*type+1*data+1*crc !!!
  1217.         {    Notify ("\p••••• Block contains no data (skipped)","\p••••• Block enthält keine Daten (übersprungen)");
  1218.             d = dsp+2;
  1219.             continue;
  1220.         };
  1221.         if (len>=0x10002) 
  1222.         {    Notify ("\p••••• Block longer than 0x10000 bytes","\p••••• Block länger als 0x10000 Bytes");
  1223.             len=0x10002;
  1224.             d=dsp+0x10004;
  1225.         };
  1226.         *dsp = len;                        // big endian byte order !!!
  1227.         *(dsp+1) = len>>8;                // .tap format was invented on PC clowns !
  1228.         *(Char**)dh = dsp = d;
  1229.         d += 2;
  1230.         DisplayScreenshot();
  1231.     };
  1232.  
  1233.     Notify("\p----- End of tape -----","\p----- Ende des Bandes -----"); ConNL();
  1234.  
  1235.     MFree ( &mh ); 
  1236.     ChangeBorder(0);    
  1237.     
  1238. // ----- write to destination file (type: 'Tape')
  1239.  
  1240.     ConTab();ConMsg("\p      Writing tape file","\p      Schreibe Banddatei");
  1241.     tapefile=rlesfile;
  1242.     if ( *tapefile.name>=5 )
  1243.     {    if ( memcmp ( tapefile.name+*tapefile.name-4, ".rles", 5 ) == 0 ) *tapefile.name -=5;
  1244.         else if ( memcmp ( tapefile.name+*tapefile.name-4, ".RLES", 5 )  == 0 ) *tapefile.name -=5;
  1245.     }
  1246.     if (*tapefile.name>25) *tapefile.name=25;
  1247.     while (tapefile.name[*tapefile.name]==' ') *tapefile.name -= 1;
  1248.     memcpy ( tapefile.name+*tapefile.name+1, " .tape", 6 );
  1249.     *tapefile.name += 6;
  1250.  
  1251.     err = FSpCreate ( &tapefile, 'ZXSP', 'Tape', 0 );    // 'ZXSP' == ID of ZX Spectrum Emulator
  1252.     if (err=dupFNErr) err=noErr;    // ignore file already exists
  1253.     if (err) return ErrMsg("\pFSpCreate()");
  1254.  
  1255.     err = FSpOpenDF ( &tapefile, fsCurPerm, &dataID );
  1256.     if (err=eofErr) err=noErr;
  1257.     if (err) return ErrMsg("\pFSpOpenDF()");
  1258.  
  1259.     err = SetEOF ( dataID, 0 );        // clear tape
  1260.     if (err) return ErrMsg("\pSetEOF()");
  1261.     
  1262.     len = *(Char**)dh-(dh+4);
  1263.     err = FSWrite ( dataID, &len, dh+4 );
  1264.     if (err) return ErrMsg("\pFSWrite()");
  1265. }
  1266.  
  1267.  
  1268. // -----    Convert Sound -> RLES ---------------------------------------------
  1269. //
  1270. RunLengthCompression()
  1271. {    OSErr err; long i,j,k, len, n,m; short whatsup; Char *z;
  1272.     char buffer[10000]; Char rll[10000];
  1273.     short hysterese = 2;
  1274.     Rect mybox;
  1275.     
  1276.     ConMsg ("\pRun length compression in progress...","\pLauflängenkompression wird durchgeführt.");
  1277.     
  1278.     err = FSpOpenDF ( &soundfile, fsCurPerm, &soundID );
  1279.     if (err) return ErrMsg("\pFSpOpenDF()");
  1280.  
  1281.     err = GetEOF ( soundID, &len );
  1282.     if (err) return ErrMsg("\pGetEOF()");
  1283.  
  1284.     rlesfile=soundfile;
  1285.     if ( *rlesfile.name>=5 )
  1286.     {    if ( memcmp ( rlesfile.name+*rlesfile.name-4, ".aiff", 5 ) == 0 ) *rlesfile.name -=5;
  1287.         else if ( memcmp ( rlesfile.name+*rlesfile.name-4, ".AIFF", 5 )  == 0 ) *rlesfile.name -=5;
  1288.     }
  1289.     if (*rlesfile.name>25) *rlesfile.name=25;
  1290.     while (rlesfile.name[*rlesfile.name]==' ') *rlesfile.name -= 1;
  1291.     memcpy ( rlesfile.name+*rlesfile.name+1, " .rles", 6 );
  1292.     *rlesfile.name += 6;
  1293.  
  1294.     err = FSpCreate ( &rlesfile, 'ZXSp', 'RLES', 0 );
  1295.     if (err=dupFNErr) err=noErr;    // ignore file already exists
  1296.     if (err) return ErrMsg("\pFSpCreate()");
  1297.  
  1298.     err = FSpOpenDF ( &rlesfile, fsCurPerm, &tapeID );
  1299.     if (err=eofErr) err=noErr;
  1300.     if (err) return ErrMsg("\p(FSpOpenDF)");
  1301.  
  1302.     err = SetEOF ( tapeID, 0 );        // clear tape
  1303.     if (err) return ErrMsg("\p(SetEOF)");
  1304.     
  1305.  
  1306.     k=1;whatsup='hi';z=rll;
  1307.     for ( i=0; i<len; i+=10000 )
  1308.     {    n=10000;
  1309.         err = FSRead ( soundID, &n, buffer );
  1310.         if ( (err==eofErr) & (i+n==len) ) err=noErr;
  1311.         if (err) return ErrMsg("\p(FSRead)");
  1312.         
  1313.         for ( j=0; j<n; )
  1314.         {    if (whatsup=='hi')
  1315.             {    while ( j<n && buffer[j]>=-hysterese ) { j++; k++; };
  1316.                 if ( j<n )  {  if ( k>mx) k=mx; if (k<mn) k=mn; *z=(k-mn)<<4 ; whatsup='lo'; k=0; };
  1317.             };
  1318.             if (whatsup=='lo')
  1319.             {    while ( j<n && buffer[j]<=hysterese ) { j++; k++; };
  1320.                 if ( j<n )  {  if ( k>mx) k=mx; if (k<mn) k=mn; *z+=k-mn; if(*z) z++; whatsup='hi'; k=0; };
  1321.             };
  1322.             if ( (z==rll+10000) || (i+j==len) ) 
  1323.             {    m=z-rll;
  1324.                 err = FSWrite ( tapeID, &m, rll );
  1325.                 if (err) return ErrMsg("\p(FSWrite)");
  1326.                 z=rll;
  1327.             };
  1328.         };
  1329.         
  1330.         RefreshWindows();
  1331.     }
  1332.         
  1333. }
  1334.  
  1335.  
  1336. // -----    record new sound to file and then open & convert this file
  1337. //
  1338. HandleNEW()                    
  1339. {    OSErr err; Point corner;
  1340.     StandardFileReply reply;
  1341.     long    inRefNum;
  1342.     short    channels;
  1343.     short    sample_size;
  1344.     Fixed    sample_rate;    short i; Short n,m;
  1345.     
  1346.     ConMsg ( "\pRead data from tape","\pDaten von Kassette lesen" );
  1347.  
  1348.     if (german)    StandardPutFile ( "\pName für die Sound-Zieldatei", "\pZXSpectrum .aiff", &reply );
  1349.     else        StandardPutFile ( "\pName for the sound file", "\pZXSpectrum .aiff", &reply );
  1350.     if (!reply.sfGood) return ConMsg("\p*** Breaked ***","\p*** Abgebrochen ***");
  1351.     soundfile = reply.sfFile;
  1352.  
  1353.     ConPrint("\pDestination file: ","\pZieldatei: ");ConMsg(soundfile.name,0);
  1354.  
  1355.     if ( !reply.sfReplacing )
  1356.     {    err = FSpCreate ( &soundfile, 'ZXSp', 'AIFF', 0 );
  1357.         if (err) return ErrMsg("\p(FSpCreate)");
  1358.     };
  1359.     
  1360.     err = FSpOpenDF ( &soundfile, fsCurPerm, &soundID );
  1361.     if (err=eofErr) err=noErr;
  1362.     if (err) soundID=0;
  1363.     if (err) return ErrMsg("\p(FSpOpenDF)");
  1364.  
  1365.     err = SPBOpenDevice ( "\p", siWritePermission, &inRefNum );        
  1366.     if (!err)  
  1367.     {    err = SPBSetDeviceInfo ( inRefNum, 'plth', (Ptr)&plth_volume );    
  1368.         if (err) ConMsg("\pSet play through volume failed",0);
  1369.         
  1370.         err = SPBSetDeviceInfo ( inRefNum, 'agc ', (Ptr)&auto_gain_control );    
  1371.         if (err) ConMsg("\pSet automatic gain control failed",0);
  1372.         
  1373.         err = SPBGetDeviceInfo ( inRefNum, 'chan', (Ptr)&channels );            
  1374.         if (err) ConMsg("\pGet number of channels failed",0);
  1375.         else
  1376.         {    if (channels==1) 
  1377.             {    if (verbose==2) ConMsg("\pNumber of channels is mono",0);
  1378.             }
  1379.             else
  1380.             {    if (verbose) ConMsg("\pSetting device to mono",0);
  1381.                 channels=1;
  1382.                 err = SPBSetDeviceInfo ( inRefNum, 'chan', (Ptr)&channels );
  1383.                 if (err) ConMsg("\pSetting to mono failed",0);
  1384.             };
  1385.         };
  1386.         
  1387.         err = SPBGetDeviceInfo ( inRefNum, 'ssiz', (Ptr)&sample_size );            
  1388.         if (err) ConMsg("\pGet sample size failed",0);
  1389.         else
  1390.         {    if (sample_size==8)
  1391.             {    if (verbose==2) ConMsg("\pSample size is 8 bit",0);
  1392.             }
  1393.             else
  1394.             {    if (verbose) ConMsg("\pSetting sample size to 8 bit",0);
  1395.                 sample_size=8;
  1396.                 err = SPBSetDeviceInfo ( inRefNum, 'ssiz', (Ptr)&sample_size );    
  1397.                 if (err) ConMsg("\pFailed to set sample size",0);
  1398.             };
  1399.         };
  1400.         
  1401.         err = SPBGetDeviceInfo ( inRefNum, 'srat', (Ptr)&sample_rate );            
  1402.         if (err) ConMsg("\pGet sample rate failed",0);
  1403.         else
  1404.         {    if ((FixRound(sample_rate)>=21000) && (FixRound(sample_rate)<=23000))
  1405.             {    if (verbose==2) ConMsg("\pSample rate is 22 kHz",0);
  1406.             }
  1407.             else
  1408.             {    struct { short cnt; Fixed **table; } sample_rate_list;
  1409.                 
  1410.                 if (verbose) ConMsg("\pSetting sample rate to 22 kHz",0);
  1411.                 err = SPBGetDeviceInfo ( inRefNum, 'srav', (Ptr)&sample_rate_list );    
  1412.                 if (err) ConMsg("\pGetting sample rate list failed",0);
  1413.                 else
  1414.                 {    if (sample_rate_list.cnt)
  1415.                     {    if (verbose==2) ConMsg("\p[list of discrete sampling rates]",0);
  1416.                         for (i=n=0;i<sample_rate_list.cnt;i++)
  1417.                         {    m=FixRound((*sample_rate_list.table)[i]);
  1418.                             if (m<20050) n=m; else { if ((20050-n)<(m-20050)) m=n; break; };
  1419.                         };
  1420.                         sample_rate = Long2Fix(m);
  1421.                     }
  1422.                     else
  1423.                     {    if (verbose==2) ConMsg("\p[range of sampling rates]",0);
  1424.                         sample_rate = Long2Fix(22000);
  1425.                     };
  1426.                     DisposHandle((Handle)sample_rate_list.table);
  1427.                     err = SPBSetDeviceInfo ( inRefNum, 'srat', (Ptr)&sample_rate );    
  1428.                     if (err) ConMsg("\pFailed to set sample rate",0);
  1429.                 
  1430.                     err = SPBGetDeviceInfo ( inRefNum, 'srat', (Ptr)&sample_rate );            
  1431.                     if (err) ConMsg("\pGet sampling rate failed",0);
  1432.                     else
  1433.                     if ((FixRound(sample_rate)>=21000) && (FixRound(sample_rate)<=23000))
  1434.                     {    if (verbose==2) ConMsg("\pSample rate is 22 kHz",0);
  1435.                     }
  1436.                     else
  1437.                         ConMsg("\pSample rate is •not• 22 kHz",0);
  1438.                 };
  1439.             };
  1440.         };        
  1441.         
  1442.         SPBCloseDevice ( inRefNum );
  1443.     }
  1444.     else ConMsg("\pFailed to initialize the sound input device","\pDer Toneingang konnte nicht parametriert werden");
  1445.  
  1446.     RefreshWindows();
  1447.     corner.v=40;
  1448.     corner.h=40;
  1449.     err = SndRecordToFile ( nil, corner, 'best', soundID );
  1450.     if (err)
  1451.     {    CloseAll();
  1452.         if (err==userCanceledErr) return ConMsg("\p*** Breaked ***","\p*** Abgebrochen ***");
  1453.         else return ErrMsg("\p(SndRecordToFile)");
  1454.     };
  1455.     
  1456.     HandleOPEN ( soundfile );
  1457. }
  1458.  
  1459.  
  1460. // ============================================================================
  1461. // ============================================================================
  1462. // ============================================================================
  1463. // ============================================================================
  1464. // ============================================================================
  1465.  
  1466.  
  1467. HandleSAVE()                // save top window to disk
  1468. {    short n;
  1469.     n = Alert ( 128, nil );
  1470.     if (n==-1) ConMsg("\pResource 'ALRT' 128 is missing.","\pRessource 'ALRT' 128 fehlt.");
  1471. }
  1472.  
  1473. HandleKEYDOWN(Char key) 
  1474. {     
  1475. //     8 = Backspace        28 = Cursor left
  1476. //     9 = Tab            29 = Cursor right
  1477. //    13 = Return            30 = Cursor up
  1478. //                        31 = Cursor down
  1479. }
  1480.  
  1481. HandlePRINT()            { }    // print top window
  1482.  
  1483. HandleACTIVATE()        { }    // window 'thePort' activated
  1484. HandleDEACTIVATE()        { }    // window 'thePort' deactivated
  1485. HandleINCONTENT()        { }     // in FrontWindow()
  1486.  
  1487. HandleZOOMOUT()          { }    // in FrontWindow()
  1488. HandleZOOMIN()           { }    // in FrontWindow()
  1489. HandleGROW()             { }    // in FrontWindow()
  1490.  
  1491. HandleUNDO()            { }    // 'Edit' menu
  1492. HandleCUT()                { }    // 'Edit' menu
  1493. HandleCOPY()            { }    // 'Edit' menu
  1494. HandlePASTE()            { }    // 'Edit' menu
  1495.  
  1496. // ***** not so often needed ***************************************
  1497.  
  1498. HandleAUTOKEY(Char key)    { HandleKEYDOWN(key); }
  1499. HandleMOUSEUP()             { }
  1500. HandleKEYUP(Char key)    { } // global event enabling needed !!
  1501. HandleDISK()            { }    // Disk inserted (( ejected? ))
  1502. HandleINDESK()           { } // this should never happen
  1503. HandleDRIVER()             { }    // used by system tasks only
  1504. HandleAPP3()            { }    // application event
  1505.  
  1506. HandleREGION()            { }    // mouse leaves 'eventRegion'
  1507. HandleSUSPEND()            { }    // application is juggled out
  1508. HandleRESUME()            { }    // application is juggled in
  1509.  
  1510.