unit _Images;

interface

uses
  Windows, SysUtils, Classes, Graphics, QDArc,
  _PicTools, _FontPack, _CSkin;

type
  TNoteYBack=array[0..50] of byte;
  PNoteYBack=^TNoteYBack;

const GripSize=16;
const PanpotSize=16;
const BarHeight=16;

const FuncWidth=640;
const FuncHeight=40;

const SFX=5;
const SFY=8;

const MathDigX=13;
const MathDigY=17;

const TitleBarWidth=636;
const TitleBarHeight=16;

const CmpBtnCount=5;
const CmpBtnOneWidth=23;
const CmpBtnHeight=18;
const CmpBtnWidth=CmpBtnOneWidth*CmpBtnCount;

type
  TBMEx=record
    Width,Height:integer;
    NegaBM,PosiBM:TBitmap;
    NegaPtr,PosiPtr:array of PRGBArray;
  end;

type
  TImages = class
  private
    AnkList:string;
    AnkListCount:integer;
    MathDigBM:TBitmap;
    MathDigPtr:array of PMonoArray;
    X68kAnkFontBM:TBitmap;
    SmallFontBM:TBitmap;
    sfptr:array of PMonoArray;
    MoeFontPackLoaded:boolean;
    MoeFontPack:TFontPack;
    LastParaSmallMath:array[0..32] of integer;
    LastNoteSmallStringLine:array[0..16] of TNoteYBack;
    LastPlayTime:array[0..9] of char;
    LastTimerBCycle:LongInt;
    LastLoopCount:LongInt;
    LastTimerBClock:LongInt;
    procedure PreInitForX68kAnkFont;
    procedure MakeTitleBar(var CSkin:TCSkin);
    procedure WriteMath(x,y:integer; var rgb:TRGB; Src:LongInt; Len:LongInt);
    procedure WriteMathOne(x,y:integer;i:integer;var rgb:TRGB);
  public
    { Public 宣言 }
    GripArray:array[0..GripSize-1,0..GripSize-1] of TGray;
    TitleBarBMEx:TBMEx;
    BGBM,ScreenBM:TBitmap;
    BGPtr,ScreenPtr:array of PRGBArray;
    HSVMapBM:TBitmap;
    PanpotBM:TBitmap;
    PanpotPtr:array of PGrayArray;
    FuncBM,FuncBGBM:TBitmap;
    FuncPtr,FuncBGPtr:array of PRGBArray;
    SeekBarBMEx,VolumeBarBMEx,TempoBarBMEx,NetPrgBarBMEx:TBMEx;
    CompactBGBM:TBitmap;
    CompactBGPtr:array of PRGBArray;
    CompactButtonBMEx:TBMEx;
    constructor Create;
    destructor Destroy; override;
    procedure Init;
    procedure FreeMemory;
    procedure InitSkin;
    procedure FreeSkin;
    procedure LoadSkin(var CSkin:TCSkin);
    procedure LoadMoeFontPack(UseFontName:string);
    procedure FreeMoeFontPack;
    procedure CreateMoeFontPack(FontName:string;NowEncording:boolean);
    procedure CreateMoeFontPackEncordCallback(pos,max:integer);
    procedure ScreenRefresh(pos:TWindowPos);
    procedure DrawSizeGrip(bm:TBitmap;x,y:integer;var BGColor,GripColor:TRGB);
    procedure WriteX68kString(DstCanvas:TCanvas; x,y:LongInt; Text:String;ClearFlag:boolean);
    procedure WriteX68kStringTransparent(bm:TBitmap; x,y:LongInt; var rgb:TRGB; Text:String);
    procedure WriteMoeString(DstBM:TBitmap; x,y:LongInt; Width:integer; Text:String; defColor:dword; ClearFlag:boolean; ESCMode:integer; UseFontName:string);
    procedure WriteSmallStringToCanvas(DstCanvas:TCanvas; x,y:LongInt; var ColBG,ColText:TRGB; Text:String);
    procedure WriteSmallStringToScreen(x,y:LongInt; var ColText:TRGB; Text:String);
    procedure WriteNoteSmallStringOne(x,y:LongInt;c:byte;var ColText:TRGB);
    procedure WriteNoteSmallStringLine(x,y:LongInt; Text:String; Line:integer;var ColText:TRGB);
    procedure WriteNoteSmallString(x,y:LongInt; Text:String;var ColText:TRGB);
    procedure WriteParaSmallMath(x,y:LongInt; Line:integer; var rgb:TRGB; val:integer);
    procedure WriteNoteMath(x,y:LongInt; var ColText:TRGB; Src:LongInt; Len:LongInt);
    procedure WritePlayTime(Tick:LongInt);
    procedure WriteTimerBClock(clk:LongInt);
    procedure WriteTimerBCycle(clk:LongInt);
    procedure WriteLoopCount(cnt:LongInt);
    function  TimeToText(Tick:LongInt):string;
    function  TimeToTextms(Tick:LongInt):string;
    procedure UnpackYM2608(Path:string);
    procedure RemoveYM2608(Path:string);
    procedure InitLastCache;
  end;

var
  Images:TImages;

implementation

uses MainWin,
     _m_Tools, _const, _MDXWinINI, _MDXWin_const, _SimpleDialog,
     _WinPosConst;

const CHRSP:char=' ';

var
  Zero99Str:array[0..99] of string;

procedure InitBMEx(var BMEx:TBMEx;_Width,_Height:integer);
var
  y:integer;
  procedure InitBM(bm:TBitmap);
  begin
    with bm do begin
      PixelFormat:=pf24bit;
      Width:=_Width;
      Height:=_Height;
    end;
  end;
begin
  with BMEx do begin
    Width:=_Width;
    Height:=_Height;
    NegaBM:=TBitmap.Create;
    PosiBM:=TBitmap.Create;
    InitBM(NegaBM);
    InitBM(PosiBM);
    SetLength(NegaPtr,_Height);
    SetLength(PosiPtr,_Height);
    for y:=0 to _Height-1 do begin
      NegaPtr[y]:=NegaBM.ScanLine[y];
      PosiPtr[y]:=PosiBM.ScanLine[y];
    end;
  end;
end;

procedure LoadBMEx(var BMEx:TBMEx;_Width,_Height:integer;Posifn,Negafn:string);
var
  y:integer;
  procedure LoadBM(bm:TBitmap;fn:string);
  begin
    with bm do begin
      LoadFromFile(fn+'.bmp');
      PixelFormat:=pf24bit;
      Width:=_Width;
      Height:=_Height;
    end;
  end;
begin
  with BMEx do begin
    Width:=_Width;
    Height:=_Height;
    LoadBM(NegaBM,Negafn);
    LoadBM(PosiBM,Posifn);
    SetLength(NegaPtr,_Height);
    SetLength(PosiPtr,_Height);
    for y:=0 to _Height-1 do begin
      NegaPtr[y]:=NegaBM.ScanLine[y];
      PosiPtr[y]:=PosiBM.ScanLine[y];
    end;
  end;
end;

