From 7c99ff50be11f58a516ec3bee617b66d3b4638c5 Mon Sep 17 00:00:00 2001 From: Anton Zhuchkov Date: Tue, 31 Oct 2017 09:13:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B2=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=D1=87=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=BC=D0=B8=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- D3DX81ab.dll | Bin 0 -> 310784 bytes Dynamic_Bass.pas | 1170 +++++++++++ SimpleXML.pas | 4479 ++++++++++++++++++++++++++++++++++++++++++ bass.dll | Bin 0 -> 111772 bytes bassmidi.pas | 158 ++ d2d.gex | 73 + d2dApplication.pas | 340 ++++ d2dClasses.pas | 126 ++ d2dCore.pas | 2847 +++++++++++++++++++++++++++ d2dDocRoot.pas | 250 +++ d2dFont.pas | 720 +++++++ d2dFormattedText.pas | 1360 +++++++++++++ d2dFrames.pas | 223 +++ d2dGIF.pas | 195 ++ d2dGUI.pas | 500 +++++ d2dGUIButtons.pas | 489 +++++ d2dGUIMenus.pas | 637 ++++++ d2dGUITextPane.pas | 823 ++++++++ d2dGUITypes.pas | 44 + d2dGUIUtils.pas | 98 + d2dInterfaces.pas | 93 + d2dPath.pas | 177 ++ d2dSimplePicture.pas | 84 + d2dSprite.pas | 537 +++++ d2dStaticText.pas | 397 ++++ d2dStorage.pas | 177 ++ d2dTexture.pas | 98 + d2dTypes.pas | 350 ++++ d2dUtils.pas | 560 ++++++ d2dZipPack.pas | 377 ++++ 31 files changed, 17383 insertions(+), 1 deletion(-) create mode 100644 D3DX81ab.dll create mode 100644 Dynamic_Bass.pas create mode 100644 SimpleXML.pas create mode 100644 bass.dll create mode 100644 bassmidi.pas create mode 100644 d2d.gex create mode 100644 d2dApplication.pas create mode 100644 d2dClasses.pas create mode 100644 d2dCore.pas create mode 100644 d2dDocRoot.pas create mode 100644 d2dFont.pas create mode 100644 d2dFormattedText.pas create mode 100644 d2dFrames.pas create mode 100644 d2dGIF.pas create mode 100644 d2dGUI.pas create mode 100644 d2dGUIButtons.pas create mode 100644 d2dGUIMenus.pas create mode 100644 d2dGUITextPane.pas create mode 100644 d2dGUITypes.pas create mode 100644 d2dGUIUtils.pas create mode 100644 d2dInterfaces.pas create mode 100644 d2dPath.pas create mode 100644 d2dSimplePicture.pas create mode 100644 d2dSprite.pas create mode 100644 d2dStaticText.pas create mode 100644 d2dStorage.pas create mode 100644 d2dTexture.pas create mode 100644 d2dTypes.pas create mode 100644 d2dUtils.pas create mode 100644 d2dZipPack.pas diff --git a/.gitignore b/.gitignore index 19864c6..13e3ec8 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,7 @@ # Delphi compiler-generated binaries (safe to delete) *.exe -*.dll +#*.dll *.bpl *.bpi *.dcp diff --git a/D3DX81ab.dll b/D3DX81ab.dll new file mode 100644 index 0000000000000000000000000000000000000000..aefcc0591993793cdabcbeaade458ed82394a2e9 GIT binary patch literal 310784 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�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̉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%0DgsFs0wq3C5RsN}Q!$Acyi^M28>E2_}OPmp5E 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 K-Tv2?!2bhM$p}>d literal 0 HcmV?d00001 diff --git a/Dynamic_Bass.pas b/Dynamic_Bass.pas new file mode 100644 index 0000000..4fbcf25 --- /dev/null +++ b/Dynamic_Bass.pas @@ -0,0 +1,1170 @@ +{ + BASS 2.4.7.1 Delphi unit (dynamic) + Copyright (c) 1999-2008 Un4seen Developments Ltd. + + See the BASS.CHM file for more detailed documentation + + How to install + ---------------- + Copy DYNAMIC_BASS.PAS to the \LIB subdirectory of your Delphi path or your project dir + + Call Load_BASSDLL (eg. in FormCreate) to load BASS before using any functions, and + Unload_BASSDLL (eg. in FormDestory) to unload it when you're done. +} + +unit Dynamic_Bass; + +interface + +uses + Windows, + SysUtils; + +const + BASSVERSION = $204; // API version + BASSVERSIONTEXT = '2.4'; + + // Use these to test for error from functions that return a DWORD or QWORD + DW_ERROR = Cardinal(-1); // -1 (DWORD) + QW_ERROR = Int64(-1); // -1 (QWORD) + + // Error codes returned by BASS_ErrorGetCode() + BASS_OK = 0; // all is OK + BASS_ERROR_MEM = 1; // memory error + BASS_ERROR_FILEOPEN = 2; // can't open the file + BASS_ERROR_DRIVER = 3; // can't find a free sound driver + BASS_ERROR_BUFLOST = 4; // the sample buffer was lost + BASS_ERROR_HANDLE = 5; // invalid handle + BASS_ERROR_FORMAT = 6; // unsupported sample format + BASS_ERROR_POSITION = 7; // invalid position + BASS_ERROR_INIT = 8; // BASS_Init has not been successfully called + BASS_ERROR_START = 9; // BASS_Start has not been successfully called + BASS_ERROR_ALREADY = 14; // already initialized/paused/whatever + BASS_ERROR_NOCHAN = 18; // can't get a free channel + BASS_ERROR_ILLTYPE = 19; // an illegal type was specified + BASS_ERROR_ILLPARAM = 20; // an illegal parameter was specified + BASS_ERROR_NO3D = 21; // no 3D support + BASS_ERROR_NOEAX = 22; // no EAX support + BASS_ERROR_DEVICE = 23; // illegal device number + BASS_ERROR_NOPLAY = 24; // not playing + BASS_ERROR_FREQ = 25; // illegal sample rate + BASS_ERROR_NOTFILE = 27; // the stream is not a file stream + BASS_ERROR_NOHW = 29; // no hardware voices available + BASS_ERROR_EMPTY = 31; // the MOD music has no sequence data + BASS_ERROR_NONET = 32; // no internet connection could be opened + BASS_ERROR_CREATE = 33; // couldn't create the file + BASS_ERROR_NOFX = 34; // effects are not enabled + BASS_ERROR_NOTAVAIL = 37; // requested data is not available + BASS_ERROR_DECODE = 38; // the channel is a "decoding channel" + BASS_ERROR_DX = 39; // a sufficient DirectX version is not installed + BASS_ERROR_TIMEOUT = 40; // connection timedout + BASS_ERROR_FILEFORM = 41; // unsupported file format + BASS_ERROR_SPEAKER = 42; // unavailable speaker + BASS_ERROR_VERSION = 43; // invalid BASS version (used by add-ons) + BASS_ERROR_CODEC = 44; // codec is not available/supported + BASS_ERROR_ENDED = 45; // the channel/file has ended + BASS_ERROR_BUSY = 46; // the device is busy + BASS_ERROR_UNKNOWN = -1; // some other mystery problem + + // BASS_SetConfig options + BASS_CONFIG_BUFFER = 0; + BASS_CONFIG_UPDATEPERIOD = 1; + BASS_CONFIG_GVOL_SAMPLE = 4; + BASS_CONFIG_GVOL_STREAM = 5; + BASS_CONFIG_GVOL_MUSIC = 6; + BASS_CONFIG_CURVE_VOL = 7; + BASS_CONFIG_CURVE_PAN = 8; + BASS_CONFIG_FLOATDSP = 9; + BASS_CONFIG_3DALGORITHM = 10; + BASS_CONFIG_NET_TIMEOUT = 11; + BASS_CONFIG_NET_BUFFER = 12; + BASS_CONFIG_PAUSE_NOPLAY = 13; + BASS_CONFIG_NET_PREBUF = 15; + BASS_CONFIG_NET_PASSIVE = 18; + BASS_CONFIG_REC_BUFFER = 19; + BASS_CONFIG_NET_PLAYLIST = 21; + BASS_CONFIG_MUSIC_VIRTUAL = 22; + BASS_CONFIG_VERIFY = 23; + BASS_CONFIG_UPDATETHREADS = 24; + BASS_CONFIG_DEV_BUFFER = 27; + BASS_CONFIG_MP3_ERRORS = 35; + BASS_CONFIG_DEV_DEFAULT = 36; + BASS_CONFIG_NET_READTIMEOUT = 37; + + BASS_CONFIG_REC_LOOPBACK = 28; // Vista Test + + // BASS_SetConfigPtr options + BASS_CONFIG_NET_AGENT = 16; + BASS_CONFIG_NET_PROXY = 17; + + // Initialization flags + BASS_DEVICE_8BITS = 1; // use 8 bit resolution, else 16 bit + BASS_DEVICE_MONO = 2; // use mono, else stereo + BASS_DEVICE_3D = 4; // enable 3D functionality + BASS_DEVICE_LATENCY = 256; // calculate device latency (BASS_INFO struct) + BASS_DEVICE_CPSPEAKERS = 1024; // detect speakers via Windows control panel + BASS_DEVICE_SPEAKERS = 2048; // force enabling of speaker assignment + BASS_DEVICE_NOSPEAKER = 4096; // ignore speaker arrangement + + BASS_DEVICE_LOOPBACK = 8; // Vista/win 7 Test //////////////////////////// + + // DirectSound interfaces (for use with BASS_GetDSoundObject) + BASS_OBJECT_DS = 1; // IDirectSound + BASS_OBJECT_DS3DL = 2; // IDirectSound3DListener + + // BASS_DEVICEINFO flags + BASS_DEVICE_ENABLED = 1; + BASS_DEVICE_DEFAULT = 2; + BASS_DEVICE_INIT = 4; + + // BASS_INFO flags (from DSOUND.H) + DSCAPS_CONTINUOUSRATE = $00000010; // supports all sample rates between min/maxrate + DSCAPS_EMULDRIVER = $00000020; // device does NOT have hardware DirectSound support + DSCAPS_CERTIFIED = $00000040; // device driver has been certified by Microsoft + DSCAPS_SECONDARYMONO = $00000100; // mono + DSCAPS_SECONDARYSTEREO = $00000200; // stereo + DSCAPS_SECONDARY8BIT = $00000400; // 8 bit + DSCAPS_SECONDARY16BIT = $00000800; // 16 bit + + // BASS_RECORDINFO flags (from DSOUND.H) + DSCCAPS_EMULDRIVER = DSCAPS_EMULDRIVER; // device does NOT have hardware DirectSound recording support + DSCCAPS_CERTIFIED = DSCAPS_CERTIFIED; // device driver has been certified by Microsoft + + // defines for formats field of BASS_RECORDINFO (from MMSYSTEM.H) + WAVE_FORMAT_1M08 = $00000001; // 11.025 kHz, Mono, 8-bit + WAVE_FORMAT_1S08 = $00000002; // 11.025 kHz, Stereo, 8-bit + WAVE_FORMAT_1M16 = $00000004; // 11.025 kHz, Mono, 16-bit + WAVE_FORMAT_1S16 = $00000008; // 11.025 kHz, Stereo, 16-bit + WAVE_FORMAT_2M08 = $00000010; // 22.05 kHz, Mono, 8-bit + WAVE_FORMAT_2S08 = $00000020; // 22.05 kHz, Stereo, 8-bit + WAVE_FORMAT_2M16 = $00000040; // 22.05 kHz, Mono, 16-bit + WAVE_FORMAT_2S16 = $00000080; // 22.05 kHz, Stereo, 16-bit + WAVE_FORMAT_4M08 = $00000100; // 44.1 kHz, Mono, 8-bit + WAVE_FORMAT_4S08 = $00000200; // 44.1 kHz, Stereo, 8-bit + WAVE_FORMAT_4M16 = $00000400; // 44.1 kHz, Mono, 16-bit + WAVE_FORMAT_4S16 = $00000800; // 44.1 kHz, Stereo, 16-bit + + BASS_SAMPLE_8BITS = 1; // 8 bit + BASS_SAMPLE_FLOAT = 256; // 32-bit floating-point + BASS_SAMPLE_MONO = 2; // mono + BASS_SAMPLE_LOOP = 4; // looped + BASS_SAMPLE_3D = 8; // 3D functionality + BASS_SAMPLE_SOFTWARE = 16; // not using hardware mixing + BASS_SAMPLE_MUTEMAX = 32; // mute at max distance (3D only) + BASS_SAMPLE_VAM = 64; // DX7 voice allocation & management + BASS_SAMPLE_FX = 128; // old implementation of DX8 effects + BASS_SAMPLE_OVER_VOL = $10000; // override lowest volume + BASS_SAMPLE_OVER_POS = $20000; // override longest playing + BASS_SAMPLE_OVER_DIST = $30000; // override furthest from listener (3D only) + + BASS_STREAM_PRESCAN = $20000; // enable pin-point seeking/length (MP3/MP2/MP1) + BASS_MP3_SETPOS = BASS_STREAM_PRESCAN; + BASS_STREAM_AUTOFREE = $40000; // automatically free the stream when it stop/ends + BASS_STREAM_RESTRATE = $80000; // restrict the download rate of internet file streams + BASS_STREAM_BLOCK = $100000;// download/play internet file stream in small blocks + BASS_STREAM_DECODE = $200000;// don't play the stream, only decode (BASS_ChannelGetData) + BASS_STREAM_STATUS = $800000;// give server status info (HTTP/ICY tags) in DOWNLOADPROC + + BASS_MUSIC_FLOAT = BASS_SAMPLE_FLOAT; + BASS_MUSIC_MONO = BASS_SAMPLE_MONO; + BASS_MUSIC_LOOP = BASS_SAMPLE_LOOP; + BASS_MUSIC_3D = BASS_SAMPLE_3D; + BASS_MUSIC_FX = BASS_SAMPLE_FX; + BASS_MUSIC_AUTOFREE = BASS_STREAM_AUTOFREE; + BASS_MUSIC_DECODE = BASS_STREAM_DECODE; + BASS_MUSIC_PRESCAN = BASS_STREAM_PRESCAN; // calculate playback length + BASS_MUSIC_CALCLEN = BASS_MUSIC_PRESCAN; + BASS_MUSIC_RAMP = $200; // normal ramping + BASS_MUSIC_RAMPS = $400; // sensitive ramping + BASS_MUSIC_SURROUND = $800; // surround sound + BASS_MUSIC_SURROUND2 = $1000; // surround sound (mode 2) + BASS_MUSIC_FT2MOD = $2000; // play .MOD as FastTracker 2 does + BASS_MUSIC_PT1MOD = $4000; // play .MOD as ProTracker 1 does + BASS_MUSIC_NONINTER = $10000; // non-interpolated sample mixing + BASS_MUSIC_SINCINTER = $800000; // sinc interpolated sample mixing + BASS_MUSIC_POSRESET = $8000; // stop all notes when moving position + BASS_MUSIC_POSRESETEX = $400000; // stop all notes and reset bmp/etc when moving position + BASS_MUSIC_STOPBACK = $80000; // stop the music on a backwards jump effect + BASS_MUSIC_NOSAMPLE = $100000; // don't load the samples + + // Speaker assignment flags + BASS_SPEAKER_FRONT = $1000000; // front speakers + BASS_SPEAKER_REAR = $2000000; // rear/side speakers + BASS_SPEAKER_CENLFE = $3000000; // center & LFE speakers (5.1) + BASS_SPEAKER_REAR2 = $4000000; // rear center speakers (7.1) + BASS_SPEAKER_LEFT = $10000000; // modifier: left + BASS_SPEAKER_RIGHT = $20000000; // modifier: right + BASS_SPEAKER_FRONTLEFT = BASS_SPEAKER_FRONT or BASS_SPEAKER_LEFT; + BASS_SPEAKER_FRONTRIGHT = BASS_SPEAKER_FRONT or BASS_SPEAKER_RIGHT; + BASS_SPEAKER_REARLEFT = BASS_SPEAKER_REAR or BASS_SPEAKER_LEFT; + BASS_SPEAKER_REARRIGHT = BASS_SPEAKER_REAR or BASS_SPEAKER_RIGHT; + BASS_SPEAKER_CENTER = BASS_SPEAKER_CENLFE or BASS_SPEAKER_LEFT; + BASS_SPEAKER_LFE = BASS_SPEAKER_CENLFE or BASS_SPEAKER_RIGHT; + BASS_SPEAKER_REAR2LEFT = BASS_SPEAKER_REAR2 or BASS_SPEAKER_LEFT; + BASS_SPEAKER_REAR2RIGHT = BASS_SPEAKER_REAR2 or BASS_SPEAKER_RIGHT; + + BASS_UNICODE = $80000000; + + BASS_RECORD_PAUSE = $8000; // start recording paused + + // DX7 voice allocation & management flags + BASS_VAM_HARDWARE = 1; + BASS_VAM_SOFTWARE = 2; + BASS_VAM_TERM_TIME = 4; + BASS_VAM_TERM_DIST = 8; + BASS_VAM_TERM_PRIO = 16; + + // BASS_CHANNELINFO types + BASS_CTYPE_SAMPLE = 1; + BASS_CTYPE_RECORD = 2; + BASS_CTYPE_STREAM = $10000; + BASS_CTYPE_STREAM_OGG = $10002; + BASS_CTYPE_STREAM_MP1 = $10003; + BASS_CTYPE_STREAM_MP2 = $10004; + BASS_CTYPE_STREAM_MP3 = $10005; + BASS_CTYPE_STREAM_AIFF = $10006; + BASS_CTYPE_STREAM_WAV = $40000; // WAVE flag, LOWORD=codec + BASS_CTYPE_STREAM_WAV_PCM = $50001; + BASS_CTYPE_STREAM_WAV_FLOAT = $50003; + BASS_CTYPE_MUSIC_MOD = $20000; + BASS_CTYPE_MUSIC_MTM = $20001; + BASS_CTYPE_MUSIC_S3M = $20002; + BASS_CTYPE_MUSIC_XM = $20003; + BASS_CTYPE_MUSIC_IT = $20004; + BASS_CTYPE_MUSIC_MO3 = $00100; // MO3 flag + + // 3D channel modes + BASS_3DMODE_NORMAL = 0; // normal 3D processing + BASS_3DMODE_RELATIVE = 1; // position is relative to the listener + BASS_3DMODE_OFF = 2; // no 3D processing + + // software 3D mixing algorithms (used with BASS_CONFIG_3DALGORITHM) + BASS_3DALG_DEFAULT = 0; + BASS_3DALG_OFF = 1; + BASS_3DALG_FULL = 2; + BASS_3DALG_LIGHT = 3; + + // EAX environments, use with BASS_SetEAXParameters + EAX_ENVIRONMENT_GENERIC = 0; + EAX_ENVIRONMENT_PADDEDCELL = 1; + EAX_ENVIRONMENT_ROOM = 2; + EAX_ENVIRONMENT_BATHROOM = 3; + EAX_ENVIRONMENT_LIVINGROOM = 4; + EAX_ENVIRONMENT_STONEROOM = 5; + EAX_ENVIRONMENT_AUDITORIUM = 6; + EAX_ENVIRONMENT_CONCERTHALL = 7; + EAX_ENVIRONMENT_CAVE = 8; + EAX_ENVIRONMENT_ARENA = 9; + EAX_ENVIRONMENT_HANGAR = 10; + EAX_ENVIRONMENT_CARPETEDHALLWAY = 11; + EAX_ENVIRONMENT_HALLWAY = 12; + EAX_ENVIRONMENT_STONECORRIDOR = 13; + EAX_ENVIRONMENT_ALLEY = 14; + EAX_ENVIRONMENT_FOREST = 15; + EAX_ENVIRONMENT_CITY = 16; + EAX_ENVIRONMENT_MOUNTAINS = 17; + EAX_ENVIRONMENT_QUARRY = 18; + EAX_ENVIRONMENT_PLAIN = 19; + EAX_ENVIRONMENT_PARKINGLOT = 20; + EAX_ENVIRONMENT_SEWERPIPE = 21; + EAX_ENVIRONMENT_UNDERWATER = 22; + EAX_ENVIRONMENT_DRUGGED = 23; + EAX_ENVIRONMENT_DIZZY = 24; + EAX_ENVIRONMENT_PSYCHOTIC = 25; + // total number of environments + EAX_ENVIRONMENT_COUNT = 26; + + BASS_STREAMPROC_END = $80000000; // end of user stream flag + + + // BASS_StreamCreateFileUser file systems + STREAMFILE_NOBUFFER = 0; + STREAMFILE_BUFFER = 1; + STREAMFILE_BUFFERPUSH = 2; + + // BASS_StreamPutFileData options + BASS_FILEDATA_END = 0; // end & close the file + + // BASS_StreamGetFilePosition modes + BASS_FILEPOS_CURRENT = 0; + BASS_FILEPOS_DECODE = BASS_FILEPOS_CURRENT; + BASS_FILEPOS_DOWNLOAD = 1; + BASS_FILEPOS_END = 2; + BASS_FILEPOS_START = 3; + BASS_FILEPOS_CONNECTED = 4; + BASS_FILEPOS_BUFFER = 5; + + // BASS_ChannelSetSync types + BASS_SYNC_POS = 0; + BASS_SYNC_END = 2; + BASS_SYNC_META = 4; + BASS_SYNC_SLIDE = 5; + BASS_SYNC_STALL = 6; + BASS_SYNC_DOWNLOAD = 7; + BASS_SYNC_FREE = 8; + BASS_SYNC_SETPOS = 11; + BASS_SYNC_MUSICPOS = 10; + BASS_SYNC_MUSICINST = 1; + BASS_SYNC_MUSICFX = 3; + BASS_SYNC_OGG_CHANGE = 12; + BASS_SYNC_MIXTIME = $40000000; // FLAG: sync at mixtime, else at playtime + BASS_SYNC_ONETIME = $80000000; // FLAG: sync only once, else continuously + + // BASS_ChannelIsActive return values + BASS_ACTIVE_STOPPED = 0; + BASS_ACTIVE_PLAYING = 1; + BASS_ACTIVE_STALLED = 2; + BASS_ACTIVE_PAUSED = 3; + + // Channel attributes + BASS_ATTRIB_FREQ = 1; + BASS_ATTRIB_VOL = 2; + BASS_ATTRIB_PAN = 3; + BASS_ATTRIB_EAXMIX = 4; + BASS_ATTRIB_NOBUFFER = 5; + BASS_ATTRIB_MUSIC_AMPLIFY = $100; + BASS_ATTRIB_MUSIC_PANSEP = $101; + BASS_ATTRIB_MUSIC_PSCALER = $102; + BASS_ATTRIB_MUSIC_BPM = $103; + BASS_ATTRIB_MUSIC_SPEED = $104; + BASS_ATTRIB_MUSIC_VOL_GLOBAL = $105; + BASS_ATTRIB_MUSIC_VOL_CHAN = $200; // + channel # + BASS_ATTRIB_MUSIC_VOL_INST = $300; // + instrument # + + // BASS_ChannelGetData flags + BASS_DATA_AVAILABLE = 0; // query how much data is buffered + BASS_DATA_FLOAT = $40000000; // flag: return floating-point sample data + BASS_DATA_FFT256 = $80000000; // 256 sample FFT + BASS_DATA_FFT512 = $80000001; // 512 FFT + BASS_DATA_FFT1024 = $80000002; // 1024 FFT + BASS_DATA_FFT2048 = $80000003; // 2048 FFT + BASS_DATA_FFT4096 = $80000004; // 4096 FFT + BASS_DATA_FFT8192 = $80000005; // 8192 FFT + BASS_DATA_FFT16384 = $80000006; // 16384 FFT + BASS_DATA_FFT_INDIVIDUAL = $10; // FFT flag: FFT for each channel, else all combined + BASS_DATA_FFT_NOWINDOW = $20; // FFT flag: no Hanning window + BASS_DATA_FFT_REMOVEDC = $40; // FFT flag: pre-remove DC bias + + // BASS_ChannelGetTags types : what's returned + BASS_TAG_ID3 = 0; // ID3v1 tags : TAG_ID3 structure + BASS_TAG_ID3V2 = 1; // ID3v2 tags : variable length block + BASS_TAG_OGG = 2; // OGG comments : series of null-terminated UTF-8 strings + BASS_TAG_HTTP = 3; // HTTP headers : series of null-terminated ANSI strings + BASS_TAG_ICY = 4; // ICY headers : series of null-terminated ANSI strings + BASS_TAG_META = 5; // ICY metadata : ANSI string + BASS_TAG_APE = 6; // APEv2 tags : series of null-terminated UTF-8 strings + BASS_TAG_VENDOR = 9; // OGG encoder : UTF-8 string + BASS_TAG_LYRICS3 = 10; // Lyric3v2 tag : ASCII string + BASS_TAG_RIFF_INFO = $100; // RIFF "INFO" tags : series of null-terminated ANSI strings + BASS_TAG_RIFF_BEXT = $101; // RIFF/BWF "bext" tags : TAG_BEXT structure + BASS_TAG_RIFF_CART = $102; // RIFF/BWF "cart" tags : TAG_CART structure + BASS_TAG_RIFF_DISP = $103; // RIFF "DISP" text tag : ANSI string + BASS_TAG_APE_BINARY = $1000; // + index #, binary APEv2 tag : TAG_APE_BINARY structure + BASS_TAG_MUSIC_NAME = $10000; // MOD music name : ANSI string + BASS_TAG_MUSIC_MESSAGE = $10001; // MOD message : ANSI string + BASS_TAG_MUSIC_ORDERS = $10002; // MOD order list : BYTE array of pattern numbers + BASS_TAG_MUSIC_INST = $10100; // + instrument #, MOD instrument name : ANSI string + BASS_TAG_MUSIC_SAMPLE = $10300; // + sample #, MOD sample name : ANSI string + + // BASS_ChannelGetLength/GetPosition/SetPosition modes + BASS_POS_BYTE = 0; // byte position + BASS_POS_MUSIC_ORDER = 1; // order.row position, MAKELONG(order,row) + BASS_POS_DECODE = $10000000; // flag: get the decoding (not playing) position + BASS_POS_DECODETO = $20000000; // flag: decode to the position instead of seeking + + // BASS_RecordSetInput flags + BASS_INPUT_OFF = $10000; + BASS_INPUT_ON = $20000; + + BASS_INPUT_TYPE_MASK = $FF000000; + BASS_INPUT_TYPE_UNDEF = $00000000; + BASS_INPUT_TYPE_DIGITAL = $01000000; + BASS_INPUT_TYPE_LINE = $02000000; + BASS_INPUT_TYPE_MIC = $03000000; + BASS_INPUT_TYPE_SYNTH = $04000000; + BASS_INPUT_TYPE_CD = $05000000; + BASS_INPUT_TYPE_PHONE = $06000000; + BASS_INPUT_TYPE_SPEAKER = $07000000; + BASS_INPUT_TYPE_WAVE = $08000000; + BASS_INPUT_TYPE_AUX = $09000000; + BASS_INPUT_TYPE_ANALOG = $0A000000; + + BASS_FX_DX8_CHORUS = 0; + BASS_FX_DX8_COMPRESSOR = 1; + BASS_FX_DX8_DISTORTION = 2; + BASS_FX_DX8_ECHO = 3; + BASS_FX_DX8_FLANGER = 4; + BASS_FX_DX8_GARGLE = 5; + BASS_FX_DX8_I3DL2REVERB = 6; + BASS_FX_DX8_PARAMEQ = 7; + BASS_FX_DX8_REVERB = 8; + + BASS_DX8_PHASE_NEG_180 = 0; + BASS_DX8_PHASE_NEG_90 = 1; + BASS_DX8_PHASE_ZERO = 2; + BASS_DX8_PHASE_90 = 3; + BASS_DX8_PHASE_180 = 4; + +type + DWORD = cardinal; + BOOL = LongBool; + FLOAT = Single; + QWORD = int64; // 64-bit (replace "int64" with "comp" if using Delphi 3) + + HMUSIC = DWORD; // MOD music handle + HSAMPLE = DWORD; // sample handle + HCHANNEL = DWORD; // playing sample's channel handle + HSTREAM = DWORD; // sample stream handle + HRECORD = DWORD; // recording handle + HSYNC = DWORD; // synchronizer handle + HDSP = DWORD; // DSP handle + HFX = DWORD; // DX8 effect handle + HPLUGIN = DWORD; // Plugin handle + + // Device info structure + BASS_DEVICEINFO = record + name: PAnsiChar; // description + driver: PAnsiChar; // driver + flags: DWORD; + end; + + BASS_INFO = record + flags: DWORD; // device capabilities (DSCAPS_xxx flags) + hwsize: DWORD; // size of total device hardware memory + hwfree: DWORD; // size of free device hardware memory + freesam: DWORD; // number of free sample slots in the hardware + free3d: DWORD; // number of free 3D sample slots in the hardware + minrate: DWORD; // min sample rate supported by the hardware + maxrate: DWORD; // max sample rate supported by the hardware + eax: BOOL; // device supports EAX? (always FALSE if BASS_DEVICE_3D was not used) + minbuf: DWORD; // recommended minimum buffer length in ms (requires BASS_DEVICE_LATENCY) + dsver: DWORD; // DirectSound version + latency: DWORD; // delay (in ms) before start of playback (requires BASS_DEVICE_LATENCY) + initflags: DWORD; // BASS_Init "flags" parameter + speakers: DWORD; // number of speakers available + freq: DWORD; // current output rate (Windows Vista and OSX only) + end; + + // Recording device info structure + BASS_RECORDINFO = record + flags: DWORD; // device capabilities (DSCCAPS_xxx flags) + formats: DWORD; // supported standard formats (WAVE_FORMAT_xxx flags) + inputs: DWORD; // number of inputs + singlein: BOOL; // only 1 input can be set at a time + freq: DWORD; // current input rate (Windows Vista and OSX only) + end; + + // Sample info structure + BASS_SAMPLE = record + freq: DWORD; // default playback rate + volume: FLOAT; // default volume (0-100) + pan: FLOAT; // default pan (-100=left, 0=middle, 100=right) + flags: DWORD; // BASS_SAMPLE_xxx flags + length: DWORD; // length (in samples, not bytes) + max: DWORD; // maximum simultaneous playbacks + origres: DWORD; // original resolution + chans: DWORD; // number of channels + mingap: DWORD; // minimum gap (ms) between creating channels + mode3d: DWORD; // BASS_3DMODE_xxx mode + mindist: FLOAT; // minimum distance + maxdist: FLOAT; // maximum distance + iangle: DWORD; // angle of inside projection cone + oangle: DWORD; // angle of outside projection cone + outvol: FLOAT; // delta-volume outside the projection cone + vam: DWORD; // voice allocation/management flags (BASS_VAM_xxx) + priority: DWORD; // priority (0=lowest, $ffffffff=highest) + end; + + // Channel info structure + BASS_CHANNELINFO = record + freq: DWORD; // default playback rate + chans: DWORD; // channels + flags: DWORD; // BASS_SAMPLE/STREAM/MUSIC/SPEAKER flags + ctype: DWORD; // type of channel + origres: DWORD; // original resolution + plugin: HPLUGIN; // plugin + sample: HSAMPLE; // sample + filename: PAnsiChar; // filename + end; + + BASS_PLUGINFORM = record + ctype: DWORD; // channel type + name: PAnsiChar; // format description + exts: PAnsiChar; // file extension filter (*.ext1;*.ext2;etc...) + end; + PBASS_PLUGINFORMS = ^TBASS_PLUGINFORMS; + TBASS_PLUGINFORMS = array[0..maxInt div sizeOf(BASS_PLUGINFORM) - 1] of BASS_PLUGINFORM; + + BASS_PLUGININFO = record + version: DWORD; // version (same form as BASS_GetVersion) + formatc: DWORD; // number of formats + formats: PBASS_PLUGINFORMS; // the array of formats + end; + PBASS_PLUGININFO = ^BASS_PLUGININFO; + + // 3D vector (for 3D positions/velocities/orientations) + BASS_3DVECTOR = record + x: FLOAT; // +=right, -=left + y: FLOAT; // +=up, -=down + z: FLOAT; // +=front, -=behind + end; + + // User file stream callback functions + FILECLOSEPROC = procedure(user: Pointer); stdcall; + FILELENPROC = function(user: Pointer): QWORD; stdcall; + FILEREADPROC = function(buffer: Pointer; length: DWORD; user: Pointer): DWORD; stdcall; + FILESEEKPROC = function(offset: QWORD; user: Pointer): BOOL; stdcall; + + BASS_FILEPROCS = record + close: FILECLOSEPROC; + length: FILELENPROC; + read: FILEREADPROC; + seek: FILESEEKPROC; + end; + + // ID3v1 tag structure + TAG_ID3 = record + id: Array[0..2] of Char; + title: Array[0..29] of Char; + artist: Array[0..29] of Char; + album: Array[0..29] of Char; + year: Array[0..3] of Char; + comment: Array[0..29] of Char; + genre: Byte; + end; + + // Binary APEv2 tag structure + TAG_APE_BINARY = record + key: PAnsiChar; + data: PAnsiChar; + length: DWORD; + end; + + // BWF Broadcast Audio Extension tag structure + TAG_BEXT = record + Description: Array[0..255] of Char; // description + Originator: Array[0..31] of Char; // name of the originator + OriginatorReference: Array[0..31] of Char; // reference of the originator + OriginationDate: Array[0..9] of Char; // date of creation (yyyy-mm-dd) + OriginationTime: Array[0..7] of Char; // time of creation (hh-mm-ss) + TimeReference: QWORD; // first sample count since midnight (little-endian) + Version: Word; // BWF version (little-endian) + UMID: Array[0..63] of Byte; // SMPTE UMID + Reserved: Array[0..189] of Byte; + CodingHistory: Array of Char; // history + end; + + BASS_DX8_CHORUS = record + fWetDryMix: FLOAT; + fDepth: FLOAT; + fFeedback: FLOAT; + fFrequency: FLOAT; + lWaveform: DWORD; // 0=triangle, 1=sine + fDelay: FLOAT; + lPhase: DWORD; // BASS_DX8_PHASE_xxx + end; + + BASS_DX8_COMPRESSOR = record + fGain: FLOAT; + fAttack: FLOAT; + fRelease: FLOAT; + fThreshold: FLOAT; + fRatio: FLOAT; + fPredelay: FLOAT; + end; + + BASS_DX8_DISTORTION = record + fGain: FLOAT; + fEdge: FLOAT; + fPostEQCenterFrequency: FLOAT; + fPostEQBandwidth: FLOAT; + fPreLowpassCutoff: FLOAT; + end; + + BASS_DX8_ECHO = record + fWetDryMix: FLOAT; + fFeedback: FLOAT; + fLeftDelay: FLOAT; + fRightDelay: FLOAT; + lPanDelay: BOOL; + end; + + BASS_DX8_FLANGER = record + fWetDryMix: FLOAT; + fDepth: FLOAT; + fFeedback: FLOAT; + fFrequency: FLOAT; + lWaveform: DWORD; // 0=triangle, 1=sine + fDelay: FLOAT; + lPhase: DWORD; // BASS_DX8_PHASE_xxx + end; + + BASS_DX8_GARGLE = record + dwRateHz: DWORD; // Rate of modulation in hz + dwWaveShape: DWORD; // 0=triangle, 1=square + end; + + BASS_DX8_I3DL2REVERB = record + lRoom: Longint; // [-10000, 0] default: -1000 mB + lRoomHF: Longint; // [-10000, 0] default: 0 mB + flRoomRolloffFactor: FLOAT; // [0.0, 10.0] default: 0.0 + flDecayTime: FLOAT; // [0.1, 20.0] default: 1.49s + flDecayHFRatio: FLOAT; // [0.1, 2.0] default: 0.83 + lReflections: Longint; // [-10000, 1000] default: -2602 mB + flReflectionsDelay: FLOAT; // [0.0, 0.3] default: 0.007 s + lReverb: Longint; // [-10000, 2000] default: 200 mB + flReverbDelay: FLOAT; // [0.0, 0.1] default: 0.011 s + flDiffusion: FLOAT; // [0.0, 100.0] default: 100.0 % + flDensity: FLOAT; // [0.0, 100.0] default: 100.0 % + flHFReference: FLOAT; // [20.0, 20000.0] default: 5000.0 Hz + end; + + BASS_DX8_PARAMEQ = record + fCenter: FLOAT; + fBandwidth: FLOAT; + fGain: FLOAT; + end; + + BASS_DX8_REVERB = record + fInGain: FLOAT; // [-96.0,0.0] default: 0.0 dB + fReverbMix: FLOAT; // [-96.0,0.0] default: 0.0 db + fReverbTime: FLOAT; // [0.001,3000.0] default: 1000.0 ms + fHighFreqRTRatio: FLOAT; // [0.001,0.999] default: 0.001 + end; + + // callback function types + STREAMPROC = function(handle: HSTREAM; buffer: Pointer; length: DWORD; user: Pointer): DWORD; stdcall; + { + User stream callback function. NOTE: A stream function should obviously be as + quick as possible, other streams (and MOD musics) can't be mixed until + it's finished. + handle : The stream that needs writing + buffer : Buffer to write the samples in + length : Number of bytes to write + user : The 'user' parameter value given when calling BASS_StreamCreate + RETURN : Number of bytes written. Set the BASS_STREAMPROC_END flag to end + the stream. + } + +const + // special STREAMPROCs + STREAMPROC_DUMMY : STREAMPROC = STREAMPROC(0); // "dummy" stream + STREAMPROC_PUSH : STREAMPROC = STREAMPROC(-1); // push stream + +type + + DOWNLOADPROC = procedure(buffer: Pointer; length: DWORD; user: Pointer); stdcall; + { + Internet stream download callback function. + buffer : Buffer containing the downloaded data... NULL=end of download + length : Number of bytes in the buffer + user : The 'user' parameter value given when calling BASS_StreamCreateURL + } + + SYNCPROC = procedure(handle: HSYNC; channel, data: DWORD; user: Pointer); stdcall; + { + Sync callback function. NOTE: a sync callback function should be very + quick as other syncs cannot be processed until it has finished. If the + sync is a "mixtime" sync, then other streams and MOD musics can not be + mixed until it's finished either. + handle : The sync that has occured + channel: Channel that the sync occured in + data : Additional data associated with the sync's occurance + user : The 'user' parameter given when calling BASS_ChannelSetSync + } + + DSPPROC = procedure(handle: HDSP; channel: DWORD; buffer: Pointer; length: DWORD; user: Pointer); stdcall; + { + DSP callback function. NOTE: A DSP function should obviously be as quick + as possible... other DSP functions, streams and MOD musics can not be + processed until it's finished. + handle : The DSP handle + channel: Channel that the DSP is being applied to + buffer : Buffer to apply the DSP to + length : Number of bytes in the buffer + user : The 'user' parameter given when calling BASS_ChannelSetDSP + } + + RECORDPROC = function(handle: HRECORD; buffer: Pointer; length: DWORD; user: Pointer): BOOL; stdcall; + { + Recording callback function. + handle : The recording handle + buffer : Buffer containing the recorded sample data + length : Number of bytes + user : The 'user' parameter value given when calling BASS_RecordStart + RETURN : TRUE = continue recording, FALSE = stop + } + + +// Vars that will hold our dynamically loaded functions... + + var + BASS_SetConfig:function(option, value: DWORD): BOOL; stdcall; + BASS_GetConfig:function(option: DWORD): DWORD; stdcall; + BASS_SetConfigPtr:function(option: DWORD; value: Pointer): BOOL; stdcall; + BASS_GetConfigPtr:function(option: DWORD): Pointer; stdcall; + BASS_GetVersion:function: DWORD; stdcall; + BASS_ErrorGetCode:function: Integer; stdcall; + BASS_GetDeviceInfo:function(device: DWORD; var info: BASS_DEVICEINFO): BOOL; stdcall; + BASS_Init:function(device: Integer; freq, flags: DWORD; win: HWND; clsid: PGUID): BOOL; stdcall; + BASS_SetDevice:function(device: DWORD): BOOL; stdcall; + BASS_GetDevice:function: DWORD; stdcall; + BASS_Free:function: BOOL; stdcall; + BASS_GetDSoundObject:function(obj: DWORD): Pointer; stdcall; + BASS_GetInfo:function(var info: BASS_INFO): BOOL; stdcall; + BASS_Update:function(length: DWORD): BOOL; stdcall; + BASS_GetCPU:function: FLOAT; stdcall; + BASS_Start:function: BOOL; stdcall; + BASS_Stop:function: BOOL; stdcall; + BASS_Pause:function: BOOL; stdcall; + BASS_SetVolume:function(volume: FLOAT): BOOL; stdcall; + BASS_GetVolume:function: FLOAT; stdcall; + + //BASS_PluginLoad:function(filename: PAnsiChar; flags: DWORD): HPLUGIN; stdcall; + BASS_PluginLoad:function(filename: PChar; flags: DWORD): HPLUGIN; stdcall; + BASS_PluginFree:function(handle: HPLUGIN): BOOL; stdcall; + BASS_PluginGetInfo:function(handle: HPLUGIN): PBASS_PLUGININFO; stdcall; + + BASS_Set3DFactors:function(distf, rollf, doppf: FLOAT): BOOL; stdcall; + BASS_Get3DFactors:function(var distf, rollf, doppf: FLOAT): BOOL; stdcall; + BASS_Set3DPosition:function(var pos, vel, front, top: BASS_3DVECTOR): BOOL; stdcall; + BASS_Get3DPosition:function(var pos, vel, front, top: BASS_3DVECTOR): BOOL; stdcall; + BASS_Apply3D:procedure; stdcall; + BASS_SetEAXParameters:function(env: Integer; vol, decay, damp: FLOAT): BOOL; stdcall; + BASS_GetEAXParameters:function(var env: DWORD; var vol, decay, damp: FLOAT): BOOL; stdcall; + + BASS_MusicLoad:function(mem: BOOL; f: Pointer; offset: QWORD; length, flags, freq: DWORD): HMUSIC; stdcall; + BASS_MusicFree:function(handle: HMUSIC): BOOL; stdcall; + + BASS_SampleLoad:function(mem: BOOL; f: Pointer; offset: QWORD; length, max, flags: DWORD): HSAMPLE; stdcall; + BASS_SampleCreate:function(length, freq, chans, max, flags: DWORD): HSAMPLE; stdcall; + BASS_SampleFree:function(handle: HSAMPLE): BOOL; stdcall; + BASS_SampleSetData:function(handle: HSAMPLE; buffer: Pointer): BOOL; stdcall; + BASS_SampleGetData:function(handle: HSAMPLE; buffer: Pointer): BOOL; stdcall; + BASS_SampleGetInfo:function(handle: HSAMPLE; var info: BASS_SAMPLE): BOOL; stdcall; + BASS_SampleSetInfo:function(handle: HSAMPLE; var info: BASS_SAMPLE): BOOL; stdcall; + BASS_SampleGetChannel:function(handle: HSAMPLE; onlynew: BOOL): HCHANNEL; stdcall; + BASS_SampleGetChannels:function(handle: HSAMPLE; channels: Pointer): DWORD; stdcall; + BASS_SampleStop:function(handle: HSAMPLE): BOOL; stdcall; + + BASS_StreamCreate:function(freq, chans, flags: DWORD; proc: STREAMPROC; user: Pointer): HSTREAM; stdcall; + BASS_StreamCreateFile:function(mem: BOOL; f: Pointer; offset, length: QWORD; flags: DWORD): HSTREAM; stdcall; + BASS_StreamCreateURL:function(url: PAnsiChar; offset: DWORD; flags: DWORD; proc: DOWNLOADPROC; user: Pointer):HSTREAM; stdcall; + BASS_StreamCreateFileUser:function(system, flags: DWORD; var procs: BASS_FILEPROCS; user: Pointer): HSTREAM; stdcall; + BASS_StreamFree:function(handle: HSTREAM): BOOL; stdcall; + BASS_StreamGetFilePosition:function(handle: HSTREAM; mode: DWORD): QWORD; stdcall; + BASS_StreamPutData:function(handle: HSTREAM; buffer: Pointer; length: DWORD): DWORD; stdcall; + BASS_StreamPutFileData:function(handle: HSTREAM; buffer: Pointer; length: DWORD): DWORD; stdcall; + + BASS_RecordGetDeviceInfo:function(device: DWORD; var info: BASS_DEVICEINFO): BOOL; stdcall; + BASS_RecordInit:function(device: Integer): BOOL; stdcall; + BASS_RecordSetDevice:function(device: DWORD): BOOL; stdcall; + BASS_RecordGetDevice:function: DWORD; stdcall; + BASS_RecordFree:function: BOOL; stdcall; + BASS_RecordGetInfo:function(var info: BASS_RECORDINFO): BOOL; stdcall; + BASS_RecordGetInputName:function(input: Integer): PAnsiChar; stdcall; + BASS_RecordSetInput:function(input: Integer; flags: DWORD; volume: FLOAT): BOOL; stdcall; + BASS_RecordGetInput:function(input: Integer; var volume: FLOAT): DWORD; stdcall; + BASS_RecordStart:function(freq, chans, flags: DWORD; proc: RECORDPROC; user: Pointer): HRECORD; stdcall; + + BASS_ChannelBytes2Seconds:function(handle: DWORD; pos: QWORD): Double; stdcall; + BASS_ChannelSeconds2Bytes:function(handle: DWORD; pos: Double): QWORD; stdcall; + BASS_ChannelGetDevice:function(handle: DWORD): DWORD; stdcall; + BASS_ChannelSetDevice:function(handle, device: DWORD): BOOL; stdcall; + BASS_ChannelIsActive:function(handle: DWORD): DWORD; stdcall; + BASS_ChannelGetInfo:function(handle: DWORD; var info: BASS_CHANNELINFO):BOOL;stdcall; + BASS_ChannelGetTags:function(handle: HSTREAM; tags: DWORD): PAnsiChar; stdcall; + BASS_ChannelFlags:function(handle, flags, mask: DWORD): DWORD; stdcall; + BASS_ChannelUpdate:function(handle, length: DWORD): BOOL; stdcall; + BASS_ChannelLock:function(handle: DWORD; lock: BOOL): BOOL; stdcall; + BASS_ChannelPlay:function(handle: DWORD; restart: BOOL): BOOL; stdcall; + BASS_ChannelStop:function(handle: DWORD): BOOL; stdcall; + BASS_ChannelPause:function(handle: DWORD): BOOL; stdcall; + BASS_ChannelSetAttribute:function(handle, attrib: DWORD; value: FLOAT): BOOL; stdcall; + BASS_ChannelGetAttribute:function(handle, attrib: DWORD; var value: FLOAT): BOOL; stdcall; + BASS_ChannelSlideAttribute:function(handle, attrib: DWORD; value: FLOAT; time: DWORD): BOOL; stdcall; + BASS_ChannelIsSliding:function(handle, attrib: DWORD): BOOL; stdcall; + BASS_ChannelSet3DAttributes:function(handle: DWORD; mode: Integer; min, max: FLOAT; iangle, oangle, outvol: Integer): BOOL; stdcall; + BASS_ChannelGet3DAttributes:function(handle: DWORD; var mode: DWORD; var min, max: FLOAT; var iangle, oangle, outvol: DWORD): BOOL; stdcall; + BASS_ChannelSet3DPosition:function(handle: DWORD; var pos, orient, vel: BASS_3DVECTOR): BOOL; stdcall; + BASS_ChannelGet3DPosition:function(handle: DWORD; var pos, orient, vel: BASS_3DVECTOR): BOOL; stdcall; + BASS_ChannelGetLength:function(handle, mode: DWORD): QWORD; stdcall; + BASS_ChannelSetPosition:function(handle: DWORD; pos: QWORD; mode: DWORD): BOOL; stdcall; + BASS_ChannelGetPosition:function(handle, mode: DWORD): QWORD; stdcall; + BASS_ChannelGetLevel:function(handle: DWORD): DWORD; stdcall; + BASS_ChannelGetData:function(handle: DWORD; buffer: Pointer; length: DWORD): DWORD; stdcall; + BASS_ChannelSetSync:function(handle: DWORD; type_: DWORD; param: QWORD; proc: SYNCPROC; user: Pointer): HSYNC; stdcall; + BASS_ChannelRemoveSync:function(handle: DWORD; sync: HSYNC): BOOL; stdcall; + BASS_ChannelSetDSP:function(handle: DWORD; proc: DSPPROC; user: Pointer; priority: Integer): HDSP; stdcall; + BASS_ChannelRemoveDSP:function(handle: DWORD; dsp: HDSP): BOOL; stdcall; + BASS_ChannelSetLink:function(handle, chan: DWORD): BOOL; stdcall; + BASS_ChannelRemoveLink:function(handle, chan: DWORD): BOOL; stdcall; + BASS_ChannelSetFX:function(handle, type_: DWORD; priority: Integer): HFX; stdcall; + BASS_ChannelRemoveFX:function(handle: DWORD; fx: HFX): BOOL; stdcall; + + BASS_FXSetParameters:function(handle: HFX; par: Pointer): BOOL; stdcall; + BASS_FXGetParameters:function(handle: HFX; par: Pointer): BOOL; stdcall; + BASS_FXReset:function(handle: HFX): BOOL; stdcall; + +{ok, now we need something that loads our DLL and gets rid of it as well...} + + var + BASS_Handle:Thandle=0; // this will hold our handle for the dll; it functions nicely as a mutli-dll prevention unit as well... + +Function Load_BASSDLL (const dllfilename : String) :boolean; +Procedure Unload_BASSDLL; +{ + This function frees the dynamically linked-in functions from memory...don't forget to call it once you're done ! + Best place to put this is probably the OnDestroy of your Main-Form; + suggested use in OnDestroy : + - Call BASS_Free to get rid of everything that's eating memory (automatically called, but just to be on the safe-side !), + - Then call this function. +} + + +function BASS_SPEAKER_N(n: DWORD): DWORD; +function BASS_SetEAXPreset(env: Integer): BOOL; +{ + This function is defined in the implementation part of this unit. + It is not part of BASS.DLL but an extra function which makes it easier + to set the predefined EAX environments. + env : a EAX_ENVIRONMENT_xxx constant +} + +implementation + +{ +var + oldmode : integer; +begin + if BASSCD_Handle <> 0 then // is it already there ? + result := true + else + begin {go & load the dll} + { oldmode := SetErrorMode($8001); + BASSCD_Handle := LoadLibrary(pchar(dllfilename)); // obtain the handle we want + SetErrorMode(oldmode); + if BASSCD_Handle <> 0 then + begin + {$IFDEF UNICODE} + + + +Function Load_BASSDLL (const dllfilename : String) :boolean; +const szBassDll = 'bass.dll' + #0; +var + oldmode:integer; +begin + if BASS_Handle <> 0 then + Result:= true {is it already there ?} + else + begin + (*go & load the dll*) + oldmode := SetErrorMode($8001); + {$IFDEF UNICODE} + BASS_Handle:= LoadLibraryW(PWideChar(dllfilename)); + {$ELSE} + BASS_Handle:= LoadLibrary(PChar(dllfilename)); + {$ENDIF} + SetErrorMode(oldmode); + if BASS_Handle <> 0 then + begin {now we tie the functions to the VARs from above} + @BASS_SetConfig:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SetConfig')); + @BASS_GetConfig:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetConfig')); + @BASS_SetConfigPtr:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SetConfigPtr')); + @BASS_GetConfigPtr:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetConfigPtr')); + @BASS_GetVersion:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetVersion')); + @BASS_ErrorGetCode:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ErrorGetCode')); + @BASS_GetDeviceInfo:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetDeviceInfo')); + @BASS_Init:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Init')); + @BASS_SetDevice:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SetDevice')); + @BASS_GetDevice:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetDevice')); + @BASS_Free:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Free')); + @BASS_GetDSoundObject:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetDSoundObject')); + @BASS_GetInfo:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetInfo')); + @BASS_Update:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Update')); + @BASS_GetCPU:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetCPU')); + @BASS_Start:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Start')); + @BASS_Stop:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Stop')); + @BASS_Pause:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Pause')); + @BASS_SetVolume:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SetVolume')); + @BASS_GetVolume:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetVolume')); + + @BASS_PluginLoad:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_PluginLoad')); + @BASS_PluginFree:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_PluginFree')); + @BASS_PluginGetInfo:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_PluginGetInfo')); + + @BASS_Set3DFactors:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Set3DFactors')); + @BASS_Get3DFactors:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Get3DFactors')); + @BASS_Set3DPosition:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Set3DPosition')); + @BASS_Get3DPosition:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Get3DPosition')); + @BASS_Apply3D:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_Apply3D')); + @BASS_SetEAXParameters:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SetEAXParameters')); + @BASS_GetEAXParameters:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_GetEAXParameters')); + + @BASS_MusicLoad:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_MusicLoad')); + @BASS_MusicFree:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_MusicFree')); + + @BASS_SampleLoad:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleLoad')); + @BASS_SampleCreate:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleCreate')); + @BASS_SampleFree:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleFree')); + @BASS_SampleSetData:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleSetData')); + @BASS_SampleGetData:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleGetData')); + @BASS_SampleGetInfo:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleGetInfo')); + @BASS_SampleSetInfo:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleSetInfo')); + @BASS_SampleGetChannel:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleGetChannel')); + @BASS_SampleGetChannels:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleGetChannels')); + @BASS_SampleStop:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_SampleStop')); + + @BASS_StreamCreate:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_StreamCreate')); + @BASS_StreamCreateFile:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_StreamCreateFile')); + @BASS_StreamCreateURL:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_StreamCreateURL')); + @BASS_StreamCreateFileUser:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_StreamCreateFileUser')); + @BASS_StreamFree:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_StreamFree')); + @BASS_StreamGetFilePosition:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_StreamGetFilePosition')); + @BASS_StreamPutData:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_StreamPutData')); + @BASS_StreamPutFileData:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_StreamPutFileData')); + + @BASS_RecordGetDeviceInfo:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordGetDeviceInfo')); + @BASS_RecordInit:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordInit')); + @BASS_RecordSetDevice:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordSetDevice')); + @BASS_RecordGetDevice:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordGetDevice')); + @BASS_RecordFree:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordFree')); + @BASS_RecordGetInfo:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordGetInfo')); + @BASS_RecordGetInputName:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordGetInputName')); + @BASS_RecordSetInput:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordSetInput')); + @BASS_RecordGetInput:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordGetInput')); + @BASS_RecordStart:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_RecordStart')); + + @BASS_ChannelBytes2Seconds:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelBytes2Seconds')); + @BASS_ChannelSeconds2Bytes:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSeconds2Bytes')); + @BASS_ChannelGetDevice:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGetDevice')); + @BASS_ChannelSetDevice:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSetDevice')); + @BASS_ChannelIsActive:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelIsActive')); + @BASS_ChannelGetInfo:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGetInfo')); + @BASS_ChannelGetTags:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGetTags')); + @BASS_ChannelFlags:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelFlags')); + @BASS_ChannelUpdate:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelUpdate')); + @BASS_ChannelLock:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelLock')); + @BASS_ChannelPlay:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelPlay')); + @BASS_ChannelStop:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelStop')); + @BASS_ChannelPause:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelPause')); + @BASS_ChannelSetAttribute:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSetAttribute')); + @BASS_ChannelGetAttribute:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGetAttribute')); + @BASS_ChannelSlideAttribute:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSlideAttribute')); + @BASS_ChannelIsSliding:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelIsSliding')); + @BASS_ChannelSet3DAttributes:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSet3DAttributes')); + @BASS_ChannelGet3DAttributes:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGet3DAttributes')); + @BASS_ChannelSet3DPosition:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSet3DPosition')); + @BASS_ChannelGet3DPosition:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGet3DPosition')); + @BASS_ChannelGetLength:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGetLength')); + @BASS_ChannelSetPosition:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSetPosition')); + @BASS_ChannelGetPosition:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGetPosition')); + @BASS_ChannelGetLevel:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGetLevel')); + @BASS_ChannelGetData:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelGetData')); + @BASS_ChannelSetSync:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSetSync')); + @BASS_ChannelRemoveSync:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelRemoveSync')); + @BASS_ChannelSetDSP:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSetDSP')); + @BASS_ChannelRemoveDSP:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelRemoveDSP')); + @BASS_ChannelSetLink:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSetLink')); + @BASS_ChannelRemoveLink:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelRemoveLink')); + @BASS_ChannelSetFX:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelSetFX')); + @BASS_ChannelRemoveFX:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_ChannelRemoveFX')); + + @BASS_FXSetParameters:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_FXSetParameters')); + @BASS_FXGetParameters:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_FXGetParameters')); + @BASS_FXReset:=GetProcAddress(BASS_Handle, PAnsiChar('BASS_FXReset')); + + {now check if everything is linked in correctly} + if (@BASS_SetConfig = nil) or + (@BASS_GetConfig = nil) or + (@BASS_SetConfigPtr = nil) or + (@BASS_GetConfigPtr = nil) or + (@BASS_GetVersion = nil) or + (@BASS_ErrorGetCode = nil) or + (@BASS_GetDeviceInfo = nil) or + (@BASS_Init = nil) or + (@BASS_SetDevice = nil) or + (@BASS_GetDevice = nil) or + (@BASS_Free = nil) or + (@BASS_GetDSoundObject = nil) or + (@BASS_GetInfo = nil) or + (@BASS_Update = nil) or + (@BASS_GetCPU = nil) or + (@BASS_Start = nil) or + (@BASS_Stop = nil) or + (@BASS_Pause = nil) or + (@BASS_SetVolume = nil) or + (@BASS_GetVolume = nil) or + + (@BASS_PluginLoad = nil) or + (@BASS_PluginFree = nil) or + (@BASS_PluginGetInfo = nil) or + + (@BASS_Set3DFactors = nil) or + (@BASS_Get3DFactors = nil) or + (@BASS_Set3DPosition = nil) or + (@BASS_Get3DPosition = nil) or + (@BASS_Apply3D = nil) or + (@BASS_SetEAXParameters = nil) or + (@BASS_GetEAXParameters = nil) or + + (@BASS_MusicLoad = nil) or + (@BASS_MusicFree = nil) or + + (@BASS_SampleLoad = nil) or + (@BASS_SampleCreate = nil) or + (@BASS_SampleFree = nil) or + (@BASS_SampleSetData = nil) or + (@BASS_SampleGetData = nil) or + (@BASS_SampleGetInfo = nil) or + (@BASS_SampleSetInfo = nil) or + (@BASS_SampleGetChannel = nil) or + (@BASS_SampleGetChannels = nil) or + (@BASS_SampleStop = nil) or + + (@BASS_StreamCreate = nil) or + (@BASS_StreamCreateFile = nil) or + (@BASS_StreamCreateURL = nil) or + (@BASS_StreamCreateFileUser = nil) or + (@BASS_StreamFree = nil) or + (@BASS_StreamGetFilePosition = nil) or + (@BASS_StreamPutData = nil) or + (@BASS_StreamPutFileData = nil) or + + (@BASS_RecordGetDeviceInfo = nil) or + (@BASS_RecordInit = nil) or + (@BASS_RecordSetDevice = nil) or + (@BASS_RecordGetDevice = nil) or + (@BASS_RecordFree = nil) or + (@BASS_RecordGetInfo = nil) or + (@BASS_RecordGetInputName = nil) or + (@BASS_RecordSetInput = nil) or + (@BASS_RecordGetInput = nil) or + (@BASS_RecordStart = nil) or + + (@BASS_ChannelBytes2Seconds = nil) or + (@BASS_ChannelSeconds2Bytes = nil) or + (@BASS_ChannelGetDevice = nil) or + (@BASS_ChannelSetDevice = nil) or + (@BASS_ChannelIsActive = nil) or + (@BASS_ChannelGetInfo = nil) or + (@BASS_ChannelGetTags = nil) or + (@BASS_ChannelFlags = nil) or + (@BASS_ChannelUpdate = nil) or + (@BASS_ChannelLock = nil) or + (@BASS_ChannelPlay = nil) or + (@BASS_ChannelStop = nil) or + (@BASS_ChannelPause = nil) or + (@BASS_ChannelSetAttribute = nil) or + (@BASS_ChannelGetAttribute = nil) or + (@BASS_ChannelSlideAttribute = nil) or + (@BASS_ChannelIsSliding = nil) or + (@BASS_ChannelSet3DAttributes = nil) or + (@BASS_ChannelGet3DAttributes = nil) or + (@BASS_ChannelSet3DPosition = nil) or + (@BASS_ChannelGet3DPosition = nil) or + (@BASS_ChannelGetLength = nil) or + (@BASS_ChannelSetPosition = nil) or + (@BASS_ChannelGetPosition = nil) or + (@BASS_ChannelGetLevel = nil) or + (@BASS_ChannelGetData = nil) or + (@BASS_ChannelSetSync = nil) or + (@BASS_ChannelRemoveSync = nil) or + (@BASS_ChannelSetDSP = nil) or + (@BASS_ChannelRemoveDSP = nil) or + (@BASS_ChannelSetLink = nil) or + (@BASS_ChannelRemoveLink = nil) or + (@BASS_ChannelSetFX = nil) or + (@BASS_ChannelRemoveFX = nil) or + + (@BASS_FXSetParameters = nil) or + (@BASS_FXGetParameters = nil) or + (@BASS_FXReset=nil) then + begin {if something went wrong during linking, free library & reset handle} + FreeLibrary(BASS_Handle); + BASS_Handle:=0; + end; + end; + Result:= (BASS_Handle <> 0); + end; +end; + +Procedure Unload_BASSDLL; +begin + if BASS_Handle <> 0 then + begin + BASS_Free; // make sure we release everything + FreeLibrary(BASS_Handle); + end; + BASS_Handle:=0; +end; + +function BASS_SPEAKER_N(n: DWORD): DWORD; +begin + Result := n shl 24; +end; + +function BASS_SetEAXPreset(env: Integer): BOOL; +begin + case (env) of + EAX_ENVIRONMENT_GENERIC: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_GENERIC, 0.5, 1.493, 0.5); + EAX_ENVIRONMENT_PADDEDCELL: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_PADDEDCELL, 0.25, 0.1, 0); + EAX_ENVIRONMENT_ROOM: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_ROOM, 0.417, 0.4, 0.666); + EAX_ENVIRONMENT_BATHROOM: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_BATHROOM, 0.653, 1.499, 0.166); + EAX_ENVIRONMENT_LIVINGROOM: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_LIVINGROOM, 0.208, 0.478, 0); + EAX_ENVIRONMENT_STONEROOM: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_STONEROOM, 0.5, 2.309, 0.888); + EAX_ENVIRONMENT_AUDITORIUM: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_AUDITORIUM, 0.403, 4.279, 0.5); + EAX_ENVIRONMENT_CONCERTHALL: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_CONCERTHALL, 0.5, 3.961, 0.5); + EAX_ENVIRONMENT_CAVE: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_CAVE, 0.5, 2.886, 1.304); + EAX_ENVIRONMENT_ARENA: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_ARENA, 0.361, 7.284, 0.332); + EAX_ENVIRONMENT_HANGAR: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_HANGAR, 0.5, 10.0, 0.3); + EAX_ENVIRONMENT_CARPETEDHALLWAY: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_CARPETEDHALLWAY, 0.153, 0.259, 2.0); + EAX_ENVIRONMENT_HALLWAY: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_HALLWAY, 0.361, 1.493, 0); + EAX_ENVIRONMENT_STONECORRIDOR: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_STONECORRIDOR, 0.444, 2.697, 0.638); + EAX_ENVIRONMENT_ALLEY: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_ALLEY, 0.25, 1.752, 0.776); + EAX_ENVIRONMENT_FOREST: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_FOREST, 0.111, 3.145, 0.472); + EAX_ENVIRONMENT_CITY: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_CITY, 0.111, 2.767, 0.224); + EAX_ENVIRONMENT_MOUNTAINS: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_MOUNTAINS, 0.194, 7.841, 0.472); + EAX_ENVIRONMENT_QUARRY: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_QUARRY, 1, 1.499, 0.5); + EAX_ENVIRONMENT_PLAIN: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_PLAIN, 0.097, 2.767, 0.224); + EAX_ENVIRONMENT_PARKINGLOT: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_PARKINGLOT, 0.208, 1.652, 1.5); + EAX_ENVIRONMENT_SEWERPIPE: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_SEWERPIPE, 0.652, 2.886, 0.25); + EAX_ENVIRONMENT_UNDERWATER: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_UNDERWATER, 1, 1.499, 0); + EAX_ENVIRONMENT_DRUGGED: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_DRUGGED, 0.875, 8.392, 1.388); + EAX_ENVIRONMENT_DIZZY: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_DIZZY, 0.139, 17.234, 0.666); + EAX_ENVIRONMENT_PSYCHOTIC: + Result := BASS_SetEAXParameters(EAX_ENVIRONMENT_PSYCHOTIC, 0.486, 7.563, 0.806); + else + Result := FALSE; + end; +end; + +end. + + diff --git a/SimpleXML.pas b/SimpleXML.pas new file mode 100644 index 0000000..56891bb --- /dev/null +++ b/SimpleXML.pas @@ -0,0 +1,4479 @@ +{************************************************************ + SimpleXML - Áèáëèîòåêà äëÿ ñèíòàêñè÷åñêîãî ðàçáîðà òåêñòîâ XML + è ïðåîáðàçîâàíèÿ â èåðàðõèþ XML-îáúåêòîâ. + È íàîáîðîò: ìîæíî ñôîðìèðîâàòü èåðàðõèþ XML-îáúåêòîâ, è + óæå èç íåå ïîëó÷èòü òåêñò XML. + Äîñòîéíàÿ çàìåíà äëÿ MSXML. Ïðè èñïîëüçîâàíèè Ansi-ñòðîê + ðàáîòàåò áûñòðåå è êóøàåò ìåíüøå ïàìÿòè. + + (ñ) Àâòîðñêèå ïðàâà 2002,2003 Ìèõàèë Âëàñîâ. + Áèáëèîòåêà áåñïëàòíàÿ è ìîæåò áûòü èñïîëüçîâàíà ïî ëþáîìó íàçíà÷åíèþ. + Ðàçðåøàåòñÿ âíåñåíèå ëþáûõ èçìåíåíèé è èñïîëüçîâàíèå èçìåíåííûõ + áèáëèîòåê áåç îãðàíè÷åíèé. + Åäèíñòâåííîå òðåáîâàíèå: Äàííûé òåêñò äîëæåí ïðèñóòñòâîâàòü + áåç èçìåíåíèé âî âñåõ ìîäèôèêàöèÿõ áèáëèîòåêè. + + Âñå ïîæåëàíèÿ ïðèâåòñòâóþ ïî àäðåñó misha@integro.ru + Òàê æå ðåêîìåíäóþ ïîñåòèòü ìîþ ñòðàíè÷êó: http://mv.rb.ru + Òàì Âû âñåãäà íàéäåòå ñàìóþ ïîñëåäíþþ âåðñèþ áèáëèîòåêè. + Æåëàþ ïðèÿòíîãî ïðîãðàììèðîâàíèÿ, Ìèõàèë Âëàñîâ. + + Òåêóùàÿ âåðñèÿ: 1.0.1 +*************************************************************} +unit SimpleXML; + +interface + +uses + Windows, SysUtils, Classes; + +const + BinXmlSignatureSize = Length('< binary-xml >'); + BinXmlSignature: String = '< binary-xml >'; + + BINXML_USE_WIDE_CHARS = 1; + BINXML_COMPRESSED = 2; + + XSTR_NULL = '{{null}}'; + + NODE_INVALID = $00000000; + NODE_ELEMENT = $00000001; + NODE_ATTRIBUTE = $00000002; + NODE_TEXT = $00000003; + NODE_CDATA_SECTION = $00000004; + NODE_ENTITY_REFERENCE = $00000005; + NODE_ENTITY = $00000006; + NODE_PROCESSING_INSTRUCTION = $00000007; + NODE_COMMENT = $00000008; + NODE_DOCUMENT = $00000009; + NODE_DOCUMENT_TYPE = $0000000A; + NODE_DOCUMENT_FRAGMENT = $0000000B; + NODE_NOTATION = $0000000C; + +type + // TXmlString - òèï ñòðîêîâûõ ïåðåìåííûõ, èñïîëüçóåìûõ â SimpleXML. + // Ìîæåò áûòü String èëè WideString. + + { $DEFINE XML_WIDE_CHARS} + + {$IFDEF XML_WIDE_CHARS} + PXmlChar = PWideChar; + TXmlChar = WideChar; + TXmlString = WideString; + {$ELSE} + PXmlChar = PChar; + TXmlChar = Char; + TXmlString = String; + {$ENDIF} + + IXmlDocument = interface; + IXmlElement = interface; + IXmlText = interface; + IXmlCDATASection = interface; + IXmlComment = interface; + IXmlProcessingInstruction = interface; + + + // IXmlBase - áàçîâûé èíòåðôåéñ äëÿ âñåõ èíòåðôåéñîâ SimpleXML. + IXmlBase = interface + // GetObject - âîçâðàùàåò ññûëêó íà îáúåêò, ðåàëèçóþùèé èíòåðôåéñ. + function GetObject: TObject; + end; + + // IXmlNameTable - òàáëèöà èìåí. Êàæäîìó èìåíè ñîïîñòàâëÿåòñÿ íåêèé + // óíèêàëüíûé ÷èñëîâîé èäåíòèôèêàòîð. Èñïîëüçóåòñÿ äëÿ õðàíåíèÿ + // íàçâàíèé òýãîâ è àòðèáóòîâ. + IXmlNameTable = interface(IXmlBase) + // GetID - âîçâðàùàåò ÷èñëîâîé èäåíòèôèêàòîð óêàçàííîé ñòðîêè. + function GetID(const aName: TXmlString): Integer; + // GetID - âîçâðàùàåò ñòðîêó, ñîîòâåòñòâóþùóþ óêàçàííîìó ÷èñëîâîìó + // èäåíòèôèêàòîðó. + function GetName(anID: Integer): TXmlString; + end; + + IXmlNode = interface; + + // IXmlNodeList - ñïèñîê óçëîâ. Ñïèñîê îðãàíèçîâàí â âèäå ìàññèâà. + // Äîñòóï ê ýëåìåíòàì ñïèñêà ïî èíäåêñó + IXmlNodeList = interface(IXmlBase) + // Get_Count - êîëè÷åñòâî óçëîâ â ñïèñêå + function Get_Count: Integer; + // Get_Item - ïîëó÷èòü óçåë ïî èíäåêñó + function Get_Item(anIndex: Integer): IXmlNode; + // Get_XML - âîçâðàùàåò ïðåäñòàâëåíèå ýëåìåíòîâ ñïèñêà â ôîðìàòå XML + function Get_XML: TXmlString; + + property Count: Integer read Get_Count; + property Item[anIndex: Integer]: IXmlNode read Get_Item; default; + property XML: TXmlString read Get_XML; + end; + + // IXmlNode - óçåë XML-äåðåâà + IXmlNode = interface(IXmlBase) + // Get_NameTable - òàáëèöà èìåí, èñïîëüçóåìàÿ äàííûì óçëîì + function Get_NameTable: IXmlNameTable; + // Get_NodeName - âîçâðàùàåò íàçâàíèå óçëà. Èíòåðïðåòàöèÿ íàçâàíèÿ óçëà + // çàâèñèò îò åãî òèïà + function Get_NodeName: TXmlString; + // Get_NodeNameID - âîçâðàùàåò êîä íàçâàíèÿ óçëà + function Get_NodeNameID: Integer; + // Get_NodeType - âîçâðàùàåò òèï óçëà + function Get_NodeType: Integer; + // Get_Text - âîçâðàùàåò òåêñò óçëà + function Get_Text: TXmlString; + // Set_Text - èçìåíÿåò òåêñò óçëà + procedure Set_Text(const aValue: TXmlString); + // Get_DataType - âîçàðàùàåò òèï äàííûõ óçëà â òåðìèíàõ âàðèàíòîâ + function Get_DataType: Integer; + // Get_TypedValue - âîçâðàùàåò + function Get_TypedValue: Variant; + // Set_TypedValue - èçìåíÿåò òåêñò óçëà íà òèïèçèðîâàííîå çíà÷åíèå + procedure Set_TypedValue(const aValue: Variant); + // Get_XML - âîçâðàùàåò ïðåäñòàâëåíèå óçëà è âñåõ âëîæåííûõ óçëîâ + // â ôîðìàòå XML. + function Get_XML: TXmlString; + + // CloneNode - ñîçäàåò òî÷íóþ êîïèþ äàííîãî óçëà + // Åñëè çàäàí ïðèçíàê aDeep, òî ñîçäàñòñÿ êîïèÿ + // âñåé âåòâè èåðàðõèè îò äàííîãî óçëà. + function CloneNode(aDeep: Boolean = True): IXmlNode; + + // Get_ParentNode - âîçâðàùàåò ðîäèòåëüñêèé óçåë + function Get_ParentNode: IXmlNode; + // Get_OwnerDocument - âîçâðàùàåò XML-äîêóìåíò, + // â êîòîðîì ðàñïîëîæåí äàííûé óçåë + function Get_OwnerDocument: IXmlDocument; + + // Get_ChildNodes - âîçâðàùàåò ñïèñîê äî÷åðíèõ óçëîâ + function Get_ChildNodes: IXmlNodeList; + // AppendChild - äîáàâëÿåò óêàçàííûé óçåë â êîíåö ñïèñêà äî÷åðíèõ óçëîâ + procedure AppendChild(const aChild: IXmlNode); + // InsertBefore - äîáàâëÿåò óêàçàííûé óçåë â óêàçàííîå ìåñòî ñïèñêà äî÷åðíèõ óçëîâ + procedure InsertBefore(const aChild, aBefore: IXmlNode); + // ReplaceChild - çàìåíÿåò óêàçàííûé óçåë äðóãèì óçëîì + procedure ReplaceChild(const aNewChild, anOldChild: IXmlNode); + // RemoveChild - óäàëÿåò óêàçàííûé óçåë èç ñïèñêà äî÷åðíèõ óçëîâ + procedure RemoveChild(const aChild: IXmlNode); + + // AppendElement - ñîçäàåò ýëåìåíò è äîáàâëÿåò åãî â êîíåö ñïèñêà + // â êîíåö ñïèñêà äî÷åðíèõ îáúåêòîâ + function AppendElement(aNameID: Integer): IXmlElement; overload; + function AppendElement(const aName: TxmlString): IXmlElement; overload; + + // AppendText - ñîçäàåò òåêñòîâûé óçåë è äîáàâëÿåò åãî + // â êîíåö ñïèñêà äî÷åðíèõ îáúåêòîâ + function AppendText(const aData: TXmlString): IXmlText; + + // AppendCDATA - ñîçäàåò ñåêöèþ CDATA è äîáàâëÿåò åå + // â êîíåö ñïèñêà äî÷åðíèõ îáúåêòîâ + function AppendCDATA(const aData: TXmlString): IXmlCDATASection; + + // AppendComment - ñîçäàåò êîììåíòàðèé è äîáàâëÿåò åãî + // â êîíåö ñïèñêà äî÷åðíèõ îáúåêòîâ + function AppendComment(const aData: TXmlString): IXmlComment; + + // AppendProcessingInstruction - ñîçäàåò èíñòðóêöèþ è äîáàâëÿåò å¸ + // â êîíåö ñïèñêà äî÷åðíèõ îáúåêòîâ + function AppendProcessingInstruction(aTargetID: Integer; + const aData: TXmlString): IXmlProcessingInstruction; overload; + function AppendProcessingInstruction(const aTarget: TXmlString; + const aData: TXmlString): IXmlProcessingInstruction; overload; + + // GetChildText - âîçâðàùàåò çíà÷åíèå äî÷åðíåãî óçëà + // SetChildText - äîáàâëÿåò èëè èçìåíÿåò çíà÷åíèå äî÷åðíåãî óçëà + function GetChildText(const aName: TXmlString; const aDefault: TXmlString = ''): TXmlString; overload; + function GetChildText(aNameID: Integer; const aDefault: TXmlString = ''): TXmlString; overload; + procedure SetChildText(const aName, aValue: TXmlString); overload; + procedure SetChildText(aNameID: Integer; const aValue: TXmlString); overload; + + // NeedChild - âîçâðàùàåò äî÷åðíèé óçåë ñ óêàçàííûì èìåíåì. + // Åñëè óçåë íå íàéäåí, òî ãåíåðèðóåòñÿ èñêëþ÷åíèå + function NeedChild(aNameID: Integer): IXmlNode; overload; + function NeedChild(const aName: TXmlString): IXmlNode; overload; + + // EnsureChild - âîçâðàùàåò äî÷åðíèé óçåë ñ óêàçàííûì èìåíåì. + // Åñëè óçåë íå íàéäåí, òî îí áóäåò ñîçäàí + function EnsureChild(aNameID: Integer): IXmlNode; overload; + function EnsureChild(const aName: TXmlString): IXmlNode; overload; + + // RemoveAllChilds - óäàëÿåò âñå äî÷åðíèå óçëû + procedure RemoveAllChilds; + + // SelectNodes - ïðîèçâîäèò âûáîðêó óçëîâ, óäîâëåòâîðÿþùèõ + // óêàçàííûì êðèòåðèÿì + function SelectNodes(const anExpression: TXmlString): IXmlNodeList; + // SelectSingleNode - ïðîèçâîäèò ïîèñê ïåðâîãî óçëà, óäîâëåòâîðÿþùåãî + // óêàçàííûì êðèòåðèÿì + function SelectSingleNode(const anExpression: TXmlString): IXmlNode; + // FindElement - ïðîèçâîäèò ïîèñê ïåðâîãî óçëà, óäîâëåòâîðÿþùåãî + // óêàçàííûì êðèòåðèÿì + function FindElement(const anElementName, anAttrName: String; const anAttrValue: Variant): IXmlElement; + + // Get_AttrCount - âîçâðàùàåò êîëè÷åñòâî àòðèáóòîâ + function Get_AttrCount: Integer; + // Get_AttrNameID - âîçâðàùàåò êîä íàçâàíèÿ àòðèáóòà + function Get_AttrNameID(anIndex: Integer): Integer; + // Get_AttrName - âîçâðàùàåò íàçâàíèå àòðèáóòà + function Get_AttrName(anIndex: Integer): TXmlString; + // RemoveAttr - óäàëÿåò àòðèáóò + procedure RemoveAttr(const aName: TXmlString); overload; + procedure RemoveAttr(aNameID: Integer); overload; + // RemoveAllAttrs - óäàëÿåò âñå àòðèáóòû + procedure RemoveAllAttrs; + + // AttrExists - ïðîâåðÿåò, çàäàí ëè óêàçàííûé àòðèáóò. + function AttrExists(aNameID: Integer): Boolean; overload; + function AttrExists(const aName: TXmlString): Boolean; overload; + + // GetAttrType - âîçàðàùàåò òèï äàííûõ àòðèáóòà â òåðìèíàõ âàðèàíòîâ + function GetAttrType(aNameID: Integer): Integer; overload; + function GetAttrType(const aName: TXmlString): Integer; overload; + + // GetAttrType - âîçâðàùàåò òèï àòðèáóòà + // Result + // GetVarAttr - âîçâðàùàåò òèïèçèðîâàííîå çíà÷åíèå óêàçàííîãî àòðèáóòà. + // Åñëè àòðèáóò íå çàäàí, òî âîçâðàùàåòñÿ çíà÷åíèå ïî óìîë÷àíèþ + // SetAttr - èçìåíÿåò èëè äîáàâëÿåò óêàçàííûé àòðèáóò + function GetVarAttr(aNameID: Integer; const aDefault: Variant): Variant; overload; + function GetVarAttr(const aName: TXmlString; const aDefault: Variant): Variant; overload; + procedure SetVarAttr(aNameID: Integer; const aValue: Variant); overload; + procedure SetVarAttr(const aName: TXmlString; aValue: Variant); overload; + + // NeedAttr - âîçâðàùàåò ñòðîêîâîå çíà÷åíèå óêàçàííîãî àòðèáóòà. + // Åñëè àòðèáóò íå çàäàí, òî ãåíåðèðóåòñÿ èñêëþ÷åíèå + function NeedAttr(aNameID: Integer): TXmlString; overload; + function NeedAttr(const aName: TXmlString): TXmlString; overload; + + // GetAttr - âîçâðàùàåò ñòðîêîâîå çíà÷åíèå óêàçàííîãî àòðèáóòà. + // Åñëè àòðèáóò íå çàäàí, òî âîçâðàùàåòñÿ çíà÷åíèå ïî óìîë÷àíèþ + // SetAttr - èçìåíÿåò èëè äîáàâëÿåò óêàçàííûé àòðèáóò + function GetAttr(aNameID: Integer; const aDefault: TXmlString = ''): TXmlString; overload; + function GetAttr(const aName: TXmlString; const aDefault: TXmlString = ''): TXmlString; overload; + procedure SetAttr(aNameID: Integer; const aValue: TXmlString); overload; + procedure SetAttr(const aName, aValue: TXmlString); overload; + + // GetBoolAttr - âîçâðàùàåò öåëî÷èñëåííîå çíà÷åíèå óêàçàííîãî àòðèáóòà + // SetBoolAttr - èçìåíÿåò èëè äîáàâëÿåò óêàçàííûé àòðèáóò öåëî÷èñëåííûì + // çíà÷åíèåì + function GetBoolAttr(aNameID: Integer; aDefault: Boolean = False): Boolean; overload; + function GetBoolAttr(const aName: TXmlString; aDefault: Boolean = False): Boolean; overload; + procedure SetBoolAttr(aNameID: Integer; aValue: Boolean = False); overload; + procedure SetBoolAttr(const aName: TXmlString; aValue: Boolean); overload; + + // GetIntAttr - âîçâðàùàåò öåëî÷èñëåííîå çíà÷åíèå óêàçàííîãî àòðèáóòà + // SetIntAttr - èçìåíÿåò èëè äîáàâëÿåò óêàçàííûé àòðèáóò öåëî÷èñëåííûì + // çíà÷åíèåì + function GetIntAttr(aNameID: Integer; aDefault: Integer = 0): Integer; overload; + function GetIntAttr(const aName: TXmlString; aDefault: Integer = 0): Integer; overload; + procedure SetIntAttr(aNameID: Integer; aValue: Integer); overload; + procedure SetIntAttr(const aName: TXmlString; aValue: Integer); overload; + + // GetDateTimeAttr - âîçâðàùàåò öåëî÷èñëåííîå çíà÷åíèå óêàçàííîãî àòðèáóòà + // SetDateTimeAttr - èçìåíÿåò èëè äîáàâëÿåò óêàçàííûé àòðèáóò öåëî÷èñëåííûì + // çíà÷åíèåì + function GetDateTimeAttr(aNameID: Integer; aDefault: TDateTime = 0): TDateTime; overload; + function GetDateTimeAttr(const aName: TXmlString; aDefault: TDateTime = 0): TDateTime; overload; + procedure SetDateTimeAttr(aNameID: Integer; aValue: TDateTime); overload; + procedure SetDateTimeAttr(const aName: TXmlString; aValue: TDateTime); overload; + + // GetFloatAttr - âîçâðàùàåò çíà÷åíèå óêàçàííîãî àòðèáóòà â âèäå + // âåùåñòâåííîãî ÷èñëà + // SetFloatAttr - èçìåíÿåò èëè äîáàâëÿåò óêàçàííûé àòðèáóò âåùåñòâåííûì + // çíà÷åíèåì + function GetFloatAttr(aNameID: Integer; aDefault: Double = 0): Double; overload; + function GetFloatAttr(const aName: TXmlString; aDefault: Double = 0): Double; overload; + procedure SetFloatAttr(aNameID: Integer; aValue: Double); overload; + procedure SetFloatAttr(const aName: TXmlString; aValue: Double); overload; + + // GetHexAttr - ïîëó÷åíèå çíà÷åíèÿ óêàçàííîãî àòðèáóòà â öåëî÷èñëåííîì âèäå. + // Ñòðîêîâîå çíà÷åíèå àòðèáóòà ïðåîáðàçóåòñÿ â öåëîå ÷èñëî. Èñõîäíàÿ + // ñòðîêà äîëæíà áûòü çàäàíà â øåñòíàäöàòèðè÷íîì âèäå áåç ïðåôèêñîâ + // ("$", "0x" è ïð.) Åñëè ïðåîáðàçîâàíèå íå ìîæåò áûòü âûïîëíåíî, + // ãåíåðèðóåòñÿ èñêëþ÷åíèå. + // Åñëè àòðèáóò íå çàäàí, âîçâðàùàåòñÿ çíà÷åíèå ïàðàìåòðà aDefault. + // SetHexAttr - èçìåíåíèå çíà÷åíèÿ óêàçàííîãî àòðèáóòà íà ñòðîêîâîå + // ïðåäñòàâëåíèå öåëîãî ÷èñëà â øåñòíàäöàòèðè÷íîì âèäå áåç ïðåôèêñîâ + // ("$", "0x" è ïð.) Åñëè ïðåîáðàçîâàíèå íå ìîæåò áûòü âûïîëíåíî, + // ãåíåðèðóåòñÿ èñêëþ÷åíèå. + // Åñëè àòðèáóò íå áûë çàäàí, äî îí áóäåò äîáàâëåí. + // Åñëè áûë çàäàí, òî áóäåò èçìåíåí. + function GetHexAttr(const aName: TXmlString; aDefault: Integer = 0): Integer; overload; + function GetHexAttr(aNameID: Integer; aDefault: Integer = 0): Integer; overload; + procedure SetHexAttr(const aName: TXmlString; aValue: Integer; aDigits: Integer = 8); overload; + procedure SetHexAttr(aNameID: Integer; aValue: Integer; aDigits: Integer = 8); overload; + + // GetEnumAttr - èùåò çíà÷åíèå àòðèáóòà â óêàçàííîì ñïèñêå ñòðîê è + // âîçâðàùàåò èíäåêñ íàéäåííîé ñòðîêè. Åñëè àòðèáóò çàäàí íî íå íàéäåí + // â ñïèñêå, òî ãåíåðèðóåòñÿ èñêëþ÷åíèå. + // Åñëè àòðèáóò íå çàäàí, âîçâðàùàåòñÿ çíà÷åíèå ïàðàìåòðà aDefault. + function GetEnumAttr(const aName: TXmlString; + const aValues: array of TXmlString; aDefault: Integer = 0): Integer; overload; + function GetEnumAttr(aNameID: Integer; + const aValues: array of TXmlString; aDefault: Integer = 0): Integer; overload; + + function NeedEnumAttr(const aName: TXmlString; + const aValues: array of TXmlString): Integer; overload; + function NeedEnumAttr(aNameID: Integer; + const aValues: array of TXmlString): Integer; overload; + + function Get_Values(const aName: String): Variant; + procedure Set_Values(const aName: String; const aValue: Variant); + + function AsElement: IXmlElement; + function AsText: IXmlText; + function AsCDATASection: IXmlCDATASection; + function AsComment: IXmlComment; + function AsProcessingInstruction: IXmlProcessingInstruction; + + property NodeName: TXmlString read Get_NodeName; + property NodeNameID: Integer read Get_NodeNameID; + property NodeType: Integer read Get_NodeType; + property ParentNode: IXmlNode read Get_ParentNode; + property OwnerDocument: IXmlDocument read Get_OwnerDocument; + property NameTable: IXmlNameTable read Get_NameTable; + property ChildNodes: IXmlNodeList read Get_ChildNodes; + property AttrCount: Integer read Get_AttrCount; + property AttrNames[anIndex: Integer]: TXmlString read Get_AttrName; + property AttrNameIDs[anIndex: Integer]: Integer read Get_AttrNameID; + property Text: TXmlString read Get_Text write Set_Text; + property DataType: Integer read Get_DataType; + property TypedValue: Variant read Get_TypedValue write Set_TypedValue; + property XML: TXmlString read Get_XML; + property Values[const aName: String]: Variant read Get_Values write Set_Values; default; + end; + + IXmlElement = interface(IXmlNode) + // ReplaceTextByCDATASection - óäàëÿåò âñå òåêñòîâûå ýëåìåíòû è äîáàâëÿåò + // îäíó ñåêöèþ CDATA, ñîäåðæàùóþ óêàçàííûé òåêñò + procedure ReplaceTextByCDATASection(const aText: TXmlString); + + // ReplaceTextByBynaryData - óäàëÿåò âñå òåêñòîâûå ýëåìåíòû è äîáàâëÿåò + // îäèí òåêñòîâûé ýëåìåíò, ñîäåðæàùèé óêàçàííûå äâîè÷íûå äàííûå + // â ôîðìàòå "base64". + // Åñëè ïàðàìåòð aMaxLineLength íå ðàâåí íóëþ, òî ïðîèçâîäèòñÿ ðàçáèâêà + // ïîëó÷åíîé ñòðîêè íà ñòðîêè äëèíîé aMaxLineLength. + // Ñòðîêè ðàçäåëÿþòñÿ ïàðîé ñèìâîëîâ #13#10 (CR,LF). + // Ïîñëå ïîñëåäíåé ñòðîêè óêàçàííûå ñèìâîëû íå âñòàâëÿþòñÿ. + procedure ReplaceTextByBynaryData(const aData; aSize: Integer; + aMaxLineLength: Integer); + + // GetTextAsBynaryData - cîáèðàåò âñå òåêñòîâûå ýëåìåíòû â îäíó ñòðîêó è + // ïðîèçâîäèò ïðåîáðàçîâàíèå èç ôîðìàòà "base64" â äâîè÷íûå äàííûå. + // Ïðè ïðåîáðàçîâàíèè èãíîðèðóþòñÿ âñå ïðîáåëüíûå ñèìâîëû (ñ êîäîì <= ' '), + // ñîäåðæàùèåñÿ â èñõîäíîé ñòðîêå. + function GetTextAsBynaryData: TXmlString; + + end; + + IXmlCharacterData = interface(IXmlNode) + end; + + IXmlText = interface(IXmlCharacterData) + end; + + IXmlCDATASection = interface(IXmlCharacterData) + end; + + IXmlComment = interface(IXmlCharacterData) + end; + + IXmlProcessingInstruction = interface(IXmlNode) + end; + + IXmlDocument = interface(IXmlNode) + function Get_DocumentElement: IXmlElement; + function Get_BinaryXML: String; + function Get_PreserveWhiteSpace: Boolean; + procedure Set_PreserveWhiteSpace(aValue: Boolean); + + function NewDocument(const aVersion, anEncoding: TXmlString; + aRootElementNameID: Integer): IXmlElement; overload; + function NewDocument(const aVersion, anEncoding, + aRootElementName: TXmlString): IXmlElement; overload; + + function CreateElement(aNameID: Integer): IXmlElement; overload; + function CreateElement(const aName: TXmlString): IXmlElement; overload; + function CreateText(const aData: TXmlString): IXmlText; + function CreateCDATASection(const aData: TXmlString): IXmlCDATASection; + function CreateComment(const aData: TXmlString): IXmlComment; + function CreateProcessingInstruction(const aTarget, + aData: TXmlString): IXmlProcessingInstruction; overload; + function CreateProcessingInstruction(aTargetID: Integer; + const aData: TXmlString): IXmlProcessingInstruction; overload; + + procedure LoadXML(const aXML: TXmlString); + procedure LoadBinaryXML(const aXML: String); + + procedure Load(aStream: TStream); overload; + procedure Load(const aFileName: TXmlString); overload; + + procedure LoadResource(aType, aName: PChar); + + procedure Save(aStream: TStream); overload; + procedure Save(const aFileName: TXmlString); overload; + + procedure SaveBinary(aStream: TStream; anOptions: LongWord = 0); overload; + procedure SaveBinary(const aFileName: TXmlString; anOptions: LongWord = 0); overload; + + property PreserveWhiteSpace: Boolean read Get_PreserveWhiteSpace write Set_PreserveWhiteSpace; + property DocumentElement: IXmlElement read Get_DocumentElement; + property BinaryXML: String read Get_BinaryXML; + end; + +function CreateNameTable(aHashTableSize: Integer = 4096): IXmlNameTable; +function CreateXmlDocument( + const aRootElementName: String = ''; + const aVersion: String = '1.0'; + const anEncoding: String = ''; // SimpleXmlDefaultEncoding + const aNames: IXmlNameTable = nil): IXmlDocument; + +function CreateXmlElement(const aName: TXmlString; const aNameTable: IXmlNameTable = nil): IXmlElement; +function LoadXmlDocumentFromXML(const aXML: TXmlString): IXmlDocument; +function LoadXmlDocumentFromBinaryXML(const aXML: String): IXmlDocument; + +function LoadXmlDocument(aStream: TStream): IXmlDocument; overload; +function LoadXmlDocument(const aFileName: TXmlString): IXmlDocument; overload; +function LoadXmlDocument(aResType, aResName: PChar): IXmlDocument; overload; + + +var + DefaultNameTable: IXmlNameTable = nil; + DefaultPreserveWhiteSpace: Boolean = False; + DefaultEncoding: String = 'windows-1251'; + DefaultIndentText: String = ^I; + +resourcestring + SSimpleXmlError1 = 'Îøèáêà ïîëó÷åíèÿ ýëåìåíòà ñïèñêà: èíäåêñ âûõîäèò çà ïðåäåëû'; + SSimpleXmlError2 = 'Íå çàâåðøåíî îïðåäåëåíèå ýëåìåíòà'; + SSimpleXmlError3 = 'Íåêîððåòíûé ñèìâîë â èìåíè ýëåìåíòà'; + SSimpleXmlError4 = 'Îøèáêà ÷òåíèÿ äâîè÷íîãî XML: íåêîððåêòíûé òèï óçëà'; + SSimpleXmlError5 = 'Îøèáêà çàïèñè äâîè÷íîãî XML: íåêîððåêòíûé òèï óçëà'; + SSimpleXmlError6 = 'Íåâåðíîå çíà÷åíèå àòðèáóòà "%s" ýëåìåíòà "%s".'^M^J + + 'Äîïóñòèìûå çíà÷åíèÿ:'^M^J + + '%s'; + SSimpleXmlError7 = 'Íå íàéäåí àòðèáóò "%s"'; + SSimpleXmlError8 = 'Íå çàäàí àòðèáóò "%s"'; + SSimpleXmlError9 = 'Äàííàÿ âîçìîæíîñòü íå ïîääåðæèâàåòñÿ SimpleXML'; + SSimpleXmlError10 = 'Îøèáêà: íå íàéäåí äî÷åðíèé ýëåìåíò "%s".'; + SSimpleXmlError11 = 'Èìÿ äîëæíî íà÷èíàòüñÿ ñ áóêâû èëè "_"'; + SSimpleXmlError12 = 'Îæèäàåòñÿ ÷èñëî'; + SSimpleXmlError13 = 'Îæèäàåòñÿ øåñòíàäöàòåðè÷íîå ÷èñëî'; + SSimpleXmlError14 = 'Îæèäàåòñÿ "#" èëè èìÿ óïðàìëÿþùåãî ñèìâîëà'; + SSimpleXmlError15 = 'Íåêîððåêòíîå èìÿ óïðàâëÿþùåãî ñèìâîëà'; + SSimpleXmlError16 = 'Îæèäàåòñÿ "%c"'; + SSimpleXmlError17 = 'Îæèäàåòñÿ "%s"'; + SSimpleXmlError18 = 'Ñèìâîë "<" íå ìîæåò èñïîëüçîâàòüñÿ â çíà÷åíèÿõ àòðèáóòîâ'; + SimpleXmlError19 = 'Îæèäàåòñÿ "%s"'; + SSimpleXmlError20 = 'Îæèäàåòñÿ çíà÷åíèå àòðèáóòà'; + SSimpleXmlError21 = 'Îæèäàåòñÿ ñòðîêîâàÿ êîíñòàíòà'; + SimpleXmlError22 = 'Îæèäàåòñÿ "%s"'; + SSimpleXmlError23 = 'Îøèáêà ÷òåíèÿ äàííûõ.'; + SSimpleXmlError24 = 'Îøèáêà ÷òåíèÿ çíà÷åíèÿ: íåêîððåêòíûé òèï.'; + SSimpleXmlError25 = 'Îøèáêà çàïèñè çíà÷åíèÿ: íåêîððåêòíûé òèï.'; + +function XSTRToFloat(s: TXmlString): Double; +function FloatToXSTR(v: Double): TXmlString; +function DateTimeToXSTR(v: TDateTime): TXmlString; +function VarToXSTR(const v: TVarData): TXmlString; + +function TextToXML(const aText: TXmlString): TXmlString; +function BinToBase64(const aBin; aSize, aMaxLineLength: Integer): String; +function Base64ToBin(const aBase64: String): String; +function IsXmlDataString(const aData: String): Boolean; +function XmlIsInBinaryFormat(const aData: String): Boolean; +procedure PrepareToSaveXml(var anElem: IXmlElement; const aChildName: String); +function PrepareToLoadXml(var anElem: IXmlElement; const aChildName: String): Boolean; + +implementation + +uses + Variants, DateUtils; + +function TextToXML(const aText: TXmlString): TXmlString; +var + i, j: Integer; +begin + j := 0; + for i := 1 to Length(aText) do + case aText[i] of + '<', '>': Inc(j, 4); + '&': Inc(j, 5); + '"': Inc(j, 6); + else + Inc(j); + end; + if j = Length(aText) then + Result := aText + else begin + SetLength(Result, j); + j := 1; + for i := 1 to Length(aText) do + case aText[i] of + '<': begin Move(PChar('<')^, Result[j], 4); Inc(j, 4) end; + '>': begin Move(PChar('>')^, Result[j], 4); Inc(j, 4) end; + '&': begin Move(PChar('&')^, Result[j], 5); Inc(j, 5) end; + '"': begin Move(PChar('"')^, Result[j], 6); Inc(j, 6) end; + else begin Result[j] := aText[i]; Inc(j) end; + end; + end; +end; + +function XSTRToFloat(s: TXmlString): Double; +var + aPos: Integer; +begin + if '.' = DecimalSeparator then + aPos := Pos(',', s) + else if ',' = DecimalSeparator then + aPos := Pos('.', s) + else begin + aPos := Pos(',', s); + if aPos = 0 then + aPos := Pos('.', s); + end; + + if aPos <> 0 then + s[aPos] := TXmlChar(DecimalSeparator); + Result := StrToFloat(s); +end; + +function FloatToXSTR(v: Double): TXmlString; +var + aPos: Integer; +begin + Result := FloatToStr(v); + aPos := Pos(DecimalSeparator, Result); + if aPos <> 0 then + Result[aPos] := '.'; +end; + +function XSTRToDateTime(const s: String): TDateTime; +var + aPos: Integer; + + function FetchTo(aStop: Char): Integer; + var + i: Integer; + begin + i := aPos; + while (i <= Length(s)) and (s[i] in ['0'..'9']) do + Inc(i); + if i > aPos then + Result := StrToInt(Copy(s, aPos, i - aPos)) + else + Result := 0; + if (i <= Length(s)) and (s[i] = aStop) then + aPos := i + 1 + else + aPos := Length(s) + 1; + end; + +var + y, m, d, h, n, ss: Integer; +begin + aPos := 1; + y := FetchTo('-'); m := FetchTo('-'); d := FetchTo('T'); + h := FetchTo('-'); n := FetchTo('-'); ss := FetchTo('-'); + Result := EncodeDateTime(y, m, d, h, n, ss, 0); +end; + +function DateTimeToXSTR(v: TDateTime): TXmlString; +var + y, m, d, h, n, s, ms: Word; +begin + DecodeDateTime(v, y, m, d, h, n, s, ms); + Result := Format('%.4d-%.2d-%.2dT%.2d-%.2d-%.2d', [y, m, d, h, n, s]) +end; + +function VarToXSTR(const v: TVarData): TXmlString; +const + BoolStr: array[Boolean] of TXmlString = ('0', '1'); +var + p: Pointer; +begin + case v.VType of + varNull: Result := XSTR_NULL; + varSmallint: Result := IntToStr(v.VSmallInt); + varInteger: Result := IntToStr(v.VInteger); + varSingle: Result := FloatToXSTR(v.VSingle); + varDouble: Result := FloatToXSTR(v.VDouble); + varCurrency: Result := FloatToXSTR(v.VCurrency); + varDate: Result := DateTimeToXSTR(v.VDate); + varOleStr: Result := v.VOleStr; + varBoolean: Result := BoolStr[v.VBoolean = True]; + varShortInt: Result := IntToStr(v.VShortInt); + varByte: Result := IntToStr(v.VByte); + varWord: Result := IntToStr(v.VWord); + varLongWord: Result := IntToStr(v.VLongWord); + varInt64: Result := IntToStr(v.VInt64); + varString: Result := String(v.VString); + varArray + varByte: + begin + p := VarArrayLock(Variant(v)); + try + Result := BinToBase64(p^, VarArrayHighBound(Variant(v), 1) - VarArrayLowBound(Variant(v), 1) + 1, 0); + finally + VarArrayUnlock(Variant(v)) + end + end; + else + Result := Variant(v) + end; +end; + +procedure PrepareToSaveXml(var anElem: IXmlElement; const aChildName: String); +begin + if aChildName <> '' then + anElem := anElem.AppendElement(aChildName); +end; + +function PrepareToLoadXml(var anElem: IXmlElement; const aChildName: String): Boolean; +begin + if (aChildName <> '') and Assigned(anElem) then + anElem := anElem.selectSingleNode(aChildName).AsElement; + Result := Assigned(anElem); +end; + +function LoadXMLResource(aModule: HMODULE; aName, aType: PChar; const aXMLDoc: IXmlDocument): boolean; +var + aRSRC: HRSRC; + aGlobal: HGLOBAL; + aSize: DWORD; + aPointer: Pointer; + + aStream: TStringStream; +begin + Result := false; + + aRSRC := FindResource(aModule, aName, aType); + if aRSRC <> 0 then begin + aGlobal := LoadResource(aModule, aRSRC); + aSize := SizeofResource(aModule, aRSRC); + if (aGlobal <> 0) and (aSize <> 0) then begin + aPointer := LockResource(aGlobal); + if Assigned(aPointer) then begin + aStream := TStringStream.Create(''); + try + aStream.WriteBuffer(aPointer^, aSize); + aXMLDoc.LoadXML(aStream.DataString); + Result := true; + finally + aStream.Free; + end; + end; + end; + end; +end; + +function IsXmlDataString(const aData: String): Boolean; +var + i: Integer; +begin + Result := Copy(aData, 1, BinXmlSignatureSize) = BinXmlSignature; + if not Result then begin + i := 1; + while (i <= Length(aData)) and (aData[i] in [#10, #13, #9, ' ']) do + Inc(i); + Result := Copy(aData, i, Length(' 2 then begin + o := po.a shl 16 or po.b shl 8 or po.c; + LongWord(pc^) := $3D3D3D3D; + pc.a := Base64Map[(o shr 18) and $3F]; + pc.b := Base64Map[(o shr 12) and $3F]; + pc.c := Base64Map[(o shr 6) and $3F]; + pc.d := Base64Map[o and $3F]; + end; +end; + +function BinToBase64(const aBin; aSize, aMaxLineLength: Integer): String; +var + o: POctet; + c: PChars; + aCount: Integer; + i: Integer; +begin + o := @aBin; + aCount := aSize; + SetLength(Result, ((aCount + 2) div 3)*4); + c := PChars(Result); + while aCount > 0 do begin + OctetToChars(o, aCount, c); + Inc(o); + Inc(c); + Dec(aCount, 3); + end; + if aMaxLineLength > 0 then begin + i := aMaxLineLength; + while i <= Length(Result) do begin + Insert(#13#10, Result, i); + Inc(i, 2 + aMaxLineLength); + end + end; +end; + +function CharTo6Bit(c: Char): Byte; +begin + if (c >= 'A') and (c <= 'Z') then + Result := Ord(c) - Ord('A') + else if (c >= 'a') and (c <= 'z') then + Result := Ord(c) - Ord('a') + 26 + else if (c >= '0') and (c <= '9') then + Result := Ord(c) - Ord('0') + 52 + else if c = '+' then + Result := 62 + else if c = '/' then + Result := 63 + else + Result := 0 +end; + +procedure CharsToOctet(c: PChars; o: POctet); +var + i: Integer; +begin + if c.c = '=' then begin // 1 byte + i := CharTo6Bit(c.a) shl 18 or CharTo6Bit(c.b) shl 12; + o.a := (i shr 16) and $FF; + end + else if c.d = '=' then begin // 2 bytes + i := CharTo6Bit(c.a) shl 18 or CharTo6Bit(c.b) shl 12 or CharTo6Bit(c.c) shl 6; + o.a := (i shr 16) and $FF; + o.b := (i shr 8) and $FF; + end + else begin // 3 bytes + i := CharTo6Bit(c.a) shl 18 or CharTo6Bit(c.b) shl 12 or CharTo6Bit(c.c) shl 6 or CharTo6Bit(c.d); + o.a := (i shr 16) and $FF; + o.b := (i shr 8) and $FF; + o.c := i and $FF; + end; +end; + +function Base64ToBin(const aBase64: String): String; +var + o: POctet; + c: PChars; + aCount: Integer; + s: String; + i, j: Integer; +begin + s := aBase64; + i := 1; + while i <= Length(s) do begin + while (i <= Length(s)) and (s[i] > ' ') do + Inc(i); + if i <= Length(s) then begin + j := i; + while (j <= Length(s)) and (s[j] <= ' ') do + Inc(j); + Delete(s, i, j - i); + end; + end; + + if Length(s) < 4 then + Result := '' + else begin + aCount := ((Length(s) + 3) div 4)*3; + if aCount > 0 then begin + if s[Length(s) - 1] = '=' then + Dec(aCount, 2) + else if s[Length(s)] = '=' then + Dec(aCount); + SetLength(Result, aCount); + FillChar(Result[1], aCount, '*'); + c := @s[1]; + o := @Result[1]; + while aCount > 0 do begin + CharsToOctet(c, o); + Inc(o); + Inc(c); + Dec(aCount, 3); + end; + end; + end; +end; + + +type + TBinXmlReader = class + private + FOptions: LongWord; + public + procedure Read(var aBuf; aSize: Integer); virtual; abstract; + + function ReadLongint: Longint; + function ReadAnsiString: String; + function ReadWideString: WideString; + function ReadXmlString: TXmlString; + procedure ReadVariant(var v: TVarData); + end; + + TStmXmlReader = class(TBinXmlReader) + private + FStream: TStream; + FOptions: LongWord; + FBufStart, + FBufEnd, + FBufPtr: PChar; + FBufSize, + FRestSize: Integer; + public + constructor Create(aStream: TStream; aBufSize: Integer); + destructor Destroy; override; + + procedure Read(var aBuf; aSize: Integer); override; + end; + + TStrXmlReader = class(TBinXmlReader) + private + FString: String; + FOptions: LongWord; + FPtr: PChar; + FRestSize: Integer; + public + constructor Create(const aStr: String); + + procedure Read(var aBuf; aSize: Integer); override; + end; + + TBinXmlWriter = class + private + FOptions: LongWord; + public + procedure Write(const aBuf; aSize: Integer); virtual; abstract; + + procedure WriteLongint(aValue: Longint); + procedure WriteAnsiString(const aValue: String); + procedure WriteWideString(const aValue: WideString); + procedure WriteXmlString(const aValue: TXmlString); + procedure WriteVariant(const v: TVarData); + end; + + TStmXmlWriter = class(TBinXmlWriter) + private + FStream: TStream; + FBufStart, + FBufEnd, + FBufPtr: PChar; + FBufSize: Integer; + public + constructor Create(aStream: TStream; anOptions: LongWord; aBufSize: Integer); + destructor Destroy; override; + + procedure Write(const aBuf; aSize: Integer); override; + end; + + TStrXmlWriter = class(TBinXmlWriter) + private + FData: String; + FBufStart, + FBufEnd, + FBufPtr: PChar; + FBufSize: Integer; + procedure FlushBuf; + public + constructor Create(anOptions: LongWord; aBufSize: Integer); + destructor Destroy; override; + + procedure Write(const aBuf; aSize: Integer); override; + end; + + TXmlBase = class(TInterfacedObject, IXmlBase) + protected + // ðåàëèçàöèÿ èíòåðôåéñà IXmlBase + function GetObject: TObject; + public + end; + + PNameIndexArray = ^TNameIndexArray; + TNameIndexArray = array of Longint; + TXmlNameTable = class(TXmlBase, IXmlNameTable) + private + FNames: array of TXmlString; + FHashTable: array of TNameIndexArray; + + FXmlTextNameID: Integer; + FXmlCDATASectionNameID: Integer; + FXmlCommentNameID: Integer; + FXmlDocumentNameID: Integer; + FXmlID: Integer; + protected + function GetID(const aName: TXmlString): Integer; + function GetName(anID: Integer): TXmlString; + public + constructor Create(aHashTableSize: Integer); + + procedure LoadBinXml(aReader: TBinXmlReader); + procedure SaveBinXml(aWriter: TBinXmlWriter); + end; + +{ TXmlBase } + +function TXmlBase.GetObject: TObject; +begin + Result := Self; +end; + +{ TXmlNameTable } + +constructor TXmlNameTable.Create(aHashTableSize: Integer); +begin + inherited Create; + SetLength(FHashTable, aHashTableSize); + FXmlTextNameID := GetID('#text'); + FXmlCDATASectionNameID := GetID('#cdata-section'); + FXmlCommentNameID := GetID('#comment'); + FXmlDocumentNameID := GetID('#document'); + FXmlID := GetID('xml'); +end; + +procedure TXmlNameTable.LoadBinXml(aReader: TBinXmlReader); +var + aCount: LongInt; + anIndex, i: Integer; +begin + // Ñ÷èòàòü ìàññèâ èìåí + aCount := aReader.ReadLongint; + SetLength(FNames, aCount); + for i := 0 to aCount - 1 do + FNames[i] := aReader.ReadXmlString; + + // Ñ÷èòàòü õýø-òàáëèöó + SetLength(FHashTable, aReader.ReadLongint); + for i := 0 to Length(FHashTable) - 1 do + SetLength(FHashTable[i], 0); + aCount := aReader.ReadLongint; + for i := 0 to aCount - 1 do begin + anIndex := aReader.ReadLongInt; + SetLength(FHashTable[anIndex], aReader.ReadLongInt); + aReader.Read(FHashTable[anIndex][0], Length(FHashTable[anIndex])*sizeof(Longint)); + end; +end; + +procedure TXmlNameTable.SaveBinXml(aWriter: TBinXmlWriter); +var + aCount: LongInt; + i: Integer; +begin + // Çàïèñàòü ìàññèâ èìåí + aCount := Length(FNames); + aWriter.WriteLongint(aCount); + for i := 0 to aCount - 1 do + aWriter.WriteXmlString(FNames[i]); + + // Çàïèñàòü õýø-òàáëèöó + aWriter.WriteLongint(Length(FHashTable)); + aCount := 0; + for i := 0 to Length(FHashTable) - 1 do + if Length(FHashTable[i]) > 0 then + Inc(aCount); + aWriter.WriteLongint(aCount); + for i := 0 to Length(FHashTable) - 1 do begin + aCount := Length(FHashTable[i]); + if aCount > 0 then begin + aWriter.WriteLongint(i); + aWriter.WriteLongint(aCount); + aWriter.Write(FHashTable[i][0], aCount*sizeof(Longint)); + end + end; +end; + +function TXmlNameTable.GetID(const aName: TXmlString): Integer; + + function NameHashKey(const aName: TXmlString): UINT; + var + i: Integer; + begin + Result := 0; + for i := 1 to Length(aName) do + Result := UINT((int64(Result) shl 5) + Result + Ord(aName[i])); + end; + +var + i: Integer; + aNameIndexes: PNameIndexArray; +begin + if aName = '' then + Result := -1 + else begin + aNameIndexes := @FHashTable[NameHashKey(aName) mod UINT(Length(FHashTable))]; + for i := 0 to Length(aNameIndexes^) - 1 do begin + Result := aNameIndexes^[i]; + if FNames[Result] = aName then + Exit + end; + Result := Length(FNames); + SetLength(FNames, Result + 1); + FNames[Result] := aName; + + SetLength(aNameIndexes^, Length(aNameIndexes^) + 1); + aNameIndexes^[Length(aNameIndexes^) - 1] := Result; + end; +end; + +function TXmlNameTable.GetName(anID: Integer): TXmlString; +begin + if anID < 0 then + Result := '' + else + Result := FNames[anID] +end; + +function CreateNameTable(aHashTableSize: Integer): IXmlNameTable; +begin + Result := TXmlNameTable.Create(aHashTableSize) +end; + +type + TXmlNode = class; + TXmlToken = class + private + FValueBuf: TXmlString; + FValueStart, + FValuePtr, + FValueEnd: PXmlChar; + public + constructor Create; + procedure Clear; + procedure AppendChar(aChar: TXmlChar); + procedure AppendText(aText: PXmlChar; aCount: Integer); + function Length: Integer; + + property ValueStart: PXmlChar read FValueStart; + end; + + TXmlSource = class + private + FTokenStack: array of TXmlToken; + FTokenStackTop: Integer; + FToken: TXmlToken; + function ExpectQuotedText(aQuote: TXmlChar): TXmlString; + public + CurChar: TXmlChar; + constructor Create; + destructor Destroy; override; + + function EOF: Boolean; virtual; abstract; + function Next: Boolean; virtual; abstract; + + procedure SkipBlanks; + function ExpectXmlName: TXmlString; + function ExpectXmlEntity: TXmlChar; + procedure ExpectChar(aChar: TXmlChar); + procedure ExpectText(aText: PXmlChar); + function ExpectDecimalInteger: Integer; + function ExpectHexInteger: Integer; + function ParseTo(aText: PXmlChar): TXmlString; + procedure ParseAttrs(aNode: TXmlNode); + + procedure NewToken; + procedure AppendTokenChar(aChar: TXmlChar); + procedure AppendTokenText(aText: PXmlChar; aCount: Integer); + function AcceptToken: TXmlString; + procedure DropToken; + end; + + TXmlStrSource = class(TXmlSource) + private + FSource: TXmlString; + FSourcePtr, + FSourceEnd: PXmlChar; + public + constructor Create(const aSource: TXmlString); + function EOF: Boolean; override; + function Next: Boolean; override; + end; + + TXmlStmSource = class(TXmlSource) + private + FStream: TStream; + FBufStart, + FBufPtr, + FBufEnd: PChar; + FBufSize: Integer; + FSize: Integer; + public + constructor Create(aStream: TStream; aBufSize: Integer); + function EOF: Boolean; override; + function Next: Boolean; override; + destructor Destroy; override; + end; + + TXmlNodeList = class(TXmlBase, IXmlNodeList) + private + FOwnerNode: TXmlNode; + + FItems: array of TXmlNode; + FCount: Integer; + procedure Grow; + protected + function Get_Count: Integer; + function Get_Item(anIndex: Integer): IXmlNode; + function Get_XML: TXmlString; + public + constructor Create(anOwnerNode: TXmlNode); + destructor Destroy; override; + + function IndexOf(aNode: TXmlNode): Integer; + procedure ParseXML(aXML: TXmlSource; aNames: TXmlNameTable; aPreserveWhiteSpace: Boolean); + + procedure LoadBinXml(aReader: TBinXmlReader; aCount: Integer; aNames: TXmlNameTable); + procedure SaveBinXml(aWriter: TBinXmlWriter); + + procedure Insert(aNode: TXmlNode; anIndex: Integer); + function Remove(aNode: TXmlNode): Integer; + procedure Delete(anIndex: Integer); + procedure Replace(anIndex: Integer; aNode: TXmlNode); + procedure Clear; + end; + + PXmlAttrData = ^TXmlAttrData; + TXmlAttrData = record + NameID: Integer; + Value: Variant; + end; + + TXmlDocument = class; + TXmlNode = class(TXmlBase, IXmlNode) + private + FParentNode: TXmlNode; + // FNames - òàáëèöà èìåí. Çàäàåòñÿ èçâíå + FNames: TXmlNameTable; + // Êîëè÷åñòâî àòðèáóòîâ â ìàññèâå FAttrs + FAttrCount: Integer; + // Ìàññèâ àòðèáóòîâ + FAttrs: array of TXmlAttrData; + // Ñïèñîê äî÷åðíèõ óçëîâ + FChilds: TXmlNodeList; + function GetChilds: TXmlNodeList; virtual; + function FindFirstChild(aNameID: Integer): TXmlNode; + function GetAttrsXML: TXmlString; + function FindAttrData(aNameID: Integer): PXmlAttrData; + function GetOwnerDocument: TXmlDocument; + procedure SetNameTable(aValue: TXmlNameTable; aMap: TList); + procedure SetNodeNameID(aValue: Integer); virtual; + function DoCloneNode(aDeep: Boolean): IXmlNode; virtual; abstract; + + protected + // IXmlNode + function Get_NameTable: IXmlNameTable; + function Get_NodeName: TXmlString; + + function Get_NodeNameID: Integer; virtual; abstract; + function Get_NodeType: Integer; virtual; abstract; + function Get_Text: TXmlString; virtual; abstract; + procedure Set_Text(const aValue: TXmlString); virtual; abstract; + function CloneNode(aDeep: Boolean): IXmlNode; + + procedure LoadBinXml(aReader: TBinXmlReader); + procedure SaveBinXml(aWriter: TBinXmlWriter); + + function Get_DataType: Integer; virtual; + function Get_TypedValue: Variant; virtual; + procedure Set_TypedValue(const aValue: Variant); virtual; + + function Get_XML: TXmlString; virtual; abstract; + + function Get_OwnerDocument: IXmlDocument; virtual; + function Get_ParentNode: IXmlNode; + + function Get_ChildNodes: IXmlNodeList; virtual; + procedure AppendChild(const aChild: IXmlNode); + + function AppendElement(aNameID: Integer): IXmlElement; overload; + function AppendElement(const aName: TxmlString): IXmlElement; overload; + function AppendText(const aData: TXmlString): IXmlText; + function AppendCDATA(const aData: TXmlString): IXmlCDATASection; + function AppendComment(const aData: TXmlString): IXmlComment; + function AppendProcessingInstruction(aTargetID: Integer; + const aData: TXmlString): IXmlProcessingInstruction; overload; + function AppendProcessingInstruction(const aTarget: TXmlString; + const aData: TXmlString): IXmlProcessingInstruction; overload; + + procedure InsertBefore(const aChild, aBefore: IXmlNode); + procedure ReplaceChild(const aNewChild, anOldChild: IXmlNode); + procedure RemoveChild(const aChild: IXmlNode); + function GetChildText(const aName: TXmlString; const aDefault: TXmlString = ''): TXmlString; overload; + function GetChildText(aNameID: Integer; const aDefault: TXmlString = ''): TXmlString; overload; + procedure SetChildText(const aName, aValue: TXmlString); overload; + procedure SetChildText(aNameID: Integer; const aValue: TXmlString); overload; + + function NeedChild(aNameID: Integer): IXmlNode; overload; + function NeedChild(const aName: TXmlString): IXmlNode; overload; + function EnsureChild(aNameID: Integer): IXmlNode; overload; + function EnsureChild(const aName: TXmlString): IXmlNode; overload; + + procedure RemoveAllChilds; + + function SelectNodes(const anExpression: TXmlString): IXmlNodeList; + function SelectSingleNode(const anExpression: TXmlString): IXmlNode; + function FindElement(const anElementName, anAttrName: String; const anAttrValue: Variant): IXmlElement; + + function Get_AttrCount: Integer; + function Get_AttrNameID(anIndex: Integer): Integer; + function Get_AttrName(anIndex: Integer): TXmlString; + procedure RemoveAttr(const aName: TXmlString); overload; + procedure RemoveAttr(aNameID: Integer); overload; + procedure RemoveAllAttrs; + + function AttrExists(aNameID: Integer): Boolean; overload; + function AttrExists(const aName: TXmlString): Boolean; overload; + + function GetAttrType(aNameID: Integer): Integer; overload; + function GetAttrType(const aName: TXmlString): Integer; overload; + + function GetVarAttr(aNameID: Integer; const aDefault: Variant): Variant; overload; + function GetVarAttr(const aName: TXmlString; const aDefault: Variant): Variant; overload; + procedure SetVarAttr(aNameID: Integer; const aValue: Variant); overload; + procedure SetVarAttr(const aName: TXmlString; aValue: Variant); overload; + + function NeedAttr(aNameID: Integer): TXmlString; overload; + function NeedAttr(const aName: TXmlString): TXmlString; overload; + + function GetAttr(aNameID: Integer; const aDefault: TXmlString = ''): TXmlString; overload; + function GetAttr(const aName: TXmlString; const aDefault: TXmlString = ''): TXmlString; overload; + procedure SetAttr(aNameID: Integer; const aValue: TXmlString); overload; + procedure SetAttr(const aName, aValue: TXmlString); overload; + + function GetBoolAttr(aNameID: Integer; aDefault: Boolean = False): Boolean; overload; + function GetBoolAttr(const aName: TXmlString; aDefault: Boolean = False): Boolean; overload; + procedure SetBoolAttr(aNameID: Integer; aValue: Boolean = False); overload; + procedure SetBoolAttr(const aName: TXmlString; aValue: Boolean); overload; + + function GetIntAttr(aNameID: Integer; aDefault: Integer = 0): Integer; overload; + function GetIntAttr(const aName: TXmlString; aDefault: Integer = 0): Integer; overload; + procedure SetIntAttr(aNameID: Integer; aValue: Integer); overload; + procedure SetIntAttr(const aName: TXmlString; aValue: Integer); overload; + + function GetDateTimeAttr(aNameID: Integer; aDefault: TDateTime = 0): TDateTime; overload; + function GetDateTimeAttr(const aName: TXmlString; aDefault: TDateTime = 0): TDateTime; overload; + procedure SetDateTimeAttr(aNameID: Integer; aValue: TDateTime); overload; + procedure SetDateTimeAttr(const aName: TXmlString; aValue: TDateTime); overload; + + function GetFloatAttr(aNameID: Integer; aDefault: Double = 0): Double; overload; + function GetFloatAttr(const aName: TXmlString; aDefault: Double = 0): Double; overload; + procedure SetFloatAttr(aNameID: Integer; aValue: Double); overload; + procedure SetFloatAttr(const aName: TXmlString; aValue: Double); overload; + + function GetHexAttr(const aName: TXmlString; aDefault: Integer = 0): Integer; overload; + function GetHexAttr(aNameID: Integer; aDefault: Integer = 0): Integer; overload; + procedure SetHexAttr(const aName: TXmlString; aValue: Integer; aDigits: Integer = 8); overload; + procedure SetHexAttr(aNameID: Integer; aValue: Integer; aDigits: Integer = 8); overload; + + function GetEnumAttr(const aName: TXmlString; + const aValues: array of TXmlString; aDefault: Integer = 0): Integer; overload; + function GetEnumAttr(aNameID: Integer; + const aValues: array of TXmlString; aDefault: Integer = 0): Integer; overload; + function NeedEnumAttr(const aName: TXmlString; + const aValues: array of TXmlString): Integer; overload; + function NeedEnumAttr(aNameID: Integer; + const aValues: array of TXmlString): Integer; overload; + + + function Get_Values(const aName: String): Variant; + procedure Set_Values(const aName: String; const aValue: Variant); + + function AsElement: IXmlElement; virtual; + function AsText: IXmlText; virtual; + function AsCDATASection: IXmlCDATASection; virtual; + function AsComment: IXmlComment; virtual; + function AsProcessingInstruction: IXmlProcessingInstruction; virtual; + + public + constructor Create(aNames: TXmlNameTable); + destructor Destroy; override; + end; + + TXmlElement = class(TXmlNode, IXmlElement) + private + FNameID: Integer; + FData: Variant; + procedure RemoveTextNodes; + procedure SetNodeNameID(aValue: Integer); override; + function DoCloneNode(aDeep: Boolean): IXmlNode; override; + protected + function GetChilds: TXmlNodeList; override; + + function Get_NodeNameID: Integer; override; + function Get_NodeType: Integer; override; + function Get_Text: TXmlString; override; + procedure Set_Text(const aValue: TXmlString); override; + function Get_DataType: Integer; override; + function Get_TypedValue: Variant; override; + procedure Set_TypedValue(const aValue: Variant); override; + function Get_XML: TXmlString; override; + function AsElement: IXmlElement; override; + function Get_ChildNodes: IXmlNodeList; override; + + // IXmlElement + procedure ReplaceTextByCDATASection(const aText: TXmlString); + procedure ReplaceTextByBynaryData(const aData; aSize: Integer; + aMaxLineLength: Integer); + function GetTextAsBynaryData: TXmlString; + public + constructor Create(aNames: TXmlNameTable; aNameID: Integer); + end; + + TXmlCharacterData = class(TXmlNode, IXmlCharacterData) + private + FData: TXmlString; + protected + function Get_Text: TXmlString; override; + procedure Set_Text(const aValue: TXmlString); override; + public + constructor Create(aNames: TXmlNameTable; const aData: TXmlString); + end; + + TXmlText = class(TXmlNode, IXmlText) + private + FData: Variant; + function DoCloneNode(aDeep: Boolean): IXmlNode; override; + protected + function Get_NodeNameID: Integer; override; + function Get_NodeType: Integer; override; + function Get_Text: TXmlString; override; + procedure Set_Text(const aValue: TXmlString); override; + function Get_DataType: Integer; override; + function Get_TypedValue: Variant; override; + procedure Set_TypedValue(const aValue: Variant); override; + function Get_XML: TXmlString; override; + function AsText: IXmlText; override; + public + constructor Create(aNames: TXmlNameTable; const aData: Variant); + end; + + TXmlCDATASection = class(TXmlCharacterData, IXmlCDATASection) + protected + function Get_NodeNameID: Integer; override; + function Get_NodeType: Integer; override; + function Get_XML: TXmlString; override; + function AsCDATASection: IXmlCDATASection; override; + function DoCloneNode(aDeep: Boolean): IXmlNode; override; + public + end; + + TXmlComment = class(TXmlCharacterData, IXmlComment) + protected + function Get_NodeNameID: Integer; override; + function Get_NodeType: Integer; override; + function Get_XML: TXmlString; override; + function AsComment: IXmlComment; override; + function DoCloneNode(aDeep: Boolean): IXmlNode; override; + public + end; + + TXmlProcessingInstruction = class(TXmlNode, IXmlProcessingInstruction) + private + FTargetID: Integer; + FData: String; + procedure SetNodeNameID(aValue: Integer); override; + function DoCloneNode(aDeep: Boolean): IXmlNode; override; + protected + function Get_NodeNameID: Integer; override; + function Get_NodeType: Integer; override; + function Get_Text: TXmlString; override; + procedure Set_Text(const aText: TXmlString); override; + function Get_XML: TXmlString; override; + function AsProcessingInstruction: IXmlProcessingInstruction; override; + + public + constructor Create(aNames: TXmlNameTable; aTargetID: Integer; + const aData: TXmlString); + end; + + TXmlDocument = class(TXmlNode, IXmlDocument) + private + FPreserveWhiteSpace: Boolean; + + function DoCloneNode(aDeep: Boolean): IXmlNode; override; + protected + function Get_NodeNameID: Integer; override; + function Get_NodeType: Integer; override; + function Get_Text: TXmlString; override; + procedure Set_Text(const aText: TXmlString); override; + function Get_XML: TXmlString; override; + function Get_PreserveWhiteSpace: Boolean; + procedure Set_PreserveWhiteSpace(aValue: Boolean); + + function NewDocument(const aVersion, anEncoding: TXmlString; + aRootElementNameID: Integer): IXmlElement; overload; + function NewDocument(const aVersion, anEncoding, + aRootElementName: TXmlString): IXmlElement; overload; + + function CreateElement(aNameID: Integer): IXmlElement; overload; + function CreateElement(const aName: TXmlString): IXmlElement; overload; + function CreateText(const aData: TXmlString): IXmlText; + function CreateCDATASection(const aData: TXmlString): IXmlCDATASection; + function CreateComment(const aData: TXmlString): IXmlComment; + function Get_DocumentElement: IXmlElement; + function CreateProcessingInstruction(const aTarget, + aData: TXmlString): IXmlProcessingInstruction; overload; + function CreateProcessingInstruction(aTargetID: Integer; + const aData: TXmlString): IXmlProcessingInstruction; overload; + procedure LoadXML(const aXML: TXmlString); + + procedure Load(aStream: TStream); overload; + procedure Load(const aFileName: TXmlString); overload; + + procedure LoadResource(aType, aName: PChar); + + procedure Save(aStream: TStream); overload; + procedure Save(const aFileName: TXmlString); overload; + + procedure SaveBinary(aStream: TStream; anOptions: LongWord); overload; + procedure SaveBinary(const aFileName: TXmlString; anOptions: LongWord); overload; + + function Get_BinaryXML: String; + procedure LoadBinaryXML(const aXML: String); + public + constructor Create(aNames: TXmlNameTable); + end; + +{ TXmlNodeList } + +procedure TXmlNodeList.Clear; +var + i: Integer; + aNode: TXmlNode; +begin + for i := 0 to FCount - 1 do begin + aNode := FItems[i]; + if Assigned(FOwnerNode) then + aNode.FParentNode := nil; + aNode._Release; + end; + FCount := 0; +end; + +procedure TXmlNodeList.Delete(anIndex: Integer); +var + aNode: TXmlNode; +begin + aNode := FItems[anIndex]; + Dec(FCount); + if anIndex < FCount then + Move(FItems[anIndex + 1], FItems[anIndex], + (FCount - anIndex)*SizeOf(TXmlNode)); + if Assigned(aNode) then begin + if Assigned(FOwnerNode) then + aNode.FParentNode := nil; + aNode._Release; + end; +end; + +constructor TXmlNodeList.Create(anOwnerNode: TXmlNode); +begin + inherited Create; + FOwnerNode := anOwnerNode; +end; + +destructor TXmlNodeList.Destroy; +begin + Clear; + inherited; +end; + +function TXmlNodeList.Get_Item(anIndex: Integer): IXmlNode; +begin + if (anIndex < 0) or (anIndex >= FCount) then + raise Exception.Create(SSimpleXmlError1); + Result := FItems[anIndex] +end; + +function TXmlNodeList.Get_Count: Integer; +begin + Result := FCount +end; + +function TXmlNodeList.IndexOf(aNode: TXmlNode): Integer; +var + i: Integer; +begin + for i := 0 to FCount - 1 do + if FItems[i] = aNode then begin + Result := i; + Exit + end; + Result := -1; +end; + +procedure TXmlNodeList.Grow; +var + aDelta: Integer; +begin + if Length(FItems) > 64 then + aDelta := Length(FItems) div 4 + else + if Length(FItems) > 8 then + aDelta := 16 + else + aDelta := 4; + SetLength(FItems, Length(FItems) + aDelta); +end; + +procedure TXmlNodeList.Insert(aNode: TXmlNode; anIndex: Integer); +begin + if anIndex = -1 then + anIndex := FCount; + if FCount = Length(FItems) then + Grow; + if anIndex < FCount then + Move(FItems[anIndex], FItems[anIndex + 1], + (FCount - anIndex)*SizeOf(TXmlNode)); + FItems[anIndex] := aNode; + Inc(FCount); + if aNode <> nil then begin + aNode._AddRef; + if Assigned(FOwnerNode) then begin + aNode.FParentNode := FOwnerNode; + aNode.SetNameTable(FOwnerNode.FNames, nil); + end; + end; +end; + +function TXmlNodeList.Remove(aNode: TXmlNode): Integer; +begin + Result := IndexOf(aNode); + if Result <> -1 then + Delete(Result); +end; + +procedure TXmlNodeList.Replace(anIndex: Integer; aNode: TXmlNode); +var + anOldNode: TXmlNode; +begin + anOldNode := FItems[anIndex]; + if aNode <> anOldNode then begin + if Assigned(anOldNode) then begin + if Assigned(FOwnerNode) then + anOldNode.FParentNode := nil; + anOldNode._Release; + end; + FItems[anIndex] := aNode; + if Assigned(aNode) then begin + aNode._AddRef; + if Assigned(FOwnerNode) then begin + aNode.FParentNode := FOwnerNode; + aNode.SetNameTable(FOwnerNode.FNames, nil); + end + end + end; +end; + +function TXmlNodeList.Get_XML: TXmlString; +var + i: Integer; +begin + Result := ''; + for i := 0 to FCount - 1 do + Result := Result + FItems[i].Get_XML; +end; + +procedure TXmlNodeList.ParseXML(aXML: TXmlSource; aNames: TXmlNameTable; aPreserveWhiteSpace: Boolean); + + // íà âõîäå: ñèìâîë òåêñòà + // íà âûõîäå: ñèìâîë ðàçìåòêè '<' + procedure ParseText; + var + aText: String; + begin + aXml.NewToken; + while not aXML.EOF and (aXML.CurChar <> '<') do + if aXML.CurChar = '&' then + aXml.AppendTokenChar(aXml.ExpectXmlEntity) + else begin + aXml.AppendTokenChar(aXML.CurChar); + aXML.Next; + end; + aText := aXml.AcceptToken; + if aPreserveWhiteSpace or (Trim(aText) <> '') then + Insert(TXmlText.Create(aNames, aText), -1); + end; + + // CurChar - '?' + procedure ParseProcessingInstruction; + var + aTarget: TXmlString; + aNode: TXmlProcessingInstruction; + begin + aXML.Next; + aTarget := aXML.ExpectXmlName; + aNode := TXmlProcessingInstruction.Create(aNames, aNames.GetID(aTarget), ''); + Insert(aNode, -1); + if aNode.FTargetID = aNames.FXmlID then begin + aXml.ParseAttrs(aNode); + aXml.ExpectText('?>'); + end + else + aNode.FData := aXml.ParseTo('?>'); + end; + + // íà âõîäå: ïåðâûé '--' + // íà âûõîäå: ñèìâîë ïîñëå '-->' + procedure ParseComment; + begin + aXml.ExpectText('--'); + Insert(TXmlComment.Create(aNames, aXml.ParseTo('-->')), -1); + end; + + // íà âõîäå: '[CDATA[' + // íà âûõîäå: ñèìâîë ïîñëå ']]>' + procedure ParseCDATA; + begin + aXml.ExpectText('[CDATA['); + Insert(TXmlCDATASection.Create(aNames, aXml.ParseTo(']]>')), -1); + end; + + + // íà âõîäå: 'DOCTYPE' + // íà âûõîäå: ñèìâîë ïîñëå '>' + procedure ParseDOCTYPE; + begin + aXml.ExpectText('DOCTYPE'); + aXml.ParseTo('>'); + end; + + // íà âõîäå: 'èìÿ-ýëåìåíòà' + // íà âûõîäå: ñèìâîë ïîñëå '>' + procedure ParseElement; + var + aNameID: Integer; + aNode: TXmlElement; + begin + aNameID := aNames.GetID(aXml.ExpectXmlName); + if aXml.EOF then + raise Exception.Create(SSimpleXMLError2); + if not ((aXml.CurChar <= ' ') or (aXml.CurChar = '/') or (aXml.CurChar = '>')) then + raise Exception.Create(SSimpleXMLError3); + aNode := TXmlElement.Create(aNames, aNameID); + Insert(aNode, -1); + aXml.ParseAttrs(aNode); + if aXml.CurChar = '/' then + aXml.ExpectText('/>') + else begin + aXml.ExpectChar('>'); + aNode.GetChilds.ParseXML(aXml, aNames, aPreserveWhiteSpace); + aXml.ExpectChar('/'); + aXml.ExpectText(PXmlChar(aNames.GetName(aNameID))); + aXml.SkipBlanks; + aXml.ExpectChar('>'); + end; + end; + +begin + while not aXML.EOF do begin + ParseText; + if aXML.CurChar = '<' then // ñèìâîë ðàçìåòêè + if aXML.Next then + if aXML.CurChar = '/' then // çàêðûâàþùèé òýã ýëåìåíòà + Exit + else if aXML.CurChar = '?' then // èíñòðóêöèÿ + ParseProcessingInstruction + else if aXML.CurChar = '!' then begin + if aXML.Next then + if aXML.CurChar = '-' then // êîìåíòàðèé + ParseComment + else if aXML.CurChar = '[' then // ñåêöèÿ CDATA + ParseCDATA + else + ParseDOCTYPE + end + else // îòêðûâàþùèé òýã ýëåìåíòà + ParseElement + end; +end; + +procedure TXmlNodeList.LoadBinXml(aReader: TBinXmlReader; + aCount: Integer; aNames: TXmlNameTable); +var + i: Integer; + aNodeType: Byte; + aNode: TXmlNode; + aNameID: LongInt; +begin + Clear; + SetLength(FItems, aCount); + for i := 0 to aCount - 1 do begin + aReader.Read(aNodeType, sizeof(aNodeType)); + case aNodeType of + NODE_ELEMENT: + begin + aNameID := aReader.ReadLongint; + aNode := TXmlElement.Create(aNames, aNameID); + Insert(aNode, -1); + aReader.ReadVariant(TVarData(TXmlElement(aNode).FData)); + aNode.LoadBinXml(aReader); + end; + NODE_TEXT: + begin + aNode := TXmlText.Create(aNames, Unassigned); + Insert(aNode, -1); + aReader.ReadVariant(TVarData(TXmlText(aNode).FData)); + end; + NODE_CDATA_SECTION: + Insert(TXmlCDATASection.Create(aNames, aReader.ReadXmlString), -1); + NODE_PROCESSING_INSTRUCTION: + begin + aNameID := aReader.ReadLongint; + aNode := TXmlProcessingInstruction.Create(aNames, aNameID, + aReader.ReadXmlString); + Insert(aNode, -1); + aNode.LoadBinXml(aReader); + end; + NODE_COMMENT: + Insert(TXmlComment.Create(aNames, aReader.ReadXmlString), -1); + else + raise Exception.Create(SSimpleXMLError4); + end + end; +end; + +procedure TXmlNodeList.SaveBinXml(aWriter: TBinXmlWriter); +const + EmptyVar: TVarData = (VType:varEmpty); +var + aCount: LongInt; + i: Integer; + aNodeType: Byte; + aNode: TXmlNode; +begin + aCount := FCount; + for i := 0 to aCount - 1 do begin + aNode := FItems[i]; + aNodeType := aNode.Get_NodeType; + aWriter.Write(aNodeType, sizeof(aNodeType)); + case aNodeType of + NODE_ELEMENT: + with TXmlElement(aNode) do begin + aWriter.WriteLongint(FNameID); + if Assigned(FChilds) and (FChilds.FCount > 0) or VarIsEmpty(FData) then + aWriter.WriteVariant(EmptyVar) + else + aWriter.WriteVariant(TVarData(FData)); + SaveBinXml(aWriter); + end; + NODE_TEXT: + aWriter.WriteVariant(TVarData(TXmlText(aNode).FData)); + NODE_CDATA_SECTION: + aWriter.WriteXmlString(TXmlCDATASection(aNode).FData); + NODE_PROCESSING_INSTRUCTION: + begin + aWriter.WriteLongint(TXmlProcessingInstruction(aNode).FTargetID); + aWriter.WriteXmlString(TXmlProcessingInstruction(aNode).FData); + aNode.SaveBinXml(aWriter); + end; + NODE_COMMENT: + aWriter.WriteXmlString(TXmlComment(aNode).FData); + else + raise Exception.Create(SSimpleXmlError5); + end + end; +end; + +{ TXmlNode } + +constructor TXmlNode.Create(aNames: TXmlNameTable); +begin + inherited Create; + FNames := aNames; + FNames._AddRef; +end; + +destructor TXmlNode.Destroy; +begin + if Assigned(FChilds) then + FChilds._Release; + FNames._Release; + inherited; +end; + +function TXmlNode.GetChilds: TXmlNodeList; +begin + if not Assigned(FChilds) then begin + FChilds := TXmlNodeList.Create(Self); + FChilds._AddRef; + end; + Result := FChilds; +end; + +procedure TXmlNode.AppendChild(const aChild: IXmlNode); +begin + GetChilds.Insert(aChild.GetObject as TXmlNode, -1); +end; + +function TXmlNode.Get_AttrCount: Integer; +begin + Result := FAttrCount; +end; + +function TXmlNode.Get_AttrName(anIndex: Integer): TXmlString; +begin + Result := FNames.GetName(FAttrs[anIndex].NameID); +end; + +function TXmlNode.Get_AttrNameID(anIndex: Integer): Integer; +begin + Result := FAttrs[anIndex].NameID; +end; + +function TXmlNode.Get_ChildNodes: IXmlNodeList; +begin + Result := GetChilds +end; + +function TXmlNode.Get_NameTable: IXmlNameTable; +begin + Result := FNames +end; + +function TXmlNode.GetAttr(const aName, aDefault: TXmlString): TXmlString; +begin + Result := GetAttr(FNames.GetID(aName), aDefault) +end; + +function TXmlNode.GetAttr(aNameID: Integer; + const aDefault: TXmlString): TXmlString; +var + aData: PXmlAttrData; +begin + aData := FindAttrData(aNameID); + if Assigned(aData) then + Result := aData.Value + else + Result := aDefault +end; + +function TXmlNode.GetBoolAttr(aNameID: Integer; + aDefault: Boolean): Boolean; +var + aData: PXmlAttrData; +begin + aData := FindAttrData(aNameID); + if Assigned(aData) then + Result := aData.Value + else + Result := aDefault +end; + +function TXmlNode.GetBoolAttr(const aName: TXmlString; + aDefault: Boolean): Boolean; +begin + Result := GetBoolAttr(FNames.GetID(aName), aDefault) +end; + +function TXmlNode.FindFirstChild(aNameID: Integer): TXmlNode; +var + i: Integer; +begin + if Assigned(FChilds) then + for i := 0 to FChilds.FCount - 1 do begin + Result := FChilds.FItems[i]; + if Result.Get_NodeNameID = aNameID then + Exit + end; + Result := nil +end; + +function TXmlNode.GetChildText(aNameID: Integer; + const aDefault: TXmlString): TXmlString; +var + aChild: TXmlNode; +begin + aChild := FindFirstChild(aNameID); + if Assigned(aChild) then + Result := aChild.Get_Text + else + Result := aDefault +end; + +function TXmlNode.GetChildText(const aName: TXmlString; + const aDefault: TXmlString): TXmlString; +begin + Result := GetChildText(FNames.GetID(aName), aDefault); +end; + +function TXmlNode.GetEnumAttr(const aName: TXmlString; + const aValues: array of TXmlString; aDefault: Integer): Integer; +begin + Result := GetEnumAttr(FNames.GetID(aName), aValues, aDefault); +end; + +function EnumAttrValue(aNode: TXmlNode; anAttrData: PXmlAttrData; + const aValues: array of TXmlString): Integer; +var + anAttrValue: TXmlString; + s: String; + i: Integer; +begin + anAttrValue := anAttrData.Value; + for Result := 0 to Length(aValues) - 1 do + if AnsiCompareText(anAttrValue, aValues[Result]) = 0 then + Exit; + if Length(aValues) = 0 then + s := '' + else begin + s := aValues[0]; + for i := 1 to Length(aValues) - 1 do + s := s + ^M^J + aValues[i]; + end; + raise Exception.CreateFmt(SSimpleXmlError6, + [aNode.FNames.GetName(anAttrData.NameID), aNode.Get_NodeName, s]); +end; + +function TXmlNode.GetEnumAttr(aNameID: Integer; + const aValues: array of TXmlString; aDefault: Integer): Integer; +var + anAttrData: PXmlAttrData; +begin + anAttrData := FindAttrData(aNameID); + if Assigned(anAttrData) then + Result := EnumAttrValue(Self, anAttrData, aValues) + else + Result := aDefault; +end; + +function TXmlNode.NeedEnumAttr(const aName: TXmlString; + const aValues: array of TXmlString): Integer; +begin + Result := NeedEnumAttr(FNames.GetID(aName), aValues) +end; + +function TXmlNode.NeedEnumAttr(aNameID: Integer; + const aValues: array of TXmlString): Integer; +var + anAttrData: PXmlAttrData; +begin + anAttrData := FindAttrData(aNameID); + if Assigned(anAttrData) then + Result := EnumAttrValue(Self, anAttrData, aValues) + else + raise Exception.CreateFmt(SSimpleXMLError7, [FNames.GetName(aNameID)]); +end; + +function TXmlNode.GetFloatAttr(const aName: TXmlString; + aDefault: Double): Double; +begin + Result := GetFloatAttr(FNames.GetID(aName), aDefault); +end; + +function TXmlNode.GetFloatAttr(aNameID: Integer; + aDefault: Double): Double; +var + aData: PXmlAttrData; +begin + aData := FindAttrData(aNameID); + if Assigned(aData) then + if VarIsNumeric(aData.Value) then + Result := aData.Value + else + Result := XSTRToFloat(aData.Value) + else + Result := aDefault +end; + +function TXmlNode.GetHexAttr(aNameID, aDefault: Integer): Integer; +var + anAttr: PXmlAttrData; +begin + anAttr := FindAttrData(aNameID); + if Assigned(anAttr) then + Result := StrToInt('$' + anAttr.Value) + else + Result := aDefault; +end; + +function TXmlNode.GetHexAttr(const aName: TXmlString; + aDefault: Integer): Integer; +begin + Result := GetHexAttr(FNames.GetID(aName), aDefault) +end; + +function TXmlNode.GetIntAttr(aNameID, aDefault: Integer): Integer; +var + anAttr: PXmlAttrData; +begin + anAttr := FindAttrData(aNameID); + if Assigned(anAttr) then + Result := anAttr.Value + else + Result := aDefault; +end; + +function TXmlNode.GetIntAttr(const aName: TXmlString; + aDefault: Integer): Integer; +begin + Result := GetIntAttr(FNames.GetID(aName), aDefault) +end; + +function TXmlNode.NeedAttr(aNameID: Integer): TXmlString; +var + anAttr: PXmlAttrData; +begin + anAttr := FindAttrData(aNameID); + if not Assigned(anAttr) then + raise Exception.CreateFmt(SSimpleXmlError8, [FNames.GetName(aNameID)]); + Result := anAttr.Value +end; + +function TXmlNode.NeedAttr(const aName: TXmlString): TXmlString; +begin + Result := NeedAttr(FNames.GetID(aName)) +end; + +function TXmlNode.GetVarAttr(aNameID: Integer; + const aDefault: Variant): Variant; +var + anAttr: PXmlAttrData; +begin + anAttr := FindAttrData(aNameID); + if Assigned(anAttr) then + Result := anAttr.Value + else + Result := aDefault; +end; + +function TXmlNode.GetVarAttr(const aName: TXmlString; + const aDefault: Variant): Variant; +begin + Result := GetVarAttr(FNames.GetID(aName), aDefault) +end; + +function TXmlNode.Get_NodeName: TXmlString; +begin + Result := FNames.GetName(Get_NodeNameID); +end; + +function TXmlNode.GetOwnerDocument: TXmlDocument; +var + aResult: TXmlNode; +begin + aResult := Self; + repeat + if aResult is TXmlDocument then + break + else + aResult := aResult.FParentNode; + until not Assigned(aResult); + Result := TXmlDocument(aResult) +end; + +function TXmlNode.Get_OwnerDocument: IXmlDocument; +var + aDoc: TXmlDocument; +begin + aDoc := GetOwnerDocument; + if Assigned(aDoc) then + Result := aDoc + else + Result := nil; +end; + +function TXmlNode.Get_ParentNode: IXmlNode; +begin + Result := FParentNode +end; + +function TXmlNode.Get_TypedValue: Variant; +begin + Result := Get_Text +end; + +procedure TXmlNode.InsertBefore(const aChild, aBefore: IXmlNode); +var + i: Integer; + aChilds: TXmlNodeList; +begin + aChilds := GetChilds; + if Assigned(aBefore) then + i := aChilds.IndexOf(aBefore.GetObject as TXmlNode) + else + i := aChilds.FCount; + GetChilds.Insert(aChild.GetObject as TXmlNode, i) +end; + +procedure TXmlNode.RemoveAllAttrs; +begin + FAttrCount := 0; +end; + +procedure TXmlNode.RemoveAllChilds; +begin + if Assigned(FChilds) then + FChilds.Clear +end; + +procedure TXmlNode.RemoveAttr(const aName: TXmlString); +begin + RemoveAttr(FNames.GetID(aName)); +end; + +procedure TXmlNode.RemoveAttr(aNameID: Integer); +var + a1, a2: PXmlAttrData; + i: Integer; +begin + a1 := @FAttrs[0]; + i := 0; + while (i < FAttrCount) and (a1.NameID <> aNameID) do begin + Inc(a1); + Inc(i) + end; + if i < FAttrCount then begin + a2 := a1; + Inc(a2); + while i < FAttrCount - 1 do begin + a1^ := a2^; + Inc(a1); + Inc(a2); + Inc(i) + end; + VarClear(a1.Value); + Dec(FAttrCount); + end; +end; + +procedure TXmlNode.RemoveChild(const aChild: IXmlNode); +begin + GetChilds.Remove(aChild.GetObject as TXmlNode) +end; + +procedure TXmlNode.ReplaceChild(const aNewChild, anOldChild: IXmlNode); +var + i: Integer; + aChilds: TXmlNodeList; +begin + aChilds := GetChilds; + i := aChilds.IndexOf(anOldChild.GetObject as TXmlNode); + if i <> -1 then + aChilds.Replace(i, aNewChild.GetObject as TXmlNode) +end; + +function NameCanBeginWith(aChar: TXmlChar): Boolean; +begin + {$IFDEF XML_WIDE_CHARS} + Result := (aChar = '_') or IsCharAlphaW(aChar) + {$ELSE} + Result := (aChar = '_') or IsCharAlpha(aChar) + {$ENDIF} +end; + +function NameCanContain(aChar: TXmlChar): Boolean; +begin + {$IFDEF XML_WIDE_CHARS} + Result := (aChar = '_') or (aChar = '-') or (aChar = ':') or (aChar = '.') or + IsCharAlphaNumericW(aChar) + {$ELSE} + Result := (aChar in ['_', '-', ':', '.']) or IsCharAlphaNumeric(aChar) + {$ENDIF} +end; + +function IsName(const s: TXmlString): Boolean; +var + i: Integer; +begin + if s = '' then + Result := False + else if not NameCanBeginWith(s[1]) then + Result := False + else begin + for i := 2 to Length(s) do + if not NameCanContain(s[i]) then begin + Result := False; + Exit + end; + Result := True; + end; +end; + +const + ntComment = -2; + ntNode = -3; + ntProcessingInstruction = -4; + ntText = -5; + +type + TAxis = (axAncestor, axAncestorOrSelf, axAttribute, axChild, + axDescendant, axDescendantOrSelf, axFollowing, axFollowingSibling, + axParent, axPreceding, axPrecedingSibling, axSelf); + + TPredicate = class + function Check(aNode: TXmlNode): Boolean; virtual; abstract; + end; + + TLocationStep = class + Next: TLocationStep; + Axis: TAxis; + NodeTest: Integer; + Predicates: TList; + end; + + + +function TXmlNode.SelectNodes( + const anExpression: TXmlString): IXmlNodeList; +var + aNodes: TXmlNodeList; + aChilds: TXmlNodeList; + aChild: TXmlNode; + aNameID: Integer; + i: Integer; +{ + aPath: TXmlPath; +} +begin + if IsName(anExpression) then begin + aNodes := TXmlNodeList.Create(nil); + Result := aNodes; + aChilds := GetChilds; + aNameID := FNames.GetID(anExpression); + for i := 0 to aChilds.FCount - 1 do begin + aChild := aChilds.FItems[i]; + if (aChild.Get_NodeType = NODE_ELEMENT) and (aChild.Get_NodeNameID = aNameID) then + aNodes.Insert(aChild, aNodes.FCount); + end; + end + else begin + raise + Exception.Create(SSimpleXmlError9); +{ + aPath := TXmlPath.Create; + try + aPath.Init(anExpression); + Result := aPath.SelectNodes(Self); + finally + aPath.Free + end +} + end; +end; + +function TXmlNode.SelectSingleNode( + const anExpression: TXmlString): IXmlNode; +var + aChilds: TXmlNodeList; + aChild: TXmlNode; + aNameID: Integer; + i: Integer; +begin + if IsName(anExpression) then begin + aChilds := GetChilds; + aNameID := FNames.GetID(anExpression); + for i := 0 to aChilds.FCount - 1 do begin + aChild := aChilds.FItems[i]; + if (aChild.Get_NodeType = NODE_ELEMENT) and (aChild.Get_NodeNameID = aNameID) then begin + Result := aChild; + Exit + end + end; + Result := nil; + end + else begin + raise + Exception.Create(SSimpleXmlError9) + end +end; + +function TXmlNode.FindElement(const anElementName, anAttrName: String; + const anAttrValue: Variant): IXmlElement; +var + aChild: TXmlNode; + aNameID, anAttrNameID: Integer; + i: Integer; + pa: PXmlAttrData; +begin + if Assigned(FChilds) then begin + aNameID := FNames.GetID(anElementName); + anAttrNameID := FNames.GetID(anAttrName); + + for i := 0 to FChilds.FCount - 1 do begin + aChild := FChilds.FItems[i]; + if (aChild.Get_NodeType = NODE_ELEMENT) and (aChild.Get_NodeNameID = aNameID) then begin + pa := aChild.FindAttrData(anAttrNameID); + try + if Assigned(pa) and VarSameValue(pa.Value, anAttrValue) then begin + Result := aChild.AsElement; + Exit + end + except + // Èñêëþ÷èòåëüíàÿ ñèòóàöèÿ ìîæåò âîçíèêíóòü â òîì ñëó÷àå, + // åñëè ïðîèçîéäåò ñáîé â ôóíêöèè VarSameValue. + // Èíûìè ñëîâàìè - åñëè çíà÷åíèÿ íåëüçÿ ñðàâíèâàòü. + end; + end + end; + end; + Result := nil; +end; + +procedure TXmlNode.Set_TypedValue(const aValue: Variant); +begin + Set_Text(aValue) +end; + +procedure TXmlNode.SetAttr(const aName, aValue: TXmlString); +begin + SetVarAttr(FNames.GetID(aName), aValue) +end; + +procedure TXmlNode.SetAttr(aNameID: Integer; const aValue: TXmlString); +begin + SetVarAttr(aNameID, aValue) +end; + +procedure TXmlNode.SetBoolAttr(aNameID: Integer; aValue: Boolean); +begin + SetVarAttr(aNameID, aValue) +end; + +procedure TXmlNode.SetBoolAttr(const aName: TXmlString; aValue: Boolean); +begin + SetVarAttr(FNames.GetID(aName), aValue) +end; + +procedure TXmlNode.SetChildText(const aName: TXmlString; + const aValue: TXmlString); +begin + SetChildText(FNames.GetID(aName), aValue) +end; + +procedure TXmlNode.SetChildText(aNameID: Integer; const aValue: TXmlString); +var + aChild: TXmlNode; +begin + aChild := FindFirstChild(aNameID); + if not Assigned(aChild) then begin + aChild := TXmlElement.Create(FNames, aNameID); + with GetChilds do + Insert(aChild, FCount); + end; + aChild.Set_Text(aValue) +end; + +procedure TXmlNode.SetFloatAttr(aNameID: Integer; aValue: Double); +begin + SetVarAttr(aNameID, aValue) +end; + +procedure TXmlNode.SetFloatAttr(const aName: TXmlString; aValue: Double); +begin + SetVarAttr(FNames.GetID(aName), aValue); +end; + +procedure TXmlNode.SetHexAttr(const aName: TXmlString; aValue, + aDigits: Integer); +begin + SetVarAttr(FNames.GetID(aName), IntToHex(aValue, aDigits)) +end; + +procedure TXmlNode.SetHexAttr(aNameID, aValue, aDigits: Integer); +begin + SetVarAttr(aNameID, IntToHex(aValue, aDigits)) +end; + +procedure TXmlNode.SetIntAttr(aNameID, aValue: Integer); +begin + SetVarAttr(aNameID, aValue) +end; + +procedure TXmlNode.SetIntAttr(const aName: TXmlString; aValue: Integer); +begin + SetVarAttr(FNames.GetID(aName), aValue) +end; + +procedure TXmlNode.SetVarAttr(const aName: TXmlString; aValue: Variant); +begin + SetVarAttr(FNames.GetID(aName), aValue) +end; + +procedure TXmlNode.SetVarAttr(aNameID: Integer; const aValue: Variant); +var + anAttr: PXmlAttrData; +var + aDelta: Integer; +begin + anAttr := FindAttrData(aNameID); + if not Assigned(anAttr) then begin + if FAttrCount = Length(FAttrs) then begin + if FAttrCount > 64 then + aDelta := FAttrCount div 4 + else if FAttrCount > 8 then + aDelta := 16 + else + aDelta := 4; + SetLength(FAttrs, FAttrCount + aDelta); + end; + anAttr := @FAttrs[FAttrCount]; + anAttr.NameID := aNameID; + Inc(FAttrCount); + end; + anAttr.Value := aValue +end; + +function TXmlNode.FindAttrData(aNameID: Integer): PXmlAttrData; +var + i: Integer; +begin + Result := @FAttrs[0]; + for i := 0 to FAttrCount - 1 do + if Result.NameID = aNameID then + Exit + else + Inc(Result); + Result := nil; +end; + +function TXmlNode.AsElement: IXmlElement; +begin + Result := nil +end; + +function TXmlNode.AsCDATASection: IXmlCDATASection; +begin + Result := nil +end; + +function TXmlNode.AsComment: IXmlComment; +begin + Result := nil +end; + +function TXmlNode.AsText: IXmlText; +begin + Result := nil +end; + +function TXmlNode.AsProcessingInstruction: IXmlProcessingInstruction; +begin + Result := nil +end; + +function TXmlNode.AppendCDATA(const aData: TXmlString): IXmlCDATASection; +var + aChild: TXmlCDATASection; +begin + aChild := TXmlCDATASection.Create(FNames, aData); + GetChilds.Insert(aChild, -1); + Result := aChild +end; + +function TXmlNode.AppendComment(const aData: TXmlString): IXmlComment; +var + aChild: TXmlComment; +begin + aChild := TXmlComment.Create(FNames, aData); + GetChilds.Insert(aChild, -1); + Result := aChild +end; + +function TXmlNode.AppendElement(const aName: TxmlString): IXmlElement; +var + aChild: TXmlElement; +begin + aChild := TXmlElement.Create(FNames, FNames.GetID(aName)); + GetChilds.Insert(aChild, -1); + Result := aChild +end; + +function TXmlNode.AppendElement(aNameID: Integer): IXmlElement; +var + aChild: TXmlElement; +begin + aChild := TXmlElement.Create(FNames, aNameID); + GetChilds.Insert(aChild, -1); + Result := aChild +end; + +function TXmlNode.AppendProcessingInstruction(const aTarget, + aData: TXmlString): IXmlProcessingInstruction; +var + aChild: TXmlProcessingInstruction; +begin + aChild := TXmlProcessingInstruction.Create(FNames, FNames.GetID(aTarget), aData); + GetChilds.Insert(aChild, -1); + Result := aChild +end; + +function TXmlNode.AppendProcessingInstruction(aTargetID: Integer; + const aData: TXmlString): IXmlProcessingInstruction; +var + aChild: TXmlProcessingInstruction; +begin + aChild := TXmlProcessingInstruction.Create(FNames, aTargetID, aData); + GetChilds.Insert(aChild, -1); + Result := aChild +end; + +function TXmlNode.AppendText(const aData: TXmlString): IXmlText; +var + aChild: TXmlText; +begin + aChild := TXmlText.Create(FNames, aData); + GetChilds.Insert(aChild, -1); + Result := aChild +end; + +function TXmlNode.GetAttrsXML: TXmlString; +var + a: PXmlAttrData; + i: Integer; +begin + Result := ''; + if FAttrCount > 0 then begin + a := @FAttrs[0]; + for i := 0 to FAttrCount - 1 do begin + Result := Result + ' ' + FNames.GetName(a.NameID) + '="' + TextToXML(VarToXSTR(TVarData(a.Value))) + '"'; + Inc(a); + end; + end; +end; + +procedure TXmlNode.LoadBinXml(aReader: TBinXmlReader); +var + aCount: LongInt; + a: PXmlAttrData; + i: Integer; +begin + // Ñ÷èòàòü àòðèáóòû + RemoveAllAttrs; + aCount := aReader.ReadLongint; + SetLength(FAttrs, aCount); + FAttrCount := aCount; + a := @FAttrs[0]; + for i := 0 to aCount - 1 do begin + a.NameID := aReader.ReadLongint; + aReader.ReadVariant(TVarData(a.Value)); + Inc(a); + end; + + // Ñ÷èòàòü äî÷åðíèå óçëû + aCount := aReader.ReadLongint; + if aCount > 0 then + GetChilds.LoadBinXml(aReader, aCount, FNames); +end; + +procedure TXmlNode.SaveBinXml(aWriter: TBinXmlWriter); +var + aCount: LongInt; + a: PXmlAttrData; + i: Integer; +begin + // Ñ÷èòàòü àòðèáóòû + aCount := FAttrCount; + aWriter.WriteLongint(aCount); + a := @FAttrs[0]; + for i := 0 to aCount - 1 do begin + aWriter.WriteLongint(a.NameID); + aWriter.WriteVariant(TVarData(a.Value)); + Inc(a); + end; + + // Çàïèñàòü äî÷åðíèå óçëû + if Assigned(FChilds) then begin + aWriter.WriteLongint(FChilds.FCount); + FChilds.SaveBinXml(aWriter); + end + else + aWriter.WriteLongint(0); +end; + +function TXmlNode.Get_DataType: Integer; +begin + {$IFDEF XML_WIDE_CHARS} + Result := varOleStr + {$ELSE} + Result := varString + {$ENDIF} +end; + +function TXmlNode.AttrExists(aNameID: Integer): Boolean; +begin + Result := FindAttrData(aNameID) <> nil +end; + +function TXmlNode.AttrExists(const aName: TXmlString): Boolean; +begin + Result := FindAttrData(FNames.GetID(aName)) <> nil +end; + +function TXmlNode.GetAttrType(aNameID: Integer): Integer; +var + a: PXmlAttrData; +begin + a := FindAttrData(aNameID); + if Assigned(a) then + Result := TVarData(a.Value).VType + else + {$IFDEF XML_WIDE_CHARS} + Result := varOleStr + {$ELSE} + Result := varString + {$ENDIF} +end; + +function TXmlNode.GetAttrType(const aName: TXmlString): Integer; +begin + Result := GetAttrType(FNames.GetID(aName)); +end; + +function TXmlNode.Get_Values(const aName: String): Variant; +var + aChild: IXmlNode; +begin + if aName = '' then + Result := Get_TypedValue + else if aName[1] = '@' then + Result := GetVarAttr(Copy(aName, 2, Length(aName) - 1), '') + else begin + aChild := SelectSingleNode(aName); + if Assigned(aChild) then + Result := aChild.TypedValue + else + Result := '' + end +end; + +procedure TXmlNode.Set_Values(const aName: String; const aValue: Variant); +var + aChild: IXmlNode; +begin + if aName = '' then + Set_TypedValue(aValue) + else if aName[1] = '@' then + SetVarAttr(Copy(aName, 2, Length(aName) - 1), aValue) + else begin + aChild := SelectSingleNode(aName); + if not Assigned(aChild) then + aChild := AppendElement(aName); + aChild.TypedValue := aValue; + end +end; + +function TXmlNode.GetDateTimeAttr(aNameID: Integer; + aDefault: TDateTime): TDateTime; +var + anAttr: PXmlAttrData; +begin + anAttr := FindAttrData(aNameID); + if Assigned(anAttr) then begin + if (VarType(anAttr.Value) = varString) or (VarType(anAttr.Value) = varOleStr) then + Result := XSTRToDateTime(anAttr.Value) + else + Result := VarAsType(anAttr.Value, varDate) + end + else + Result := aDefault; +end; + +function TXmlNode.GetDateTimeAttr(const aName: TXmlString; + aDefault: TDateTime): TDateTime; +begin + Result := GetDateTimeAttr(FNames.GetID(aName), aDefault) +end; + +procedure TXmlNode.SetDateTimeAttr(aNameID: Integer; aValue: TDateTime); +begin + SetVarAttr(aNameID, VarAsType(aValue, varDate)) +end; + +procedure TXmlNode.SetDateTimeAttr(const aName: TXmlString; + aValue: TDateTime); +begin + SetVarAttr(aName, VarAsType(aValue, varDate)) +end; + +function TXmlNode.EnsureChild(aNameID: Integer): IXmlNode; +var + aChild: TXmlNode; +begin + aChild := FindFirstChild(aNameID); + if Assigned(aChild) then + Result := aChild + else + Result := AppendElement(aNameID) +end; + +function TXmlNode.EnsureChild(const aName: TXmlString): IXmlNode; +begin + Result := EnsureChild(FNames.GetID(aName)) +end; + +function TXmlNode.NeedChild(aNameID: Integer): IXmlNode; +var + aChild: TXmlNode; +begin + aChild := FindFirstChild(aNameID); + if not Assigned(aChild) then + raise Exception.CreateFmt(SSimpleXmlError10, [FNames.GetName(aNameID)]); + Result := aChild +end; + +function TXmlNode.NeedChild(const aName: TXmlString): IXmlNode; +begin + Result := NeedChild(FNames.GetID(aName)); +end; + +procedure TXmlNode.SetNameTable(aValue: TXmlNameTable; aMap: TList); +var + i: Integer; + aNewMap: Boolean; +begin + if aValue <> FNames then begin + aNewMap := not Assigned(aMap); + if aNewMap then begin + aMap := TList.Create; + for i := 0 to Length(FNames.FNames) do + aMap.Add(Pointer(aValue.GetID(FNames.FNames[i]))); + end; + try + SetNodeNameID(Integer(aMap[Get_NodeNameID])); + for i := 0 to Length(FAttrs) do + with FAttrs[i] do + NameID := Integer(aMap[NameID]); + if Assigned(FChilds) then + for i := 0 to FChilds.FCount - 1 do + FChilds.FItems[i].SetNameTable(aValue, aMap); + finally + if aNewMap then + aMap.Free + end; + end; +end; + +procedure TXmlNode.SetNodeNameID(aValue: Integer); +begin + +end; + + +function TXmlNode.CloneNode(aDeep: Boolean): IXmlNode; +begin + Result := DoCloneNode(aDeep) +end; + +{ TXmlElement } + +constructor TXmlElement.Create(aNames: TXmlNameTable; aNameID: Integer); +begin + inherited Create(aNames); + FNameID := aNameID; +end; + +function TXmlElement.Get_NodeNameID: Integer; +begin + Result := FNameID +end; + +function TXmlElement.Get_NodeType: Integer; +begin + Result := NODE_ELEMENT +end; + +function TXmlElement.GetChilds: TXmlNodeList; +begin + Result := inherited GetChilds; + if not TVarData(FData).VType in [varEmpty, varNull] then begin + AppendChild(TXmlText.Create(FNames, FData)); + VarClear(FData); + end; +end; + +function TXmlElement.Get_Text: TXmlString; +var + aChilds: TXmlNodeList; + aChild: TXmlNode; + aChildText: TXmlString; + i: Integer; +begin + Result := ''; + if Assigned(FChilds) and (FChilds.FCount > 0) then begin + aChilds := FChilds; + for i := 0 to aChilds.FCount - 1 do begin + aChild := aChilds.FItems[i]; + if aChild.Get_NodeType in [NODE_ELEMENT, NODE_TEXT, NODE_CDATA_SECTION] then begin + aChildText := aChild.Get_Text; + if aChildText <> '' then + if Result = '' then + Result := aChildText + else + Result := Result + ' ' + aChildText + end + end; + end + else if VarIsEmpty(FData) then + Result := '' + else + Result := VarToXSTR(TVarData(FData)) +end; + +function TXmlElement.GetTextAsBynaryData: TXmlString; +begin + Result := Base64ToBin(Get_Text); +end; + +procedure TXmlElement.ReplaceTextByBynaryData(const aData; aSize: Integer; + aMaxLineLength: Integer); +begin + RemoveTextNodes; + GetChilds.Insert(TXmlText.Create(FNames, BinToBase64(aData, aSize, aMaxLineLength)), -1); +end; + +procedure TXmlElement.RemoveTextNodes; +var + i: Integer; + aNode: TXmlNode; +begin + if Assigned(FChilds) then + for i := FChilds.FCount - 1 downto 0 do begin + aNode := FChilds.FItems[i]; + if aNode.Get_NodeType in [NODE_TEXT, NODE_CDATA_SECTION] then + FChilds.Remove(aNode); + end; +end; + +procedure TXmlElement.ReplaceTextByCDATASection(const aText: TXmlString); + + procedure AddCDATASection(const aText: TXmlString); + var + i: Integer; + aChilds: TXmlNodeList; + begin + i := Pos(']]>', aText); + aChilds := GetChilds; + if i = 0 then + aChilds.Insert(TXmlCDATASection.Create(FNames, aText), aChilds.FCount) + else begin + aChilds.Insert(TXmlCDATASection.Create(FNames, Copy(aText, 1, i)), aChilds.FCount); + AddCDATASection(Copy(aText, i + 1, Length(aText) - i - 1)); + end; + end; + +begin + RemoveTextNodes; + AddCDATASection(aText); +end; + +procedure TXmlElement.Set_Text(const aValue: TXmlString); +begin + if Assigned(FChilds) then + FChilds.Clear; + FData := aValue; +end; + +function TXmlElement.AsElement: IXmlElement; +begin + Result := Self +end; + +var + FGetXMLIntend: Integer = 0; + +function GetIndentStr: String; +var + i: Integer; +begin + SetLength(Result, FGetXMLIntend*Length(DefaultIndentText)); + for i := 0 to FGetXMLIntend - 1 do + Move(DefaultIndentText[1], Result[i*Length(DefaultIndentText) + 1], Length(DefaultIndentText)); +end; + +function HasCRLF(const s: String): Boolean; +var + i: Integer; +begin + for i := 1 to Length(s) do + if (s[i] = ^M) or (s[i] = ^J) then begin + Result := True; + Exit + end; + Result := False; +end; + +function EndWithCRLF(const s: String): Boolean; +begin + Result := + (Length(s) > 1) and + (s[Length(s) - 1] = ^M) and + (s[Length(s)] = ^J); +end; + +function TXmlElement.Get_XML: TXmlString; +var + aChildsXML: TXmlString; + aTag: TXmlString; + aDoc: TXmlDocument; + aPreserveWhiteSpace: Boolean; +begin + aDoc := GetOwnerDocument; + if Assigned(aDoc) then + aPreserveWhiteSpace := aDoc.Get_PreserveWhiteSpace + else + aPreserveWhiteSpace := DefaultPreserveWhiteSpace; + if aPreserveWhiteSpace then begin + if Assigned(FChilds) and (FChilds.FCount > 0) then + aChildsXML := FChilds.Get_XML + else if VarIsEmpty(FData) then + aChildsXML := '' + else + aChildsXML := TextToXML(VarToXSTR(TVarData(FData))); + + aTag := FNames.GetName(FNameID); + Result := '<' + aTag + GetAttrsXML; + if aChildsXML = '' then + Result := Result + '/>' + else + Result := Result + '>' + aChildsXML + '' + end + else begin + if Assigned(FChilds) and (FChilds.FCount > 0) then begin + Inc(FGetXMLIntend); + try + aChildsXML := FChilds.Get_XML + finally + Dec(FGetXMLIntend) + end + end + else if VarIsEmpty(FData) then + aChildsXML := '' + else + aChildsXML := TextToXML(VarToXSTR(TVarData(FData))); + aTag := FNames.GetName(FNameID); + Result := ^M^J+GetIndentStr + '<' + aTag + GetAttrsXML; + if aChildsXML = '' then + Result := Result + '/>' + else if HasCRLF(aChildsXML) then + if EndWithCRLF(aChildsXML) then + Result := Result + '>' + aChildsXML + GetIndentStr + '' + else + Result := Result + '>' + aChildsXML + ^M^J + GetIndentStr + '' + else + Result := Result + '>' + aChildsXML + ''; + end; +end; + +function TXmlElement.Get_TypedValue: Variant; +begin + if Assigned(FChilds) and (FChilds.FCount > 0) then + Result := Get_Text + else + Result := FData +end; + +procedure TXmlElement.Set_TypedValue(const aValue: Variant); +begin + if Assigned(FChilds) then + FChilds.Clear; + FData := aValue; +end; + +function TXmlElement.Get_DataType: Integer; +begin + if (Assigned(FChilds) and (FChilds.FCount > 0)) or VarIsEmpty(FData) then + {$IFDEF XML_WIDE_CHARS} + Result := varOleStr + {$ELSE} + Result := varString + {$ENDIF} + else + Result := TVarData(FData).VType; +end; + +function TXmlElement.Get_ChildNodes: IXmlNodeList; +begin + Result := inherited Get_ChildNodes; +end; + +procedure TXmlElement.SetNodeNameID(aValue: Integer); +begin + FNameID := aValue +end; + +function TXmlElement.DoCloneNode(aDeep: Boolean): IXmlNode; +var + aClone: TXmlElement; + i: Integer; +begin + aClone := TXmlElement.Create(FNames, FNameID); + Result := aClone; + SetLength(aClone.FAttrs, FAttrCount); + aClone.FAttrCount := FAttrCount; + for i := 0 to FAttrCount - 1 do + aClone.FAttrs[i] := FAttrs[i]; + if aDeep and Assigned(FChilds) and (FChilds.FCount > 0) then + for i := 0 to FChilds.FCount - 1 do + aClone.AppendChild(FChilds.FItems[i].CloneNode(True)); +end; + +{ TXmlCharacterData } + +constructor TXmlCharacterData.Create(aNames: TXmlNameTable; + const aData: TXmlString); +begin + inherited Create(aNames); + FData := aData; +end; + +function TXmlCharacterData.Get_Text: TXmlString; +var + aDoc: TXmlDocument; + aPreserveWhiteSpace: Boolean; +begin + aDoc := GetOwnerDocument; + if Assigned(aDoc) then + aPreserveWhiteSpace := aDoc.Get_PreserveWhiteSpace + else + aPreserveWhiteSpace := DefaultPreserveWhiteSpace; + if aPreserveWhiteSpace then + Result := FData + else + Result := Trim(FData); +end; + +procedure TXmlCharacterData.Set_Text(const aValue: TXmlString); +begin + FData := aValue +end; + +{ TXmlText } + +function TXmlText.AsText: IXmlText; +begin + Result := Self; +end; + +constructor TXmlText.Create(aNames: TXmlNameTable; const aData: Variant); +begin + inherited Create(aNames); + FData := aData; +end; + +function TXmlText.DoCloneNode(aDeep: Boolean): IXmlNode; +begin + Result := TXmlText.Create(FNames, FData); +end; + +function TXmlText.Get_DataType: Integer; +begin + Result := TVarData(FData).VType +end; + +function TXmlText.Get_NodeNameID: Integer; +begin + Result := FNames.FXmlTextNameID +end; + +function TXmlText.Get_NodeType: Integer; +begin + Result := NODE_TEXT +end; + +function TXmlText.Get_Text: TXmlString; +begin + Result := VarToXSTR(TVarData(FData)) +end; + +function TXmlText.Get_TypedValue: Variant; +begin + Result := FData +end; + +function TXmlText.Get_XML: TXmlString; +begin + Result := TextToXML(VarToXSTR(TVarData(FData))); +end; + +procedure TXmlText.Set_Text(const aValue: TXmlString); +begin + FData := aValue +end; + +procedure TXmlText.Set_TypedValue(const aValue: Variant); +begin + FData := aValue +end; + +{ TXmlCDATASection } + +function TXmlCDATASection.AsCDATASection: IXmlCDATASection; +begin + Result := Self +end; + +function TXmlCDATASection.DoCloneNode(aDeep: Boolean): IXmlNode; +begin + Result := TXmlCDATASection.Create(FNames, FData); +end; + +function TXmlCDATASection.Get_NodeNameID: Integer; +begin + Result := FNames.FXmlCDATASectionNameID +end; + +function TXmlCDATASection.Get_NodeType: Integer; +begin + Result := NODE_CDATA_SECTION +end; + +function GenCDATAXML(const aValue: TXmlString): TXmlString; +var + i: Integer; +begin + i := Pos(']]>', aValue); + if i = 0 then + Result := '' + else + Result := '' + GenCDATAXML(Copy(aValue, i + 1, Length(aValue) - i - 1)); +end; + +function TXmlCDATASection.Get_XML: TXmlString; +begin + Result := GenCDATAXML(FData); +end; + +{ TXmlComment } + +function TXmlComment.AsComment: IXmlComment; +begin + Result := Self +end; + +function TXmlComment.DoCloneNode(aDeep: Boolean): IXmlNode; +begin + Result := TXmlComment.Create(FNames, FData); +end; + +function TXmlComment.Get_NodeNameID: Integer; +begin + Result := FNames.FXmlCommentNameID +end; + +function TXmlComment.Get_NodeType: Integer; +begin + Result := NODE_COMMENT +end; + +function TXmlComment.Get_XML: TXmlString; +begin + Result := '' +end; + +{ TXmlDocument } + +constructor TXmlDocument.Create(aNames: TXmlNameTable); +begin + inherited Create(aNames); + FPreserveWhiteSpace := DefaultPreserveWhiteSpace; +end; + +function TXmlDocument.CreateCDATASection( + const aData: TXmlString): IXmlCDATASection; +begin + Result := TXmlCDATASection.Create(FNames, aData) +end; + +function TXmlDocument.CreateComment(const aData: TXmlString): IXmlComment; +begin + Result := TXmlComment.Create(FNames, aData) +end; + +function TXmlDocument.CreateElement(aNameID: Integer): IXmlElement; +begin + Result := TXmlElement.Create(FNames, aNameID) +end; + +function TXmlDocument.CreateElement(const aName: TXmlString): IXmlElement; +begin + Result := TXmlElement.Create(FNames, FNames.GetID(aName)); +end; + +function TXmlDocument.CreateProcessingInstruction(const aTarget, + aData: TXmlString): IXmlProcessingInstruction; +begin + Result := TXmlProcessingInstruction.Create(FNames, FNames.GetID(aTarget), aData) +end; + +function TXmlDocument.CreateProcessingInstruction(aTargetID: Integer; + const aData: TXmlString): IXmlProcessingInstruction; +begin + Result := TXmlProcessingInstruction.Create(FNames, aTargetID, aData) +end; + +function TXmlDocument.CreateText(const aData: TXmlString): IXmlText; +begin + Result := TXmlText.Create(FNames, aData) +end; + +function TXmlDocument.DoCloneNode(aDeep: Boolean): IXmlNode; +var + aClone: TXmlDocument; + i: Integer; +begin + aClone := TXmlDocument.Create(FNames); + Result := aClone; + if aDeep and Assigned(FChilds) and (FChilds.FCount > 0) then + for i := 0 to FChilds.FCount - 1 do + aClone.AppendChild(FChilds.FItems[i].CloneNode(True)); +end; + +function TXmlDocument.Get_BinaryXML: String; +var + aWriter: TStrXmlWriter; +begin + aWriter := TStrXmlWriter.Create(0, $10000); + try + FNames.SaveBinXml(aWriter); + SaveBinXml(aWriter); + aWriter.FlushBuf; + Result := aWriter.FData; + finally + aWriter.Free + end +end; + +function TXmlDocument.Get_DocumentElement: IXmlElement; +var + aChilds: TXmlNodeList; + aChild: TXmlNode; + i: Integer; +begin + aChilds := GetChilds; + for i := 0 to aChilds.FCount - 1 do begin + aChild := aChilds.FItems[i]; + if aChild.Get_NodeType = NODE_ELEMENT then begin + Result := aChild.AsElement; + Exit + end + end; + Result := nil; +end; + +function TXmlDocument.Get_NodeNameID: Integer; +begin + Result := FNames.FXmlDocumentNameID +end; + +function TXmlDocument.Get_NodeType: Integer; +begin + Result := NODE_DOCUMENT +end; + +function TXmlDocument.Get_PreserveWhiteSpace: Boolean; +begin + Result := FPreserveWhiteSpace; +end; + +function TXmlDocument.Get_Text: TXmlString; +var + aChilds: TXmlNodeList; + aChild: TXmlNode; + aChildText: TXmlString; + i: Integer; +begin + Result := ''; + aChilds := GetChilds; + for i := 0 to aChilds.FCount - 1 do begin + aChild := aChilds.FItems[i]; + if aChild.Get_NodeType in [NODE_ELEMENT, NODE_TEXT, NODE_CDATA_SECTION] then begin + aChildText := aChild.Get_Text; + if aChildText <> '' then + if Result = '' then + Result := aChildText + else + Result := Result + ' ' + aChildText + end + end; +end; + +function TXmlDocument.Get_XML: TXmlString; +begin + Result := GetChilds.Get_XML +end; + +procedure TXmlDocument.Load(aStream: TStream); +var + aXml: TXmlStmSource; + aBinarySign: String; + aReader: TBinXmlReader; +begin + RemoveAllChilds; + RemoveAllAttrs; + if aStream.Size > BinXmlSignatureSize then begin + SetLength(aBinarySign, BinXmlSignatureSize); + aStream.ReadBuffer(aBinarySign[1], BinXmlSignatureSize); + if aBinarySign = BinXmlSignature then begin + FNames._Release; + FNames := TXmlNameTable.Create(4096); + FNames._AddRef; + aReader := TStmXmlReader.Create(aStream, $10000); + try + FNames.LoadBinXml(aReader); + LoadBinXml(aReader); + finally + aReader.Free + end; + Exit; + end; + aStream.Position := aStream.Position - BinXmlSignatureSize; + end; + aXml := TXmlStmSource.Create(aStream, 1 shl 16); + try + GetChilds.ParseXML(aXml, FNames, FPreserveWhiteSpace); + finally + aXml.Free + end +end; + +procedure TXmlDocument.Load(const aFileName: TXmlString); +var + aFile: TFileStream; +begin + aFile := TFileStream.Create(aFileName, fmOpenRead, fmShareDenyWrite); + try + Load(aFile); + finally + aFile.Free + end +end; + +procedure TXmlDocument.LoadBinaryXML(const aXML: String); +var + aReader: TStrXmlReader; +begin + RemoveAllChilds; + RemoveAllAttrs; + aReader := TStrXmlReader.Create(aXML); + try + FNames._Release; + FNames := TXmlNameTable.Create(4096); + FNames._AddRef; + FNames.LoadBinXml(aReader); + LoadBinXml(aReader); + finally + aReader.Free + end +end; + +procedure TXmlDocument.LoadResource(aType, aName: PChar); +var + aRSRC: HRSRC; + aGlobal: HGLOBAL; + aSize: DWORD; + aPointer: Pointer; + aStream: TStringStream; +begin + aRSRC := FindResource(HInstance, aName, aType); + if aRSRC <> 0 then begin + aGlobal := Windows.LoadResource(HInstance, aRSRC); + aSize := SizeofResource(HInstance, aRSRC); + if (aGlobal <> 0) and (aSize <> 0) then begin + aPointer := LockResource(aGlobal); + if Assigned(aPointer) then begin + aStream := TStringStream.Create(''); + try + aStream.WriteBuffer(aPointer^, aSize); + LoadXML(aStream.DataString); + finally + aStream.Free; + end; + end; + end; + end; +end; + +procedure TXmlDocument.LoadXML(const aXML: TXmlString); +var + aSource: TXmlStrSource; +begin + if XmlIsInBinaryFormat(aXML) then + LoadBinaryXML(aXML) + else begin + RemoveAllChilds; + RemoveAllAttrs; + aSource := TXmlStrSource.Create(aXML); + try + GetChilds.ParseXML(aSource, FNames, FPreserveWhiteSpace); + finally + aSource.Free + end + end +end; + +function TXmlDocument.NewDocument(const aVersion, anEncoding, + aRootElementName: TXmlString): IXmlElement; +begin + Result := NewDocument(aVersion, anEncoding, FNames.GetID(aRootElementName)); +end; + +function TXmlDocument.NewDocument(const aVersion, anEncoding: TXmlString; + aRootElementNameID: Integer): IXmlElement; +var + aChilds: TXmlNodeList; + aRoot: TXmlElement; + e: String; +begin + aChilds := GetChilds; + aChilds.Clear; + if anEncoding = '' then + e := DefaultEncoding + else + e := anEncoding; + aChilds.Insert(TXmlProcessingInstruction.Create(FNames, FNames.FXmlID, + 'version="' + aVersion + '" encoding="' + e + '"'), 0); + aRoot := TXmlElement.Create(FNames, aRootElementNameID); + aChilds.Insert(aRoot, 1); + Result := aRoot; +end; + +procedure TXmlDocument.Save(aStream: TStream); +var + aXml: TXmlString; +begin + aXml := Get_XML; + aStream.WriteBuffer(aXml[1], sizeof(TXmlChar)*Length(aXml)); +end; + +procedure TXmlDocument.Save(const aFileName: TXmlString); +var + aFile: TFileStream; +begin + aFile := TFileStream.Create(aFileName, fmCreate, fmShareDenyWrite); + try + Save(aFile); + finally + aFile.Free + end +end; + +procedure TXmlDocument.SaveBinary(aStream: TStream; anOptions: LongWord); +var + aWriter: TBinXmlWriter; +begin + aWriter := TStmXmlWriter.Create(aStream, anOptions, 65536); + try + FNames.SaveBinXml(aWriter); + SaveBinXml(aWriter); + finally + aWriter.Free + end +end; + +procedure TXmlDocument.SaveBinary(const aFileName: TXmlString; anOptions: LongWord); +var + aFile: TFileStream; +begin + aFile := TFileStream.Create(aFileName, fmCreate, fmShareDenyWrite); + try + SaveBinary(aFile, anOptions); + finally + aFile.Free + end +end; + +procedure TXmlDocument.Set_PreserveWhiteSpace(aValue: Boolean); +begin + FPreserveWhiteSpace := aValue; +end; + +procedure TXmlDocument.Set_Text(const aText: TXmlString); +var + aChilds: TXmlNodeList; +begin + aChilds := GetChilds; + aChilds.Clear; + aChilds.Insert(TXmlText.Create(FNames, aText), 0); +end; + +{ TXmlProcessingInstruction } + +function TXmlProcessingInstruction.AsProcessingInstruction: IXmlProcessingInstruction; +begin + Result := Self +end; + +constructor TXmlProcessingInstruction.Create(aNames: TXmlNameTable; + aTargetID: Integer; const aData: TXmlString); +begin + inherited Create(aNames); + FTargetID := aTargetID; + FData := aData; +end; + +function TXmlProcessingInstruction.DoCloneNode(aDeep: Boolean): IXmlNode; +begin + Result := TXmlProcessingInstruction.Create(FNames, FTargetID, FData); +end; + +function TXmlProcessingInstruction.Get_NodeNameID: Integer; +begin + Result := FTargetID +end; + +function TXmlProcessingInstruction.Get_NodeType: Integer; +begin + Result := NODE_PROCESSING_INSTRUCTION +end; + +function TXmlProcessingInstruction.Get_Text: TXmlString; +begin + Result := FData; +end; + +function TXmlProcessingInstruction.Get_XML: TXmlString; +begin + if FData = '' then + Result := '' + else + Result := '' +end; + +procedure TXmlProcessingInstruction.SetNodeNameID(aValue: Integer); +begin + FTargetID := aValue +end; + +procedure TXmlProcessingInstruction.Set_Text(const aText: TXmlString); +begin + FData := aText +end; + +{ TXmlStrSource } + +constructor TXmlStrSource.Create(const aSource: TXmlString); +begin + inherited Create; + FSource := aSource; + FSourcePtr := PXmlChar(FSource); + FSourceEnd := FSourcePtr + Length(FSource); + if FSourcePtr = FSourceEnd then + CurChar := #0 + else + CurChar := FSourcePtr^; +end; + +function TXmlStrSource.EOF: Boolean; +begin + Result := FSourcePtr = FSourceEnd +end; + +function TXmlStrSource.Next: Boolean; +begin + if FSourcePtr < FSourceEnd then + Inc(FSourcePtr); + if FSourcePtr = FSourceEnd then begin + Result := False; + CurChar := #0; + end + else begin + Result := True; + CurChar := FSourcePtr^; + end; +end; + +{ TXmlSource } + +procedure TXmlSource.NewToken; +begin + Inc(FTokenStackTop); + if FTokenStackTop < Length(FTokenStack) then begin + FToken := FTokenStack[FTokenStackTop]; + FToken.Clear + end + else begin + SetLength(FTokenStack, FTokenStackTop + 1); + FToken := TXmlToken.Create; + FTokenStack[FTokenStackTop] := FToken; + end +end; + +function TXmlSource.AcceptToken: TXmlString; +begin + SetLength(Result, FToken.FValuePtr - FToken.ValueStart); + if Length(Result) > 0 then + Move(FToken.ValueStart^, Result[1], Length(Result)*sizeof(TXmlChar)); + DropToken; +end; + +procedure TXmlSource.SkipBlanks; +begin + while not EOF and (CurChar <= ' ') do + Next; +end; + +// íà âõîäå - ïåðâûé ñèìâîë èìåíè +// íà âûõîäå - ïåðâûé ñèìâîë, êîòîðûé íå ÿâëÿåòñÿ äîïóñòèìûì äëÿ èìåí +function TXmlSource.ExpectXmlName: TXmlString; +begin + if not NameCanBeginWith(CurChar) then + raise Exception.Create(SSimpleXmlError11); + NewToken; + AppendTokenChar(CurChar); + while Next and NameCanContain(CurChar) do + AppendTokenChar(CurChar); + Result := AcceptToken; +end; + +// íà âõîäå - ïåðâûé ñèìâîë ÷èñëà +// íà âûõîäå - ïåðâûé ñèìâîë, êîòîðûé íå ÿâëÿåòñÿ äîïóñòèìûì äëÿ ÷èñåë +function TXmlSource.ExpectDecimalInteger: Integer; +var + s: TXmlString; + e: Integer; +begin + NewToken; + while (CurChar >= '0') and (CurChar <= '9') do begin + AppendTokenChar(CurChar); + Next; + end; + s := AcceptToken; + if Length(s) = 0 then + raise Exception.Create(SSimpleXmlError12); + Val(s, Result, e); +end; + +// íà âõîäå - ïåðâûé ñèìâîë ÷èñëà +// íà âûõîäå - ïåðâûé ñèìâîë, êîòîðûé íå ÿâëÿåòñÿ äîïóñòèìûì äëÿ +// ùåñòíàäöàòèðè÷íûõ ÷èñåë +function TXmlSource.ExpectHexInteger: Integer; +var + s: TXmlString; + e: Integer; +begin + NewToken; + {$IFDEF XML_WIDE_CHARS} + while (CurChar >= '0') and (CurChar <= '9') or + (CurChar >= 'A') and (CurChar <= 'F') or + (CurChar >= 'a') and (CurChar <= 'f') do begin + {$ELSE} + while CurChar in ['0'..'9', 'A'..'F', 'a'..'f'] do begin + {$ENDIF} + AppendTokenChar(CurChar); + Next; + end; + s := '$'; + s := s + AcceptToken; + if Length(s) = 1 then + raise Exception.Create(SSimpleXmlError13); + Val(s, Result, e); +end; + +// íà âõîäå: "&" +// íà âûõîäå: ñëåäóþùèé çà ";" +function TXmlSource.ExpectXmlEntity: TXmlChar; +var + s: TXmlString; +begin + if not Next then + raise Exception.Create(SSimpleXmlError14); + if CurChar = '#' then begin + if not Next then + raise Exception.Create(SSimpleXmlError12); + if CurChar = 'x' then begin + Next; + Result := TXmlChar(ExpectHexInteger); + end + else + Result := TXmlChar(ExpectDecimalInteger); + ExpectChar(';'); + end + else begin + s := ExpectXmlName; + ExpectChar(';'); + if s = 'amp' then + Result := '&' + else if s = 'quot' then + Result := '"' + else if s = 'lt' then + Result := '<' + else if s = 'gt' then + Result := '>' + else if s = 'apos' then + Result := '''' + else + raise Exception.Create(SSimpleXmlError15); + end +end; + +procedure TXmlSource.ExpectChar(aChar: TXmlChar); +begin + if EOF or (CurChar <> aChar) then + raise Exception.CreateFmt(SSimpleXmlError16, [aChar]); + Next; +end; + +procedure TXmlSource.ExpectText(aText: PXmlChar); +begin + while aText^ <> #0 do begin + if (CurChar <> aText^) or EOF then + raise Exception.CreateFmt(SSimpleXmlError17, [aText]); + Inc(aText); + Next; + end; +end; + +// íà âõîäå: îòêðûâàþùàÿ êàâû÷êà +// íà âûõîäå: ñèìâîë, ñëåäóþùèé çà çàêðûâàþùåé êàâû÷êîé +function TXmlSource.ExpectQuotedText(aQuote: TXmlChar): TXmlString; +begin + NewToken; + Next; + while not EOF and (CurChar <> aQuote) do begin + if CurChar = '&' then + AppendTokenChar(ExpectXmlEntity) + else if CurChar = '<' then + raise Exception.Create(SSimpleXmlError18) + else begin + AppendTokenChar(CurChar); + Next; + end + end; + if EOF then + raise Exception.CreateFmt(SimpleXmlError19, [aQuote]); + Next; + Result := AcceptToken; +end; + +procedure TXmlSource.ParseAttrs(aNode: TXmlNode); +var + aName: TXmlString; + aValue: TXmlString; +begin + SkipBlanks; + while not EOF and NameCanBeginWith(CurChar) do begin + aName := ExpectXmlName; + SkipBlanks; + ExpectChar('='); + SkipBlanks; + if EOF then + raise Exception.Create(SSimpleXmlError20); + if (CurChar = '''') or (CurChar = '"') then + aValue := ExpectQuotedText(CurChar) + else + raise Exception.Create(SSimpleXmlError21); + aNode.SetAttr(aName, aValue); + SkipBlanks; + end; +end; + +function StrEquals(p1, p2: PXmlChar; aLen: Integer): Boolean; +begin + {$IFDEF XML_WIDE_CHARS} + while aLen > 0 do + if p1^ <> p2^ then begin + Result := False; + Exit + end + else if (p1^ = #0) or (p2^ = #0) then begin + Result := p1^ = p2^; + Exit + end + else begin + Inc(p1); + Inc(p2); + Dec(aLen); + end; + Result := True; + {$ELSE} + Result := StrLComp(p1, p2, aLen) = 0 + {$ENDIF} +end; + +// íà âõîäå: ïåðâûé ñèìâîë òåêñòà +// íà âûõîäå: ñèìâîë, ñëåäóþùèé çà ïîñëåäíèì ñèìâîëîì îãðàíè÷èòåëÿ +function TXmlSource.ParseTo(aText: PXmlChar): TXmlString; +var + aCheck: PXmlChar; + p: PXmlChar; +begin + NewToken; + aCheck := aText; + while not EOF do begin + if CurChar = aCheck^ then begin + Inc(aCheck); + Next; + if aCheck^ = #0 then begin + Result := AcceptToken; + Exit; + end; + end + else if aCheck = aText then begin + AppendTokenChar(CurChar); + Next; + end + else begin + p := aText + 1; + while (p < aCheck) and not StrEquals(p, aText, aCheck - p) do + Inc(p); + AppendTokenText(aText, p - aText); + if p < aCheck then + aCheck := p + else + aCheck := aText; + end; + end; + raise Exception.CreateFmt(SimpleXmlError22, [aText]); +end; + +procedure TXmlSource.AppendTokenChar(aChar: TXmlChar); +begin + FToken.AppendChar(aChar); +end; + +procedure TXmlSource.AppendTokenText(aText: PXmlChar; aCount: Integer); +begin + FToken.AppendText(aText, aCount) +end; + +procedure TXmlSource.DropToken; +begin + Dec(FTokenStackTop); + if FTokenStackTop >= 0 then + FToken := FTokenStack[FTokenStackTop] + else + FToken := nil +end; + +constructor TXmlSource.Create; +begin + inherited Create; + FTokenStackTop := -1; +end; + +destructor TXmlSource.Destroy; +var + i: Integer; +begin + for i := 0 to Length(FTokenStack) - 1 do + FTokenStack[i].Free; + inherited; +end; + +{ TXmlToken } + +procedure TXmlToken.AppendChar(aChar: TXmlChar); +var + aSaveLength: Integer; +begin + if FValuePtr >= FValueEnd then begin + aSaveLength := FValuePtr - FValueStart; + SetLength(FValueBuf, aSaveLength + 1); + FValueStart := PXmlChar(FValueBuf); + FValuePtr := FValueStart + aSaveLength; + FValueEnd := FValueStart + System.Length(FValueBuf); + end; + FValuePtr^ := aChar; + Inc(FValuePtr); +end; + +procedure TXmlToken.AppendText(aText: PXmlChar; aCount: Integer); +var + aSaveLength: Integer; +begin + if (FValuePtr + aCount) > FValueEnd then begin + aSaveLength := FValuePtr - FValueStart; + SetLength(FValueBuf, aSaveLength + aCount); + FValueStart := PXmlChar(FValueBuf); + FValuePtr := FValueStart + aSaveLength; + FValueEnd := FValueStart + System.Length(FValueBuf); + end; + Move(aText^, FValuePtr^, aCount*sizeof(TXmlChar)); + Inc(FValuePtr, aCount); +end; + +procedure TXmlToken.Clear; +begin + FValuePtr := FValueStart; +end; + +constructor TXmlToken.Create; +begin + inherited Create; + SetLength(FValueBuf, 32); + FValueStart := PXmlChar(FValueBuf); + FValuePtr := FValueStart; + FValueEnd := FValueStart + 32; +end; + +function TXmlToken.Length: Integer; +begin + Result := FValuePtr - FValueStart; +end; + +{$IFDEF XML_WIDE_CHARS} +function AnsiToUnicode(c: AnsiChar): WideChar; +begin + MultiByteToWideChar(CP_ACP, 0, @c, 1, @Result, 1); +end; +{$ENDIF} + +{ TXmlStmSource } + +constructor TXmlStmSource.Create(aStream: TStream; aBufSize: Integer); +var + aSize: Integer; +begin + inherited Create; + FStream := aStream; + FBufSize := aBufSize; + FBufStart := AllocMem(aBufSize); + FBufPtr := FBufStart; + FBufEnd := FBufStart; + FSize := aStream.Size; + if FSize = 0 then + CurChar := #0 + else begin + if FSize < FBufSize then + aSize := FSize + else + aSize := FBufSize; + FStream.ReadBuffer(FBufStart^, aSize); + FBufEnd := FBufStart + aSize; + FBufPtr := FBufStart; + Dec(FSize, aSize); + {$IFDEF XML_WIDE_CHARS} + CurChar := AnsiToUnicode(FBufPtr^); + {$ELSE} + CurChar := FBufPtr^; + {$ENDIF} + end +end; + +destructor TXmlStmSource.Destroy; +begin + FreeMem(FBufStart); + inherited; +end; + +function TXmlStmSource.EOF: Boolean; +begin + Result := (FBufPtr = FBufEnd) and (FSize = 0) +end; + +function TXmlStmSource.Next: Boolean; +var + aSize: Integer; +begin + if FBufPtr < FBufEnd then + Inc(FBufPtr); + if FBufPtr = FBufEnd then + if FSize = 0 then begin + Result := False; + CurChar := #0; + end + else begin + if FSize < FBufSize then + aSize := FSize + else + aSize := FBufSize; + FStream.ReadBuffer(FBufStart^, aSize); + FBufEnd := FBufStart + aSize; + FBufPtr := FBufStart; + Dec(FSize, aSize); + Result := True; + {$IFDEF XML_WIDE_CHARS} + CurChar := AnsiToUnicode(FBufPtr^); + {$ELSE} + CurChar := FBufPtr^; + {$ENDIF} + end + else begin + Result := True; + {$IFDEF XML_WIDE_CHARS} + CurChar := AnsiToUnicode(FBufPtr^); + {$ELSE} + CurChar := FBufPtr^; + {$ENDIF} + end; +end; + +{ TStmXmlReader } + +constructor TStmXmlReader.Create(aStream: TStream; aBufSize: Integer); +begin + inherited Create; + FStream := aStream; + FRestSize := aStream.Size - aStream.Position; + FBufSize := aBufSize; + FBufStart := AllocMem(aBufSize); + FBufEnd := FBufStart; + FBufPtr := FBufEnd; + Read(FOptions, sizeof(FOptions)); +end; + +destructor TStmXmlReader.Destroy; +begin + FreeMem(FBufStart); + inherited; +end; + +procedure TStmXmlReader.Read(var aBuf; aSize: Integer); +var + aBufRest: Integer; + aDst: PChar; + aBufSize: Integer; +begin + if aSize > FRestSize then + raise Exception.Create(SSimpleXmlError23); + + aBufRest := FBufEnd - FBufPtr; + if aSize <= aBufRest then begin + Move(FBufPtr^, aBuf, aSize); + Inc(FBufPtr, aSize); + Dec(FRestSize, aSize); + end + else begin + aDst := @aBuf; + Move(FBufPtr^, aDst^, aBufRest); + Inc(aDst, aBufRest); + FStream.ReadBuffer(aDst^, aSize - aBufRest); + Dec(FRestSize, aSize); + + if FRestSize < FBufSize then + aBufSize := FRestSize + else + aBufSize := FBufSize; + FBufPtr := FBufStart; + FBufEnd := FBufStart + aBufSize; + if aBufSize > 0 then + FStream.ReadBuffer(FBufStart^, aBufSize); + end; +end; + +{ TStrXmlReader } + +constructor TStrXmlReader.Create(const aStr: String); +var + aSig: array [1..BinXmlSignatureSize] of Char; +begin + inherited Create; + FString := aStr; + FRestSize := Length(aStr); + FPtr := PChar(FString); + Read(aSig, BinXmlSignatureSize); + Read(FOptions, sizeof(FOptions)); +end; + +procedure TStrXmlReader.Read(var aBuf; aSize: Integer); +begin + if aSize > FRestSize then + raise Exception.Create(SSimpleXmlError23); + Move(FPtr^, aBuf, aSize); + Inc(FPtr, aSize); + Dec(FRestSize, aSize); +end; + +{ TBinXmlReader } + +function TBinXmlReader.ReadAnsiString: String; +var + aLength: LongInt; +begin + aLength := ReadLongint; + if aLength = 0 then + Result := '' + else begin + SetLength(Result, aLength); + Read(Result[1], aLength); + end +end; + +function TBinXmlReader.ReadLongint: Longint; +var + b: Byte; +begin + Result := 0; + Read(Result, 1); + if Result >= $80 then + if Result = $FF then + Read(Result, sizeof(Result)) + else begin + Read(b, 1); + Result := (Result and $7F) shl 8 or b; + end +end; + +procedure TBinXmlReader.ReadVariant(var v: TVarData); +var + aDataType: Word; + aSize: Longint; + p: Pointer; +begin + VarClear(Variant(v)); + aDataType := ReadLongint; + case aDataType of + varEmpty: ; + varNull: ; + varSmallint: + Read(v.VSmallint, sizeof(SmallInt)); + varInteger: + Read(v.VInteger, sizeof(Integer)); + varSingle: + Read(v.VSingle, sizeof(Single)); + varDouble: + Read(v.VDouble, sizeof(Double)); + varCurrency: + Read(v.VCurrency, sizeof(Currency)); + varDate: + Read(v.VDate, sizeof(TDateTime)); + varOleStr: + Variant(v) := ReadWideString; + varBoolean: + Read(v.VBoolean, sizeof(WordBool)); + varShortInt: + Read(v.VShortInt, sizeof(ShortInt)); + varByte: + Read(v.VByte, sizeof(Byte)); + varWord: + Read(v.VWord, sizeof(Word)); + varLongWord: + Read(v.VLongWord, sizeof(LongWord)); + varInt64: + Read(v.VInt64, sizeof(Int64)); + varString: + Variant(v) := ReadAnsiString; + varArray + varByte: + begin + aSize := ReadLongint; + Variant(v) := VarArrayCreate([0, aSize - 1], varByte); + p := VarArrayLock(Variant(v)); + try + Read(p^, aSize); + finally + VarArrayUnlock(Variant(v)) + end + end; + else + raise Exception.Create(SSimpleXmlError24); + end; + v.VType := aDataType; +end; + +function TBinXmlReader.ReadWideString: WideString; +var + aLength: LongInt; +begin + aLength := ReadLongint; + if aLength = 0 then + Result := '' + else begin + SetLength(Result, aLength); + Read(Result[1], aLength*sizeof(WideChar)); + end +end; + +function TBinXmlReader.ReadXmlString: TXmlString; +begin + if (FOptions and BINXML_USE_WIDE_CHARS) <> 0 then + Result := ReadWideString + else + Result := ReadAnsiString +end; + +{ TStmXmlWriter } + +constructor TStmXmlWriter.Create(aStream: TStream; anOptions: LongWord; + aBufSize: Integer); +begin + inherited Create; + FStream := aStream; + FOptions := anOptions; + FBufSize := aBufSize; + FBufStart := AllocMem(aBufSize); + FBufEnd := FBufStart + aBufSize; + FBufPtr := FBufStart; + Write(BinXmlSignature[1], BinXmlSignatureSize); + Write(FOptions, sizeof(FOptions)); +end; + +destructor TStmXmlWriter.Destroy; +begin + if FBufPtr > FBufStart then + FStream.WriteBuffer(FBufStart^, FBufPtr - FBufStart); + FreeMem(FBufStart); + inherited; +end; + +procedure TStmXmlWriter.Write(const aBuf; aSize: Integer); +var + aBufRest: Integer; +begin + aBufRest := FBufEnd - FBufPtr; + if aSize <= aBufRest then begin + Move(aBuf, FBufPtr^, aSize); + Inc(FBufPtr, aSize); + end + else begin + if FBufPtr > FBufStart then begin + FStream.WriteBuffer(FBufStart^, FBufPtr - FBufStart); + FBufPtr := FBufStart; + end; + FStream.WriteBuffer(aBuf, aSize); + end +end; + +{ TStrXmlWriter } + +constructor TStrXmlWriter.Create(anOptions: LongWord; aBufSize: Integer); +begin + inherited Create; + FData := ''; + FOptions := anOptions; + FBufSize := aBufSize; + FBufStart := AllocMem(aBufSize); + FBufEnd := FBufStart + aBufSize; + FBufPtr := FBufStart; + Write(BinXmlSignature[1], BinXmlSignatureSize); + Write(FOptions, sizeof(FOptions)); +end; + +destructor TStrXmlWriter.Destroy; +begin + FreeMem(FBufStart); + inherited; +end; + +procedure TStrXmlWriter.FlushBuf; +var + aPrevSize: Integer; + aSize: Integer; +begin + aPrevSize := Length(FData); + aSize := FBufPtr - FBufStart; + SetLength(FData, aPrevSize + aSize); + Move(FBufStart^, FData[aPrevSize + 1], aSize); + FBufPtr := FBufStart; +end; + +procedure TStrXmlWriter.Write(const aBuf; aSize: Integer); +var + aBufRest: Integer; + aPrevSize: Integer; + aBufSize: Integer; +begin + aBufRest := FBufEnd - FBufPtr; + if aSize <= aBufRest then begin + Move(aBuf, FBufPtr^, aSize); + Inc(FBufPtr, aSize); + end + else begin + aPrevSize := Length(FData); + aBufSize := FBufPtr - FBufStart; + SetLength(FData, aPrevSize + aBufSize + aSize); + if aBufSize > 0 then + Move(FBufStart^, FData[aPrevSize + 1], aBufSize); + Move(aBuf, FData[aPrevSize + aBufSize + 1], aSize); + FBufPtr := FBufStart; + end +end; + +{ TBinXmlWriter } + +procedure TBinXmlWriter.WriteAnsiString(const aValue: String); +var + aLength: LongInt; +begin + aLength := Length(aValue); + WriteLongint(aLength); + if aLength > 0 then + Write(aValue[1], aLength); +end; + +procedure TBinXmlWriter.WriteLongint(aValue: Longint); +var + b: array [0..1] of Byte; +begin + if aValue < 0 then begin + b[0] := $FF; + Write(b[0], 1); + Write(aValue, SizeOf(aValue)); + end + else if aValue < $80 then + Write(aValue, 1) + else if aValue <= $7FFF then begin + b[0] := (aValue shr 8) or $80; + b[1] := aValue and $FF; + Write(b, 2); + end + else begin + b[0] := $FF; + Write(b[0], 1); + Write(aValue, SizeOf(aValue)); + end; +end; + +procedure TBinXmlWriter.WriteVariant(const v: TVarData); +var + aSize: Integer; + p: Pointer; +begin + WriteLongint(v.VType); + case v.VType of + varEmpty: ; + varNull: ; + varSmallint: + Write(v.VSmallint, sizeof(SmallInt)); + varInteger: + Write(v.VInteger, sizeof(Integer)); + varSingle: + Write(v.VSingle, sizeof(Single)); + varDouble: + Write(v.VDouble, sizeof(Double)); + varCurrency: + Write(v.VCurrency, sizeof(Currency)); + varDate: + Write(v.VDate, sizeof(TDateTime)); + varOleStr: + WriteWideString(Variant(v)); + varBoolean: + Write(v.VBoolean, sizeof(WordBool)); + varShortInt: + Write(v.VShortInt, sizeof(ShortInt)); + varByte: + Write(v.VByte, sizeof(Byte)); + varWord: + Write(v.VWord, sizeof(Word)); + varLongWord: + Write(v.VLongWord, sizeof(LongWord)); + varInt64: + Write(v.VInt64, sizeof(Int64)); + varString: + WriteAnsiString(Variant(v)); + varArray + varByte: + begin + aSize := VarArrayHighBound(Variant(v), 1) - VarArrayLowBound(Variant(v), 1) + 1; + WriteLongint(aSize); + p := VarArrayLock(Variant(v)); + try + Write(p^, aSize); + finally + VarArrayUnlock(Variant(v)) + end + end; + else + raise Exception.Create(SSimpleXmlError25); + end; +end; + +procedure TBinXmlWriter.WriteWideString(const aValue: WideString); +var + aLength: LongInt; +begin + aLength := Length(aValue); + WriteLongint(aLength); + if aLength > 0 then + Write(aValue[1], aLength*sizeof(WideChar)); +end; + + +procedure TBinXmlWriter.WriteXmlString(const aValue: TXmlString); +begin + if (FOptions and BINXML_USE_WIDE_CHARS) <> 0 then + WriteWideString(aValue) + else + WriteAnsiString(aValue) +end; + +var + DefaultNameTableImpl: TXmlNameTable = nil; + +function CreateXmlElement(const aName: TXmlString; const aNameTable: IXmlNameTable): IXmlElement; +var + aNameTableImpl: TXmlNameTable; +begin + if Assigned(aNameTable) then + aNameTableImpl := aNameTable.GetObject as TXmlNameTable + else + aNameTableImpl := DefaultNameTableImpl; + Result := TXmlElement.Create(aNameTableImpl, aNameTableImpl.GetID(aName)); +end; + +function CreateXmlDocument( + const aRootElementName: String; + const aVersion: String; + const anEncoding: String; + const aNames: IXmlNameTable): IXmlDocument; +var + aNameTable: TXmlNameTable; + s: String; +begin + if Assigned(aNames) then + aNameTable := aNames.GetObject as TXmlNameTable + else + aNameTable := DefaultNameTableImpl; + if anEncoding = '' then + s := DefaultEncoding + else + s := anEncoding; + Result := TXmlDocument.Create(aNameTable); + if aRootElementName <> '' then + Result.NewDocument(aVersion, anEncoding, aRootElementName); +end; + +function LoadXmlDocumentFromXML(const aXML: TXmlString): IXmlDocument; +begin + Result := TXmlDocument.Create(DefaultNameTableImpl); + Result.LoadXML(aXML); +end; + +function LoadXmlDocumentFromBinaryXML(const aXML: String): IXmlDocument; +begin + Result := TXmlDocument.Create(DefaultNameTableImpl); + Result.LoadBinaryXML(aXML); +end; + +function LoadXmlDocument(aStream: TStream): IXmlDocument; +begin + Result := TXmlDocument.Create(DefaultNameTableImpl); + Result.Load(aStream); +end; + +function LoadXmlDocument(const aFileName: TXmlString): IXmlDocument; overload; +begin + Result := TXmlDocument.Create(DefaultNameTableImpl); + Result.Load(aFileName); +end; + +function LoadXmlDocument(aResType, aResName: PChar): IXmlDocument; overload; +begin + Result := TXmlDocument.Create(DefaultNameTableImpl); + Result.LoadResource(aResType, aResName); +end; + +initialization + DefaultNameTableImpl := TXmlNameTable.Create(4096); + DefaultNameTable := DefaultNameTableImpl; +finalization + DefaultNameTable := nil; + DefaultNameTableImpl := nil; +end. diff --git a/bass.dll b/bass.dll new file mode 100644 index 0000000000000000000000000000000000000000..87cd83148de898c69c801a6c5b269d9c904497c5 GIT binary patch literal 111772 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%0D`Dj;RZFPZC?8hkG%P1JTU=%^@^Kh%G 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 literal 0 HcmV?d00001 diff --git a/bassmidi.pas b/bassmidi.pas new file mode 100644 index 0000000..89d7f63 --- /dev/null +++ b/bassmidi.pas @@ -0,0 +1,158 @@ +{ + BASSMIDI 2.4 Delphi unit + Copyright (c) 2006-2009 Un4seen Developments Ltd. + + See the BASSMIDI.CHM file for more detailed documentation +} + +unit BassMIDI; + +interface + +uses Windows, Bass; + +const + // Additional config options + BASS_CONFIG_MIDI_COMPACT = $10400; + BASS_CONFIG_MIDI_VOICES = $10401; + BASS_CONFIG_MIDI_AUTOFONT = $10402; + + // Additional sync types + BASS_SYNC_MIDI_MARKER = $10000; + BASS_SYNC_MIDI_CUE = $10001; + BASS_SYNC_MIDI_LYRIC = $10002; + BASS_SYNC_MIDI_TEXT = $10003; + BASS_SYNC_MIDI_EVENT = $10004; + BASS_SYNC_MIDI_TICK = $10005; + + // Additional BASS_MIDI_StreamCreateFile/etc flags + BASS_MIDI_DECAYEND = $1000; + BASS_MIDI_NOFX = $2000; + BASS_MIDI_DECAYSEEK = $4000; + + // Marker types + BASS_MIDI_MARK_MARKER = 0; // marker events + BASS_MIDI_MARK_CUE = 1; // cue events + BASS_MIDI_MARK_LYRIC = 2; // lyric events + BASS_MIDI_MARK_TEXT = 3; // text events + + // MIDI events + MIDI_EVENT_NOTE = 1; + MIDI_EVENT_PROGRAM = 2; + MIDI_EVENT_CHANPRES = 3; + MIDI_EVENT_PITCH = 4; + MIDI_EVENT_PITCHRANGE = 5; + MIDI_EVENT_DRUMS = 6; + MIDI_EVENT_FINETUNE = 7; + MIDI_EVENT_COARSETUNE = 8; + MIDI_EVENT_MASTERVOL = 9; + MIDI_EVENT_BANK = 10; + MIDI_EVENT_MODULATION = 11; + MIDI_EVENT_VOLUME = 12; + MIDI_EVENT_PAN = 13; + MIDI_EVENT_EXPRESSION = 14; + MIDI_EVENT_SUSTAIN = 15; + MIDI_EVENT_SOUNDOFF = 16; + MIDI_EVENT_RESET = 17; + MIDI_EVENT_NOTESOFF = 18; + MIDI_EVENT_PORTAMENTO = 19; + MIDI_EVENT_PORTATIME = 20; + MIDI_EVENT_PORTANOTE = 21; + MIDI_EVENT_MODE = 22; + MIDI_EVENT_REVERB = 23; + MIDI_EVENT_CHORUS = 24; + MIDI_EVENT_CUTOFF = 25; + MIDI_EVENT_RESONANCE = 26; + MIDI_EVENT_REVERB_MACRO = 30; + MIDI_EVENT_CHORUS_MACRO = 31; + MIDI_EVENT_REVERB_TIME = 32; + MIDI_EVENT_REVERB_DELAY = 33; + MIDI_EVENT_REVERB_LOCUTOFF = 34; + MIDI_EVENT_REVERB_HICUTOFF = 35; + MIDI_EVENT_REVERB_LEVEL = 36; + MIDI_EVENT_CHORUS_DELAY = 37; + MIDI_EVENT_CHORUS_DEPTH = 38; + MIDI_EVENT_CHORUS_RATE = 39; + MIDI_EVENT_CHORUS_FEEDBACK = 40; + MIDI_EVENT_CHORUS_LEVEL = 41; + MIDI_EVENT_CHORUS_REVERB = 42; + MIDI_EVENT_DRUM_FINETUNE = 50; + MIDI_EVENT_DRUM_COARSETUNE = 51; + MIDI_EVENT_DRUM_PAN = 52; + MIDI_EVENT_DRUM_REVERB = 53; + MIDI_EVENT_DRUM_CHORUS = 54; + MIDI_EVENT_DRUM_CUTOFF = 55; + MIDI_EVENT_DRUM_RESONANCE = 56; + MIDI_EVENT_TEMPO = 62; + MIDI_EVENT_MIXLEVEL = $10000; + MIDI_EVENT_TRANSPOSE = $10001; + + // BASS_CHANNELINFO type + BASS_CTYPE_STREAM_MIDI = $10d00; + + // Additional attributes + BASS_ATTRIB_MIDI_PPQN = $12000; + BASS_ATTRIB_MIDI_TRACK_VOL = $12100; // + track # + + // Additional tag type + BASS_TAG_MIDI_TRACK = $11000; // + track #, track text : array of null-terminated ANSI strings + + // BASS_ChannelGetLength/GetPosition/SetPosition mode + BASS_POS_MIDI_TICK = 2; // tick position + + +type + HSOUNDFONT = DWORD; // soundfont handle + + BASS_MIDI_FONT = record + font: HSOUNDFONT; // soundfont + preset: LongInt; // preset number (-1=all) + bank: Longint; + end; + + BASS_MIDI_FONTINFO = record + name: PAnsiChar; + copyright: PAnsiChar; + comment: PAnsiChar; + presets: DWORD; // number of presets/instruments + samsize: DWORD; // total size (in bytes) of the sample data + samload: DWORD; // amount of sample data currently loaded + samtype: DWORD; // sample format (CTYPE) if packed + end; + + BASS_MIDI_MARK = record + track: DWORD; // track containing marker + pos: DWORD; // marker position + text: PAnsiChar; // marker text + end; + + +const + bassmididll = 'bassmidi.dll'; + +function BASS_MIDI_StreamCreate(channels,flags,freq:DWORD): HSTREAM; stdcall; external bassmididll; +function BASS_MIDI_StreamCreateFile(mem:BOOL; fl:pointer; offset,length:QWORD; flags,freq:DWORD): HSTREAM; stdcall; external bassmididll; +function BASS_MIDI_StreamCreateURL(URL:PAnsiChar; offset:DWORD; flags:DWORD; proc:DOWNLOADPROC; user:Pointer; freq:DWORD): HSTREAM; stdcall; external bassmididll; +function BASS_MIDI_StreamCreateFileUser(system,flags:DWORD; var procs:BASS_FILEPROCS; user:Pointer; freq:DWORD): HSTREAM; stdcall; external bassmididll; +function BASS_MIDI_StreamGetMark(handle:HSTREAM; type_,index:DWORD; var mark:BASS_MIDI_MARK): BOOL; stdcall; external bassmididll; +function BASS_MIDI_StreamSetFonts(handle:HSTREAM; var fonts:BASS_MIDI_FONT; count:DWORD): BOOL; stdcall; external bassmididll; +function BASS_MIDI_StreamGetFonts(handle:HSTREAM; var fonts:BASS_MIDI_FONT; count:DWORD): DWORD; stdcall; external bassmididll; +function BASS_MIDI_StreamLoadSamples(handle:HSTREAM): BOOL; stdcall; external bassmididll; +function BASS_MIDI_StreamEvent(handle:HSTREAM; chan,event,param:DWORD): BOOL; stdcall; external bassmididll; +function BASS_MIDI_StreamGetEvent(handle:HSTREAM; chan,event:DWORD): DWORD; stdcall; external bassmididll; +function BASS_MIDI_StreamGetChannel(handle:HSTREAM; chan:DWORD): HSTREAM; stdcall; external bassmididll; + +function BASS_MIDI_FontInit(fname:PChar; flags:DWORD): HSOUNDFONT; stdcall; external bassmididll; +function BASS_MIDI_FontFree(handle:HSOUNDFONT): BOOL; stdcall; external bassmididll; +function BASS_MIDI_FontGetInfo(handle:HSOUNDFONT; var info:BASS_MIDI_FONTINFO): BOOL; stdcall; external bassmididll; +function BASS_MIDI_FontGetPreset(handle:HSOUNDFONT; preset,bank:LongInt): PAnsiChar; stdcall; external bassmididll; +function BASS_MIDI_FontLoad(handle:HSOUNDFONT; preset,bank:LongInt): BOOL; stdcall; external bassmididll; +function BASS_MIDI_FontCompact(handle:HSOUNDFONT): BOOL; stdcall; external bassmididll; +function BASS_MIDI_FontPack(handle:HSOUNDFONT; outfile,encoder:PChar; flags:DWORD): BOOL; stdcall; external bassmididll; +function BASS_MIDI_FontUnpack(handle:HSOUNDFONT; outfile:PChar; flags:DWORD): BOOL; stdcall; external bassmididll; +function BASS_MIDI_FontSetVolume(handle:HSOUNDFONT; volume:Single): BOOL; stdcall; external bassmididll; +function BASS_MIDI_FontGetVolume(handle:HSOUNDFONT): Single; stdcall; external bassmididll; + +implementation + +end. diff --git a/d2d.gex b/d2d.gex new file mode 100644 index 0000000..297433e --- /dev/null +++ b/d2d.gex @@ -0,0 +1,73 @@ +[General] +Name=d2d +Project=0 +[Classes] +Count=68 +Class0="Td2dApplication","","d2dApplication","0","C:\TON\Programming\d2dge\d2dApplication.pas" +Class1="Td2dScene","","d2dApplication","0","C:\TON\Programming\d2dge\d2dApplication.pas" +Class2="Td2dCore","","d2dCore","0","C:\TON\Programming\d2dge\d2dCore.pas" +Class3="Td2dTextureHolder","","d2dCore","0","C:\TON\Programming\d2dge\d2dCore.pas" +Class4="Td2dHGELetter","Td2dSprite","d2dFont","0","C:\TON\Programming\d2dge\d2dFont.pas" +Class5="Td2dCustomFont","TInterfacedObject, Id2dFont","d2dFont","0","C:\TON\Programming\d2dge\d2dFont.pas" +Class6="Td2dHGEFont","Td2dCustomFont","d2dFont","0","C:\TON\Programming\d2dge\d2dFont.pas" +Class7="Td2dDXFont","TObject","d2dFont","0","C:\TON\Programming\d2dge\d2dFont.pas" +Class8="Td2dBMLetter","Td2dSprite","d2dFont","0","C:\TON\Programming\d2dge\d2dFont.pas" +Class9="Td2dBMFont","Td2dCustomFont","d2dFont","0","C:\TON\Programming\d2dge\d2dFont.pas" +Class10="Td2dHorizFrame","","d2dFrames","0","C:\TON\Programming\d2dge\d2dFrames.pas" +Class11="Td2dFrame","","d2dFrames","0","C:\TON\Programming\d2dge\d2dFrames.pas" +Class12="Td2dGUI","","d2dGUI","0","C:\TON\Programming\d2dge\d2dGUI.pas" +Class13="Td2dControl","","d2dGUI","0","C:\TON\Programming\d2dge\d2dGUI.pas" +Class14="Td2dCustomButton","Td2dControl","d2dGUIButtons","0","C:\TON\Programming\d2dge\d2dGUIButtons.pas" +Class15="Td2dBitButton","Td2dCustomButton","d2dGUIButtons","0","C:\TON\Programming\d2dge\d2dGUIButtons.pas" +Class16="Td2dFramedButtonView","","d2dGUIButtons","0","C:\TON\Programming\d2dge\d2dGUIButtons.pas" +Class17="Td2dFramedTextButton","Td2dCustomButton","d2dGUIButtons","0","C:\TON\Programming\d2dge\d2dGUIButtons.pas" +Class18="Td2dMenuItem","","d2dGUIMenus","0","C:\TON\Programming\d2dge\d2dGUIMenus.pas" +Class19="Td2dMenu","Td2dControl","d2dGUIMenus","0","C:\TON\Programming\d2dge\d2dGUIMenus.pas" +Class20="Td2dCustomTextChunk","","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class21="Td2dTextPara","","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class22="Td2dStringChunk","Td2dCustomTextChunk","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class23="Td2dPictureChunk","Td2dCustomTextChunk","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class24="Td2dTextSource","","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class25="Td2dCustomSlice","","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class26="Td2dUnionSlice","Td2dCustomSlice","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class27="Td2dTextSlice","Td2dCustomSlice","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class28="Td2dPictureSlice","Td2dCustomSlice","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class29="Td2dDocRoot","Td2dUnionSlice","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class30="Td2dFormatter","","d2dGUITextPane","0","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class31="Td2dTextPane","Td2dControl","d2dGUITextPane","218","C:\TON\Programming\d2dge\d2dGUITextPane.pas" +Class32="Td2dCommonPath","","d2dPath","0","C:\TON\Programming\d2dge\d2dPath.pas" +Class33="Td2dPathCursor","","d2dPath","0","C:\TON\Programming\d2dge\d2dPath.pas" +Class34="Td2dSprite","","d2dSprite","0","C:\TON\Programming\d2dge\d2dSprite.pas" +Class35="Td2dMultiFrameSprite","Td2dSprite","d2dSprite","0","C:\TON\Programming\d2dge\d2dSprite.pas" +Class36="Td2dBaseAnimation","Td2dMultiFrameSprite","d2dSprite","0","C:\TON\Programming\d2dge\d2dSprite.pas" +Class37="Td2dTimedAnimation","Td2dBaseAnimation","d2dSprite","0","C:\TON\Programming\d2dge\d2dSprite.pas" +Class38="Td2dSetupStorage","TInterfacedObject, Id2dStorage","d2dStorage","0","C:\TON\Programming\d2dge\d2dStorage.pas" +Class39="Td2dBasicBlender","","d2dUtils","0","C:\TON\Programming\d2dge\d2dUtils.pas" +Class40="Td2dColorBlender","Td2dBasicBlender","d2dUtils","0","C:\TON\Programming\d2dge\d2dUtils.pas" +Class41="Td2dPositionBlender","Td2dBasicBlender","d2dUtils","0","C:\TON\Programming\d2dge\d2dUtils.pas" +Class42="Td2dZipPack","","d2dZipPack","0","C:\TON\Programming\d2dge\d2dZipPack.pas" +Class43="EZipFileError","Exception","d2dZipPack","0","C:\TON\Programming\d2dge\d2dZipPack.pas" +Class44="EZipFileCRCError","Exception","d2dZipPack","0","C:\TON\Programming\d2dge\d2dZipPack.pas" +Class45="TBinXmlReader","","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class46="TStmXmlReader","TBinXmlReader","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class47="TStrXmlReader","TBinXmlReader","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class48="TBinXmlWriter","","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class49="TStmXmlWriter","TBinXmlWriter","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class50="TStrXmlWriter","TBinXmlWriter","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class51="TXmlBase","TInterfacedObject, IXmlBase","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class52="TXmlNameTable","TXmlBase, IXmlNameTable","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class53="TXmlToken","","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class54="TXmlSource","","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class55="TXmlStrSource","TXmlSource","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class56="TXmlStmSource","TXmlSource","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class57="TXmlNodeList","TXmlBase, IXmlNodeList","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class58="TXmlNode","TXmlBase, IXmlNode","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class59="TXmlElement","TXmlNode, IXmlElement","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class60="TXmlCharacterData","TXmlNode, IXmlCharacterData","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class61="TXmlText","TXmlNode, IXmlText","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class62="TXmlCDATASection","TXmlCharacterData, IXmlCDATASection","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class63="TXmlComment","TXmlCharacterData, IXmlComment","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class64="TXmlProcessingInstruction","TXmlNode, IXmlProcessingInstruction","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class65="TXmlDocument","TXmlNode, IXmlDocument","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class66="TPredicate","","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" +Class67="TLocationStep","","SimpleXML","0","C:\TON\Programming\d2dge\SimpleXML.pas" diff --git a/d2dApplication.pas b/d2dApplication.pas new file mode 100644 index 0000000..62f6036 --- /dev/null +++ b/d2dApplication.pas @@ -0,0 +1,340 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dApplication; + +interface +uses + Classes, + d2dTypes; + +type + Td2dScene = class; + + Td2dSceneClass = class of Td2dScene; + + Td2dApplication = class + private + f_CurrentScene: string; + f_CurrentSceneObj: Td2dScene; + f_Finished: Boolean; + f_Scenes: TStringList; + procedure pm_SetCurrentScene(const Value: string); + protected + procedure FrameFunc(aDelta: Single; var theFinish: Boolean); + function Init: Boolean; virtual; + procedure LoadGlobalResources; virtual; + procedure RenderFunc; + procedure UnloadGlobalResources; virtual; + public + constructor Create(const aResWidth, aResHeight: Word; const aWindowed: Boolean = True; aTitle: string = ''); + destructor Destroy; override; + procedure AddScene(aKey: string; aScene: Td2dScene); + procedure Run; + property CurrentScene: string read f_CurrentScene write pm_SetCurrentScene; + property Finished: Boolean read f_Finished write f_Finished; + end; + + Td2dScene = class + private + f_Application: Td2dApplication; + f_Loaded: Boolean; + f_Child : Td2dScene; + f_Parent: Td2dScene; + f_RunningAsChild: Boolean; + function pm_GetHasRunningChild: Boolean; + protected + procedure DoLoad; virtual; + procedure DoUnload; virtual; + procedure Frame(aDelta: Single); + procedure DoFrame(aDelta: Single); virtual; + procedure ProcessEvent(var theEvent: Td2dInputEvent); + procedure DoProcessEvent(var theEvent: Td2dInputEvent); virtual; + procedure Render; + procedure DoRender; virtual; abstract; + procedure StartChild(const aScene: Td2dScene); + procedure StopChild; // îñòàíîâèòü âûïîëíåíèå äî÷åðíåé ñöåíû + procedure StopAsChild; + procedure BeforeStoppingChild; virtual; + property HasRunningChild: Boolean read pm_GetHasRunningChild; + public + constructor Create(aApplication: Td2dApplication); + destructor Destroy; override; + procedure Load; + procedure Unload; + property Application: Td2dApplication read f_Application; + end; + +implementation +uses + SysUtils, + {$IFDEF TRACE_STACK} + JclDebug, + {$ENDIF} + d2dCore; + +constructor Td2dApplication.Create(const aResWidth, aResHeight: Word; + const aWindowed: Boolean = True; + aTitle: string = ''); +begin + inherited Create; + f_Scenes := TStringList.Create; + f_Scenes.Sorted := True; + f_Scenes.CaseSensitive := False; + f_Scenes.Duplicates := dupError; + D2DInit(aResWidth, aResHeight, aWindowed, aTitle); + f_Finished := False; + gD2DE.OnFrame := FrameFunc; + gD2DE.OnRender := RenderFunc; +end; + +destructor Td2dApplication.Destroy; +begin + f_Scenes.Free; + D2DDone; + inherited; +end; + +procedure Td2dApplication.AddScene(aKey: string; aScene: Td2dScene); +begin + f_Scenes.AddObject(aKey, aScene); +end; + +procedure Td2dApplication.FrameFunc(aDelta: Single; var theFinish: Boolean); +var + l_Ev: Td2dInputEvent; +begin + if f_CurrentSceneObj <> nil then + begin + while gD2DE.Input_GetEvent(l_Ev) do + begin + f_CurrentSceneObj.ProcessEvent(l_Ev); + end; + f_CurrentSceneObj.Frame(aDelta); + end; + theFinish := f_Finished; +end; + +function Td2dApplication.Init: Boolean; +begin + Result := True; + // here we supposed to register scenes and initialize all needed data +end; + +procedure Td2dApplication.LoadGlobalResources; +begin + // here we load application-wide resources +end; + +procedure Td2dApplication.pm_SetCurrentScene(const Value: string); +var + l_Idx: Integer; +begin + if (f_CurrentSceneObj <> nil) and (gD2DE.D3DDevice <> nil) then + f_CurrentSceneObj.Unload; + + l_Idx := f_Scenes.IndexOf(Value); + if l_Idx >= 0 then + begin + f_CurrentSceneObj := Td2dScene(f_Scenes.Objects[l_Idx]); + if gD2DE.D3DDevice <> nil then + f_CurrentSceneObj.Load; + f_CurrentScene := Value; + end + else + begin + gD2DE.System_Log('Can''t find scene "%s". Terminating application.', [Value]); + f_Finished := True; + end; +end; + +procedure Td2dApplication.RenderFunc; +begin + gD2DE.Gfx_BeginScene; + try + if f_CurrentSceneObj <> nil then + f_CurrentSceneObj.Render + else + gD2DE.Gfx_Clear(0); + finally + gD2DE.Gfx_EndScene; + end; +end; + +procedure Td2dApplication.Run; +var + I: Integer; +begin + if not Init then + Exit; + if gD2DE.System_Start then + begin + try + if f_Scenes.Count > 0 then + begin + LoadGlobalResources; + try + if f_CurrentSceneObj = nil then + CurrentScene := f_Scenes[0] + else + f_CurrentSceneObj.Load; + try + gD2DE.System_Run; + except + on E: Exception do + begin + gD2DE.System_Log('ÎØÈÁÊÀ: '+E.Message); + {$IFDEF TRACE_STACK} + l_List := TStringList.Create; + try + JclLastExceptStackListToStrings(l_List); + gD2DE.System_Log(l_List.Text); + finally + FreeAndNil(l_List); + end; + {$ENDIF} + end; + end; + finally + for I := 0 to f_Scenes.Count-1 do + f_Scenes.Objects[I].Free; + f_Scenes.Clear; + UnloadGlobalResources; + end; + end + else + gD2DE.System_Log('No scenes defined!'); + finally + gD2DE.System_Shutdown; + end; + end; +end; + +procedure Td2dApplication.UnloadGlobalResources; +begin + // here we unload application-wide resources +end; + +constructor Td2dScene.Create(aApplication: Td2dApplication); +begin + inherited Create; + f_Application := aApplication; +end; + +destructor Td2dScene.Destroy; +begin + StopChild; + Unload; + inherited; +end; + +procedure Td2dScene.BeforeStoppingChild; +begin + // does nothing in base class +end; + +procedure Td2dScene.DoLoad; +begin + // does nothing in base class +end; + +procedure Td2dScene.DoUnload; +begin + // does nothing in base class +end; + +procedure Td2dScene.DoFrame(aDelta: Single); +begin + // does nothing in base class +end; + +procedure Td2dScene.Load; +begin + if not f_Loaded then + begin + DoLoad; + f_Loaded := True; + end; +end; + +procedure Td2dScene.DoProcessEvent(var theEvent: Td2dInputEvent); +begin + // does nothing in base class +end; + +procedure Td2dScene.Frame(aDelta: Single); +begin + if HasRunningChild then + f_Child.Frame(aDelta) + else + DoFrame(aDelta); +end; + +function Td2dScene.pm_GetHasRunningChild: Boolean; +begin + Result := f_Child <> nil; +end; + +procedure Td2dScene.ProcessEvent(var theEvent: Td2dInputEvent); +begin + if HasRunningChild then + begin + f_Child.ProcessEvent(theEvent); + if not f_Child.f_RunningAsChild then + StopChild; + end + else + DoProcessEvent(theEvent); +end; + +procedure Td2dScene.Render; +begin + DoRender; + if HasRunningChild then + f_Child.Render; +end; + +procedure Td2dScene.StartChild(const aScene: Td2dScene); +begin + StopChild; + f_Child := aScene; + f_Child.f_Parent := Self; + f_Child.f_RunningAsChild := True; + f_Child.Load; +end; + +procedure Td2dScene.StopAsChild; +begin + if f_Parent <> nil then + f_RunningAsChild := False; +end; + +procedure Td2dScene.StopChild; +begin + if HasRunningChild then + begin + BeforeStoppingChild; + f_Child.Unload; + f_Child.f_Parent := nil; + f_Child := nil; + end; +end; + +procedure Td2dScene.Unload; +begin + if f_Loaded then + begin + DoUnload; + f_Loaded := False; + end; +end; + +end. \ No newline at end of file diff --git a/d2dClasses.pas b/d2dClasses.pas new file mode 100644 index 0000000..a7547c5 --- /dev/null +++ b/d2dClasses.pas @@ -0,0 +1,126 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dClasses; + +interface + +type + Td2dProtoObjectPrim = class(TObject, IInterface) + private + f_RefCount: Integer; + protected + function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; + function _AddRef: Integer; stdcall; + function _Release: Integer; stdcall; + protected + procedure Cleanup; virtual; + procedure FreeInstance; override; + public + destructor Destroy; override; + class function NewInstance: TObject; override; + function Use: Pointer; + property RefCount: Integer read f_RefCount; + end; + + Td2dProtoObject = class(Td2dProtoObjectPrim) + protected + destructor Destroy; + end; + +implementation +{.$DEFINE LogRefCount} +uses + Windows +{$IFDEF LogRefCount} + ,d2dCore +{$ENDIF} + ; + +destructor Td2dProtoObjectPrim.Destroy; +begin + if InterlockedDecrement(f_RefCount) = 0 then + begin + Inc(f_RefCount); + try + try + Cleanup; + finally + inherited Destroy; + end; + finally + Dec(f_RefCount); + end; + end; +end; + +procedure Td2dProtoObjectPrim.Cleanup; +begin + // empty in base class +end; + +procedure Td2dProtoObjectPrim.FreeInstance; +begin + if f_RefCount = 0 then + begin + {$IFDEF LogRefCount} + gD2DE.System_Log('%s destroyed.', [ClassName]); + {$ENDIF} + inherited FreeInstance; + end +end; + +class function Td2dProtoObjectPrim.NewInstance: TObject; +begin + Result := inherited NewInstance; + {$IFDEF LogRefCount} + gD2DE.System_Log('%s created.', [ClassName]); + {$ENDIF} + Td2dProtoObjectPrim(Result).Use; +end; + +function Td2dProtoObjectPrim.QueryInterface(const IID: TGUID; out Obj): HResult; +begin + if GetInterface(IID, Obj) then + Result := 0 + else + Result := E_NOINTERFACE; +end; + +function Td2dProtoObjectPrim.Use: Pointer; +begin + if Self <> nil then + InterlockedIncrement(f_RefCount); + Result := Self; +end; + +function Td2dProtoObjectPrim._AddRef: Integer; +begin + Use; + Result := f_RefCount; +end; + +function Td2dProtoObjectPrim._Release: Integer; +var + l_RC: Integer; +begin + l_RC := f_RefCount - 1; + Free; + Result := l_RC; +end; + +destructor Td2dProtoObject.Destroy; +begin + Assert(False, 'This should never happen!'); + inherited; +end; + +end. diff --git a/d2dCore.pas b/d2dCore.pas new file mode 100644 index 0000000..08a5d23 --- /dev/null +++ b/d2dCore.pas @@ -0,0 +1,2847 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- + +unit d2dCore; + +interface + +uses + Windows, Direct3D8, D3DX8, Classes, Contnrs, + + Dynamic_Bass, + + JclStringLists, + + d2dTypes, + d2dInterfaces; + +type + Td2dCore = class + private + f_Active: Boolean; + f_CurBlendMode: Integer; + f_CurPrim: Integer; + f_CurTarget: Pd2dRenderTarget; + f_CurTexture: IDirect3DTexture8; + f_Instance: Cardinal; + f_Time0 : Cardinal; // + f_Time0FPS : Cardinal; + f_FPSCount : Cardinal; // FPS in current frame counter + f_Time : Double; // Total time elapsed since start + f_DeltaTime : Single; + f_DeltaTicks: Cardinal; + f_FixedDelta: Cardinal; // precalculated - if used fixed FPS rate + f_LastTexGC : Cardinal; // last time then GC was performed + + f_AttachedPacks: IJclStringList; + + // private fields + f_LogFileName: string; + f_RectFS : TRect; + f_RectW : TRect; // rect of window + f_WHandle : HWND; + f_WStyleFS : Longint; + f_WStyleW : Longint; // Window styles (for windowed and fullscreen) + + // Graphics + f_D3DPP : D3DPRESENT_PARAMETERS; + + f_D3DPP_W : D3DPRESENT_PARAMETERS; + f_D3DPP_FS : D3DPRESENT_PARAMETERS; + + f_D3D : IDirect3D8; + f_D3DDevice: IDirect3DDevice8; + f_DontSuspend: Boolean; + + f_VB : IDirect3DVertexBuffer8; + f_IB : IDirect3DIndexBuffer8; + + f_ScreenSurf : IDirect3DSurface8; + f_ScreenDepth: IDirect3DSurface8; + + f_matView : TD3DMatrix; + f_matViewFS : TD3DMatrix; + f_matProj : TD3DMatrix; + + f_Targets : TList; + f_Textures : IJclStringList; // texture load cache + f_SndSamples : TStringList; + f_VertArray : Pd2dVertexArray; + + // property fields + f_ScreenWidth: LongWord; + f_ScreenHeight: LongWord; + f_Windowed: Boolean; + f_WindowTitle: string; + f_FixedFPS: Integer; + f_FPS: LongWord; + f_FSDX: Integer; + f_FSDY: Integer; + f_FSHeight: Integer; + f_FSScale: Single; + f_FSWidth: Integer; + f_HideMouse: Boolean; + f_InputEvents: TList; + f_KeyChar: Integer; + f_KeyCode: Integer; + f_MidiDevice: Integer; + f_MouseCaptured: Boolean; + f_MouseOver: Boolean; + f_MouseX: Single; + f_MouseY: Single; + f_MouseZ: Integer; + f_Music: Byte; + f_MusicData: Pointer; + f_MusicFadeList: TObjectList; + f_MusicHandle: DWORD; + f_MusicLooped: Boolean; + f_MusicMidiTempFile: string; + f_MusicVolume: Byte; + f_OnExit: Td2dExitEvent; + f_OnFocusGain: Td2dSimpleEvent; + f_OnFocusLost: Td2dSimpleEvent; + f_OnFrame: Td2dFrameEvent; + f_OnRender: Td2dSimpleEvent; + f_PrimCount: LongWord; + f_ScreenBPP: Byte; + f_SoundMute: Boolean; + f_SoundOn: Boolean; + f_SoundSampleRate: Integer; + f_TextureFilter: Boolean; + f_UseZBuffer: Boolean; + + procedure AdjustWindow; + procedure BassMusicStop(aChanel: Longword); + procedure ClearQueue; + procedure MakeEvent(aType: Integer; aKey: Integer; aScan: Integer; aFlags: Integer; aX, aY: Integer); + procedure GfxDone; + function GfxInit: Boolean; + procedure ClearTargets; + procedure FocusChange(const anActive: Boolean); + procedure ForceAllMusicStop; + procedure GfxRestore; + {$IFDEF D2DGIF} + function GIFasBMPLoad(aFileName: string; aSize: PLongword; aPackOnly: Boolean = False): Pointer; + {$ENDIF} + function InitLost: Boolean; + function pm_GetMusicFadeList: TObjectList; + procedure pm_SetFixedFPS(const Value: Integer); + procedure pm_SetMusicVolume(const Value: Byte); + procedure pm_SetScreenBPP(const Value: Byte); + procedure pm_SetScreenHeight(const Value: LongWord); + procedure pm_SetScreenWidth(const Value: LongWord); + procedure pm_SetSoundMute(const Value: Boolean); + procedure pm_SetTextureFilter(const Value: Boolean); + procedure pm_SetUseZBuffer(const Value: Boolean); + procedure pm_SetWindowed(const Value: Boolean); + procedure pm_SetWindowTitle(const Value: string); + procedure RenderBatch(aEndScene: Boolean = False); + procedure SetBlendMode(aBlend: Longint); + procedure SetProjectionMatrix(const aWidth, aHeight: Integer); + procedure Snd_FreeSamplePrim(aIdx: Integer); + procedure SoundSystemStart; + procedure SoundSystemStop; + procedure DoTextureCacheGarbageCollection; + procedure GfxSetViewMatrix; + procedure Input_SetMousePos(newX, newY: Integer); + procedure pm_SetHideMouse(const Value: Boolean); + property MusicFadeList: TObjectList read pm_GetMusicFadeList; + protected + procedure ReplayMIDI; + public + constructor Create(aWidth, aHeight: Integer; aWindowed: Boolean = True; aWindowTitle: string = ''); + destructor Destroy; override; + procedure FlushPrimitives; + function Gfx_BeginScene(aTarget: Pd2dRenderTarget = nil): Boolean; + + procedure Gfx_Clear(aColor: Longword); + procedure Gfx_EndScene; + procedure Gfx_RenderLine(const X1, Y1, X2, Y2: Single; const aColor: Longword; const Z: Single); + procedure Gfx_RenderTriple(const aTriple: Td2dTriple); + procedure Gfx_RenderQuad(const aQuad: Td2dQuad); + procedure Gfx_SetClipping(aX: Integer = 0; aY: Integer = 0; aWidth: Integer = 0; aHeight: Integer = 0); + procedure Gfx_SetTransform(const aX: Single = 0; const aY: Single = 0; const aDX: Single = 0; const aDY: Single = 0; + const aRot: Single = 0; const aHScale: Single = 0; const aVScale: Single = 0); + + function Input_GetEvent(var anEvent: Td2dInputEvent): Boolean; + function Input_GetKeyState(aKey: Integer): Boolean; + procedure Input_TouchMousePos; + + function System_Start: Boolean; + procedure System_Log(const aFormat: string; Args: array of TVarRec); overload; + procedure System_Log(const aString: string); overload; + procedure System_Run; + procedure System_Shutdown; + + function Resource_AttachPack(const aName: string; const aPack: Id2dResourcePack): Boolean; + procedure Resource_Free(aResource: Pointer); + function Resource_Load(const aFileName: string; aSize: PLongword; aPackOnly: Boolean = False; aSilent: Boolean = + False): Pointer; + procedure Resource_RemoveAllPacks; + procedure Resource_RemovePack(const aFileName: string); + function Resource_CreateStream(aFilename: string; aPackOnly: Boolean = False): TStream; + function Resource_Exists(aFileName: string; aPackOnly: Boolean = False): Boolean; + function Resource_FindInPacks(aWildCard: string): string; + + function Target_Create(aWidth, aHeight: Integer; aZBuffer: Boolean = False): Pd2dRenderTarget; + procedure Target_Free(aTarget: Pd2dRenderTarget); + procedure Texture_ClearCache; + + function Texture_Create(const aWidth, aHeight: Integer): Id2dTexture; + function Texture_GetWidth(aTex: IDirect3DTexture8): Integer; overload; + function Texture_GetHeight(aTex: IDirect3DTexture8): Integer; overload; + function Texture_GetWidth(aTex: Id2dTexture): Integer; overload; + function Texture_GetHeight(aTex: Id2dTexture): Integer; overload; + function Texture_FromMemory(const aData: Pointer; const aSize: LongWord; const aMipMap: Boolean = false): Id2dTexture; + function Texture_Load(const aFileName: string; aPackOnly: Boolean = False; + aUseCache: Boolean = True; const aMipMap: Boolean = False; aPicSize: PPoint = nil): Id2dTexture; + procedure Texture_RemoveFromCache(aFileName: string); overload; + procedure Texture_RemoveFromCache(const aTexture: Id2dTexture); overload; + + { + function Snd_StreamLoad(aFilename: PChar): HSTREAM; + function Snd_StreamPlay(aStream: HSTREAM; aLooped: Boolean = True; aVolume: + Integer = -1): HCHANNEL; + procedure Snd_ChannelStop(aChannel: HCHANNEL); + procedure Snd_StreamFree(aStream: HSTREAM); + } + function Snd_PreLoadSample(aFileName: string; aPackOnly: Boolean = False): Integer; + procedure Snd_PlaySample(aFilename: string; aVolume: Byte = 255; aLooped: Boolean = False; aPackOnly: Boolean = False); + procedure Snd_FreeSample(aFilename: string); + procedure Snd_FreeAll; + + procedure Music_StreamPlay(aFileName: string; aLooped: Boolean = True; + aFadeinTime: Longword =0; aPackOnly: Boolean = False); + procedure Music_MIDIPlay(aFileName: string; aLooped: Boolean = True; aPackOnly: Boolean = False); + procedure Music_MODPlay(aFileName: string; aLooped: Boolean = True; aFadeinTime: Longword = 0; aPackOnly: Boolean = + False); + procedure Music_Pause; + procedure Music_Play(aFileName: string; aLooped: Boolean = True; aFadeinTime: Longword = 0; aPackOnly: Boolean = False); + procedure Music_Resume; + procedure Music_SetVolume(const aVolume: Byte; const aFadeTime: Longword = 0); + procedure Music_Stop(aFadeoutTime: Longword = 0); + procedure Snd_StopAll; + procedure Snd_StopSample(aFilename: string); + procedure System_SoundPause; + procedure System_SoundResume; + function Texture_CreatePrim(aData: Pointer; aSize: Longword; aMipMap: Boolean = False; aColorKey: Td2dColor = 0): + Id2dTexture; + + property Active: Boolean read f_Active; + property FixedFPS: Integer read f_FixedFPS write pm_SetFixedFPS; + property OnFocusGain: Td2dSimpleEvent read f_OnFocusGain write f_OnFocusGain; + property OnFocusLost: Td2dSimpleEvent read f_OnFocusLost write f_OnFocusLost; + property DontSuspend: Boolean read f_DontSuspend write f_DontSuspend; + property FPS: LongWord read f_FPS; + property OnFrame: Td2dFrameEvent read f_OnFrame write f_OnFrame; + property OnRender: Td2dSimpleEvent read f_OnRender write f_OnRender; + //1 Hide mouse cursor + property HideMouse: Boolean read f_HideMouse write pm_SetHideMouse; + property KeyChar: Integer read f_KeyChar; + property KeyCode: Integer read f_KeyCode; + property MouseOver: Boolean read f_MouseOver; + property MouseX: Single read f_MouseX; + property MouseY: Single read f_MouseY; + property MouseZ: Integer read f_MouseZ; + property OnExit: Td2dExitEvent read f_OnExit write f_OnExit; + property D3DDevice: IDirect3DDevice8 read f_D3DDevice; + property LogFileName: string read f_LogFileName write f_LogFileName; + property Music: Byte read f_Music; + property MusicVolume: Byte read f_MusicVolume write pm_SetMusicVolume; + property ScreenBPP: Byte read f_ScreenBPP write pm_SetScreenBPP; + property ScreenHeight: LongWord read f_ScreenHeight write pm_SetScreenHeight; + property ScreenWidth: LongWord read f_ScreenWidth write pm_SetScreenWidth; + property SoundMute: Boolean read f_SoundMute write pm_SetSoundMute; + property SoundOn: Boolean read f_SoundOn write f_SoundOn; + property SoundSampleRate: Integer read f_SoundSampleRate write + f_SoundSampleRate; + property TextureFilter: Boolean read f_TextureFilter write pm_SetTextureFilter; + property UseZBuffer: Boolean read f_UseZBuffer write pm_SetUseZBuffer; + property WHandle: HWND read f_WHandle write f_WHandle; + property Windowed: Boolean read f_Windowed write pm_SetWindowed; + property WindowTitle: string read f_WindowTitle write pm_SetWindowTitle; + end; + +var + gD2DE: Td2dCore = nil; + +procedure D2DInit(aWidth, aHeight: Integer; aWindowed: Boolean = True; aWindowTitle: string = ''); +procedure D2DDone; + +implementation +uses + Messages, + MMSystem, + SysUtils, + {$IFDEF TRACE_STACK} + JclDebug, + {$ENDIF} + Types, + Math, + + d2dTexture + + {$IFDEF D2DGIF} + ,Graphics + ,GIFImage + ,d2dUtils + {$ENDIF} + + ; + +const + cWinClassName = 'D2DENGINE_WNDCLASS'; + c_TexGCThreshold = 10000; // 10 sec + c_NoMouseCoord = 5000000; + +type + Td2dMusicHolder = class + public + Chanel: Longword; + Data : Pointer; + constructor Create(aChanel: Longword; aData: Pointer = nil); + destructor Destroy; override; + end; + +function FormatID(aFormat: D3DFORMAT): Integer; +begin + case aFormat of + D3DFMT_R5G6B5: Result := 1; + D3DFMT_X1R5G5B5: Result := 2; + D3DFMT_A1R5G5B5: Result := 3; + D3DFMT_X8R8G8B8: Result := 4; + D3DFMT_A8R8G8B8: Result := 5; + else + Result := 0; + end; +end; + +function WindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; +var + l_X, l_Y: SmallInt; + + function IsRepeatFlag: Integer; + begin + if (lParam and $40000000) <> 0 then + Result := D2DINP_REPEAT + else + Result := 0; + end; +begin + Result := 0; + case Msg of + WM_CREATE: Exit; + + WM_PAINT : + if Assigned(gD2DE.f_OnRender) then + gD2DE.f_OnRender(); + + WM_DESTROY: + begin + PostQuitMessage(0); + Exit; + end; + + WM_ACTIVATEAPP: + begin + if (Assigned(gD2DE.f_D3D) and gD2DE.Active) <> (wparam <> 0) then + gD2DE.FocusChange(wparam <> 0); + Exit; + end; + + WM_SETCURSOR: + begin + if gD2DE.Active and (LongRec(lParam).Lo = HTCLIENT) and gD2DE.HideMouse then + SetCursor(0) + else + SetCursor(LoadCursor(0, IDC_ARROW)); + {if gD2DE.Active and (LongRec(lParam).Lo = HTBOTTOMRIGHT) then + SetCursor(LoadCursor(0, IDC_SIZENWSE));} + Exit; + end; + + WM_SYSKEYDOWN: + if wParam = VK_F4 then + begin + if Assigned(gD2DE.f_OnExit) then + begin + if gD2DE.OnExit then + Result := DefWindowProc(hWnd, Msg, wParam, lParam); + end + else + Result := DefWindowProc(hWnd, Msg, wParam, lParam); + Exit; + end + else + begin + gD2DE.MakeEvent(INPUT_KEYDOWN, wParam, LongRec(lParam).Hi and $FF, IsRepeatFlag, c_NoMouseCoord, c_NoMouseCoord); + Exit; + end; + + WM_KEYDOWN: + begin + gD2DE.MakeEvent(INPUT_KEYDOWN, wParam, LongRec(lParam).Hi and $FF, IsRepeatFlag, c_NoMouseCoord, c_NoMouseCoord); + { + FillChar(l_MsgRec, SizeOf(TMsg), 0); + l_MsgRec.hwnd := hWnd; + l_MsgRec.message := WM_KEYDOWN; + l_MsgRec.wParam := wParam; + l_MsgRec.lParam := lParam; + //TranslateMessage(l_MsgRec); + } + Exit; + end; + + WM_SYSKEYUP: + begin + gD2DE.MakeEvent(INPUT_KEYUP, wParam, LongRec(lParam).Hi and $FF, 0, c_NoMouseCoord, c_NoMouseCoord); + Exit; + end; + + WM_KEYUP: + begin + gD2DE.MakeEvent(INPUT_KEYUP, wParam, LongRec(lParam).Hi and $FF, 0, c_NoMouseCoord, c_NoMouseCoord); + Exit; + end; + + WM_LBUTTONDOWN: + begin + SetFocus(gD2DE.f_WHandle); + gD2DE.MakeEvent(INPUT_MBUTTONDOWN, D2DK_LBUTTON, 0, 0, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + WM_MBUTTONDOWN: + begin + SetFocus(gD2DE.f_WHandle); + gD2DE.MakeEvent(INPUT_MBUTTONDOWN, D2DK_MBUTTON, 0, 0, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + WM_RBUTTONDOWN: + begin + SetFocus(gD2DE.f_WHandle); + gD2DE.MakeEvent(INPUT_MBUTTONDOWN, D2DK_RBUTTON, 0, 0, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + + WM_LBUTTONDBLCLK: + begin + gD2DE.MakeEvent(INPUT_MBUTTONDOWN, D2DK_LBUTTON, 0, D2DINP_REPEAT, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + WM_MBUTTONDBLCLK: + begin + gD2DE.MakeEvent(INPUT_MBUTTONDOWN, D2DK_MBUTTON, 0, D2DINP_REPEAT, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + WM_RBUTTONDBLCLK: + begin + gD2DE.MakeEvent(INPUT_MBUTTONDOWN, D2DK_RBUTTON, 0, D2DINP_REPEAT, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + + WM_LBUTTONUP: + begin + gD2DE.MakeEvent(INPUT_MBUTTONUP, D2DK_LBUTTON, 0, 0, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + WM_MBUTTONUP: + begin + gD2DE.MakeEvent(INPUT_MBUTTONUP, D2DK_MBUTTON, 0, 0, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + WM_RBUTTONUP: + begin + gD2DE.MakeEvent(INPUT_MBUTTONUP, D2DK_RBUTTON, 0, 0, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + + WM_MOUSEMOVE: + begin + l_X := SmallInt(LongRec(lParam).Lo); + l_Y := SmallInt(LongRec(lParam).Hi); + //gD2DE.System_Log('MM: %d, %d', [l_X, l_Y]); + gD2DE.Input_SetMousePos(l_X, l_Y); + Exit; + end; + WM_MOUSEWHEEL: + begin + gD2DE.MakeEvent(INPUT_MOUSEWHEEL, SmallInt(LongRec(wParam).Hi) div 120, + 0, 0, SmallInt(LongRec(lParam).Lo), SmallInt(LongRec(lParam).Hi)); + Exit; + end; + + WM_SYSCOMMAND: + if wParam = SC_CLOSE then + begin + if Assigned(gD2DE.f_OnExit) then + begin + if gD2DE.OnExit then + begin + Result := DefWindowProc(hWnd, Msg, wParam, lParam); + gD2DE.f_Active := False; + end; + end + else + begin + Result := DefWindowProc(hWnd, Msg, wParam, lParam); + gD2DE.f_Active := False; + end; + Exit; + end; + + MM_MCINOTIFY: + begin + if (wParam = MCI_NOTIFY_SUCCESSFUL) then + if (gD2DE.f_MusicLooped) then + gD2DE.ReplayMIDI + else + gD2DE.Music_Stop; + end; + end; + + Result := DefWindowProc(hWnd, Msg, wParam, lParam); +end; + +procedure D2DInit(aWidth, aHeight: Integer; aWindowed: Boolean = True; aWindowTitle: string = ''); +begin + if gD2DE = nil then + Td2dCore.Create(aWidth, aHeight, aWindowed, aWindowTitle); +end; + +procedure D2DDone; +begin + FreeAndNil(gD2DE); +end; + +constructor Td2dCore.Create(aWidth, aHeight: Integer; aWindowed: Boolean = True; aWindowTitle: string = ''); +begin + inherited Create; + f_ScreenWidth := aWidth; + f_ScreenHeight := aHeight; + f_Windowed := aWindowed; + f_Active := True; + if aWindowTitle = '' then + f_WindowTitle := 'Delphi 2D Engine Application' + else + f_WindowTitle := aWindowTitle; + f_FixedFPS := D2D_FPS_UNLIMITED; + f_FixedDelta := 0; + f_Instance := 0; + f_WHandle := 0; + f_ScreenBPP := 32; + f_UseZBuffer := False; + f_Targets := TList.Create; + f_Textures := JclStringList; + f_InputEvents := TList.Create; + f_AttachedPacks := JclStringList; + f_AttachedPacks.CaseSensitive := False; + f_AttachedPacks.Sorted := True; + f_SndSamples := TStringList.Create; + f_TextureFilter := True; + f_OnFocusGain := nil; + f_OnFocusLost := nil; + f_OnFrame := nil; + f_OnRender := nil; + f_CurTarget := nil; + f_HideMouse := True; + f_D3D := nil; + f_D3DDevice := nil; + f_SoundOn := True; + f_SoundSampleRate := 44100; + f_LogFileName := ChangeFileExt(ParamStr(0), '.log'); + gD2DE := Self; + {$IFDEF TRACE_STACK} + // Enable raw mode (default mode uses stack frames which aren't always generated by the compiler) + Include(JclStackTrackingOptions, stRawMode); + // Disable stack tracking in dynamically loaded modules (it makes stack tracking code a bit faster) + Include(JclStackTrackingOptions, stStaticModuleList); + // Initialize Exception tracking + JclStartExceptionTracking; + {$ENDIF} +end; + +destructor Td2dCore.Destroy; +begin + {$IFDEF TRACE_STACK} + // Uninitialize Exception tracking + JclStopExceptionTracking; + {$ENDIF} + ClearTargets; + f_Targets.Free; + Texture_ClearCache; + ClearQueue; + Resource_RemoveAllPacks; + f_AttachedPacks := nil; + f_SndSamples.Free; + ForceAllMusicStop; + FreeAndNil(f_MusicFadeList); + f_Textures := nil; + inherited; +end; + +procedure Td2dCore.AdjustWindow; +var + l_Rect : TRect; + l_Style : Longint; +begin + if f_Windowed then + begin + l_Rect := f_RectW; + l_Style := f_WStyleW; + end + else + begin + l_Rect := f_RectFS; + l_Style := f_WStyleFS; + end; + SetWindowLong(f_WHandle, GWL_STYLE, l_Style); + + l_Style := GetWindowLong(f_WHandle, GWL_EXSTYLE); + if f_Windowed then + begin + SetWindowLong(f_WHandle, GWL_EXSTYLE, l_Style and not WS_EX_TOPMOST); + with l_Rect do + SetWindowPos(f_WHandle, HWND_NOTOPMOST, Left, Top, Right-Left, Bottom-Top, SWP_FRAMECHANGED); + end + else + begin + SetWindowLong(f_WHandle, GWL_EXSTYLE, l_Style or WS_EX_TOPMOST); + with l_Rect do + SetWindowPos(f_WHandle, HWND_TOPMOST, Left, Top, Right-Left, Bottom-Top, SWP_FRAMECHANGED); + end; +end; + +procedure Td2dCore.BassMusicStop(aChanel: Longword); +var + I: Integer; + l_FH: Td2dMusicHolder; +begin + if aChanel = f_MusicHandle then + begin + if f_Music = mp_Stream then + Resource_Free(f_MusicData); + f_MusicHandle := 0; + f_Music := mp_None; + end + else + if f_MusicFadeList <> nil then + begin + for I := 0 to MusicFadeList.Count - 1 do + begin + l_FH := Td2dMusicHolder(MusicFadeList[I]); + if l_FH.Chanel = aChanel then + begin + MusicFadeList.Delete(I); + Break; + end; + end; + end; +end; + +procedure Td2dCore.ClearQueue; +var + I: Integer; +begin + f_InputEvents.Pack; + for I := 0 to f_InputEvents.Count - 1 do + Dispose(Pd2dInputEvent(f_InputEvents.Items[I])); + f_InputEvents.Clear; + f_KeyCode := 0; + f_KeyChar := 0; + f_MouseZ := 0; +end; + +procedure Td2dCore.MakeEvent(aType: Integer; aKey: Integer; aScan: Integer; aFlags: Integer; aX, aY: Integer); +var + l_Event: Pd2dInputEvent; + l_Pnt: TPoint; + l_KbdState: TKeyboardState; +begin + New(l_Event); + l_Event.EventType := aType; + l_Event.KeyChar := 0; + l_Event.KeyScan := aScan; + + if l_Pnt.X <> c_NoMouseCoord then + begin + l_Pnt.X := aX; + l_Pnt.Y := aY; + end + else + begin + l_Pnt.X := Round(f_MouseX); + l_Pnt.Y := Round(f_MouseY); + end; + + GetKeyboardState(l_KbdState); + + if (aType = INPUT_KEYDOWN) or (aType = INPUT_KEYUP) then + ToAscii(aKey, aScan, l_KbdState, @(l_Event^.KeyChar), 0); + + if aType = INPUT_MOUSEWHEEL then + begin + l_Event.KeyCode := 0; + l_Event.Wheel := aKey; + //ScreenToClient(f_WHandle, l_Pnt); + end + else + begin + l_Event.KeyCode := aKey; + l_Event.Wheel := 0; + end; + + if aType = INPUT_MBUTTONDOWN then + begin + SetCapture(f_WHandle); + f_MouseCaptured := True; + end; + + if aType = INPUT_MBUTTONUP then + begin + ReleaseCapture; + Input_TouchMousePos; + l_Pnt.X := Round(f_MouseX); + l_Pnt.Y := Round(f_MouseY); + f_MouseCaptured := False; + end; + + if (l_KbdState[VK_SHIFT] and $80) <> 0 then + aFlags := aFlags or D2DINP_SHIFT; + + if (l_KbdState[VK_CONTROL] and $80) <> 0 then + aFlags := aFlags or D2DINP_CTRL; + + if (l_KbdState[VK_MENU] and $80) <> 0 then + aFlags := aFlags or D2DINP_ALT; + + if (l_KbdState[VK_CAPITAL] and $80) <> 0 then + aFlags := aFlags or D2DINP_CAPSLOCK; + + if (l_KbdState[VK_SCROLL] and $80) <> 0 then + aFlags := aFlags or D2DINP_SCROLLLOCK; + + if (l_KbdState[VK_NUMLOCK] and $80) <> 0 then + aFlags := aFlags or D2DINP_NUMLOCK; + + l_Event.Flags := aFlags; + + l_Event.X := l_Pnt.X; + l_Event.Y := l_Pnt.Y; + + f_InputEvents.Add(l_Event); + + if (l_Event.EventType = INPUT_KEYDOWN) or (l_Event.EventType = INPUT_MBUTTONDOWN) then + begin + f_KeyCode := l_Event.KeyCode; + f_KeyChar := l_Event.KeyChar; + end + else + if l_Event.EventType = INPUT_MOUSEMOVE then + begin + f_MouseX := l_Event.X; + f_MouseY := l_Event.Y; + end + else + if l_Event.EventType = INPUT_MOUSEWHEEL then + f_MouseZ := f_MouseZ + l_Event.Wheel; +end; + +procedure Td2dCore.ClearTargets; +var + I: Integer; +begin + f_Targets.Pack; + for I := 0 to f_Targets.Count - 1 do + Dispose(Pd2dRenderTarget(f_Targets.Items[I])); + f_Targets.Clear; +end; + +procedure Td2dCore.ReplayMIDI; +begin + if mciSendString('play d2dmidi from 0 notify', nil, 0, f_WHandle) = 0 then + f_Music := mp_MIDI + else + Music_Stop; +end; + +procedure Td2dCore.FlushPrimitives; +begin + RenderBatch; +end; + +procedure Td2dCore.FocusChange(const anActive: Boolean); +begin + f_Active := anActive; + if f_Active then + begin + GfxRestore; + System_SoundResume; + if Assigned(f_OnFocusGain) then + f_OnFocusGain; + end + else + begin + System_SoundPause; + if Assigned(f_OnFocusLost) then + f_OnFocusLost; + end; +end; + +procedure Td2dCore.GfxDone; +begin + f_ScreenSurf := nil; + f_ScreenDepth := nil; + ClearTargets; + if Assigned(f_IB) then + begin + f_D3DDevice.SetIndices(nil, 0); + f_IB := nil; + end; + + if Assigned(f_VB) then + begin + if f_VertArray <> nil then + begin + f_VB.Unlock; + f_VertArray := nil; + end; + f_D3DDevice.SetStreamSource(0, nil, SizeOf(Td2dVertex)); + f_VB := nil; + end; + + if Assigned(f_D3DDevice) then + f_D3DDevice := nil; + + if Assigned(f_D3D) then + f_D3D := nil; +end; + +function Td2dCore.GfxInit: Boolean; +const + cModeNames: array [0..5] of string = + ('UNKNOWN', 'R5G6B5', 'X1R5G5B5', 'A1R5G5B5', 'X8R8G8B8', 'A8R8G8B8'); +var + l_AdID : D3DADAPTER_IDENTIFIER8; + l_DVer : array [1..2] of Word; + l_Mode : D3DDISPLAYMODE; + l_Tmp: TD3DMatrix; + l_XScale, l_YScale: Single; + +begin + Result := False; + // Creating DirectX8 interface... + f_D3D := Direct3DCreate8(120); + if f_D3D = nil then + begin + System_Log('Can''t create D3D interface'); + Exit; + end; + + // Get adapter info + f_D3D.GetAdapterIdentifier(D3DADAPTER_DEFAULT, D3DENUM_NO_WHQL_LEVEL, l_AdID); + System_Log('D3D Driver : %s', [l_AdID.Driver]); + System_Log('Description: %s', [l_AdID.Description]); + Move(l_AdID.DriverVersion, l_DVer, SizeOf(Cardinal)); + System_Log('Version: %d.%d', [l_DVer[2], l_DVer[1]]); + + + // Determine windowed mode presentation parameters + if Failed(f_D3D.GetAdapterDisplayMode(D3DADAPTER_DEFAULT, l_Mode)) or (l_Mode.Format = D3DFMT_UNKNOWN) then + begin + System_Log('Can''t determine desktop video mode'); + Exit; + end; + + FillChar(f_D3DPP_W, SizeOf(f_D3DPP_W), 0); + with f_D3DPP_W do + begin + BackBufferWidth := f_ScreenWidth; + BackBufferHeight := f_ScreenHeight; + BackBufferFormat := l_Mode.Format; + BackBufferCount := 1; + MultiSampleType := D3DMULTISAMPLE_NONE; + hDeviceWindow := f_WHandle; + Windowed := True; + end; + + if f_FixedFPS = D2D_FPS_VSYNC then + f_D3DPP_W.SwapEffect := D3DSWAPEFFECT_COPY_VSYNC + else + f_D3DPP_W.SwapEffect := D3DSWAPEFFECT_COPY; + + if f_UseZBuffer then + begin + f_D3DPP_W.EnableAutoDepthStencil := True; + f_D3DPP_W.AutoDepthStencilFormat := D3DFMT_D16; + end; + + // Determine fullscreen mode presentation parameters + FillChar(f_D3DPP_FS, SizeOf(f_D3DPP_W), 0); + with f_D3DPP_FS do + begin + BackBufferWidth := l_Mode.Width; + BackBufferHeight := l_Mode.Height; + BackBufferFormat := l_Mode.Format; + BackBufferCount := 1; + MultiSampleType := D3DMULTISAMPLE_NONE; + hDeviceWindow := f_WHandle; + Windowed := False; + SwapEffect := D3DSWAPEFFECT_FLIP; + FullScreen_RefreshRateInHz := D3DPRESENT_RATE_DEFAULT; + + if f_FixedFPS = D2D_FPS_VSYNC then + FullScreen_PresentationInterval := D3DPRESENT_INTERVAL_ONE + else + FullScreen_PresentationInterval := D3DPRESENT_INTERVAL_IMMEDIATE; + + if f_UseZBuffer then + begin + EnableAutoDepthStencil := True; + AutoDepthStencilFormat := D3DFMT_D16; + end; + end; // with + + if f_Windowed then + f_D3DPP := f_D3DPP_W + else + f_D3DPP := f_D3DPP_FS; + + // Create device + if Failed(f_D3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, f_WHandle, + D3DCREATE_HARDWARE_VERTEXPROCESSING or D3DCREATE_FPU_PRESERVE, f_D3DPP, f_D3DDevice)) then + begin + System_Log('The hardware vertex processing is not available. Switching to software one.'); + if Failed(f_D3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, f_WHandle, + D3DCREATE_SOFTWARE_VERTEXPROCESSING or D3DCREATE_FPU_PRESERVE, f_D3DPP, f_D3DDevice)) then + begin + System_Log('Can''t create D3D device'); + Exit; + end; + end; + + AdjustWindow; + + System_Log('Mode: %d x %d x %s',[f_ScreenWidth, f_ScreenHeight, + cModeNames[FormatID(f_D3DPP.BackBufferFormat)]]); + + SetProjectionMatrix(f_D3DPP.BackBufferWidth, f_D3DPP.BackBufferHeight); + + // preparing view matrix for fullscreen mode + f_FSScale := 1; + if (f_D3DPP_W.BackBufferWidth <> f_D3DPP_FS.BackBufferWidth) or (f_D3DPP_W.BackBufferHeight <> f_D3DPP_FS.BackBufferHeight) then + begin + l_XScale := f_D3DPP_FS.BackBufferWidth / f_D3DPP_W.BackBufferWidth; + l_YScale := f_D3DPP_FS.BackBufferHeight / f_D3DPP_W.BackBufferHeight; + if l_YScale < l_XScale then + begin + f_FSScale := l_YScale; + f_FSWidth := Trunc(f_D3DPP_W.BackBufferWidth * f_FSScale + 0.5); + f_FSHeight := f_D3DPP_FS.BackBufferHeight; + f_FSDX := (f_D3DPP_FS.BackBufferWidth - f_FSWidth) div 2; + f_FSDY := 0; + end + else + begin + f_FSScale := l_XScale; + f_FSWidth := f_D3DPP_FS.BackBufferWidth; + f_FSHeight := Trunc(f_D3DPP_W.BackBufferHeight * f_FSScale + 0.5); + f_FSDX := 0; + f_FSDY := (f_D3DPP_FS.BackBufferHeight - f_FSHeight) div 2; + end; + D3DXMatrixScaling(f_matViewFS, f_FSScale, f_FSScale, 1); + D3DXMatrixTranslation(l_Tmp, f_FSDX, f_FSDY, 0); + D3DXMatrixMultiply(f_MatViewFS, f_MatViewFS, l_Tmp); + end + else + D3DXMatrixIdentity(f_matViewFS); + + GfxSetViewMatrix; + + if not InitLost then + Exit; + + Gfx_Clear(0); + + Result := True; +end; + +procedure Td2dCore.GfxRestore; +begin + if not Assigned(f_D3DDevice) then + Exit; + if f_D3DDevice.TestCooperativeLevel = D3DERR_DEVICELOST then //<> D3DERR_DEVICENOTRESET then + Exit; + f_ScreenSurf := nil; + f_ScreenDepth := nil; + ClearTargets; + + if Assigned(f_IB) then + begin + f_D3DDevice.SetIndices(nil, 0); + f_IB := nil; + end; + + if Assigned(f_VB) then + begin + f_D3DDevice.SetStreamSource(0, nil, SizeOf(Td2dVertex)); + f_VB := nil; + end; + + f_D3DDevice.Reset(f_D3DPP); + InitLost; +end; + +function Td2dCore.Gfx_BeginScene(aTarget: Pd2dRenderTarget = nil): Boolean; +var + l_Surf, l_Depth : IDirect3DSurface8; +begin + Result := False; + if f_VertArray <> nil then + begin + System_Log('Gfx_BeginScene: Scene is already being rendered.'); + Exit; + end; + + if aTarget <> f_CurTarget then + begin + if aTarget <> nil then + begin + aTarget.Tex.GetSurfaceLevel(0, l_Surf); + l_Depth := aTarget.Depth; + end + else + begin + l_Surf := f_ScreenSurf; + l_Depth := f_ScreenDepth; + end; + if Failed(f_D3DDevice.SetRenderTarget(l_Surf, l_Depth)) then + begin + System_Log('Gfx_BeginScene: Can''t set render target.'); + Exit; + end; + if aTarget <> nil then + begin + l_Surf := nil; + if aTarget.Depth <> nil then + f_D3DDevice.SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE) + else + f_D3DDevice.SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); + SetProjectionMatrix(aTarget.Width, aTarget.Height); + end + else + begin + if f_UseZBuffer then + f_D3DDevice.SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE) + else + f_D3DDevice.SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); + SetProjectionMatrix(f_ScreenWidth, f_ScreenHeight); + end; + f_D3DDevice.SetTransform(D3DTS_PROJECTION, f_matProj); + GfxSetViewMatrix; + f_D3DDevice.SetTransform(D3DTS_VIEW, f_matView); + + f_CurTarget := aTarget; + end; + + f_D3DDevice.BeginScene; + f_VB.Lock(0,0, PByte(f_VertArray), 0); + + Result := True; +end; + +procedure Td2dCore.Gfx_Clear(aColor: Longword); +begin + if f_CurTarget <> nil then + begin + if f_CurTarget.Depth <> nil then + f_D3DDevice.Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, aColor, 1.0, 0) + else + f_D3DDevice.Clear(0, nil, D3DCLEAR_TARGET, aColor, 1.0, 0); + end + else + begin + if f_UseZBuffer then + f_D3DDevice.Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, aColor, 1.0, 0) + else + f_D3DDevice.Clear(0, nil, D3DCLEAR_TARGET, aColor, 1.0, 0); + end; +end; + +procedure Td2dCore.Gfx_EndScene; +begin + if f_VertArray = nil then + Exit; + RenderBatch(True); + f_D3DDevice.EndScene; + if f_CurTarget = nil then // if current target is screen then populate + f_D3DDevice.Present(nil, nil, 0, nil); + f_VertArray := nil; +end; + +procedure Td2dCore.Gfx_RenderLine(const X1, Y1, X2, Y2: Single; const aColor: Longword; const Z: Single); +var + I: Integer; +begin + if (f_CurPrim <> D2DPRIM_LINES) or (f_PrimCount >= VertexBufferSize div D2DPRIM_LINES) or (f_CurTexture <> nil) or + (f_CurBlendMode <> BLEND_DEFAULT) then + begin + RenderBatch; + f_CurPrim := D2DPRIM_LINES; + if f_CurBlendMode <> BLEND_DEFAULT then + SetBlendMode(BLEND_DEFAULT); + if f_CurTexture <> nil then + begin + f_D3DDevice.SetTexture(0, nil); + f_CurTexture := nil; + end; + end; + I := f_PrimCount * D2DPRIM_LINES; + with f_VertArray[I] do + begin + X := X1; + Y := Y1; + Z := Z; + Col := aColor; + TX := 0; TY := 0; + end; + with f_VertArray[I+1] do + begin + X := X2; + Y := Y2; + Z := Z; + Col := aColor; + TX := 0; TY := 0; + end; + Inc(f_PrimCount); +end; + +procedure Td2dCore.Gfx_RenderTriple(const aTriple: Td2dTriple); +begin + if (f_CurPrim <> D2DPRIM_TRIPLES) or (f_PrimCount >= VertexBufferSize div D2DPRIM_TRIPLES) or + (f_CurTexture <> aTriple.Tex) or (f_CurBlendMode <> aTriple.Blend) then + begin + RenderBatch; + f_CurPrim := D2DPRIM_TRIPLES; + if f_CurBlendMode <> aTriple.Blend then + SetBlendMode(aTriple.Blend); + if f_CurTexture <> aTriple.Tex then + begin + f_D3DDevice.SetTexture(0, aTriple.Tex); + f_CurTexture := aTriple.Tex; + end; + end; + Move(aTriple.V, f_VertArray[f_PrimCount*D2DPRIM_TRIPLES], SizeOf(Td2dVertex)*D2DPRIM_TRIPLES); + Inc(f_PrimCount); +end; + +procedure Td2dCore.Gfx_RenderQuad(const aQuad: Td2dQuad); +begin + if (f_CurPrim <> D2DPRIM_QUADS) or (f_PrimCount >= VertexBufferSize div D2DPRIM_QUADS) or + (f_CurTexture <> aQuad.Tex) or (f_CurBlendMode <> aQuad.Blend) then + begin + RenderBatch; + f_CurPrim := D2DPRIM_QUADS; + if f_CurBlendMode <> aQuad.Blend then + SetBlendMode(aQuad.Blend); + if f_CurTexture <> aQuad.Tex then + begin + f_D3DDevice.SetTexture(0, aQuad.Tex); + f_CurTexture := aQuad.Tex; + end; + end; + Move(aQuad.V, f_VertArray[f_PrimCount*D2DPRIM_QUADS], SizeOf(Td2dVertex)*D2DPRIM_QUADS); + Inc(f_PrimCount); +end; + +procedure Td2dCore.Gfx_SetClipping(aX: Integer = 0; aY: Integer = 0; aWidth: Integer = 0; aHeight: Integer = 0); +var + l_VP: D3DVIEWPORT8; + l_ScrWidth, l_ScrHeight: Integer; + l_Tmp: TD3DXMatrix; + l_L: Single; + l_R: Single; + l_B: Single; + l_T: Single; +begin + if not Assigned(f_CurTarget) then + begin + if f_Windowed then + begin + l_ScrWidth := f_ScreenWidth; + l_ScrHeight := f_ScreenHeight; + end + else + begin + l_ScrWidth := f_D3DPP_FS.BackBufferWidth; + l_ScrHeight := f_D3DPP_FS.BackBufferHeight; + aX := Round(aX * f_FSScale + f_FSDX); + aY := Round(aY * f_FSScale + f_FSDY); + aWidth := Round(aWidth * f_FSScale); + aHeight := Round(aHeight * f_FSScale); + end; + end + else + begin + l_ScrWidth := Texture_GetWidth(f_CurTarget.Tex); + l_ScrHeight := Texture_GetHeight(f_CurTarget.Tex); + end; + + if aWidth = 0 then + begin + l_VP.X := 0; + l_VP.Y := 0; + l_VP.Width := l_ScrWidth; + l_VP.Height := l_ScrHeight; + l_VP.MinZ := 0.0; + l_VP.MaxZ := 1.0; + end + else + begin + if aX < 0 then + begin + aWidth := aWidth + aX; + aX := 0; + end; + if aY < 0 then + begin + aHeight := aHeight + aY; + aY := 0; + end; + + if aX + aWidth > l_ScrWidth then + aWidth := l_ScrWidth - aX; + if aY + aHeight > l_ScrHeight then + aHeight := l_ScrHeight - aY; + + with l_VP do + begin + X := aX; + Y := aY; + Width := aWidth; + Height := aHeight; + MinZ := 0.0; + MaxZ := 1.0; + end; + end; + + RenderBatch; + f_D3DDevice.SetViewport(l_VP); + + D3DXMatrixScaling(f_matProj, 1.0, -1.0, 1.0); + D3DXMatrixTranslation(l_Tmp, -0.5{ * f_FSScale}, +0.5{ * f_FSScale}, 0.0); + D3DXMatrixMultiply(f_matProj, f_matProj, l_Tmp); + l_L := l_VP.X; + l_R := l_VP.X+l_VP.Width; + l_B := l_VP.Y+l_VP.Height; + l_T := l_VP.Y; + D3DXMatrixOrthoOffCenterLH(l_Tmp, l_L, l_R, -l_B, -l_T, l_VP.MinZ, l_VP.MaxZ); + D3DXMatrixMultiply(f_matProj, f_matProj, l_Tmp); + f_D3DDevice.SetTransform(D3DTS_PROJECTION, f_matProj); +end; + +procedure Td2dCore.Gfx_SetTransform(const aX: Single = 0; const aY: Single = 0; const aDX: Single = 0; const aDY: + Single = 0; const aRot: Single = 0; const aHScale: Single = 0; const aVScale: Single = 0); +var + l_Tmp: TD3DXMatrix; +begin + if aVScale = 0 then + GfxSetViewMatrix// D3DXMatrixIdentity(f_matView) + else + begin + D3DXMatrixTranslation(f_matView, -aX, -aY, 0.0); + D3DXMatrixScaling(l_Tmp, aHScale, aVScale, 1.0); + D3DXMatrixMultiply(f_matView, f_matView, l_Tmp); + D3DXMatrixRotationZ(l_Tmp, -aRot); + D3DXMatrixMultiply(f_matView, f_matView, l_Tmp); + D3DXMatrixTranslation(l_Tmp, aX+aDX, aY+aDY, 0.0); + D3DXMatrixMultiply(f_matView, f_matView, l_Tmp); + end; + RenderBatch; + f_D3DDevice.SetTransform(D3DTS_VIEW, f_matView); +end; + +function Td2dCore.InitLost: Boolean; +var + I: Integer; + l_Target: Pd2dRenderTarget; + l_Index : PWord; + N: Integer; +begin + Result := False; + f_ScreenSurf := nil; + f_ScreenDepth := nil; + f_D3DDevice.GetRenderTarget(f_ScreenSurf); + f_D3DDevice.GetDepthStencilSurface(f_ScreenDepth); + + // Rebuild all render target surfaces + for I := 0 to f_Targets.Count - 1 do + begin + l_Target := Pd2dRenderTarget(f_Targets.Items[I]); + if Assigned(l_Target) then + begin + if l_Target.Tex <> nil then + D3DXCreateTexture(f_D3DDevice, l_Target.Width, l_Target.Height, 1, D3DUSAGE_RENDERTARGET, + f_D3DPP.BackBufferFormat, D3DPOOL_DEFAULT, l_Target.Tex); + if l_Target.Depth <> nil then + f_D3DDevice.CreateDepthStencilSurface(l_Target.Width, l_Target.Height, + D3DFMT_D16, D3DMULTISAMPLE_NONE, l_Target.Depth); + end; + end; + + // Create vertex buffer + if Failed(f_D3DDevice.CreateVertexBuffer(VertexBufferSize*SizeOf(Td2dVertex), + D3DUSAGE_WRITEONLY, D3DFVF_D2DVERTEX, D3DPOOL_DEFAULT, f_VB)) then + begin + System_Log('Can''t create D3D vertex buffer'); + Exit; + end; + + f_D3DDevice.SetVertexShader(D3DFVF_D2DVERTEX); + f_D3DDevice.SetStreamSource(0, f_VB, SizeOf(Td2dVertex)); + + // Create and setup Index buffer + if Failed(f_D3DDevice.CreateIndexBuffer(VertexBufferSize * 6 div 4 * SizeOf(Word), + D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, f_IB)) then + begin + System_Log('Can''t create D3D index buffer'); + Exit; + end; + + if Failed(f_IB.Lock(0, 0, PByte(l_Index), 0)) then + begin + System_Log('Can''t lock index buffer'); + Exit; + end; + + N := 0; + for I := 0 to VertexBufferSize div 4 - 1 do + begin + l_Index^ := N; Longword(l_Index) := Longword(l_Index) + 2; + l_Index^ := N+1; Longword(l_Index) := Longword(l_Index) + 2; + l_Index^ := N+2; Longword(l_Index) := Longword(l_Index) + 2; + l_Index^ := N+2; Longword(l_Index) := Longword(l_Index) + 2; + l_Index^ := N+3; Longword(l_Index) := Longword(l_Index) + 2; + l_Index^ := N; Longword(l_Index) := Longword(l_Index) + 2; + N := N + 4; + end; + + f_IB.Unlock; + f_D3DDevice.SetIndices(f_IB, 0); + + // Set common render states + + //f_D3DDevice.SetRenderState( D3DRS_LASTPIXEL, 0 {FALSE }); + f_D3DDevice.SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE); + f_D3DDevice.SetRenderState( D3DRS_LIGHTING, 0); + + f_D3DDevice.SetRenderState( D3DRS_ALPHABLENDENABLE, 1); + f_D3DDevice.SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + f_D3DDevice.SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + + f_D3DDevice.SetRenderState( D3DRS_ALPHATESTENABLE, 1); + f_D3DDevice.SetRenderState( D3DRS_ALPHAREF, $01 ); + f_D3DDevice.SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ); + + f_D3DDevice.SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); + f_D3DDevice.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); + f_D3DDevice.SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); + + f_D3DDevice.SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); + f_D3DDevice.SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); + f_D3DDevice.SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); + + f_D3DDevice.SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_POINT); + + //f_D3DDevice.SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP); + //f_D3DDevice.SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP); + + if f_TextureFilter then + begin + f_D3DDevice.SetTextureStageState(0,D3DTSS_MAGFILTER,D3DTEXF_LINEAR); + f_D3DDevice.SetTextureStageState(0,D3DTSS_MINFILTER,D3DTEXF_LINEAR); + end + else + begin + f_D3DDevice.SetTextureStageState(0,D3DTSS_MAGFILTER,D3DTEXF_POINT); + f_D3DDevice.SetTextureStageState(0,D3DTSS_MINFILTER,D3DTEXF_POINT); + end; + + f_PrimCount := 0; + f_CurPrim := D2DPRIM_QUADS; + f_CurBlendMode := BLEND_DEFAULT; + f_CurTexture := nil; + f_VertArray := nil; + + f_D3DDevice.SetTransform(D3DTS_VIEW, f_matView); + f_D3DDevice.SetTransform(D3DTS_PROJECTION, f_matProj); + + Result := True; +end; + +function Td2dCore.Input_GetEvent(var anEvent: Td2dInputEvent): Boolean; +begin + Result := False; + if f_InputEvents.Count > 0 then + begin + anEvent := Pd2dInputEvent(f_InputEvents.Items[0])^; + f_InputEvents.Delete(0); + Result := True; + end; +end; + +function Td2dCore.Input_GetKeyState(aKey: Integer): Boolean; +begin + Result := f_Active and (GetAsyncKeyState(aKey) and $8000 <> 0); +end; + +procedure Td2dCore.Input_SetMousePos(newX, newY: Integer); +var + l_Pnt: TPoint; +begin + if f_Windowed then + begin + l_Pnt.X := newX; + l_Pnt.Y := newY; + end + else + begin + l_Pnt.X := Round((newX - f_FSDX) / f_FSScale); + l_Pnt.Y := Round((newY - f_FSDY) / f_FSScale); + end; + //System_log('MM: %d, %d', [l_Pnt.X, l_Pnt.Y]); + MakeEvent(INPUT_MOUSEMOVE, 0, 0, 0, l_Pnt.X, l_Pnt.Y); +end; + +procedure StopMusicSync(handle: HSYNC; channel, data: DWORD; user: Pointer); stdcall; +begin + gD2DE.BassMusicStop(channel); +end; + +procedure FadeoutMusicSync(handle: HSYNC; channel, data: DWORD; user: Pointer); stdcall; +begin + BASS_ChannelStop(channel); +end; + +function Td2dCore.Texture_CreatePrim(aData: Pointer; aSize: Longword; aMipMap: Boolean = False; + aColorKey: Td2dColor = 0): Id2dTexture; +var + l_Fmt1, l_Fmt2: D3DFORMAT; + l_MipLevel: Integer; + l_Info : D3DXIMAGE_INFO; + l_ResultTex: IDirect3DTexture8; +begin + Result := nil; + if PLongWord(aData)^ = $20534444 then // Compressed DDS format magic number + begin + l_Fmt1 := D3DFMT_UNKNOWN; + l_Fmt2 := D3DFMT_A8R8G8B8; + end + else + begin + l_Fmt1 := D3DFMT_A8R8G8B8; + l_Fmt2 := D3DFMT_UNKNOWN; + end; + + if aMipMap then + l_MipLevel := 1 + else + l_MipLevel := 0; + + if Failed(D3DXCreateTextureFromFileInMemoryEx( + f_D3DDevice, aData^, aSize, D3DX_DEFAULT, D3DX_DEFAULT, l_MipLevel, + 0, l_Fmt1, D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_DEFAULT, aColorKey, @l_Info, + nil, l_ResultTex)) then + D3DXCreateTextureFromFileInMemoryEx( + f_D3DDevice, aData^, aSize, D3DX_DEFAULT, D3DX_DEFAULT, l_MipLevel, + 0, l_Fmt2, D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_DEFAULT, aColorKey, @l_Info, + nil, l_ResultTex); + if l_ResultTex <> nil then + Result := Td2dTexture.Make(l_ResultTex); +end; + +procedure Td2dCore.Music_MIDIPlay(aFileName: string; aLooped: Boolean = True; aPackOnly: Boolean = False); +var + l_FS: TFileStream; + TempPath, TempFile : array [0..MAX_PATH] of Char; + l_Size: Longword; + l_Str: string; + l_MidiVolume: LongWord; +begin + if f_Music <> mp_None then + Music_Stop; + f_MusicData := Resource_Load(aFileName, @l_Size, aPackOnly); + if f_MusicData <> nil then + begin + try + Windows.GetTempPath(MAX_PATH, TempPath); + Windows.GetTempFileName(TempPath, 'bass', 0, TempFile); + f_MusicMidiTempFile := TempFile; + l_FS := TFileStream.Create(f_MusicMidiTempFile, fmCreate); + try + l_FS.WriteBuffer(f_MusicData^, l_Size); + finally + l_FS.Free; + end; + l_Str := Format('open sequencer!%s alias d2dmidi', [f_MusicMidiTempFile]); + //l_Str := 'open sequencer!7.mid alias d2dmidi'; + if mciSendString(PChar(l_Str), nil, 0, 0) = 0 then + begin + f_MusicLooped := aLooped; + l_MidiVolume := (f_MusicVolume shl 24) or (f_MusicVolume shl 8); + midiOutSetVolume(f_MidiDevice, l_MidiVolume); + ReplayMIDI; + end + else + System_Log('Can''t play music (%s).', [aFileName]); + finally + Resource_Free(f_MusicData); + end; + end + else + System_Log('Can''t load music (%s).', [aFileName]); +end; + +procedure Td2dCore.Music_MODPlay(aFileName: string; aLooped: Boolean = True; aFadeinTime: Longword = 0; aPackOnly: + Boolean = False); +var + l_Size: Longword; + l_Flags: Cardinal; +begin + if f_Music <> mp_None then + Music_Stop(aFadeinTime); + if BASS_Handle <> 0 then + begin + f_MusicData := Resource_Load(aFileName, @l_Size, aPackOnly); + if f_MusicData <> nil then + begin + try + l_Flags := BASS_MUSIC_AUTOFREE or BASS_MUSIC_SINCINTER or BASS_MUSIC_RAMPS or BASS_MUSIC_SURROUND2; + if aLooped then + l_Flags := l_Flags or BASS_MUSIC_LOOP; + f_MusicHandle := BASS_MusicLoad(True, f_MusicData, 0, l_Size, l_Flags, 0); + if f_MusicHandle <> 0 then + begin + BASS_ChannelSetSync(f_MusicHandle, BASS_SYNC_FREE or BASS_SYNC_MIXTIME or BASS_SYNC_ONETIME, + 0, StopMusicSync, nil); + if aFadeinTime = 0 then + BASS_ChannelSetAttribute(f_MusicHandle, BASS_ATTRIB_VOL, f_MusicVolume/255) + else + begin + BASS_ChannelSetAttribute(f_MusicHandle, BASS_ATTRIB_VOL, 0); + BASS_ChannelSlideAttribute(f_MusicHandle, BASS_ATTRIB_VOL, f_MusicVolume/255, aFadeinTime); + end; + BASS_ChannelPlay(f_MusicHandle, True); + f_Music := mp_Module; + end + else + System_Log('Can''t play music (%s).', [aFileName]); + finally + Resource_Free(f_MusicData); + end; + end + else + System_Log('Can''t load music (%s).', [aFileName]); + end; +end; + +procedure Td2dCore.Music_Play(aFileName: string; aLooped: Boolean = True; aFadeinTime: Longword = 0; aPackOnly: Boolean + = False); +var + l_Ext: string; +begin + l_Ext := LowerCase(ExtractFileExt(aFileName)); + + if (l_Ext = '.mid') or (l_Ext = '.midi') or (l_Ext = '.rmi') then + begin + Music_MIDIPlay(aFilename, aLooped, aPackOnly); + Exit; + end; + + if (l_Ext = '.wav') or (l_Ext = '.mp3') or (l_Ext = '.mp2') or (l_Ext = '.mp1') or (l_Ext = '.ogg') or + (l_Ext = '.aiff') then + begin + Music_StreamPlay(aFilename, aLooped, aFadeinTime, aPackOnly); + Exit; + end; + + if (l_Ext = '.mo3') or (l_Ext = '.it') or (l_Ext = '.xm') or (l_Ext = '.s3m') or (l_Ext = '.mtm') or + (l_Ext = '.mod') or (l_Ext = '.umx') then + begin + Music_MODPlay(aFilename, aLooped, aFadeinTime, aPackOnly); + Exit; + end; + + System_Log('Can''t recognize music type (%s).', [aFileName]); +end; + +procedure Td2dCore.Music_Stop(aFadeoutTime: Longword = 0); +begin + case f_Music of + mp_MIDI: + begin + mciSendString('close d2dmidi', nil, 0, 0); + DeleteFile(f_MusicMidiTempFile); + f_Music := mp_None; + end; + mp_Stream, mp_Module: + begin + if aFadeoutTime = 0 then + begin + BASS_ChannelStop(f_MusicHandle); // resources are freed through BASS sync event (see BassMusicStop) + ForceAllMusicStop; + end + else + begin + BASS_ChannelSetSync(f_MusicHandle, BASS_SYNC_SLIDE or BASS_SYNC_MIXTIME or BASS_SYNC_ONETIME, + 0, FadeoutMusicSync, nil); + BASS_ChannelSlideAttribute(f_MusicHandle, BASS_ATTRIB_VOL, 0, aFadeoutTime); + if f_Music = mp_Stream then + MusicFadeList.Add(Td2dMusicHolder.Create(f_MusicHandle, f_MusicData)) + else + MusicFadeList.Add(Td2dMusicHolder.Create(f_MusicHandle)); + f_Music := mp_None; + f_MusicHandle := 0; + end; + end; + end; +end; + +procedure Td2dCore.Music_StreamPlay(aFileName : string; + aLooped : Boolean = True; + aFadeinTime : Longword = 0; + aPackOnly : Boolean = False); +var + l_Size: Longword; + l_Flags: Cardinal; +begin + if f_Music <> mp_None then + Music_Stop(aFadeinTime); + if BASS_Handle <> 0 then + begin + f_MusicData := Resource_Load(aFileName, @l_Size, aPackOnly); + if f_MusicData <> nil then + begin + l_Flags := BASS_MUSIC_AUTOFREE; + if aLooped then + l_Flags := l_Flags or BASS_MUSIC_LOOP; + f_MusicHandle := BASS_StreamCreateFile(True, f_MusicData, 0, l_Size, l_Flags); + if f_MusicHandle <> 0 then + begin + BASS_ChannelSetSync(f_MusicHandle, BASS_SYNC_FREE or BASS_SYNC_MIXTIME or BASS_SYNC_ONETIME, + 0, StopMusicSync, nil); + if aFadeinTime = 0 then + BASS_ChannelSetAttribute(f_MusicHandle, BASS_ATTRIB_VOL, f_MusicVolume/255) + else + begin + BASS_ChannelSetAttribute(f_MusicHandle, BASS_ATTRIB_VOL, 0); + BASS_ChannelSlideAttribute(f_MusicHandle, BASS_ATTRIB_VOL, f_MusicVolume/255, aFadeinTime); + end; + BASS_ChannelPlay(f_MusicHandle, True); + f_Music := mp_Stream; + end + else + begin + System_Log('Can''t play music (%s).', [aFileName]); + Resource_Free(f_MusicData); + end; + end + else + System_Log('Can''t load music (%s).', [aFileName]); + end; +end; + +procedure Td2dCore.pm_SetFixedFPS(const Value: Integer); +begin + if Assigned(f_VertArray) then + Exit; + if Assigned(f_D3DDevice) then + begin + if ((f_FixedFPS < 0) and (Value >= 0)) or ((f_FixedFPS >= 0) and (Value < 0)) then + begin + if Value = D2D_FPS_VSYNC then + begin + f_D3DPP_W.SwapEffect := D3DSWAPEFFECT_COPY_VSYNC; + f_D3DPP_FS.FullScreen_PresentationInterval := D3DPRESENT_INTERVAL_ONE; + end + else + begin + f_D3DPP_W.SwapEffect := D3DSWAPEFFECT_COPY; + f_D3DPP_FS.FullScreen_PresentationInterval := D3DPRESENT_INTERVAL_IMMEDIATE; + end; + if Assigned(f_OnFocusLost) then + f_OnFocusLost(); + GfxRestore; + if Assigned(f_OnFocusGain) then + f_OnFocusGain(); + end; + end; + f_FixedFPS := Value; + if f_FixedFPS > 0 then + f_FixedDelta := 1000 div Value + else + f_FixedDelta := 0; +end; + +procedure Td2dCore.pm_SetMusicVolume(const Value: Byte); +begin + Music_SetVolume(Value); +end; + +procedure Td2dCore.pm_SetScreenBPP(const Value: Byte); +begin + if not Assigned(f_D3DDevice) then + f_ScreenBPP := Value; +end; + +procedure Td2dCore.pm_SetScreenHeight(const Value: LongWord); +begin + if not Assigned(f_D3DDevice) then + f_ScreenHeight := Value; +end; + +procedure Td2dCore.pm_SetScreenWidth(const Value: LongWord); +begin + if not Assigned(f_D3DDevice) then + f_ScreenWidth := Value; +end; + +procedure Td2dCore.pm_SetTextureFilter(const Value: Boolean); +begin + f_TextureFilter := Value; + if Assigned(f_D3DDevice) then + begin + RenderBatch; + if f_TextureFilter then + begin + f_D3DDevice.SetTextureStageState(0,D3DTSS_MAGFILTER,D3DTEXF_LINEAR); + f_D3DDevice.SetTextureStageState(0,D3DTSS_MINFILTER,D3DTEXF_LINEAR); + end + else + begin + f_D3DDevice.SetTextureStageState(0,D3DTSS_MAGFILTER,D3DTEXF_POINT); + f_D3DDevice.SetTextureStageState(0,D3DTSS_MINFILTER,D3DTEXF_POINT); + end; + end; +end; + +procedure Td2dCore.pm_SetUseZBuffer(const Value: Boolean); +begin + if not Assigned(f_D3DDevice) then + f_UseZBuffer := Value; +end; + +procedure Td2dCore.pm_SetWindowed(const Value: Boolean); +begin + if Assigned(f_VertArray) then + Exit; + if Assigned(f_D3DDevice) and (Value <> f_Windowed) then + begin + if (f_D3DPP_W.BackBufferFormat = D3DFMT_UNKNOWN) or (f_D3DPP_W.BackBufferFormat = D3DFMT_UNKNOWN) then + Exit; + if Assigned(f_OnFocusLost) then + f_OnFocusLost(); + if f_Windowed then + GetWindowRect(f_WHandle, f_RectW); + + f_Windowed := Value; + + if f_Windowed then + f_D3DPP := f_D3DPP_W + else + f_D3DPP := f_D3DPP_FS; + + if FormatID(f_D3DPP.BackBufferFormat) < 4 then + f_ScreenBPP := 16 + else + f_ScreenBPP := 32; + + SetProjectionMatrix(f_D3DPP.BackBufferWidth, f_D3DPP.BackBufferHeight); + GfxSetViewMatrix; + GfxRestore; + AdjustWindow; + + if Assigned(f_OnFocusGain) then + f_OnFocusGain(); + end + else + f_Windowed := Value; +end; + +procedure Td2dCore.pm_SetWindowTitle(const Value: string); +begin + f_WindowTitle := Value; + if f_WHandle <> 0 then + SetWindowText(f_WHandle, PChar(f_WindowTitle)); +end; + +procedure Td2dCore.RenderBatch(aEndScene: Boolean = False); +begin + if (f_PrimCount = 0) and not aEndScene then + Exit; + f_VB.Unlock; + if f_PrimCount > 0 then + begin + case f_CurPrim of + D2DPRIM_QUADS: + f_D3DDevice.DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, f_PrimCount shl 2, 0, f_PrimCount shl 1); + + D2DPRIM_TRIPLES: + f_D3DDevice.DrawPrimitive(D3DPT_TRIANGLELIST, 0, f_PrimCount); + + D2DPRIM_LINES: + f_D3DDevice.DrawPrimitive(D3DPT_LINELIST, 0, f_PrimCount); + end; + end; + f_PrimCount := 0; + if not aEndScene then + f_VB.Lock(0,0, PByte(f_VertArray), 0); +end; + +function Td2dCore.Resource_AttachPack(const aName: string; const aPack: Id2dResourcePack): Boolean; +var + I: Integer; +begin + Result := False; + if f_AttachedPacks.Find(aName, I) then + begin + Result := True; // pack is already attached + Exit; + end; + I := f_AttachedPacks.Add(aName); + f_AttachedPacks.Interfaces[I] := aPack; + Result := True; +end; + +function Td2dCore.Resource_CreateStream(aFilename: string; aPackOnly: Boolean = False): TStream; +var + l_Data: Pointer; + l_Size: Longword; +begin + Result := nil; + if Resource_Exists(aFilename, aPackOnly) then + begin + l_Data := Resource_Load(aFilename, @l_Size, True, True); + if l_Data <> nil then + begin + try + Result := TMemoryStream.Create; + with TMemoryStream(Result) do + begin + SetSize(l_Size); + Move(l_Data^, Memory^, l_Size); + end; + finally + Resource_Free(l_Data); + end; + end + else + Result := TFileStream.Create(aFilename, fmOpenRead); + end; +end; + +function Td2dCore.Resource_Exists(aFileName: string; aPackOnly: Boolean = False): Boolean; +var + I: Integer; + l_Pack: Id2dResourcePack; +begin + Result := False; + for I := 0 to f_AttachedPacks.Count - 1 do + begin + l_Pack := f_AttachedPacks.Interfaces[I] as Id2dResourcePack; + if l_Pack.IndexOf(aFileName) >= 0 then + begin + Result := True; + Exit; + end; + end; + if not aPackOnly then + Result := FileExists(aFileName); +end; + +procedure Td2dCore.Resource_Free(aResource: Pointer); +begin + if Assigned(aResource) then + FreeMem(aResource); +end; + +function Td2dCore.Resource_Load(const aFileName: string; aSize: PLongword; aPackOnly: Boolean = False; aSilent: Boolean = False): Pointer; +var + I: Integer; + l_Pack: Id2dResourcePack; + l_Idx: Integer; + l_FS: TFileStream; +begin + Result := nil; + // trying to find file in attached packs + for I := 0 to f_AttachedPacks.Count - 1 do + begin + l_Pack := f_AttachedPacks.Interfaces[I] as Id2dResourcePack; + l_Idx := l_Pack.IndexOf(aFileName); + if l_Idx <> -1 then + begin + l_Pack.Extract(l_Idx, Result); + if aSize <> nil then + aSize^ := l_Pack.Size[l_Idx]; + Exit; + end; + end; + if aPackOnly then + begin + if not aSilent then + System_Log('Can''t find resource: %s', [aFileName]); + Exit; + end; + // didn't found in packs - let's try to load from disk... + if FileExists(aFileName) then + begin + l_FS := TFileStream.Create(aFileName, fmOpenRead or fmShareDenyWrite); + try + GetMem(Result, l_FS.Size); + l_FS.Read(Result^, l_FS.Size); + if aSize <> nil then + aSize^ := l_FS.Size; + finally + l_FS.Free; + end; + end + else + if not aSilent then + System_Log('Can''t find resource: %s', [aFileName]); +end; + +procedure Td2dCore.Resource_RemoveAllPacks; +begin + f_AttachedPacks.Clear; +end; + +procedure Td2dCore.Resource_RemovePack(const aFileName: string); +var + I : Integer; +begin + if f_AttachedPacks.Find(aFileName, I) then + f_AttachedPacks.Delete(I); +end; + +procedure Td2dCore.SetBlendMode(aBlend: Longint); +begin + if (aBlend and BLEND_ALPHABLEND) <> (f_CurBlendMode and BLEND_ALPHABLEND) then + if (aBlend and BLEND_ALPHABLEND) <> 0 then + f_D3DDevice.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA) + else + f_D3DDevice.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + + if (aBlend and BLEND_ZWRITE) <> (f_CurBlendMode and BLEND_ZWRITE) then + if (aBlend and BLEND_ZWRITE) <> 0 then + f_D3DDevice.SetRenderState(D3DRS_ZWRITEENABLE, 1) + else + f_D3DDevice.SetRenderState(D3DRS_ZWRITEENABLE, 0); + + + if (aBlend and BLEND_COLORADD) <> (f_CurBlendMode and BLEND_COLORADD) then + if (aBlend and BLEND_COLORADD) <> 0 then + f_D3DDevice.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADD) + else + f_D3DDevice.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + + f_CurBlendMode := aBlend; +end; + +procedure Td2dCore.SetProjectionMatrix(const aWidth, aHeight: Integer); +var + l_Temp: TD3DMatrix; +begin + D3DXMatrixScaling(f_matProj, 1.0, -1.0, 1.0); + D3DXMatrixTranslation(l_Temp, -0.5, aHeight+0.5, 0.0); + D3DXMatrixMultiply(f_matProj, f_matProj, l_Temp); + D3DXMatrixOrthoOffCenterLH(l_Temp, 0, aWidth, 0, aHeight, 0, 1.0); + D3DXMatrixMultiply(f_matProj, f_matProj, l_Temp); +end; + +procedure Td2dCore.Snd_FreeAll; +begin + if BASS_Handle <> 0 then + begin + while f_SndSamples.Count > 0 do + Snd_FreeSamplePrim(0); + end + else + PlaySound(nil, 0, 0); +end; + +procedure Td2dCore.Snd_FreeSample(aFilename: string); +var + l_Idx: Integer; +begin + if BASS_Handle <> 0 then + begin + l_Idx := f_SndSamples.IndexOf(LowerCase(aFilename)); + if l_Idx >= 0 then + Snd_FreeSamplePrim(l_Idx); + end; +end; + +procedure Td2dCore.Snd_FreeSamplePrim(aIdx: Integer); +var + l_Hnd: HSAMPLE; +begin + if BASS_Handle <> 0 then + begin + l_Hnd := HSAMPLE(f_SndSamples.Objects[aIdx]); + BASS_SampleStop(l_Hnd); + BASS_SampleFree(l_Hnd); + f_SndSamples.Delete(aIdx); + end; +end; + +procedure Td2dCore.Snd_PlaySample(aFilename: string; aVolume: Byte = 255; + aLooped: Boolean = False; aPackOnly: Boolean = False); +var + l_Idx: Integer; + l_Ch: HCHANNEL; + l_Data: Pointer; + l_Size: LongWord; +begin + if BASS_Handle <> 0 then + begin + l_Idx := Snd_PreLoadSample(aFilename); + if l_Idx >= 0 then + begin + l_Ch := BASS_SampleGetChannel(HSAMPLE(f_SndSamples.Objects[l_Idx]), False); + BASS_ChannelSetAttribute(l_Ch, BASS_ATTRIB_VOL, aVolume/255); + if aLooped then + BASS_ChannelFlags(l_Ch, BASS_SAMPLE_LOOP, BASS_SAMPLE_LOOP); + BASS_ChannelPlay(l_Ch, True); + end; + end + else + begin + l_Data := Resource_Load(aFilename, @l_Size); + if l_Data <> nil then + begin + try + PlaySound(PAnsiChar(l_Data), 0, SND_MEMORY or SND_SYNC or SND_NODEFAULT); + finally + Resource_Free(l_Data); + end; + end + else + System_Log('Can''t load sample (%s).', [aFilename]); + end; +end; + +function Td2dCore.Snd_PreLoadSample(aFileName: string; aPackOnly: Boolean = False): Integer; +var + l_Data : Pointer; + l_Size : Longword; + l_Handle: HSAMPLE; +begin + Result := -1; + if BASS_Handle <> 0 then + begin + aFilename := LowerCase(aFilename); + Result := f_SndSamples.IndexOf(aFilename); + if Result < 0 then + begin + l_Data := Resource_Load(aFilename, @l_Size, aPackOnly); + if l_Data = nil then + System_Log('Can''t load sample (%s).', [aFilename]) + else + begin + l_Handle := BASS_SampleLoad(True, l_Data, 0, l_Size, 32, 0); + if l_Handle = 0 then + System_Log('Error loading sample (%s).', [aFilename]) + else + Result := f_SndSamples.AddObject(aFilename, TObject(l_Handle)); + Resource_Free(l_Data); + end; + end; + end; +end; + +const + MOD_SWSYNTH = 7; + +procedure MyCopyFile(aFrom, aTo: string); +var + l_FSFrom, l_FSTo: TFileStream; +begin + l_FSFrom := TFileStream.Create(aFrom, fmOpenRead); + try + l_FSTo := TFileStream.Create(aTo, fmCreate); + try + l_FSTo.CopyFrom(l_FSFrom, l_FSFrom.Size); + finally + l_FSTo.Free; + end; + finally + l_FSFrom.Free; + end; +end; + +procedure Td2dCore.DoTextureCacheGarbageCollection; +var + I: Integer; + l_T: Id2dTexture; +begin + if timeGetTime > f_LastTexGC + c_TexGCThreshold then + begin + for I := f_Textures.Count - 1 downto 0 do + begin + l_T := f_Textures.Interfaces[I] as Id2dTexture; + if l_T.IsOrphan then + f_Textures.Delete(I); + end; + f_LastTexGC := timeGetTime; + end; +end; + +procedure Td2dCore.ForceAllMusicStop; +var + I: Integer; +begin + if f_MusicFadeList <> nil then + begin + for I := f_MusicFadeList.Count - 1 downto 0 do + BASS_ChannelStop(Td2dMusicHolder(f_MusicFadeList[I]).Chanel); + while f_MusicFadeList.Count > 0 do + Sleep(5); + end; +end; + +procedure Td2dCore.GfxSetViewMatrix; +begin + if f_Windowed then + D3DXMatrixIdentity(f_matView) + else + f_matView := f_matViewFS; +end; + +{$IFDEF D2DGIF} +function Td2dCore.GIFasBMPLoad(aFileName: string; aSize: PLongword; aPackOnly: Boolean = False): Pointer; +var + l_Stream: TStream; + l_GIF : TGIFImage; + l_Bmp : TBitmap; + l_Rect : TRect; + l_MemStream: Td2dMemoryStream; +begin + Result := nil; + aFileName := StringReplace(aFileName, '/', '\', [rfReplaceAll]); + l_Stream := Resource_CreateStream(aFileName, aPackOnly); + if l_Stream <> nil then + begin + l_GIF := TGIFImage.Create; + try + try + l_GIF.LoadFromStream(l_Stream); + finally + FreeAndNil(l_Stream); + end; + l_Bmp := TBitmap.Create; + try + l_Bmp.Assign(l_GIF); + l_Bmp.PixelFormat := pf32bit; + with l_Bmp.Canvas.Brush do + begin + Color := $FF00FF; + Style := bsSolid; + end; + l_Rect := Rect(0,0,l_GIF.Width, l_GIF.Height); + l_Bmp.Canvas.FillRect(l_Rect); + l_GIF.Images[0].Draw(l_Bmp.Canvas, l_Rect, True, False); + l_MemStream := Td2dMemoryStream.Create; + try + l_Bmp.SaveToStream(l_MemStream); + Result := l_MemStream.ExtractMemory(aSize); + finally + FreeAndNil(l_MemStream); + end; + finally + FreeAndNil(l_Bmp); + end; + finally + FreeAndNil(l_GIF); + end; + end; +end; +{$ENDIF} + +procedure Td2dCore.Input_TouchMousePos; +begin + //Input_SetMousePos(f_MouseX, f_MouseY);// - òàê íåëüçÿ, ïîòîìó ÷òî ïåðåêèäûâàåò êóðñîð â îêíî + MakeEvent(INPUT_MOUSEMOVE, 0, 0, 0, Round(f_MouseX), Round(f_MouseY)); + //System_Log('M: %d, %d', [Round(f_MouseX), Round(f_MouseY)]); +end; + +procedure Td2dCore.Music_Pause; +begin + if f_Music = mp_None then + Exit; + if f_Music = mp_MIDI then + begin + mciSendString('pause d2dmidi', nil, 0, 0); + end + else + BASS_ChannelPause(f_MusicHandle); +end; + +procedure Td2dCore.Music_Resume; +begin + if f_Music = mp_None then + Exit; + if f_Music = mp_MIDI then + begin + mciSendString('play d2dmidi notify', nil, 0, f_WHandle); + end + else + BASS_ChannelPlay(f_MusicHandle, False); +end; + +procedure Td2dCore.Music_SetVolume(const aVolume: Byte; const aFadeTime: Longword = 0); +var + l_MidiVolume: Longword; +begin + case f_Music of + mp_MIDI: + begin + l_MidiVolume := (aVolume shl 24) or (aVolume shl 8); + midiOutSetVolume(f_MidiDevice, l_MidiVolume); + end; + mp_Stream, mp_Module: + if aFadeTime = 0 then + BASS_ChannelSetAttribute(f_MusicHandle, BASS_ATTRIB_VOL, aVolume/255) + else + BASS_ChannelSlideAttribute(f_MusicHandle, BASS_ATTRIB_VOL, aVolume/255, aFadeTime); + end; + f_MusicVolume := aVolume; +end; + +function Td2dCore.pm_GetMusicFadeList: TObjectList; +begin + if f_MusicFadeList = nil then + f_MusicFadeList := TObjectList.Create(True); + Result := f_MusicFadeList; +end; + +procedure Td2dCore.pm_SetHideMouse(const Value: Boolean); +begin + if f_HideMouse <> Value then + begin + f_HideMouse := Value; + PostMessage(WHandle, WM_SETCURSOR, 0, HTCLIENT); + end; +end; + +procedure Td2dCore.pm_SetSoundMute(const Value: Boolean); +var + l_GVolume: Integer; + l_MidiVolume: Longword; +begin + if Value <> f_SoundMute then + begin + f_SoundMute := Value; + if f_SoundMute then + l_GVolume := 0 + else + l_GVolume := 10000; + BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, l_GVolume); + BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, l_GVolume); + BASS_SetConfig(BASS_CONFIG_GVOL_MUSIC, l_GVolume); + + if Music = mp_MIDI then + begin + if f_SoundMute then + midiOutSetVolume(f_MidiDevice, 0) + else + begin + l_MidiVolume := (f_MusicVolume shl 24) or (f_MusicVolume shl 8); + midiOutSetVolume(f_MidiDevice, l_MidiVolume); + end; + end; + end; +end; + +function Td2dCore.Resource_FindInPacks(aWildCard: string): string; +var + I : Integer; + l_Idx: Integer; + l_Pack: Id2dResourcePack; +begin + Result := ''; + for I := 0 to f_AttachedPacks.Count-1 do + begin + l_Pack := f_AttachedPacks.Interfaces[I] as Id2dResourcePack; + l_Idx := l_Pack.Find(aWildCard); + if l_Idx >= 0 then + begin + Result := l_Pack.Name[l_Idx]; + Exit; + end; + end; +end; + +procedure Td2dCore.Snd_StopAll; +var + I: Integer; +begin + if BASS_Handle <> 0 then + begin + for I := 0 to f_SndSamples.Count - 1 do + BASS_SampleStop(HSAMPLE(f_SndSamples.Objects[I])); + end + else + PlaySound(nil, 0, 0); +end; + +procedure Td2dCore.Snd_StopSample(aFilename: string); +var + l_Idx: Integer; +begin + if BASS_Handle <> 0 then + begin + l_Idx := f_SndSamples.IndexOf(LowerCase(aFilename)); + if l_Idx >= 0 then + BASS_SampleStop(HSAMPLE(f_SndSamples.Objects[l_Idx])); + end; +end; + +procedure Td2dCore.SoundSystemStart; +var + l_DevInfo: BASS_DEVICEINFO; + I: Integer; + l_MidiOutCaps : TMidiOutCaps; + l_BassDLL: string; +begin + // find MIDI device so we can manipulate volume + f_MidiDevice := -1; + for I := 0 to midiOutGetNumDevs - 1 do + begin + MidiOutGetDevCaps(I, @l_MidiOutCaps, SizeOf(l_MidiOutCaps)); + if (l_MidiOutCaps.wTechnology = MOD_SWSYNTH) then + begin + f_MidiDevice := I; + Break; + end; + end; + f_MusicVolume := 128; + // Init BASS sound system + if BASS_Handle <> 0 then // already started + Exit; + l_BassDLL := ExtractFilePath(ParamStr(0)) + 'bass.dll'; + if FileExists(l_BassDLL) then + begin + if Load_BASSDLL(l_BassDLL) then + begin + if HiWord(BASS_GetVersion) = BASSVERSION then + begin + if BASS_Init(-1, f_SoundSampleRate, 0, f_WHandle, nil) then + begin + BASS_GetDeviceInfo(1, l_DevInfo); + System_Log(#13#10'Sound Device: %s', [l_DevInfo.name]); + System_Log('Sample rate : %d'#13#10, [f_SoundSampleRate]); + end + else + begin + System_Log(#13#10'BASS init failed, so expect simplified (WAV and MIDI) sound'#13#10); + Unload_BASSDLL; + end; + end + else + begin + System_Log(#13#10'Incorrect BASS.DLL version'#13#10); + Unload_BASSDLL; + end; + end + else + System_Log(#13#10'Error loading BASS.DLL, so expect simplified (WAV and MIDI) sound'#13#10); + end + else + System_Log(#13#10'BASS.DLL not found, so expect simplified (WAV and MIDI) sound'#13#10); +end; + +procedure Td2dCore.SoundSystemStop; +begin + Music_Stop; + if BASS_Handle <> 0 then + begin + Snd_FreeAll; + BASS_Stop; + BASS_Free; + Unload_BASSDLL; + end; +end; + +function Td2dCore.System_Start: Boolean; +var + l_Str: string; + l_WndClass: TWndClass; + l_Width, l_Height: Integer; + l_Icon: HICON; +begin + Result := False; + if FileExists(f_LogFileName) then + DeleteFile(f_LogFileName); + DateTimeToString(l_Str, 'dddddd, tt', Now); + System_Log('%s', [l_Str]); + System_Log('Delphi 2D engine starting...'); + System_Log('Engine version: %x.%x'#13#10, [D2DVersion shr 8, D2DVersion and $FF]); + f_Instance := SysInit.HInstance;//GetModuleHandle(nil); + // Register application window class + with l_WndClass do + begin + style := CS_DBLCLKS or CS_OWNDC or CS_HREDRAW or CS_VREDRAW; + lpfnWndProc := @WindowProc; + cbClsExtra := 0; + cbWndExtra := 0; + hInstance := f_Instance; + hCursor := LoadCursor(0, IDC_ARROW); + hbrBackground := GetStockObject(BLACK_BRUSH); + lpszMenuName := nil; + lpszClassName := cWinClassName; + hIcon := LoadIcon(0, IDI_APPLICATION); + end; + if Windows.RegisterClass(l_WndClass) = 0 then + begin + System_Log('Can''t register window class'); + Exit; + end; + + l_Width := f_ScreenWidth + GetSystemMetrics(SM_CXFIXEDFRAME)*2; + l_Height := f_ScreenHeight + GetSystemMetrics(SM_CYFIXEDFRAME)*2 + GetSystemMetrics(SM_CYCAPTION); + + f_RectW.Left :=(GetSystemMetrics(SM_CXSCREEN)-l_Width) div 2; + f_RectW.Top := (GetSystemMetrics(SM_CYSCREEN)-l_Height) div 2; + f_RectW.Right := f_RectW.Left + l_Width; + f_RectW.Bottom := f_RectW.Top + l_Height; + f_WStyleW := WS_POPUP or WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_VISIBLE{ or WS_THICKFRAME}; + + f_RectFS.Left := 0; + f_RectFS.Top := 0; + f_RectFS.Right := GetSystemMetrics(SM_CXSCREEN); + f_RectFS.Bottom := GetSystemMetrics(SM_CYSCREEN); + f_WStyleFS := WS_POPUP or WS_VISIBLE; + + if f_Windowed then + f_WHandle := CreateWindowEx(0, cWinClassName, PChar(f_WindowTitle), Cardinal(f_WStyleW), + f_RectW.left, f_RectW.top, f_RectW.right - f_RectW.left, f_RectW.bottom - f_RectW.top, + 0, 0, f_Instance, nil) + else + f_WHandle := CreateWindowEx(WS_EX_TOPMOST, cWinClassName, PChar(f_WindowTitle), Cardinal(f_WStyleFS), 0, 0, 0, 0, + 0, 0, f_Instance, nil); + + if f_WHandle = 0 then + begin + System_Log('Can''t create window...'); + Exit; + end; + + ShowWindow(f_WHandle, SW_SHOW); + l_Icon := LoadIcon(MainInstance, 'MAINICON'); + SetClassLong(f_WHandle, GCL_HICON, l_Icon); + SendMessage(f_WHandle, WM_SETICON, 1, l_Icon); + timeBeginPeriod(1); + Randomize; + + if not GfxInit then + begin + System_Shutdown; + Exit; + end; + + if f_SoundOn then + SoundSystemStart; + + System_Log('Init done.'#13#10); + + f_FPS := 0; + f_FPSCount := 0; + f_Time := 0.0; + f_FixedDelta := 0; + + f_Time0 := timeGetTime; + f_Time0FPS := f_Time0; + + Result := True; +end; + +procedure Td2dCore.System_Log(const aFormat: string; Args: array of TVarRec); +begin + System_Log(Format(aFormat, Args)); +end; + +procedure Td2dCore.System_Log(const aString: string); +var + l_FS: TFileStream; +const + cCRLF: PChar = #13#10; +begin + try + if FileExists(f_LogFileName) then + l_FS := TFileStream.Create(f_LogFileName, fmOpenReadWrite or fmShareDenyNone) + else + l_FS := TFileStream.Create(f_LogFileName, fmCreate or fmShareDenyNone); + try + l_FS.Seek(0, soFromEnd); + if aString <> '' then + l_FS.Write((@aString[1])^, Length(aString)); + l_FS.Write(cCRLF^, 2); + finally + FreeAndNil(l_FS); + end; + except + // if log can't be written it should not brake the program + end; +end; + +procedure Td2dCore.System_Run; +var + l_Msg: TMsg; + l_Point: TPoint; + l_Rect : TRect; + l_Finish: Boolean; +begin + if f_WHandle = 0 then + begin + System_Log('Engine was not started!'); + Exit; + end; + + if not Assigned(f_OnFrame) then + begin + System_Log('Frame function is not assigned!'); + Exit; + end; + + // MAIN LOOP + l_Finish := False; + while not l_Finish do + begin + // dispatch messages + if PeekMessage(l_Msg, 0, 0, 0, PM_REMOVE) then + begin + if l_Msg.message = WM_QUIT then + l_Finish := True; + DispatchMessage(l_Msg); + Continue; + end; + + GetCursorPos(l_Point); + GetClientRect(f_WHandle, l_Rect); + MapWindowPoints(f_WHandle, 0, l_Rect, 2); + f_MouseOver := f_MouseCaptured or (PtInRect(l_Rect, l_Point) and (WindowFromPoint(l_Point) = f_WHandle)); + if f_Active or f_DontSuspend then + begin + repeat + f_DeltaTicks := timeGetTime - f_Time0; + if f_DeltaTicks <= f_FixedDelta then + Sleep(1); + until f_DeltaTicks > f_FixedDelta; + //if f_DeltaTicks >= f_FixedDelta then + begin + f_DeltaTime := f_DeltaTicks / 1000.0; + + // if delay was too big, count it as if where was no delay + // (return from suspended state for instance) + if f_DeltaTime > 0.2 then + if f_FixedDelta > 0 then + f_DeltaTime := f_FixedDelta / 1000.0 + else + f_DeltaTime := 0.01; + + f_Time := f_Time + f_DeltaTime; + + f_Time0 := timeGetTime; + + if(f_Time0 - f_Time0FPS < 1000) then + Inc(f_FPSCount) + else + begin + f_FPS := f_FPSCount; + f_FPSCount := 0; + f_Time0FPS := f_Time0; + end; + + f_OnFrame(f_DeltaTime, l_Finish); + if Assigned(f_OnRender) then + f_OnRender(); + ClearQueue; + { + if (not f_Windowed) and (f_FixedFPS = D2D_FPS_VSYNC) then + Sleep(1); + } + end; + { + else + if (f_FixedDelta > 0) and (f_DeltaTicks+3 < f_FixedDelta) then + Sleep(1); + } + end + else + Sleep(1); + CheckSynchronize; + end; +end; + +procedure Td2dCore.System_Shutdown; +begin + System_Log('Finishing...'); + timeEndPeriod(1); + ClearQueue; + GfxDone; + if f_SoundOn then + SoundSystemStop; + + if f_WHandle <> 0 then + begin + DestroyWindow(f_WHandle); + f_WHandle := 0; + end; + if f_Instance <> 0 then + Windows.UnregisterClass(cWinClassName, f_Instance); + + System_Log('The End.'); +end; + +procedure Td2dCore.System_SoundPause; +begin + if BASS_Handle <> 0 then + BASS_Pause; + if f_Music = mp_MIDI then + mciSendString('pause d2dmidi', nil, 0, 0); +end; + +procedure Td2dCore.System_SoundResume; +begin + if BASS_Handle <> 0 then + BASS_Start; + if f_Music = mp_MIDI then + mciSendString('play d2dmidi notify', nil, 0, f_WHandle); +end; + +function Td2dCore.Target_Create(aWidth, aHeight: Integer; aZBuffer: Boolean = False): Pd2dRenderTarget; +var + l_Target: Pd2dRenderTarget; + l_Desc : D3DSURFACE_DESC; +begin + Result := nil; + New(l_Target); + l_Target.Tex := nil; + l_Target.Depth := nil; + + if Failed(D3DXCreateTexture(f_D3DDevice, aWidth, aHeight, 1, D3DUSAGE_RENDERTARGET, f_D3DPP.BackBufferFormat, + D3DPOOL_DEFAULT, l_Target.Tex)) then + begin + System_Log('Can''t create render target texture'); + Dispose(l_Target); + Exit; + end; + + l_Target.Tex.GetLevelDesc(0, l_Desc); + l_Target.Width := l_Desc.Width; + l_Target.Height := l_Desc.Height; + + if aZBuffer then + begin + if Failed(f_D3DDevice.CreateDepthStencilSurface(aWidth, aHeight, D3DFMT_D16, D3DMULTISAMPLE_NONE, + l_Target.Depth)) then + begin + System_Log('Can''t create render target depth buffer'); + Dispose(l_Target); + Exit; + end; + end; + + f_Targets.Add(l_Target); + Result := l_Target; +end; + +procedure Td2dCore.Target_Free(aTarget: Pd2dRenderTarget); +var + l_Idx: Integer; +begin + l_Idx := f_Targets.IndexOf(aTarget); + if l_Idx >= 0 then + begin + Dispose(Pd2dRenderTarget(f_Targets.Items[l_Idx])); + f_Targets.Delete(l_Idx); + end; +end; + +procedure Td2dCore.Texture_ClearCache; +begin + f_Textures.Clear; +end; + +function Td2dCore.Texture_Create(const aWidth, aHeight: Integer): Id2dTexture; +var + l_Res: IDirect3DTexture8; +begin + Result := nil; + if Failed(D3DXCreateTexture(f_D3DDevice, aWidth, aHeight, 1, 0, + D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, l_Res)) then + System_Log('Can''t create texture') + else + Result := Td2dTexture.Make(l_Res); +end; + +function Td2dCore.Texture_GetWidth(aTex: IDirect3DTexture8): Integer; +var + l_Desc: D3DSURFACE_DESC; +begin + if Failed(aTex.GetLevelDesc(0, l_Desc)) then + Result := 0 + else + Result := l_Desc.Width; +end; + +function Td2dCore.Texture_GetHeight(aTex: IDirect3DTexture8): Integer; +var + l_Desc: D3DSURFACE_DESC; +begin + if Failed(aTex.GetLevelDesc(0, l_Desc)) then + Result := 0 + else + Result := l_Desc.Height; +end; + +function Td2dCore.Texture_Load(const aFileName: string; aPackOnly: Boolean = False; aUseCache: Boolean = True; const + aMipMap: Boolean = False; aPicSize: PPoint = nil): Id2dTexture; +var + l_Data: Pointer; + l_Size: Longword; + l_Idx: Integer; + l_ImgInfo: TD3DXImageInfo; + l_PicSize: TPoint; + l_TH: Id2dTexture; + {$IFDEF D2DGIF} + l_IsGIF : Boolean; + {$ENDIF} +begin + Result := nil; + if aUseCache then // check out in the cache + begin + l_Idx := f_Textures.IndexOf(LowerCase(aFileName)); + if l_Idx <> -1 then + begin + l_TH := f_Textures.Interfaces[l_Idx] as Id2dTexture; + Result := l_TH; + if aPicSize <> nil then + aPicSize^ := Point(l_TH.SrcPicWidth, l_TH.SrcPicHeight); + Exit; // found texture in the cache + end; + end; + {$IFDEF D2DGIF} + l_IsGIF := LowerCase(ExtractFileExt(aFileName)) = '.gif'; + if l_IsGIF then + l_Data := GIFasBMPLoad(aFilename, @l_Size, aPackOnly) + else + {$ENDIF} + l_Data := Resource_Load(aFileName, @l_Size, aPackOnly); + if l_Data = nil then + begin + System_Log('Can''t load texture (%s)', [aFilename]); + Exit; + end; + try + D3DXGetImageInfoFromFileInMemory(l_Data^, l_Size, l_ImgInfo); + l_PicSize.X := l_ImgInfo.Width; + l_PicSize.Y := l_ImgInfo.Height; + if aPicSize <> nil then + aPicSize^ := l_PicSize; + {$IFDEF D2DGIF} + if l_IsGIF then + Result := Texture_CreatePrim(l_Data, l_Size, aMipMap, $FFFF00FF) + else + {$ENDIF} + Result := Texture_CreatePrim(l_Data, l_Size, aMipMap); + if Result = nil then + System_Log('Can''t create texture (%s)', [aFileName]) + else + begin + Result.SrcPicWidth := l_PicSize.X; + Result.SrcPicHeight := l_PicSize.Y; + if aUseCache then + begin + l_Idx := f_Textures.Add(LowerCase(aFileName)); + f_Textures.Interfaces[l_Idx] := Result; + DoTextureCacheGarbageCollection; + end; + end; + finally + FreeMem(l_Data); + end; +end; + +function Td2dCore.Texture_FromMemory(const aData: Pointer; const aSize: LongWord; const aMipMap: Boolean = false): + Id2dTexture; +begin + Result := Texture_CreatePrim(aData, aSize, aMipMap); + if Result = nil then + System_Log('Can''t create texture from memory'); +end; + +function Td2dCore.Texture_GetHeight(aTex: Id2dTexture): Integer; +begin + if aTex <> nil then + Result := Texture_GetHeight(aTex.DirectXTexture) + else + Result := 0; +end; + +function Td2dCore.Texture_GetWidth(aTex: Id2dTexture): Integer; +begin + if aTex <> nil then + Result := Texture_GetWidth(aTex.DirectXTexture) + else + Result := 0; +end; + +procedure Td2dCore.Texture_RemoveFromCache(aFileName: string); +var + l_Idx: Integer; +begin + l_Idx := f_Textures.IndexOf(LowerCase(aFileName)); + if l_Idx <> -1 then + f_Textures.Delete(l_Idx); +end; + +procedure Td2dCore.Texture_RemoveFromCache(const aTexture: Id2dTexture); +var + I: Integer; + l_Tex: Id2dTexture; +begin + for I := 0 to f_Textures.Count-1 do + begin + l_Tex := f_Textures.Interfaces[I] as Id2dTexture; + if l_Tex = aTexture then + begin + f_Textures.Delete(I); + Exit; + end; + end; +end; + +constructor Td2dMusicHolder.Create(aChanel: Longword; aData: Pointer = nil); +begin + inherited Create; + Chanel := aChanel; + Data := aData; +end; + +destructor Td2dMusicHolder.Destroy; +begin + if Data <> nil then + FreeMem(Data); + inherited; +end; + +end. diff --git a/d2dDocRoot.pas b/d2dDocRoot.pas new file mode 100644 index 0000000..b74306a --- /dev/null +++ b/d2dDocRoot.pas @@ -0,0 +1,250 @@ +unit d2dDocRoot; + +interface + +uses + Contnrs, + + d2dTypes, + d2dFormattedText; + +type + Td2dLinkSliceCacheItem = class + private + f_Rect: Td2dRect; + f_Slice: Td2dLinkSlice; + public + constructor Create(aRect: Td2dRect; const aSlice: Td2dLinkSlice); + property Rect: Td2dRect read f_Rect write f_Rect; + property Slice: Td2dLinkSlice read f_Slice write f_Slice; + end; + + +type + Td2dDocRoot = class(Td2dUnionSlice) + private + f_CurLinkAction: Td2dLinkActionProc; + f_HighlightedLink: Td2dLinkSliceCacheItem; + f_LinkCacheIsValid: Boolean; + f_LinksAllowed: Boolean; + f_LinksCache: TObjectList; + procedure BuildLinkSliceCache; + procedure pm_SetLinksAllowed(const Value: Boolean); + procedure _AllowLinks(const aLinkSlice: Td2dLinkSlice); + procedure _DisallowLinks(const aLinkSlice: Td2dLinkSlice); + procedure _DropHighlight(const aLinkSlice: Td2dLinkSlice); + procedure _LinkIteratorProc(const aSlice: Td2dCustomSlice); + protected + function GetTopChild: Integer; virtual; + function GetBottomChild: Integer; virtual; + function GetVerticalShift: Single; virtual; + public + constructor Create(aWidth: Single); + destructor Destroy; override; + procedure Clear; override; + procedure DropLinkCache; + procedure DropLinkHighlight; + procedure FindLinkHighlight(const aX, aY: Single; var theEvent: Td2dInputEvent); + procedure ForceLinkAllowance; + procedure IterateLinks(anAction: Td2dLinkActionProc; aFrom: Integer = 0); + property HighlightedLink: Td2dLinkSliceCacheItem read f_HighlightedLink write f_HighlightedLink; + property LinksAllowed: Boolean read f_LinksAllowed write pm_SetLinksAllowed; + end; + +implementation +uses + d2dCore, + d2dUtils; + +constructor Td2dDocRoot.Create(aWidth: Single); +begin + inherited Create(aWidth); + f_LinksAllowed := True; + f_LinksCache := TObjectList.Create(True); + f_LinkCacheIsValid := False; +end; + +destructor Td2dDocRoot.Destroy; +begin + inherited; + f_LinksCache.Free; +end; + +procedure Td2dDocRoot.BuildLinkSliceCache; +var + I : Integer; + l_BottomVisible: Integer; + l_Child: Td2dCustomSlice; + l_Shift: Single; + l_TopVisible: Integer; + + procedure ScanForLinkSlices(const aSlice: Td2dCustomSlice); + var + J: Integer; + l_CI: Td2dLinkSliceCacheItem; + l_R: Td2dRect; + l_UC: Td2dUnionSlice; + begin + if aSlice.SliceType = stUnion then + begin + l_UC := Td2dUnionSlice(aSlice); + for J := 0 to l_UC.ChildrenCount-1 do + ScanForLinkSlices(l_UC.Children[J]); + end + else + if aSlice.SliceType = stLink then + begin + l_R.Left := aSlice.AbsLeft; + l_R.Top := aSlice.AbsTop - l_Shift; + l_R.Right := l_R.Left + aSlice.Width; + l_R.Bottom := l_R.Top + aSlice.Height; + l_CI := Td2dLinkSliceCacheItem.Create(l_R, aSlice as Td2dLinkSlice{(aSlice)}); + f_LinksCache.Add(l_CI); + end; + end; + +begin + f_LinksCache.Clear; + l_TopVisible := GetTopChild; + if l_TopVisible >= 0 then + begin + l_BottomVisible := GetBottomChild; + l_Shift := Int(GetVerticalShift + 0.5); // round shift + for I := l_TopVisible to l_BottomVisible do + begin + l_Child := Children[I]; + ScanForLinkSlices(l_Child); + end; + end; + f_LinkCacheIsValid := True; + f_HighlightedLink := nil; +end; + +procedure Td2dDocRoot.Clear; +begin + DropLinkCache; + inherited Clear; +end; + +procedure Td2dDocRoot.DropLinkCache; +begin + f_LinkCacheIsValid := False; + f_HighlightedLink := nil; + IterateLinks(_DropHighlight); +end; + +procedure Td2dDocRoot.DropLinkHighlight; +begin + if f_HighlightedLink <> nil then + begin + f_HighlightedLink.Slice.IsHighlighted := False; + f_HighlightedLink.Slice.SpreadHighlight; + f_HighlightedLink := nil; + end; +end; + +procedure Td2dDocRoot.FindLinkHighlight(const aX, aY: Single; var theEvent: Td2dInputEvent); +var + I: Integer; + l_CI: Td2dLinkSliceCacheItem; +begin + if not f_LinkCacheIsValid then + BuildLinkSliceCache; + if f_HighlightedLink <> nil then + begin + if not D2DIsPointInRect(gD2DE.MouseX - aX, gD2DE.MouseY - aY, f_HighlightedLink.Rect) or IsMouseMoveMasked(theEvent) then + DropLinkHighlight + else + begin + MaskMouseMove(theEvent); + Exit; // it's still highlighted + end; + end; + + if not IsMouseMoveMasked(theEvent) then + begin + for I := 0 to f_LinksCache.Count - 1 do + begin + l_CI := Td2dLinkSliceCacheItem(f_LinksCache.Items[I]); + if D2DIsPointInRect(gD2DE.MouseX - aX, gD2DE.MouseY - aY, l_CI.Rect) and (l_CI.Slice.IsActive) then + begin + l_CI.Slice.IsHighlighted := True; + l_CI.Slice.SpreadHighlight; + f_HighlightedLink := l_CI; + MaskMouseMove(theEvent); + Break; + end; + end; + end; // if not IsMouseMoveMasked(theEvent) then +end; + +procedure Td2dDocRoot.ForceLinkAllowance; +begin + if f_LinksAllowed then + IterateLinks(_AllowLinks) + else + IterateLinks(_DisallowLinks); +end; + +function Td2dDocRoot.GetTopChild: Integer; +begin + if ChildrenCount = 0 then + Result := -1 + else + Result := 0; +end; + +function Td2dDocRoot.GetBottomChild: Integer; +begin + Result := ChildrenCount - 1; +end; + +function Td2dDocRoot.GetVerticalShift: Single; +begin + Result := 0.0; +end; + +procedure Td2dDocRoot.IterateLinks(anAction: Td2dLinkActionProc; aFrom: Integer = 0); +begin + f_CurLinkAction := anAction; + IterateLeafSlices(_LinkIteratorProc, aFrom); +end; + +procedure Td2dDocRoot.pm_SetLinksAllowed(const Value: Boolean); +begin + if f_LinksAllowed <> Value then + begin + f_LinksAllowed := Value; + ForceLinkAllowance; + end; +end; + +procedure Td2dDocRoot._AllowLinks(const aLinkSlice: Td2dLinkSlice); +begin + aLinkSlice.Allowed := True; +end; + +procedure Td2dDocRoot._DisallowLinks(const aLinkSlice: Td2dLinkSlice); +begin + aLinkSlice.Allowed := False; +end; + +procedure Td2dDocRoot._DropHighlight(const aLinkSlice: Td2dLinkSlice); +begin + aLinkSlice.IsHighlighted := False; +end; + +procedure Td2dDocRoot._LinkIteratorProc(const aSlice: Td2dCustomSlice); +begin + if aSlice.SliceType = stLink then + f_CurLinkAction(Td2dLinkSlice(aSlice)); +end; + +constructor Td2dLinkSliceCacheItem.Create(aRect: Td2dRect; const aSlice: Td2dLinkSlice); +begin + inherited Create; + f_Rect := aRect; + f_Slice := aSlice; +end; + +end. \ No newline at end of file diff --git a/d2dFont.pas b/d2dFont.pas new file mode 100644 index 0000000..e4f3291 --- /dev/null +++ b/d2dFont.pas @@ -0,0 +1,720 @@ +unit d2dFont; + +interface + +uses + Windows, + D3DX8, + d2dTypes, + d2dInterfaces, + d2dSprite, + + SimpleXML; + +type + Td2dHGELetter = class(Td2dSprite) + private + f_KernPost: Single; + f_KernPre: Single; + public + constructor Create(aTex: Id2dTexture; aTexX, aTexY: Integer; + aWidth, aHeight: Integer; aKernPre, aKernPost: Single); + property KernPost: Single read f_KernPost write f_KernPost; + property KernPre: Single read f_KernPre write f_KernPre; + end; + + Td2dCustomFont = class(TInterfacedObject, Id2dFont) + private + f_BlendMode: Integer; + f_Color: Longword; + f_ID: string; + function pm_GetBlendMode: Integer; + function pm_GetColor: Longword; + function pm_GetID: string; + procedure pm_SetBlendMode(const aValue: Integer); + procedure pm_SetColor(const aValue: Longword); + procedure pm_SetID(const Value: string); + protected + procedure DoSetBlendMode(aBlendMode: Integer); virtual; abstract; + procedure DoSetColor(aColor: Td2dColor); virtual; abstract; + function pm_GetHeight: Single; virtual; abstract; + function pm_GetSize: Single; virtual; abstract; + public + function CalcStringBySize(const aStr: string; aWidth: Single; const aEllipsis: string = '...'): string; + procedure CalcSize(const aStr: string; var theSize: Td2dPoint; aLength: Integer = MaxInt); virtual; abstract; + function CanRenderChar(aChar: Char): Boolean; virtual; abstract; + procedure Render(aX, aY: Single; aStr: string); virtual; abstract; + property BlendMode: Integer read pm_GetBlendMode write pm_SetBlendMode; + property Color: Longword read pm_GetColor write pm_SetColor; + property Height: Single read pm_GetHeight; + property ID: string read pm_GetID write pm_SetID; + property Size: Single read pm_GetSize; + end; + + Td2dHGEFont = class(Td2dCustomFont) + private + f_Height: Single; + f_Letters: array[#0..#255] of Td2dHGELetter; + f_Scale: Single; + f_Spacing: Single; + f_Tracking: Single; + protected + procedure DoSetBlendMode(aBlendMode: Integer); override; + procedure DoSetColor(aColor: Td2dColor); override; + function pm_GetHeight: Single; override; + function pm_GetSize: Single; override; + public + constructor Create(aFontFileName: string; aPackOnly: Boolean = False); + destructor Destroy; override; + procedure CalcSize(const aStr: string; var theSize: Td2dPoint; aLength: Integer = MaxInt); override; + function CanRenderChar(aChar: Char): Boolean; override; + procedure Render(aX, aY: Single; aStr: string); override; + property Scale: Single read f_Scale write f_Scale; + property Spacing: Single read f_Spacing write f_Spacing; + property Tracking: Single read f_Tracking write f_Tracking; + end; + + Td2dDXFont = class(TObject) + private + f_Bold: Boolean; + f_Color: Longword; + f_Typeface: string; + f_Intf : ID3DXFont; + f_Italic: Boolean; + f_Size: Integer; + procedure pm_SetBold(const Value: Boolean); + procedure pm_SetTypeface(const Value: string); + procedure pm_SetItalic(const Value: Boolean); + procedure pm_SetSize(const Value: Integer); + procedure RecreateDXFont; + property Italic: Boolean read f_Italic write pm_SetItalic; + public + constructor Create(aTypeface: string; aSize: Integer; aBold: Boolean = False); + procedure Render(aString: string; aLeft, aTop, aRight, aBottom: Integer; aFlags: Longword = DT_TOP + + DT_LEFT+DT_NOPREFIX); + property Bold: Boolean read f_Bold write pm_SetBold; + property Color: Longword read f_Color write f_Color; + property Typeface: string read f_Typeface write pm_SetTypeface; + property Size: Integer read f_Size write pm_SetSize; + end; + + Td2dBMLetter = class(Td2dSprite) + private + f_XAdvance: Integer; + f_XOffset : Integer; + f_YOffset : Integer; + public + property XAdvance: Integer read f_XAdvance; + property XOffset: Integer read f_XOffset; + property YOffset: Integer read f_YOffset; + end; + + Td2dBMFont = class(Td2dCustomFont) + private + f_FontHeight: Integer; + f_FontSize: Integer; + f_Letters: array[#33..#255] of Td2dBMLetter; + f_SpaceAdv: Integer; + procedure LoadFromXML(aXML: IXmlDocument); + procedure Load(aFilename: string; aFromPack: Boolean); + protected + procedure DoSetBlendMode(aBlendMode: Integer); override; + procedure DoSetColor(aColor: Td2dColor); override; + function pm_GetHeight: Single; override; + function pm_GetSize: Single; override; + public + constructor Create(const aFilename: string; aFromPack: Boolean = False); + constructor CreateFromXML(aXML: IXmlDocument); + destructor Destroy; override; + procedure CalcSize(const aStr: string; var theSize: Td2dPoint; aLength: Integer = MaxInt); override; + function CanRenderChar(aChar: Char): Boolean; override; + procedure Render(aX, aY: Single; aStr: string); override; + end; + +implementation + +uses + Classes, + SysUtils, + StrUtils, + + d2dCore, + d2dUtils; + +constructor Td2dHGELetter.Create(aTex: Id2dTexture; aTexX, aTexY: Integer; + aWidth, aHeight: Integer; aKernPre, aKernPost: Single); +begin + inherited Create(aTex, aTexX, aTexY, aWidth, aHeight); + f_KernPost := aKernPost; + f_KernPre := aKernPre; +end; + +constructor Td2dHGEFont.Create(aFontFileName: string; aPackOnly: Boolean = False); +type + TCharset = set of Char; +const + cBitmapSig = 'Bitmap='; + cCharSig = 'Char='; +var + l_Temp : Pointer; + l_DescSize: Longword; + l_CurPos : Integer; + l_FontDesc: string; + l_Tex : Id2dTexture; + l_TempStr: string; + l_Char: Char; + l_X, l_Y, l_W, l_H, l_Pre, l_Post: Integer; + + function ScanTo(aStopChars: TCharset): string; + begin + Result := ''; + while not ((l_CurPos > Length(l_FontDesc)) or (l_FontDesc[l_CurPos] in aStopChars)) do + begin + Result := Result + l_FontDesc[l_CurPos]; + Inc(l_CurPos); + end; + end; + + function GetNumber: Integer; + var + l_Tmp: string; + begin + l_Tmp := ScanTo([#10, #13, ',']); + Result := StrToIntDef(l_Tmp, 0); + end; + + function GetChar: Char; + var + l_Tmp : string; + l_Code: Byte; + begin + if l_FontDesc[l_CurPos] = '"' then + begin + Inc(l_CurPos, 1); + Result := l_FontDesc[l_CurPos]; + Inc(l_CurPos, 3); + end + else + begin + l_Tmp := Trim(ScanTo([','])); + l_Code := StrToIntDef('$'+l_Tmp, 0); + Result := Char(l_Code); + Inc(l_CurPos, 1); + end; + end; + +begin + inherited Create; + aFontFileName := StringReplace(aFontFileName, '/', '\', [rfReplaceAll]); + f_Scale := 1.0; + f_Tracking := 0.0; + f_Spacing := 1.0; + f_Height := 0.0; + FillChar(f_Letters, SizeOf(f_Letters), 0); + l_Temp := gD2DE.Resource_Load(aFontFileName, @l_DescSize, aPackOnly); + if l_Temp <> nil then + begin + SetLength(l_FontDesc, l_DescSize); + Move(l_Temp^, l_FontDesc[1], l_DescSize); + FreeMem(l_Temp); + + l_CurPos := PosEx(cBitmapSig, l_FontDesc); + if l_CurPos > 0 then + begin + Inc(l_CurPos, Length(cBitmapSig)); + l_TempStr := ScanTo([#13,#10]); + if l_TempStr <> '' then + begin + l_TempStr := ExtractFilePath(aFontFileName) + l_TempStr; + l_Tex := gD2DE.Texture_Load(PAnsiChar(l_TempStr), aPackOnly, False); + if l_Tex <> nil then + begin + repeat + l_CurPos := PosEx(cCharSig, l_FontDesc, l_CurPos); + if l_CurPos > 0 then + begin + Inc(l_CurPos, Length(cCharSig)); + l_Char := GetChar; + + l_X := GetNumber; Inc(l_CurPos, 1); + l_Y := GetNumber; Inc(l_CurPos, 1); + l_W := GetNumber; Inc(l_CurPos, 1); + l_H := GetNumber; Inc(l_CurPos, 1); + l_Pre := GetNumber; Inc(l_CurPos, 1); + l_Post := GetNumber; + Assert(f_Letters[l_Char] = nil, 'Reassigning character in Td2dFont.Create'); + f_Letters[l_Char] := Td2dHGELetter.Create(l_Tex, l_X, l_Y, l_W, l_H, l_Pre, l_Post); + if l_H > f_Height then + f_Height := l_H; + end; + until (l_CurPos = 0) or (l_CurPos > Length(l_FontDesc)); + end; + end; + end; + end; + Color := $FFFFFFFF; + BlendMode := BLEND_DEFAULT; +end; + +destructor Td2dHGEFont.Destroy; +var + C: Char; +begin + for C := #0 to #255 do + if f_Letters[C] <> nil then + FreeAndNil(f_Letters[C]); + inherited; +end; + +procedure Td2dHGEFont.CalcSize(const aStr: string; var theSize: Td2dPoint; aLength: Integer = MaxInt); +var + I: Integer; + l_Char: Char; + l_CurWidth : Single; + l_MaxIdx: Integer; +begin + theSize.X := 0; + theSize.Y := f_Height * f_Scale; + l_CurWidth := 0; + I := 1; + l_MaxIdx := aLength; + if l_MaxIdx > Length(aStr) then + l_MaxIdx := Length(aStr); + while I <= l_MaxIdx do + begin + l_Char := aStr[I]; + if l_Char in [#10, #13] then + begin + theSize.Y := theSize.Y + (f_Height * f_Scale * f_Spacing); + l_CurWidth := 0; + while (I <= Length(aStr)) and (aStr[I] in [#10, #13]) do + Inc(I); + Continue; + end + else + begin + if not CanRenderChar(l_Char) then + if l_Char = ' ' then + l_CurWidth := l_CurWidth + f_Height/4*f_Scale + else + l_Char := '?'; + if CanRenderChar(l_Char) then + with f_Letters[l_Char] do + l_CurWidth := l_CurWidth + (KernPre + Width + KernPost + f_Tracking)*f_Scale; + if l_CurWidth > theSize.X then + theSize.X := l_CurWidth; + end; + Inc(I); + end; +end; + +function Td2dHGEFont.CanRenderChar(aChar: Char): Boolean; +begin + Result := f_Letters[aChar] <> nil; +end; + +procedure Td2dHGEFont.DoSetBlendMode(aBlendMode: Integer); +var + C: Char; +begin + for C := #0 to #255 do + if f_Letters[C] <> nil then + f_Letters[C].BlendMode := aBlendMode; +end; + +procedure Td2dHGEFont.DoSetColor(aColor: Td2dColor); +var + C: Char; +begin + for C := #0 to #255 do + if f_Letters[C] <> nil then + f_Letters[C].SetColor(aColor); +end; + +function Td2dHGEFont.pm_GetHeight: Single; +begin + Result := f_Height; +end; + +function Td2dHGEFont.pm_GetSize: Single; +begin + Result := pm_GetHeight; +end; + +procedure Td2dHGEFont.Render(aX, aY: Single; aStr: string); +var + l_Char: Char; + l_FX, l_FY : Single; + I: Integer; +begin + l_FX := aX; + l_FY := aY; + I := 1; + while I <= Length(aStr) do + begin + l_Char := aStr[I]; + if l_Char in [#10, #13] then + begin + l_FY := l_FY + Int(f_Height * f_Scale * f_Spacing); + l_FX := aX; + while (I <= Length(aStr)) and (aStr[I] in [#10, #13]) do + Inc(I); + Continue; + end + else + begin + if not CanRenderChar(l_Char) then + if l_Char = ' ' then + l_FX := l_FX + Int(f_Height/4*f_Scale) + else + l_Char := '?'; + if CanRenderChar(l_Char) then + with f_Letters[l_Char] do + begin + l_FX := l_FX + Int(KernPre * f_Scale); + RenderEx(l_FX, l_FY, 0.0, f_Scale); + l_FX := l_FX + Int((Width + KernPost + f_Tracking)*f_Scale); + end; + end; + Inc(I); + end; +end; + +constructor Td2dDXFont.Create(aTypeface: string; aSize: Integer; aBold: Boolean = False); +begin + inherited Create; + f_Typeface := aTypeface; + f_Size := aSize; + f_Bold := aBold; + f_Color := $FFFFFFFF; + RecreateDXFont; +end; + +procedure Td2dDXFont.pm_SetBold(const Value: Boolean); +begin + f_Bold := Value; +end; + +procedure Td2dDXFont.pm_SetTypeface(const Value: string); +begin + f_Typeface := Value; +end; + +procedure Td2dDXFont.pm_SetItalic(const Value: Boolean); +begin + f_Italic := Value; +end; + +procedure Td2dDXFont.pm_SetSize(const Value: Integer); +begin + f_Size := Value; +end; + +procedure Td2dDXFont.RecreateDXFont; +var + lf: TLogFont; + l_N: Integer; + I: Integer; +begin + FillChar(lf, SizeOf(lf), 0); + with lf do + begin + lfHeight := -f_Size; + if f_Bold then + lfWeight := FW_BOLD; + if f_Italic then + lfItalic := 1; + lfCharSet := DEFAULT_CHARSET; + lfOutPrecision := OUT_DEFAULT_PRECIS; + lfClipPrecision := CLIP_DEFAULT_PRECIS; + lfQuality := PROOF_QUALITY; + lfPitchAndFamily := DEFAULT_PITCH or FF_DONTCARE; + l_N := Length(f_Typeface); + if l_N > 31 then + l_N := 31; + for I := 1 to l_N do + lfFaceName[I-1] := f_Typeface[I]; + end; + f_Intf := nil; + D3DXCreateFontIndirect(gD2DE.D3DDevice, lf, f_Intf); +end; + +procedure Td2dDXFont.Render(aString: string; aLeft, aTop, aRight, aBottom: Integer; aFlags: Longword = DT_TOP + + DT_LEFT+DT_NOPREFIX); +var + l_Rect: TRect; +begin + l_Rect := Rect(aLeft, aTop, aRight, aBottom); + gD2DE.FlushPrimitives; + f_Intf.DrawTextA(PChar(aString), Length(aString), l_Rect, aFlags, f_Color); +end; + +function Td2dCustomFont.CalcStringBySize(const aStr: string; aWidth: Single; const aEllipsis: string = '...'): string; +var + l_Len: Integer; + l_Size: Td2dPoint; +begin + Result := aStr; + l_Len := Length(aStr); + while True do + begin + CalcSize(Result, l_Size); + if l_Size.X <= aWidth then + Break; + Dec(l_Len); + if l_Len = 0 then + begin + Result := ''; + Break; + end; + Result := Copy(aStr, 1, l_Len) + aEllipsis; + end; +end; + +function Td2dCustomFont.pm_GetBlendMode: Integer; +begin + Result := f_BlendMode; +end; + +function Td2dCustomFont.pm_GetColor: Longword; +begin + Result := f_Color; +end; + +function Td2dCustomFont.pm_GetID: string; +begin + Result := f_ID; +end; + +procedure Td2dCustomFont.pm_SetBlendMode(const aValue: Integer); +begin + if f_BlendMode <> aValue then + begin + f_BlendMode := aValue; + DoSetBlendMode(aValue); + end; +end; + +procedure Td2dCustomFont.pm_SetColor(const aValue: Longword); +begin + if f_Color <> aValue then + begin + f_Color := aValue; + DoSetColor(aValue); + end; +end; + +procedure Td2dCustomFont.pm_SetID(const Value: string); +begin + f_ID := Value; +end; + +constructor Td2dBMFont.Create(const aFilename: string; aFromPack: Boolean = False); +begin + inherited Create; + Load(aFilename, aFromPack); +end; + +constructor Td2dBMFont.CreateFromXML(aXML: IXmlDocument); +begin + inherited Create; + LoadFromXML(aXML); +end; + +destructor Td2dBMFont.Destroy; +var + C: Char; +begin + for C := #33 to #255 do + if f_Letters[C] <> nil then + FreeAndNil(f_Letters[C]); + inherited; +end; + +procedure Td2dBMFont.CalcSize(const aStr: string; var theSize: Td2dPoint; aLength: Integer = MaxInt); +var + I: Integer; + l_Char: Char; + l_CurWidth : Single; + l_MaxIdx: Integer; +begin + theSize.X := 0; + theSize.Y := f_FontHeight; + l_CurWidth := 0; + I := 1; + l_MaxIdx := aLength; + if l_MaxIdx > Length(aStr) then + l_MaxIdx := Length(aStr); + while I <= l_MaxIdx do + begin + l_Char := aStr[I]; + if l_Char in [#10, #13] then + begin + theSize.Y := theSize.Y + f_FontHeight; + l_CurWidth := 0; + while (I <= Length(aStr)) and (aStr[I] in [#10, #13]) do + Inc(I); + Continue; + end + else + if l_Char in [#32, #160] then + l_CurWidth := l_CurWidth + f_SpaceAdv + else + begin + if not CanRenderChar(l_Char) then + l_Char := '?'; + if CanRenderChar(l_Char) then + with f_Letters[l_Char] do + l_CurWidth := l_CurWidth + XAdvance; + end; + if l_CurWidth > theSize.X then + theSize.X := l_CurWidth; + Inc(I); + end; +end; + +function Td2dBMFont.CanRenderChar(aChar: Char): Boolean; +begin + Result := (aChar in [#33..#255]) and (f_Letters[aChar] <> nil); +end; + +procedure Td2dBMFont.DoSetBlendMode(aBlendMode: Integer); +var + C: Char; +begin + for C := #33 to #255 do + if f_Letters[C] <> nil then + f_Letters[C].BlendMode := aBlendMode; +end; + +procedure Td2dBMFont.DoSetColor(aColor: Td2dColor); +var + C: Char; +begin + for C := #33 to #255 do + if f_Letters[C] <> nil then + f_Letters[C].SetColor(aColor); +end; + +const + cMaxBMPages = 100; + +procedure Td2dBMFont.LoadFromXML(aXML: IXmlDocument); +var + l_Tex: Id2dTexture; + I: Integer; + l_Node: IXmlNode; + l_List: IXmlNodeList; + l_Char: Char; + l_Code: Integer; + l_PNGData: string; + //l_FS: TFileStream; +begin + if (aXML <> nil) and (aXML.DocumentElement <> nil) then + begin + l_Node := aXML.DocumentElement.SelectSingleNode('properties'); + f_FontHeight := l_Node.GetIntAttr('lineheight'); + f_FontSize := l_Node.GetIntAttr('size'); + f_SpaceAdv := l_Node.GetIntAttr('space'); + + l_Node := aXML.DocumentElement.SelectSingleNode('png'); + l_PNGData := Base64ToBin(l_Node.Text); + { + l_FS := TFileStream.Create('c:\temp\font.png', fmCreate); + l_FS.Write(l_PNGData[1], Length(l_PNGData)); + FreeAndNil(l_FS); + } + l_Tex := gD2DE.Texture_FromMemory(@l_PNGData[1], Length(l_PNGData)); + + l_List := aXML.DocumentElement.SelectSingleNode('letters').ChildNodes; + for I := 0 to l_List.Count-1 do + begin + l_Node := l_List.Item[I]; + l_Code := l_Node.GetIntAttr('code'); + Assert((l_Code <= 255) and (l_Code > 32), 'Wrong charcode in BM Font!'); + l_Char := Char(l_Code); + f_Letters[l_Char] := Td2dBMLetter.Create(l_Tex, + l_Node.GetIntAttr('tx'), l_Node.GetIntAttr('ty'), + l_Node.GetIntAttr('w'), l_Node.GetIntAttr('h')); + f_Letters[l_Char].f_XOffset := l_Node.GetIntAttr('sx'); + f_Letters[l_Char].f_YOffset := l_Node.GetIntAttr('sy'); + f_Letters[l_Char].f_XAdvance := l_Node.GetIntAttr('adv'); + end; + end; +end; + +procedure Td2dBMFont.Load(aFilename: string; aFromPack: Boolean); +var + l_XML: string; + l_XMLLen: Longword; + l_Temp: Pointer; + l_Doc: IXmlDocument; +begin + l_Temp := gD2DE.Resource_Load(aFilename, @l_XMLLen, aFromPack); + if l_Temp <> nil then + begin + SetLength(l_XML, l_XMLLen); + Move(l_Temp^, l_XML[1], l_XMLLen); + gD2DE.Resource_Free(l_Temp); + l_Doc := LoadXmlDocumentFromXML(l_XML); + LoadFromXML(l_Doc); + end; +end; + +function Td2dBMFont.pm_GetHeight: Single; +begin + Result := f_FontHeight; +end; + +function Td2dBMFont.pm_GetSize: Single; +begin + Result := f_FontSize; +end; + +procedure Td2dBMFont.Render(aX, aY: Single; aStr: string); +var + l_Char: Char; + l_FX, l_FY : Single; + I: Integer; +begin + l_FX := aX; + l_FY := aY; + I := 1; + while I <= Length(aStr) do + begin + l_Char := aStr[I]; + + if l_Char in [#10, #13] then + begin + l_FY := l_FY + f_FontHeight; + l_FX := aX; + while (I <= Length(aStr)) and (aStr[I] in [#10, #13]) do + Inc(I); + Continue; + end + else + begin + if l_Char in [#32, #160] then + l_FX := l_FX + f_SpaceAdv + else + begin + if not CanRenderChar(l_Char) then + l_Char := '?'; + if CanRenderChar(l_Char) then + begin + with f_Letters[l_Char] do + begin + Render(l_FX + XOffset - 1, l_FY+YOffset - 1); + //D2DRenderRect(D2DRect(l_FX + XOffset, l_FY, l_FX + XAdvance, l_FY + f_FontHeight), $FFFF0000); + l_FX := l_FX + XAdvance; + end; + end + else + l_FX := l_FX + f_SpaceAdv; + end; + end; + Inc(I); + end; +end; + +end. diff --git a/d2dFormattedText.pas b/d2dFormattedText.pas new file mode 100644 index 0000000..816ddf9 --- /dev/null +++ b/d2dFormattedText.pas @@ -0,0 +1,1360 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dFormattedText; + +interface +uses + Classes, + Contnrs, + + d2dTypes, + d2dInterfaces, + d2dUtils; + +type + Td2dCustomSlice = class; + Td2dLinkSlice = class; + Td2dCustomTextChunk = class; + + Td2dLinkActionProc = procedure(const aLinkSlice: Td2dLinkSlice) of object; + Td2dSliceActionProc = procedure(const aSlice: Td2dCustomSlice) of object; + Td2dChunkActionProc = procedure(const aChunk: Td2dCustomTextChunk) of object; + Td2dOnLinkClickEvent = procedure(const aSender: TObject; const aRect: Td2dRect; const aTarget: string) of object; + + Td2dCustomTextChunk = class + protected + function pm_GetChunkType: Td2dTextChunkType; virtual; + public + procedure Save(aFiler: Td2dFiler); virtual; + procedure Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); virtual; abstract; + property ChunkType: Td2dTextChunkType read pm_GetChunkType; + end; + + Td2dTextPara = class + private + f_IsClosed: Boolean; + f_List: TObjectList; + f_ParaType: Td2dTextAlignType; + function pm_GetChunks(Index: Integer): Td2dCustomTextChunk; + function pm_GetCount: Integer; + procedure _DropLink(const aChunk: Td2dCustomTextChunk); + public + constructor Create(aParaType: Td2dTextAlignType); + destructor Destroy; override; + procedure AddPicture(const aPicture: Id2dPicture; const anID: string = ''); + procedure AddRawChunk(aChunk: Td2dCustomTextChunk); + procedure AddText(const aText: string; const aFont: Id2dFont; aColor: Td2dColor; aForceNewChunk: Boolean = False); + procedure AddLink(const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, aHighlightColor: Td2dColor); + procedure ClosePara; + procedure DropLinks; + procedure IterateChunks(anAction: Td2dChunkActionProc); + procedure Save(aFiler: Td2dFiler); + procedure Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); + property Chunks[Index: Integer]: Td2dCustomTextChunk read pm_GetChunks; + property Count: Integer read pm_GetCount; + property IsClosed: Boolean read f_IsClosed; + property ParaType: Td2dTextAlignType read f_ParaType write f_ParaType; + end; + + Td2dStringChunk = class(Td2dCustomTextChunk) + private + f_Font: Id2dFont; + protected + f_Color: Td2dColor; + f_Text: string; + function pm_GetChunkType: Td2dTextChunkType; override; + public + constructor Create(const aText: string; const aFont: Id2dFont; aColor: Td2dColor); + procedure ConcatText(aText: string); + procedure Save(aFiler: Td2dFiler); override; + procedure Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); override; + property Color: Td2dColor read f_Color write f_Color; + property Font: Id2dFont read f_Font write f_Font; + property Text: string read f_Text write f_Text; + end; + + Td2dLinkChunk = class(Td2dStringChunk) + private + f_Active: Boolean; + f_HighlightColor: Td2dColor; + f_LinkColor: Td2dColor; + f_Target: string; + protected + function pm_GetChunkType: Td2dTextChunkType; override; + public + constructor Create(const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, aHighlightColor: Td2dColor); + procedure Save(aFiler: Td2dFiler); override; + procedure Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); override; + property Active: Boolean read f_Active write f_Active; + property HighlightColor: Td2dColor read f_HighlightColor write f_HighlightColor; + property LinkColor: Td2dColor read f_LinkColor write f_LinkColor; + property Target: string read f_Target write f_Target; + end; + + Td2dPictureChunk = class(Td2dCustomTextChunk) + private + protected + f_Picture: Id2dPicture; + function pm_GetChunkType: Td2dTextChunkType; override; + public + procedure Save(aFiler: Td2dFiler); override; + procedure Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); override; + constructor Create(const aPicture: Id2dPicture; const anID: string); + property Picture: Id2dPicture read f_Picture; + end; + + Id2dTextAddingTool = interface + ['{99660309-19E2-4131-8E67-88EDC936D269}'] + procedure AddText(const aText: string); + procedure AddLink(const aText, aTarget: string); + end; + + Td2dTextSource = class + private + f_ParaList: TObjectList; + procedure AddTextOrLink(aIsLink: Boolean; const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, + aHighlightColor: Td2dColor; aParaType: Td2dTextAlignType); + function pm_GetParagraphs(Index: Integer): Td2dTextPara; + function pm_GetCount: Integer; + public + constructor Create; + destructor Destroy; override; + procedure Clear; + procedure Save(aFiler: Td2dFiler); + procedure Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); + function AddRawPara(aParaType: Td2dTextAlignType): Td2dTextPara; + procedure AddText(const aText: string; const aFont: Id2dFont; aColor: Td2dColor; aParaType: Td2dTextAlignType); + procedure AddLink(const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, aHighlightColor: Td2dColor; + aParaType: Td2dTextAlignType); + procedure AddPicture(aPicture: Id2dPicture; aAlign: Td2dTextAlignType); + procedure DropLinks(aFrom: Integer = 0); + procedure IterateChunks(anAction: Td2dChunkActionProc); + property Paragraphs[Index: Integer]: Td2dTextPara read pm_GetParagraphs; + property Count: Integer read pm_GetCount; + end; + + // Ñëàéñ - åäèíèöà ôîðìàòèðîâàííîãî òåêñòà + Td2dCustomSlice = class + private + f_Left: Single; + f_Parent: Td2dCustomSlice; + f_Top: Single; + function pm_GetAbsLeft: Single; + function pm_GetAbsTop: Single; + protected + function pm_GetHeight: Single; virtual; abstract; + function pm_GetWidth: Single; virtual; abstract; + procedure DoDraw(X, Y: Single); virtual; abstract; + function pm_GetSliceType: Td2dTextSliceType; virtual; + procedure pm_SetWidth(const Value: Single); virtual; abstract; + public + procedure Draw(X, Y: Single); + property AbsLeft: Single read pm_GetAbsLeft; + property AbsTop: Single read pm_GetAbsTop; + property Height: Single read pm_GetHeight; + property Left: Single read f_Left write f_Left; + property Parent: Td2dCustomSlice read f_Parent write f_Parent; + property SliceType: Td2dTextSliceType read pm_GetSliceType; + property Top: Single read f_Top write f_Top; + property Width: Single read pm_GetWidth write pm_SetWidth; + end; + + Td2dUnionSlice = class(Td2dCustomSlice) + private + f_ChList: TObjectList; + f_Height: Single; + f_Parent: Td2dUnionSlice; + f_Width: Single; + function pm_GetChildren(Index: Integer): Td2dCustomSlice; + function pm_GetChildrenCount: Integer; + function pm_GetChList: TObjectList; + protected + function pm_GetHeight: Single; override; + function pm_GetSliceType: Td2dTextSliceType; override; + function pm_GetWidth: Single; override; + procedure pm_SetWidth(const Value: Single); override; + property ChList: TObjectList read pm_GetChList; + public + constructor Create(aWidth: Single); + destructor Destroy; override; + procedure AddChild(aChild: Td2dCustomSlice); virtual; + procedure Clear; virtual; + procedure DeleteFrom(aIndex : Integer); virtual; + procedure DoDraw(X, Y: Single); override; + procedure IterateLeafSlices(anAction: Td2dSliceActionProc; aFrom: Integer = 0); + procedure RecalcHeight; + property Children[Index: Integer]: Td2dCustomSlice read pm_GetChildren; + property ChildrenCount: Integer read pm_GetChildrenCount; + property Parent: Td2dUnionSlice read f_Parent write f_Parent; + end; + + Td2dTextSlice = class(Td2dCustomSlice) + private + f_Color: Td2dColor; + f_Size : Td2dPoint; + procedure CalcSize; + protected + f_Font: Id2dFont; + f_Text : string; + procedure DoDraw(X, Y: Single); override; + function pm_GetHeight: Single; override; + function pm_GetSliceType: Td2dTextSliceType; override; + function pm_GetWidth: Single; override; + public + constructor Create(const aText: string; const aFont: Id2dFont; aColor: Td2dColor); + property Color: Td2dColor read f_Color write f_Color; + end; + + Td2dPictureSlice = class(Td2dCustomSlice) + private + f_Picture : Id2dPicture; + protected + function pm_GetHeight: Single; override; + function pm_GetSliceType: Td2dTextSliceType; override; + function pm_GetWidth: Single; override; + public + constructor Create(const aPicture: Id2dPicture); + procedure DoDraw(X, Y: Single); override; + end; + + Td2dFormatParamsRec = record + rLineSpacing: Single; + rParaSpacing: Single; + end; + + // Formatter + Td2dFormatter = class + private + f_Document: Td2dUnionSlice; + f_FormatParams: Td2dFormatParamsRec; + f_LastFormatted: Integer; + f_TextSource: Td2dTextSource; + procedure FormatFrom(aParaIndex: Integer); + function FormatParagraph(aParaIdx: Integer): Td2dUnionSlice; + procedure pm_SetLastFormatted(const Value: Integer); + public + constructor Create(aTS: Td2dTextSource; aDoc: Td2dUnionSlice); + procedure Format; + procedure ReformatAll; + procedure UpdateFormat; + property FormatParams: Td2dFormatParamsRec read f_FormatParams write f_FormatParams; + property LastFormatted: Integer read f_LastFormatted write pm_SetLastFormatted; + end; + + Td2dLinkSlice = class(Td2dTextSlice) + private + f_Allowed: Boolean; + f_HighlightColor: Td2dColor; + f_IsActive: Boolean; + f_IsHighlighted: Boolean; + f_LinkColor: Td2dColor; + f_Next: Td2dLinkSlice; + f_Prev: Td2dLinkSlice; + f_Target: string; + protected + procedure DoDraw(X, Y: Single); override; + function pm_GetSliceType: Td2dTextSliceType; override; + public + constructor Create(const aText, aTarget: string; const aFont: Id2dFont; aTextColor, aLinkColor, aHighlightColor: Td2dColor); + function GetLinkText: string; + procedure SpreadHighlight; + property Allowed: Boolean read f_Allowed write f_Allowed; + property HighlightColor: Td2dColor read f_HighlightColor write f_HighlightColor; + property IsActive: Boolean read f_IsActive write f_IsActive; + property IsHighlighted: Boolean read f_IsHighlighted write f_IsHighlighted; + property LinkColor: Td2dColor read f_LinkColor write f_LinkColor; + property Next: Td2dLinkSlice read f_Next write f_Next; + property Prev: Td2dLinkSlice read f_Prev write f_Prev; + property Target: string read f_Target write f_Target; + end; + +implementation +uses + SysUtils, + d2dCore, + StrUtils; + +type + TChunkCursor = record + rChunk: Integer; + rPos : Integer; + end; + +const + csClosedPara = 'Ïàðàãðàô óæå çàêðûò!'; + +type + Td2dTextChunkClass = class of Td2dCustomTextChunk; + +function TextChunkType2Class(const aType: Td2dTextChunkType): Td2dTextChunkClass; +begin + case aType of + ctText: Result := Td2dStringChunk; + ctLink: Result := Td2dLinkChunk; + ctPicture: Result := Td2dPictureChunk; + else + Result := nil; + end; +end; + +function d2dLoadChunk(const aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider): Td2dCustomTextChunk; +var + l_CP: Td2dTextChunkType; + l_ChunkClass: Td2dTextChunkClass; +begin + l_CP := Td2dTextChunkType(aFiler.ReadByte); + l_ChunkClass := TextChunkType2Class(l_CP); + if l_ChunkClass <> nil then + begin + Result := l_ChunkClass.Create; + Result.Load(aFiler, aFP, aPP); + end + else + Result := nil; +end; + + +function Td2dCustomTextChunk.pm_GetChunkType: Td2dTextChunkType; +begin + Result := ctUndefined; +end; + +procedure Td2dCustomTextChunk.Save(aFiler: Td2dFiler); +begin + aFiler.WriteByte(Ord(ChunkType)); +end; + +constructor Td2dStringChunk.Create(const aText: string; const aFont: Id2dFont; aColor: Td2dColor); +begin + inherited Create; + f_Text := aText; + f_Font := aFont; + f_Color := aColor; +end; + +procedure Td2dStringChunk.ConcatText(aText: string); +begin + f_Text := f_Text + aText; +end; + +procedure Td2dStringChunk.Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); +var + l_FN: string; +begin + l_FN := aFiler.ReadString; + f_Font := aFP.GetByID(l_FN); + f_Color := aFiler.ReadColor; + f_Text := aFiler.ReadString; +end; + +function Td2dStringChunk.pm_GetChunkType: Td2dTextChunkType; +begin + Result := ctText; +end; + +procedure Td2dStringChunk.Save(aFiler: Td2dFiler); +begin + inherited; + aFiler.WriteString(f_Font.ID); + aFiler.WriteColor(f_Color); + aFiler.WriteString(f_Text); +end; + +constructor Td2dTextSource.Create; +begin + inherited; + f_ParaList := TObjectList.Create(True); +end; + +destructor Td2dTextSource.Destroy; +begin + f_ParaList.Free; + inherited; +end; + +procedure Td2dTextSource.AddLink(const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, + aHighlightColor: Td2dColor; aParaType: Td2dTextAlignType); +begin + AddTextOrLink(True, aText, aTarget, aFont, aColor, aLinkColor, aHighlightColor, aParaType); +end; + +procedure Td2dTextSource.AddPicture(aPicture: Id2dPicture; aAlign: Td2dTextAlignType); +var +// l_Picture: Id2dPicture; + l_Para: Td2dTextPara; +begin + { + if (aWidth = 0) then + aWidth := gD2DE.Texture_GetWidth(aTex) - aTX; + if (aHeight = 0) then + aHeight := gD2DE.Texture_GetHeight(aTex) - aTY; + if (aWidth < 1) or (aHeight < 1) then + Exit; + l_Picture := Id2dPicture.Create(aTex, aTX, aTY, aWidth, aHeight); + l_Picture.SetColor(aColor); + } + if Count > 0 then + begin + l_Para := Paragraphs[Pred(Count)]; + if (l_Para.IsClosed) or (l_Para.ParaType <> aAlign) then + l_Para := AddRawPara(aAlign); + end + else + l_Para := AddRawPara(aAlign); + l_Para.AddPicture(aPicture); +end; + +function Td2dTextSource.AddRawPara(aParaType: Td2dTextAlignType): Td2dTextPara; +begin + Result := Td2dTextPara.Create(aParaType); + f_ParaList.Add(Result); +end; + +procedure Td2dTextSource.AddText(const aText: string; const aFont: Id2dFont; aColor: Td2dColor; aParaType: + Td2dTextAlignType); +begin + AddTextOrLink(False, aText, '', aFont, aColor, 0, 0, aParaType); +end; + +procedure Td2dTextSource.AddTextOrLink(aIsLink: Boolean; const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, aHighlightColor: Td2dColor; + aParaType: Td2dTextAlignType); +var + l_Para: Td2dTextPara; + l_Pos: Integer; + l_SubStr: string; + l_Text : string; + + procedure AddToPara(aStr: string; aDoClose: Boolean); + begin + if l_Para = nil then + l_Para := AddRawPara(aParaType); + if aIsLink then + l_Para.AddLink(aStr, aTarget, aFont, aColor, aLinkColor, aHighlightColor) + else + l_Para.AddText(aStr, aFont, aColor); + if aDoClose then + begin + l_Para.ClosePara; + l_Para := nil; + end; + end; + +begin + l_Text := aText; + if Count > 0 then + begin + l_Para := Paragraphs[Pred(Count)]; + if (l_Para.IsClosed) or (l_Para.ParaType <> aParaType) then + l_Para := nil; + end + else + l_Para := nil; + + l_Pos := Pos(#13#10, l_Text); + while l_Pos > 0 do + begin + l_SubStr := Copy(l_Text, 1, l_Pos-1); + AddToPara(l_SubStr, True); + Delete(l_Text, 1, l_Pos+1); + l_Pos := Pos(#13#10, l_Text); + end; + if l_Text <> '' then + AddToPara(l_Text, False); +end; + +procedure Td2dTextSource.Clear; +begin + f_ParaList.Clear; +end; + +procedure Td2dTextSource.DropLinks(aFrom: Integer = 0); +var + I: Integer; +begin + for I := aFrom to Count - 1 do + Paragraphs[I].DropLinks; +end; + +procedure Td2dTextSource.IterateChunks(anAction: Td2dChunkActionProc); +var + I: Integer; +begin + for I := 0 to Count - 1 do + Paragraphs[I].IterateChunks(anAction); +end; + +procedure Td2dTextSource.Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); +var + I: Integer; + l_Count: Integer; + l_Para: Td2dTextPara; +begin + Clear; + l_Count := aFiler.ReadInteger; + for I := 1 to l_Count do + begin + l_Para := Td2dTextPara.Create(ptLeftAligned); + l_Para.Load(aFiler, aFP, aPP); + f_ParaList.Add(l_Para); + end; +end; + +function Td2dTextSource.pm_GetParagraphs(Index: Integer): Td2dTextPara; +begin + Result := Td2dTextPara(f_ParaList[Index]); +end; + +function Td2dTextSource.pm_GetCount: Integer; +begin + Result := f_ParaList.Count; +end; + +procedure Td2dTextSource.Save(aFiler: Td2dFiler); +var + I: Integer; +begin + aFiler.WriteInteger(Count); + for I := 0 to Count-1 do + Paragraphs[I].Save(aFiler); +end; + +constructor Td2dTextPara.Create(aParaType: Td2dTextAlignType); +begin + inherited Create; + f_ParaType := aParaType; + f_List := TObjectList.Create(True); +end; + +destructor Td2dTextPara.Destroy; +begin + f_List.Free; + inherited; +end; + +procedure Td2dTextPara.AddPicture(const aPicture: Id2dPicture; const anID: string = ''); +var + l_Chunk: Td2dPictureChunk; +begin + l_Chunk := Td2dPictureChunk.Create(aPicture, anID); + f_List.Add(l_Chunk) +end; + +procedure Td2dTextPara.AddRawChunk(aChunk: Td2dCustomTextChunk); +begin + Assert(not IsClosed, csClosedPara); + f_List.Add(aChunk); +end; + +procedure Td2dTextPara.AddText(const aText: string; const aFont: Id2dFont; aColor: Td2dColor; aForceNewChunk: Boolean = False); +var + l_Chunk: Td2dCustomTextChunk; +begin + Assert(not IsClosed, csClosedPara); + if f_List.Count > 0 then + begin + l_Chunk := Td2dCustomTextChunk(f_List[Pred(f_List.Count)]); + if (not aForceNewChunk) and (l_Chunk.ChunkType = ctText) and + (Td2dStringChunk(l_Chunk).Font = aFont) and + (Td2dStringChunk(l_Chunk).Color = aColor) then + begin + Td2dStringChunk(l_Chunk).ConcatText(aText); + Exit; + end; + end; + l_Chunk := Td2dStringChunk.Create(aText, aFont, aColor); + f_List.Add(l_Chunk); +end; + +procedure Td2dTextPara.AddLink(const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, aHighlightColor: Td2dColor); +var + l_Chunk: Td2dCustomTextChunk; +begin + Assert(not IsClosed, csClosedPara); + l_Chunk := Td2dLinkChunk.Create(aText, aTarget, aFont, aColor, aLinkColor, aHighlightColor); + f_List.Add(l_Chunk); +end; + +procedure Td2dTextPara.ClosePara; +begin + f_IsClosed := True; +end; + +procedure Td2dTextPara.DropLinks; +begin + IterateChunks(_DropLink); +end; + +procedure Td2dTextPara._DropLink(const aChunk: Td2dCustomTextChunk); +begin + if aChunk.ChunkType = ctLink then + Td2dLinkChunk(aChunk).Active := False; +end; + +procedure Td2dTextPara.IterateChunks(anAction: Td2dChunkActionProc); +var + I: Integer; +begin + for I := 0 to Count - 1 do + anAction(Chunks[I]); +end; + +procedure Td2dTextPara.Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); +var + I: Integer; + l_Count: Integer; + l_Chunk: Td2dCustomTextChunk; +begin + f_List.Clear; + f_ParaType := Td2dTextAlignType(aFiler.ReadByte); + f_IsClosed := aFiler.ReadBoolean; + l_Count := aFiler.ReadInteger; + for I := 1 to l_Count do + begin + l_Chunk := d2dLoadChunk(aFiler, aFP, aPP); // ôàáðèêà + if l_Chunk <> nil then + f_List.Add(l_Chunk); + end; +end; + +function Td2dTextPara.pm_GetChunks(Index: Integer): Td2dCustomTextChunk; +begin + Result := Td2dCustomTextChunk(f_List[Index]); +end; + +function Td2dTextPara.pm_GetCount: Integer; +begin + Result := f_List.Count; +end; + +procedure Td2dTextPara.Save(aFiler: Td2dFiler); +var + I: Integer; +begin + aFiler.WriteByte(Ord(f_ParaType)); + aFiler.WriteBoolean(f_IsClosed); + aFiler.WriteInteger(Count); + for I := 0 to Count-1 do + Chunks[I].Save(aFiler); +end; + +constructor Td2dUnionSlice.Create(aWidth: Single); +begin + inherited Create; + f_Width := aWidth; +end; + +destructor Td2dUnionSlice.Destroy; +begin + if f_ChList <> nil then + FreeAndNil(f_ChList); + inherited; +end; + +procedure Td2dUnionSlice.AddChild(aChild: Td2dCustomSlice); +begin + ChList.Add(aChild); + aChild.Parent := Self; +end; + +procedure Td2dUnionSlice.DoDraw(X, Y: Single); +var + I : Integer; + l_Child: Td2dCustomSlice; +begin + for I := 0 to Pred(ChildrenCount) do + begin + l_Child := Children[I]; + l_Child.Draw(X, Y); + end; +end; + +function Td2dUnionSlice.pm_GetChildren(Index: Integer): Td2dCustomSlice; +begin + Result := Td2dCustomSlice(ChList[Index]); +end; + +function Td2dUnionSlice.pm_GetChildrenCount: Integer; +begin + if f_ChList = nil then + Result := 0 + else + Result := f_ChList.Count; +end; + +function Td2dUnionSlice.pm_GetChList: TObjectList; +begin + if f_ChList = nil then + f_ChList := TObjectList.Create(True); + Result := f_ChList; +end; + +function Td2dUnionSlice.pm_GetHeight: Single; +begin + Result := f_Height; +end; + +function Td2dUnionSlice.pm_GetWidth: Single; +begin + Result := f_Width; +end; + +procedure Td2dUnionSlice.RecalcHeight; +var + I: Integer; + l_Child: Td2dCustomSlice; + l_FarPoint: Single; +begin + f_Height := 0; + for I := 0 to Pred(ChildrenCount) do + begin + l_Child := Children[I]; + l_FarPoint := l_Child.Top + l_Child.Height; + if l_FarPoint > f_Height then + f_Height := l_FarPoint; + end; +end; + +procedure Td2dUnionSlice.Clear; +var + I: Integer; +begin + if f_ChList <> nil then + begin + for I := 0 to Pred(ChildrenCount) do + begin + if Children[I].SliceType = stUnion then + Td2dUnionSlice(Children[I]).Clear; + end; + f_ChList.Clear; + end; +end; + +procedure Td2dUnionSlice.DeleteFrom(aIndex : Integer); +var + I: Integer; +begin + for I := Pred(ChildrenCount) downto aIndex do + ChList.Delete(I); + RecalcHeight; +end; + +procedure Td2dUnionSlice.IterateLeafSlices(anAction: Td2dSliceActionProc; aFrom: Integer = 0); + + procedure ScanForLinkSlices(const aSlice: Td2dCustomSlice); + var + J: Integer; + l_UC: Td2dUnionSlice; + begin + if aSlice.SliceType = stUnion then + begin + l_UC := Td2dUnionSlice(aSlice); + l_UC.IterateLeafSlices(anAction); + end + else + anAction(aSlice); + end; + +var + I: Integer; +begin + for I := aFrom to ChildrenCount-1 do + ScanForLinkSlices(Children[I]); +end; + +function Td2dUnionSlice.pm_GetSliceType: Td2dTextSliceType; +begin + Result := stUnion; +end; + +procedure Td2dUnionSlice.pm_SetWidth(const Value: Single); +begin + f_Width := Value; +end; + +constructor Td2dTextSlice.Create(const aText: string; const aFont: Id2dFont; aColor: Td2dColor); +begin + inherited Create; + f_Text := aText; + f_Font := aFont; + f_Color := aColor; +end; + +procedure Td2dTextSlice.CalcSize; +begin + f_Font.CalcSize(f_Text, f_Size); +end; + +procedure Td2dTextSlice.DoDraw(X, Y: Single); +begin + f_Font.Color := f_Color; + f_Font.Render(X, Y, f_Text); + //D2DRenderRect(D2DRect(X, Y, X+Width, Y+Height), $FFFF0000); +end; + +function Td2dTextSlice.pm_GetHeight: Single; +begin + if f_Size.Y = 0 then + CalcSize; + Result := f_Size.Y; +end; + +function Td2dTextSlice.pm_GetSliceType: Td2dTextSliceType; +begin + Result := stText; +end; + +function Td2dTextSlice.pm_GetWidth: Single; +begin + if f_Size.X = 0 then + CalcSize; + Result := f_Size.X; +end; + +constructor Td2dFormatter.Create(aTS: Td2dTextSource; aDoc: Td2dUnionSlice); +begin + inherited Create; + f_TextSource := aTS; + f_Document := aDoc; + f_LastFormatted := 0; +end; + +procedure Td2dFormatter.Format; +begin + FormatFrom(f_LastFormatted); +end; + +procedure Td2dFormatter.FormatFrom(aParaIndex: Integer); +var + I : Integer; + l_Para : Td2dUnionSlice; + l_CurTop : Single; +begin + // first, delete paragraphs starting from aParaIndex + f_Document.DeleteFrom(aParaIndex); + if f_Document.ChildrenCount = 0 then + l_CurTop := 0.0 + else + with f_Document.Children[Pred(f_Document.ChildrenCount)] do + l_CurTop := Top + Height + f_FormatParams.rParaSpacing; + + for I := aParaIndex to Pred(f_TextSource.Count) do + begin + l_Para := FormatParagraph(I); + l_Para.Top := l_CurTop; + l_CurTop := l_CurTop + l_Para.Height + f_FormatParams.rParaSpacing; + f_Document.AddChild(l_Para); + end; + f_Document.RecalcHeight; +end; + +function Td2dFormatter.FormatParagraph(aParaIdx: Integer): Td2dUnionSlice; +var + l_Para : Td2dTextPara; + l_ParaSlice : Td2dUnionSlice; + l_Line : Td2dUnionSlice; + l_Start : TChunkCursor; + l_LastBreak : TChunkCursor; + l_LastNewBreak : TChunkCursor; + l_NewBreak : TChunkCursor; + l_IsEnd : Boolean; + l_CurPos: Td2dPoint; + l_Slice : Td2dCustomSlice; + I, J : Integer; + l_LWidth: Single; + l_LastLinkChunk : Td2dLinkChunk; + l_LastLinkSlice : Td2dLinkSlice; + + function CalcTextWidth(aChunk: Integer; aFrom, aTo: Integer): Single; + var + l_Str: string; + l_Size: Td2dPoint; + begin + Assert(l_Para.Chunks[aChunk].ChunkType in [ctText, ctLink]); + l_Str := Copy(Td2dStringChunk(l_Para.Chunks[aChunk]).Text, aFrom, aTo - aFrom + 1); + Td2dStringChunk(l_Para.Chunks[aChunk]).Font.CalcSize(l_Str, l_Size); + Result := l_Size.X; + end; + + function CalcWidth(aFrom, aTo: TChunkCursor): Single; + var + l_From: TChunkCursor; + begin + Assert((aFrom.rChunk <= aTo.rChunk)); + Result := 0; + l_From := aFrom; + while l_From.rChunk < aTo.rChunk do + begin + case l_Para.Chunks[l_From.rChunk].ChunkType of + ctText, ctLink : Result := Result + CalcTextWidth(l_From.rChunk, l_From.rPos, MaxInt); + ctPicture : Result := Result + Td2dPictureChunk(l_Para.Chunks[l_From.rChunk]).Picture.Width; + end; + Inc(l_From.rChunk); + l_From.rPos := 1; + end; + case l_Para.Chunks[l_From.rChunk].ChunkType of + ctText, ctLink : Result := Result + CalcTextWidth(l_From.rChunk, l_From.rPos, aTo.rPos); + ctPicture : Result := Result + Td2dPictureChunk(l_Para.Chunks[l_From.rChunk]).Picture.Width; + end; + end; + + function IncChunkCursor(var theCur: TChunkCursor): Boolean; + var + l_Ch: Td2dCustomTextChunk; + l_CC: TChunkCursor; + begin + l_CC := theCur; + l_Ch := l_Para.Chunks[l_CC.rChunk]; + case l_Ch.ChunkType of + ctText, ctLink: + begin + Inc(l_CC.rPos); + if l_CC.rPos > Length(Td2dStringChunk(l_Ch).f_Text) then + begin + Inc(l_CC.rChunk); + l_CC.rPos := 1; + end; + end; + ctPicture: + begin + Inc(l_CC.rChunk); + l_CC.rPos := 1; + end; + end; + Result := l_CC.rChunk < l_Para.Count; + if Result then + theCur := l_CC; + end; + + function ScanToNextBreak(var theCur: TChunkCursor): Boolean; + var + l_Text: string; + l_Changed: Boolean; + begin + Result := False; + // if current chunk is picture then break would be at the beginning of a next chunk + if l_Para.Chunks[theCur.rChunk].ChunkType = ctPicture then + begin + if Pred(l_Para.Count) = theCur.rChunk then + Result := True + else + begin + Inc(theCur.rChunk); + theCur.rPos := 1; + end; + Exit; + end; + l_Changed := False; + while True do + begin + l_Text := Td2dStringChunk(l_Para.Chunks[theCur.rChunk]).Text; + while theCur.rPos < Length(l_Text) do + begin + l_Changed := True; + Inc(theCur.rPos); + if l_Text[theCur.rPos] in [' ', '-'] then + Exit; + end; + if Pred(l_Para.Count) = theCur.rChunk then + begin + Result := True; // it's + Exit; + end; + if l_Para.Chunks[theCur.rChunk+1].ChunkType = ctPicture then + begin + if not l_Changed then + begin + Inc(theCur.rChunk); + theCur.rPos := 1; + end; + Exit; + end; + Inc(theCur.rChunk); + theCur.rPos := 1; + end; + end; + + function CompareCursors(const aCur1, aCur2: TChunkCursor): Integer; + begin + if aCur1.rChunk < aCur2.rChunk then + Result := -1 + else + if aCur1.rChunk > aCur2.rChunk then + Result := 1 + else + if aCur1.rPos < aCur2.rPos then + Result := -1 + else + if aCur1.rPos > aCur2.rPos then + Result := 1 + else + Result := 0; + end; + + function FlushChunkToLine(const aLine: Td2dUnionSlice; const aCur: TChunkCursor; const aTo: Integer = MaxInt): Td2dCustomSlice; + var + l_Slice: Td2dCustomSlice; + l_TC: Td2dStringChunk; + l_LC: Td2dLinkChunk; + l_LS: Td2dLinkSlice; + begin + l_Slice := nil; + case l_Para.Chunks[aCur.rChunk].ChunkType of + ctText: + begin + l_TC := Td2dStringChunk(l_Para.Chunks[aCur.rChunk]); + l_Slice := Td2dTextSlice.Create(Copy(l_TC.Text, aCur.rPos, aTo-aCur.rPos+1), l_TC.Font, l_TC.Color); + aLine.AddChild(l_Slice); + end; + ctLink: + begin + l_LC := Td2dLinkChunk(l_Para.Chunks[aCur.rChunk]); + l_LS := Td2dLinkSlice.Create(Copy(l_LC.Text, aCur.rPos, aTo-aCur.rPos+1), + l_LC.Target, l_LC.Font, l_LC.Color, l_LC.f_LinkColor, l_LC.f_HighlightColor); + l_LS.IsActive := l_LC.Active; + aLine.AddChild(l_LS); + if (l_LC = l_LastLinkChunk) and (l_LastLinkSlice <> nil) then + begin + l_LastLinkSlice.Next := l_LS; + l_LS.Prev := l_LastLinkSlice; + end; + l_LastLinkChunk := l_LC; + l_LastLinkSlice := l_LS; + end; + ctPicture : + begin + l_Slice := Td2dPictureSlice.Create(Td2dPictureChunk(l_Para.Chunks[aCur.rChunk]).Picture); + aLine.AddChild(l_Slice); + end; + end; + Result := l_Slice; + end; + + procedure FlushToLine(const aStart, aFinish: TChunkCursor); + var + l_Cur: TChunkCursor; + l_Line: Td2dUnionSlice; + begin + l_Cur := aStart; + l_Line := Td2dUnionSlice.Create(f_Document.Width); + l_ParaSlice.AddChild(l_Line); + while CompareCursors(l_Cur, aFinish) <= 0 do + begin + if l_Cur.rChunk < aFinish.rChunk then + begin + FlushChunkToLine(l_Line, l_Cur); + Inc(l_Cur.rChunk); + l_Cur.rPos := 1; + end + else + begin + FlushChunkToLine(l_Line, l_Cur, aFinish.rPos); + Exit; + end; + end; + end; + + function IsChunkAHugePicture(const aChunk: Td2dCustomTextChunk): Boolean; + var + l_PC: Td2dPictureChunk; + begin + Result := False; + if (aChunk.ChunkType = ctPicture) then + begin + l_PC := Td2dPictureChunk(aChunk); + Result := l_PC.Picture.Width > f_Document.Width; + end; + end; + +const + cZeroPos: TChunkCursor = (rChunk:0;rPos:0); + +begin + l_LastLinkChunk := nil; + l_Para := f_TextSource.Paragraphs[aParaIdx]; + l_ParaSlice := Td2dUnionSlice.Create(f_Document.Width); + l_Start.rChunk := 0; + l_Start.rPos := 1; + l_NewBreak := l_Start; + l_LastBreak := cZeroPos; + l_IsEnd := False; + while not l_IsEnd do + begin + l_IsEnd := ScanToNextBreak(l_NewBreak); + if (CalcWidth(l_Start, l_NewBreak) > f_Document.Width) and (CompareCursors(l_Start, l_NewBreak) <> 0) then + begin + // if width of newly found piece is wider than document width and + // it is not the last char in paragraph then... + if not IsChunkAHugePicture(l_Para.Chunks[l_Start.rChunk]) then + l_IsEnd := False; + if CompareCursors(l_Start, l_LastBreak) > 0 then // if first break gives too much width... + begin + l_NewBreak := l_Start; // let's break it + l_LastNewBreak := l_NewBreak; + IncChunkCursor(l_NewBreak); + while (CalcWidth(l_Start, l_NewBreak) < f_Document.Width) do + begin + l_LastNewBreak := l_NewBreak; + IncChunkCursor(l_NewBreak); + end; + l_NewBreak := l_LastNewBreak; + l_LastBreak := l_NewBreak; + end; + FlushToLine(l_Start, l_LastBreak); + l_Start := l_LastBreak; + IncChunkCursor(l_Start); + l_NewBreak := l_Start; + end + else + begin + if l_IsEnd then + FlushToLine(l_Start, l_NewBreak); + l_LastBreak := l_NewBreak; + end; + end; + + // arrange the data inside lines and lines themselves + l_CurPos.Y := 0; + for I := 0 to Pred(l_ParaSlice.ChildrenCount) do + begin + l_Line := Td2dUnionSlice(l_ParaSlice.Children[I]); + l_Line.RecalcHeight; + l_Line.Top := l_CurPos.Y; + l_LWidth := 0; + if l_Para.ParaType <> ptLeftAligned then + begin + // Calc the width of the line + for J := 0 to Pred(l_Line.ChildrenCount) do + begin + l_Slice := l_Line.Children[J]; + l_LWidth := l_LWidth + l_Slice.Width; + end; + case l_Para.ParaType of + ptRightAligned: l_CurPos.X := l_Line.Width - l_LWidth; + ptCentered : l_CurPos.X := Int((l_Line.Width - l_LWidth)/2 + 0.5); // pixel rounding + end; + end + else + l_CurPos.X := 0; + + // place slices + for J := 0 to Pred(l_Line.ChildrenCount) do + begin + l_Slice := l_Line.Children[J]; + l_Slice.Top := l_Line.Height - l_Slice.Height; + l_Slice.Left := l_CurPos.X; + l_CurPos.X := l_CurPos.X + l_Slice.Width; + end; + l_CurPos.Y := l_CurPos.Y + l_Line.Height + f_FormatParams.rLineSpacing; + end; + l_ParaSlice.RecalcHeight; + f_LastFormatted := aParaIdx; + Result := l_ParaSlice; +end; + +procedure Td2dFormatter.pm_SetLastFormatted(const Value: Integer); +begin + f_LastFormatted := Value; + if f_LastFormatted < 0 then + f_LastFormatted := 0; +end; + +procedure Td2dFormatter.ReformatAll; +begin + FormatFrom(0); +end; + +procedure Td2dFormatter.UpdateFormat; +begin + FormatFrom(f_LastFormatted); +end; + +procedure Td2dCustomSlice.Draw(X, Y: Single); +begin + DoDraw(X+Left, Y+Top); +end; + +function Td2dCustomSlice.pm_GetAbsLeft: Single; +var + l_Par: Td2dCustomSlice; +begin + Result := Left; + l_Par := f_Parent; + while l_Par <> nil do + begin + Result := Result + l_Par.Left; + l_Par := l_Par.Parent; + end; +end; + +function Td2dCustomSlice.pm_GetAbsTop: Single; +var + l_Par: Td2dCustomSlice; +begin + Result := Top; + l_Par := f_Parent; + while l_Par <> nil do + begin + Result := Result + l_Par.Top; + l_Par := l_Par.Parent; + end; +end; + +function Td2dCustomSlice.pm_GetSliceType: Td2dTextSliceType; +begin + Result := stUnknown; +end; + +constructor Td2dPictureChunk.Create(const aPicture: Id2dPicture; const anID: string); +begin + inherited Create; + f_Picture := aPicture; +end; + +procedure Td2dPictureChunk.Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); +var + l_ID: string; +begin + l_ID := aFiler.ReadString; + f_Picture := aPP.GetByID(l_ID); +end; + +function Td2dPictureChunk.pm_GetChunkType: Td2dTextChunkType; +begin + Result := ctPicture; +end; + +procedure Td2dPictureChunk.Save(aFiler: Td2dFiler); +begin + inherited; + aFiler.WriteString(f_Picture.ID); +end; + +constructor Td2dPictureSlice.Create(const aPicture: Id2dPicture); +begin + inherited Create; + f_Picture := aPicture; +end; + +procedure Td2dPictureSlice.DoDraw(X, Y: Single); +begin + f_Picture.Render(X, Y); +end; + +function Td2dPictureSlice.pm_GetHeight: Single; +begin + Result := f_Picture.Height; +end; + +function Td2dPictureSlice.pm_GetSliceType: Td2dTextSliceType; +begin + Result := stPicture; +end; + +function Td2dPictureSlice.pm_GetWidth: Single; +begin + Result := f_Picture.Width; +end; + +constructor Td2dLinkSlice.Create(const aText, aTarget: string; const aFont: Id2dFont; aTextColor, aLinkColor, aHighlightColor: Td2dColor); +begin + inherited Create(aText, aFont, aTextColor); + f_Target := aTarget; + f_LinkColor := aLinkColor; + f_HighlightColor := aHighlightColor; + f_IsActive := True; + f_Allowed := True; +end; + +procedure Td2dLinkSlice.DoDraw(X, Y: Single); +begin + if f_Allowed and f_IsActive then + begin + if f_IsHighlighted then + f_Font.Color := f_HighlightColor + else + f_Font.Color := f_LinkColor; + end + else + f_Font.Color := f_Color; + f_Font.Render(X, Y, f_Text); +end; + +function Td2dLinkSlice.GetLinkText: string; +var + l_Sibling: Td2dLinkSlice; +begin + l_Sibling := Self; + while l_Sibling.Prev <> nil do + l_Sibling := l_Sibling.Prev; + Result := ''; + repeat + Result := Result + l_Sibling.f_Text; + l_Sibling := l_Sibling.Next; + until l_Sibling = nil; +end; + +function Td2dLinkSlice.pm_GetSliceType: Td2dTextSliceType; +begin + Result := stLink; +end; + +procedure Td2dLinkSlice.SpreadHighlight; +var + l_Sibling: Td2dLinkSlice; +begin + l_Sibling := f_Prev; + while l_Sibling <> nil do + begin + l_Sibling.IsHighlighted := IsHighlighted; + l_Sibling := l_Sibling.Prev; + end; + l_Sibling := f_Next; + while l_Sibling <> nil do + begin + l_Sibling.IsHighlighted := IsHighlighted; + l_Sibling := l_Sibling.Next; + end; +end; + +constructor Td2dLinkChunk.Create(const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, aHighlightColor: Td2dColor); +begin + inherited Create(aText, aFont, aColor); + f_Target := aTarget; + f_LinkColor := aLinkColor; + f_HighlightColor := aHighlightColor; + f_Active := True; +end; + +procedure Td2dLinkChunk.Load(aFiler: Td2dFiler; const aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); +begin + inherited; + f_Active := aFiler.ReadBoolean; + f_HighlightColor := aFiler.ReadColor; + f_LinkColor := aFiler.ReadColor; + f_Target := aFiler.ReadString; +end; + +function Td2dLinkChunk.pm_GetChunkType: Td2dTextChunkType; +begin + Result := ctLink +end; + +procedure Td2dLinkChunk.Save(aFiler: Td2dFiler); +begin + inherited; + aFiler.WriteBoolean(f_Active); + aFiler.WriteColor(f_HighlightColor); + aFiler.WriteColor(f_LinkColor); + aFiler.WriteString(f_Target); +end; + +end. diff --git a/d2dFrames.pas b/d2dFrames.pas new file mode 100644 index 0000000..6172c03 --- /dev/null +++ b/d2dFrames.pas @@ -0,0 +1,223 @@ +unit d2dFrames; + +interface + +uses + d2dTypes, + d2dInterfaces; + +type + Td2dHorizontalFrameParts = (hfpLeft, hfpMiddle, hfpRight); + Td2dVerticalFrameParts = (vfpTop, vfpMiddle, vfpBottom); + + Td2dHorizFrame = class + private + f_Height : Integer; + f_Quad : array [Td2dHorizontalFrameParts] of Td2dQuad; + f_Width : array [Td2dHorizontalFrameParts] of Integer; + f_MinWidth : Integer; + function pm_GetLeftWidth: Integer; + function pm_GetRightWidth: Integer; + public + constructor Create(const aTex: Id2dTexture; aLeft, aTop, aRight, aBottom, aLeftWidth, aMidWidth: Integer); + procedure CorrectWidth(var theWidth: Integer; aToGreater: Boolean = True); + procedure Render(aX, aY: Single; aWidth: Integer); + function GetMidWidth(const aWidth: Integer): Integer; + property Height: Integer read f_Height; + property LeftWidth: Integer read pm_GetLeftWidth; + property MinWidth: Integer read f_MinWidth; + property RightWidth: Integer read pm_GetRightWidth; + end; + + + + Td2dFrame = class + private + f_Quad : array [Td2dHorizontalFrameParts, Td2dVerticalFrameParts] of Td2dQuad; + f_Width : array [Td2dHorizontalFrameParts] of Integer; + f_Height : array [Td2dVerticalFrameParts] of Integer; + f_MinWidth : Integer; + f_MinHeight: Integer; + public + constructor Create(aTex: Id2dTexture; aLeft, aTop, aRight, aBottom, + aLeftWidth, aMidWidth, aTopHeight, aMidHeight: Integer); + end; + +implementation +uses + Direct3D8, + d2dCore; + +constructor Td2dHorizFrame.Create(const aTex: Id2dTexture; aLeft, aTop, aRight, aBottom, aLeftWidth, aMidWidth: Integer); +var + l_TexWidth: Single; + l_TexHeight: Single; + l_TY, l_BY: Single; + l_LCX, l_LMX, l_RMX, l_RCX: Single; + l_DXTex : IDirect3DTexture8; +begin + inherited Create; + f_Width[hfpLeft] := aLeftWidth; + f_Width[hfpMiddle] := aMidWidth; + f_MinWidth := aRight - aLeft; + f_Width[hfpRight] := f_MinWidth - f_Width[hfpMiddle] - f_Width[hfpLeft]; + f_Height := aBottom - aTop; + if aTex <> nil then + begin + l_TexWidth := gD2DE.Texture_GetWidth(aTex); + l_TexHeight := gD2DE.Texture_GetHeight(aTex); + l_DXTex := aTex.DirectXTexture; + end + else + begin + l_TexWidth := 1.0; + l_TexHeight := 1.0; + l_DXTex := nil; + end; + // y-coords + l_TY := aTop / l_TexHeight; // top + l_BY := aBottom / l_TexHeight; // bottom + // x-coords + l_LCX := aLeft / l_TexWidth; // left cap x + l_RCX := aRight / l_TexWidth; // right cap x + l_LMX := (aLeft + aLeftWidth) / l_TexWidth; // left middle x + l_RMX := (aLeft + aLeftWidth + aMidWidth) / l_TexWidth; // right middle x + + // left cap quad + with f_Quad[hfpLeft] do + begin + V[0].TX := l_LCX; V[0].TY := l_TY; V[0].Z := 0.5; V[0].Col := $FFFFFFFF; + V[1].TX := l_LMX; V[1].TY := l_TY; V[1].Z := 0.5; V[1].Col := $FFFFFFFF; + V[2].TX := l_LMX; V[2].TY := l_BY; V[2].Z := 0.5; V[2].Col := $FFFFFFFF; + V[3].TX := l_LCX; V[3].TY := l_BY; V[3].Z := 0.5; V[3].Col := $FFFFFFFF; + Blend := BLEND_DEFAULT; + Tex := l_DXTex; + end; + + // right cap quad + with f_Quad[hfpRight] do + begin + V[0].TX := l_RMX; V[0].TY := l_TY; V[0].Z := 0.5; V[0].Col := $FFFFFFFF; + V[1].TX := l_RCX; V[1].TY := l_TY; V[1].Z := 0.5; V[1].Col := $FFFFFFFF; + V[2].TX := l_RCX; V[2].TY := l_BY; V[2].Z := 0.5; V[2].Col := $FFFFFFFF; + V[3].TX := l_RMX; V[3].TY := l_BY; V[3].Z := 0.5; V[3].Col := $FFFFFFFF; + Blend := BLEND_DEFAULT; + Tex := l_DXTex; + end; + + // middle quad + with f_Quad[hfpMiddle] do + begin + V[0].TX := l_LMX; V[0].TY := l_TY; V[0].Z := 0.5; V[0].Col := $FFFFFFFF; + V[1].TX := l_RMX; V[1].TY := l_TY; V[1].Z := 0.5; V[1].Col := $FFFFFFFF; + V[2].TX := l_RMX; V[2].TY := l_BY; V[2].Z := 0.5; V[2].Col := $FFFFFFFF; + V[3].TX := l_LMX; V[3].TY := l_BY; V[3].Z := 0.5; V[3].Col := $FFFFFFFF; + Blend := BLEND_DEFAULT; + Tex := l_DXTex; + end; +end; + +procedure Td2dHorizFrame.CorrectWidth(var theWidth: Integer; aToGreater: Boolean = True); +var + l_MW: Integer; + l_Frac: Integer; +begin + l_MW := theWidth - f_Width[hfpLeft] - f_Width[hfpRight]; + if l_MW < f_Width[hfpMiddle] then + begin + theWidth := f_MinWidth; + Exit; + end; + l_Frac := l_MW mod f_Width[hfpMiddle]; + if l_Frac > 0 then + begin + if aToGreater then + theWidth := theWidth - l_Frac + f_Width[hfpMiddle] + else + theWidth := theWidth - l_Frac; + end; +end; + +function Td2dHorizFrame.GetMidWidth(const aWidth: Integer): Integer; +begin + Result := aWidth - f_Width[hfpLeft] - f_Width[hfpRight]; +end; + +function Td2dHorizFrame.pm_GetLeftWidth: Integer; +begin + Result := f_Width[hfpLeft]; +end; + +function Td2dHorizFrame.pm_GetRightWidth: Integer; +begin + Result := f_Width[hfpRight]; +end; + +procedure Td2dHorizFrame.Render(aX, aY: Single; aWidth: Integer); +var + l_BottomY: Single; + l_LeftM, l_RightM: Single; + l_MiddlesCount: Integer; + I: Integer; +begin + CorrectWidth(aWidth); + + l_BottomY := aY + f_Height; + l_LeftM := aX + f_Width[hfpLeft]; + + // render left cap + with f_Quad[hfpLeft] do + begin + V[0].X := aX; V[0].Y := aY; + V[1].X := l_LeftM; V[1].Y := aY; + V[2].X := l_LeftM; V[2].Y := l_BottomY; + V[3].X := aX; V[3].Y := l_BottomY; + end; + gD2DE.Gfx_RenderQuad(f_Quad[hfpLeft]); + + // render middle part + l_MiddlesCount := (aWidth - f_Width[hfpLeft] - f_Width[hfpRight]) div f_Width[hfpMiddle]; + for I := 1 to l_MiddlesCount do + begin + l_RightM := l_LeftM + f_Width[hfpMiddle]; + with f_Quad[hfpMiddle] do + begin + V[0].X := l_LeftM; V[0].Y := aY; + V[1].X := l_RightM; V[1].Y := aY; + V[2].X := l_RightM; V[2].Y := l_BottomY; + V[3].X := l_LeftM; V[3].Y := l_BottomY; + end; + gD2DE.Gfx_RenderQuad(f_Quad[hfpMiddle]); + l_LeftM := l_RightM; + end; + + // render right cap + l_RightM := l_LeftM + f_Width[hfpRight]; + with f_Quad[hfpRight] do + begin + V[0].X := l_LeftM; V[0].Y := aY; + V[1].X := l_RightM; V[1].Y := aY; + V[2].X := l_RightM; V[2].Y := l_BottomY; + V[3].X := l_LeftM; V[3].Y := l_BottomY; + end; + gD2DE.Gfx_RenderQuad(f_Quad[hfpRight]); +end; + +constructor Td2dFrame.Create(aTex: Id2dTexture; + aLeft, aTop, aRight, aBottom, aLeftWidth, + aMidWidth, aTopHeight, aMidHeight: Integer); +begin + inherited Create; + f_Width[hfpLeft] := aLeftWidth; + f_Width[hfpMiddle] := aMidWidth; + f_MinWidth := (aRight - aLeft); + f_Width[hfpRight] := f_MinWidth - f_Width[hfpLeft] - f_Width[hfpMiddle]; + + f_Height[vfpTop] := aTopHeight; + f_Height[vfpMiddle] := aMidHeight; + f_MinHeight := (aBottom - aTop); + f_Height[vfpBottom] := f_MinHeight - f_Height[vfpTop] - f_Height[vfpMiddle]; +end; + + +end. \ No newline at end of file diff --git a/d2dGIF.pas b/d2dGIF.pas new file mode 100644 index 0000000..f354697 --- /dev/null +++ b/d2dGIF.pas @@ -0,0 +1,195 @@ +unit d2dGIF; + +interface +uses + Windows, + Classes, + Graphics, + d2dTypes, + d2dSprite, + GIFImage; + +type + Td2dGIFSprite = class(Td2dSprite) + private + f_GIF: TGIFImage; + f_Painter: TGIFPainter; + f_Bitmap: TBitmap; + f_BmpIsSaved: Boolean; + f_NeedToUpdateFrame: Boolean; + f_SavedBmp: TMemoryStream; + procedure DoOnPaint(aSender: TObject); + function pm_GetAniSpeed: Integer; + function pm_GetFrame: Integer; + procedure pm_SetAniSpeed(const Value: Integer); + procedure pm_SetFrame(const Value: Integer); + procedure RenderFrame(aFrameNo: Integer); + function SetupFrame: Boolean; + protected + procedure Cleanup; override; + public + constructor Create(aGIFFilename: string); + procedure Start; + procedure Stop; + procedure Update(aDeltaTime: Single); + property AniSpeed: Integer read pm_GetAniSpeed write pm_SetAniSpeed; + property Frame: Integer read pm_GetFrame write pm_SetFrame; + end; + +implementation +uses + SysUtils, + Direct3D8, + D3DX8, + d2dInterfaces, + d2dCore; + +constructor Td2dGIFSprite.Create(aGIFFilename: string); +var + l_Stream: TStream; + l_Rect: TRect; +begin + inherited Create(nil, 0, 0, 10, 10); + aGIFFilename := StringReplace(aGIFFilename, '/', '\', [rfReplaceAll]); + f_GIF := TGIFImage.Create; + l_Stream := gD2DE.Resource_CreateStream(aGIFFilename); + if l_Stream <> nil then + begin + try + f_GIF.LoadFromStream(l_Stream); + finally + FreeAndNil(l_Stream); + end; + f_Width := f_GIF.Width; + f_Height := f_GIF.Height; + f_Bitmap := TBitmap.Create; + f_Bitmap.Assign(f_GIF); + f_Bitmap.PixelFormat := pf32bit; + with f_Bitmap.Canvas.Brush do + begin + Color := $FF00FF; + Style := bsSolid; + end; + l_Rect := Rect(0,0,f_GIF.Width, f_GIF.Height); + f_Bitmap.Canvas.FillRect(l_Rect); + if f_GIF.Images.Count > 1 then + begin + f_GIF.OnPaint := DoOnPaint; + f_Painter := f_GIF.Paint(f_Bitmap.Canvas, l_Rect, GIFImageDefaultDrawOptions + [goLoopContinously]); + end; + RenderFrame(0); + end; +end; + +procedure Td2dGIFSprite.Cleanup; +begin + FreeAndNil(f_GIF); + FreeAndNil(f_Bitmap); + FreeAndNil(f_SavedBmp); + inherited; +end; + +procedure Td2dGIFSprite.DoOnPaint(aSender: TObject); +begin + f_BmpIsSaved := False; + f_NeedToUpdateFrame := True; +end; + +function Td2dGIFSprite.pm_GetAniSpeed: Integer; +begin + if f_Painter <> nil then + Result := f_Painter.AnimationSpeed + else + Result := 0; +end; + +function Td2dGIFSprite.pm_GetFrame: Integer; +begin + if f_Painter <> nil then + Result := f_Painter.ActiveImage + else + Result := 0; +end; + +procedure Td2dGIFSprite.pm_SetAniSpeed(const Value: Integer); +var + l_Frame: Integer; +begin + if f_Painter <> nil then + begin + //l_Frame := Frame; + f_Painter.AnimationSpeed := Value; + //f_FrameToSet := l_Frame; + end; +end; + +procedure Td2dGIFSprite.pm_SetFrame(const Value: Integer); +var + l_FrameNo: Integer; +begin + if f_GIF.Images.Count > 0 then + begin + l_FrameNo := Abs(Value) mod f_GIF.Images.Count; + if f_Painter <> nil then + f_Painter.ActiveImage := l_FrameNo; + RenderFrame(l_FrameNo); + end; +end; + +procedure Td2dGIFSprite.RenderFrame(aFrameNo: Integer); +var + l_Rect: TRect; +begin + with f_Bitmap.Canvas.Brush do + begin + Color := $FF00FF; + Style := bsSolid; + end; + l_Rect := Rect(0,0,f_GIF.Width, f_GIF.Height); + f_Bitmap.Canvas.FillRect(l_Rect); + f_GIF.Images[aFrameNo].Draw(f_Bitmap.Canvas, l_Rect, True, False); + f_BmpIsSaved := False; + f_NeedToUpdateFrame := True; +end; + +function Td2dGIFSprite.SetupFrame: Boolean; +var + l_Tex : Id2dTexture; + l_Info : D3DXIMAGE_INFO; +begin + if f_SavedBmp = nil then + f_SavedBmp := TMemoryStream.Create; + if not f_BmpIsSaved then + begin + f_SavedBmp.Clear; + f_Bitmap.SaveToStream(f_SavedBmp); + f_BmpIsSaved := True; + end; + l_Tex := gD2DE.Texture_CreatePrim(f_SavedBmp.Memory, f_SavedBmp.Size, False, $FFFF00FF); + Result := l_Tex <> nil; + if Result then + Texture := l_Tex; +end; + +procedure Td2dGIFSprite.Start; +begin + if f_Painter <> nil then + f_Painter.Start; +end; + +procedure Td2dGIFSprite.Stop; +begin + if f_Painter <> nil then + f_Painter.Suspend; +end; + +procedure Td2dGIFSprite.Update(aDeltaTime: Single); +begin + if f_NeedToUpdateFrame then + begin + if SetupFrame then + f_NeedToUpdateFrame := False; + end; +end; + +end. \ No newline at end of file diff --git a/d2dGUI.pas b/d2dGUI.pas new file mode 100644 index 0000000..787efdc --- /dev/null +++ b/d2dGUI.pas @@ -0,0 +1,500 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- + +unit d2dGUI; + +interface +uses + Contnrs, + + d2dTypes + ; + +type + Td2dControl = class; + + Td2dGUI = class + private + f_BringFocusedToFront: Boolean; + f_ROList: TObjectList; // render order + f_Controls: TObjectList; + f_FocusedControl: Td2dControl; + procedure FindNextFocused(aCanReturnFocus: Boolean); + procedure FindPrevFocused(aCanReturnFocus: Boolean); + function pm_GetControls(Index: Integer): Td2dControl; + function pm_GetCount: Integer; + function pm_GetROControls(Index: Integer): Td2dControl; + function pm_GetTopmostVisible: Td2dControl; + procedure pm_SetFocusedControl(const Value: Td2dControl); + protected + procedure DeleteControl(aControl: Td2dControl); + property ROControls[Index: Integer]: Td2dControl read pm_GetROControls; + public + constructor Create; + destructor Destroy; override; + procedure AddControl(aControl: Td2dControl); + procedure BringToFront(const aControl: Td2dControl); + procedure SendToBack(const aControl: Td2dControl); + procedure CheckFocused; + function ControlAtMouse: Td2dControl; + procedure FocusNext; + procedure FocusPrev; + procedure FrameFunc(aDelta: Single); virtual; + procedure ProcessEvent(var theEvent: Td2dInputEvent); virtual; + procedure Render; virtual; + property BringFocusedToFront: Boolean read f_BringFocusedToFront write f_BringFocusedToFront; + property Controls[Index: Integer]: Td2dControl read pm_GetControls; + property Count: Integer read pm_GetCount; + property FocusedControl: Td2dControl read f_FocusedControl write pm_SetFocusedControl; + property TopmostVisible: Td2dControl read pm_GetTopmostVisible; + end; + + Td2dControl = class(TInterfacedObject) + private + f_CanBeFocused: Boolean; + f_GUI: Td2dGUI; + f_Tag: Integer; + function pm_GetCanBeFocused: Boolean; + function pm_GetFocused: Boolean; + procedure pm_SetFocused(const Value: Boolean); + procedure pm_SetX(const Value: Single); + procedure pm_SetY(const Value: Single); + protected + f_Enabled: Boolean; + f_Visible: Boolean; + f_X: Single; + f_Y: Single; + function pm_GetHeight: Single; virtual; + function pm_GetWidth: Single; virtual; + procedure pm_SetEnabled(const Value: Boolean); virtual; + procedure pm_SetHeight(const Value: Single); virtual; + procedure pm_SetVisible(const Value: Boolean); virtual; + procedure pm_SetWidth(const Value: Single); virtual; + public + constructor Create(aX, aY: Single); + destructor Destroy; override; + procedure FrameFunc(aDelta: Single); virtual; + function IsMouseInControl: Boolean; + function IsPointInControl(aX, aY: Single): Boolean; virtual; + procedure ProcessEvent(var theEvent: Td2dInputEvent); virtual; + procedure Render; virtual; + procedure Update; virtual; + property CanBeFocused: Boolean read pm_GetCanBeFocused write f_CanBeFocused; + property Enabled: Boolean read f_Enabled write pm_SetEnabled; + property Focused: Boolean read pm_GetFocused write pm_SetFocused; + property Height: Single read pm_GetHeight write pm_SetHeight; + property GUI: Td2dGUI read f_GUI; + property Tag: Integer read f_Tag write f_Tag; + property Visible: Boolean read f_Visible write pm_SetVisible; + property Width: Single read pm_GetWidth write pm_SetWidth; + property X: Single read f_X write pm_SetX; + property Y: Single read f_Y write pm_SetY; + end; + +implementation +uses + SysUtils, + Classes, + + d2dCore, + d2dUtils; + + +constructor Td2dControl.Create(aX, aY: Single); +begin + inherited Create; + f_X := aX; + f_Y := aY; + f_Enabled := True; + f_Visible := True; +end; + +destructor Td2dControl.Destroy; +begin + if f_GUI <> nil then + f_GUI.DeleteControl(Self); + inherited; +end; + +procedure Td2dControl.FrameFunc(aDelta: Single); +begin + // does nothing in base class +end; + +function Td2dControl.IsMouseInControl: Boolean; +begin + if f_GUI = nil then + Result := IsPointInControl(gD2DE.MouseX, gD2DE.MouseY) + else + Result := f_GUI.ControlAtMouse = Self; +end; + +function Td2dControl.pm_GetFocused: Boolean; +begin + Result := (f_GUI <> nil) and (f_GUI.FocusedControl = Self); +end; + +function Td2dControl.pm_GetHeight: Single; +begin + Result := 0; +end; + +function Td2dControl.pm_GetWidth: Single; +begin + Result := 0; +end; + +procedure Td2dControl.pm_SetEnabled(const Value: Boolean); +begin + f_Enabled := Value; + if CanBeFocused and (GUI <> nil) then + GUI.CheckFocused; +end; + +procedure Td2dControl.pm_SetFocused(const Value: Boolean); +begin + if Value then + begin + if (GUI <> nil) and Visible and Enabled and CanBeFocused then + GUI.FocusedControl := Self; + end + else + if (GUI <> nil) then + begin + GUI.FocusedControl := nil; + GUI.CheckFocused; + end; +end; + +procedure Td2dControl.pm_SetHeight(const Value: Single); +begin + // empty in base class +end; + +procedure Td2dControl.pm_SetVisible(const Value: Boolean); +begin + f_Visible := Value; + if CanBeFocused and (GUI <> nil) then + GUI.CheckFocused; +end; + +procedure Td2dControl.pm_SetWidth(const Value: Single); +begin + // empty in base class +end; + +function Td2dControl.IsPointInControl(aX, aY: Single): Boolean; +begin + Result := D2DIsPointInRect(aX, aY, f_X, f_Y, Width, Height); +end; + +function Td2dControl.pm_GetCanBeFocused: Boolean; +begin + Result := f_CanBeFocused and (f_GUI <> nil); +end; + +procedure Td2dControl.pm_SetX(const Value: Single); +begin + f_X := Value; + Update; +end; + +procedure Td2dControl.pm_SetY(const Value: Single); +begin + f_Y := Value; + Update; +end; + +procedure Td2dControl.ProcessEvent(var theEvent: Td2dInputEvent); +begin + // empty in base class +end; + +procedure Td2dControl.Render; +begin + // empty in base class +end; + +procedure Td2dControl.Update; +begin + // empty in base class +end; + +constructor Td2dGUI.Create; +begin + inherited; + f_Controls := TObjectList.Create; + f_ROList := TObjectList.Create(False); + f_BringFocusedToFront := True; +end; + +destructor Td2dGUI.Destroy; +begin + f_Controls.Free; + f_ROList.Free; + inherited; +end; + +procedure Td2dGUI.AddControl(aControl: Td2dControl); +begin + f_Controls.Add(aControl); + f_ROList.Insert(0, aControl); + aControl.f_GUI := Self; + CheckFocused; +end; + +procedure Td2dGUI.BringToFront(const aControl: Td2dControl); +var + l_Idx: Integer; +begin + l_Idx := f_ROList.IndexOf(FocusedControl); + if l_Idx >= 0 then + f_ROList.Move(l_Idx, 0); +end; + +procedure Td2dGUI.SendToBack(const aControl: Td2dControl); +var + l_Idx: Integer; +begin + l_Idx := f_ROList.IndexOf(FocusedControl); + if l_Idx >= 0 then + f_ROList.Move(l_Idx, f_ROList.Count-1); +end; + +procedure Td2dGUI.CheckFocused; +begin + if (FocusedControl = nil) or (not FocusedControl.Visible) or (not FocusedControl.Enabled) then + FindNextFocused(False); +end; + +function Td2dGUI.ControlAtMouse: Td2dControl; +var + I: Integer; +begin + Result := nil; + for I := 0 to Pred(Count) do + if ROControls[I].Visible and ROControls[I].IsPointInControl(gD2DE.MouseX, gD2DE.MouseY) then + begin + Result := ROControls[I]; + Break; + end; +end; + +procedure Td2dGUI.DeleteControl(aControl: Td2dControl); +begin + if FocusedControl = aControl then + FocusedControl := nil; + f_Controls.Extract(aControl); + f_ROList.Remove(aControl); + aControl.f_GUI := nil; + CheckFocused; +end; + +procedure Td2dGUI.FindNextFocused(aCanReturnFocus: Boolean); +var + l_CIdx: Integer; + l_OldFocused: Integer; +begin + if Count = 0 then + Exit; + if FocusedControl = nil then + l_CIdx := -1 + else + l_CIdx := f_Controls.IndexOf(FocusedControl); + l_OldFocused := l_CIdx; + Inc(l_CIdx); + while True do + begin + if l_CIdx = Count then + begin + if l_OldFocused = -1 then + Exit; + l_CIdx := 0; + end; + if l_CIdx = l_OldFocused then + begin + if not aCanReturnFocus then + FocusedControl := nil; + Break; + end; + with Controls[l_CIdx] do + if Visible and Enabled and CanBeFocused then + begin + FocusedControl := Controls[l_CIdx]; + Break; + end; + Inc(l_CIdx); + end; +end; + +procedure Td2dGUI.FindPrevFocused(aCanReturnFocus: Boolean); +var + l_CIdx: Integer; + l_OldFocused: Integer; +begin + if Count = 0 then + Exit; + if FocusedControl = nil then + l_CIdx := -1 + else + l_CIdx := f_Controls.IndexOf(FocusedControl); + l_OldFocused := l_CIdx; + Dec(l_CIdx); + while True do + begin + if l_CIdx = -1 then + begin + if l_OldFocused = -1 then + Exit; + l_CIdx := Count - 1; + end; + if l_CIdx = l_OldFocused then + begin + if not aCanReturnFocus then + FocusedControl := nil; + Break; + end; + with Controls[l_CIdx] do + if Visible and Enabled and CanBeFocused then + begin + FocusedControl := Controls[l_CIdx]; + Break; + end; + Dec(l_CIdx); + end; +end; + +procedure Td2dGUI.FocusNext; +begin + FindNextFocused(True); +end; + +procedure Td2dGUI.FocusPrev; +begin + FindPrevFocused(True); +end; + +procedure Td2dGUI.FrameFunc(aDelta: Single); +var + I: Integer; +begin + for I := 0 to Pred(Count) do + Controls[I].FrameFunc(aDelta); +end; + +function Td2dGUI.pm_GetControls(Index: Integer): Td2dControl; +begin + Result := f_Controls[Index] as Td2dControl; +end; + +function Td2dGUI.pm_GetCount: Integer; +begin + Result := f_Controls.Count; +end; + +function Td2dGUI.pm_GetROControls(Index: Integer): Td2dControl; +begin + Result := Td2dControl(f_ROList[Index]); +end; + +function Td2dGUI.pm_GetTopmostVisible: Td2dControl; +var + I: Integer; +begin + Result := nil; + for I := 0 to Pred(Count) do + if ROControls[I].Visible then + begin + Result := ROControls[I]; + Break; + end; +end; + +procedure Td2dGUI.pm_SetFocusedControl(const Value: Td2dControl); +var + l_OldFocused: Td2dControl; +begin + if Value <> nil then + if (f_Controls.IndexOf(Value) < 0) or + (not Value.Visible) or (not Value.Enabled) or (not Value.CanBeFocused) then + Exit; + + l_OldFocused := FocusedControl; + + f_FocusedControl := Value; + + if l_OldFocused <> nil then + l_OldFocused.Update; + + if FocusedControl <> nil then + begin + if f_BringFocusedToFront then + BringToFront(FocusedControl); + FocusedControl.Update; + end; +end; + +procedure Td2dGUI.ProcessEvent(var theEvent: Td2dInputEvent); +var + I: Integer; + l_Control: Td2dControl; +begin + if (theEvent.EventType = INPUT_KEYDOWN) and (theEvent.KeyCode = D2DK_TAB) then + begin + FocusNext; + Processed(theEvent); + Exit; + end; + + // if there is focused control then let it handle keyboard events + if IsKeyboardEvent(theEvent) then + begin + if (FocusedControl <> nil) then + FocusedControl.ProcessEvent(theEvent); + //Processed(theEvent); + Exit; + end; + + if (theEvent.EventType = INPUT_MBUTTONDOWN) and (theEvent.KeyCode = D2DK_LBUTTON) then + begin + l_Control := ControlAtMouse; + if (l_Control <> nil) and (l_Control <> FocusedControl) then + FocusedControl := l_Control; + end; + + if (FocusedControl <> nil) then + FocusedControl.ProcessEvent(theEvent); + + for I := 0 to Pred(Count) do + begin + if IsProcessed(theEvent) then + Break; + l_Control := Controls[I]; + if l_Control.Visible and l_Control.Enabled and (l_Control <> FocusedControl) then + l_Control.ProcessEvent(theEvent); + end; +end; + +procedure Td2dGUI.Render; +var + I: Integer; + l_Ctrl: Td2dControl; +begin + for I := Pred(Count) downto 0 do + begin + l_Ctrl := ROControls[I]; + if l_Ctrl.Visible then + l_Ctrl.Render; + end; +end; + +end. diff --git a/d2dGUIButtons.pas b/d2dGUIButtons.pas new file mode 100644 index 0000000..945b4ed --- /dev/null +++ b/d2dGUIButtons.pas @@ -0,0 +1,489 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dGUIButtons; + +interface + +uses + d2dTypes, + d2dInterfaces, + d2dGUITypes, + d2dGUI, + d2dSprite, + d2dFrames, + d2dFont; + +type + Td2dCustomButton = class(Td2dControl) + private + f_AutoFocus: Boolean; + f_Fixed: Boolean; + f_HasStates: Td2dButtonStates; + f_OnTrigger: Td2dTriggerEvent; + f_Pressed: Boolean; + f_Trigger: Boolean; + procedure DetectState(var theEvent: Td2dInputEvent); + procedure pm_SetFixed(aFixed: Boolean); + protected + f_OnClick: Td2dNotifyEvent; + f_State: Td2dButtonState; + procedure DoClick; + procedure pm_SetEnabled(const Value: Boolean); override; + procedure pm_SetState(const Value: Td2dButtonState); virtual; + public + constructor Create(aX, aY: Single); + procedure Click; + procedure ProcessEvent(var theEvent: Td2dInputEvent); override; + procedure Update; override; + property OnClick: Td2dNotifyEvent read f_OnClick write f_OnClick; + property Fixed: Boolean read f_Fixed write pm_SetFixed; + property OnTrigger: Td2dTriggerEvent read f_OnTrigger write f_OnTrigger; + property AutoFocus: Boolean read f_AutoFocus write f_AutoFocus default False; + property HasStates: Td2dButtonStates read f_HasStates write f_HasStates; + property State: Td2dButtonState read f_State write pm_SetState; + property Trigger: Boolean read f_Trigger write f_Trigger; + end; + + Td2dBitButton = class(Td2dCustomButton) + private + f_Sprite: Td2dMultiFrameSprite; + protected + function pm_GetHeight: Single; override; + function pm_GetWidth: Single; override; + procedure pm_SetState(const Value: Td2dButtonState); override; + public + constructor Create(aX, aY: Single; aTex: Id2dTexture; aTx, aTy, aWidth, aHeight: Integer); + destructor Destroy; override; + procedure Render; override; + end; + + Id2dFramedButtonView = interface + ['{C0BA7393-BAED-410C-B289-6539ED8A5758}'] + function pm_GetHeight: Single; + function pm_GetMinWidth: Integer; + function pm_GetStateColor(aState: Td2dButtonState): Td2dColor; + procedure pm_SetStateColor(aState: Td2dButtonState; const Value: Td2dColor); + function CalcWidth(aCaption: string): Integer; + function CorrectCaption(const aCaption: string; aWidth: Integer): string; + function CorrectCaptionEx(const aCaption: string; aWidth: Integer; const aObligatoryPart: string): string; + procedure Render(aX, aY: Single; aCaption: string; aState: Td2dButtonState; + aPrecalcWidth: Integer = 0; aTextAlign: Td2dTextAlignType = ptLeftAligned); + procedure CorrectWidth(var theWidth: Integer); + property Height: Single read pm_GetHeight; + property MinWidth: Integer read pm_GetMinWidth; + property StateColor[aState: Td2dButtonState]: Td2dColor read pm_GetStateColor write pm_SetStateColor; + end; + + Td2dFramedButtonView = class(TInterfacedObject, Id2dFramedButtonView) + private + f_StateColor: array[Td2dButtonState] of Td2dColor; + f_Font: Id2dFont; + f_StateFrames: array[Td2dButtonState] of Td2dHorizFrame; + function pm_GetHeight: Single; + function pm_GetMinWidth: Integer; + function pm_GetStateColor(aState: Td2dButtonState): Td2dColor; + procedure pm_SetStateColor(aState: Td2dButtonState; const Value: Td2dColor); + public + constructor Create(aTex: Id2dTexture; + aTx, aTy, aWidth, aHeight, aLeftCapW, aMidW: Integer; + aFont: Id2dFont); + destructor Destroy; override; + function CalcWidth(aCaption: string): Integer; + function CorrectCaption(const aCaption: string; aWidth: Integer): string; + function CorrectCaptionEx(const aCaption: string; aWidth: Integer; const aObligatoryPart: string): string; + procedure Render(aX, aY: Single; aCaption: string; aState: Td2dButtonState; + aPrecalcWidth: Integer = 0; aTextAlign: Td2dTextAlignType = ptLeftAligned); + procedure CorrectWidth(var theWidth: Integer); + property Height: Single read pm_GetHeight; + property MinWidth: Integer read pm_GetMinWidth; + property StateColor[aState: Td2dButtonState]: Td2dColor read pm_GetStateColor write pm_SetStateColor; + end; + + Td2dFramedTextButton = class(Td2dCustomButton) + private + f_AutoSize: Boolean; + f_Caption: string; + f_TextAlign: Td2dTextAlignType; + f_View: Id2dFramedButtonView; + f_Width: Integer; + procedure pm_SetAutoSize(const Value: Boolean); + procedure pm_SetCaption(const Value: string); + protected + function pm_GetHeight: Single; override; + function pm_GetWidth: Single; override; + procedure pm_SetWidth(const Value: Single); override; + procedure RecalcWidth; + public + constructor Create(aX, aY: Single; aView: Id2dFramedButtonView; aCaption: string); + procedure Render; override; + property AutoSize: Boolean read f_AutoSize write pm_SetAutoSize; + property Caption: string read f_Caption write pm_SetCaption; + property TextAlign: Td2dTextAlignType read f_TextAlign write f_TextAlign; + end; + + +implementation +uses + d2dCore, + d2dUtils; + +constructor Td2dCustomButton.Create(aX, aY: Single); +begin + inherited; + CanBeFocused := True; + f_HasStates := [bsNormal, bsDisabled, bsFocused, bsPressed]; +end; + +procedure Td2dCustomButton.Click; +begin + if Trigger then + begin + Fixed := not Fixed; + DoClick; + end + else + DoClick; + gD2DE.Input_TouchMousePos; + //Update; +end; + +procedure Td2dCustomButton.DetectState(var theEvent: Td2dInputEvent); +begin + if not Enabled then + begin + State := bsDisabled; + f_Pressed := False; + Exit; + end; + + if f_Fixed then + begin + State := bsPressed; + Exit; + end; + + if IsMouseInControl and not IsMouseMoveMasked(theEvent) then + begin + if f_Pressed then + State := bsPressed + else + begin + State := bsFocused; + MaskMouseMove(theEvent); + end; + end + else + if Focused then + State := bsFocused + else + State := bsNormal; +end; + +procedure Td2dCustomButton.DoClick; +begin + if Assigned(f_OnClick) then + f_OnClick(Self); +end; + +procedure Td2dCustomButton.Update; +begin + gD2DE.Input_TouchMousePos; +end; + +procedure Td2dCustomButton.pm_SetEnabled(const Value: Boolean); +begin + inherited; + if not f_Enabled then + begin + f_Pressed := False; + Fixed := False; + f_State := bsDisabled; + end; + Update; +end; + +procedure Td2dCustomButton.pm_SetFixed(aFixed: Boolean); +begin + if f_Trigger and (f_Fixed <> aFixed) then + begin + f_Fixed := aFixed; + if Assigned(f_OnTrigger) then + f_OnTrigger(f_Fixed); + end; +end; + +procedure Td2dCustomButton.pm_SetState(const Value: Td2dButtonState); +begin + if Value in f_HasStates then + f_State := Value; +end; + +procedure Td2dCustomButton.ProcessEvent(var theEvent: Td2dInputEvent); +var + l_InButton: Boolean; + l_OldState: Td2dButtonState; +begin + { + if not f_Enabled then + begin + State := bsDisabled; + Exit; + end; + } + if theEvent.EventType = INPUT_MOUSEMOVE then + begin + l_OldState := f_State; + DetectState(theEvent); + if f_AutoFocus and (l_OldState = bsNormal) and (f_State = bsFocused) then + Focused := True; + Exit; + end; + + if Enabled then + begin + l_InButton := IsMouseInControl; + + if l_InButton and (theEvent.EventType = INPUT_MBUTTONDOWN) and (theEvent.KeyCode = D2DK_LBUTTON) then + begin + f_Pressed := True; + State := bsPressed; + Processed(theEvent); + end; + + if (theEvent.EventType = INPUT_MBUTTONUP) and (theEvent.KeyCode = D2DK_LBUTTON) then + begin + if l_InButton and f_Pressed then + begin + Click; + Processed(theEvent); + end + else + State := bsNormal; + f_Pressed := False; + end; + + if Focused and (theEvent.EventType = INPUT_KEYDOWN) and + ((theEvent.KeyCode = D2DK_ENTER) or (theEvent.KeyCode = D2DK_SPACE)) then + begin + Click; + Processed(theEvent); + end; + end; +end; + +constructor Td2dBitButton.Create(aX, aY: Single; aTex: Id2dTexture; aTx, aTy, aWidth, aHeight: Integer); +begin + inherited Create(aX, aY); + f_Sprite := Td2dMultiFrameSprite.Create(aTex, 4, aTx, aTy, aWidth, aHeight); +end; + +destructor Td2dBitButton.Destroy; +begin + f_Sprite.Free; + inherited; +end; + +function Td2dBitButton.pm_GetHeight: Single; +begin + Result := f_Sprite.Height; +end; + +function Td2dBitButton.pm_GetWidth: Single; +begin + Result := f_Sprite.Width; +end; + +procedure Td2dBitButton.pm_SetState(const Value: Td2dButtonState); +begin + inherited; + case f_State of + bsNormal : f_Sprite.CurFrame := 0; + bsDisabled: f_Sprite.CurFrame := 1; + bsFocused : f_Sprite.CurFrame := 2; + bsPressed : f_Sprite.CurFrame := 3; + end; +end; + +procedure Td2dBitButton.Render; +begin + f_Sprite.Render(f_X, f_Y); +end; + +constructor Td2dFramedButtonView.Create(aTex: Id2dTexture; + aTx, aTy, aWidth, aHeight, aLeftCapW, aMidW: Integer; + aFont: Id2dFont); +var + I : Td2dButtonState; + l_Shift: Integer; +begin + inherited Create; + l_Shift := 0; + for I := Low(Td2dButtonState) to High(Td2dButtonState) do + begin + f_StateFrames[I] := Td2dHorizFrame.Create(aTex, aTx + l_Shift, aTy, aTx + l_Shift + aWidth - 1, aTy + aHeight - 1, + aLeftCapW, aMidW); + f_StateColor[I] := $FF000000; + l_Shift := l_Shift + aWidth; + end; + f_Font := aFont; +end; + +destructor Td2dFramedButtonView.Destroy; +var + I: Td2dButtonState; +begin + for I := Low(Td2dButtonState) to High(Td2dButtonState) do + f_StateFrames[I].Free; + inherited; +end; + +function Td2dFramedButtonView.CalcWidth(aCaption: string): Integer; +var + l_TextSize: Td2dPoint; +begin + f_Font.CalcSize(aCaption, l_TextSize); + Result := f_StateFrames[bsNormal].LeftWidth + Trunc(l_TextSize.X + 1) + f_StateFrames[bsNormal].RightWidth; + CorrectWidth(Result); +end; + +function Td2dFramedButtonView.CorrectCaption(const aCaption: string; aWidth: Integer): string; +begin + Result := CorrectCaptionEx(aCaption, aWidth, ''); +end; + +function Td2dFramedButtonView.CorrectCaptionEx(const aCaption: string; aWidth: Integer; const aObligatoryPart: string): string; +var + l_MaxTextWidth: Integer; + l_Str: string; + l_Size: Td2dPoint; +begin + l_MaxTextWidth := aWidth - f_StateFrames[bsNormal].LeftWidth - f_StateFrames[bsNormal].RightWidth; + f_Font.CalcSize(aCaption + aObligatoryPart, l_Size); + if l_Size.X > l_MaxTextWidth then + begin + l_Str := aCaption; + repeat + SetLength(l_Str, Length(l_Str)-1); + f_Font.CalcSize(l_Str + '...' + aObligatoryPart, l_Size); + until (l_Size.X <= l_MaxTextWidth) or (l_Str = ''); + Result := l_Str + '...' + aObligatoryPart; + end + else + Result := aCaption + aObligatoryPart; +end; + +procedure Td2dFramedButtonView.CorrectWidth(var theWidth: Integer); +begin + f_StateFrames[bsNormal].CorrectWidth(theWidth); +end; + +function Td2dFramedButtonView.pm_GetHeight: Single; +begin + Result := f_StateFrames[bsNormal].Height; +end; + +function Td2dFramedButtonView.pm_GetMinWidth: Integer; +begin + Result := f_StateFrames[bsNormal].MinWidth; +end; + +function Td2dFramedButtonView.pm_GetStateColor(aState: Td2dButtonState): Td2dColor; +begin + Result := f_StateColor[aState]; +end; + +procedure Td2dFramedButtonView.pm_SetStateColor(aState: Td2dButtonState; const Value: Td2dColor); +begin + f_StateColor[aState] := Value; +end; + +procedure Td2dFramedButtonView.Render(aX, aY: Single; aCaption: string; aState: Td2dButtonState; + aPrecalcWidth: Integer = 0; aTextAlign: Td2dTextAlignType = ptLeftAligned); +var + l_TextX: Single; + l_TextY: Single; + l_IntWidth: Integer; + l_TextSize: Td2dPoint; +begin + if aPrecalcWidth > 0 then + l_IntWidth := aPrecalcWidth + else + l_IntWidth := CalcWidth(aCaption); + f_StateFrames[aState].Render(aX, aY, l_IntWidth); + f_Font.Color := f_StateColor[aState]; + l_TextX := aX + f_StateFrames[aState].LeftWidth; + if aTextAlign <> ptLeftAligned then + begin + f_Font.CalcSize(aCaption, l_TextSize); + case aTextAlign of + ptRightAligned: l_TextX := l_TextX + f_StateFrames[aState].GetMidWidth(l_IntWidth) - Round(l_TextSize.X); + ptCentered : l_TextX := l_TextX + f_StateFrames[aState].GetMidWidth(l_IntWidth) div 2 - Round(l_TextSize.X) div 2; + end; + end; + l_TextY := aY + Int((f_StateFrames[aState].Height - f_Font.Size) / 2); + f_Font.Render(l_TextX, l_TextY, aCaption); +end; + +constructor Td2dFramedTextButton.Create(aX, aY: Single; aView: Id2dFramedButtonView; aCaption: string); +begin + inherited Create(aX, aY); + f_View := aView; + f_AutoSize := True; + f_TextAlign := ptLeftAligned; + Caption := aCaption; +end; + +function Td2dFramedTextButton.pm_GetHeight: Single; +begin + Result := f_View.Height; +end; + +function Td2dFramedTextButton.pm_GetWidth: Single; +begin + Result := f_Width; +end; + +procedure Td2dFramedTextButton.pm_SetAutoSize(const Value: Boolean); +begin + f_AutoSize := Value; + if f_AutoSize then + RecalcWidth; +end; + +procedure Td2dFramedTextButton.pm_SetCaption(const Value: string); +begin + f_Caption := Value; + if Autosize then + RecalcWidth; +end; + +procedure Td2dFramedTextButton.pm_SetWidth(const Value: Single); +begin + f_AutoSize := False; + f_Width := Round(Value); + f_View.CorrectWidth(f_Width); +end; + +procedure Td2dFramedTextButton.RecalcWidth; +begin + f_Width := f_View.CalcWidth(f_Caption); +end; + +procedure Td2dFramedTextButton.Render; +begin + f_View.Render(X, Y, f_Caption, f_State, f_Width, f_TextAlign); + //D2DRenderRect(D2DRect(X,Y,X+Width,Y+Height), $FFFF0000); +end; + + + +end. \ No newline at end of file diff --git a/d2dGUIMenus.pas b/d2dGUIMenus.pas new file mode 100644 index 0000000..d1da710 --- /dev/null +++ b/d2dGUIMenus.pas @@ -0,0 +1,637 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- + +unit d2dGUIMenus; + +interface +uses + Contnrs, + d2dTypes, + d2dInterfaces, + d2dGUI, + d2dGUITypes; + +type + Td2dMenu = class; + + Td2dMenuItem = class + private + f_Caption: string; + f_Checkable: Boolean; + f_Checked: Boolean; + f_Enabled: Boolean; + f_OnClick: Td2dNotifyEvent; + f_Items: TObjectList; + f_Menu: Td2dMenu; + f_Parent: Td2dMenuItem; + f_Tag: Variant; + function pm_GetChildren(Index: Integer): Td2dMenuItem; + function pm_GetChildrenCount: Integer; + function pm_GetEnabled: Boolean; + function pm_GetHasChildren: Boolean; + procedure pm_SetCaption(const Value: string); + public + constructor Create(aCaption: string); + destructor Destroy; override; + function AddChild(aChild: Td2dMenuItem): Integer; + procedure Click; + procedure ClearChildren(aFrom: Integer = 0); + property Caption: string read f_Caption write pm_SetCaption; + property Checkable: Boolean read f_Checkable write f_Checkable; + property Checked: Boolean read f_Checked write f_Checked; + property Children[Index: Integer]: Td2dMenuItem read pm_GetChildren; + property ChildrenCount: Integer read pm_GetChildrenCount; + property Enabled: Boolean read pm_GetEnabled write f_Enabled; + property HasChildren: Boolean read pm_GetHasChildren; + property Menu: Td2dMenu read f_Menu write f_Menu; + property Parent: Td2dMenuItem read f_Parent write f_Parent; + property Tag: Variant read f_Tag write f_Tag; + property OnClick: Td2dNotifyEvent read f_OnClick write f_OnClick; + end; + + Td2dMenu = class(Td2dControl) + private + f_Height: Single; + f_MouseDown: Boolean; + f_OwnItems: Boolean; + f_PaneQuad: Td2dQuad; + f_ParentMenu: Td2dMenu; + f_Root: Td2dMenuItem; + f_SelectedItem: Integer; + f_SelectedQuad: Td2dQuad; + f_Submenu: Td2dMenu; + f_VisulalStyle: Td2dMenuVisualStyle; + f_Width: Single; + procedure CalcSelectionQuad; + function IsSubmenuOpen: Boolean; + function pm_GetItemHeight: Single; + procedure pm_SetFont(const Value: Id2dFont); + procedure pm_SetRoot(const Value: Td2dMenuItem); + procedure pm_SetSelectedItem(const Value: Integer); + procedure pm_SetSubmenu(const Value: Td2dMenu); + procedure pm_SetVisulalStyle(const Value: Td2dMenuVisualStyle); + protected + f_Font: Id2dFont; + procedure DoClick(aItemIdx: Integer); + procedure OpenSubmenu(aIdx: Integer); + function pm_GetHeight: Single; override; + function pm_GetWidth: Single; override; + property ItemHeight: Single read pm_GetItemHeight; + property Submenu: Td2dMenu read f_Submenu write pm_SetSubmenu; + public + constructor Create(aFont: Id2dFont; aRoot: Td2dMenuItem = nil); overload; + constructor Create(aFont: Id2dFont; aItems: array of Td2dMenuItem); overload; + constructor Create(aParent: Td2dMenu; aItemIdx: Integer); overload; + destructor Destroy; override; + procedure Close(aGlobal: Boolean = False); + function IsPointInControl(aX, aY: Single): Boolean; override; + function ItemAtPoint(aX, aY: Single): Integer; + procedure Popup(aX, aY: Single; aAlign: Td2dAlign = alTopLeft); overload; + procedure Popup(const aRect: Td2dRect; aAlign: Td2dAlign = alTopLeft); overload; + procedure ProcessEvent(var theEvent: Td2dInputEvent); override; + procedure RecalcSize; + procedure Render; override; + procedure Update; override; + property Font: Id2dFont read f_Font write pm_SetFont; + property ParentMenu: Td2dMenu read f_ParentMenu; + property Root: Td2dMenuItem read f_Root write pm_SetRoot; + property SelectedItem: Integer read f_SelectedItem write pm_SetSelectedItem; + property VisulalStyle: Td2dMenuVisualStyle read f_VisulalStyle write pm_SetVisulalStyle; + end; + +function D2DMenuDash: Td2dMenuItem; + +const + cDefaultMenuVisualStyle: Td2dMenuVisualStyle = + (rBGColor :$FFFFFFFF; + rBorderColor :$FFA0A0A0; + rTextColor :$FF000000; + rHIndent :2; + rVIndent :2; + rSelectionColor :$FF0000A0; + rSelectedColor :$FFFFFFFF; + rDisabledColor :$FFC0C0C0; + ); + + +implementation +uses + Classes, + SysUtils, + + d2dCore, + d2dUtils, + d2dGUIUtils; + +function D2DMenuDash: Td2dMenuItem; +begin + Result := Td2dMenuItem.Create('-'); +end; + +constructor Td2dMenuItem.Create(aCaption: string); +begin + inherited Create; + f_Items := TObjectList.Create; + f_Caption := aCaption; + f_Enabled := True; +end; + +destructor Td2dMenuItem.Destroy; +begin + f_Items.Free; + inherited; +end; + +function Td2dMenuItem.AddChild(aChild: Td2dMenuItem): Integer; +begin + Result := f_Items.Add(aChild); + aChild.f_Parent := Self; + if f_Menu <> nil then + f_Menu.RecalcSize; +end; + +procedure Td2dMenuItem.ClearChildren(aFrom: Integer = 0); +begin + if aFrom = 0 then + f_Items.Clear + else + while f_Items.Count > aFrom do + f_Items.Delete(f_Items.Count-1); + if f_Menu <> nil then + f_Menu.RecalcSize; +end; + +procedure Td2dMenuItem.Click; +begin + if Enabled and Assigned(f_OnClick) then + f_OnClick(Self); +end; + +function Td2dMenuItem.pm_GetChildren(Index: Integer): Td2dMenuItem; +begin + Result := Td2dMenuItem(f_Items[Index]); +end; + +function Td2dMenuItem.pm_GetChildrenCount: Integer; +begin + Result := f_Items.Count; +end; + +function Td2dMenuItem.pm_GetEnabled: Boolean; +begin + Result := {Assigned(f_OnClick) and} f_Enabled; +end; + +function Td2dMenuItem.pm_GetHasChildren: Boolean; +begin + Result := (ChildrenCount > 0); +end; + +procedure Td2dMenuItem.pm_SetCaption(const Value: string); +begin + f_Caption := Value; + if Menu <> nil then + Menu.RecalcSize; +end; + +constructor Td2dMenu.Create(aFont: Id2dFont; aRoot: Td2dMenuItem = nil); +begin + inherited Create(0, 0); + f_Font := aFont; + if aRoot <> nil then + Root := aRoot + else + begin + Root := Td2dMenuItem.Create(''); + f_OwnItems := True; + end; + f_VisulalStyle := cDefaultMenuVisualStyle; + f_Visible := False; + CanBeFocused := True; +end; + +constructor Td2dMenu.Create(aFont: Id2dFont; aItems: array of Td2dMenuItem); +var + I: Integer; + l_Root: Td2dMenuItem; +begin + l_Root := Td2dMenuItem.Create(''); + for I := 0 to High(aItems) do + l_Root.AddChild(aItems[I]); + Create(aFont, l_Root); + f_OwnItems := True; +end; + +constructor Td2dMenu.Create(aParent: Td2dMenu; aItemIdx: Integer); +begin + Create(aParent.Font, aParent.Root.Children[aItemIdx]); + f_ParentMenu := aParent; + VisulalStyle := f_ParentMenu.VisulalStyle; +end; + +destructor Td2dMenu.Destroy; +begin + Submenu := nil; + if f_OwnItems then + f_Root.Free; + inherited; +end; + +procedure Td2dMenu.CalcSelectionQuad; +var + l_Rect: Td2dRect; +begin + if f_SelectedItem < 0 then + Exit; + l_Rect := D2DRect(X, Y + ItemHeight*SelectedItem, X + Width, Y + ItemHeight*(SelectedItem+1)); + f_SelectedQuad := D2DMakeFilledRectQuad(l_Rect, f_VisulalStyle.rSelectionColor); +end; + +procedure Td2dMenu.Close(aGlobal: Boolean = False); +begin + Submenu := nil; + if aGlobal and (ParentMenu <> nil) then + ParentMenu.Close(True) + else + Visible := False; +end; + +procedure Td2dMenu.DoClick(aItemIdx: Integer); +var + l_Item: Td2dMenuItem; +begin + l_Item := f_Root.Children[aItemIdx]; + if l_Item.Enabled then + begin + if l_Item.HasChildren then + OpenSubmenu(aItemIdx) + else + begin + if l_Item.Checkable then + l_Item.Checked := not l_Item.Checked; + Close(True); + l_Item.Click; + end; + end; +end; + +function Td2dMenu.IsPointInControl(aX, aY: Single): Boolean; +begin + Result := D2DIsPointInRect(aX, aY, f_X, f_Y, Width, Height); + if IsSubmenuOpen then + Result := Result or Submenu.IsPointInControl(aX, aY); +end; + +function Td2dMenu.IsSubmenuOpen: Boolean; +begin + Result := (Submenu <> nil) and (Submenu.Visible); +end; + +function Td2dMenu.ItemAtPoint(aX, aY: Single): Integer; +begin + Result := -1; + if IsPointInControl(aX, aY) then + begin + aY := aY - Y; + Result := Trunc(aY / ItemHeight); + if (Result > f_Root.ChildrenCount-1) or (f_Root.Children[Result].Caption = '-') then + Result := -1; + end; +end; + +procedure Td2dMenu.OpenSubmenu(aIdx: Integer); +var + l_SubX: Single; + l_SubY: Single; +begin + if f_Root.Children[aIdx].HasChildren and f_Root.Children[aIdx].Enabled then + begin + Submenu := Td2dMenu.Create(Self, aIdx); + Submenu.RecalcSize; + if X + Width + Submenu.Width < gD2DE.ScreenWidth then + l_SubX := X + Width + else + l_SubX := X - Submenu.Width; + l_SubY := Y + aIdx * ItemHeight; + Submenu.Popup(l_SubX, l_SubY); + end; +end; + +function Td2dMenu.pm_GetHeight: Single; +begin + Result := f_Height; +end; + +function Td2dMenu.pm_GetItemHeight: Single; +begin + Result := f_VisulalStyle.rVIndent*2 + f_Font.Height; +end; + +function Td2dMenu.pm_GetWidth: Single; +begin + Result := f_Width; +end; + +procedure Td2dMenu.pm_SetFont(const Value: Id2dFont); +begin + f_Font := Value; + RecalcSize; +end; + +procedure Td2dMenu.pm_SetRoot(const Value: Td2dMenuItem); +begin + if (f_Root <> nil) and f_OwnItems then + FreeAndNil(f_Root); + f_Root := Value; + f_Root.Menu := Self; +end; + +procedure Td2dMenu.pm_SetSelectedItem(const Value: Integer); +begin + if (Value >= -1) and (Value < f_Root.ChildrenCount) then + begin + f_SelectedItem := Value; + CalcSelectionQuad; + end; +end; + +procedure Td2dMenu.pm_SetSubmenu(const Value: Td2dMenu); +begin + if f_Submenu <> nil then + FreeAndNil(f_Submenu); + f_Submenu := Value; +end; + +procedure Td2dMenu.pm_SetVisulalStyle(const Value: Td2dMenuVisualStyle); +begin + f_VisulalStyle := Value; + RecalcSize; +end; + +procedure Td2dMenu.Popup(aX, aY: Single; aAlign: Td2dAlign = alTopLeft); +begin + Popup(D2DRect(aX, aY, aX, aY), aAlign); +end; + +procedure Td2dMenu.Popup(const aRect: Td2dRect; aAlign: Td2dAlign = alTopLeft); +var + l_Selected: Integer; + l_Pos : Td2dPoint; +begin + Visible := True; + Focused := True; + RecalcSize; + SelectedItem := 0; + l_Pos := D2DAlignRect(aRect, D2DPoint(f_Width, f_Height), aAlign); + f_X := l_Pos.X; + f_Y := l_Pos.Y; + Update; + l_Selected := ItemAtPoint(gD2DE.MouseX, gD2DE.MouseY); + if l_Selected >= 0 then + SelectedItem := l_Selected; +end; + +procedure Td2dMenu.ProcessEvent(var theEvent: Td2dInputEvent); +var + l_SelItem: Integer; +begin + if IsSubmenuOpen then + Submenu.ProcessEvent(theEvent); + + if IsMouseEvent(theEvent) then + begin + if D2DIsPointInRect(gD2DE.MouseX, gD2DE.MouseY, X, Y, Width, Height) then + begin + case theEvent.EventType of + INPUT_MOUSEMOVE: + begin + SelectedItem := ItemAtPoint(gD2DE.MouseX, gD2DE.MouseY); + // because menu is ALWAYS on top + if IsPointInControl(gD2DE.MouseX, gD2DE.MouseY) then + MaskMouseMove(theEvent); + end; + + INPUT_MBUTTONDOWN: + begin + f_MouseDown := True; + Processed(theEvent); + end; + + INPUT_MBUTTONUP: + begin + if f_MouseDown then + begin + f_MouseDown := False; + if SelectedItem >= 0 then + DoClick(SelectedItem); + Processed(theEvent); + end; + end; + end; + end + else + begin + case theEvent.EventType of + INPUT_MBUTTONDOWN: + Close; + INPUT_MBUTTONUP: + f_MouseDown := False; + end; + end; + end; + + if theEvent.EventType = INPUT_KEYDOWN then + begin + if (theEvent.KeyCode = D2DK_DOWN) and (SelectedItem < f_Root.ChildrenCount-1) then + begin + l_SelItem := SelectedItem + 1; + while (f_Root.Children[l_SelItem].Caption = '-') and (l_SelItem < f_Root.ChildrenCount) do + Inc(l_SelItem); + if (f_Root.Children[l_SelItem].Caption <> '-') then + SelectedItem := l_SelItem; + Processed(theEvent); + end; + + if (theEvent.KeyCode = D2DK_UP) and (SelectedItem > 0) then + begin + l_SelItem := SelectedItem - 1; + while (f_Root.Children[l_SelItem].Caption = '-') and (l_SelItem < f_Root.ChildrenCount) do + Dec(l_SelItem); + if (f_Root.Children[l_SelItem].Caption <> '-') then + SelectedItem := l_SelItem; + Processed(theEvent); + end; + + if (theEvent.KeyCode = D2DK_ENTER) then + begin + if SelectedItem >= 0 then + DoClick(SelectedItem); + Processed(theEvent); + end; + + if (theEvent.KeyCode = D2DK_RIGHT) then + begin + if SelectedItem >= 0 then + OpenSubmenu(SelectedItem); + Processed(theEvent); + end; + + if (theEvent.KeyCode = D2DK_LEFT) then + begin + if ParentMenu <> nil then + Close; + Processed(theEvent); + end; + + if theEvent.KeyCode = D2DK_ESCAPE then + begin + Processed(theEvent); + Close; + end; + end; +end; + +procedure Td2dMenu.RecalcSize; +var + I: Integer; + l_ItemWidth: Single; + l_LineSize: Td2dPoint; +begin + f_Height := ItemHeight * f_Root.ChildrenCount; + f_Width := 0; + for I := 0 to Pred(f_Root.ChildrenCount) do + begin + f_Font.CalcSize(f_Root.Children[I].Caption, l_LineSize); + l_ItemWidth := f_VisulalStyle.rHIndent*4 + f_Font.Height*2 + l_LineSize.X; + if l_ItemWidth > f_Width then + f_Width := l_ItemWidth; + end; + Update; +end; + +procedure Td2dMenu.Render; +var + l_TextLeft: Single; + l_TextTop : Single; + l_HalfFH: Single; + I: Integer; + l_TriLeft: Single; + l_QuartFH: Single; + l_CheckLeft: single; + + procedure RenderTriangle; + var + l_Tri: Td2dTriple; + begin + FillChar(l_Tri, SizeOf(l_Tri), 0); + l_Tri.Blend := BLEND_DEFAULT; + l_Tri.V[0].X := l_TriLeft; + l_Tri.V[0].Y := l_TextTop + l_QuartFH; + l_Tri.V[0].Col := f_Font.Color; + + l_Tri.V[1].X := l_TriLeft + l_QuartFH; + l_Tri.V[1].Y := l_Tri.V[0].Y + l_QuartFH; + l_Tri.V[1].Col := f_Font.Color; + + l_Tri.V[2].X := l_TriLeft; + l_Tri.V[2].Y := l_Tri.V[0].Y + l_HalfFH; + l_Tri.V[2].Col := f_Font.Color; + gD2DE.Gfx_RenderTriple(l_Tri); + end; + + procedure RenderCheck(aChecked: Boolean); + var + l_Rect: Td2dRect; + l_CheckTop: Single; + begin + l_CheckTop := l_TextTop + l_QuartFH; + l_Rect := D2DRect(l_CheckLeft, l_CheckTop, l_CheckLeft+l_HalfFH, l_CheckTop+l_HalfFH); + D2DRenderRect(l_Rect, f_Font.Color); + + if aChecked then + begin + with l_Rect do + begin + Left := Left + 1.0; // why 1?! i don't know... + Top := Top + 1.0; + // here goes the dirty trick (can't understand yet why) + if gD2DE.Windowed then + begin + Right := Right - 2.0; + Bottom := Bottom - 2.0; + end + else + begin + Right := Right - 1.0; + Bottom := Bottom - 1.0; + end; + end; + D2DRenderFilledRect(l_Rect, f_Font.Color); + end; + end; + + +begin + gD2DE.Gfx_RenderQuad(f_PaneQuad); + if f_SelectedItem >= 0 then + gD2DE.Gfx_RenderQuad(f_SelectedQuad); + + l_HalfFH := Round(Font.Height / 2); + l_QuartFH := Round(Font.Height / 4); + l_TextLeft := X + f_Font.Height + f_VisulalStyle.rHIndent*2; + l_TriLeft := X + Width - f_VisulalStyle.rHIndent - l_HalfFH; + l_CheckLeft := X + f_VisulalStyle.rHIndent+l_QuartFH; + + for I := 0 to Pred(f_Root.ChildrenCount) do + begin + l_TextTop := Y + ItemHeight*I + f_VisulalStyle.rVIndent; + if f_Root.Children[I].Caption = '-' then + begin + l_TextTop := Int(l_TextTop + ItemHeight/2); + gD2DE.Gfx_RenderLine(X + f_VisulalStyle.rHIndent + l_QuartFH, l_TextTop, + X + Width - f_VisulalStyle.rHIndent - l_QuartFH, l_TextTop, f_VisulalStyle.rBorderColor, 0); + end + else + begin + if f_Root.Children[I].Enabled then + begin + if I = SelectedItem then + f_Font.Color := f_VisulalStyle.rSelectedColor + else + f_Font.Color := f_VisulalStyle.rTextColor; + end + else + f_Font.Color := f_VisulalStyle.rDisabledColor; + f_Font.Render(l_TextLeft, l_TextTop, f_Root.Children[I].Caption); + if f_Root.Children[I].HasChildren then + RenderTriangle; + if f_Root.Children[I].Checkable then + RenderCheck(f_Root.Children[I].Checked); + end; + end; + D2DRenderRect(D2DRect(X, Y, X+f_Width, Y+f_Height), f_VisulalStyle.rBorderColor); + if IsSubmenuOpen then + Submenu.Render; +end; + +procedure Td2dMenu.Update; +begin + if Assigned(GUI) and (not Focused) then + begin + Close; + Exit; + end; + f_PaneQuad := D2DMakeFilledRectQuad(D2DRect(X, Y, X+f_Width, Y+f_Height), f_VisulalStyle.rBGColor); + CalcSelectionQuad; +end; + +end. diff --git a/d2dGUITextPane.pas b/d2dGUITextPane.pas new file mode 100644 index 0000000..5be9240 --- /dev/null +++ b/d2dGUITextPane.pas @@ -0,0 +1,823 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dGUITextPane; + +interface + +uses + Contnrs, + + d2dTypes, + d2dInterfaces, + d2dFormattedText, + d2dDocRoot, + d2dGUITypes, + d2dGUI, + d2dUtils; + + +type + Td2dOnAddTextEvent = procedure(const aSender: TObject; const aText: string; aColor: Td2dColor; aParaType: Td2dTextAlignType) of object; + + Td2dTextPaneDocRoot = class(Td2dDocRoot) + private + f_TopVisible: Integer; + f_VerticalShift: Single; + f_VisibleHeight: Single; + f_BottomVisible: Integer; + procedure pm_SetVerticalShift(const Value: Single); + procedure pm_SetVisibleHeight(const Value: Single); + protected + function GetBottomChild: Integer; override; + function GetTopChild: Integer; override; + function GetVerticalShift: Single; override; + public + constructor Create(aWidth: Single; aHeight: Single); + procedure AddChild(aChild: Td2dCustomSlice); override; + procedure Clear; override; + procedure DeleteFrom(aIndex : Integer); override; + procedure DoDraw(X, Y: Single); override; + procedure RecalcBounds; + property BottomVisible: Integer read f_BottomVisible; + property TopVisible: Integer read f_TopVisible; + property VerticalShift: Single read f_VerticalShift write pm_SetVerticalShift; + property VisibleHeight: Single read f_VisibleHeight write pm_SetVisibleHeight; + end; + +type + Td2dTextPane = class(Td2dControl) + private + f_CurScrollSpeed: Single; + f_CurScrollDir : Single; + f_CursorOn: Boolean; + f_CursorTime: Single; + f_DocRoot: Td2dTextPaneDocRoot; + f_FixedMorePosition: Single; + f_Width : Single; + f_Height : Single; + f_TS : Td2dTextSource; + f_Formatter: Td2dFormatter; + f_InputResult: string; + f_PanningSpeed: Integer; + f_State: Td2dTextPaneState; + f_TargetShift: Single; + f_MoreTargetShift: Single; + f_OnAddText: Td2dOnAddTextEvent; + f_OnEndInput: Td2dNotifyEvent; + f_OnLinkClick: Td2dOnLinkClickEvent; + f_OnStateChange: Td2dNotifyEvent; + f_ScrollSpeed: Integer; + f_StateBeforeScroll: Td2dTextPaneState; + f_ParaPossiblyWithLinks: Integer; + procedure CalcMoreTargetShift; + procedure Format; + procedure FormatAll; + function pm_GetLastChunk: Td2dCustomTextChunk; + function pm_GetLineSpacing: Single; + function pm_GetLinksAllowed: Boolean; + function pm_GetParaSpacing: Single; + function pm_GetParaCount: Integer; + function pm_GetScrollShift: Single; + procedure pm_SetLineSpacing(const Value: Single); + procedure pm_SetLinksAllowed(const Value: Boolean); + procedure pm_SetParaSpacing(const Value: Single); + procedure pm_SetScrollShift(const Value: Single); + procedure _UnHighlightLink(const aLinkSlice: Td2dLinkSlice); + procedure UnHighlightLinks; + procedure _DeactivateLink(const aLinkSlice: Td2dLinkSlice); + protected + function IsSliceVisible(aSlice: Td2dCustomSlice): Boolean; + procedure PageDown(aDelta: Single = 0); + procedure PageUp(aDelta: Single = 0); + function pm_GetHeight: Single; override; + function pm_GetWidth: Single; override; + procedure pm_SetHeight(const Value: Single); override; + procedure pm_SetState(const Value: Td2dTextPaneState); virtual; + procedure pm_SetWidth(const Value: Single); override; + procedure Scroll(aDelta: Single); virtual; + property DocRoot: Td2dTextPaneDocRoot read f_DocRoot; + property LastChunk: Td2dCustomTextChunk read pm_GetLastChunk; + public + constructor Create(aX, aY, aWidth, aHeight: Single); + destructor Destroy; override; + procedure AddPara(aSlice: Td2dCustomSlice); + procedure AddText(const aText: string; const aFont: Id2dFont; aColor: Td2dColor; aParaType: Td2dTextAlignType); + procedure AddPicture(const aPicture: Id2dPicture;aAlign: Td2dTextAlignType); + procedure AddLink(const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, aHighLightColor: Td2dColor; aParaType: Td2dTextAlignType); + procedure ClearAll; virtual; + procedure ClearFromPara(aIdx: Integer); + procedure DropLinks; + procedure EndInput; + procedure FixMorePosition; + procedure FrameFunc(aDelta: Single); override; + function GetHighlightedLinkText: string; + function GetPara(aIdx: Integer): Td2dCustomSlice; + procedure LoadText(const aFiler: Td2dFiler; aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); + procedure Render; override; + procedure ScrollTo(aTargetShift: Single; aScrollSpeed: Single; aWithMore: Boolean = False); + procedure ScrollToBottom; + procedure ProcessEvent(var theEvent: Td2dInputEvent); override; + procedure SaveText(const aFiler: Td2dFiler); + procedure StartInput(aFont: Id2dFont; aColor: Td2dColor; aParaType: Td2dTextAlignType); + property InputResult: string read f_InputResult; + property OnEndInput: Td2dNotifyEvent read f_OnEndInput write f_OnEndInput; + property OnAddText: Td2dOnAddTextEvent read f_OnAddText write f_OnAddText; + property OnStateChange: Td2dNotifyEvent read f_OnStateChange write + f_OnStateChange; + property LineSpacing: Single read pm_GetLineSpacing write pm_SetLineSpacing; + property OnLinkClick: Td2dOnLinkClickEvent read f_OnLinkClick write f_OnLinkClick; + property LinksAllowed: Boolean read pm_GetLinksAllowed write pm_SetLinksAllowed; + property ParaSpacing: Single read pm_GetParaSpacing write pm_SetParaSpacing; + property ParaCount: Integer read pm_GetParaCount; + property ScrollShift: Single read pm_GetScrollShift write pm_SetScrollShift; + property PanningSpeed: Integer read f_PanningSpeed write f_PanningSpeed; + property ScrollSpeed: Integer read f_ScrollSpeed write f_ScrollSpeed; + property State: Td2dTextPaneState read f_State write pm_SetState; + end; + + +implementation +uses + d2dCore, + Classes; + +constructor Td2dTextPane.Create(aX, aY, aWidth, aHeight: Single); +begin + inherited Create(aX, aY); + CanBeFocused := True; + f_Width := aWidth; + f_Height := aHeight; + f_TS := Td2dTextSource.Create; + f_DocRoot := Td2dTextPaneDocRoot.Create(f_Width, f_Height); + f_Formatter := Td2dFormatter.Create(f_TS, f_DocRoot); + f_State := tpsIdle; + f_PanningSpeed := 500; + f_ScrollSpeed := 2500; + f_FixedMorePosition := -1; + f_MoreTargetShift := -1; +end; + +destructor Td2dTextPane.Destroy; +begin + f_Formatter.Free; + f_DocRoot.Free; + f_TS.Free; + inherited; +end; + +procedure Td2dTextPane.AddPara(aSlice: Td2dCustomSlice); +var + l_Para: Td2dCustomSlice; + l_NewTop: Single; +begin + if f_DocRoot.ChildrenCount > 0 then + begin + l_Para := f_DocRoot.Children[f_DocRoot.ChildrenCount-1]; + l_NewTop := l_Para.Top + l_Para.Height + f_Formatter.FormatParams.rParaSpacing; + end + else + l_NewTop := 0; + aSlice.Top := l_NewTop; + f_DocRoot.AddChild(aSlice); + f_DocRoot.RecalcHeight; + f_DocRoot.DropLinkCache; +end; + +procedure Td2dTextPane.AddText(const aText: string; const aFont: Id2dFont; aColor: Td2dColor; aParaType: Td2dTextAlignType); +begin + f_TS.AddText(aText, aFont, aColor, aParaType); + Format; + if Assigned(f_OnAddText) then + f_OnAddText(Self, aText, aColor, aParaType); +end; + +procedure Td2dTextPane.AddPicture(const aPicture: Id2dPicture; aAlign: Td2dTextAlignType); +begin + f_TS.AddPicture(aPicture, aAlign); + Format; +end; + +procedure Td2dTextPane.AddLink(const aText, aTarget: string; const aFont: Id2dFont; aColor, aLinkColor, + aHighLightColor: Td2dColor; aParaType: Td2dTextAlignType); +begin + f_TS.AddLink(aText, aTarget, aFont, aColor, aLinkColor, aHighLightColor, aParaType); + Format; + if Assigned(f_OnAddText) then + f_OnAddText(Self, aText, aColor, aParaType); +end; + +procedure Td2dTextPane.CalcMoreTargetShift; +var + l_LastPara: Td2dUnionSlice; + l_Line : Td2dCustomSlice; + I : Integer; + l_NewShift: Single; +begin + if f_FixedMorePosition > -1 then + begin + f_MoreTargetShift := f_FixedMorePosition; + f_FixedMorePosition := -1; + end + else + begin + if f_DocRoot.Children[f_DocRoot.BottomVisible].SliceType = stUnion then + begin + l_LastPara := Td2dUnionSlice(f_DocRoot.Children[f_DocRoot.BottomVisible]); + f_MoreTargetShift := l_LastPara.Top; + I := l_LastPara.ChildrenCount - 1; + while I > 0 do // find target line to scroll to + begin + l_Line := l_LastPara.Children[I]; + l_NewShift := f_MoreTargetShift + l_Line.Top; + if l_NewShift <= ScrollShift + f_DocRoot.VisibleHeight then + begin + f_MoreTargetShift := l_NewShift; + Break; + end; + Dec(I); + end; + if f_MoreTargetShift = ScrollShift then + f_MoreTargetShift := ScrollShift + Height; + end + else + f_MoreTargetShift := f_DocRoot.Children[f_DocRoot.BottomVisible].Top; + end; + if f_MoreTargetShift > f_TargetShift then + f_MoreTargetShift := -1; +end; + +procedure Td2dTextPane.ClearAll; +begin + f_TS.Clear; + f_DocRoot.Clear; + f_DocRoot.RecalcHeight; + f_Formatter.LastFormatted := 0; + f_FixedMorePosition := -1; + State := tpsIdle; + f_StateBeforeScroll := tpsIdle; + f_ParaPossiblyWithLinks := 0; +end; + +procedure Td2dTextPane.ClearFromPara(aIdx: Integer); +begin + f_DocRoot.DeleteFrom(aIdx); + if f_TS.Count <= aIdx then + f_Formatter.LastFormatted := f_TS.Count - 1 + else + f_Formatter.LastFormatted := aIdx; +end; + +procedure Td2dTextPane.DropLinks; +begin + if f_TS.Count > 0 then + begin + f_TS.DropLinks(f_ParaPossiblyWithLinks); + f_DocRoot.IterateLinks(_DeactivateLink, f_ParaPossiblyWithLinks); + f_ParaPossiblyWithLinks := f_TS.Count - 1; + end; +end; + +procedure Td2dTextPane.EndInput; +begin + f_InputResult := Td2dStringChunk(LastChunk).Text; + f_TS.Paragraphs[f_TS.Count-1].ClosePara; + State := tpsIdle; + LinksAllowed := True; + if Assigned(f_OnEndInput) then + f_OnEndInput(Self); +end; + +procedure Td2dTextPane.FixMorePosition; +begin + f_FixedMorePosition := f_DocRoot.Height; +end; + +procedure Td2dTextPane.Format; +begin + f_Formatter.Format; + f_DocRoot.ForceLinkAllowance; + f_DocRoot.DropLinkCache; + gD2DE.Input_TouchMousePos; + //f_DocRoot.FindLinkHighlight(X, Y); +end; + +procedure Td2dTextPane.FormatAll; +begin + f_Formatter.ReformatAll; + f_DocRoot.ForceLinkAllowance; + f_DocRoot.DropLinkCache; + gD2DE.Input_TouchMousePos; + //f_DocRoot.FindLinkHighlight(X, Y); +end; + +procedure Td2dTextPane.FrameFunc(aDelta: Single); +begin + case State of + tpsScrolling: + begin + Scroll(aDelta * f_CurScrollSpeed * f_CurScrollDir); + if f_MoreTargetShift > -1 then + begin + if ScrollShift >= f_MoreTargetShift then + begin + ScrollShift := f_MoreTargetShift; + State := tpsMore; + end; + end + else + begin + if ((f_CurScrollDir > 0) and (ScrollShift > f_TargetShift)) or + ((f_CurScrollDir < 0) and (ScrollShift < f_TargetShift)) then + begin + ScrollShift := f_TargetShift; + State := f_StateBeforeScroll; + f_DocRoot.DropLinkCache; + gD2DE.Input_TouchMousePos; + end; + end; + end; + tpsInput: + begin + f_CursorTime := f_CursorTime + aDelta; + if f_CursorTime > 0.5 then + begin + f_CursorOn := not f_CursorOn; + f_CursorTime := 0.0; + end; + end; + end; +end; + +function Td2dTextPane.GetHighlightedLinkText: string; +begin + if f_DocRoot.HighlightedLink = nil then + Result := '' + else + Result := f_DocRoot.HighlightedLink.Slice.GetLinkText; +end; + +function Td2dTextPane.GetPara(aIdx: Integer): Td2dCustomSlice; +begin + Result := f_DocRoot.Children[aIdx]; +end; + +function Td2dTextPane.IsSliceVisible(aSlice: Td2dCustomSlice): Boolean; +var + l_AbsTop: Single; +begin + l_AbsTop := aSlice.AbsTop; + Result := (l_AbsTop + aSlice.Height > ScrollShift) and (l_AbsTop < ScrollShift + Height); +end; + +procedure Td2dTextPane.LoadText(const aFiler: Td2dFiler; aFP: Id2dFontProvider; const aPP: Id2dPictureProvider); +var + l_Shift: Single; +begin + f_TS.Load(aFiler, aFP, aPP); + FormatAll; + l_Shift := f_DocRoot.Height - Height; + if l_Shift > 0.0 then + f_DocRoot.VerticalShift := l_Shift + else + f_DocRoot.VerticalShift := 0.0; + +end; + +procedure Td2dTextPane.PageDown(aDelta: Single = 0); +var + l_ScrollPosition: Single; + l_MaxDownPos: Single; +begin + l_MaxDownPos := f_DocRoot.Height - Height; + if (l_MaxDownPos < 0) or (ScrollShift = l_MaxDownPos) then + Exit; + if aDelta = 0 then + aDelta := Height; + l_ScrollPosition := ScrollShift + aDelta; + if l_ScrollPosition > l_MaxDownPos then + l_ScrollPosition := l_MaxDownPos; + ScrollTo(l_ScrollPosition, ScrollSpeed); +end; + +procedure Td2dTextPane.PageUp(aDelta: Single = 0); +var + l_ScrollPosition: Single; +begin + if ScrollShift = 0 then + Exit; + if aDelta = 0 then + aDelta := Height; + l_ScrollPosition := ScrollShift - aDelta; + if l_ScrollPosition < 0 then + l_ScrollPosition := 0; + ScrollTo(l_ScrollPosition, ScrollSpeed); +end; + +function Td2dTextPane.pm_GetHeight: Single; +begin + Result := f_Height; +end; + +function Td2dTextPane.pm_GetLastChunk: Td2dCustomTextChunk; +var + l_Para: Td2dTextPara; +begin + Result := nil; + if f_TS.Count > 0 then + begin + l_Para := f_TS.Paragraphs[f_TS.Count-1]; + Result := l_Para.Chunks[l_Para.Count-1]; + end; +end; + +function Td2dTextPane.pm_GetLineSpacing: Single; +begin + Result := f_Formatter.FormatParams.rLineSpacing; +end; + +function Td2dTextPane.pm_GetLinksAllowed: Boolean; +begin + Result := f_DocRoot.LinksAllowed; +end; + +function Td2dTextPane.pm_GetParaSpacing: Single; +begin + Result := f_Formatter.FormatParams.rParaSpacing; +end; + +function Td2dTextPane.pm_GetParaCount: Integer; +begin + Result := f_DocRoot.ChildrenCount; +end; + +function Td2dTextPane.pm_GetScrollShift: Single; +begin + Result := f_DocRoot.VerticalShift; +end; + +function Td2dTextPane.pm_GetWidth: Single; +begin + Result := f_Width; +end; + +procedure Td2dTextPane.pm_SetHeight(const Value: Single); +begin + if f_Height <> Value then + begin + f_Height := Value; + f_DocRoot.VisibleHeight := f_Height; + end; +end; + +procedure Td2dTextPane.pm_SetLineSpacing(const Value: Single); +var + l_FP: Td2dFormatParamsRec; +begin + l_FP := f_Formatter.FormatParams; + l_FP.rLineSpacing := Value; + f_Formatter.FormatParams := l_FP; + f_Formatter.ReformatAll; + if f_FixedMorePosition > -1 then + FixMorePosition; +end; + +procedure Td2dTextPane.pm_SetLinksAllowed(const Value: Boolean); +begin + f_DocRoot.LinksAllowed := Value; +end; + +procedure Td2dTextPane.pm_SetParaSpacing(const Value: Single); +var + l_FP: Td2dFormatParamsRec; +begin + l_FP := f_Formatter.FormatParams; + l_FP.rParaSpacing := Value; + f_Formatter.FormatParams := l_FP; + f_Formatter.ReformatAll; + if f_FixedMorePosition > -1 then + FixMorePosition; +end; + +procedure Td2dTextPane.pm_SetScrollShift(const Value: Single); +begin + f_DocRoot.VerticalShift := Value; +end; + +procedure Td2dTextPane.pm_SetState(const Value: Td2dTextPaneState); +begin + if Value <> f_State then + begin + f_State := Value; + if Assigned(f_OnStateChange) then + f_OnStateChange(Self); + end; +end; + +procedure Td2dTextPane.pm_SetWidth(const Value: Single); +begin + if f_Width <> Value then + begin + f_Width := Value; + f_DocRoot.Width := f_Width; + f_Formatter.ReformatAll; + if f_FixedMorePosition > -1 then + FixMorePosition; + end; +end; + +procedure Td2dTextPane.ProcessEvent(var theEvent: Td2dInputEvent); +var + l_Chunk: Td2dStringChunk; + l_Char: Char; + l_Scroll: Single; +begin + case State of + tpsIdle : + begin + if (theEvent.EventType = INPUT_KEYDOWN) then + begin + if theEvent.KeyCode = D2DK_PGUP then + begin + PageUp; + Processed(theEvent); + end; + if theEvent.KeyCode = D2DK_PGDN then + begin + PageDown; + Processed(theEvent); + end; + end; + if (theEvent.EventType = INPUT_MOUSEWHEEL) then + begin + if theEvent.Flags and D2DINP_CTRL <> 0 then + l_Scroll := 0 + else + l_Scroll := 25; + if theEvent.Wheel < 0 then + PageDown(l_Scroll) + else + PageUp(l_Scroll); + Processed(theEvent); + end; + if (theEvent.EventType = INPUT_MOUSEMOVE) then + f_DocRoot.FindLinkHighlight(X, Y, theEvent); + if (theEvent.EventType = INPUT_MBUTTONDOWN) and (theEvent.KeyCode = D2DK_LBUTTON) and (f_DocRoot.HighlightedLink <> nil) then + begin + if Assigned(f_OnLinkClick) then + f_OnLinkClick(Self, D2DMoveRect(f_DocRoot.HighlightedLink.Rect, X, Y), f_DocRoot.HighlightedLink.Slice.Target); + Processed(theEvent); + end; + end; + tpsScrolling : ; + tpsMore : + begin + if (theEvent.EventType = INPUT_KEYDOWN) or + ((theEvent.EventType = INPUT_MBUTTONDOWN) and IsMouseInControl) then + begin + CalcMoreTargetShift; + State := tpsScrolling; + Processed(theEvent); + end; + end; + tpsInput : + begin + if (theEvent.EventType = INPUT_KEYDOWN) then + begin + l_Chunk := Td2dStringChunk(LastChunk); + if theEvent.KeyCode = D2DK_BACKSPACE then + begin + if l_Chunk.Text <> '' then + l_Chunk.Text := Copy(l_Chunk.Text, 1, Length(l_Chunk.Text)-1); + Format; + Processed(theEvent); + Exit; + end; + if theEvent.KeyCode = D2DK_ENTER then + begin + EndInput; + Processed(theEvent); + Exit; + end; + l_Char := Char(theEvent.KeyChar); + if (l_Char > #31) and (l_Chunk.Font.CanRenderChar(l_Char) or (l_Char = #32)) then // space is always allowed + l_Chunk.Text := l_Chunk.Text + l_Char; + Format; + ScrollToBottom; + Processed(theEvent); + end; + end; + end; +end; + +procedure Td2dTextPane.Render; +var + l_Sl: Td2dCustomSlice; + l_X : Single; + l_Y : Single; + l_Color: Td2dColor; +begin + //D2DRenderRect(D2DRect(X,Y,X+Width,Y+Height), $FFFF0000); + gD2DE.Gfx_SetClipping(Trunc(X), Trunc(Y), Trunc(Width+0.5), Trunc(Height+0.5)); + try + f_DocRoot.Draw(X, Y); + if (State = tpsInput) and f_CursorOn then + begin + l_Sl := f_DocRoot; + while (l_Sl.SliceType = stUnion) do // getting last slice + l_Sl := Td2dUnionSlice(l_Sl).Children[Td2dUnionSlice(l_Sl).ChildrenCount - 1]; + l_X := l_Sl.AbsLeft + l_Sl.Width + X + 1; + l_Y := l_Sl.AbsTop - ScrollShift + Y + l_Sl.Height; + l_Color := Td2dStringChunk(LastChunk).Color; + gD2DE.Gfx_RenderLine(l_X, l_Y, l_X, l_Y - Td2dStringChunk(LastChunk).Font.Height, l_Color, 0); + end; + finally + gD2DE.Gfx_SetClipping(0); + end; +end; + +procedure Td2dTextPane.SaveText(const aFiler: Td2dFiler); +begin + f_TS.Save(aFiler); +end; + +procedure Td2dTextPane.Scroll(aDelta: Single); +begin + ScrollShift := ScrollShift + aDelta; +end; + +procedure Td2dTextPane.ScrollTo(aTargetShift: Single; aScrollSpeed: Single; aWithMore: Boolean = False); +begin + if not (State in [tpsIdle, tpsInput]) then + Exit; + if Abs(aTargetShift - ScrollShift) > 1.0 then + begin + f_TargetShift := aTargetShift; + f_CurScrollSpeed := aScrollSpeed; + if f_TargetShift > ScrollShift then + begin + f_CurScrollDir := 1.0; + if aWithMore then + CalcMoreTargetShift; + end + else + f_CurScrollDir := -1.0; + f_StateBeforeScroll := State; + State := tpsScrolling; + UnHighlightLinks; + end; +end; + +procedure Td2dTextPane.ScrollToBottom; +begin + if f_DocRoot.Height - ScrollShift > Height then + ScrollTo(f_DocRoot.Height - Height, f_PanningSpeed, True); +end; + +procedure Td2dTextPane.StartInput(aFont: Id2dFont; aColor: Td2dColor; aParaType: Td2dTextAlignType); +var + l_Para: Td2dTextPara; + l_Chunk: Td2dStringChunk; +begin + // add chunk + if f_TS.Count > 0 then + begin + l_Para := f_TS.Paragraphs[f_TS.Count-1]; + if l_Para.IsClosed or (l_Para.ParaType <> aParaType) then + l_Para := nil; + end + else + l_Para := nil; + if l_Para = nil then + l_Para := f_TS.AddRawPara(aParaType); + l_Chunk := Td2dStringChunk.Create('', aFont, aColor); + l_Para.AddRawChunk(l_Chunk); + Format; + State := tpsInput; + LinksAllowed := False; +end; + +procedure Td2dTextPane.UnHighlightLinks; +begin + f_DocRoot.IterateLinks(_UnHighlightLink, f_ParaPossiblyWithLinks); + f_DocRoot.HighlightedLink := nil; +end; + +procedure Td2dTextPane._DeactivateLink(const aLinkSlice: Td2dLinkSlice); +begin + aLinkSlice.IsActive := False; +end; + +procedure Td2dTextPane._UnHighlightLink(const aLinkSlice: Td2dLinkSlice); +begin + aLinkSlice.IsHighlighted := False; +end; + +constructor Td2dTextPaneDocRoot.Create(aWidth: Single; aHeight: Single); +begin + inherited Create(aWidth); + f_VisibleHeight := aHeight; + f_TopVisible := -1; + f_BottomVisible := -1; +end; + +procedure Td2dTextPaneDocRoot.AddChild(aChild: Td2dCustomSlice); +begin + inherited; + RecalcBounds; +end; + +procedure Td2dTextPaneDocRoot.Clear; +begin + inherited Clear; + f_TopVisible := -1; + f_BottomVisible := -1; + f_VerticalShift := 0; +end; + +procedure Td2dTextPaneDocRoot.DeleteFrom(aIndex : Integer); +begin + inherited; + RecalcBounds; +end; + +procedure Td2dTextPaneDocRoot.DoDraw(X, Y: Single); +var + I : Integer; + l_Child: Td2dCustomSlice; + l_Shift: Single; +begin + if f_TopVisible >= 0 then + begin + l_Shift := Int(f_VerticalShift + 0.5); // round shift + for I := f_TopVisible to f_BottomVisible do + begin + l_Child := Children[I]; + l_Child.Draw(X, Y - l_Shift); + end; + end; +end; + +function Td2dTextPaneDocRoot.GetBottomChild: Integer; +begin + Result := f_BottomVisible; +end; + +function Td2dTextPaneDocRoot.GetTopChild: Integer; +begin + Result := f_TopVisible; +end; + +function Td2dTextPaneDocRoot.GetVerticalShift: Single; +begin + Result := f_VerticalShift; +end; + +procedure Td2dTextPaneDocRoot.pm_SetVerticalShift(const Value: Single); +begin + if f_VerticalShift <> Value then + begin + f_VerticalShift := Value; + RecalcBounds; + end; +end; + +procedure Td2dTextPaneDocRoot.pm_SetVisibleHeight(const Value: Single); +begin + if f_VisibleHeight <> Value then + begin + f_VisibleHeight := Value; + RecalcBounds; + end; +end; + +procedure Td2dTextPaneDocRoot.RecalcBounds; +var + I : Integer; + l_Child: Td2dCustomSlice; +begin + I := 0; + f_TopVisible := -1; + f_BottomVisible := -1; + while (I < ChildrenCount) and ((f_TopVisible < 0) or (f_BottomVisible < 0)) do + begin + l_Child := Children[I]; + if (f_TopVisible < 0) and (l_Child.Top+l_Child.Height > f_VerticalShift) then + f_TopVisible := I; + if (l_Child.Top > f_VerticalShift + f_VisibleHeight) then + f_BottomVisible := I - 1; + Inc(I); + end; + if f_BottomVisible < 0 then + f_BottomVisible := Pred(ChildrenCount); +end; + +end. diff --git a/d2dGUITypes.pas b/d2dGUITypes.pas new file mode 100644 index 0000000..e7989e0 --- /dev/null +++ b/d2dGUITypes.pas @@ -0,0 +1,44 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dGUITypes; + +interface +uses + d2dTypes; + +type + Td2dButtonState = (bsNormal, bsDisabled, bsFocused, bsPressed); + Td2dTextPaneState = (tpsIdle, tpsScrolling, tpsMore, tpsInput); + + Td2dButtonStates = set of Td2dButtonState; + + Td2dMenuVisualStyle = record + rBGColor: Td2dColor; + rBorderColor: Td2dColor; + rTextColor: Td2dColor; + rHIndent: Single; + rVIndent: Single; + rSelectionColor: Td2dColor; + rSelectedColor: Td2dColor; + rDisabledColor: Td2dColor; + end; + + Td2dAlign = (alLeftTop, alLeftBottom, alLeftCenter, + alRightTop, alRightBottom, alRightCenter, + alTopLeft, alTopRight, alTopCenter, + alBottomLeft, alBottomRight, alBottomCenter); + + + +implementation + +end. \ No newline at end of file diff --git a/d2dGUIUtils.pas b/d2dGUIUtils.pas new file mode 100644 index 0000000..18cdfb7 --- /dev/null +++ b/d2dGUIUtils.pas @@ -0,0 +1,98 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dGUIUtils; + +interface +uses + d2dTypes, + d2dGUITypes; + +// aMasterRect - is master rect which target rect is relative to +// aDim - dimensions (width and height) of the target rect +// returns the point of top left corner of the target rect +function D2DAlignRect(const aMasterRect: Td2dRect; const aDim: Td2dPoint; aAlign: Td2dAlign): Td2dPoint; +function D2DAlignToStr(const aAlign: Td2dAlign): string; +function D2DStrToAlign(aStr: string): Td2dAlign; + + +implementation +uses + d2dCore, SysUtils; + +const + c_AlignString : array [Td2dAlign] of string = ('LT','LB','LC','RT','RB','RC','TL','TR','TC','BL','BR','BC'); + +function D2DAlignRect(const aMasterRect: Td2dRect; const aDim: Td2dPoint; aAlign: Td2dAlign): Td2dPoint; +var + l_MasterHalf: Td2dPoint; +begin + l_MasterHalf.X := (aMasterRect.Right - aMasterRect.Left)/2; + l_MasterHalf.Y := (aMasterRect.Bottom - aMasterRect.Top)/2; + // initial relocation + case aAlign of + alLeftTop, alLeftBottom, alLeftCenter : Result.X := aMasterRect.Left - aDim.X - 1; + alRightTop, alRightBottom, alRightCenter : Result.X := aMasterRect.Right + 1; + alTopLeft, alTopRight, alTopCenter : Result.Y := aMasterRect.Top - aDim.Y - 1; + alBottomLeft, alBottomRight, alBottomCenter : Result.Y := aMasterRect.Bottom + 1; + end; + case aAlign of + alLeftTop, alRightTop : Result.Y := aMasterRect.Top; + alLeftBottom, alRightBottom : Result.Y := aMasterRect.Bottom - aDim.Y; + alLeftCenter, alRightCenter : Result.Y := aMasterRect.Top + l_MasterHalf.Y - (aDim.Y/2); + alTopLeft, alBottomLeft : Result.X := aMasterRect.Left; + alTopRight, alBottomRight : Result.X := aMasterRect.Right - aDim.X; + alTopCenter, alBottomCenter : Result.X := aMasterRect.Left + l_MasterHalf.X - (aDim.X/2); + end; + // now if we out of the screen because of MAIN relocation functor then we should flip result + // i.e. if alRightTop is out of the right edge of screen we should make it alLeftTop + if (aAlign in [alLeftTop, alLeftBottom, alLeftCenter]) and (Result.X < 0) then + Result.X := aMasterRect.Right + 1; + if (aAlign in [alRightTop, alRightBottom, alRightCenter]) and (Result.X + aDim.X > gD2DE.ScreenWidth) then + Result.X := aMasterRect.Left - aDim.X - 1; + if (aAlign in [alTopLeft, alTopRight, alTopCenter]) and (Result.Y < 0) then + Result.Y := aMasterRect.Bottom + 1; + if (aAlign in [alBottomLeft, alBottomRight, alBottomCenter]) and (Result.Y + aDim.Y > gD2DE.ScreenHeight) then + Result.Y := aMasterRect.Top - aDim.Y - 1; + // now is the final aligment by edges of the screen + if Result.X + aDim.X > gD2DE.ScreenWidth then + Result.X := gD2DE.ScreenWidth - aDim.X; + if Result.X < 0 then + Result.X := 0; + if Result.Y + aDim.Y > gD2DE.ScreenHeight then + Result.Y := gD2DE.ScreenHeight - aDim.Y; + if Result.Y < 0 then + Result.Y := 0; + Result.X := Round(Result.X); + Result.Y := Round(Result.Y); +end; + + +function D2DAlignToStr(const aAlign: Td2dAlign): string; +begin + Result := c_AlignString[aAlign]; +end; + +function D2DStrToAlign(aStr: string): Td2dAlign; +var + A: Td2dAlign; +begin + Result := alTopLeft; + aStr := UpperCase(aStr); + for A := alLeftTop to alBottomCenter do + if aStr = c_AlignString[A] then + begin + Result := A; + Break; + end; +end; + +end. \ No newline at end of file diff --git a/d2dInterfaces.pas b/d2dInterfaces.pas new file mode 100644 index 0000000..dc46230 --- /dev/null +++ b/d2dInterfaces.pas @@ -0,0 +1,93 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dInterfaces; + +interface +uses + Types, + Direct3D8, + d2dTypes; + +type + Id2dTexture = interface + ['{40AAA101-1A6D-4403-BF99-75DA1F195CA3}'] + function pm_GetDirectXTexture: IDirect3DTexture8; + function pm_GetSrcPicHeight: Integer; + function pm_GetSrcPicWidth: Integer; + procedure pm_SetSrcPicHeight(const Value: Integer); + procedure pm_SetSrcPicWidth(const Value: Integer); + function IsOrphan: Boolean; + property DirectXTexture: IDirect3DTexture8 read pm_GetDirectXTexture; + property SrcPicHeight: Integer read pm_GetSrcPicHeight write pm_SetSrcPicHeight; + property SrcPicWidth: Integer read pm_GetSrcPicWidth write pm_SetSrcPicWidth; + end; + + Id2dFont = interface + ['{07A0B812-9EE0-4196-9D4A-E67A889C65D9}'] + function pm_GetBlendMode: Integer; + function pm_GetColor: Longword; + procedure pm_SetBlendMode(const aValue: Integer); + procedure pm_SetColor(const aValue: Longword); + procedure DoSetBlendMode(aBlendMode: Integer); + procedure DoSetColor(aColor: Td2dColor); + function pm_GetHeight: Single; + procedure CalcSize(const aStr: string; var theSize: Td2dPoint; aLength: Integer = MaxInt); + function CalcStringBySize(const aStr: string; aWidth: Single; const aEllipsis: string = '...'): string; + function CanRenderChar(aChar: Char): Boolean; + function pm_GetID: string; + function pm_GetSize: Single; + procedure pm_SetID(const Value: string); + procedure Render(aX, aY: Single; aStr: string); + property BlendMode: Integer read pm_GetBlendMode write pm_SetBlendMode; + property Color: Longword read pm_GetColor write pm_SetColor; + property Height: Single read pm_GetHeight; + property ID: string read pm_GetID write pm_SetID; + property Size: Single read pm_GetSize; + end; + + Id2dFontProvider = interface + ['{4218D8D0-6544-435C-9B07-C25CEBD0AB16}'] + function GetByID(const anID: string): Id2dFont; + end; + + Id2dPicture = interface + ['{C5039CBF-F03C-4B4B-80C8-51B2A43E67EF}'] + function pm_GetHeight: Single; + function pm_GetID: string; + function pm_GetWidth: Single; + procedure Render(const aX, aY: Single); + property Height: Single read pm_GetHeight; + property ID: string read pm_GetID; + property Width: Single read pm_GetWidth; + end; + + Id2dPictureProvider = interface + ['{AC6FDDC7-7388-4CA9-A882-DB4B758FFAE2}'] + function GetByID(const anID: string): Id2dPicture; + end; + + Id2dResourcePack = interface + ['{319E44BB-6B66-4B84-9C4F-10339C8A383C}'] + function IndexOf(const aFileName: string): Integer; + function Find(aWildCard: string; aFrom: Integer = 0): Integer; + function Extract(I: integer; var aBuffer: Pointer; aSize: DWORD = 0): Boolean; + function pm_GetCount: Integer; + function pm_GetName(aIndex: Integer): AnsiString; + function pm_GetSize(aIndex: Integer): LongWord; + property Count: Integer read pm_GetCount; + property Name[aIndex: Integer]: AnsiString read pm_GetName; + property Size[aIndex: Integer]: LongWord read pm_GetSize; + end; + + +implementation +end. \ No newline at end of file diff --git a/d2dPath.pas b/d2dPath.pas new file mode 100644 index 0000000..a3904e9 --- /dev/null +++ b/d2dPath.pas @@ -0,0 +1,177 @@ +unit d2dPath; + +interface +uses + Classes + ; + +type + + Td2dCommonPath = class + private + f_SegmentsBuilt: Boolean; + f_TotalLength: Single; + procedure CalcTotalLength; + protected + f_Points: TList; + f_Segments: Tlist; + procedure AddSegment(aX1, aY1, aX2, aY2: Single); + procedure ClearPoints; + procedure ClearSegments; + procedure DoRecalcSegments; virtual; + procedure CheckSegments; + public + constructor Create; + destructor Destroy; override; + function AddPoint(const aX, aY: Single): Integer; + procedure Clear; + procedure Recalc; + property TotalLength: Single read f_TotalLength; + end; + + Td2dPathCursor = class + private + f_Path: Td2dCommonPath; + f_Speed: Single; + public + constructor Create(aPath: Td2dCommonPath; aSpeed: Single); + end; + +implementation +uses + SysUtils, + Math, + + d2dTypes; + +type + //1 path segment descriptor + Pd2dPathSegment = ^Td2dPathSegment; + Td2dPathSegment = record + X : Single; + Y : Single; + DX : Single; + DY : Single; + Len : Single; + Angle : Single; + end; + +constructor Td2dCommonPath.Create; +begin + inherited; + f_Points := TList.Create; + f_Segments := TList.Create; + f_SegmentsBuilt := False; + f_TotalLength := 0; +end; + +destructor Td2dCommonPath.Destroy; +begin + Clear; + FreeAndNil(f_Points); + FreeAndNil(f_Segments); + inherited; +end; + +function Td2dCommonPath.AddPoint(const aX, aY: Single): Integer; +var + l_New: Pointer; +begin + GetMem(l_New, SizeOf(Td2dPoint)); + Result := f_Points.Add(l_New); + f_SegmentsBuilt := False; +end; + +procedure Td2dCommonPath.AddSegment(aX1, aY1, aX2, aY2: Single); +var + l_Seg: Pd2dPathSegment; +begin + GetMem(l_Seg, SizeOf(Td2dPathSegment)); + with l_Seg^ do + begin + X := aX1; + Y := aY2; + DX := aX2-aX1; + DY := aY2-aY1; + Len := Sqrt(DX*DX+DY*DY); + Angle := ArcCos(DY/Len); + if DX < 0 then + Angle := 2*Pi-Angle; + end; + f_Segments.Add(l_Seg); +end; + +procedure Td2dCommonPath.CalcTotalLength; +var + I: Integer; +begin + f_TotalLength := 0; + for I := 0 to Pred(f_Segments.Count) do + f_TotalLength := f_TotalLength + Pd2dPathSegment(f_Segments[I]).Len; +end; + +procedure Td2dCommonPath.ClearPoints; +var + I : Integer; +begin + for I := 0 to Pred(f_Points.Count) do + FreeMem(f_Points.Items[I], SizeOf(Td2dPoint)); + f_Points.Clear; + f_SegmentsBuilt := False; +end; + +procedure Td2dCommonPath.ClearSegments; +var + I : Integer; +begin + for I := 0 to Pred(f_Segments.Count) do + FreeMem(f_Segments.Items[I], SizeOf(Td2dPathSegment)); + f_Segments.Clear; + f_SegmentsBuilt := False; +end; + +procedure Td2dCommonPath.DoRecalcSegments; +var + I: Integer; + P1, P2: Pd2dPoint; +begin + for I := 1 to Pred(f_Points.Count) do + begin + P1 := f_Points.Items[I-1]; + P2 := f_Points.Items[I]; + AddSegment(P1.X, P1.Y, P2.X, P2.Y); + end; +end; + +procedure Td2dCommonPath.CheckSegments; +begin + if f_SegmentsBuilt then + Exit; + if f_Points.Count < 2 then + Exit; + ClearSegments; + DoRecalcSegments; + CalcTotalLength; + f_SegmentsBuilt := True; +end; + +procedure Td2dCommonPath.Clear; +begin + ClearPoints; + ClearSegments; +end; + +procedure Td2dCommonPath.Recalc; +begin + f_SegmentsBuilt := False; + CheckSegments; +end; + +constructor Td2dPathCursor.Create(aPath: Td2dCommonPath; aSpeed: Single); +begin + inherited Create; + f_Path := aPath; + f_Speed := aSpeed; +end; + +end. diff --git a/d2dSimplePicture.pas b/d2dSimplePicture.pas new file mode 100644 index 0000000..72f873a --- /dev/null +++ b/d2dSimplePicture.pas @@ -0,0 +1,84 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- + +unit d2dSimplePicture; + +interface +uses + d2dTypes, + d2dInterfaces, + d2dClasses, + d2dSprite; + +type + Td2dSimplePicture = class(Td2dProtoObject, Id2dPicture) + private + f_ID: string; + f_Sprite: Td2dSprite; + function pm_GetHeight: Single; + function pm_GetID: string; + function pm_GetWidth: Single; + procedure Render(const aX, aY: Single); + protected + procedure Cleanup; override; + public + class function Make(const anID: string; const aTex: Id2dTexture; aX, aY, aWidth, aHeight: Integer; aColor: Td2dColor): Id2dPicture; + end; + + +implementation +uses + SysUtils; + +procedure Td2dSimplePicture.Cleanup; +begin + FreeAndNil(f_Sprite); + inherited; +end; + +class function Td2dSimplePicture.Make(const anID: string; const aTex: Id2dTexture; aX, aY, aWidth, aHeight: Integer; + aColor: Td2dColor): Id2dPicture; +var + l_Pic: Td2dSimplePicture; +begin + l_Pic := Td2dSimplePicture.Create; + try + l_Pic.f_ID := anID; + l_Pic.f_Sprite := Td2dSprite.Create(aTex, aX, aY, aWidth, aHeight); + l_Pic.f_Sprite.SetColor(aColor); + Result := l_Pic; + finally + FreeAndNil(l_Pic); + end; +end; + +function Td2dSimplePicture.pm_GetHeight: Single; +begin + Result := f_Sprite.Height; +end; + +function Td2dSimplePicture.pm_GetID: string; +begin + Result := f_ID; +end; + +function Td2dSimplePicture.pm_GetWidth: Single; +begin + Result := f_Sprite.Width; +end; + +procedure Td2dSimplePicture.Render(const aX, aY: Single); +begin + f_Sprite.Render(aX, aY); +end; + +end. \ No newline at end of file diff --git a/d2dSprite.pas b/d2dSprite.pas new file mode 100644 index 0000000..b60c263 --- /dev/null +++ b/d2dSprite.pas @@ -0,0 +1,537 @@ +unit d2dSprite; + +interface +uses + d2dTypes, + d2dInterfaces, + d2dClasses; + +type + Td2dSprite = class(Td2dProtoObject) + private + procedure DoFlipX; + procedure DoFlipY; + function pm_GetBlendMode: Integer; + function pm_GetHeight: Single; + function pm_GetWidth: Single; + procedure pm_SetBlendMode(const Value: Integer); + procedure pm_SetFlipX(const Value: Boolean); + procedure pm_SetFlipY(const Value: Boolean); + procedure pm_SetHeight(const Value: Single); + procedure pm_SetTexture(const Value: Id2dTexture); + procedure pm_SetWidth(const Value: Single); + protected + f_FlipX: Boolean; + f_FlipY: Boolean; + f_Height: Single; + f_HotX: Integer; + f_HotY: Integer; + f_Quad: Td2dQuad; + f_Texture: Id2dTexture; + f_TexX, f_TexY: Single; + f_TexWidth, f_TexHeight: Single; + f_Width: Single; + function pm_GetValid: Boolean; virtual; + procedure RecalcQuad; + public + constructor Create(aTex: Id2dTexture; aTexX, aTexY: Integer; aWidth, aHeight: Integer); + procedure Render(const aX, aY: Single); + procedure RenderEx(const aX, aY: Single; aRot: Single; aHScale: Single = 1; aVScale: Single = 0); + procedure SetColor(aColor: Longword; aVertex: Integer = -1); + procedure SetZ(aZ: Single; aVertex: Integer = -1); + property BlendMode: Integer read pm_GetBlendMode write pm_SetBlendMode; + property FlipX: Boolean read f_FlipX write pm_SetFlipX; + property FlipY: Boolean read f_FlipY write pm_SetFlipY; + property Height: Single read pm_GetHeight write pm_SetHeight; + property HotX: Integer read f_HotX write f_HotX; + property HotY: Integer read f_HotY write f_HotY; + property Texture: Id2dTexture read f_Texture write pm_SetTexture; + property Valid: Boolean read pm_GetValid; + property Width: Single read pm_GetWidth write pm_SetWidth; + end; + + Td2dMultiFrameSprite = class(Td2dSprite) + private + procedure pm_SetCurFrame(const Value: Integer); + protected + f_CurFrame: Integer; + f_FramesCount: Integer; + public + constructor Create(aTex: Id2dTexture; aNFrames: Integer; aTx, aTy, aWidth, aHeight: Integer); + property CurFrame: Integer read f_CurFrame write pm_SetCurFrame; + property FramesCount: Integer read f_FramesCount; + end; + + Td2dBaseAnimation = class(Td2dMultiFrameSprite) + private + protected + f_Playing: Boolean; + procedure DoUpdate(aDeltaTime: Single); virtual; abstract; + public + constructor Create(aTex: Id2dTexture; aNFrames: Integer; aTx, aTy, aWidth, aHeight: Integer); + procedure Play; virtual; + procedure Resume; virtual; + procedure Stop; virtual; + procedure Update(aDeltaTime: Single); + property Playing: Boolean read f_Playing write f_Playing; + end; + + Td2dLoopType = (lt_PingPong, lt_OverAgain); + + Td2dTimedAnimation = class(Td2dBaseAnimation) + private + f_DFrame: Integer; + f_Looped: Boolean; + f_LoopType: Td2dLoopType; + f_Speed: Single; + f_TimeSinceLastFrame: Double; + function pm_GetReverse: Boolean; + procedure pm_SetReverse(const aValue: Boolean); + protected + procedure DoUpdate(aDeltaTime: Single); override; + public + constructor Create(aTex: Id2dTexture; aNFrames: Integer; aTx, aTy, aWidth, + aHeight: Integer); + procedure Play; override; + property Looped: Boolean read f_Looped write f_Looped; + property LoopType: Td2dLoopType read f_LoopType write f_LoopType; + property Reverse: Boolean read pm_GetReverse write pm_SetReverse; + property Speed: Single read f_Speed write f_Speed; + property TimeSinceLastFrame: Double read f_TimeSinceLastFrame write + f_TimeSinceLastFrame; + end; + + Td2dRectangle = class(Td2dSprite) + protected + function pm_GetValid: Boolean; override; + public + constructor Create(const aWidth, aHeight: Integer; const aColor: Td2dColor); + end; + + + +implementation +uses + d2dCore; + +constructor Td2dSprite.Create(aTex: Id2dTexture; aTexX, aTexY: Integer; aWidth, aHeight: Integer); +begin + inherited Create; + f_TexX := aTexX; + f_TexY := aTexY; + f_Width := aWidth; + f_Height := aHeight; + Texture := aTex; + SetColor($FFFFFFFF); +end; + +procedure Td2dSprite.DoFlipX; +var + l_TX: Single; + l_TY: Single; +begin + with f_Quad do + begin + l_TX := V[0].TX; V[0].TX := V[1].TX; V[1].TX := l_TX; + l_TY := V[0].TY; V[0].TY := V[1].TY; V[1].TY := l_TY; + l_TX := V[3].TX; V[3].TX := V[2].TX; V[2].TX := l_TX; + l_TY := V[3].TY; V[3].TY := V[2].TY; V[2].TY := l_TY; + end; +end; + +procedure Td2dSprite.DoFlipY; +var + l_TX: Single; + l_TY: Single; +begin + with f_Quad do + begin + l_TX := V[0].TX; V[0].TX := V[3].TX; V[3].TX := l_TX; + l_TY := V[0].TY; V[0].TY := V[3].TY; V[3].TY := l_TY; + l_TX := V[1].TX; V[1].TX := V[2].TX; V[2].TX := l_TX; + l_TY := V[1].TY; V[1].TY := V[2].TY; V[2].TY := l_TY; + end; +end; + +function Td2dSprite.pm_GetBlendMode: Integer; +begin + Result := f_Quad.Blend; +end; + +function Td2dSprite.pm_GetHeight: Single; +begin + Result := f_Height; +end; + +function Td2dSprite.pm_GetValid: Boolean; +begin + Result := f_Texture <> nil; +end; + +function Td2dSprite.pm_GetWidth: Single; +begin + Result := f_Width; +end; + +procedure Td2dSprite.pm_SetBlendMode(const Value: Integer); +begin + f_Quad.Blend := Value; +end; + +procedure Td2dSprite.pm_SetFlipX(const Value: Boolean); +begin + if f_FlipX <> Value then + begin + DoFlipX; + f_FlipX := not f_FlipX; + end; +end; + +procedure Td2dSprite.pm_SetFlipY(const Value: Boolean); +begin + if f_FlipY <> Value then + begin + DoFlipY; + f_FlipY := not f_FlipY; + end; +end; + +procedure Td2dSprite.pm_SetHeight(const Value: Single); +begin + if Value <> f_Height then + begin + f_Height := Value; + RecalcQuad; + end; +end; + +procedure Td2dSprite.pm_SetTexture(const Value: Id2dTexture); +begin + f_Texture := Value; + RecalcQuad; +end; + +procedure Td2dSprite.pm_SetWidth(const Value: Single); +begin + if Value <> f_Width then + begin + f_Width := Value; + RecalcQuad; + end; +end; + +procedure Td2dSprite.RecalcQuad; +var + l_TexX1, l_TexY1, l_TexX2, l_TexY2: Single; +begin + if f_Texture <> nil then + begin + f_TexWidth := gD2DE.Texture_GetWidth(f_Texture); + f_TexHeight := gD2DE.Texture_GetHeight(f_Texture); + end + else + begin + f_TexWidth := 1.0; + f_TexHeight := 1.0; + end; + + if f_Texture <> nil then + f_Quad.Tex := f_Texture.DirectXTexture + else + f_Quad.Tex := nil; + + l_TexX1 := f_TexX / f_TexWidth; + l_TexY1 := f_TexY / f_TexHeight; + l_TexX2 := (f_TexX + f_Width) / f_TexWidth; + l_TexY2 := (f_TexY + f_Height) / f_TexHeight; + + with f_Quad do + begin + V[0].TX := l_TexX1; V[0].TY := l_TexY1; V[0].Z := 0.5; + V[1].TX := l_TexX2; V[1].TY := l_TexY1; V[1].Z := 0.5; + V[2].TX := l_TexX2; V[2].TY := l_TexY2; V[2].Z := 0.5; + V[3].TX := l_TexX1; V[3].TY := l_TexY2; V[3].Z := 0.5; + Blend := BLEND_DEFAULT; + end; + + if f_FlipX then + DoFlipX; + if f_FlipY then + DoFlipY; +end; + +procedure Td2dSprite.Render(const aX, aY: Single); +var + l_X1, l_X2, l_Y1, l_Y2: Single; +begin + if not Valid then + Exit; + l_X1 := aX - f_HotX; + l_Y1 := aY - f_HotY; + l_X2 := aX + f_Width - f_HotX; + l_Y2 := aY + f_Height - f_HotY; + with f_Quad do + begin + V[0].X := l_X1; V[0].Y := l_Y1; + V[1].X := l_X2; V[1].Y := l_Y1; + V[2].X := l_X2; V[2].Y := l_Y2; + V[3].X := l_X1; V[3].Y := l_Y2; + end; + gD2DE.Gfx_RenderQuad(f_Quad); +end; + +procedure Td2dSprite.RenderEx(const aX, aY: Single; aRot: Single; aHScale: Single = 1; aVScale: Single = 0); +var + l_TX1, l_TX2, l_TY1, l_TY2: Single; + l_Cos, l_Sin: Single; +begin + if not Valid then + Exit; + if aVScale = 0 then + aVScale := aHScale; + l_TX1 := -f_HotX * aHScale; + l_TY1 := -f_HotY * aVScale; + l_TX2 := (f_Width - f_HotX)*aHScale; + l_TY2 := (f_Height - f_HotY)*aVScale; + + if aRot <> 0.0 then + begin + l_Cos := Cos(aRot); + l_Sin := Sin(aRot); + + with f_Quad do + begin + V[0].X := l_TX1*l_Cos - l_TY1*l_Sin + aX; + V[0].Y := l_TX1*l_Sin + l_TY1*l_Cos + aY; + + V[1].X := l_TX2*l_Cos - l_TY1*l_Sin + aX; + V[1].Y := l_TX2*l_Sin + l_TY1*l_Cos + aY; + + V[2].X := l_TX2*l_Cos - l_TY2*l_Sin + aX; + V[2].Y := l_TX2*l_Sin + l_TY2*l_Cos + aY; + + V[3].X := l_TX1*l_Cos - l_TY2*l_Sin + aX; + V[3].Y := l_TX1*l_Sin + l_TY2*l_Cos + aY; + end; + end + else + with f_Quad do + begin + V[0].X := l_TX1 + aX; V[0].Y := l_TY1 + aY; + V[1].X := l_TX2 + aX; V[1].Y := l_TY1 + aY; + V[2].X := l_TX2 + aX; V[2].Y := l_TY2 + aY; + V[3].X := l_TX1 + aX; V[3].Y := l_TY2 + aY; + end; + gD2DE.Gfx_RenderQuad(f_Quad); +end; + +procedure Td2dSprite.SetColor(aColor: Longword; aVertex: Integer = -1); +begin + if (aVertex > -1) and (aVertex < 4) then + f_Quad.V[aVertex].Col := aColor + else + with f_Quad do + begin + V[0].Col := aColor; + V[1].Col := aColor; + V[2].Col := aColor; + V[3].Col := aColor; + end; +end; + +procedure Td2dSprite.SetZ(aZ: Single; aVertex: Integer = -1); +begin + if (aVertex > -1) and (aVertex < 4) then + f_Quad.V[aVertex].Z := aZ + else + with f_Quad do + begin + V[0].Z := aZ; + V[1].Z := aZ; + V[2].Z := aZ; + V[3].Z := aZ; + end; +end; + +constructor Td2dBaseAnimation.Create(aTex: Id2dTexture; aNFrames: Integer; aTx, aTy, aWidth, aHeight: Integer); +begin + inherited Create(aTex, aNFrames, aTx, aTy, aWidth, aHeight); + f_Playing := False; +end; + +procedure Td2dBaseAnimation.Play; +begin + f_Playing := True; +end; + +procedure Td2dBaseAnimation.Resume; +begin + f_Playing := True; +end; + +procedure Td2dBaseAnimation.Stop; +begin + f_Playing := False; +end; + +procedure Td2dBaseAnimation.Update(aDeltaTime: Single); +begin + if f_Playing then + DoUpdate(aDeltaTime); +end; + +constructor Td2dTimedAnimation.Create(aTex: Id2dTexture; aNFrames: Integer; + aTx, aTy, aWidth, aHeight: Integer); +begin + inherited; + f_Looped := True; + f_LoopType := lt_OverAgain; + f_Speed := 0.25; + Reverse := False; +end; + +procedure Td2dTimedAnimation.DoUpdate(aDeltaTime: Single); +var + l_IncFrames: Integer; + l_TargetFrame: Integer; +begin + if f_TimeSinceLastFrame < 0 then + f_TimeSinceLastFrame := 0 + else + f_TimeSinceLastFrame := f_TimeSinceLastFrame + aDeltaTime; + + l_IncFrames := Trunc(f_TimeSinceLastFrame / f_Speed); + if l_IncFrames > 0 then + begin + f_TimeSinceLastFrame := f_TimeSinceLastFrame - (f_Speed * l_IncFrames); + l_TargetFrame := f_CurFrame + (f_DFrame * l_IncFrames); + if l_TargetFrame > f_FramesCount-1 then + begin + if f_Looped then + begin + if f_LoopType = lt_PingPong then + begin + f_DFrame := -1; + l_TargetFrame := f_FramesCount + l_TargetFrame - 1; + end; + end + else + begin + l_TargetFrame := f_FramesCount-1; + f_Playing := False; + end; + end + else + if l_TargetFrame < 0 then + begin + if f_Looped then + begin + if f_LoopType = lt_PingPong then + begin + f_DFrame := 1; + l_TargetFrame := -l_TargetFrame; + end; + end + else + begin + l_TargetFrame := 0; + f_Playing := False; + end; + end; + CurFrame := l_TargetFrame; + end; +end; + +procedure Td2dTimedAnimation.pm_SetReverse(const aValue: Boolean); +begin + if aValue then + f_DFrame := -1 + else + f_DFrame := 1; +end; + +procedure Td2dTimedAnimation.Play; +begin + inherited; + f_TimeSinceLastFrame := -1; + if Reverse then + CurFrame := f_FramesCount-1 + else + CurFrame := 0; +end; + +function Td2dTimedAnimation.pm_GetReverse: Boolean; +begin + Result := f_DFrame < 0; +end; + +constructor Td2dMultiFrameSprite.Create(aTex: Id2dTexture; aNFrames: Integer; aTx, aTy, aWidth, aHeight: Integer); +begin + inherited Create(aTex, aTx, aTy, aWidth, aHeight); + f_FramesCount := aNFrames; + f_CurFrame := 0; +end; + +procedure Td2dMultiFrameSprite.pm_SetCurFrame(const Value: Integer); +var + l_Tx1, l_Tx2, l_Ty1, l_Ty2: Single; + l_CurFlipX, l_CurFlipY: Boolean; + l_NumCols: Integer; + l_TargetFrame: Integer; +begin + if f_CurFrame <> Value then + begin + l_CurFlipX := f_FlipX; + l_CurFlipY := f_FlipY; + l_NumCols := Trunc((f_Texture.SrcPicWidth - f_TexX) / f_Width); + if l_NumCols <= 0 then + l_NumCols := 1; + l_TargetFrame := Value mod f_FramesCount; + if l_TargetFrame < 0 then + l_TargetFrame := f_FramesCount + l_TargetFrame; + f_CurFrame := l_TargetFrame; + + l_Ty1 := f_TexY; + l_Tx1 := f_TexX + l_TargetFrame * f_Width; + if (l_Tx1 > f_Texture.SrcPicWidth - f_Width) then + begin + l_Tx1 := f_TexX + (l_TargetFrame mod l_NumCols) * f_Width; + l_Ty1 := l_Ty1 + (l_TargetFrame div l_NumCols) * f_Height; + end; + + l_Tx2 := l_Tx1 + f_Width; + l_Ty2 := l_Ty1 + f_Height; + + l_Tx1 := l_Tx1 / f_TexWidth; + l_Tx2 := l_Tx2 / f_TexWidth; + l_Ty1 := l_Ty1 / f_TexHeight; + l_Ty2 := l_Ty2 / f_TexHeight; + + with f_Quad do + begin + V[0].TX := l_Tx1; V[0].TY := l_Ty1; + V[1].TX := l_Tx2; V[1].TY := l_Ty1; + V[2].TX := l_Tx2; V[2].TY := l_Ty2; + V[3].TX := l_Tx1; V[3].TY := l_Ty2; + end; + + // restoring flips + f_FlipX := False; + f_FlipY := False; + FlipX := l_CurFlipX; + FlipY := l_CurFlipY; + end; +end; + +constructor Td2dRectangle.Create(const aWidth, aHeight: Integer; const aColor: Td2dColor); +begin + inherited Create(nil, 0, 0, aWidth, aHeight); + SetColor(aColor); +end; + +function Td2dRectangle.pm_GetValid: Boolean; +begin + Result := True; +end; + +end. diff --git a/d2dStaticText.pas b/d2dStaticText.pas new file mode 100644 index 0000000..deca5fc --- /dev/null +++ b/d2dStaticText.pas @@ -0,0 +1,397 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dStaticText; + +interface +uses + d2dTypes, + d2dInterfaces, + d2dFormattedText, + d2dDocRoot; + +type + Td2dStaticText = class(TInterfacedObject, Id2dTextAddingTool) + private + f_Align: Td2dTextAlignType; + f_AutoWidth: Boolean; + f_TextColor: Td2dColor; + f_Font: Id2dFont; + f_Formatter: Td2dFormatter; + f_Root: Td2dDocRoot; + f_Text: string; + f_LinkColor: Td2dColor; + f_LinkHColor: Td2dColor; + f_OnLinkClick: Td2dOnLinkClickEvent; + f_TextSource: Td2dTextSource; + f_X: Single; + f_Y: Single; + function pm_GetHeight: Single; + function pm_GetLineSpacing: Single; + function pm_GetParaSpacing: Single; + function pm_GetWidth: Single; + procedure pm_SetAlign(const Value: Td2dTextAlignType); + procedure pm_SetTextColor(const Value: Td2dColor); + procedure pm_SetFont(const Value: Id2dFont); + procedure pm_SetLineSpacing(const Value: Single); + procedure pm_SetParaSpacing(const Value: Single); + procedure pm_SetText(const Value: string); + procedure pm_SetWidth(const Value: Single); + procedure ApplyColors; + procedure ApplyFont; + procedure ReFormat; + procedure _ApplyChunkColors(const aChunk: Td2dCustomTextChunk); + procedure _ApplyFontToChunk(const aChunk: Td2dCustomTextChunk); + procedure _ApplySliceColors(const aSlice: Td2dCustomSlice); + // + procedure AddText(const aText: string); + procedure AddLink(const aText, aTarget: string); + procedure CheckAutowidth; + function pm_GetLinksEnabled: Boolean; + procedure pm_SetAutoWidth(const Value: Boolean); + procedure pm_SetLinkColor(const Value: Td2dColor); + procedure pm_SetLinkHColor(const Value: Td2dColor); + procedure pm_SetLinksEnabled(const Value: Boolean); + procedure pm_SetX(const Value: Single); + procedure pm_SetY(const Value: Single); + procedure RenewLinkState; + public + constructor Create(const aFont: Id2dFont; const aColor: Td2dColor); + destructor Destroy; override; + procedure EndText; + procedure Render; + procedure ProcessEvent(var theEvent: Td2dInputEvent); + procedure StartText; + property Align: Td2dTextAlignType read f_Align write pm_SetAlign; + property AutoWidth: Boolean read f_AutoWidth write pm_SetAutoWidth; + property TextColor: Td2dColor read f_TextColor write pm_SetTextColor; + property Font: Id2dFont read f_Font write pm_SetFont; + property Height: Single read pm_GetHeight; + property LineSpacing: Single read pm_GetLineSpacing write pm_SetLineSpacing; + property ParaSpacing: Single read pm_GetParaSpacing write pm_SetParaSpacing; + property Text: string read f_Text write pm_SetText; + property LinkColor: Td2dColor read f_LinkColor write pm_SetLinkColor; + property LinkHColor: Td2dColor read f_LinkHColor write pm_SetLinkHColor; + property LinksEnabled: Boolean read pm_GetLinksEnabled write pm_SetLinksEnabled; + property Width: Single read pm_GetWidth write pm_SetWidth; + property X: Single read f_X write pm_SetX; + property Y: Single read f_Y write pm_SetY; + property OnLinkClick: Td2dOnLinkClickEvent read f_OnLinkClick write f_OnLinkClick; + end; + +implementation +uses + SysUtils, + + d2dCore, + d2dUtils; + +const + c_BadLinkColor = $FFB52929; + c_BadLinkHColor = $FFFF3A3A; + +constructor Td2dStaticText.Create(const aFont: Id2dFont; const aColor: Td2dColor); +begin + inherited Create; + f_TextSource := Td2dTextSource.Create; + f_Root := Td2dDocRoot.Create(0); + f_Formatter := Td2dFormatter.Create(f_TextSource, f_Root); + f_TextColor := aColor; + f_LinkColor := $FF0019FF; + f_LinkHColor := $FF4F60FF; + f_Font := aFont; + f_AutoWidth := True; +end; + +destructor Td2dStaticText.Destroy; +begin + FreeAndNil(f_Formatter); + FreeAndNil(f_TextSource); + FreeAndNil(f_Root); + inherited; +end; + +procedure Td2dStaticText.AddLink(const aText, aTarget: string); +begin + f_Text := f_Text + aText; + if aTarget <> '' then + f_TextSource.AddLink(aText, aTarget, f_Font, f_TextColor, f_LinkColor, f_LinkHColor, f_Align) + else + f_TextSource.AddLink(aText, aTarget, f_Font, f_TextColor, c_BadLinkColor, c_BadLinkHColor, f_Align) +end; + +procedure Td2dStaticText.AddText(const aText: string); +begin + f_Text := f_Text + aText; + f_TextSource.AddText(aText, f_Font, f_TextColor, f_Align); +end; + +procedure Td2dStaticText.ApplyColors; +begin + f_TextSource.IterateChunks(_ApplyChunkColors); + f_Root.IterateLeafSlices(_ApplySliceColors); +end; + +procedure Td2dStaticText.ApplyFont; +begin + f_TextSource.IterateChunks(_ApplyFontToChunk); + ReFormat; +end; + +procedure Td2dStaticText.CheckAutowidth; +var + l_Size: Td2dPoint; +begin + if f_AutoWidth then + begin + f_Font.CalcSize(f_Text, l_Size); + f_Root.Width := l_Size.X; + end; +end; + +procedure Td2dStaticText.EndText; +begin + CheckAutowidth; + ReFormat; +end; + +function Td2dStaticText.pm_GetHeight: Single; +begin + Result := f_Root.Height; +end; + +function Td2dStaticText.pm_GetLineSpacing: Single; +begin + Result := f_Formatter.FormatParams.rLineSpacing; +end; + +function Td2dStaticText.pm_GetLinksEnabled: Boolean; +begin + Result := f_Root.LinksAllowed; +end; + +function Td2dStaticText.pm_GetParaSpacing: Single; +begin + Result := f_Formatter.FormatParams.rParaSpacing; +end; + +function Td2dStaticText.pm_GetWidth: Single; +begin + Result := f_Root.Width; +end; + +procedure Td2dStaticText.pm_SetAlign(const Value: Td2dTextAlignType); +var + I: Integer; +begin + if f_Align <> Value then + begin + f_Align := Value; + with f_TextSource do + for I := 0 to Count - 1 do + Paragraphs[I].ParaType := f_Align; + ReFormat; + end; +end; + +procedure Td2dStaticText.pm_SetAutoWidth(const Value: Boolean); +begin + f_AutoWidth := Value; + if f_AutoWidth then + begin + CheckAutowidth; + ReFormat; + end; +end; + +procedure Td2dStaticText.pm_SetTextColor(const Value: Td2dColor); +begin + if f_TextColor <> Value then + begin + f_TextColor := Value; + ApplyColors; + end; +end; + +procedure Td2dStaticText.pm_SetFont(const Value: Id2dFont); +begin + if f_Font <> Value then + begin + f_Font := Value; + ApplyFont; + end; +end; + +procedure Td2dStaticText.pm_SetLineSpacing(const Value: Single); +var + l_FP: Td2dFormatParamsRec; +begin + l_FP := f_Formatter.FormatParams; + l_FP.rLineSpacing := Value; + f_Formatter.FormatParams := l_FP; + ReFormat; +end; + +procedure Td2dStaticText.pm_SetParaSpacing(const Value: Single); +var + l_FP: Td2dFormatParamsRec; +begin + l_FP := f_Formatter.FormatParams; + l_FP.rParaSpacing := Value; + f_Formatter.FormatParams := l_FP; + ReFormat; +end; + +procedure Td2dStaticText.pm_SetText(const Value: string); +begin + if f_Text <> Value then + begin + StartText; + try + AddText(Value); + finally + EndText; + end; + end; +end; + +procedure Td2dStaticText.pm_SetLinkColor(const Value: Td2dColor); +begin + if f_LinkColor <> Value then + begin + f_LinkColor := Value; + ApplyColors; + end; +end; + +procedure Td2dStaticText.pm_SetLinkHColor(const Value: Td2dColor); +begin + if f_LinkHColor <> Value then + begin + f_LinkHColor := Value; + ApplyColors; + end; +end; + +procedure Td2dStaticText.pm_SetLinksEnabled(const Value: Boolean); +begin + if Value <> f_Root.LinksAllowed then + begin + f_Root.LinksAllowed := Value; + f_Root.DropLinkHighlight; + f_Root.DropLinkCache; + if Value then + gD2DE.Input_TouchMousePos; + end; + +end; + +procedure Td2dStaticText.pm_SetWidth(const Value: Single); +begin + if (not f_AutoWidth) and (Width <> Value) then + begin + f_Root.Width := Value; + ReFormat; + end; +end; + +procedure Td2dStaticText.pm_SetX(const Value: Single); +begin + if f_X <> Value then + begin + f_X := Value; + RenewLinkState; + end; +end; + +procedure Td2dStaticText.pm_SetY(const Value: Single); +begin + if f_Y <> Value then + begin + f_Y := Value; + RenewLinkState; + end; +end; + +procedure Td2dStaticText.ProcessEvent(var theEvent: Td2dInputEvent); +begin + if (theEvent.EventType = INPUT_MOUSEMOVE) and LinksEnabled then + begin + f_Root.FindLinkHighlight(X, Y, theEvent); + end; + if (theEvent.EventType = INPUT_MBUTTONDOWN) and (theEvent.KeyCode = D2DK_LBUTTON) and (f_Root.HighlightedLink <> nil) then + begin + if Assigned(f_OnLinkClick) then + f_OnLinkClick(Self, D2DMoveRect(f_Root.HighlightedLink.Rect, X, Y), f_Root.HighlightedLink.Slice.Target); + Processed(theEvent); + end; +end; + +procedure Td2dStaticText.ReFormat; +begin + f_Formatter.ReformatAll; + f_Root.DropLinkCache; + f_Root.ForceLinkAllowance; +end; + +procedure Td2dStaticText.Render; +begin + f_Root.Draw(f_X, f_Y); +end; + +procedure Td2dStaticText.RenewLinkState; +begin + f_Root.DropLinkCache; + gD2DE.Input_TouchMousePos; +end; + +procedure Td2dStaticText.StartText; +begin + f_Root.Clear; + f_TextSource.Clear; + f_Text := ''; +end; + +procedure Td2dStaticText._ApplyChunkColors(const aChunk: Td2dCustomTextChunk); +begin + if aChunk.ChunkType in [ctText, ctLink] then + begin + Td2dStringChunk(aChunk).Color := f_TextColor; + if aChunk.ChunkType = ctLink then + begin + Td2dLinkChunk(aChunk).LinkColor := f_LinkColor; + Td2dLinkChunk(aChunk).HighlightColor := f_LinkHColor; + end; + end; +end; + +procedure Td2dStaticText._ApplyFontToChunk(const aChunk: Td2dCustomTextChunk); +begin + if aChunk.ChunkType in [ctText, ctLink] then + begin + Td2dStringChunk(aChunk).Font := f_Font; + end; +end; + +procedure Td2dStaticText._ApplySliceColors(const aSlice: Td2dCustomSlice); +begin + if aSlice.SliceType in [stText, stLink] then + begin + Td2dTextSlice(aSlice).Color := f_TextColor; + if aSlice.SliceType = stLink then + begin + Td2dLinkSlice(aSlice).LinkColor := f_LinkColor; + Td2dLinkSlice(aSlice).HighlightColor := f_LinkHColor; + end; + end; +end; + +end. \ No newline at end of file diff --git a/d2dStorage.pas b/d2dStorage.pas new file mode 100644 index 0000000..85365a9 --- /dev/null +++ b/d2dStorage.pas @@ -0,0 +1,177 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dStorage; + +interface +uses + Classes, + IniFiles, + Registry; + +type + Td2dSetupLocation = (slInifile, slRegistry); + + Id2dStorage = interface(IInterface) + ['{839C0B95-962B-4379-8FE7-92BDB53BD125}'] + function GetInteger(Key: PChar; aDefault: Integer): Integer; + procedure SetInteger(Key: PChar; aValue: Integer); + function GetFloat(Key: PChar; aDefault: Double): Double; + procedure SetFloat(Key: PChar; aValue: Double); + function GetString(Key: PChar; aDefault: string): string; + procedure SetString(Key: PChar; aValue: string); + function GetBoolean(Key: PChar; aDefault: Boolean): Boolean; + procedure SetBoolean(Key: PChar; aValue: Boolean); + function GetDateTime(Key: PChar; aDefault: TDateTime): TDateTime; + procedure SetDateTime(Key: PChar; aValue: TDateTime); + function GetStorage(Key: PChar): Id2dStorage; + end; + + Td2dSetupStorage = class(TInterfacedObject, Id2dStorage) + private + f_CurSection: string; + f_Ini: TCustomIniFile; + // Id2dStorage methods + function GetBoolean(Key: PChar; aDefault: Boolean): Boolean; + function GetDateTime(Key: PChar; aDefault: TDateTime): TDateTime; + function GetFloat(Key: PChar; aDefault: Double): Double; + function GetInteger(Key: PChar; aDefault: Integer): Integer; + function GetString(Key: PChar; aDefault: string): string; + procedure SetBoolean(Key: PChar; aValue: Boolean); + procedure SetCurSection(const Value: string); + procedure SetDateTime(Key: PChar; aValue: TDateTime); + procedure SetFloat(Key: PChar; aValue: Double); + procedure SetInteger(Key: PChar; aValue: Integer); + procedure SetString(Key: PChar; aValue: string); + function GetStorage(Key: PChar): Id2dStorage; + public + constructor Create(const aLocation: Td2dSetupLocation; aIniFileName, + aIniSection: string); + destructor Destroy; override; + class function Make(const aLocation: Td2dSetupLocation; aIniFileName, + aIniSection: string): Id2dStorage; overload; + class function Make(const aStorage: Td2dSetupStorage; aIniSection: string): Id2dStorage; overload; + property CurSection: string read f_CurSection write SetCurSection; + end; + +implementation +uses + StrUtils, Variants; + +function CRLF2Codes(const aString: string): string; +begin + Result := AnsiReplaceStr(AnsiReplaceStr(aString, #10, '#10'), #13, '#13'); +end; + +function Codes2CRLF(const aString: string): string; +begin + Result := AnsiReplaceStr(AnsiReplaceStr(aString, '#10', #10), '#13', #13); +end; + +constructor Td2dSetupStorage.Create(const aLocation: Td2dSetupLocation; + aIniFileName, aIniSection: string); +begin + inherited Create; + case aLocation of + slInifile : f_Ini := TIniFile.Create(aIniFileName); + slRegistry : f_Ini := TRegistryIniFile.Create(aIniFileName); + end; + f_CurSection := aIniSection; +end; + +destructor Td2dSetupStorage.Destroy; +begin + f_Ini.Free; + inherited; +end; + +class function Td2dSetupStorage.Make(const aLocation: Td2dSetupLocation; + aIniFileName, aIniSection: string): Id2dStorage; +begin + Result := Td2dSetupStorage.Create(aLocation, aIniFileName, aIniSection); +end; + +function Td2dSetupStorage.GetBoolean(Key: PChar; aDefault: Boolean): Boolean; +begin + Result := f_Ini.ReadBool(f_CurSection, Key, aDefault); +end; + +function Td2dSetupStorage.GetDateTime(Key: PChar; aDefault: TDateTime): TDateTime; +begin + Result := f_Ini.ReadDateTime(f_CurSection, Key, aDefault); +end; + +function Td2dSetupStorage.GetFloat(Key: PChar; aDefault: Double): Double; +begin + Result := f_Ini.ReadFloat(f_CurSection, Key, aDefault); +end; + +function Td2dSetupStorage.GetInteger(Key: PChar; aDefault: Integer): Integer; +begin + Result := f_Ini.ReadInteger(f_CurSection, Key, aDefault); +end; + +function Td2dSetupStorage.GetStorage(Key: PChar): Id2dStorage; +begin + Result := Td2dSetupStorage.Make(Self, f_CurSection + '.' + Key); +end; + +function Td2dSetupStorage.GetString(Key: PChar; aDefault: string): string; +begin + Result := Codes2CRLF(f_Ini.ReadString(f_CurSection, Key, aDefault)); +end; + +class function Td2dSetupStorage.Make(const aStorage: Td2dSetupStorage; aIniSection: string): Id2dStorage; +var + l_Loc: Td2dSetupLocation; +begin + if aStorage.f_Ini is TRegistryIniFile then + l_Loc := slRegistry + else + l_Loc := slInifile; + Result := Create(l_Loc, aStorage.f_Ini.FileName, aIniSection); +end; + +procedure Td2dSetupStorage.SetBoolean(Key: PChar; aValue: Boolean); +begin + f_Ini.WriteBool(f_CurSection, Key, aValue); +end; + +procedure Td2dSetupStorage.SetCurSection(const Value: string); +begin + if f_CurSection <> Value then + begin + f_CurSection := Value; + end; +end; + +procedure Td2dSetupStorage.SetDateTime(Key: PChar; aValue: TDateTime); +begin + f_Ini.WriteDateTime(f_CurSection, Key, aValue); +end; + +procedure Td2dSetupStorage.SetFloat(Key: PChar; aValue: Double); +begin + f_Ini.WriteFloat(f_CurSection, Key, aValue); +end; + +procedure Td2dSetupStorage.SetInteger(Key: PChar; aValue: Integer); +begin + f_Ini.WriteInteger(f_CurSection, Key, aValue); +end; + +procedure Td2dSetupStorage.SetString(Key: PChar; aValue: string); +begin + f_Ini.WriteString(f_CurSection, Key, CRLF2Codes(aValue)); +end; + + +end. diff --git a/d2dTexture.pas b/d2dTexture.pas new file mode 100644 index 0000000..ba9c1bc --- /dev/null +++ b/d2dTexture.pas @@ -0,0 +1,98 @@ +unit d2dTexture; + +interface + +uses + Direct3D8, + d2dInterfaces, + d2dClasses; + +type + Td2dTexture = class(Td2dProtoObjectPrim, Id2dTexture) + private + f_DXTexture: IDirect3DTexture8; + f_SrcPicHeight: Integer; + f_SrcPicWidth: Integer; + protected + function pm_GetDirectXTexture: IDirect3DTexture8; + function pm_GetSrcPicHeight: Integer; + function pm_GetSrcPicWidth: Integer; + procedure pm_SetSrcPicHeight(const Value: Integer); + procedure pm_SetSrcPicWidth(const Value: Integer); + public + function IsOrphan: Boolean; + class function Make(aDXTex: IDirect3DTexture8): Id2dTexture; + end; + +implementation + +uses + SysUtils, + d2dCore; + +function Td2dTexture.IsOrphan: Boolean; +var + l_Count: Integer; +begin + Result := True; + if f_DXTexture <> nil then + begin + l_Count := f_DXTexture._AddRef; + try + Result := l_Count = 2; + finally + f_DXTexture._Release; + end; + end; +end; + +class function Td2dTexture.Make(aDXTex: IDirect3DTexture8): Id2dTexture; +var + l_Tex: Td2dTexture; +begin + Result := nil; + if aDXTex <> nil then + begin + l_Tex := Td2dTexture.Create; + try + l_Tex.f_DXTexture := aDXTex; + Result := l_Tex; + finally + FreeAndNil(l_Tex); + end; + end; +end; + +function Td2dTexture.pm_GetDirectXTexture: IDirect3DTexture8; +begin + if Self <> nil then + Result := f_DXTexture; +end; + +function Td2dTexture.pm_GetSrcPicHeight: Integer; +begin + if f_SrcPicHeight > 0 then + Result := f_SrcPicHeight + else + Result := gD2DE.Texture_GetHeight(f_DXTexture); +end; + +function Td2dTexture.pm_GetSrcPicWidth: Integer; +begin + if f_SrcPicWidth > 0 then + Result := f_SrcPicWidth + else + Result := gD2DE.Texture_GetWidth(f_DXTexture); +end; + +procedure Td2dTexture.pm_SetSrcPicHeight(const Value: Integer); +begin + f_SrcPicHeight := Value; +end; + +procedure Td2dTexture.pm_SetSrcPicWidth(const Value: Integer); +begin + f_SrcPicWidth := Value; +end; + +end. \ No newline at end of file diff --git a/d2dTypes.pas b/d2dTypes.pas new file mode 100644 index 0000000..08522ff --- /dev/null +++ b/d2dTypes.pas @@ -0,0 +1,350 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dTypes; + +interface +uses + Direct3D8, + Dynamic_Bass;//, + //d2dClasses; + +const + D2DVersion = $0102; + + D2D_FPS_UNLIMITED = 0; + D2D_FPS_VSYNC = -1; + +const + VertexBufferSize = 4000; + D3DFVF_D2DVERTEX = D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_TEX1; + + // Primitive types + D2DPRIM_LINES = 2; + D2DPRIM_TRIPLES = 3; + D2DPRIM_QUADS = 4; + + // Blending constants + BLEND_COLORADD = 1; + BLEND_COLORMUL = 0; + BLEND_ALPHABLEND = 2; + BLEND_ALPHAADD = 0; + BLEND_ZWRITE = 4; + BLEND_NOZWRITE = 0; + + BLEND_DEFAULT : Integer = BLEND_COLORMUL or BLEND_ALPHABLEND or BLEND_NOZWRITE; + BLEND_DEFAULT_Z : Integer = BLEND_COLORMUL or BLEND_ALPHABLEND or BLEND_ZWRITE; + +const // input types + INPUT_NONE = 0; + INPUT_KEYDOWN = 1; + INPUT_KEYUP = 2; + INPUT_MBUTTONDOWN = 3; + INPUT_MBUTTONUP = 4; + INPUT_MOUSEMOVE = 5; + INPUT_MOUSEWHEEL = 6; + +const //Input Event flags + D2DINP_SHIFT = 1; + D2DINP_CTRL = 2; + D2DINP_ALT = 4; + D2DINP_CAPSLOCK = 8; + D2DINP_SCROLLLOCK = 16; + D2DINP_NUMLOCK = 32; + D2DINP_REPEAT = 64; + D2DINP_MASK = 128; // used for masking of mouse events + + +const //D2D Virtual-key codes + + D2DK_LBUTTON = $01; + D2DK_RBUTTON = $02; + D2DK_MBUTTON = $04; + + D2DK_ESCAPE = $1B; + D2DK_BACKSPACE = $08; + D2DK_TAB = $09; + D2DK_ENTER = $0D; + D2DK_SPACE = $20; + + D2DK_SHIFT = $10; + D2DK_CTRL = $11; + D2DK_ALT = $12; + + D2DK_LWIN = $5B; + D2DK_RWIN = $5C; + D2DK_APPS = $5D; + + D2DK_PAUSE = $13; + D2DK_CAPSLOCK = $14; + D2DK_NUMLOCK = $90; + D2DK_SCROLLLOCK = $91; + + D2DK_PGUP = $21; + D2DK_PGDN = $22; + D2DK_HOME = $24; + D2DK_END = $23; + D2DK_INSERT = $2D; + D2DK_DELETE = $2E; + + D2DK_LEFT = $25; + D2DK_UP = $26; + D2DK_RIGHT = $27; + D2DK_DOWN = $28; + + D2DK_0 = $30; + D2DK_1 = $31; + D2DK_2 = $32; + D2DK_3 = $33; + D2DK_4 = $34; + D2DK_5 = $35; + D2DK_6 = $36; + D2DK_7 = $37; + D2DK_8 = $38; + D2DK_9 = $39; + + D2DK_A = $41; + D2DK_B = $42; + D2DK_C = $43; + D2DK_D = $44; + D2DK_E = $45; + D2DK_F = $46; + D2DK_G = $47; + D2DK_H = $48; + D2DK_I = $49; + D2DK_J = $4A; + D2DK_K = $4B; + D2DK_L = $4C; + D2DK_M = $4D; + D2DK_N = $4E; + D2DK_O = $4F; + D2DK_P = $50; + D2DK_Q = $51; + D2DK_R = $52; + D2DK_S = $53; + D2DK_T = $54; + D2DK_U = $55; + D2DK_V = $56; + D2DK_W = $57; + D2DK_X = $58; + D2DK_Y = $59; + D2DK_Z = $5A; + + D2DK_GRAVE = $C0; + D2DK_MINUS = $BD; + D2DK_EQUALS = $BB; + D2DK_BACKSLASH = $DC; + D2DK_LBRACKET = $DB; + D2DK_RBRACKET = $DD; + D2DK_SEMICOLON = $BA; + D2DK_APOSTROPHE = $DE; + D2DK_COMMA = $BC; + D2DK_PERIOD = $BE; + D2DK_SLASH = $BF; + + D2DK_NUMPAD0 = $60; + D2DK_NUMPAD1 = $61; + D2DK_NUMPAD2 = $62; + D2DK_NUMPAD3 = $63; + D2DK_NUMPAD4 = $64; + D2DK_NUMPAD5 = $65; + D2DK_NUMPAD6 = $66; + D2DK_NUMPAD7 = $67; + D2DK_NUMPAD8 = $68; + D2DK_NUMPAD9 = $69; + + D2DK_MULTIPLY = $6A; + D2DK_DIVIDE = $6F; + D2DK_ADD = $6B; + D2DK_SUBTRACT = $6D; + D2DK_DECIMAL = $6E; + + D2DK_F1 = $70; + D2DK_F2 = $71; + D2DK_F3 = $72; + D2DK_F4 = $73; + D2DK_F5 = $74; + D2DK_F6 = $75; + D2DK_F7 = $76; + D2DK_F8 = $77; + D2DK_F9 = $78; + D2DK_F10 = $79; + D2DK_F11 = $7A; + D2DK_F12 = $7B; + + // Music playback constants + mp_None = 0; + mp_MIDI = 1; + mp_Stream = 2; + mp_Module = 3; + +type + Td2dColor = Longword; + + // Vertex + Pd2dVertex = ^Td2dVertex; + Td2dVertex = packed record + X : Single; // Screen position + Y : Single; + Z : Single; // Depth (0..1) + Col : Longword; // color + TX : Single; // texture coordinates + TY : Single; + end; + + // Triple + Pd2dTriple = ^Td2dTriple; + Td2dTriple = packed record + V : array [0..2] of Td2dVertex; + Tex : IDirect3DTexture8; + Blend : Integer; + end; + + // Quad + Pd2dQuad = ^Td2dQuad; + Td2dQuad = packed record + V : array [0..3] of Td2dVertex; + Tex : IDirect3DTexture8; + Blend : Integer; + end; + + // Render target + Pd2dRenderTarget = ^Td2dRenderTarget; + Td2dRenderTarget = record + Width : Integer; + Height : Integer; + Tex : IDirect3DTexture8; + Depth : IDirect3DSurface8; + end; + + + // input event + Pd2dInputEvent = ^Td2dInputEvent; + Td2dInputEvent = record + EventType : Integer; + KeyCode : Integer; + KeyScan : Integer; + Flags : Integer; + KeyChar: Integer; + Wheel : Integer; + X : Single; + Y : Single; + end; + + Pd2dPoint = ^Td2dPoint; + Td2dPoint = record + X, Y : Single; + end; + + Td2dRect = record + Left, Right, Top, Bottom: Single; + end; + + Pd2dVertexArray = ^Td2dVertexArray; + Td2dVertexArray = array [0..VertexBufferSize-1] of Td2dVertex; + + Td2dSimpleEvent = procedure () of object; + Td2dNotifyEvent = procedure (aSender: TObject) of object; + Td2dTriggerEvent = procedure (aTrigger: Boolean) of object; + Td2dFrameEvent = procedure (aDelta: Single; var aFinish: Boolean) of object; + Td2dExitEvent = function (): Boolean of object; + + // Sound handlers (inherited from BASS) + + HMUSIC = Dynamic_Bass.HMUSIC; // MOD music handle + HSAMPLE = Dynamic_Bass.HSAMPLE; // sample handle + HCHANNEL = Dynamic_Bass.HCHANNEL; // playing sample's channel handle + HSTREAM = Dynamic_Bass.HSTREAM; // sample stream handle + +type // Formatted text types + Td2dTextChunkType = (ctUndefined, ctText, ctLink, ctPicture, ctEOL); + Td2dTextSliceType = (stUnknown, stText, stLink, stPicture, stUnion); + Td2dTextAlignType = (ptLeftAligned, ptRightAligned, ptCentered); + +function ARGB(A,R,G,B: Longword): Td2dColor; +procedure Color2ARGB(const aColor: Td2dColor; var A,R,G,B: Byte); + +function D2DPoint(const aX, aY: Single): Td2dPoint; +function D2DRect(aX1, aY1, aX2, aY2: Single): Td2dRect; + +procedure Processed(var theEvent: Td2dInputEvent); +function IsKeyboardEvent(const aEvent: Td2dInputEvent): Boolean; +function IsMouseEvent(const aEvent: Td2dInputEvent): Boolean; +function IsProcessed(const aEvent: Td2dInputEvent): Boolean; +function IsMouseMoveMasked(const aEvent: Td2dInputEvent): Boolean; +procedure MaskMouseMove(var theEvent: Td2dInputEvent); + + + +implementation +uses + SysUtils, + d2dCore; + +function ARGB(A,R,G,B: Longword): Td2dColor; +begin + Result := (A shl 24) or (R shl 16) or (G shl 8) or B; +end; + +procedure Processed(var theEvent: Td2dInputEvent); +begin + FillChar(theEvent, SizeOf(Td2dInputEvent), 0); +end; + +function IsKeyboardEvent(const aEvent: Td2dInputEvent): Boolean; +begin + Result := (aEvent.EventType = INPUT_KEYDOWN) or (aEvent.EventType = INPUT_KEYUP); +end; + +function IsMouseEvent(const aEvent: Td2dInputEvent): Boolean; +begin + Result := aEvent.EventType in [INPUT_MBUTTONDOWN, INPUT_MBUTTONUP, INPUT_MOUSEMOVE, INPUT_MOUSEWHEEL]; +end; + +function IsProcessed(const aEvent: Td2dInputEvent): Boolean; +begin + Result := aEvent.EventType = INPUT_NONE; +end; + +procedure Color2ARGB(const aColor: Td2dColor; var A,R,G,B: Byte); +begin + A := (aColor shr 24) and $FF; + R := (aColor shr 16) and $FF; + G := (aColor shr 8) and $FF; + B := aColor and $FF; +end; + +function D2DPoint(const aX, aY: Single): Td2dPoint; +begin + Result.X := aX; + Result.Y := aY; +end; + +function D2DRect(aX1, aY1, aX2, aY2: Single): Td2dRect; +begin + Result.Left := aX1; + Result.Right := aX2; + Result.Top := aY1; + Result.Bottom := aY2; +end; + +function IsMouseMoveMasked(const aEvent: Td2dInputEvent): Boolean; +begin + Result := (aEvent.EventType = INPUT_MOUSEMOVE) and (aEvent.Flags and D2DINP_MASK <> 0); +end; + +procedure MaskMouseMove(var theEvent: Td2dInputEvent); +begin + if theEvent.EventType = INPUT_MOUSEMOVE then + theEvent.Flags := theEvent.Flags or D2DINP_MASK; +end; + +end. diff --git a/d2dUtils.pas b/d2dUtils.pas new file mode 100644 index 0000000..901f37a --- /dev/null +++ b/d2dUtils.pas @@ -0,0 +1,560 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- +unit d2dUtils; + +interface +uses + Classes, + d2dTypes; + +type + Td2dFiler = class(TObject) + protected + f_Stream: TStream; + public + constructor Create(aStream: TStream); + function ReadInteger: Integer; + function ReadDouble: Double; + function ReadString: string; + function ReadBoolean: Boolean; + function ReadSingle: Single; + function ReadPoint: Td2dPoint; + function ReadColor: Td2dColor; + function ReadByte: Byte; + procedure WriteInteger(aValue: Integer); + procedure WriteDouble(aValue: Double); + procedure WriteString(aString: string); + procedure WriteBoolean(aValue: Boolean); + procedure WriteSingle(aValue: Single); + procedure WritePoint(aValue: Td2dPoint); + procedure WriteColor(aValue: Td2dColor); + procedure WriteByte(aValue: Byte); + property Stream: TStream read f_Stream; + end; + + Td2dBasicBlender = class + private + f_IsRunning: Boolean; + protected + procedure DoUpdate(aDelta: Single); virtual; abstract; + public + procedure Run; virtual; + procedure Stop; virtual; + procedure Update(aDelta: Single); + procedure Save(aFiler: Td2dFiler); virtual; + constructor Load(aFiler: Td2dFiler); virtual; + property IsRunning: Boolean read f_IsRunning write f_IsRunning; + end; + + Td2dColorBlender = class(Td2dBasicBlender) + private + f_Current: Td2dColor; + f_Target: Td2dColor; + f_CA, f_CR, f_CG, f_CB: Single; + f_TA, f_TR, f_TG, f_TB: Single; + f_SpeedA, f_SpeedR, f_SpeedG, f_SpeedB: Single; + protected + procedure DoUpdate(aDelta: Single); override; + public + constructor Create(const aTime: Single; const aStartColor, aTargetColor: Td2dColor); + constructor Load(aFiler: Td2dFiler); override; + procedure Save(aFiler: Td2dFiler); override; + property Current: Td2dColor read f_Current; + property Target: Td2dColor read f_Target; + end; + + Td2dPositionBlender = class(Td2dBasicBlender) + private + f_Current: Td2dPoint; + f_Target: Td2dPoint; + f_Speed: Td2dPoint; + protected + procedure DoUpdate(aDelta: Single); override; + public + constructor Create(const aTime: Single; const aStartPos, aTargetPos: Td2dPoint); + constructor Load(aFiler: Td2dFiler); override; + procedure Save(aFiler: Td2dFiler); override; + property Current: Td2dPoint read f_Current; + end; + + Td2dSimpleBlender = class(Td2dBasicBlender) + private + f_Current: Single; + f_Speed: Single; + f_Target: Single; + protected + procedure DoUpdate(aDelta: Single); override; + public + constructor Create(const aTime: Single; const aStartValue, aTargetValue: Single); + constructor Load(aFiler: Td2dFiler); override; + procedure Save(aFiler: Td2dFiler); override; + property Current: Single read f_Current; + end; + + Td2dMemoryStream = class(TMemoryStream) + public + function ExtractMemory(theSize: PLongWord): Pointer; + end; + +procedure D2DRenderCross(aX, aY: Single; aSize: Integer = 4; aColor: Cardinal = $FFFF0000); + +function D2DIsRectIntersected(const aR1, aR2: Td2dRect): Boolean; + +function D2DIsPointInRect(const aX, aY: Single; const aRect: Td2dRect): Boolean; overload; +function D2DIsPointInRect(const aX, aY: Single; aLeft, aTop, aWidth, aHeight: Single): Boolean; overload; + +function D2DMoveRect(const aRect: Td2dRect; aDX, aDY: Single): Td2dRect; + +procedure D2DRenderRect(const aRect: Td2dRect; aColor: Cardinal; aZ: Single = 0); + +function D2DMakeFilledRectQuad(const aRect: Td2dRect; aColor: Cardinal): Td2dQuad; +procedure D2DRenderFilledRect(const aRect: Td2dRect; aColor: Cardinal); + +function D2AttachZipPack(const aFilename: AnsiString): Boolean; + + +implementation +uses + SysUtils, + d2dCore, + d2dInterfaces, + d2dZipPack; + +procedure D2DRenderCross(aX, aY: Single; aSize: Integer = 4; aColor: Cardinal = $FFFF0000); +begin + gD2DE.Gfx_RenderLine(aX-aSize, aY-aSize, aX+aSize, aY+aSize, aColor, 1); + gD2DE.Gfx_RenderLine(aX-aSize, aY+aSize, aX+aSize, aY-aSize, aColor, 1); +end; + +function D2DIsPointInRect(const aX, aY: Single; aLeft, aTop, aWidth, aHeight: Single): Boolean; +begin + Result := (aX >= aLeft) and (aX <= aLeft + aWidth) and (aY >= aTop) and (aY <= aTop + aHeight); +end; + +function D2DIsPointInRect(const aX, aY: Single; const aRect: Td2dRect): Boolean; +begin + Result := (aX >= aRect.Left) and (aX <= aRect.Right) and (aY >= aRect.Top) and (aY <= aRect.Bottom); +end; + +function D2DIsRectIntersected(const aR1, aR2: Td2dRect): Boolean; + +begin + Result := + D2DIsPointInRect(aR1.Left, aR1.Top, aR2) + or + D2DIsPointInRect(aR1.Right, aR1.Top, aR2) + or + D2DIsPointInRect(aR1.Left, aR1.Bottom, aR2) + or + D2DIsPointInRect(aR1.Right, aR1.Bottom, aR2); +end; + +procedure D2DRenderRect(const aRect: Td2dRect; aColor: Cardinal; aZ: Single = 0); +begin + gD2DE.Gfx_RenderLine(aRect.Left, aRect.Top, aRect.Right, aRect.Top, aColor, aZ); + gD2DE.Gfx_RenderLine(aRect.Right, aRect.Top, aRect.Right, aRect.Bottom, aColor, aZ); + gD2DE.Gfx_RenderLine(aRect.Right, aRect.Bottom, aRect.Left, aRect.Bottom, aColor, aZ); + // here goes the dirty trick + if gD2DE.Windowed then + gD2DE.Gfx_RenderLine(aRect.Left, aRect.Bottom, aRect.Left, aRect.Top-1, aColor, aZ) + else + gD2DE.Gfx_RenderLine(aRect.Left, aRect.Bottom, aRect.Left, aRect.Top, aColor, aZ); +end; + +procedure D2DRenderFilledRect(const aRect: Td2dRect; aColor: Cardinal); +begin + gD2DE.Gfx_RenderQuad(D2DMakeFilledRectQuad(aRect, aColor)); +end; + +function D2DMakeFilledRectQuad(const aRect: Td2dRect; aColor: Cardinal): Td2dQuad; +var + I: Integer; +begin + FillChar(Result, SizeOf(Td2dQuad), 0); + with Result do + begin + Blend := BLEND_DEFAULT; + for I := 0 to 3 do + V[I].Col := aColor; + Result.V[0].X := aRect.Left; Result.V[0].Y := aRect.Top; + Result.V[1].X := aRect.Right; Result.V[1].Y := aRect.Top; + Result.V[2].X := aRect.Right; Result.V[2].Y := aRect.Bottom; + Result.V[3].X := aRect.Left; Result.V[3].Y := aRect.Bottom; + end; +end; + +constructor Td2dBasicBlender.Load(aFiler: Td2dFiler); +begin + inherited Create; + f_IsRunning := aFiler.ReadBoolean; +end; + +procedure Td2dBasicBlender.Run; +begin + f_IsRunning := True; +end; + +procedure Td2dBasicBlender.Save(aFiler: Td2dFiler); +begin + aFiler.WriteBoolean(f_IsRunning); +end; + +procedure Td2dBasicBlender.Stop; +begin + f_IsRunning := False; +end; + +procedure Td2dBasicBlender.Update(aDelta: Single); +begin + if f_IsRunning then + DoUpdate(aDelta); +end; + +constructor Td2dColorBlender.Create(const aTime: Single; const aStartColor, aTargetColor: Td2dColor); +var + l_SA, l_SR, l_SG, l_SB: Byte; + l_TA, l_TR, l_TG, l_TB: Byte; +begin + inherited Create; + f_Current := aStartColor; + f_Target := aTargetColor; + Color2ARGB(aStartColor, l_SA, l_SR, l_SG, l_SB); + Color2ARGB(aTargetColor, l_TA, l_TR, l_TG, l_TB); + f_CA := l_SA; + f_CR := l_SR; + f_CG := l_SG; + f_CB := l_SB; + f_TA := l_TA; + f_TR := l_TR; + f_TG := l_TG; + f_TB := l_TB; + f_SpeedA := (l_TA - l_SA) / aTime; + f_SpeedR := (l_TR - l_SR) / aTime; + f_SpeedG := (l_TG - l_SG) / aTime; + f_SpeedB := (l_TB - l_SB) / aTime; +end; + +procedure Td2dColorBlender.DoUpdate(aDelta: Single); +begin + f_CA := f_CA + f_SpeedA * aDelta; + f_CR := f_CR + f_SpeedR * aDelta; + f_CG := f_CG + f_SpeedG * aDelta; + f_CB := f_CB + f_SpeedB * aDelta; + + if ((f_SpeedA > 0) and (f_CA > f_TA)) or ((f_SpeedA < 0) and (f_CA < f_TA)) then + begin + f_CA := f_TA; + f_SpeedA := 0; + end; + if ((f_SpeedR > 0) and (f_CR > f_TR)) or ((f_SpeedR < 0) and (f_CR < f_TR)) then + begin + f_CR := f_TR; + f_SpeedR := 0; + end; + if ((f_SpeedG > 0) and (f_CG > f_TG)) or ((f_SpeedG < 0) and (f_CG < f_TG)) then + begin + f_CG := f_TG; + f_SpeedG := 0; + end; + if ((f_SpeedB > 0) and (f_CB > f_TB)) or ((f_SpeedB < 0) and (f_CB < f_TB)) then + begin + f_CB := f_TB; + f_SpeedB := 0; + end; + if (f_SpeedA = 0) and (f_SpeedR = 0) and (f_SpeedG = 0) and (f_SpeedB = 0) then + Stop; + if not IsRunning then + f_Current := f_Target + else + f_Current := ARGB(Trunc(f_CA), Trunc(f_CR), Trunc(f_CG), Trunc(f_CB)); +end; + +constructor Td2dColorBlender.Load(aFiler: Td2dFiler); +begin + inherited Load(aFiler); + with aFiler do + begin + f_CA := ReadSingle; + f_CR := ReadSingle; + f_CG := ReadSingle; + f_CB := ReadSingle; + + f_SpeedA := ReadSingle; + f_SpeedR := ReadSingle; + f_SpeedG := ReadSingle; + f_SpeedB := ReadSingle; + + f_TA := ReadSingle; + f_TR := ReadSingle; + f_TG := ReadSingle; + f_TB := ReadSingle; + + f_Current := ReadColor; + f_Target := ReadColor; + end; +end; + +procedure Td2dColorBlender.Save(aFiler: Td2dFiler); +begin + inherited Save(aFiler); + with aFiler do + begin + WriteSingle(f_CA); + WriteSingle(f_CR); + WriteSingle(f_CG); + WriteSingle(f_CB); + + WriteSingle(f_SpeedA); + WriteSingle(f_SpeedR); + WriteSingle(f_SpeedG); + WriteSingle(f_SpeedB); + + WriteSingle(f_TA); + WriteSingle(f_TR); + WriteSingle(f_TG); + WriteSingle(f_TB); + + WriteColor(f_Current); + WriteColor(f_Target); + end; +end; + +constructor Td2dPositionBlender.Create(const aTime: Single; const aStartPos, aTargetPos: Td2dPoint); +begin + inherited Create; + f_Current := aStartPos; + f_Target := aTargetPos; + f_Speed.X := (aTargetPos.X - aStartPos.X) / aTime; + f_Speed.Y := (aTargetPos.Y - aStartPos.Y) / aTime; +end; + +procedure Td2dPositionBlender.DoUpdate(aDelta: Single); +begin + f_Current.X := f_Current.X + f_Speed.X * aDelta; + f_Current.Y := f_Current.Y + f_Speed.Y * aDelta; + + if ((f_Speed.X > 0) and (f_Current.X > f_Target.X)) or + ((f_Speed.X < 0) and (f_Current.X < f_Target.X)) then + begin + f_Current.X := f_Target.X; + f_Speed.X := 0; + end; + if ((f_Speed.Y > 0) and (f_Current.Y > f_Target.Y)) or + ((f_Speed.Y < 0) and (f_Current.Y < f_Target.Y)) then + begin + f_Current.Y := f_Target.Y; + f_Speed.Y := 0; + end; + if (f_Speed.X = 0) and (f_Speed.Y = 0) then + Stop; +end; + +constructor Td2dPositionBlender.Load(aFiler: Td2dFiler); +begin + inherited Load(aFiler); + with aFiler do + begin + f_Speed := ReadPoint; + f_Current := ReadPoint; + f_Target := ReadPoint; + end; +end; + +procedure Td2dPositionBlender.Save(aFiler: Td2dFiler); +begin + inherited Save(aFiler); + with aFiler do + begin + WritePoint(f_Speed); + WritePoint(f_Current); + WritePoint(f_Target); + end; +end; + +constructor Td2dSimpleBlender.Create(const aTime: Single; const aStartValue, aTargetValue: Single); +begin + inherited Create; + f_Current := aStartValue; + f_Target := aTargetValue; + f_Speed := (aTargetValue - aStartValue)/aTime; +end; + +procedure Td2dSimpleBlender.DoUpdate(aDelta: Single); +begin + f_Current := f_Current + aDelta * f_Speed; + if ((f_Speed > 0) and (f_Current >= f_Target)) or ((f_Speed < 0) and (f_Current <= f_Target)) then + begin + f_Current := f_Target; + Stop; + end; +end; + +constructor Td2dSimpleBlender.Load(aFiler: Td2dFiler); +begin + inherited Load(aFiler); + with aFiler do + begin + f_Speed := ReadSingle; + f_Current := ReadSingle; + f_Target := ReadSingle; + end; +end; + +procedure Td2dSimpleBlender.Save(aFiler: Td2dFiler); +begin + inherited Save(aFiler); + with aFiler do + begin + WriteSingle(f_Speed); + WriteSingle(f_Current); + WriteSingle(f_Target); + end; +end; + +constructor Td2dFiler.Create(aStream: TStream); +begin + inherited Create; + f_Stream := aStream; +end; + +function Td2dFiler.ReadBoolean: Boolean; +begin + f_Stream.ReadBuffer(Result, SizeOf(Boolean)); +end; + +function Td2dFiler.ReadByte: Byte; +begin + f_Stream.ReadBuffer(Result, SizeOf(Byte)); +end; + +function Td2dFiler.ReadColor: Td2dColor; +begin + f_Stream.ReadBuffer(Result, SizeOf(Td2dColor)); +end; + +function Td2dFiler.ReadInteger: Integer; +begin + f_Stream.ReadBuffer(Result, SizeOf(Integer)); +end; + +function Td2dFiler.ReadDouble: Double; +begin + f_Stream.ReadBuffer(Result, SizeOf(Double)); +end; + +function Td2dFiler.ReadPoint: Td2dPoint; +begin + f_Stream.ReadBuffer(Result, SizeOf(Td2dPoint)); +end; + +function Td2dFiler.ReadSingle: Single; +begin + f_Stream.ReadBuffer(Result, SizeOf(Single)); +end; + +function Td2dFiler.ReadString: string; +var + l_Len: Cardinal; +begin + Result := ''; + f_Stream.ReadBuffer(l_Len, SizeOf(Cardinal)); + if l_Len > 0 then + begin + SetLength(Result, l_Len); + f_Stream.ReadBuffer(Result[1], l_Len); + end; +end; + +procedure Td2dFiler.WriteBoolean(aValue: Boolean); +begin + f_Stream.WriteBuffer(aValue, SizeOf(Boolean)); +end; + +procedure Td2dFiler.WriteByte(aValue: Byte); +begin + f_Stream.WriteBuffer(aValue, SizeOf(Byte)); +end; + +procedure Td2dFiler.WriteColor(aValue: Td2dColor); +begin + f_Stream.WriteBuffer(aValue, SizeOf(Td2dColor)); +end; + +procedure Td2dFiler.WriteInteger(aValue: Integer); +begin + f_Stream.WriteBuffer(aValue, SizeOf(Integer)); +end; + +procedure Td2dFiler.WriteDouble(aValue: Double); +begin + f_Stream.WriteBuffer(aValue, SizeOf(Double)); +end; + +procedure Td2dFiler.WritePoint(aValue: Td2dPoint); +begin + f_Stream.WriteBuffer(aValue, SizeOf(Td2dPoint)); +end; + +procedure Td2dFiler.WriteSingle(aValue: Single); +begin + f_Stream.WriteBuffer(aValue, SizeOf(Single)); +end; + +procedure Td2dFiler.WriteString(aString: string); +var + l_Len: Cardinal; +begin + l_Len := Length(aString); + f_Stream.WriteBuffer(l_Len, SizeOf(l_Len)); + if l_Len > 0 then + f_Stream.Write(aString[1], l_Len); +end; + +function Td2dMemoryStream.ExtractMemory(theSize: PLongWord): Pointer; +begin + Result := GetMemory(Size); + Move(Memory^, Result^, Size); + if theSize <> nil then + theSize^ := Size; +end; + +function D2DMoveRect(const aRect: Td2dRect; aDX, aDY: Single): Td2dRect; +begin + Result.Left := aRect.Left + aDX; + Result.Top := aRect.Top + aDY; + Result.Right := aRect.Right + aDX; + Result.Bottom := aRect.Bottom + aDY; +end; + +function D2AttachZipPack(const aFilename: AnsiString): Boolean; +var + l_ZP: Id2dResourcePack; +begin + Result := False; + if not FileExists(aFileName) then + begin + gD2DE.System_Log('Can''t find resource pack: %s',[aFileName]); + Exit; + end; + l_ZP := Td2dZipPack.Make(aFileName{, aOffset}); + if l_ZP.Count = 0 then + begin + gD2DE.System_Log('Corrupted resource pack: %s',[aFileName]); + Exit; + end; + gD2DE.Resource_AttachPack(aFilename, l_ZP); + Result := True; +end; + + + +end. diff --git a/d2dZipPack.pas b/d2dZipPack.pas new file mode 100644 index 0000000..153abf7 --- /dev/null +++ b/d2dZipPack.pas @@ -0,0 +1,377 @@ +//--------------------------------------------------------------------------- +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (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.mozilla.org/MPL/ +// +// 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. +//--------------------------------------------------------------------------- + +// This unit based on SciZipFile unit by Patrik Spanel +// scilib@sendme.cz +// Written from scratch using InfoZip PKZip file specification application note +// ftp://ftp.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip +// uses the Borland out of the box zlib + +unit d2dZipPack; + +interface + +uses + SysUtils, + Types, + Classes, + zlib, + + d2dClasses, + d2dInterfaces; + +type + + TCommonFileHeader = packed record + VersionNeededToExtract: WORD; // 2 bytes + GeneralPurposeBitFlag: WORD; // 2 bytes + CompressionMethod: WORD; // 2 bytes + LastModFileTimeDate: DWORD; // 4 bytes + Crc32: DWORD; // 4 bytes + CompressedSize: DWORD; // 4 bytes + UncompressedSize: DWORD; // 4 bytes + FilenameLength: WORD; // 2 bytes + ExtraFieldLength: WORD; // 2 bytes + end; + + TLocalFile = packed record + LocalFileHeaderSignature: DWORD; // 4 bytes (0x04034b50) + CommonFileHeader: TCommonFileHeader; // + filename: AnsiString; //variable size + extrafield: AnsiString; //variable size + FileOffset: DWORD; + end; + + Td2dZipPack = class(Td2dProtoObject, Id2dResourcePack) + private + Files: array of TLocalFile; + f_PackFileName: string; + function pm_GetCount: Integer; + function pm_GetName(aIndex: Integer): AnsiString; + procedure LoadFromFile(const filename: string; aOffset: Integer); + procedure LoadFromStream(const ZipFileStream: TStream); + function pm_GetSize(Index: Integer): LongWord; + public + constructor Create(aPackFileName: string; aOffset: Integer = 0); + function IndexOf(const aFileName: string): Integer; + function Find(aWildCard: string; aFrom: Integer = 0): Integer; + function Extract(I: integer; var aBuffer: Pointer; aSize: DWORD = 0): Boolean; + class function Make(const aFilename: AnsiString): Id2dResourcePack; + property Count: integer read pm_GetCount; + property Name[Index: Integer]: string read pm_GetName; + property Size[Index: Integer]: LongWord read pm_GetSize; + end; + + EZipFileError = class(Exception); + EZipFileCRCError = class(Exception); + +function ZipCRC32(const aBuffer: Pointer; aLength: DWORD): longword; + +implementation +uses + Windows, + StrUtils, + JclStrings; + +const + shlwapi = 'shlwapi.dll'; + + UTF8ENCODEDFILENAME_FLAG = $0800; + +function PathMatchSpec(aFile: PAnsiChar; aSpec: PAnsiChar): BOOL; stdcall; external shlwapi name 'PathMatchSpecA'; + +constructor Td2dZipPack.Create(aPackFileName: string; aOffset: Integer = 0); +begin + inherited Create; + f_PackFileName := aPackFileName; + LoadFromFile(f_PackFileName, aOffset); +end; + +function Td2dZipPack.Find(aWildCard: string; aFrom: Integer = 0): Integer; +var + l_List: TStringList; + I,J : Integer; +begin + Result := -1; + l_List := TStringList.Create; + try + StrToStrings(aWildCard, ';', l_List, False); + if l_List.Count > 0 then + begin + for I := aFrom to Count-1 do + begin + for J := 0 to Pred(l_List.Count) do + begin + if PathMatchSpec(PAnsiChar(Files[I].filename), PAnsiChar(l_List[J])) then + begin + Result := I; + Exit; + end; + end; + end; + end; + finally + l_List.Free; + end; +end; + +procedure Td2dZipPack.LoadFromStream(const ZipFileStream: TStream); +var + n: integer; + signature: DWORD; +begin + n := 0; + repeat + signature := 0; + ZipFileStream.Read(signature, 4); + if (ZipFileStream.Position = ZipFileStream.Size) then exit; + until signature = $04034B50; + repeat + begin + if (signature = $04034B50) then + begin + inc(n); + SetLength(Files, n); + with Files[n - 1] do + begin + LocalFileHeaderSignature := signature; + ZipFileStream.Read(CommonFileHeader, SizeOf(CommonFileHeader)); + SetLength(filename, CommonFileHeader.FilenameLength); + ZipFileStream.Read(PChar(filename)^, + CommonFileHeader.FilenameLength); + filename := StringReplace(filename, '/', '\', [rfReplaceAll]); + SetLength(extrafield, CommonFileHeader.ExtraFieldLength); + ZipFileStream.Read(PChar(extrafield)^, + CommonFileHeader.ExtraFieldLength); + FileOffset := ZipFileStream.Position; + if CommonFileHeader.GeneralPurposeBitFlag and UTF8ENCODEDFILENAME_FLAG <> 0 then + filename := UTF8ToAnsi(filename) + else + OemToCharBuff(PAnsiChar(filename), PAnsiChar(filename), Length(filename)); + filename := AnsiLowerCase(filename); + ZipFileStream.Seek(CommonFileHeader.CompressedSize, soFromCurrent); + end; + end; + end; + signature := 0; + ZipFileStream.Read(signature, 4); + until signature <> ($04034B50); +end; + +procedure Td2dZipPack.LoadFromFile(const filename: string; aOffset: Integer); +var + ZipFileStream: TFileStream; +begin + ZipFileStream := TFileStream.Create(filename, fmOpenRead or fmShareDenyWrite); + try + ZipFileStream.Position := aOffset; + LoadFromStream(ZipFileStream); + finally + ZipFileStream.Free; + end; +end; + +function Td2dZipPack.Extract(I: integer; var aBuffer: Pointer; aSize: DWORD = 0): Boolean; +var + Decompressor: TDecompressionStream; + UncompressedStream: TMemoryStream; + l_ZipFileStream : TFileStream; + ReadBytes: DWORD; + LoadedCrc32: DWORD; + l_CompMem: PByteArray; +begin + Result := False; + if (aSize > 0) and (aSize < Files[I].CommonFileHeader.UncompressedSize) then + Exit; + + if (i < 0) or (i > High(Files)) then + raise Exception.Create('Index out of range.'); + + if Files[I].CommonFileHeader.CompressionMethod <> 0 then // if not "stored" + begin + UncompressedStream := TMemoryStream.Create; + UncompressedStream.SetSize(Files[i].CommonFileHeader.CompressedSize+2); + l_CompMem := PByteArray(UncompressedStream.Memory); + //manufacture a 2 byte header for zlib; 4 byte footer is not required. + l_CompMem[0] := $78; + l_CompMem[1] := $9C; + l_ZipFileStream := TFileStream.Create(f_PackFileName, fmOpenRead or fmShareDenyWrite); + try + l_ZipFileStream.Position := Files[I].FileOffset; + l_ZipFileStream.Read(l_CompMem[2], Files[I].CommonFileHeader.CompressedSize); + finally + l_ZipFileStream.Free; + end; + UncompressedStream.Position := 0; + try {+} + Decompressor := TDecompressionStream.Create(UncompressedStream); + try {+} + if aSize = 0 then + GetMem(aBuffer, Size[I]); + ReadBytes := Decompressor.Read(aBuffer^, Size[I]); + if ReadBytes <> Size[I] then + begin + if aSize = 0 then + FreeMem(aBuffer); + Exit; + end; + finally + Decompressor.Free; + end; + finally + UncompressedStream.Free; + end; + end + else + begin + l_ZipFileStream := TFileStream.Create(f_PackFileName, fmOpenRead or fmShareDenyWrite); + if aSize = 0 then + GetMem(aBuffer, Size[I]); + l_ZipFileStream.Position := Files[I].FileOffset; + ReadBytes := l_ZipFileStream.Read(aBuffer^, Size[I]); + if ReadBytes <> Size[I] then + begin + if aSize = 0 then + FreeMem(aBuffer); + Exit; + end; + end; + + LoadedCRC32 := ZipCRC32(aBuffer, ReadBytes); + if LoadedCRC32 <> Files[i].CommonFileHeader.Crc32 then + // - Result := ''; + raise EZipFileCRCError.CreateFmt('CRC Error in "%s".', [Files[i].filename]); + Result := True; +end; + +function Td2dZipPack.pm_GetCount: Integer; +begin + Result := High(Files) + 1; +end; + +function Td2dZipPack.pm_GetName(aIndex: Integer): AnsiString; +begin + Result := Files[aIndex].Filename; +end; + +function Td2dZipPack.IndexOf(const aFileName: string): Integer; +var + I: Integer; + l_Str: string; +begin + Result := -1; + I := 0; + l_Str := AnsiLowerCase(aFileName); + l_Str := StringReplace(l_Str, '/', '\', [rfReplaceAll]); + while I < Count do + begin + if Name[I] = l_Str then + begin + Result := I; + Exit; + end; + Inc(I); + end; +end; + +class function Td2dZipPack.Make(const aFilename: AnsiString): Id2dResourcePack; +var + l_ZP: Td2dZipPack; +begin + l_ZP := Td2dZipPack.Create(aFilename); + try + Result := l_ZP; + finally + FreeAndNil(l_ZP); + end; +end; + +function Td2dZipPack.pm_GetSize(Index: Integer): LongWord; +begin + if (Index < 0) or (Index > High(Files)) then + raise Exception.Create('Index out of range.'); + Result := Files[Index].CommonFileHeader.UncompressedSize; +end; + +{ ZipCRC32 } + +//calculates the zipfile CRC32 value from a string + +function ZipCRC32(const aBuffer: Pointer; aLength: DWORD): longword; +const + CRCtable: array[0..255] of DWORD = ( + $00000000, $77073096, $EE0E612C, $990951BA, $076DC419, $706AF48F, $E963A535, + $9E6495A3, $0EDB8832, $79DCB8A4, + $E0D5E91E, $97D2D988, $09B64C2B, $7EB17CBD, $E7B82D07, $90BF1D91, $1DB71064, + $6AB020F2, $F3B97148, $84BE41DE, + $1ADAD47D, $6DDDE4EB, $F4D4B551, $83D385C7, $136C9856, $646BA8C0, $FD62F97A, + $8A65C9EC, $14015C4F, $63066CD9, + $FA0F3D63, $8D080DF5, $3B6E20C8, $4C69105E, $D56041E4, $A2677172, $3C03E4D1, + $4B04D447, $D20D85FD, $A50AB56B, + $35B5A8FA, $42B2986C, $DBBBC9D6, $ACBCF940, $32D86CE3, $45DF5C75, $DCD60DCF, + $ABD13D59, $26D930AC, $51DE003A, + $C8D75180, $BFD06116, $21B4F4B5, $56B3C423, $CFBA9599, $B8BDA50F, $2802B89E, + $5F058808, $C60CD9B2, $B10BE924, + $2F6F7C87, $58684C11, $C1611DAB, $B6662D3D, $76DC4190, $01DB7106, $98D220BC, + $EFD5102A, $71B18589, $06B6B51F, + $9FBFE4A5, $E8B8D433, $7807C9A2, $0F00F934, $9609A88E, $E10E9818, $7F6A0DBB, + $086D3D2D, $91646C97, $E6635C01, + $6B6B51F4, $1C6C6162, $856530D8, $F262004E, $6C0695ED, $1B01A57B, $8208F4C1, + $F50FC457, $65B0D9C6, $12B7E950, + $8BBEB8EA, $FCB9887C, $62DD1DDF, $15DA2D49, $8CD37CF3, $FBD44C65, $4DB26158, + $3AB551CE, $A3BC0074, $D4BB30E2, + $4ADFA541, $3DD895D7, $A4D1C46D, $D3D6F4FB, $4369E96A, $346ED9FC, $AD678846, + $DA60B8D0, $44042D73, $33031DE5, + $AA0A4C5F, $DD0D7CC9, $5005713C, $270241AA, $BE0B1010, $C90C2086, $5768B525, + $206F85B3, $B966D409, $CE61E49F, + $5EDEF90E, $29D9C998, $B0D09822, $C7D7A8B4, $59B33D17, $2EB40D81, $B7BD5C3B, + $C0BA6CAD, $EDB88320, $9ABFB3B6, + $03B6E20C, $74B1D29A, $EAD54739, $9DD277AF, $04DB2615, $73DC1683, $E3630B12, + $94643B84, $0D6D6A3E, $7A6A5AA8, + $E40ECF0B, $9309FF9D, $0A00AE27, $7D079EB1, $F00F9344, $8708A3D2, $1E01F268, + $6906C2FE, $F762575D, $806567CB, + $196C3671, $6E6B06E7, $FED41B76, $89D32BE0, $10DA7A5A, $67DD4ACC, $F9B9DF6F, + $8EBEEFF9, $17B7BE43, $60B08ED5, + $D6D6A3E8, $A1D1937E, $38D8C2C4, $4FDFF252, $D1BB67F1, $A6BC5767, $3FB506DD, + $48B2364B, $D80D2BDA, $AF0A1B4C, + $36034AF6, $41047A60, $DF60EFC3, $A867DF55, $316E8EEF, $4669BE79, $CB61B38C, + $BC66831A, $256FD2A0, $5268E236, + $CC0C7795, $BB0B4703, $220216B9, $5505262F, $C5BA3BBE, $B2BD0B28, $2BB45A92, + $5CB36A04, $C2D7FFA7, $B5D0CF31, + $2CD99E8B, $5BDEAE1D, $9B64C2B0, $EC63F226, $756AA39C, $026D930A, $9C0906A9, + $EB0E363F, $72076785, $05005713, + $95BF4A82, $E2B87A14, $7BB12BAE, $0CB61B38, $92D28E9B, $E5D5BE0D, $7CDCEFB7, + $0BDBDF21, $86D3D2D4, $F1D4E242, + $68DDB3F8, $1FDA836E, $81BE16CD, $F6B9265B, $6FB077E1, $18B74777, $88085AE6, + $FF0F6A70, $66063BCA, $11010B5C, + $8F659EFF, $F862AE69, $616BFFD3, $166CCF45, $A00AE278, $D70DD2EE, $4E048354, + $3903B3C2, $A7672661, $D06016F7, + $4969474D, $3E6E77DB, $AED16A4A, $D9D65ADC, $40DF0B66, $37D83BF0, $A9BCAE53, + $DEBB9EC5, $47B2CF7F, $30B5FFE9, + $BDBDF21C, $CABAC28A, $53B39330, $24B4A3A6, $BAD03605, $CDD70693, $54DE5729, + $23D967BF, $B3667A2E, $C4614AB8, + $5D681B02, $2A6F2B94, $B40BBE37, $C30C8EA1, $5A05DF1B, $2D02EF8D); +var + i: integer; + l_Data: PByte; +begin + result := $FFFFFFFF; + l_Data := PByte(aBuffer); + for i := 0 to aLength - 1 do + begin + result := (result shr 8) xor (CRCtable[byte(result) xor Ord(l_Data^)]); + l_Data := PByte(Longword(l_Data)+1); + end; + result := result xor $FFFFFFFF; +end; +end. +