diff --git a/src/game/g_main.pas b/src/game/g_main.pas
index b0648049143f24291d0b00ea063294c3b5a4df70..6e393b262f29152ce225cb4698b108970389a58f 100644 (file)
--- a/src/game/g_main.pas
+++ b/src/game/g_main.pas
procedure CharPress (C: AnsiChar);
var
procedure CharPress (C: AnsiChar);
var
- {--- TO REMOVE ---}
- GameDir: string;
- {-----------------}
-
{--- Read-only dirs ---}
GameWAD: string;
DataDirs: SSArray;
{--- Read-only dirs ---}
GameWAD: string;
DataDirs: SSArray;
CacheDirs: SSArray;
ConfigDirs: SSArray;
ScreenshotDirs: SSArray;
CacheDirs: SSArray;
ConfigDirs: SSArray;
ScreenshotDirs: SSArray;
+ StatsDirs: SSArray;
MapDownloadDirs: SSArray;
WadDownloadDirs: SSArray;
MapDownloadDirs: SSArray;
WadDownloadDirs: SSArray;
+ GameWADName: string = 'GAME';
+
implementation
uses
{$INCLUDE ../nogl/noGLuses.inc}
{$IFDEF ENABLE_HOLMES}
g_holmes, sdlcarcass, fui_ctls, fui_wadread, fui_style, fui_gfx_gl,
implementation
uses
{$INCLUDE ../nogl/noGLuses.inc}
{$IFDEF ENABLE_HOLMES}
g_holmes, sdlcarcass, fui_ctls, fui_wadread, fui_style, fui_gfx_gl,
+{$ENDIF}
+{$IFDEF LINUX}
+ BaseUnix,
+{$ENDIF}
+{$IFDEF DARWIN}
+ MacOSAll, CocoaAll,
+{$ENDIF}
+{$IFDEF USE_SDL2}
+ SDL2,
{$ENDIF}
wadreader, e_log, g_window,
e_graphics, e_input, g_game, g_console, g_gui,
{$ENDIF}
wadreader, e_log, g_window,
e_graphics, e_input, g_game, g_console, g_gui,
var
charbuff: packed array [0..15] of AnsiChar;
var
charbuff: packed array [0..15] of AnsiChar;
+ binPath: AnsiString = '';
+ forceBinDir: Boolean;
+
+function GetBinaryPath (): AnsiString;
+{$IFDEF LINUX}
+var
+ //cd: AnsiString;
+ sl: AnsiString;
+{$ENDIF}
+begin
+ result := ExtractFilePath(ParamStr(0));
+ {$IFDEF LINUX}
+ // it may be a symlink; do some guesswork here
+ sl := fpReadLink(ExtractFileName(ParamStr(0)));
+ if (sl = ParamStr(0)) then
+ begin
+ // use current directory, as we don't have anything better
+ //result := '.';
+ GetDir(0, result);
+ end;
+ {$ENDIF}
+ result := fixSlashes(result);
+ if (length(result) > 0) and (result[length(result)] <> '/') then result := result+'/';
+end;
+
+procedure PrintDirs (msg: AnsiString; dirs: SSArray);
+ var dir: AnsiString;
+begin
+ e_LogWriteln(msg + ':');
+ for dir in dirs do
+ e_LogWriteln(' ' + dir);
+end;
+
+{$IFDEF DARWIN}
+ function NSStringToAnsiString (s: NSString): AnsiString;
+ var i: Integer;
+ begin
+ result := '';
+ for i := 0 to s.length - 1 do
+ result := result + AnsiChar(s.characterAtIndex(i));
+ end;
+
+ function GetBundlePath (): AnsiString;
+ var pathRef: CFURLRef; pathCFStr: CFStringRef; pathStr: ShortString;
+ begin
+ pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
+ pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);
+ CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());
+ CFRelease(pathRef);
+ CFRelease(pathCFStr);
+ Result := pathStr;
+ end;
+{$ENDIF}
procedure InitPath;
procedure InitPath;
- var i: Integer; rwdir, rodir: AnsiString;
+ var i: Integer; rwdir, rodir: AnsiString; rwdirs, rodirs: SSArray;
+
+ procedure AddDir (var dirs: SSArray; append: AnsiString);
+ begin
+ SetLength(dirs, Length(dirs) + 1);
+ dirs[High(dirs)] := ExpandFileName(append)
+ end;
+
+ function IsSep (ch: Char): Boolean;
+ begin
+ {$IFDEF WINDOWS}
+ result := (ch = '/') or (ch = '\');
+ {$ELSE}
+ result := (ch = '/');
+ {$ENDIF}
+ end;
- procedure AddPath (var arr: SSArray; str: AnsiString);
+ function OptimizePath (dir: AnsiString): AnsiString;
+ var i, len: Integer; s: AnsiString;
begin
begin
- SetLength(arr, Length(arr) + 1);
- arr[High(arr)] := ExpandFileName(str)
+ i := 1; len := Length(dir); s := '';
+ while i <= len do
+ begin
+ if IsSep(dir[i]) then
+ begin
+ s := s + DirectorySeparator;
+ Inc(i);
+ while (i <= len) and IsSep(dir[i]) do Inc(i);
+ if (i <= len) and (dir[i] = '.') then
+ begin
+ if (i = len) or IsSep(dir[i + 1]) then
+ begin
+ Inc(i)
+ end
+ else if (i + 1 <= len) and (dir[i + 1] = '.') then
+ begin
+ if (i + 1 = len) or IsSep(dir[i + 2]) then
+ begin
+ s := e_UpperDir(s);
+ Inc(i, 2)
+ end
+ end
+ end
+ end
+ else
+ begin
+ s := s + dir[i];
+ Inc(i)
+ end
+ end;
+ result := s
end;
end;
- procedure AddDef (var arr: SSArray; str: AnsiString);
+ procedure OptimizeDirs (var dirs: SSArray);
+ var i, j, k: Integer;
begin
begin
- if arr = nil then
- AddPath(arr, str)
+ for i := 0 to High(dirs) do
+ dirs[i] := OptimizePath(dirs[i]);
+ // deduplicate
+ i := High(dirs);
+ while i >= 0 do
+ begin
+ j := 0;
+ while j < i do
+ begin
+ if dirs[j] = dirs[i] then
+ begin
+ for k := j + 1 to High(dirs) do
+ dirs[k - 1] := dirs[k];
+ Dec(i);
+ SetLength(dirs, High(dirs))
+ end
+ else
+ begin
+ Inc(j)
+ end
+ end;
+ Dec(i)
+ end
+ end;
+
+ procedure AddDef (var dirs: SSArray; base: SSArray; append: AnsiString);
+ var s: AnsiString;
+ begin
+ if Length(dirs) = 0 then
+ for s in base do
+ AddDir(dirs, e_CatPath(s, append));
+ OptimizeDirs(dirs)
+ end;
+
+ function GetDefaultRODirs (): SSArray;
+ {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
+ var home: AnsiString;
+ {$ENDIF}
+ {$IFDEF WINDOWS}
+ var appdata: AnsiString;
+ {$ENDIF}
+ {$IFDEF DARWIN}
+ var bundle, s: AnsiString; dirArr: NSArray; i: Integer;
+ {$ENDIF}
+ begin
+ result := nil;
+ {$IFDEF DARWIN}
+ bundle := GetBundlePath();
+ if ExtractFileExt(bundle) <> '.app' then
+ AddDir(result, binpath);
+ {$ELSE}
+ AddDir(result, binPath);
+ {$ENDIF}
+ if forceBinDir = false then
+ begin
+ {$IFDEF USE_SDL2}
+ AddDir(result, SDL_GetBasePath());
+ AddDir(result, SDL_GetPrefPath('', 'doom2df'));
+ {$ENDIF}
+ {$IFDEF WINDOWS}
+ appdata := GetEnvironmentVariable('APPDATA') + '\doom2df';
+ if appdata <> '' then
+ AddDir(result, appdata);
+ {$ENDIF}
+ {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
+ AddDir(result, '/usr/share/doom2df');
+ AddDir(result, '/usr/local/share/doom2df');
+ home := GetEnvironmentVariable('HOME');
+ if home <> '' then
+ AddDir(result, e_CatPath(home, '.doom2df'));
+ {$ENDIF}
+ {$IFDEF DARWIN}
+ bundle := GetBundlePath();
+ if bundle <> '' then
+ AddDir(result, e_CatPath(bundle, 'Contents/Resources'));
+ dirArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true);
+ for i := 0 to dirArr.count - 1 do
+ begin
+ s := NSStringToAnsiString(dirArr.objectAtIndex(i));
+ AddDir(result, e_CatPath(s, 'Doom 2D Forever'))
+ end;
+ {$ENDIF}
+ {$IF DEFINED(ANDROID) AND DEFINED(USE_SDL2)}
+ AddDir(result, SDL_AndroidGetInternalStoragePath());
+ if SDL_AndroidGetExternalStorageState() <> 0 then
+ AddDir(result, SDL_AndroidGetExternalStoragePath());
+ {$ENDIF}
+ end
+ end;
+
+ function GetDefaultRWDirs (): SSArray;
+ {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
+ var home: AnsiString;
+ {$ENDIF}
+ {$IFDEF WINDOWS}
+ var appdata: AnsiString;
+ {$ENDIF}
+ {$IFDEF DARWIN}
+ var bundle, s: AnsiString; dirArr: NSArray; i: Integer;
+ {$ENDIF}
+ begin
+ result := nil;
+ {$IFDEF DARWIN}
+ bundle := GetBundlePath();
+ if ExtractFileExt(bundle) <> '.app' then
+ AddDir(result, binPath);
+ {$ELSE}
+ AddDir(result, binPath);
+ {$ENDIF}
+ if forceBinDir = false then
+ begin
+ {$IFDEF USE_SDL2}
+ AddDir(result, SDL_GetPrefPath('', 'doom2df'));
+ {$ENDIF}
+ {$IFDEF WINDOWS}
+ appdata := GetEnvironmentVariable('APPDATA') + '\doom2df';
+ if appdata <> '' then
+ AddDir(result, appdata);
+ {$ENDIF}
+ {$IF DEFINED(UNIX) AND NOT DEFINED(DARWIN) AND NOT DEFINED(ANDROID)}
+ home := GetEnvironmentVariable('HOME');
+ if home <> '' then
+ AddDir(result, e_CatPath(home, '.doom2df'));
+ {$ENDIF}
+ {$IFDEF DARWIN}
+ dirArr := NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true);
+ for i := 0 to dirArr.count - 1 do
+ begin
+ s := NSStringToAnsiString(dirArr.objectAtIndex(i));
+ AddDir(result, e_CatPath(s, 'Doom 2D Forever'))
+ end;
+ {$ENDIF}
+ {$IF DEFINED(ANDROID) AND DEFINED(USE_SDL2)}
+ if SDL_AndroidGetExternalStorageState() <> 0 then
+ AddDir(result, SDL_AndroidGetExternalStoragePath());
+ {$ENDIF}
+ end
end;
begin
end;
begin
- GetDir(0, GameDir);
+ forceBinDir := false;
+ binPath := GetBinaryPath();
i := 1;
while i < ParamCount do
begin
case ParamStr(i) of
i := 1;
while i < ParamCount do
begin
case ParamStr(i) of
+ '--like-windoze': forceBinDir := true;
'--rw-dir':
begin
Inc(i);
rwdir := ParamStr(i);
(* RW *)
'--rw-dir':
begin
Inc(i);
rwdir := ParamStr(i);
(* RW *)
- AddPath(LogDirs, e_CatPath(rwdir, ''));
- AddPath(SaveDirs, e_CatPath(rwdir, 'data'));
- AddPath(CacheDirs, e_CatPath(rwdir, 'data/cache'));
- AddPath(ConfigDirs, e_CatPath(rwdir, ''));
- AddPath(MapDownloadDirs, e_CatPath(rwdir, 'maps/downloads'));
- AddPath(WadDownloadDirs, e_CatPath(rwdir, 'wads/downloads'));
- AddPath(ScreenshotDirs, e_CatPath(rwdir, 'screenshots'));
+ AddDir(LogDirs, e_CatPath(rwdir, ''));
+ AddDir(SaveDirs, e_CatPath(rwdir, 'data'));
+ AddDir(CacheDirs, e_CatPath(rwdir, 'data/cache'));
+ AddDir(ConfigDirs, e_CatPath(rwdir, ''));
+ AddDir(MapDownloadDirs, e_CatPath(rwdir, 'maps/downloads'));
+ AddDir(WadDownloadDirs, e_CatPath(rwdir, 'wads/downloads'));
+ AddDir(ScreenshotDirs, e_CatPath(rwdir, 'screenshots'));
+ AddDir(StatsDirs, e_CatPath(rwdir, 'stats'));
(* RO *)
(* RO *)
- AddPath(DataDirs, e_CatPath(rwdir, 'data'));
- AddPath(ModelDirs, e_CatPath(rwdir, 'data/models'));
- AddPath(MegawadDirs, e_CatPath(rwdir, 'maps/megawads'));
- AddPath(MapDirs, e_CatPath(rwdir, 'maps'));
- AddPath(WadDirs, e_CatPath(rwdir, 'wads'));
+ AddDir(DataDirs, e_CatPath(rwdir, 'data'));
+ AddDir(ModelDirs, e_CatPath(rwdir, 'data/models'));
+ AddDir(MegawadDirs, e_CatPath(rwdir, 'maps/megawads'));
+ AddDir(MapDirs, e_CatPath(rwdir, 'maps'));
+ AddDir(WadDirs, e_CatPath(rwdir, 'wads'));
end;
'--ro-dir':
begin
Inc(i);
rodir := ParamStr(i);
(* RO *)
end;
'--ro-dir':
begin
Inc(i);
rodir := ParamStr(i);
(* RO *)
- AddPath(DataDirs, e_CatPath(rodir, 'data'));
- AddPath(ModelDirs, e_CatPath(rodir, 'data/models'));
- AddPath(MegawadDirs, e_CatPath(rodir, 'maps/megawads'));
- AddPath(MapDirs, e_CatPath(rodir, 'maps'));
- AddPath(WadDirs, e_CatPath(rodir, 'wads'));
+ AddDir(DataDirs, e_CatPath(rodir, 'data'));
+ AddDir(ModelDirs, e_CatPath(rodir, 'data/models'));
+ AddDir(MegawadDirs, e_CatPath(rodir, 'maps/megawads'));
+ AddDir(MapDirs, e_CatPath(rodir, 'maps'));
+ AddDir(WadDirs, e_CatPath(rodir, 'wads'));
+ end;
+ '--game-wad':
+ begin
+ Inc(i);
+ GameWADName := ParamStr(i);
end;
end;
Inc(i)
end;
end;
end;
Inc(i)
end;
+ // prefer bin dir if it writable and contains game.wad
+ if forceBinDir = false then
+ begin
+ if findDiskWad(binPath + 'data' + '/' + GameWADName) <> '' then
+ if e_CanCreateFilesAt(binPath) then
+ forceBinDir := true
+ end;
+
(* RO *)
(* RO *)
- AddDef(DataDirs, 'data');
- AddDef(ModelDirs, 'data/models');
- AddDef(MegawadDirs, 'maps/megawads');
- AddDef(MapDirs, 'maps');
- AddDef(WadDirs, 'wads');
+ rodirs := GetDefaultRODirs();
+ AddDef(DataDirs, rodirs, 'data');
+ AddDef(ModelDirs, rodirs, 'data/models');
+ AddDef(MegawadDirs, rodirs, 'maps/megawads');
+ AddDef(MapDirs, rodirs, 'maps');
+ AddDef(WadDirs, rodirs, 'wads');
+
(* RW *)
(* RW *)
- AddDef(LogDirs, '.');
- AddDef(SaveDirs, 'data');
- AddDef(CacheDirs, 'data/cache');
- AddDef(ConfigDirs, '.');
- AddDef(MapDownloadDirs, 'maps/downloads');
- AddDef(WadDownloadDirs, 'wad/downloads');
- AddDef(ScreenshotDirs, 'screenshots');
+ rwdirs := GetDefaultRWDirs();
+ AddDef(LogDirs, rwdirs, '');
+ AddDef(SaveDirs, rwdirs, 'data');
+ AddDef(CacheDirs, rwdirs, 'data/cache');
+ AddDef(ConfigDirs, rwdirs, '');
+ AddDef(MapDownloadDirs, rwdirs, 'maps/downloads');
+ AddDef(WadDownloadDirs, rwdirs, 'wads/downloads');
+ AddDef(ScreenshotDirs, rwdirs, 'screenshots');
+ AddDef(StatsDirs, rwdirs, 'stats');
for i := 0 to High(MapDirs) do
for i := 0 to High(MapDirs) do
- AddPath(AllMapDirs, MapDirs[i]);
+ AddDir(AllMapDirs, MapDirs[i]);
for i := 0 to High(MegawadDirs) do
for i := 0 to High(MegawadDirs) do
- AddPath(AllMapDirs, MegawadDirs[i]);
+ AddDir(AllMapDirs, MegawadDirs[i]);
+ OptimizeDirs(AllMapDirs);
if LogFileName = '' then
begin
if LogFileName = '' then
begin
{$IFDEF HEADLESS}
LogFileName := e_CatPath(rwdir, 'Doom2DF_H.log');
{$ELSE}
{$IFDEF HEADLESS}
LogFileName := e_CatPath(rwdir, 'Doom2DF_H.log');
{$ELSE}
- LogFileName := e_Catpath(rwdir, 'Doom2DF.log');
+ LogFileName := e_CatPath(rwdir, 'Doom2DF.log');
{$ENDIF}
end
{$ENDIF}
end
- end
+ end;
+
+ // HACK: ensure the screenshots folder also has a stats subfolder in it
+ rwdir := e_GetWriteableDir(ScreenshotDirs, false);
+ if rwdir <> '' then CreateDir(rwdir + '/stats');
+end;
+
+procedure InitPrep;
+ {$IF DEFINED(ANDROID) AND DEFINED(USE_SDLMIXER)}
+ var timiditycfg: AnsiString;
+ {$ENDIF}
+ var i: Integer;
+begin
+ {$IFDEF HEADLESS}
+ conbufDumpToStdOut := true;
+ {$ENDIF}
+ for i := 1 to ParamCount do
+ begin
+ if (ParamStr(i) = '--con-stdout') then
+ begin
+ conbufDumpToStdOut := true;
+ break
+ end
+ end;
+
+ if LogFileName <> '' then
+ e_InitLog(LogFileName, TWriteMode.WM_NEWFILE);
+ e_InitWritelnDriver();
+ e_WriteLog('Doom 2D: Forever version ' + GAME_VERSION + ' proto ' + IntToStr(NET_PROTOCOL_VER), TMsgType.Notify);
+ e_WriteLog('Build date: ' + GAME_BUILDDATE + ' ' + GAME_BUILDTIME, TMsgType.Notify);
+ e_WriteLog('Build hash: ' + g_GetBuildHash(), TMsgType.Notify);
+ e_WriteLog('Build by: ' + g_GetBuilderName(), TMsgType.Notify);
+
+ e_LogWritefln('Force bin dir: %s', [forceBinDir], TMsgType.Notify);
+ e_LogWritefln('BINARY PATH: [%s]', [binPath], TMsgType.Notify);
+
+ PrintDirs('DataDirs', DataDirs);
+ PrintDirs('ModelDirs', ModelDirs);
+ PrintDirs('MegawadDirs', MegawadDirs);
+ PrintDirs('MapDirs', MapDirs);
+ PrintDirs('WadDirs', WadDirs);
+
+ PrintDirs('LogDirs', LogDirs);
+ PrintDirs('SaveDirs', SaveDirs);
+ PrintDirs('CacheDirs', CacheDirs);
+ PrintDirs('ConfigDirs', ConfigDirs);
+ PrintDirs('ScreenshotDirs', ScreenshotDirs);
+ PrintDirs('StatsDirs', StatsDirs);
+ PrintDirs('MapDownloadDirs', MapDownloadDirs);
+ PrintDirs('WadDownloadDirs', WadDownloadDirs);
+
+ GameWAD := e_FindWad(DataDirs, GameWADName);
+ if GameWad = '' then
+ begin
+ e_WriteLog('WAD ' + GameWADName + ' not found in data directories.', TMsgType.Fatal);
+ {$IF DEFINED(USE_SDL2) AND NOT DEFINED(HEADLESS)}
+ if forceBinDir = false then
+ SDL_ShowSimpleMessageBox(
+ SDL_MESSAGEBOX_ERROR,
+ 'Doom 2D Forever',
+ PChar('WAD ' + GameWADName + ' not found in data directories.'),
+ nil
+ );
+ {$ENDIF}
+ e_DeinitLog;
+ Halt(1);
+ end;
+
+ {$IF DEFINED(ANDROID) AND DEFINED(USE_SDLMIXER)}
+ timiditycfg := 'timidity.cfg';
+ if e_FindResource(ConfigDirs, timiditycfg) = true then
+ begin
+ timiditycfg := ExpandFileName(timiditycfg);
+ SetEnvVar('TIMIDITY_CFG', timiditycfg);
+ e_LogWritefln('Set TIMIDITY_CFG = "%s"', [timiditycfg]);
+ end;
+ {$ENDIF}
end;
procedure Main();
end;
procedure Main();
var s: AnsiString;
begin
InitPath;
var s: AnsiString;
begin
InitPath;
- if LogFileName <> '' then
- e_InitLog(LogFileName, TWriteMode.WM_NEWFILE);
- e_InitWritelnDriver();
-
- GameWAD := e_FindWad(DataDirs, 'GAME');
- assert(GameWad <> '', 'GAME.WAD not installed?');
-
-
-// e_InitLog(GameDir + '/' + LogFileName, TWriteMode.WM_NEWFILE);
-
- e_WriteLog(
- 'Doom 2D: Forever version ' + GAME_VERSION +
- ' proto ' + IntToStr(NET_PROTOCOL_VER),
- TMsgType.Notify
- );
- e_WriteLog(
- 'Build date: ' + GAME_BUILDDATE + ' ' + GAME_BUILDTIME,
- TMsgType.Notify
- );
-
-{$IFDEF HEADLESS}
- conbufDumpToStdOut := true;
-{$ENDIF}
- e_WriteToStdOut := False; //{$IFDEF HEADLESS}True;{$ELSE}False;{$ENDIF}
-
+ InitPrep;
e_InitInput;
e_InitInput;
-
sys_Init;
sys_Init;
+ g_Options_SetDefault;
+ g_Options_SetDefaultVideo;
s := CONFIG_FILENAME;
if e_FindResource(ConfigDirs, s) = true then
s := CONFIG_FILENAME;
if e_FindResource(ConfigDirs, s) = true then
- begin
- g_Options_Read(s)
- end
- else
- begin
- g_Options_SetDefault;
- g_Options_SetDefaultVideo
- end;
- if sys_SetDisplayMode(gScreenWidth, gScreenHeight, gBPP, gFullScreen) = False then
+ g_Options_Read(s);
+ g_Console_SysInit;
+ if sys_SetDisplayMode(gRC_Width, gRC_Height, gBPP, gRC_FullScreen, gRC_Maximized) = False then
raise Exception.Create('Failed to set videomode on startup.');
raise Exception.Create('Failed to set videomode on startup.');
- g_Console_SysInit;
e_WriteLog(gLanguage, TMsgType.Notify);
g_Language_Set(gLanguage);
e_WriteLog(gLanguage, TMsgType.Notify);
g_Language_Set(gLanguage);
if assigned(oglInitCB) then oglInitCB;
{$ENDIF}
if assigned(oglInitCB) then oglInitCB;
{$ENDIF}
- //g_Res_CreateDatabases(); // it will be done before connecting to the server for the first time
+ //g_Res_CreateDatabases(true); // it will be done before connecting to the server for the first time
e_WriteLog('Entering SDLMain', TMsgType.Notify);
e_WriteLog('Entering SDLMain', TMsgType.Notify);
if assigned(oglDeinitCB) then oglDeinitCB;
{$ENDIF}
if assigned(oglDeinitCB) then oglDeinitCB;
{$ENDIF}
+ g_Console_WriteGameConfig;
sys_Final;
end;
sys_Final;
end;