procedure LoadCSkinBMEx(var CSkin:TCSkin;var BMEx:TBMEx;_Width,_Height:integer;ID:string);
var
  y:integer;
begin
  with BMEx do begin
    Width:=_Width;
    Height:=_Height;
    CSkin.LoadBitmap(ID+'Nega.bmp',NegaBM,Width,Height,pf24bit);
    CSkin.LoadBitmap(ID+'Posi.bmp',PosiBM,Width,Height,pf24bit);
    SetLength(NegaPtr,_Height);
    SetLength(PosiPtr,_Height);
    for y:=0 to _Height-1 do begin
      NegaPtr[y]:=NegaBM.ScanLine[y];
      PosiPtr[y]:=PosiBM.ScanLine[y];
    end;
  end;
end;

procedure FreeBMEx(var BMEx:TBMEx);
begin
  with BMEx do begin
    Width:=0;
    Height:=0;
    if NegaBM<>nil then NegaBM.Free;
    NegaBM:=nil;
    if PosiBM<>nil then PosiBM.Free;
    PosiBM:=nil;
  end;
end;

constructor TImages.Create;
begin
  inherited Create;

  InitLastCache;

  AnkList:='';
  AnkListCount:=0;
  X68kAnkFontBM:=nil;
  PreInitForX68kAnkFont;

  SmallFontBM:=nil;
  MoeFontPackLoaded:=False;
  MoeFontPack:=nil;

  HSVMapBM:=nil;
end;

destructor TImages.Destroy;
begin
  AnkList:='';
  AnkListCount:=0;
  X68kAnkFontBM.Free;
  X68kAnkFontBM:=nil;

  inherited Destroy;
end;

procedure TImages.InitLastCache;
var
  x,i:integer;
begin
  for x:=0 to 32-1 do begin
    LastParaSmallMath[x]:=-1;
  end;
  for x:=0 to 16-1 do begin
    for i:=0 to 50-1 do begin
      LastNoteSmallStringLine[x][i]:=$00;
    end;
  end;

  for x:=0 to 9 do begin
    LastPlayTime[x]:=chr($00);
  end;
  LastTimerBCycle:=-1;
  LastLoopCount:=-1;
  LastTimerBClock:=-1;
end;

procedure TImages.ScreenRefresh(pos:TWindowPos);
begin
  with pos do begin
    BitBlt(Main.Canvas.Handle,Left,Top,Width,Height,ScreenBM.Canvas.Handle,Left,Top,SRCCOPY);
  end;
end;

procedure TImages.DrawSizeGrip(bm:TBitmap;x,y:integer;var BGColor,GripColor:TRGB);
var
  dx,dy:integer;
  pBM:PRGB;
  pGrip:PGray;
begin
  for dy:=0 to GripSize-1 do begin
    pBM:=bm.ScanLine[y+dy];
    inc(pBM,x);
    pGrip:=@GripArray[dy][0];
    for dx:=0 to GripSize-1 do begin
      pBM^:=RGBModulate(BGColor,GripColor,pGrip^);
      inc(pBM);
      inc(pGrip);
    end;
  end;
end;

procedure TImages.PreInitForX68kAnkFont;
var
  cnt:integer;
begin
  X68kAnkFontBM:=TBitmap.Create;
  LoadBitmapFromQDA('MDXWin.qda','X68kAnkFont.bmp',X68kAnkFontBM);
  X68kAnkFontBM.PixelFormat:=pf8bit;
  AnkList:='';
  for cnt:=ord(' ') to ord('~')+1 do begin
    AnkList:=AnkList+char(cnt);
  end;
  AnkListCount:=Length(AnkList);
end;

procedure TImages.Init;
var
  y:integer;
begin
  HSVMapBM:=TBitmap.Create;
  LoadBitmapFromQDA('MDXWin.qda','HSVMap.bmp',HSVMapBM);
  HSVMapBM.PixelFormat:=pf24bit;

  SmallFontBM:=TBitmap.Create;
  LoadBitmapFromQDA('MDXWin.qda','SmallFont.bmp',SmallFontBM);
  SmallFontBM.PixelFormat:=pf8bit;
  setlength(sfptr,SFY);
  for y:=0 to SFY-1 do begin
    sfptr[y]:=SmallFontBM.ScanLine[y];
  end;

  InitSkin;

  MoeFontPackLoaded:=False;
end;

procedure TImages.FreeMemory;
begin
  FreeSkin;

  SmallFontBM.Free;
  HSVMapBM.Free;

  FreeMoeFontPack;
end;

procedure TImages.InitSkin;
var
  y:integer;
begin
  InitBMEx(TitleBarBMEx,TitleBarWidth,TitleBarHeight);

  BGBM:=TBitmap.Create;
  MakeBlankBM(BGBM,640,340,pf24bit);
  SetLength(BGPtr,BGBM.Height);
  for y:=0 to BGBM.Height-1 do begin
    BGPtr[y]:=BGBM.ScanLine[y];
  end;

  ScreenBM:=TBitmap.Create;
  MakeBlankBM(ScreenBM,640,340,pf24bit);
  SetLength(ScreenPtr,ScreenBM.Height);
  for y:=0 to ScreenBM.Height-1 do begin
    ScreenPtr[y]:=ScreenBM.ScanLine[y];
  end;

  MathDigBM:=TBitmap.Create;
  MakeBlankBM(MathDigBM,MathDigX*12,MathDigY,pf8bit);
  CreateGrayscalePalette(MathDigBM);
  SetLength(MathDigPtr,MathDigY);
  for y:=0 to MathDigY-1 do begin
    MathDigPtr[y]:=MathDigBM.ScanLine[y];
  end;

  PanpotBM:=TBitmap.Create;
  MakeBlankBM(PanpotBM,PanpotSize*4,PanpotSize,pf8bit);
  CreateGrayscalePalette(PanpotBM);
  SetLength(PanpotPtr,PanpotSize);
  for y:=0 to PanpotSize-1 do begin
    PanpotPtr[y]:=PanpotBM.ScanLine[y];
  end;

  FuncBM:=TBitmap.Create;
  FuncBGBM:=TBitmap.Create;
  MakeBlankBM(FuncBM,FuncWidth,FuncHeight,pf24bit);
  MakeBlankBM(FuncBGBM,FuncWidth,FuncHeight,pf24bit);
  SetLength(FuncPtr,FuncHeight);
  SetLength(FuncBGPtr,FuncHeight);
  for y:=0 to FuncHeight-1 do begin
    FuncPtr[y]:=FuncBM.ScanLine[y];
    FuncBGPtr[y]:=FuncBGBM.ScanLine[y];
  end;

  CompactBGBM:=TBitmap.Create;
  MakeBlankBM(CompactBGBM,640,24,pf24bit);
  SetLength(CompactBGPtr,24);
  for y:=0 to 24-1 do begin
    CompactBGPtr[y]:=CompactBGBM.ScanLine[y];
  end;

  InitBMEx(CompactButtonBMEx,CmpBtnWidth,CmpBtnHeight);

  InitBMEx(SeekBarBMEx,640,BarHeight);
  InitBMEx(VolumeBarBMEx,64,BarHeight);
  InitBMEx(TempoBarBMEx,64,BarHeight);
  InitBMEx(NetPrgBarBMEx,384,BarHeight);
