📄 observersprites.pas
字号:
//////////////////////////////////////////////////////////////////////
// //
// observerSprites.pas: Sprite observer //
// //
// 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: //
// None at present. //
// //
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
unit observerSprites; ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
interface ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ComCtrls, clipbrd,
cpuObservers, console, nexus, addressSpace, Menus;
//////////////////////////////////////////////////////////////////////
type
TjdevSprites = class(TCpuObserver)
llSpriteInFocus: TLabel;
spriteOpen: TEdit;
spriteOpenSpinner: TUpDown;
gSpriteParams: TGroupBox;
bSpritePreviewBevel: TBevel;
spriteHeight: TLabel;
spriteWidth: TLabel;
llSpriteWidth: TLabel;
llSpriteHeight: TLabel;
llSpriteX: TLabel;
spriteXPos: TLabel;
spriteYPos: TLabel;
llSpriteY: TLabel;
spritePreview: TImage;
lSpritePriority: TLabel;
llSpriteName: TLabel;
spriteName: TLabel;
spritePriority: TLabel;
spritePal: TLabel;
llSpritePalette: TLabel;
spriteVdy: TLabel;
lSpriteDmy: TLabel;
lSpriteDmx: TLabel;
spriteVdx: TLabel;
lSpriteDy: TLabel;
spriteHdy: TLabel;
lSpriteDx: TLabel;
spriteHdx: TLabel;
lSpriteMatrix: TLabel;
spriteMatrixSet: TLabel;
lSpriteMode: TLabel;
spriteMode: TLabel;
lSpritePreview: TLabel;
spriteApplyBlending: TCheckBox;
spriteApplyMosiac: TCheckBox;
spriteFlipY: TCheckBox;
spriteFlipX: TCheckBox;
spriteDoubleSized: TCheckBox;
saveDialog: TSaveDialog;
bSaveToFile: TButton;
bCopyToClipboard: TButton;
lOccupied: TLabel;
scrollbar: TTrackBar;
occupied: TPaintBox;
bHelp: TButton;
procedure SelectNewSprite(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure SaveSprite(Sender: TObject);
procedure CopySprite(Sender: TObject);
procedure scrollbarChange(Sender: TObject);
procedure ShowHelp(Sender: TObject);
public
activeSprite: uint32;
bmp: TBitmap;
procedure UpdateObserver; override;
class function OCaption: string; override;
end;
//////////////////////////////////////////////////////////////////////
var
jdevSprites: TjdevSprites;
//////////////////////////////////////////////////////////////////////
implementation ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
{$R *.DFM}
//////////////////////////////////////////////////////////////////////
procedure TjdevSprites.UpdateObserver;
type
Pint16 = ^smallint;
var
spr: PSprite;
t: uint16;
height, x, y: integer;
line: PWordArray;
banks: TvmMemoryLock1;
begin
vmLockMemory(banks);
// Draw the occupancy graph
for y := 0 to 127 do begin
spr := @(banks.OAM[y shl 3]);
if ((spr.a = 0) and (spr.b = 0) and (spr.c = 0)) or
((spr.a and SPRITE_A_DOUBLESIZE <> 0) and (spr.a and SPRITE_A_ROTSCALE = 0)) then
occupied.canvas.Pen.Color := clWhite
else
occupied.canvas.Pen.Color := clBlack;
occupied.Canvas.MoveTo(0, y);
occupied.Canvas.LineTo(15, y);
end;
occupied.canvas.Pen.Color := clRed;
occupied.Canvas.MoveTo(0, activeSprite);
occupied.Canvas.LineTo(15, activeSprite);
spr := @(banks.OAM[activeSprite shl 3]);
t := ((spr.b shr 14) or ((spr.a shr 12) and $C)) shl 1;
if t > 22 then begin
spriteWidth.caption := 'inv';
spriteHeight.caption := 'inv';
height := -1;
end else begin
spriteWidth.caption := IntToStr(SpriteSizes[t]);
height := SpriteSizes[t+1];
spriteHeight.caption := IntToStr(height);
end;
spriteXPos.caption := IntToStr(spr.b and $1FF);
spriteYPos.caption := IntToStr(spr.a and $FF);
if spr.a and SPRITE_A_256MODE <> 0 then
spritePal.caption := '1/1'
else
spritePal.caption := IntToStr(spr.c shr 12 + 1) + '/16';
spriteName.caption := IntToHex(spr.c and $3FF, 5);
spriteDoubleSized.checked := spr.a and SPRITE_A_DOUBLESIZE <> 0;
if spriteDoubleSized.checked then height := height shl 1;
spriteApplyMosiac.checked := spr.a and SPRITE_A_MOSAIC <> 0;
spriteApplyBlending.checked := (spr.a shr 10) and 3 = 1;
if spr.a and SPRITE_A_ROTSCALE <> 0 then begin
spriteMode.caption := 'Rot / Scale';
t := (spr.b shr 9) and $1F;
spriteMatrixSet.caption := IntToStr(t);
t := t shl 5 + 6;
spriteHdx.caption := Format('%6f', [Pint16(@(banks.OAM[t]))^ / 256.0]);
spriteHdy.caption := Format('%6f', [Pint16(@(banks.OAM[t + 8]))^ / 256.0]);
spriteVdx.caption := Format('%6f', [Pint16(@(banks.OAM[t + 16]))^ / 256.0]);
spriteVdy.caption := Format('%6f', [Pint16(@(banks.OAM[t + 24]))^ / 256.0]);
end else begin
spriteMode.caption := 'Normal';
spriteMatrixSet.caption := 'n/a';
spriteHdx.caption := 'n/a';
spriteHdy.caption := 'n/a';
spriteVdx.caption := 'n/a';
spriteVdy.caption := 'n/a';
end;
for y := 0 to 127 do begin
// Get the pointer and clear the scanline
line := bmp.ScanLine[y];
if not Assigned(line) then Exit;
t := vmReadHalfword($05000000);
for x := 0 to 127 do line^[x] := t;
// Render the sprite
if y < height then vmRenderSprite(activeSprite, y, @(line^));
// Remap the colors to BGR, because windows is stupid
for x := 0 to 127 do
line^[x] := (line^[x] shr 10) and $1f + (line^[x] and $1f) shl 10 + ((line^[x] shr 5) and $1f) shl 5;
end;
// spritePreview.Canvas.StretchDraw(Rect(0, 0, 64, 64), bmp);
spritePreview.Canvas.Draw(0, 0, bmp);
spritePriority.caption := IntToStr((spr.c shr 10) and 3);
spriteFlipX.checked := spr.b and SPRITE_B_FLIP_X <> 0;
spriteFlipY.checked := spr.b and SPRITE_B_FLIP_Y <> 0;
vmUnlockMemory(banks);
end;
//////////////////////////////////////////////////////////////////////
procedure TjdevSprites.FormCreate(Sender: TObject);
begin
HelpContext := LinkHelp('sprite_viewer.html');
activeSprite := 0;
bmp := TBitmap.Create;
bmp.PixelFormat := pf15bit;
bmp.width := 128;
bmp.height := 128;
end;
//////////////////////////////////////////////////////////////////////
procedure TjdevSprites.SelectNewSprite(Sender: TObject);
var
num: integer;
begin
num := StrToIntDef(spriteOpen.Text, 0);
if num < 0 then num := 0;
if num > 127 then num := 127;
scrollbar.Position := num;
end;
//////////////////////////////////////////////////////////////////////
procedure TjdevSprites.FormDestroy(Sender: TObject);
begin
bmp.Free;
end;
//////////////////////////////////////////////////////////////////////
class function TjdevSprites.OCaption: string;
begin
Result := 'Sprite Viewer';
end;
//////////////////////////////////////////////////////////////////////
procedure TjdevSprites.SaveSprite(Sender: TObject);
begin
if saveDialog.Execute then
spritePreview.Picture.SaveToFile(saveDialog.FileName);
end;
//////////////////////////////////////////////////////////////////////
procedure TjdevSprites.CopySprite(Sender: TObject);
var
myFormat: word;
data: THandle;
pal: HPalette;
begin
spritePreview.Picture.SaveToClipBoardFormat(myFormat, data, pal);
clipboard.SetAsHandle(myFormat, data);
end;
//////////////////////////////////////////////////////////////////////
procedure TjdevSprites.scrollbarChange(Sender: TObject);
begin
spriteOpen.Text := IntToStr(scrollbar.Position);
activeSprite := scrollbar.Position;
UpdateObserver;
end;
//////////////////////////////////////////////////////////////////////
procedure TjdevSprites.ShowHelp(Sender: TObject);
begin
ShowWebPage(helpFiles.strings[HelpContext-1]);
end;
//////////////////////////////////////////////////////////////////////
initialization
RegisterViewer(TjdevSprites);
end.
//////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -