2021-02-03 17:47:45 -08:00
|
|
|
program downloader;
|
|
|
|
|
2021-02-05 23:20:06 -08:00
|
|
|
uses fphttpclient, base64, opensslsockets, fpjson, jsonparser, SysUtils, process, Classes, libtar, CustApp, Math;
|
2021-02-03 17:47:45 -08:00
|
|
|
|
|
|
|
var
|
|
|
|
gameId: Integer;
|
|
|
|
gameData: TJSONData;
|
|
|
|
client: TFPHTTPClient;
|
2021-02-05 20:13:48 -08:00
|
|
|
authString, tarFile, errorMsg: String;
|
|
|
|
app: TCustomApplication;
|
2021-02-03 17:47:45 -08:00
|
|
|
|
2021-02-04 17:01:29 -08:00
|
|
|
procedure createFileDir(filePath: AnsiString);
|
|
|
|
var
|
|
|
|
splitDir: TStringArray;
|
|
|
|
s: String;
|
|
|
|
newDir: String = '';
|
|
|
|
|
|
|
|
begin
|
|
|
|
splitDir := filePath.Split(['\', '/']);
|
|
|
|
for s in splitDir do
|
|
|
|
begin
|
|
|
|
if not (s = splitDir[Length(splitDir)-1]) then
|
|
|
|
begin
|
|
|
|
newDir := newDir + s + '/';
|
|
|
|
CreateDir(newDir);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure copyFile(oldFile, newFile: AnsiString);
|
|
|
|
var
|
|
|
|
source, dest: TFileStream;
|
|
|
|
|
|
|
|
begin
|
|
|
|
createFileDir(newFile);
|
|
|
|
source := TFileStream.Create(oldFile, fmOpenRead);
|
|
|
|
dest := TFileStream.Create(newFile, fmCreate);
|
|
|
|
dest.CopyFrom(source, source.Size);
|
|
|
|
source.Free;
|
|
|
|
dest.Free;
|
|
|
|
end;
|
|
|
|
|
2021-02-03 17:47:45 -08:00
|
|
|
function getGameData(var client: TFPHTTPClient; gameId: Integer; out gameData: TJSONData): Boolean;
|
|
|
|
const
|
|
|
|
url = 'https://rpi.narnian.us/games/get_game/';
|
|
|
|
|
|
|
|
var
|
|
|
|
jData: TJSONData;
|
|
|
|
|
|
|
|
begin
|
|
|
|
writeln('start get game data');
|
|
|
|
client.AllowRedirect := true;
|
|
|
|
try
|
|
|
|
jData := GetJSON(client.Get(url+IntToStr(gameId)));
|
|
|
|
gameData := jData;
|
|
|
|
getGameData := true;
|
|
|
|
Except
|
|
|
|
getGameData := false;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function downloadGameTar(var client: TFPHTTPClient; gameId: Integer): String;
|
|
|
|
const
|
|
|
|
url = 'https://rpi.narnian.us/games/download/windows/';
|
|
|
|
|
|
|
|
var
|
|
|
|
tarFile: TFileStream;
|
|
|
|
tarFileName: String;
|
|
|
|
|
|
|
|
begin
|
|
|
|
writeln('start download tar');
|
|
|
|
client.AllowRedirect := true;
|
|
|
|
tarFileName := gameData.FindPath('title_sanitized').AsString+'.tar';
|
|
|
|
tarFile := TFileStream.Create(tarFileName, fmCreate);
|
|
|
|
client.Get(url+IntToStr(gameId), tarFile);
|
|
|
|
tarFile.Free;
|
|
|
|
downloadGameTar := tarFileName;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure extractTar(fileName: String);
|
|
|
|
var
|
2021-02-04 15:30:29 -08:00
|
|
|
tarFile: TTarArchive;
|
|
|
|
tarContent: TTarDirRec;
|
2021-02-04 17:01:29 -08:00
|
|
|
files: TJSONArray;
|
|
|
|
firstFile: String;
|
|
|
|
i: Integer;
|
2021-02-03 17:47:45 -08:00
|
|
|
|
|
|
|
begin
|
|
|
|
writeln('start extract tar');
|
2021-02-04 15:30:29 -08:00
|
|
|
tarFile := TTarArchive.Create(fileName);
|
|
|
|
while tarFile.FindNext(tarContent) do
|
|
|
|
begin
|
2021-02-04 17:01:29 -08:00
|
|
|
files := TJSONArray(gameData.FindPath('windows.files.'+tarContent.Name));
|
|
|
|
firstFile := files.Strings[0];
|
|
|
|
createFileDir(firstFile);
|
|
|
|
tarFile.ReadFile(firstFile);
|
|
|
|
for i := 1 to files.Count-1 do
|
2021-02-03 17:47:45 -08:00
|
|
|
begin
|
2021-02-04 17:01:29 -08:00
|
|
|
createFileDir(files.Strings[i]);
|
|
|
|
copyFile(firstFile, files.Strings[i]);
|
2021-02-03 17:47:45 -08:00
|
|
|
end;
|
|
|
|
end;
|
2021-02-04 17:01:29 -08:00
|
|
|
tarFile.Destroy;
|
2021-02-03 17:47:45 -08:00
|
|
|
end;
|
|
|
|
|
2021-02-04 20:46:39 -08:00
|
|
|
function authenticate(client: TFPHTTPClient): Boolean;
|
|
|
|
const
|
|
|
|
url = 'https://rpi.narnian.us';
|
|
|
|
|
|
|
|
var
|
|
|
|
username, password: String;
|
|
|
|
|
|
|
|
begin
|
2021-02-05 20:13:48 -08:00
|
|
|
if app.HasOption('u', 'user') then
|
|
|
|
begin
|
|
|
|
username := app.GetOptionValue('u', 'username');
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
Write('Enter username: ');
|
|
|
|
ReadLn(username);
|
|
|
|
end;
|
|
|
|
if app.HasOption('p', 'password') then
|
|
|
|
begin
|
|
|
|
password := app.GetOptionValue('p', 'password');
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
Write('Enter password: ');
|
|
|
|
ReadLn(password);
|
|
|
|
end;
|
2021-02-04 20:46:39 -08:00
|
|
|
authString := username + ':' + password;
|
|
|
|
client.AddHeader('Authorization', 'Basic '+base64.EncodeStringBase64(authString));
|
|
|
|
client.AllowRedirect := true;
|
|
|
|
try
|
|
|
|
client.Get(url);
|
|
|
|
authenticate := true;
|
|
|
|
except
|
|
|
|
authenticate := false;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2021-02-05 23:20:06 -08:00
|
|
|
procedure displayHelp;
|
|
|
|
const
|
|
|
|
optionFlags: TStringArray = (
|
|
|
|
'-h, --help',
|
|
|
|
'-g, --gameid <game id>',
|
|
|
|
'-u, --username <username>',
|
|
|
|
'-p, --password <password>');
|
|
|
|
optionDescriptions: TStringArray = (
|
|
|
|
'Display help.',
|
|
|
|
'Sets the game id. This is required.',
|
|
|
|
'Sets the username for authentication.',
|
|
|
|
'Sets the password for authentication.');
|
|
|
|
|
|
|
|
var
|
|
|
|
i, lenFlag, lenDesc, maxLenFlag, maxLenDesc, minLenFlag, minLenDesc: Integer;
|
|
|
|
|
|
|
|
begin
|
|
|
|
maxLenFlag := 0;
|
|
|
|
maxLenDesc := 0;
|
|
|
|
minLenFlag := 1000;
|
|
|
|
minLenDesc := 1000;
|
|
|
|
for i := 0 to Length(optionFlags)-1 do
|
|
|
|
begin
|
|
|
|
minLenFlag := Min(minLenFlag, Length(optionFlags[i]));
|
|
|
|
maxLenFlag := Max(maxLenFlag, Length(optionFlags[i]));
|
|
|
|
minLenDesc := Min(minLenDesc, Length(optionDescriptions[i]));
|
|
|
|
maxLenDesc := Max(maxLenDesc, Length(optionDescriptions[i]));
|
|
|
|
end;
|
|
|
|
WriteLn('Usage: downloader.exe [options]');
|
|
|
|
WriteLn;
|
|
|
|
WriteLn('Options:');
|
|
|
|
for i := 0 to Length(optionFLags)-1 do
|
|
|
|
begin
|
|
|
|
lenFlag := Length(optionFLags[i]);
|
|
|
|
lenDesc := Length(optionDescriptions[i]);
|
|
|
|
Write(optionFlags[i]:(lenFlag+2));
|
|
|
|
WriteLn(optionDescriptions[i]:(lenDesc+maxLenFlag-lenFlag+2))
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2021-02-03 17:47:45 -08:00
|
|
|
begin
|
2021-02-05 20:13:48 -08:00
|
|
|
app := TCustomApplication.Create(nil);
|
2021-02-05 23:20:06 -08:00
|
|
|
errorMsg := app.checkOptions('hg:u:p:', 'help gameid: username: password:');
|
|
|
|
if (errorMsg <> '') or app.HasOption('h', 'help') then
|
|
|
|
begin
|
|
|
|
WriteLn(errorMsg);
|
|
|
|
displayHelp;
|
|
|
|
end
|
|
|
|
else
|
2021-02-03 17:47:45 -08:00
|
|
|
begin
|
2021-02-05 20:13:48 -08:00
|
|
|
if app.HasOption('g', 'gameid') then
|
2021-02-03 17:47:45 -08:00
|
|
|
begin
|
2021-02-05 20:13:48 -08:00
|
|
|
gameId := StrToInt(app.GetOptionValue('g', 'gameid'));
|
|
|
|
client := TFPHTTPClient.Create(nil);
|
|
|
|
if authenticate(client) then
|
2021-02-04 20:46:39 -08:00
|
|
|
begin
|
2021-02-05 20:13:48 -08:00
|
|
|
if getGameData(client, gameId, gameData) then
|
|
|
|
begin
|
|
|
|
tarFile := downloadGameTar(client, gameId);
|
|
|
|
extractTar(tarFile);
|
|
|
|
DeleteFile(tarFile);
|
|
|
|
WriteLn('Download finished');
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
WriteLn('Invalid game id given');
|
|
|
|
end;
|
2021-02-04 20:46:39 -08:00
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
2021-02-05 20:13:48 -08:00
|
|
|
WriteLn('Invalid username or password');
|
2021-02-04 20:46:39 -08:00
|
|
|
end;
|
2021-02-03 17:47:45 -08:00
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
2021-02-05 20:13:48 -08:00
|
|
|
WriteLn('No game id was given');
|
2021-02-03 17:47:45 -08:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end.
|
|
|
|
|