end;

procedure TImages.FreeSkin;
begin
  FreeBMEx(TitleBarBMEx);

  BGBM.Free;
  BGBM:=nil;
  ScreenBM.Free;
  ScreenBM:=nil;
  MathDigBM.Free;
  MathDigBM:=nil;
  PanpotBM.Free;
  PanpotBM:=nil;
  FuncBM.Free;
  FuncBM:=nil;
  FuncBGBM.Free;
  FuncBGBM:=nil;
  CompactBGBM.Free;
  CompactBGBM:=nil;

  FreeBMEx(CompactButtonBMEx);

  FreeBMEx(SeekBarBMEx);
  FreeBMEx(VolumeBarBMEx);
  FreeBMEx(TempoBarBMEx);
  FreeBMEx(NetPrgBarBMEx);
end;

procedure TImages.LoadSkin(var CSkin:TCSkin);
var
  bm:TBitmap;
  GripBM:TBitmap;
  pGripBM:PGray;
  x,y:integer;
  Alpha:integer;
  pBM,pBG:PRGB;
begin
  MakeTitleBar(CSkin);

  bm:=TBitmap.Create;

  CSkin.LoadBitmap('BG.bmp',bm,640,480,pf24bit);
  with BGBM do begin
    BitBlt(Canvas.Handle,0,0,Width,Height,bm.Canvas.Handle,0,0,SRCCOPY);
  end;

  FillAlphaBM(BGBM,CSkin.ColBG,CSkin.BGAlpha);
  FillAlphaBMPos(BGBM,SpeFlamePos,CSkin.ColWinSpeFrameBG,CSkin.WinSpeFrameAlpha);
  FillAlphaBMPos(BGBM,SpePos,CSkin.ColWinSpeBG,CSkin.WinSpeAlpha);
  FillAlphaBMPos(BGBM,VelPos,CSkin.ColWinVelBG,CSkin.WinVelAlpha);
  FillAlphaBMPos(BGBM,ParaPos,CSkin.ColWinParamBG,CSkin.WinParamAlpha);

  with ScreenBM do begin
    BitBlt(Canvas.Handle,0,0,Width,Height,BGBM.Canvas.Handle,0,0,SRCCOPY);
  end;

  GripBM:=TBitmap.Create;
  CSkin.LoadBitmap('Grip.bmp',GripBM,GripSize,GripSize,pf8bit);
  GripBM.PixelFormat:=pf8bit;
  for y:=0 to GripSize-1 do begin
    pGripBM:=GripBM.ScanLine[y];
    for x:=0 to GripSize-1 do begin
      GripArray[y,x]:=pGripBM^;
      inc(pGripBM);
    end;
  end;
  GripBM.Free;

  CSkin.LoadBitmap('MathDig.bmp',bm,MathDigX*12,MathDigY,pf24bit);
  with MathDigBM do begin
    BitBlt(Canvas.Handle,0,0,Width,Height,bm.Canvas.Handle,0,0,SRCCOPY);
  end;
  CSkin.LoadBitmap('Panpot.bmp',bm,PanpotSize*4,PanpotSize,pf24bit);
  with PanpotBM do begin
    BitBlt(Canvas.Handle,0,0,Width,Height,bm.Canvas.Handle,0,0,SRCCOPY);
  end;

  CSkin.LoadBitmap('FuncBG.bmp',bm,FuncWidth,FuncHeight,pf24bit);
  with FuncBGBM do begin
    BitBlt(Canvas.Handle,0,0,Width,Height,bm.Canvas.Handle,0,0,SRCCOPY);
  end;
  FillAlphaBM(FuncBGBM,CSKin.ColFuncBG,CSkin.FuncBGAlpha);
  with FuncBM do begin
    BitBlt(Canvas.Handle,0,0,Width,Height,FuncBGBM.Canvas.Handle,0,0,SRCCOPY);
  end;

  CSkin.LoadBitmap('CompactBG.bmp',bm,640,24,pf24bit);
  with CompactBGBM do begin
    BitBlt(Canvas.Handle,0,0,Width,Height,bm.Canvas.Handle,0,0,SRCCOPY);
  end;
  FillAlphaBM(CompactBGBM,CSkin.ColCompactBG,CSkin.CompactBGAlpha);

  CSkin.LoadBitmap('CompactButton.bmp',bm,CmpBtnWidth,CmpBtnHeight,pf24bit);
  with CompactButtonBMEx do begin
    BitBlt(PosiBM.Canvas.Handle,0,0,CmpBtnWidth,CmpBtnHeight,bm.Canvas.Handle,0,0,SRCCOPY);
    BitBlt(NegaBM.Canvas.Handle,0,0,CmpBtnWidth,CmpBtnHeight,bm.Canvas.Handle,0,CmpBtnHeight,SRCCOPY);
  end;

  bm.Free;

  LoadCSkinBMEx(CSkin,SeekBarBMEx,640,BarHeight,'SeekBar');
  LoadCSkinBMEx(CSkin,VolumeBarBMEx,64,BarHeight,'VolumeBar');
  LoadCSkinBMEx(CSkin,TempoBarBMEx,64,BarHeight,'TempoBar');
  LoadCSkinBMEx(CSkin,NetPrgBarBMEx,384,BarHeight,'NetPrgBar');

  // Make NoteAnalizer BackScreen
  for y:=0 to 271 do begin
    if ((y+2) mod 34)=0 then begin
      Alpha:=CSkin.VisKeyBGLineAlpha;
      end else begin
      Alpha:=CSkin.VisKeyBGAlpha;
    end;
    pBM:=@NoteBGPtr[y][0];
    pBG:=@BGPtr[y+24][7];
    for x:=0 to 282 do begin
      pBM^:=RGBModulate(pBG^,CSkin.ColVisKeyBG,Alpha);
      inc(pBM);
      inc(pBG);
    end;
  end;
end;

procedure TImages.MakeTitleBar(var CSkin:TCSkin);
var
  msg:string;
  BGBM,IconBM,TextBM,SysBtnBM:TBitmap;
  pSrc24:PRGB;
  pSrc8:PGray;
  pBG,pDst:PRGB;
  x,y:integer;
