Polyphem Posted September 7, 2008 Share Posted September 7, 2008 (edited) HiHo, an idea I had...maybe useful to some, but mainly I hope for comments - to show me how bad this idea is, - to further improve this function, - or where I ignored coding conventions ... Performance is quiet okay, some 38 seconds for >30.000 pictures. Main advantage I see is that any subsequent operation for file analyzing can now be performed on the DB. expandcollapse popup#cs ---------------------------------------------------------------------------- AutoIt Version: 3.2.12.1 Author: Polyphem Script Function: _FileListToSqlite Script Version: v03 (2008-Sep-07) #ce ---------------------------------------------------------------------------- #include<SQLite.au3> Global $hQuery, $aRow MsgBox(270400,"IMPORTANT","This application uses an SQLite database component. Make sure it is are properly installed on your machine.") _SQLite_Startup() _SQLite_Open() ; open :memory: Database _SQLite_Exec(-1, "CREATE TABLE filelist (File_Path_Long,File_Path_Short,File_Name_Long,File_Name_Short,File_Extension,File_Time_Modified,File_Time_Created,File_Time_Accessed,File_Size,File_Attrib);") $begin = TimerInit() _FileListToSqlite(@ScriptDir) $dif = TimerDiff($begin) MsgBox(0,"Runtime","Runtime: " & $dif & @crlf & "Returned Error-Code: " & @error) _SQLite_Query(-1, "SELECT * FROM filelist;", $hQuery) $i = 0 While _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK $i += 1 ;MsgBox(0,"",$aRow[0] & @crlf & $aRow[1] & @crlf & $aRow[2] & @crlf & $aRow[3] & @crlf & $aRow[4] & @crlf & $aRow[5] & @crlf & $aRow[6] & @crlf & $aRow[7] & @crlf& $aRow[8] & @crlf & $aRow[9]) WEnd MsgBox(0,"","Finished, found dirs/files: " & $i) _SQLite_Exec(-1, "DROP TABLE filelist;") _SQLite_Close() _SQLite_Shutdown() ;=============================================================================== ; ; Function Name: _FileListToSqlite($sPath) ; Description:: ; Parameter(s): $sPath - Top Folder Location to scan ; $sRecurse - 0 = dont recurse, 1 = recurse ; Requirement(s): AutoIt 3.2.12.1 ; Return Value(s): 0 - Success ; 1 - $sPath is not a directory ; Author(s): Polyphem ; ; ;=============================================================================== ; Func _FileListToSqlite($sPath = @ScriptDir,$sRecurse = 1) if StringInStr(FileGetAttrib($sPath), "D") = false Then Return SetError(1, 0, 0) If StringRight($sPath, 1) <> "\" Then $sPath &= "\" ;Ensure starting folder has a trailing slash $sSearch = FileFindFirstFile($sPath & "*.*") While 1 ;Search through all files and folders in directory $sNext = FileFindNextFile($sSearch) If @error Then ExitLoop if stringinstr($sNext,".",0,-1) > 0 Then $sFileExtension = StringRight($sNext,StringLen($sNext)-stringinstr($sNext,".",0,-1)) Else $sFileExtension = "" EndIf _SQLite_Exec(-1, 'INSERT INTO filelist (File_Path_Long,File_Path_Short,File_Name_Long,File_Name_Short,File_Extension,File_Time_Modified,File_Time_Created,File_Time_Accessed,File_Size,File_Attrib) VALUES' & '("' & $sPath & '","' & FileGetShortName($sPath) & '","' & $sNext & '","' & FileGetShortName($sNext) & '","' & $sFileExtension & '","' & FileGetTime($sPath & $sNext,0,1) & '","' & FileGetTime($sPath & $sNext,1,1) & '","' & FileGetTime($sPath & $sNext,2,1) & '","' & FileGetSize($sPath & $sNext) & '","' & FileGetAttrib($sPath & $sNext) & '");') If StringInStr(FileGetAttrib($sPath & $sNext), "D") AND $sRecurse = 1 Then _FileListToSqlite($sPath & $sNext) EndIf WEnd Return SetError(0, 0, 0) EndFunc Cheers and a nice sunday m8s Poly Edited September 8, 2008 by Polyphem This post will be edited again by Polyphem: Tomorrow, 11:55 AM Link to comment Share on other sites More sharing options...
Polyphem Posted September 8, 2008 Author Share Posted September 8, 2008 (edited) Sliiightly improved performance AND functions and added an example to the code. The performance is influenced heavily by the type of search performed, sloooooow with looooooots of details, quiet fast with few details. For usage in your own script I would recommend to perform SQL-Queries as needed. _SQLite_GetTable2d is not a too fast function ... Performance Test: Scan 1: 51 files, 7.8 secs (full AutoIt native + _FileGetProperty details, ProgressOn) Scan 2: 2.627 files, 2.65 secs (full AutoIt native details, ProgressOn) Scan 3: 2.627 files, 0.62 secs (minimal AutoIt native details, ProgressOn) Scan 4: 2.627 files, 0.55 secs (minimal AutoIt native details, ProgressOff) Scan 5: 30.152 files, 6.5 secs (minimal AutoIt native details, ProgressOff) => same file-set as above! Performance-Improvment of some 580% For best performance design your SQL-Queries on 'filelist' carefully, I think many operations now performed with AutoIt can be ported to SQLite... ToDo's: - Add include/exclude selection by file-extension / Regex optional - Add include/exclude selection by folder-patern / Regex optional - ... Update: Upsa, naming collision caused by _FileGetProperty() in SQL-statement due to different OS Language... tested it with German OS which returned "Größe", now working with an US language OS which returned "Size"... sadly the same name I used before for the AutoIt-native Size field... expandcollapse popup#cs ---------------------------------------------------------------------------- AutoIt Version: 3.2.12.1 Author: Polyphem Script Function: _FileListToSqlite Script Version: v10b (2008-Sep-09) #ce ---------------------------------------------------------------------------- #include<Array.au3> #include<SQLite.au3> #include<SQLite.dll.au3> Global $hQuery, $aRow, $sTimerInit, $sTimerDif, $sScanProgressCount, $sPathSizeCount, $sProgressCount, $sProgressCountSave Local $aResult, $iRows, $iColumns, $iRval _SQLite_Startup() if @error = 1 Then MsgBox(270400,"IMPORTANT","This application uses an SQLite database component (SQLite3.dll)." & @crlf & @crlf & "It tried to install a copy in the directory" & @crlf & @crlf & @SystemDir & @crlf & @crlf & "and failed. Please check manually.") Exit EndIf _SQLite_Open() ; open :memory: Database - add filename to store information for later use $select_input_folder = FileSelectFolder ( "Select Folder to Scan..." & @crlf & @crlf & "First scan with all attributes set... choose a small folder : )...", "",2 , @ScriptDir) if $select_input_folder = true then _FileListToSqlite($select_input_folder,1,16383,1) $sFileListToSqliteError = @error MsgBox(0,"Runtime","Runtime: " & $sTimerDif & @crlf & @crlf & "SQLite returned Error-Code: " & $sFileListToSqliteError & @crlf & @crlf & "Found dirs/files: " & $sPathSizeCount) _SQLite_GetTable2d (-1, "SELECT * FROM filelist;", $aResult, $iRows, $iColumns) _ArrayDisplay($aResult) endif $select_input_folder = FileSelectFolder ( "Select Folder to Scan..." & @crlf & @crlf & "Second scan with AutoIt native functions only... choose a bigger folder *g*...", "",2 , "C:\") if $select_input_folder = true then _FileListToSqlite($select_input_folder,1,8191,1) $sFileListToSqliteError = @error MsgBox(0,"Runtime","Runtime: " & $sTimerDif & @crlf & @crlf & "SQLite returned Error-Code: " & $sFileListToSqliteError & @crlf & @crlf & "Found dirs/files: " & $sPathSizeCount) _SQLite_GetTable2d (-1, "SELECT * FROM filelist;", $aResult, $iRows, $iColumns) _ArrayDisplay($aResult) endif $select_input_folder = FileSelectFolder ( "Select Folder to Scan..." & @crlf & @crlf & "Third scan with minimal AutoIt native functions only... choose biggest folder *ggg*...", "",2 , "C:\") if $select_input_folder = true then _FileListToSqlite($select_input_folder,1,4,1) $sFileListToSqliteError = @error MsgBox(0,"Runtime","Runtime: " & $sTimerDif & @crlf & @crlf & "SQLite returned Error-Code: " & $sFileListToSqliteError & @crlf & @crlf & "Found dirs/files: " & $sPathSizeCount) _SQLite_GetTable2d (-1, "SELECT * FROM filelist;", $aResult, $iRows, $iColumns) _ArrayDisplay($aResult) endif $select_input_folder = FileSelectFolder ( "Select Folder to Scan..." & @crlf & @crlf & "Forth scan with minimal AutoIt native functions only and progress off... fastest", "",2 , "C:\") if $select_input_folder = true then _FileListToSqlite($select_input_folder,1,4,0) $sFileListToSqliteError = @error MsgBox(0,"Runtime","Runtime: " & $sTimerDif & @crlf & @crlf & "SQLite returned Error-Code: " & $sFileListToSqliteError & @crlf & @crlf & "Found dirs/files: " & $sPathSizeCount) _SQLite_GetTable2d (-1, "SELECT * FROM filelist;", $aResult, $iRows, $iColumns) _ArrayDisplay($aResult) endif $select_input_folder = FileSelectFolder ( "Select Folder to Scan..." & @crlf & @crlf & "Fifth scan with minimal AutoIt native functions only and progress off... fastest", "",2 , "C:\") if $select_input_folder = true then _FileListToSqlite($select_input_folder,1,2048,0) $sFileListToSqliteError = @error MsgBox(0,"Runtime","Runtime: " & $sTimerDif & @crlf & @crlf & "SQLite returned Error-Code: " & $sFileListToSqliteError & @crlf & @crlf & "Found dirs/files: " & $sPathSizeCount) _SQLite_GetTable2d (-1, "SELECT * FROM filelist;", $aResult, $iRows, $iColumns) _ArrayDisplay($aResult) endif _SQLite_Exec(-1, "DROP TABLE filelist;") _SQLite_Close() _SQLite_Shutdown() ;=============================================================================== ; ; Function Name: _FileListToSqlite($sPath,$sRecurse,$sFiledetails,$sShowProgress) ; Description:: ; Parameter(s): $sPath - Top Folder Location to scan (default = @scriptdir) ; $sRecurse - 0 = dont recurse, 1 = recurse (default) ; $sFiledetails (Add up values as required) => ; ; AutoIt-Functions ; ; 1 = include only Folder information ; 2 = include only File information (overrides above) ; 4 = include File AND Folder information (Default, overrides both above) ; ; 8 = include Filepath Long (Default, forced if Filepath Short is not selected) ; 16 = include Filepath Short ; 32 = include Filename Long (Default, forced if Filename Short is not selected) ; 64 = include Filename Short ; 128 = include File Extension ; 256 = include Filetime (Modified) ; 512 = include Filetime (Created) ; 1024 = include Filetime (Accessed) ; 2048 = include File-Size in Byte ; 4096 = include File-Attributes ; ; 8191 = Add all Information based on AutoIt-Functions to Table ; ; _FileGetProperty Function ; ; 8192 = Add all attributes... ; kinda overkill, but seems to be Windows Language and SP/KB dependant... all or nothing *g*... ; sadly this makes it nearly impossible to port : (... anyone got an idea? ; ; 16383 = Add all Information to Table ; ; $sShowProgress - 0 = off, 1 = on (default) ; ; Requirement(s): AutoIt 3.2.12.1 ; Return Value(s): 0 - Success ; 1 - $sPath is not a directory ; Author(s): Polyphem ; ; ;=============================================================================== ; Func _FileListToSqlite($sPath = @ScriptDir, $sRecurse = 1, $sFiledetails = 8191 , $sShowProgress = 1) Local $aNames $sTimerInit = TimerInit() $sPathSizeCount = 0 ; reset $sPathSizeCount if BitOR($sRecurse,0,1) <> 1 then $sRecurse = 1 ; if $sRecurse is not set to 0 or 1 set to 1 if BitAnd($sFiledetails, 4) Then ; if $sFiledetails contains 4 (include File AND Folder information) then subtract 1 (include only Folder information) and 2 (include only File information) if in $sFiledetails if BitAnd($sFiledetails, 2) Then $sFiledetails -= 2 EndIf if BitAnd($sFiledetails, 1) Then $sFiledetails -= 1 EndIf elseif BitAnd($sFiledetails, 2) Then ; if $sFiledetails contains 2 (include only File information) then subtract 1 if in $sFiledetails if BitAnd($sFiledetails, 1) Then $sFiledetails -= 1 EndIf Else ; else add 4 (include File AND Folder information ) to $sFiledetails (default) $sFiledetails += 4 EndIf if $sRecurse = 1 Then $sPathSize = DirGetSize($sPath, 1) Else $sPathSize = DirGetSize($sPath, 2) EndIf if @error Then Return SetError(1, 0, 0) ;Ensure $sPath is a directory, DirGetSize returns @error 1 if the path doesn't exist. if BitAnd($sFiledetails, 1) Then $sPathSizeCount = $sPathSize[2] ElseIf BitAnd($sFiledetails, 2) Then $sPathSizeCount = $sPathSize[1] Else $sPathSizeCount = $sPathSize[1]+$sPathSize[2] EndIf if $sShowProgress = 1 Then $sProgressCount = 0 ; reset $sProgressCount $sProgressCountSave = 0 ; reset $sProgressCountSave ProgressOn("Filelist Scan Progress", "Percentage done", stringformat("%3s","0") & "%, Scanning file 0 of " & $sPathSizeCount & " files") WinMove("Filelist Scan Progress","",650,250) endif _SQLite_FetchNames(-1, $aNames) _SQLite_Exec(-1, "DROP TABLE filelist;") ; How to check efficently, that table already existed : )? $sSqlBuild_Table = "" ; Start building the Query if BitAnd($sFiledetails, 8) or BitAnd($sFiledetails, 16) = false then ; Filepath Long, default and forced if Filepath Short is not selected $sSqlBuild_Table &= ',Path_Long' endif if BitAnd($sFiledetails, 16) OR BitAnd($sFiledetails, 64) then ; Filepath Short, also needed for determination of $sFileGetShortName_File final value $sSqlBuild_Table &= ',Path_Short' endif if BitAnd($sFiledetails, 32) or BitAnd($sFiledetails, 64) = false then ; Filename Long, default and forced if Filename Short is not selected $sSqlBuild_Table &= ',Name_Long' endif if BitAnd($sFiledetails, 64) then ; Filename Short $sSqlBuild_Table &= ',Name_Short' endif if BitAnd($sFiledetails, 128) then ; File Extension $sSqlBuild_Table &= ',Extension' endif if BitAnd($sFiledetails, 256) then ; Filetime (Modified) $sSqlBuild_Table &= ',Time_Modified' endif if BitAnd($sFiledetails, 512) then ; Filetime (Created) $sSqlBuild_Table &= ',Time_Created' endif if BitAnd($sFiledetails, 1024) then ; Filetime (Accessed) $sSqlBuild_Table &= ',Time_Accessed' endif if BitAnd($sFiledetails, 2048) then ; Filetime (Accessed) $sSqlBuild_Table &= ',Size' endif if BitAnd($sFiledetails, 4096) then ; File-Attributes $sSqlBuild_Table &= ',Attrib' endif if BitAnd($sFiledetails, 8192) then ; FileGetProperty Function, add all attributes $aSqlBuild_Table_FileGetProperty = _FileGetProperty(@ScriptFullPath) for $i = 1 to $aSqlBuild_Table_FileGetProperty[0][0] $sSqlBuild_Table &= ',"' & "FPG_" & $aSqlBuild_Table_FileGetProperty[$i][0] & '"' Next endif $sSqlBuild_Table &= ')' $sSqlBuild_Table = '(' & StringRight($sSqlBuild_Table,stringlen($sSqlBuild_Table)-1) _SQLite_Exec(-1, 'CREATE TABLE filelist ' & $sSqlBuild_Table & ';') _FileListToSqliteDo($sPath, $sRecurse, $sFiledetails, $sShowProgress, $sSqlBuild_Table) $sTimerDif = TimerDiff($sTimerInit) if $sShowProgress = 1 Then ProgressSet(100 , "Done", "Complete") sleep(500) ProgressOff() endif if @error Then Return SetError(@error, 0, 0) EndFunc Func _FileListToSqliteDo($sPath, $sRecurse, $sFiledetails, $sShowProgress, $sSqlBuild_Table) Local $sFileExtension, $aFilelistQuery, $sFileGetAttrib If StringRight($sPath, 1) <> "\" Then $sPath &= "\" ; Ensure $sPath has a trailing slash $sSearch = FileFindFirstFile($sPath & "*.*") While 1 ; Search through all files and folders in directory $sNext = FileFindNextFile($sSearch) If @error Then ExitLoop if $sRecurse = 1 or BitAnd($sFiledetails, 128) or BitAnd($sFiledetails, 4096) or BitAnd($sFiledetails, 1) or BitAnd($sFiledetails, 2) or BitAnd($sFiledetails, 8192) Then ; FileGetAttrib needed for Recurse search OR determination of file-extension OR determination of file-attributes themselves $sFileGetAttrib = FileGetAttrib($sPath & $sNext) endif if BitAnd($sFiledetails, 1) Then ; add Folder-Flag set only If StringInStr($sFileGetAttrib, "D") = 0 Then ContinueLoop EndIf if $sShowProgress = 1 Then $sProgressCount += 1 if round($sProgressCount/$sPathSizeCount*100,0) > $sProgressCountSave Then $sProgressCountSave = round($sProgressCount/$sPathSizeCount*100,0) ProgressSet( $sProgressCountSave, stringformat("%3s",round($sProgressCount/$sPathSizeCount*100,0)) & "%, Scanning file " & $sProgressCount & " of " & $sPathSizeCount & " files") endif endif $sSqlBuild_Query = "" ; Start building the Query if BitAnd($sFiledetails, 8) or BitAnd($sFiledetails, 16) = false then ; Filepath Long, default and forced if Filepath Short is not selected $sSqlBuild_Query &= ',"' & $sPath & '"' endif if BitAnd($sFiledetails, 16) OR BitAnd($sFiledetails, 64) then ; Filepath Short, also needed for determination of $sFileGetShortName_File final value if BitAnd($sFiledetails, 64) Then $sFileGetShortName_Path = FileGetShortName($sPath) $sSqlBuild_Query &= ',"' & $sFileGetShortName_Path & '"' Else $sSqlBuild_Query &= ',"' & FileGetShortName($sPath) & '"' endif endif if BitAnd($sFiledetails, 32) or BitAnd($sFiledetails, 64) = false then ; Filename Long, default and forced if Filename Short is not selected $sSqlBuild_Query &= ',"' & $sNext & '"' endif if BitAnd($sFiledetails, 64) then ; Filename Short $sFileGetShortName_File = FileGetShortName($sPath & $sNext) $sSqlBuild_Query &= ',"' & stringright($sFileGetShortName_File,stringlen($sFileGetShortName_File)-stringlen($sFileGetShortName_Path)) & '"' endif if BitAnd($sFiledetails, 128) then ; File Extension if stringinstr($sNext,".",0,-1) > 0 AND StringInStr($sFileGetAttrib, "D") = false Then $sSqlBuild_Query &= ',"' & StringLower(StringRight($sNext,StringLen($sNext)-stringinstr($sNext,".",0,-1))) & '"' Else $sSqlBuild_Query &= ',""' $sFileExtension = "" EndIf endif if BitAnd($sFiledetails, 256) then ; Filetime (Modified) $sSqlBuild_Query &= ',"' & FileGetTime($sPath & $sNext,0,1) & '"' endif if BitAnd($sFiledetails, 512) then ; Filetime (Created) $sSqlBuild_Query &= ',"' & FileGetTime($sPath & $sNext,1,1) & '"' endif if BitAnd($sFiledetails, 1024) then ; Filetime (Accessed) $sSqlBuild_Query &= ',"' & FileGetTime($sPath & $sNext,2,1) & '"' endif if BitAnd($sFiledetails, 2048) then ; Filetime (Accessed) $sSqlBuild_Query &= ',"' & FileGetSize($sPath & $sNext) & '"' endif if BitAnd($sFiledetails, 4096) then ; File-Attributes $sSqlBuild_Query &= ',"' & $sFileGetAttrib & '"' endif if BitAnd($sFiledetails, 8192) then ; FileGetProperty Function, add all attributes $aSqlBuild_Query_FileGetProperty = _FileGetProperty($sPath & $sNext) for $i = 1 to $aSqlBuild_Query_FileGetProperty[0][0] $sSqlBuild_Query &= ',"' & $aSqlBuild_Query_FileGetProperty[$i][1] & '"' Next endif $sSqlBuild_Query &= ');' $sSqlBuild_Query = '(' & StringRight($sSqlBuild_Query,stringlen($sSqlBuild_Query)-1) If (BitAnd($sFiledetails, 2) <> 0 AND StringInStr($sFileGetAttrib, "D") = 0) or (BitAnd($sFiledetails, 1) AND StringInStr($sFileGetAttrib, "D") > 0) or BitAnd($sFiledetails, 4) Then ; Evaluate add Files-Flag, add Folder-Flag and add Files-and-Folders-Flag ; Can only be done here, because even when File-Flag only is set $sRecurse might be set and Folder-Info is needed for loop below _SQLite_Exec(-1, 'INSERT INTO filelist ' & $sSqlBuild_Table & ' VALUES' & $sSqlBuild_Query) EndIf if $sRecurse = 1 then If StringInStr($sFileGetAttrib, "D") Then _FileListToSqliteDo($sPath & $sNext ,1 , $sFiledetails, $sShowProgress, $sSqlBuild_Table) endif EndIf WEnd Return SetError(0, 0, 0) EndFunc ; http://www.autoitscript.com/forum/index.php?showtopic=34732 ;=============================================================================== ; Function Name.....: _FileGetProperty ; Description.......: Returns a property or all properties for a file. ; Version...........: 1.0.2 ; Change Date.......: 2008-07-28 ; AutoIt Version....: 3.2.12.1 ; ; Parameter(s)......: $S_PATH - String containing the file path to return the property from. ; $S_PROPERTY - [optional] String containing the name of the property to return. (default = "") ; Requirements(s)...: None ; Return Value(s)...: Success: Returns a string containing the property value. ; If $S_PROPERTY is empty, an two-dimensional array is returned: ; $av_array[0][0] = Number of properties. ; $av_array[1][0] = 1st property name. ; $as_array[1][1] = 1st property value. ; $av_array[n][0] = nth property name. ; $as_array[n][1] = nth property value. ; Failure: Returns 0 and sets @error to: ; 1 = The folder $S_PATH does not exist. ; 2 = The property $S_PROPERTY does not exist or the array could not be created. ; 3 = Unable to create the "Shell.Application" object $objShell. ; ; Author(s).........: - Simucal <Simucal@gmail.com> ; - Modified by: Sean Hart <autoit@hartmail.ca> ; - Modified by: teh_hahn <sPiTsHiT@gmx.de> ; Company...........: None ; URL...............: None ; Note(s)...........: None ;=============================================================================== Func _FileGetProperty(Const $S_PATH, Const $S_PROPERTY = "") If Not FileExists($S_PATH) Then Return SetError(1, 0, 0) Local Const $S_FILE = StringTrimLeft($S_PATH, StringInStr($S_PATH, "\", 0, -1)) Local Const $S_DIR = StringTrimRight($S_PATH, StringLen($S_FILE) + 1) Local Const $objShell = ObjCreate("Shell.Application") If @error Then Return SetError(3, 0, 0) Local Const $objFolder = $objShell.NameSpace($S_DIR) Local Const $objFolderItem = $objFolder.Parsename($S_FILE) If $S_PROPERTY Then For $i = 0 To 99 If $objFolder.GetDetailsOf($objFolder.Items, $i) = $S_PROPERTY Then Return $objFolder.GetDetailsOf($objFolderItem, $i) Next Return SetError(2, 0, 0) EndIf Local $av_ret[1][2] = [[1]] For $i = 0 To 99 If $objFolder.GetDetailsOf($objFolder.Items, $i) Then ReDim $av_ret[$av_ret[0][0] + 1][2] $av_ret[$av_ret[0][0]][0] = $objFolder.GetDetailsOf($objFolder.Items, $i) $av_ret[$av_ret[0][0]][1] = $objFolder.GetDetailsOf($objFolderItem, $i) $av_ret[0][0] += 1 EndIf Next If Not $av_ret[1][0] Then Return SetError(2, 0, 0) $av_ret[0][0] -= 1 Return $av_ret EndFunc ;==>_FileGetProperty Pls comment ... Cheers Poly Edited September 9, 2008 by Polyphem This post will be edited again by Polyphem: Tomorrow, 11:55 AM Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now