Add projects including README.md(s), .gitignore(s) and the actual project files.

Signed-off-by: Michael Fabian Dirks <michael.dirks@project-kube.de>
This commit is contained in:
Michael Fabian Dirks
2014-11-24 18:18:24 +01:00
parent 934da62076
commit 8f3114e377
170 changed files with 22028 additions and 3 deletions
@@ -0,0 +1,435 @@
SuperStrict
Import BaH.libcurl
Import BRL.Bank
Import BRL.FileSystem
Import BRL.LinkedList
Import BRL.StandardIO
Import BRL.Stream
Import BRL.Timer
Import "Clock.c"
Import "Digest.bmx"
Type TPatcher
Const STATE_DEFAULT:Byte = 0
Const STATE_PATCHINFO:Byte = 1
Const STATE_CHECKING:Byte = 2
Const STATE_PREPATCH:Byte = 3
Const STATE_PATCHING:Byte = 4
Const STATE_COMPLETE:Byte = 5
Const STATE_LAUNCHER:Byte = 6
Const SSTATE_ENTER:Byte = 0
Const SSTATE_WORK:Byte = 1
Const SSTATE_LEAVE:Byte = 2
Const SERVER:String = "http://sirius-online.us.to/patch/"
' State-based Patcher
Field m_bState:Byte = TPatcher.STATE_DEFAULT
Field m_bSubState:Byte = TPatcher.SSTATE_ENTER
' lib/cURL is used for downloading of data.
Field m_oCurlMulti:TCurlMulti = Null
Field m_oCurl:TCurlEasy = Null
Field m_fCurlProgress:Float
' State: All
Field m_oTaskList:TList = New TList
Field m_fProgress:Float = 0
' State: Patchinfo, Checking, Patching
Field m_oFileHashList:TList = New TList
Field m_iFileCount:Int = 0
' State: Checking, Patching
Field m_oFileLoader:TAsyncLoader = New TAsyncLoader
Field m_oFileHashPair:TFileHash = Null
' State: Patching
Field m_oFileList:TList = New TList
Field m_oFile:String, m_oFileDL:String
Field m_oFileStream:TStream
' Construction
Method New()
EndMethod
Function Create:TPatcher()
Local oPatcher:TPatcher = New TPatcher
Return oPatcher
EndFunction
' Deconstruction
Method Destroy()
m_oCurlMulti.multiCleanup()
m_oCurl.cleanup()
EndMethod
' Basically the Main Loop of the Patcher, handles everything. Call GetShutdown:String() if you want to know when the patcher is telling you to run an executable and close.
Method Update()
' Check
Select m_bState
Case TPatcher.STATE_DEFAULT
If m_bSubState = TPatcher.SSTATE_ENTER Then
m_oCurlMulti = TCurlMulti.Create()
m_oCurl = m_oCurlMulti.newEasy()
m_oCurl.setOptInt(CURLOPT_FOLLOWLOCATION, 1)
m_oCurl.setOptInt(CURLOPT_CRLF, False)
m_oCurl.setOptString(CURLOPT_USERAGENT, "Sirius Online Launcher")
m_oCurl.setOptString(CURLOPT_REFERER, TPatcher.SERVER)
m_oCurl.setOptString(CURLOPT_URL, TPatcher.SERVER + "info")
m_oCurl.setProgressCallback(TPatcher.ProgressCallback, Self)
m_oCurl.setWriteString()
m_oTaskList.AddFirst("n[WAIT] Getting patch information...")
m_bSubState = TPatcher.SSTATE_WORK
EndIf
If m_bSubState = TPatcher.SSTATE_WORK Then
Local iResult:Int = Perform()
m_fProgress = m_fCurlProgress
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("g[" + RSet(String(Int(m_fProgress * 100)), 3) + "%] Retrieving patch information...")
If iResult = CURLM_OK Then
If m_fCurlProgress = 1.0 And m_iCurlMultiHandles = 0 Then
m_bSubState = TPatcher.SSTATE_LEAVE
EndIf
Else
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("g[FAIL] Retrieving patch information... ("+CurlError(iResult)+")")
SetState(TPatcher.STATE_COMPLETE)
EndIf
EndIf
If m_bSubState = TPatcher.SSTATE_LEAVE Then
m_oCurlMulti.multiRemove(m_oCurl)
m_oCurlMulti.multiCleanup()
m_oCurlMulti = Null
Local oCurlRCode:Int = m_oCurl.getInfo().responseCode()
Local stResult:String = m_oCurl.toString()
m_oCurl.cleanup()
m_oCurl = Null
If oCurlRCode <> 404 Then
m_oFileHashList.Clear()
If stResult.length > 0 Then
Local stFileHashArr:String[] = stResult.Split("~n")
For Local stFileHashPair:String = EachIn stFileHashArr
If stFileHashPair.length > 0 And stFileHashPair[0..1] <> "#" Then ' Ignore Comments
Local stPairArr:String[] = stFileHashPair.Split(":")
m_oFileHashList.AddLast(TFileHash.Create(stPairArr[0], stPairArr[1]))
EndIf
Next
m_iFileCount = m_oFileHashList.Count()
m_oFileList.Clear()
SetState(TPatcher.STATE_PATCHINFO) ' Got Patchinfo, and it had information.
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("g[ OK ] Retrieving patch information... done.")
Else
SetState(TPatcher.STATE_COMPLETE)
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("e[FAIL] Retrieving patch information... invalid information file.")
EndIf
Else
SetState(TPatcher.STATE_COMPLETE)
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("e[FAIL] Retrieving patch information... file not found.")
EndIf
EndIf
Case TPatcher.STATE_CHECKING
If m_bSubState = TPatcher.SSTATE_ENTER Then ' Used to load a new File
m_oFileHashPair = TFileHash(m_oFileHashList.RemoveFirst())
If m_oFileHashPair <> Null Then
m_oFileLoader.Initialize(m_oFileHashPair.stName)
m_oTaskList.AddFirst("n[WAIT] Checking '" + m_oFileHashPair.stName + "'...")
m_bSubState = TPatcher.SSTATE_WORK
Else
m_bSubState = TPatcher.SSTATE_LEAVE
EndIf
EndIf
If m_bSubState = TPatcher.SSTATE_WORK Then
If m_oFileLoader.m_bComplete = 0 Then
m_oFileLoader.Process(2, 1024)
m_fProgress = ((m_iFileCount - (m_oFileHashList.Count() + 1)) / Float(m_iFileCount)) + m_oFileLoader.m_fProgress * (1.0 / m_iFileCount)
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("g[" + RSet(String(Int(m_fProgress * 100)), 3) + "%] Checking '" + m_oFileHashPair.stName + "'...")
Else
If m_oFileLoader.m_bComplete = 1
If m_oFileLoader.m_oBank <> Null Then
Local stHash:String = MD5Bank(m_oFileLoader.m_oBank).ToUpper()
If stHash <> m_oFileHashPair.stHash Then
m_oFileList.AddLast(m_oFileHashPair.stName)
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("h[ OK ] Checking '" + m_oFileHashPair.stName + "'... requires update.")
Else
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("g[ OK ] Checking '" + m_oFileHashPair.stName + "'... up to date.")
EndIf
Else
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("e[FAIL] Checking '" + m_oFileHashPair.stName + "'... (File in use?)")
EndIf
Else
m_oFileList.AddLast(m_oFileHashPair.stName)
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("h[ OK ] Checking '" + m_oFileHashPair.stName + "'... missing.")
EndIf
m_bSubState = TPatcher.SSTATE_ENTER
m_oFileLoader.Cleanup()
EndIf
EndIf
If m_bSubState = TPatcher.SSTATE_LEAVE Then
If m_oFileList.Count() > 0 Then
m_iFileCount = m_oFileList.Count()
SetState(TPatcher.STATE_PREPATCH)
Else
SetState(TPatcher.STATE_COMPLETE)
EndIf
EndIf
Case TPatcher.STATE_PATCHING
If m_bSubState = TPatcher.SSTATE_ENTER Then
Local m_oEntry:Object = m_oFileList.RemoveFirst()
If m_oEntry <> Null Then
m_oFile = String(m_oEntry);m_oFileDL = m_oFile + ".part"
CreateDir(ExtractDir(m_oFile), True)
m_oFileStream = WriteFile(m_oFileDL)
If m_oFileStream <> Null Then
m_oCurlMulti = TCurlMulti.Create()
m_oCurl = m_oCurlMulti.newEasy()
m_oCurl.setOptInt(CURLOPT_FOLLOWLOCATION, 1)
m_oCurl.setOptInt(CURLOPT_CRLF, False)
m_oCurl.setOptString(CURLOPT_USERAGENT, "Sirius Online Launcher")
m_oCurl.setOptString(CURLOPT_REFERER, TPatcher.SERVER + "info")
m_oCurl.setOptString(CURLOPT_URL, TPatcher.SERVER + m_oFile)
m_oCurl.setWriteStream(m_oFileStream)
m_oCurl.setProgressCallback(TPatcher.ProgressCallback, Self)
m_oTaskList.AddFirst("n[WAIT] Downloading '" + m_oFile + "'...")
m_bSubState = TPatcher.SSTATE_WORK
EndIf
Else
SetState(TPatcher.STATE_DEFAULT)
EndIf
EndIf
If m_bSubState = TPatcher.SSTATE_WORK Then
Local iResult:Int = Perform()
m_fProgress = ((m_iFileCount - (m_oFileList.Count() + 1)) / Float(m_iFileCount)) + m_fCurlProgress * (1.0 / m_iFileCount)
If iResult = CURLM_OK Then
m_oFileStream.Flush()
If m_fCurlProgress = 1.0 And m_iCurlMultiHandles = 0 Then
m_oCurlMulti.multiRemove(m_oCurl)
m_oCurlMulti.multiCleanup()
Local oCurlRCode:Int = m_oCurl.getInfo().responseCode()
m_oCurl.cleanup()
' Cleanup(1)
m_oCurl = Null
m_oCurlMulti = Null
m_oFileStream.Close()
m_oFileStream = Null
If oCurlRCode <> 404 Then
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("g[ OK ] Downloading '" + m_oFile + "'...")
' Rename files
If m_oFile <> "Launcher.exe" Then
RenameFile(m_oFile, m_oFile + ".old")
If RenameFile(m_oFileDL, m_oFile) Then
DeleteFile(m_oFile + ".old")
Else
RenameFile(m_oFile + ".old", m_oFile)
EndIf
Else' If we patched the launcher, tell the parent program to start the new one.
RenameFile(m_oFileDL, "-" + m_oFile)
m_oFileList.Clear() ' Clear the list to be sure
SetState(TPatcher.STATE_LAUNCHER)
EndIf
Else
DeleteFile(m_oFileDL)
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("e[FAIL] Downloading '" + m_oFile + "'... file not found.")
EndIf
'Cleanup (2)
m_oFileDL = Null
m_oFile = Null
If m_oFileList.Count() = 0 Then
m_bSubState = TPatcher.SSTATE_LEAVE
Else
m_bSubState = TPatcher.SSTATE_ENTER
EndIf
Else
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("n[" + RSet(String(Int(m_fCurlProgress * 100)), 3) + "%] Downloading '" + m_oFile + "'...")
EndIf
Else
m_oFileStream.Close()
m_oTaskList.RemoveFirst()
m_oTaskList.AddFirst("e[FAIL] Downloading '" + m_oFile + "'... " + CurlError(iResult))
EndIf
EndIf
If m_bSubState = TPatcher.SSTATE_LEAVE And m_bState = TPatcher.STATE_PATCHING Then
SetState(TPatcher.STATE_DEFAULT)
EndIf
EndSelect
EndMethod
Method Advance()
Select m_bState
Case TPatcher.STATE_PATCHINFO
SetState(TPatcher.STATE_CHECKING)
Case TPatcher.STATE_PREPATCH
SetState(TPatcher.STATE_PATCHING)
Case TPatcher.STATE_COMPLETE
m_stProcessToRun = "ExeFile.exe"
Case TPatcher.STATE_LAUNCHER
m_stProcessToRun = "-Launcher.exe 1"
EndSelect
EndMethod
Field m_iCurlMultiHandles:Int, m_bCurlMultiState:Byte = 0
Method Perform:Int()
Local iResult:Int = 0
iResult = MultiPerform()
Select m_bCurlMultiState
Case 0, 2
m_bCurlMultiState = (m_bCurlMultiState + 1) Mod 3
Case 1
If m_oCurlMulti.multiSelect(0.1) <> -1 And m_iCurlMultiHandles Then
iResult = MultiPerform()
If m_iCurlMultiHandles = 0 Then m_bCurlMultiState = 2
EndIf
EndSelect
Return iResult
EndMethod
Method MultiPerform:Int()
Local iResult:Int = m_oCurlMulti.multiPerform(m_iCurlMultiHandles)
While iResult = CURLM_CALL_MULTI_PERFORM
iResult = m_oCurlMulti.multiPerform(m_iCurlMultiHandles)
Wend
Return iResult
EndMethod
' State Management
Method SetState(bState:Byte)
m_bState = bState
m_bSubState = TPatcher.SSTATE_ENTER
m_fProgress = 0.0
EndMethod
Method GetState:Byte()
Return m_bState
EndMethod
' Information
Method GetProgress:Float()
Return m_fProgress
EndMethod
Field m_stProcessToRun:String = NUll
Method GetShutdown:String()
Return m_stProcessToRun
EndMethod
' Callbacks
Function ProgressCallback:Int(oPatcherObj:Object, dDownloadTotal:Double, dDownloadNow:Double, dUploadTotal:Double, dUploadNow:Double)
If dDownloadTotal = 0 And dDownloadNow = 0 And dUploadTotal = 0 And dUploadNow = 0 Then TPatcher(oPatcherObj).m_fCurlProgress = 0.0;Return 0
TPatcher(oPatcherObj).m_fCurlProgress = (dDownloadNow / dDownloadTotal)
EndFunction
EndType
Type TFileHash
Field stName:String, stHash:String
Function Create:TFileHash(stName:String, stHash:String)
Local oFH:TFileHash = New TFileHash
oFH.stName = stName
oFH.stHash = stHash
Return oFH
EndFunction
EndType
Type TAsyncLoader
Field m_oBank:TBank
Field m_oStream:TStream, m_iFileSize:Int
Field m_fProgress:Float, m_bComplete:Byte
Method Initialize(stFile:String)
If FileType(stFile) = FILETYPE_FILE Then
m_oStream = ReadFile(stFile)
If m_oStream <> Null Then
m_iFileSize = FileSize(stFile)
m_oBank = CreateBank(m_iFileSize)
If m_oBank <> Null Then
m_fProgress = 0.0
m_bComplete = False
Else
m_oStream.Close()
EndIf
Else
m_fProgress = 1.0
m_bComplete = -2
EndIf
Else
m_fProgress = 1.0
m_bComplete = -2
EndIf
EndMethod
Method Process(iMs:Float, iBufferSize:Int = 1024)
Local iClockStart:Int = getClock()
If (m_oStream <> Null And m_oBank <> Null) And m_oStream.Eof() = False Then
Local iClockNow:Int = getClock()
Repeat
Local iRemaining:Int = Min(m_iFileSize - m_oStream.Pos(), iBufferSize)
m_oBank.Read(m_oStream, m_oStream.Pos(), iRemaining)
iClockNow = getClock()
Until m_oStream.Eof() = True or getClockDiff(iClockStart, iClockNow) > (iMs / 1000.0 * getClocksPerSecond())
m_fProgress = m_oStream.Pos() / Float(m_iFileSize)
m_bComplete = m_oStream.Eof()
If m_oStream.Eof() = True Then
m_oStream.Close()
m_oStream = Null
EndIf
ElseIf m_bComplete = 0 Then
m_fProgress = 1.0
m_bComplete = -1
EndIf
EndMethod
Method Cleanup()
m_oBank = Null
EndMethod
EndType
Function DebugClock(Text:String)
DebugLog getClock() + ":" + Text
EndFunction
Extern "c"
Function getClocksPerSecond:Int()="getClocksPerSecond_"
Function getClock:Int()="getClock_"
Function getClockDiff:Int(clockStart:Int, clockEnd:Int)="getClockDiff_"
EndExtern