begin
  BGBM:=TBitmap.Create;
  CSkin.LoadBitmap('TitleBar_BG.bmp',BGBM,TitleBarWidth,TitleBarHeight,pf24bit);
  FillAlphaBM(BGBM,CSkin.ColTitleBarBG,CSkin.TitleBarBGAlpha);

  IconBM:=TBitmap.Create;
  CSkin.LoadBitmap('TitleBar_Icon.bmp',IconBM,16,TitleBarHeight,pf8bit);

  TextBM:=TBitmap.Create;
  CSkin.LoadBitmap('TitleBar_Text.bmp',TextBM,564,TitleBarHeight,pf8bit);
  msg:='Ver '+MDXWinVersion+'-'+MDXWinDate;
  WriteX68kString(TextBM.Canvas,TextBM.Width-((length(msg)+1)*8),1,msg,False);

  SysBtnBM:=TBitmap.Create;
  CSkin.LoadBitmap('TitleBar_SysBtn.bmp',SysBtnBM,56,TitleBarHeight,pf24bit);

  for y:=0 to TitleBarY-1 do begin
    pDst:=Images.TitleBarBMEx.PosiBM.ScanLine[y];
    pBG:=BGBM.ScanLine[y];

    pSrc8:=IconBM.ScanLine[y];
    for x:=0 to IconBM.Width-1 do begin
      pDst^:=RGBModulate(pBG^,CSkin.ColTitleBarIcon,pSrc8^);
      inc(pDst);
      inc(pBG);
      inc(pSrc8);
    end;
    pSrc8:=TextBM.ScanLine[y];
    for x:=0 to TextBM.Width-1 do begin
      pDst^:=RGBModulate(pBG^,CSkin.ColTitleBarText,pSrc8^);
      inc(pDst);
      inc(pBG);
      inc(pSrc8);
    end;
    pSrc24:=SysBtnBM.ScanLine[y];
    for x:=0 to SysBtnBM.Width-1 do begin
      if (pSrc24^.b=$00) and (pSrc24^.g=$ff) and (pSrc24^.r=$00) then begin
        pDst^:=pBG^;
        end else begin
        pDst^:=RGBModulate(pBG^,pSrc24^,CSkin.TitleBarSysBtnAlpha);
      end;
      inc(pDst);                   
      inc(pBG);
      inc(pSrc24);
    end;
  end;

  BGBM.Free;
  IconBM.Free;
  TextBM.Free;
  SysBtnBM.Free;

  with TitleBarBMEx do begin
    BitBlt(NegaBM.Canvas.Handle,0,0,Width,Height,PosiBM.Canvas.Handle,0,0,SRCCOPY);
    FillAlphaBM(NegaBM,CSkin.ColTitleBarNega,CSkin.TitleBarNegaAlpha);
  end;
end;

procedure TImages.WriteX68kString(DstCanvas:TCanvas; x,y:LongInt; Text:String;ClearFlag:boolean);
var
  cnt:integer;
  px:integer;
begin
  if ClearFlag=True then DstCanvas.FillRect(Rect(0,0,999,999));
  cnt:=0;
  while (cnt<Length(Text)) do begin
    if isAnkChar(byte(Text[cnt+1]))=True then begin
      px:=pos(Text[cnt+1],AnkList);
      if px<>0 then begin
        BitBlt(DstCanvas.Handle,x+cnt*8,y,8,16,X68kAnkFontBM.Canvas.Handle,(px-1)*8,0,SRCCOPY);
        end else begin
        DstCanvas.TextOut(x+cnt*8,y,Text[cnt+1]);
      end;
      inc(cnt,1);
      end else begin
      if (cnt+2)<=Length(Text) then begin
        DstCanvas.TextOut(x+cnt*8,y,Text[cnt+1]+Text[cnt+2]);
      end;
      inc(cnt,2);
    end;
  end;
end;

procedure TImages.WriteX68kStringTransparent(bm:TBitmap; x,y:LongInt; var rgb:TRGB; Text:String);
var
  pFont:PMono;
  pBM:PRGB;
  dx,dy:integer;
  cnt:integer;
  px:integer;
begin
  Text:=copy(Text,1,((bm.Width-x) div 8)-1);

  bm.Canvas.Brush.Style:=bsClear;
  bm.Canvas.Font.Color:=rgb2dword(rgb);

  cnt:=0;
  while (cnt<Length(Text)) do begin
    if isAnkChar(byte(Text[cnt+1]))=True then begin
      px:=pos(Text[cnt+1],AnkList);
      if px<>0 then begin
        for dy:=0 to 16-1 do begin
          pFont:=X68kAnkFontBM.ScanLine[dy];
          inc(pFont,(px-1)*8);
          pBM:=bm.ScanLine[y+dy];
          inc(pBM,x+(cnt*8));
          for dx:=0 to 8-1 do begin
            if pFont^<>$00 then pBM^:=rgb;
            inc(pFont);
            inc(pBM);
          end;
        end;
        end else begin
        bm.Canvas.TextOut(x+cnt*8,y,Text[cnt+1]);
      end;
      inc(cnt,1);
      end else begin
      if (cnt+2)<=Length(Text) then begin
        bm.Canvas.TextOut(x+cnt*8,y,Text[cnt+1]+Text[cnt+2]);
      end;
      inc(cnt,2);
    end;
  end;
end;

