observervram.pas

来自「一个不出名的GBA模拟器」· PAS 代码 · 共 892 行 · 第 1/2 页

PAS
892
字号
//////////////////////////////////////////////////////////////////////
//                                                                  //
// observerVRAM.pas: VRAM observer (all video modes)                //
//                                                                  //
// The contents of this file are subject to the Bottled Light       //
// Public License Version 1.0 (the "License"); you may not use this //
// file except in compliance with the License. You may obtain a     //
// copy of the License at http://www.bottledlight.com/BLPL/         //
//                                                                  //
// Software distributed under the License is distributed on an      //
// "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or   //
// implied. See the License for the specific language governing     //
// rights and limitations under the License.                        //
//                                                                  //
// The Original Code is the Mappy VM User Interface, released       //
// April 1st, 2003. The Initial Developer of the Original Code is   //
// Bottled Light, Inc. Portions created by Bottled Light, Inc. are  //
// Copyright (C) 2001-2003 Bottled Light, Inc. All Rights Reserved. //
//                                                                  //
// Author(s):                                                       //
//   Michael Noland (joat), michael@bottledlight.com                //
//                                                                  //
// Changelog:                                                       //
//   1.0: First public release (April 1st, 2003)                    //
//                                                                  //
// Notes:                                                           //
//   When this observer is active, it slows everything down         //
//   considerably, its probably a good candidate for optimization.  //
//                                                                  //
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
unit observerVRAM; ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
interface ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, IniFiles, CheckLst, StdCtrls, ComCtrls, Menus, ExtCtrls,
  Math, ClipBrd, CpuObservers, console, nexus, AddressSpace;

//////////////////////////////////////////////////////////////////////