procedure TImages.LoadMoeFontPack(UseFontName:string);
begin
  if MoeFontPackLoaded=True then begin
    if MoeFontPack.FontName<>UseFontName then begin
      FreeMoeFontPack;
      MoeFontPackLoaded:=False;
    end;
  end;

  if MoeFontPackLoaded=False then begin
    if FileExists(StartPath+'FontCache\'+UseFontName+'.fpk')=True then begin
      MoeFontPackLoaded:=True;
      MoeFontPack:=TFontPack.Create;
      MoeFontPack.LoadFromFile(StartPath+'FontCache\'+UseFontName+'.fpk');
      end else begin
      CreateMoeFontPack(UseFontName,False);
      MoeFontPackLoaded:=True;
    end;
  end;
end;

procedure TImages.FreeMoeFontPack;
begin
  if MoeFontPackLoaded=True then begin
    if MoeFontPack.FontAdded=True then MoeFontPack.SaveToFile(StartPath+'FontCache\'+MoeFontPack.FontName+'.fpk');
    MoeFontPack.Free;
    MoeFontPackLoaded:=False;
  end;
end;

procedure TImages.CreateMoeFontPack(FontName:string;NowEncording:boolean);
begin
  MoeFontPackLoaded:=True;
  MoeFontPack:=TFontPack.Create;

  MoeFontPack.FontHeight:=18;
  MoeFontPack.FontName:=FontName;
  MoeFontPack.EnabledAntialias:=True;
  MoeFontPack.EncordCallback:=CreateMoeFontPackEncordCallback;

  if NowEncording=True then begin
    with MoeFontPack.FontSet do begin
      ANK_Alpha:=True;
      ANK_Kana:=True;
      SJIS_noKanji:=True;
      SJIS_KanjiLv1:=True;
      SJIS_AlabiaNum:=True;
      SJIS_KanjiLv2:=True;
      SJIS_KanjiEtc:=True;
      X68kPlus:=True;
    end;

{
    with Main do begin
      DownloadPrgGroup.Font.Color:=CSkin.dwBright;
      DownloadTitleLbl.Caption:='タイトル用フォントキャッシュを作成しています…';
      DownloadStopShape.Pen.Color:=CSkin.dwBright;
      DownloadStopLbl.Font.Color:=CSkin.dwBright;
      DownloadStopShape.Pen.Color:=CSkin.dwMask;
      DownloadStopLbl.Font.Color:=CSkin.dwMask;
      DownloadInfoLbl.Caption:='Standby...';
      DownloadPrgBar.Min:=0;
      DownloadPrgBar.Position:=0;
      DownloadPrgBar.Max:=0;
      DownloadPrgGroup.Visible:=True;
      DownloadPrgGroup.Refresh;
    end;
}

    if MoeFontPack.FullAutoEncord=True then begin
{
      with Main do begin
        DownloadPrgBar.Position:=DownloadPrgBar.Max;
        DownloadInfoLbl.Caption:='save cache...';
        DownloadInfoLbl.Refresh;
      end;
}
      MoeFontPack.SaveToFile(StartPath+'FontCache\'+FontName+'.fpk');
      end else begin
      ShowMessage('CreateFontPack','フォントの作成に失敗しました。');
    end;

{
    with Main do begin
      DownloadPrgGroup.Visible:=False;
      DownloadPrgGroup.Refresh;
    end;
}
  end;
end;

procedure TImages.CreateMoeFontPackEncordCallback(pos,max:integer);
begin
  if (pos mod 128)<>0 then exit;

{
  with Main do begin
    DownloadPrgBar.Max:=max;
    DownloadPrgBar.Position:=pos;
    DownloadInfoLbl.Caption:=IntToStr(pos)+'/'+IntToStr(max)+'chars.';
    DownloadInfoLbl.Refresh;
  end;
}
end;

procedure TImages.WriteMoeString(DstBM:TBitmap; x,y:LongInt; Width:integer; Text:String; defColor:dword; ClearFlag:boolean; ESCMode:integer; UseFontName:string);
var
  cnt,ax:integer;
  c:word;
  ch:char;
  isESC:boolean;
  ESCStr:string;
  txColor,txBGColor:dword;
  txHighLight,txPerticalLine,txUnderLine,txBlink,txReverse,txSecret:boolean;
  procedure ProcessESCX68k(ESCStr:string; var txColor,txBGColor:dword; var txBold,txReverse:boolean);
  var
    attr:integer;
    cnt:integer;
    ch:char;
  begin
    if ESCStr='' then exit;
    if (ESCStr[1]<>'[') or (ESCStr[length(ESCStr)]<>'m') then exit; // 文字属性変更以外は無視

    attr:=0;
    cnt:=2;
    while (cnt<=Length(ESCStr)) do begin
      ch:=ESCStr[cnt];
      if attr<=99 then begin
        if ('0'<=ch) and (ch<='9') then attr:=attr*10+StrToIntDef(ch,0);
      end;
      if (ch='m') or (ch=';') then begin
        case attr of
          0: begin // 初期設定に戻す
            txColor:=defColor;
            txBGColor:=$000000;
            txBold:=False;
            txReverse:=False;
          end;
          1: begin // 強調トグル(30〜33,40〜43 と 34〜37,44〜47 を交互に変更)
            txBold:=not txBold;
          end;
          7: begin // 逆転トグル(30〜33,34〜37 と 40〜43,44〜47 を交互に変更)
            txReverse:=not txReverse;
          end;
          30: txColor:=$000000; // 黒
          31: txColor:=$00e3e7; // 水色
          32: txColor:=$e7e300; // 黄色
          33: txColor:=$ffffff; // 白
          34: txColor:=$000000; // 黒
          35: txColor:=$00e3e7; // 水色
          36: txColor:=$e7e300; // 黄色
          37: txColor:=$ffffff; // 白
          40: txColor:=$000000; // 黒
          41: txColor:=$00e3e7; // 水色
          42: txColor:=$e7e300; // 黄色
          43: txColor:=$ffffff; // 白
          44: txColor:=$000000; // 黒
          45: txColor:=$00e3e7; // 水色
          46: txColor:=$e7e300; // 黄色
          47: txColor:=$ffffff; // 白
        end;
        if (30<=attr) and (attr<=33) then begin // 標準属性
          txBold:=False;
          txReverse:=False;
        end;
        if (34<=attr) and (attr<=37) then begin // 強調属性
          txBold:=True;
          txReverse:=False;
        end;
        if (40<=attr) and (attr<=43) then begin // 逆転属性
          txBold:=False;
          txReverse:=True;
        end;
        if (44<=attr) and (attr<=47) then begin // 強調逆転属性
          txBold:=True;
          txReverse:=True;
        end;
      end;
      inc(cnt);
    end;
  end;
  procedure ProcessESCPC98(ESCStr:string; var txColor,txBGColor:dword; var txBold,txPerticalLine,txUnderLine,txBlink,txReverse,txSecret:boolean);
  var
    attr:integer;
    cnt:integer;
    ch:char;
  begin
    if ESCStr='' then exit;
    if (ESCStr[1]<>'[') or (ESCStr[length(ESCStr)]<>'m') then exit; // 文字属性変更以外は無視

    // 開始前に初期化（なぜ？）
    txColor:=defColor;
    txBGColor:=$000000;
    txBold:=False;
    txPerticalLine:=False;
    txUnderLine:=False;
    txBlink:=False;
    txReverse:=False;
    txSecret:=False;

    attr:=0;
    cnt:=2;
    while (cnt<=Length(ESCStr)) do begin
      ch:=ESCStr[cnt];
      if attr<=99 then begin
        if ('0'<=ch) and (ch<='9') then attr:=attr*10+StrToIntDef(ch,0);
      end;
      if (ch='m') or (ch=';') then begin
        case attr of
          0: begin // 初期設定に戻す
            txColor:=defColor;
            txBGColor:=$000000;
            txBold:=False;
            txPerticalLine:=False;
            txUnderLine:=False;
            txBlink:=False;
            txReverse:=False;
            txSecret:=False;
            txBold:=False;
          end;
          1: begin // 強調有効
            txBold:=True;
          end;
          2: begin // 縦中線有効
            txPerticalLine:=True;
          end;
          4: begin // アンダーライン有効
            txUnderLine:=True;
          end;
          5: begin // 点滅有効
            txBlink:=True;
          end;
          7: begin // 反転有効
            txReverse:=True;
          end;
          8: begin // 隠密有効
            txSecret:=True;
          end;
          16: txColor:=$000000; // Black
          17: txColor:=$0000FF; // Red
          18: txColor:=$FF0000; // Blue
          19: txColor:=$FF00FF; // Perple
          20: txColor:=$00FF00; // Green
          21: txColor:=$00FFFF; // Yellow
          22: txColor:=$FFFF00; // Water
          23: txColor:=$FFFFFF; // White
          30: txColor:=$000000; // Black
          31: txColor:=$0000FF; // Red
          32: txColor:=$00FF00; // Green
          33: txColor:=$00FFFF; // Yellow
          34: txColor:=$FF0000; // Blue
          35: txColor:=$FF00FF; // Perple
          36: txColor:=$FFFF00; // Water
          37: txColor:=$FFFFFF; // White
          40: txColor:=$000000; // B.Black
          41: txColor:=$0000FF; // B.Red
          42: txColor:=$00FF00; // B.Green
          43: txColor:=$00FFFF; // B.Yellow
          44: txColor:=$FF0000; // B.Blue
          45: txColor:=$FF00FF; // B.Perple
          46: txColor:=$FFFF00; // B.Water
          47: txColor:=$FFFFFF; // B.White
        end;
        if (40<=attr) and (attr<=47) then begin // 反転属性
          txReverse:=True;
        end;
      end;
      inc(cnt);
    end;
  end;
begin
  LoadMoeFontPack(UseFontName);

  txColor:=defColor;
  txBGColor:=$000000;
  txHighLight:=False;
  txPerticalLine:=False;
  txUnderLine:=False;
  txBlink:=False;
  txReverse:=False;
  txSecret:=False;

  with MoeFontPack.DrawMode do begin
    SetRGBPack(TextColor,txColor);
    Transparent:=False;
    SetRGBPack(BGColor,txBGColor);
  end;

  if ClearFlag=True then DstBM.Canvas.FillRect(Rect(0,0,999,999));

  cnt:=1;
  ax:=0;

  isESC:=False;
  ESCStr:='';

  while (cnt<=Length(Text)) do begin
    if Width=-1 then begin
      if ((ax+1)*(MoeFontPack.FontHeight div 2))>=DstBM.Width then exit;
      end else begin
      if ((ax+1)*(MoeFontPack.FontHeight div 2))>=Width then exit;
    end;
    if isESC=False then begin
      if byte(Text[cnt])=$1b then begin // startESC
        isESC:=True;
        ESCStr:='';
        inc(cnt,1);
        end else begin
        if isAnkChar(byte(Text[cnt]))=True then begin // 1byte
          c:=word(Text[cnt])*$100+$00;
          MoeFontPack.DrawMode.Transparent:=(txBGColor=$000000);
          MoeFontPack.DrawDecord(DstBM,x+ax*(MoeFontPack.FontHeight div 2),y,c,txHighLight,txPerticalLine,txUnderLine);
          if MoeFontPack.Data[c].Enabled=False then begin
            DstBM.Canvas.TextOut(x+ax*(MoeFontPack.FontHeight div 2),y,Text[cnt]);
          end;
          inc(ax,1);
          inc(cnt,1);
          end else begin // 2byte
          if (cnt+1)<=Length(Text) then begin
            c:=word(Text[cnt])*$100+word(Text[cnt+1]);
            MoeFontPack.DrawMode.Transparent:=(txBGColor=$000000);
            MoeFontPack.DrawDecord(DstBM,x+ax*(MoeFontPack.FontHeight div 2),y,c,txHighLight,txPerticalLine,txUnderLine);
            if MoeFontPack.Data[c].Enabled=False then begin
              DstBM.Canvas.TextOut(x+ax*(MoeFontPack.FontHeight div 2),y,Text[cnt]+Text[cnt+1]);
            end;
          end;
          c:=byte(Text[cnt]);
          if (c=$80) or (c=$f0) or (c=$f1) or (c=$f2) or (c=$f3) then begin // 2byte8dot
            inc(ax,1);
            end else begin // 2byte16dot
            inc(ax,2);
          end;
          inc(cnt,2);
        end;
      end;
      end else begin // nowESC
      ch:=Text[cnt];
      ESCStr:=ESCStr+ch;
      if (('a'<=ch) and (ch<='z')) or (('A'<=ch) and (ch<='Z')) or (ch='@') or (ch='*') then begin // endESC
        case ESCMode of
          ESCModeNone: begin
            txColor:=defColor;
            txBGColor:=$000000;
            txHighLight:=False;
            txPerticalLine:=False;
            txUnderLine:=False;
            txBlink:=False;
            txReverse:=False;
            txSecret:=False;
          end;
          ESCModeX68k: begin
            ProcessESCX68k(ESCStr,txColor,txBGColor,txHighLight,txReverse);
            with MoeFontPack.DrawMode do begin
              if txReverse=False then begin
                SetRGBPack(TextColor,txColor);
                SetRGBPack(BGColor,txBGColor);
                end else begin
                SetRGBPack(TextColor,txBGColor);
                SetRGBPack(BGColor,txColor);
              end;
            end;
          end;
          ESCModePC98: begin
            ProcessESCPC98(ESCStr,txColor,txBGColor,txHighLight,txPerticalLine,txUnderLine,txBlink,txReverse,txSecret);
            with MoeFontPack.DrawMode do begin
              if (txReverse=False) and (txBlink=False) then begin
                SetRGBPack(TextColor,txColor);
                SetRGBPack(BGColor,txBGColor);
                end else begin
                SetRGBPack(TextColor,txBGColor);
                SetRGBPack(BGColor,txColor);
              end;
              if txSecret=True then begin
                SetRGBPack(TextColor,txBGColor);
                SetRGBPack(BGColor,txBGColor);
              end;
            end;
          end;
        end;
        isESC:=False;
        ESCStr:='';
      end;
      if length(ESCStr)>=16 then begin // 16文字以上のESCシーケンスは処理しない。
        isESC:=False;
        ESCStr:='';
      end;
      inc(cnt,1);
    end;
  end;
end;

procedure TImages.WriteSmallStringToCanvas(DstCanvas:TCanvas; x,y:LongInt; var ColBG,ColText:TRGB; Text:String);
var
  dsbm:TBitmap;
  dsptr:array[0..7] of PRGBArray;
  pds:PRGB;
  psf:PMono;
  cnt,Count:integer;
  dx,dy,px:integer;
begin
  if Text='' then exit;

  Count:=Length(Text);

  dsbm:=TBitmap.Create;
  MakeBlankBM(dsbm,Count*SFX,SFY,pf24bit);
  for dy:=0 to SFY-1 do begin
    dsptr[dy]:=dsbm.ScanLine[dy];
  end;

  for cnt:=0 to Count-1 do begin
    px:=byte(Text[cnt+1])-byte(CHRSP);
    for dy:=0 to SFY-1 do begin
      pds:=@dsptr[dy][cnt*SFX];
      psf:=@sfptr[dy][px*SFX];
      for dx:=0 to SFX-1 do begin
        if psf^=$00 then begin
          pds^:=ColBG;
          end else begin
          pds^:=ColText;
        end;
        inc(pds);
        inc(psf);
      end;
    end;
  end;

  BitBlt(DstCanvas.Handle,x,y,dsbm.Width,dsbm.Height,dsbm.Canvas.Handle,0,0,SRCCOPY);
  dsbm.Free;
end;

procedure TImages.WriteSmallStringToScreen(x,y:LongInt; var ColText:TRGB; Text:String);
var
  pos:TWindowPos;
  cnt,Count:integer;
  c:byte;
  tx:integer;
  dx,dy:integer;
  SrcX:integer;
  psf:PMono;
  pBG,pScr:PRGB;
begin
  if Text='' then exit;

  Count:=Length(Text);

  for cnt:=0 to Count-1 do begin
    tx:=x+(cnt*SFX);
    c:=byte(Text[cnt+1]);
    if c<=byte(CHRSP) then begin
      for dy:=0 to SFY-1 do begin
        pBG:=@BGPtr[y+dy][tx];
        pScr:=@ScreenPtr[y+dy][tx];
        for dx:=0 to SFX-1 do begin
          pScr^:=pBG^;
          inc(pBG);
          inc(pScr);
        end;
      end;
      end else begin
      SrcX:=integer(c-byte(CHRSP))*SFX;
      for dy:=0 to SFY-1 do begin
        pBG:=@BGPtr[y+dy][tx];
        pScr:=@ScreenPtr[y+dy][tx];
        psf:=@sfptr[dy][SrcX];
        for dx:=0 to SFX-1 do begin
          if psf^=$00 then begin
            pScr^:=pBG^;
            end else begin
            pScr^:=ColText;
          end;
          inc(pBG);
          inc(pScr);
          inc(psf);
        end;
      end;
    end;
  end;

  pos.Left:=x;
  pos.Top:=y;
  pos.Width:=Count*SFX;
  pos.Height:=SFY;
  ScreenRefresh(pos);
end;

procedure TImages.WriteNoteSmallStringOne(x,y:LongInt;c:byte;var ColText:TRGB);
var
  cx,cy,py:integer;
  SrcX:integer;
  pNote,pNoteBG:PRGB;
  psf:PMono;
begin
  if c<=byte(CHRSP) then begin
    for cy:=0 to SFY-1 do begin
      py:=y+cy;
      pNote:=@NotePtr[py][x];
      pNoteBG:=@NoteBGPtr[py][x];
      for cx:=0 to SFX-1 do begin
        pNote^:=pNoteBG^;
        inc(pNote);
        inc(pNoteBG);
      end;
    end;
    end else begin
    SrcX:=integer(c-byte(CHRSP))*SFX;
    for cy:=0 to SFY-1 do begin
      py:=y+cy;
      pNote:=@NotePtr[py][x];
      pNoteBG:=@NoteBGPtr[py][x];
      psf:=@sfptr[cy][SrcX];
      for cx:=0 to SFX-1 do begin
        if psf^=$00 then begin
          pNote^:=pNoteBG^;
          end else begin
          pNote^:=ColText;
        end;
        inc(pNote);
        inc(pNoteBG);
        inc(psf);
      end;
    end;
  end;
end;

procedure TImages.WriteNoteSmallStringLine(x,y:LongInt; Text:String; Line:integer;var ColText:TRGB);
var
  cnt:integer;
  DstX:integer;
  TextNo:byte;
begin
  cnt:=0;
  while (cnt<Length(Text)) do begin
    DstX:=(x+(cnt*SFX));
    TextNo:=byte(Text[cnt+1]);
    if TextNo<>LastNoteSmallStringLine[Line][cnt] then begin
      LastNoteSmallStringLine[Line][cnt]:=TextNo;
      WriteNoteSmallStringOne(DstX,y,TextNo,ColText);
    end;
    inc(cnt,1);
  end;
end;

procedure TImages.WriteNoteSmallString(x,y:LongInt; Text:String;var ColText:TRGB);
var
  cnt:integer;
  DstX:integer;
  TextNo:byte;
begin
  cnt:=0;
  while (cnt<Length(Text)) do begin
    DstX:=(x+(cnt*SFX));
    TextNo:=byte(Text[cnt+1]);
    WriteNoteSmallStringOne(DstX,y,TextNo,ColText);
    inc(cnt,1);
  end;
end;

procedure TImages.WriteParaSmallMath(x,y:LongInt; Line:integer; var rgb:TRGB; val:integer);
var
  c0,c1,c2:byte;
  procedure DrawChar(c:byte;DstX:integer);
  var
    SrcX:integer;
    px,py:integer;
    pPara,pParaBG:PRGB;
    psf:PMono;
  begin
    DstX:=x+DstX;
    if c<=byte(CHRSP) then begin
      for py:=0 to SFY-1 do begin
        pPara:=@ParaPtr[y+py][DstX];
        pParaBG:=@ParaBGPtr[y+py][DstX];
        for px:=0 to SFX-1 do begin
          pPara^:=pParaBG^;
          inc(pPara);
          inc(pParaBG);
        end;
      end;
      end else begin
      SrcX:=(c-byte(CHRSP))*SFX;
      for py:=0 to SFY-1 do begin
        pPara:=@ParaPtr[y+py][DstX];
        pParaBG:=@ParaBGPtr[y+py][DstX];
        psf:=@sfptr[py][SrcX];
        for px:=0 to SFX-1 do begin
          if psf^=$00 then begin
            pPara^:=pParaBG^;
            end else begin
            pPara^:=rgb;
          end;
          inc(pPara);
          inc(pParaBG);
          inc(psf);
        end;
      end;
    end;
  end;
begin
  if LastParaSmallMath[Line]=val then exit;
  LastParaSmallMath[Line]:=val;

  if val<=9 then begin
    c0:=byte(CHRSP);
    c1:=byte(CHRSP);
    c2:=byte('0')+val;
    end else begin
    if val<=99 then begin
      c0:=byte(CHRSP);
      c1:=byte('0')+(val div 10 mod 10);
      c2:=byte('0')+(val mod 10);
      end else begin
      c0:=byte('0')+(val div 100 mod 10);
      c1:=byte('0')+(val div 10 mod 10);
      c2:=byte('0')+(val mod 10);
    end;
  end;

  DrawChar(c0,0*SFX);
  DrawChar(c1,1*SFX);
  DrawChar(c2,2*SFX);
end;

procedure TImages.WriteMathOne(x,y:integer;i:integer;var rgb:TRGB);
var
  pSrc:PMono;
  pBG,pScr:PRGB;
  px,py:integer;
begin
  i:=i*MathDigX;

  for py:=0 to MathDigY-1 do begin
    pSrc:=@MathDigPtr[py][i];
    pBG:=@BGPtr[y+py][x];
    pScr:=@ScreenPtr[y+py][x];
    for px:=0 to MathDigX-1 do begin
      pScr^:=RGBModulate(pBG^,rgb,pSrc^);
      inc(pSrc);
      inc(pBG);
      inc(pScr);
    end;
  end;
end;

procedure TImages.WriteMath(x,y:integer; var rgb:TRGB; Src:LongInt; Len:LongInt);
var
  WStr:string;
  Cnt:LongInt;
  SrcX:LongInt;
begin
  if Src<0 then Src:=0;
  WStr:=Zero99Str[Src div 1000000 mod 100]+Zero99Str[Src div 10000 mod 100]+Zero99Str[Src div 100 mod 100]+Zero99Str[Src mod 100];
  WStr:=copy(WStr,Length(WStr)-Len+1,Len);

  for Cnt:=0 to Length(WStr)-1 do begin
    SrcX:=(byte(WStr[Cnt+1])-Ord('0'));
    WriteMathOne(x+(cnt*MathDigX),y,SrcX,rgb);
  end;
end;

procedure TImages.WriteNoteMath(x,y:LongInt; var ColText:TRGB; Src:LongInt; Len:LongInt);
var
  WStr:string;
  Cnt:LongInt;
  SrcX:LongInt;
  pSrc:PMono;
  pNote,pNoteBG:PRGB;
  px,py:integer;
begin
  if Src<0 then Src:=0;
  WStr:=Zero99Str[Src div 1000000 mod 100]+Zero99Str[Src div 10000 mod 100]+Zero99Str[Src div 100 mod 100]+Zero99Str[Src mod 100];
  WStr:=copy(WStr,Length(WStr)-Len+1,Len);

  for Cnt:=0 to Length(WStr)-1 do begin
    SrcX:=(byte(WStr[Cnt+1])-Ord('0'))*MathDigX;
    for py:=0 to MathDigY-1 do begin
      pSrc:=@MathDigPtr[py][SrcX];
      pNote:=@NotePtr[y+py][x+(Cnt*MathDigX)];
      pNoteBG:=@NoteBGPtr[y+py][x+(Cnt*MathDigX)];
      for px:=0 to MathDigX-1 do begin
        pNote^:=RGBModulate(pNoteBG^,ColText,pSrc^);
        inc(pSrc);
        inc(pNote);
        inc(pNoteBG);
      end;
    end;
  end;
end;

procedure TImages.WritePlayTime(Tick:LongInt);
var
  PlayTimePos:TWindowPos;
  WStr:string;
  Cnt:LongInt;
  SrcX:LongInt;
  x,y:integer;
begin
  PlayTimePos.Left:=435;
  PlayTimePos.Top:=46;
  PlayTimePos.Width:=MathDigX*9;
  PlayTimePos.Height:=MathDigY;

  if Tick<0 then Tick:=0;
  WStr:=TimeToTextms(Tick);

  for Cnt:=0 to Length(WStr)-1 do begin
    if LastPlayTime[Cnt]<>WStr[Cnt+1] then begin
      LastPlayTime[Cnt]:=WStr[Cnt+1];
      x:=PlayTimePos.Left+(Cnt*MathDigX);
      y:=PlayTimePos.Top;
      if WStr[Cnt+1]=CHRSP then
        SrcX:=11
        else
        SrcX:=byte(WStr[Cnt+1])-Ord('0');
      WriteMathOne(x,y,SrcX,CSkin.ColBright);
    end;
  end;

  ScreenRefresh(PlayTimePos);
end;

procedure TImages.WriteTimerBClock(clk:LongInt);
var
  TimerBClockPos:TWindowPos;
begin
  TimerBClockPos.Left:=435;
  TimerBClockPos.Top:=70;
  TimerBClockPos.Width:=MathDigX*7;
  TimerBClockPos.Height:=MathDigY;

  if clk<0 then clk:=0;
  if LastTimerBClock<>clk then begin
    LastTimerBClock:=clk;
    WriteMath(TimerBClockPos.Left,TimerBClockPos.Top,CSkin.ColBright,clk,7);
  end;

  ScreenRefresh(TimerBClockPos);
end;

procedure TImages.WriteTimerBCycle(clk:LongInt);
var
  TimerBCyclePos:TWindowPos;
begin
  TimerBCyclePos.Left:=581;
  TimerBCyclePos.Top:=70;
  TimerBCyclePos.Width:=MathDigX*4;
  TimerBCyclePos.Height:=MathDigY;

  if clk<0 then clk:=0;
  if LastTimerBCycle<>clk then begin
    LastTimerBCycle:=clk;
    WriteMath(TimerBCyclePos.Left,TimerBCyclePos.Top,CSkin.ColBright,clk,4);
  end;

  ScreenRefresh(TimerBCyclePos);
end;

procedure TImages.WriteLoopCount(cnt:LongInt);
var
  LoopCountPos:TWindowPos;
begin
  LoopCountPos.Left:=607;
  LoopCountPos.Top:=46;
  LoopCountPos.Width:=MathDigX*2;
  LoopCountPos.Height:=MathDigY;

  if cnt<0 then cnt:=0;
  if LastLoopCount<>cnt then begin
    LastLoopCount:=cnt;
    WriteMath(LoopCountPos.Left,LoopCountPos.Top,CSkin.ColBright,cnt,2);
  end;

  ScreenRefresh(LoopCountPos);
end;

function TImages.TimeToText(Tick:LongInt):string;
var
  WStr:string;
  WTmp:string;
begin
  if Tick<0 then Tick:=0;
  
  WTmp:=Zero99Str[(Tick div 60 div 2) mod 100];
  if Length(WTmp)=1 then WTmp:='0'+WTmp;
  if (Tick mod 2)=0 then
    WStr:=WTmp+':'
    else
    WStr:=WTmp+CHRSP;

  WTmp:=Zero99Str[(Tick div 2) mod 60];
  if Length(WTmp)=1 then WTmp:='0'+WTmp;
  WStr:=WStr+WTmp;

  Result:=WStr;
end;

function TImages.TimeToTextms(Tick:LongInt):string;
var
  iTmp:integer;
  sDemi:char;
  sTmp:string;
begin
  if Tick<0 then Tick:=0;
  
  sTmp:='';

  if (Tick mod 1000)<500 then
    sDemi:=CHRSP
    else
    sDemi:=':';

  iTmp:=(Tick div 1000 div 60) mod 100;
  sTmp:=sTmp+Zero99Str[iTmp]+sDemi;

  iTmp:=(Tick div 1000) mod 60;
  sTmp:=sTmp+Zero99Str[iTmp]+sDemi;

  iTmp:=Tick mod 1000;
  sTmp:=sTmp+inttostr(iTmp div 100)+Zero99Str[iTmp mod 100];

  Result:=sTmp;
end;

procedure TImages.UnpackYM2608(Path:string);
var
  WF:TMemoryStream;
  procedure CreateFile(Path,Filename:string);
  begin
    WF:=ExtractFromQDAFile(StartPath+'MDXWin.qda',Filename);
    WF.SaveToFile(Path+Filename);
    WF.Free;
  end;
begin
  CreateDir(Path);
  CreateFile(Path,'2608_BD.WAV');
  CreateFile(Path,'2608_HH.WAV');
  CreateFile(Path,'2608_RIM.WAV');
  CreateFile(Path,'2608_SD.WAV');
  CreateFile(Path,'2608_TOM.WAV');
  CreateFile(Path,'2608_TOP.WAV');
end;

procedure TImages.RemoveYM2608(Path:string);
begin
  DeleteFile(Path+'2608_BD.WAV');
  DeleteFile(Path+'2608_HH.WAV');
  DeleteFile(Path+'2608_RIM.WAV');
  DeleteFile(Path+'2608_SD.WAV');
  DeleteFile(Path+'2608_TOM.WAV');
  DeleteFile(Path+'2608_TOP.WAV');
  RemoveDir(Path);
end;

var
  Zero99StrCnt:integer;

initialization
  for Zero99StrCnt:=0 to 99 do begin
    if Zero99StrCnt<=9 then begin
      Zero99Str[Zero99StrCnt]:='0'+IntToStr(Zero99StrCnt);
      end else begin
      Zero99Str[Zero99StrCnt]:=IntToStr(Zero99StrCnt);
    end;
  end;

finalization

end.