type
  TjdevVRAMViewer = class(TCpuObserver)
    pages: TPageControl;
    pTileMap: TTabSheet;
    pTiles: TTabSheet;
    pSprites: TTabSheet;
    pFramebuffer: TTabSheet;
    rMode3: TRadioButton;
    rMode4fb0: TRadioButton;
    rMode5fb0: TRadioButton;
    Label1: TLabel;
    rMode5fb1: TRadioButton;
    rMode4fb1: TRadioButton;
    cbScreenSize: TComboBox;
    cbTileMap: TComboBox;
    cbTileSet: TComboBox;
    cbBackgrounds: TComboBox;
    loadBGButton: TButton;
    r16Colors: TRadioButton;
    r256Colors: TRadioButton;
    lMapLocation: TLabel;
    lTilesetLocation: TLabel;
    lScreenSize: TLabel;
    lGeneral: TLabel;
    rWraparound: TCheckBox;
    lColorMode: TLabel;
    rRotScale: TCheckBox;
    lxferSettings: TLabel;
    bevel: TBevel;
    image: TPaintBox;
    horizScroll: TScrollBar;
    vertScroll: TScrollBar;
    cbTilesetTiles: TComboBox;
    lTilesetLocationTiles: TLabel;
    r256ColorTiles: TRadioButton;
    r16ColorTiles: TRadioButton;
    lColorModeTiles: TLabel;
    lColorModeSprites: TLabel;
    r16ColorSprites: TRadioButton;
    r256ColorSprites: TRadioButton;
    saveBGButton: TButton;
    lPosition: TLabel;
    lPosX: TLabel;
    paletteBase: TScrollBar;
    lPaletteBase: TLabel;
    lPaletteBaseDisp: TLabel;
    lPaletteBase2: TLabel;
    lPaletteBaseDisp2: TLabel;
    paletteBase2: TScrollBar;
    pLayerSheet: TTabSheet;
    activeLayers: TCheckListBox;
    Label2: TLabel;
    bResetLayers: TButton;
    Label3: TLabel;
    lLayerPos: TLabel;
    lLayerColor: TLabel;
    lLayerLayer: TLabel;
    bSaveToFile: TButton;
    bCopyToClipboard: TButton;
    saveDialog: TSaveDialog;
    magnifier: TTrackBar;
    lMagnifier: TLabel;
    bSaveLayers: TButton;
    lTileMapCR: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure toggleRotScaleMode(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure UpdateObserverX(Sender: TObject);
    procedure paletteBaseChange(Sender: TObject);
    procedure loadBGSettings(Sender: TObject);
    procedure SaveBGSettings(Sender: TObject);
    procedure paletteBase2Change(Sender: TObject);
    procedure UpdateScrollDisp(Sender: TObject);
    procedure reloadLayersFromDispCR(Sender: TObject);
    procedure imageMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure SaveToFile(Sender: TObject);
    procedure CopyToClipboard(Sender: TObject);
    procedure SaveLayersToDispCR(Sender: TObject);
    procedure imagePaint(Sender: TObject);
  private
    // banks is only valid in the functions below
    banks: TvmMemoryLock1;
    backups: array[0..REGISTERS_MASK] of byte;
    myScreen: TBitmap;
    active: boolean;
    mult: integer;
    layerWatchX, layerWatchY: integer;
    tileWidth, tileHeight: integer;
    lastFx, lastFy: integer;

    zoom: integer;
    pageIndex: integer;

    procedure RenderTileMap;
    procedure RenderTiles;
    procedure RenderSprites;
    procedure RenderFramebuffer;
    procedure RenderLayers;
    function PackBGSettings: uint16;
    procedure UnpackBGSettings(cr: uint16);
    procedure DispScreen;
    procedure DoScrollbar;
  public
    procedure UpdateObserver; override;
    class function OCaption: string; override;
    procedure LoadSettings(ini: TIniFile); override;
    procedure SaveSettings(ini: TIniFile); override;
  end;

//////////////////////////////////////////////////////////////////////

var
  jdevVRAMViewer: TjdevVRAMViewer;

//////////////////////////////////////////////////////////////////////
implementation ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

{$R *.DFM}

const
  binarySt: array[0..3] of string = ('%00', '%01', '%10', '%11');
  normalSizes: array[0..3] of TPoint = ((x:256; y:256), (x:512; y:256), (x:256; y:512), (x:512; y:512));
  rotScaleSizes: array[0..3] of TPoint = ((x:128; y:128), (x:256; y:256), (x:512; y:512), (x:1024; y:1024));

//////////////////////////////////////////////////////////////////////

class function TjdevVRAMViewer.OCaption: string;
begin
  Result := 'Video Viewer';
end;

//////////////////////////////////////////////////////////////////////

const
  layerStrings: array[0..5] of string = (
    'BG0', 'BG1', 'BG2', 'BG3', 'Sprite', 'Backdrop'
  );

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.UpdateObserver;
var
  x, y: integer;
  line: Puint16;
begin
  // Precautions
  if not active then Exit;
  active := false;

  // Lock VM memory and make some backups
  vmLockMemory(banks);
  Move(banks.iospace^, backups, REGISTERS_MASK+1);

  // Render the observer display
  banks.iospace^[BLEND_S1] := 0;
  case pages.ActivePageIndex of
    0: RenderTileMap;
    1: RenderTiles;
    2: RenderSprites;
    3: RenderFramebuffer;
    4: RenderLayers;
  end;

  // Remap the colors to BGR, because windows is stupid
  if pages.ActivePageIndex in [0, 3, 4] then
    for y := 0 to myScreen.Height - 1 do begin
      line := myScreen.ScanLine[y];
      for x := 0 to myScreen.Width - 1 do begin
        line^ := (line^ shr 10) and $1f + (line^ and $1f) shl 10 + ((line^ shr 5) and $1f) shl 5;
        Inc(line);
      end;
    end;

  // Draw it
  DispScreen;

  // Restore the backups and unlock VM memory
  Move(backups, banks.iospace^, REGISTERS_MASK+1);
  vmUnlockMemory(banks);

  active := true;
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.FormCreate(Sender: TObject);
var
  i: integer;
begin
  HelpContext := LinkHelp('video_viewer.html');

  if (pageIndex >= 0) and (pageIndex < pages.PageCount) then pages.ActivePageIndex := pageIndex;
  if (zoom > 0) and (zoom <= magnifier.Max) then magnifier.Position := zoom;

  // Add entries to the tile map box, in increments of 2kb
  for i := 0 to 31 do
    cbTileMap.Items.Add(Format('$%2.2x ($%8.8x)', [i, $06000000+i*2048]));
  cbTileMap.ItemIndex := 0;

  // Set up the tileset address boxes and the screen size box
  for i := 0 to 3 do begin
    cbTileSet.Items.Add(Format('%s ($%8.8x)', [binarySt[i], $06000000+i*16384]));
    cbTileSetTiles.Items.Add(Format('%s ($%8.8x)', [binarySt[i], $06000000+i*16384]));
  end;
  cbTileSet.ItemIndex := 0;
  cbTileSetTiles.ItemIndex := 0;

  // Take care of the backgrounds avaiable for transfer
  for i := 0 to 3 do
    cbBackgrounds.Items.Add(Format('Background %d', [i]));
  cbBackgrounds.ItemIndex := 0;

  // Set up the frame buffer selection on the frame buffer page
  rMode4fb0.Checked := true;

  // Set up the color mode on each page
  r256Colors.Checked := true;
  r16Colors.Checked := not r256Colors.Checked;
  r256ColorTiles.Checked := true;
  r256ColorSprites.Checked := true;

  // Set up the general settins on the tile maps page
  rRotScale.Checked := false;
  rWraparound.Checked := false;
  rWraparound.Enabled := false;

  // Set up the default screen sizes
  for i := 0 to 3 do
    cbScreenSize.Items.Add(Format('%s (%dx%d)', [binarySt[i], normalSizes[i].x, normalSizes[i].y]));
  cbScreenSize.ItemIndex := 0;

  // Set up the screen
  myScreen := TBitmap.Create;
  myScreen.PixelFormat := pf15bit;
  myScreen.width := 256;
  myScreen.height := 256;

  // Set up the layers page
  reloadLayersFromDispCR(Sender);
  layerWatchX := -1;
  layerWatchY := -1;

  active := true;
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.toggleRotScaleMode(Sender: TObject);
var
  i: integer;
  sizeIndex: integer;
begin
  sizeIndex := cbScreenSize.ItemIndex;
  if sizeIndex < 0 then sizeIndex := 0;
  if rRotScale.Checked then begin
    // Toggle some things
    rWraparound.Enabled := true;
    r16Colors.Enabled := false;
    r256Colors.Checked := true;
    r16Colors.Checked := not r256Colors.Checked;

    // Take care of the screen size
    cbScreenSize.Items.Clear;
    for i := 0 to 3 do
      cbScreenSize.Items.Add(Format('%s (%dx%d)', [binarySt[i], rotScaleSizes[i].x, rotScaleSizes[i].y]));
    cbScreenSize.ItemIndex := 0;

    // Take care of the backgrounds avaiable for transfer
    cbBackgrounds.Clear;
    for i := 2 to 3 do
      cbBackgrounds.Items.Add(Format('Background %d', [i]));
    cbBackgrounds.ItemIndex := 0;
  end else begin
    // Toggle some things
    rWraparound.Enabled := false;
    r16Colors.Enabled := true;

    // Take care of the screen size
    cbScreenSize.Items.Clear;
    for i := 0 to 3 do
      cbScreenSize.Items.Add(Format('%s (%dx%d)', [binarySt[i], normalSizes[i].x, normalSizes[i].y]));
    cbScreenSize.ItemIndex := 0;

    // Take care of the backgrounds avaiable for transfer
    cbBackgrounds.Clear;
    for i := 0 to 3 do
      cbBackgrounds.Items.Add(Format('Background %d', [i]));
    cbBackgrounds.ItemIndex := 0;
  end;
  cbScreenSize.ItemIndex := sizeIndex;

  // Redraw
  UpdateScrollDisp(Sender);
  UpdateObserver;
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.FormDestroy(Sender: TObject);
begin
  myScreen.Free;
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.UpdateObserverX(Sender: TObject);
begin
  zoom := magnifier.Position;
  pageIndex := pages.ActivePageIndex;
  DoScrollbar;
  UpdateObserver;
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.paletteBaseChange(Sender: TObject);
begin
  lPaletteBaseDisp.Caption := Format('%d ($%2.2x)', [paletteBase.Position, paletteBase.Position*16]);
  UpdateObserver;
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.loadBGSettings(Sender: TObject);
var
  index: integer;
begin
  if cbBackgrounds.ItemIndex < 0 then cbBackgrounds.ItemIndex := 0;
  index := cbBackgrounds.ItemIndex;
  if rRotScale.Checked then Inc(index, 2);
  UnpackBGSettings(vmReadHalfword($04000000 + BG0_CR + index shl 1));
  UpdateObserver;
end;

//////////////////////////////////////////////////////////////////////

function TjdevVRAMViewer.PackBGSettings: uint16;
var
  cr: uint16;
begin
  cr := 0;
  if cbTileSet.ItemIndex < 0 then cbTileSet.ItemIndex := 0;
  cr := cr or (cbTileSet.ItemIndex shl 2);
  if rWraparound.Checked then cr := cr or (1 shl 13);
  if r256Colors.Checked then cr := cr or TEXT_BG_256COLORS;
  if cbTileMap.ItemIndex < 0 then cbTileMap.ItemIndex := 0;
  cr := cr or (cbTileMap.ItemIndex shl 8);
  if cbScreenSize.ItemIndex < 0 then cbScreenSize.ItemIndex := 0;
  cr := cr or (cbScreenSize.ItemIndex shl 14);
  Result := cr;
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.UnpackBGSettings(cr: uint16);
begin
  // Pack the tile set
  r256Colors.Checked := cr and TEXT_BG_256COLORS <> 0;
  rWraparound.Checked := cr and (1 shl 13) <> 0;
  cbTileSet.ItemIndex := (cr shr 2) and 3;
  cbTileMap.ItemIndex := (cr shr 8) and $1F;
  cbScreenSize.ItemIndex := cr shr 14;

  if rRotScale.Checked then r256Colors.Checked := true;
  r16Colors.Checked := not r256Colors.Checked;

  DoScrollbar;
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.SaveBGSettings(Sender: TObject);
begin
  if cbBackgrounds.ItemIndex < 0 then cbBackgrounds.ItemIndex := 0;
  vmWriteHalfword($04000000 + BG0_CR + cbBackgrounds.ItemIndex shl 1, PackBGSettings);
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.paletteBase2Change(Sender: TObject);
begin
  lPaletteBaseDisp2.Caption := Format('%d ($%2.2x)', [paletteBase2.Position, paletteBase2.Position*16]);
  UpdateObserver;
end;

//////////////////////////////////////////////////////////////////////

procedure TjdevVRAMViewer.UpdateScrollDisp(Sender: TObject);
begin
  if rRotScale.Checked then begin
    lPosX.Caption := Format('X: %d.0, Y: %d.0', [horizScroll.position, vertScroll.position]);
  end else begin
    lPosX.Caption := Format('X: %d, X: %d', [horizScroll.position, vertScroll.position]);
  end;
  UpdateObserver;
end;

//////////////////////////////////////////////////////////////////////

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?