Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <#
- .SYNOPSIS
- FF7 PC to Gaming Device Synchronizer
- .DESCRIPTION
- Connects via FTP to a compatible gaming device that utilizes a common format for memory cards, compares the age of the
- targeted save file versus the age of the save file in the local save path and then synchronizes the two save files,
- including the conversion between file types and the modification of the metadata.xml file.
- .NOTES
- Author: n1ckn4m3 - my.n1ckn4m3@gmail.com
- .DEPENDENCIES
- PSFTP Module by Michal Gajda: https://gallery.technet.microsoft.com/scriptcenter/PowerShell-FTP-Client-db6fe0cb
- FF7Save Converter: http://www.zophar.net/utilities/genutil/ff7-savegame-converter.html
- .USAGE
- * Download and Import the PSFTP Module and ensure that Import-Module PSFTP successfully completes
- * Create a working directory for the script and copy the contents of the FF7Save.zip to that directory, as well as this script
- ** Make sure that the user account running the script has write access to the working directory you've chosen, else you'll need to elevate privileges for the script to work
- * Update the script variables to be accurate for your environment. Key ones include $WORKINGDIR, $SAVESLOT, $ANDROIDSAVE, $ANDROIDFTP, $ANDROIDDIR, $USERNAME, and $PASSWORD
- * MAKE BACKUP COPIES OF YOUR SAVE FILES FIRST. The script does this, but considering it replaces files, better safe than sorry. I accept no liability if you overwrite
- saves or nuke a 400 hour perfect game, or if your HDD turns into a megalomanical robot trying to take over the world. In short, use at your own risk. Provided free with
- no warranty of fitness for any purpose, implied or otherwise.
- #>
- # I tried doing this without this module and it was hacky, ugly, and stupid. Thanks, guy who wrote this module.
- Import-Module PSFTP
- # Working Directory. Change this to whatever you'd like, just make sure it has the FF7 Save converter in it, that it exists, and that it's writable by your user account.
- $WORKINGDIR = ([environment]::getfolderpath(“mydocuments”)) + "\Square Enix\FF7 Save Sync\"
- # General Script Variables
- $SAVESLOT = "00" # Use the physical save slot ID, e.g., for save00.ff7, use 00 - NOT 1 - the script relies on this
- $SAVEDIRS = Get-ChildItem ([environment]::getfolderpath(“mydocuments”))"Square Enix\Final Fantasy VII Steam\"
- $LOCALSAVE = $SAVEDIRS[1].FullName + "\save" + $SAVESLOT + ".ff7"
- $LOCALBACKUP = $LOCALSAVE + ".backup"
- $WORKINGLOCALSAVE = $WORKINGDIR + "save" + $SAVESLOT + ".ff7"
- $LOCALSAVEFILE = "save"+$SAVESLOT+".ff7"
- # Metadata.XML Specific Variables
- $MD5SALT = $WORKINGDIR + "md5salt.bin"
- $METADATAXML = $SAVEDIRS[1].FullName + "\Metadata.xml"
- $METADATABACKUP = $METADATAXML + ".backup"
- $WORKINGMETADATAXML = $WORKINGDIR + "Metadata.xml"
- $WORKINGLOCALMD5 = $WORKINGDIR + "save" + $SAVESLOT + ".ff7.md5"
- # Metadata.XML Lookup - Save Slot to XML Line ID
- If ($SAVESLOT -eq "00"){ $XMLLINE = 18 ; $SAVEID = 1}
- If ($SAVESLOT -eq "01"){ $XMLLINE = 36 ; $SAVEID = 2}
- If ($SAVESLOT -eq "02"){ $XMLLINE = 54 ; $SAVEID = 3}
- If ($SAVESLOT -eq "03"){ $XMLLINE = 72 ; $SAVEID = 4}
- If ($SAVESLOT -eq "04"){ $XMLLINE = 90 ; $SAVEID = 5}
- If ($SAVESLOT -eq "05"){ $XMLLINE = 108 ; $SAVEID = 6}
- If ($SAVESLOT -eq "06"){ $XMLLINE = 126 ; $SAVEID = 7}
- If ($SAVESLOT -eq "07"){ $XMLLINE = 144 ; $SAVEID = 8}
- If ($SAVESLOT -eq "08"){ $XMLLINE = 162 ; $SAVEID = 9}
- If ($SAVESLOT -eq "09"){ $XMLLINE = 180 ; $SAVEID = 10}
- # Android .MCD file locations and names, update to reflect yours. Backup should be in the same directory to streamline but I suppose it would be fine anywhere.
- $ANDROIDDIR = "/mnt/external_sd/MEMCARDS/"
- $ANDROIDSAVEFILE = "ff7_pc.mcd"
- $ANDROIDSAVE = $ANDROIDDIR + $ANDROIDSAVEFILE
- $ANDROIDBACKUP = $ANDROIDDIR + $ANDROIDSAVEFILE + ".backup"
- $WORKINGANDROIDSAVE = $WORKINGDIR + $ANDROIDSAVEFILE
- $WORKINGANDROIDBACKUP = $WORKINGDIR + $ANDROIDSAVEFILE + ".backup"
- #FTP Support Variables, update to reflect your specifics.
- $ANDROIDFTP="ftp://gpdxd:31337"
- $USERNAME = "<username>"
- $PASSWORD = "android" | ConvertTo-SecureString -asPlainText -Force
- $CREDENTIAL = New-Object System.Management.Automation.PSCredential($USERNAME,$PASSWORD)
- # Set Save Sync Directory
- Set-Location $WORKINGDIR
- # Delete working copies of saves if they exist
- If (Test-Path $WORKINGLOCALSAVE){Remove-Item $WORKINGLOCALSAVE}
- If (Test-Path $WORKINGANDROIDSAVE){Remove-Item $WORKINGANDROIDSAVE}
- If (Test-Path $WORKINGANDROIDBACKUP){Remove-Item $WORKINGANDROIDBACKUP}
- If (Test-Path $WORKINGMETADATAXML){Remove-Item $WORKINGMETADATAXML}
- If (Test-Path $WORKINGDIR\local.txt){Remove-Item $WORKINGDIR\local.txt}
- If (Test-Path $WORKINGDIR\remote.txt){Remove-Item $WORKINGDIR\remote.txt}
- If (Test-Path $WORKINGDIR\local.cmd){Remove-Item $WORKINGDIR\local.cmd}
- If (Test-Path $WORKINGDIR\remote.cmd){Remove-Item $WORKINGDIR\remote.cmd}
- # Create remote.txt to feed into FF7 Converter when Remote File is newer
- $REMOTETXT="1",$ANDROIDSAVEFILE,$SAVEID,"4",$LOCALSAVEFILE,$SAVEID,"1"
- $REMOTETXT | Set-Content -Path $WORKINGDIR\remote.txt
- $REMOTECMD = "ff7save < remote.txt"
- $REMOTECMD | Set-Content -Path $WORKINGDIR\remote.cmd
- # Create local.txt to feed into FF7 Converter when Local File is newer
- $LOCALTXT="4",$LOCALSAVEFILE,$SAVEID,"1",$ANDROIDSAVEFILE,$SAVEID,"1"
- $LOCALTXT | Set-Content -Path $WORKINGDIR\local.txt
- $LOCALCMD = "ff7save < local.txt"
- $LOCALCMD | Set-Content -Path $WORKINGDIR\local.cmd
- # Copy Local Save to Save Sync Directory
- Copy-Item $LOCALSAVE $WORKINGLOCALSAVE
- #Initiate Connection to Android Device, break script if we can't connect.
- Try {
- Set-FTPConnection -Credentials $CREDENTIAL -Server $ANDROIDFTP -Session AndroidFF7SyncSession -UsePassive -ErrorAction Stop | Out-Null
- }
- Catch {
- Write-Host "No response from FTP server, check variables and network connectivity. Aborting synchronization."
- # Clean up after ourselves, even if stuff goes south pretty early.
- If (Test-Path $WORKINGLOCALSAVE){Remove-Item $WORKINGLOCALSAVE}
- If (Test-Path $WORKINGANDROIDSAVE){Remove-Item $WORKINGANDROIDSAVE}
- If (Test-Path $WORKINGANDROIDBACKUP){Remove-Item $WORKINGANDROIDBACKUP}
- If (Test-Path $WORKINGLOCALMD5){Remove-Item $WORKINGLOCALMD5}
- If (Test-Path $MD5SALT){Remove-Item $MD5SALT}
- If (Test-Path $WORKINGDIR\local.txt){Remove-Item $WORKINGDIR\local.txt}
- If (Test-Path $WORKINGDIR\remote.txt){Remove-Item $WORKINGDIR\remote.txt}
- If (Test-Path $WORKINGDIR\local.cmd){Remove-Item $WORKINGDIR\local.cmd}
- If (Test-Path $WORKINGDIR\remote.cmd){Remove-Item $WORKINGDIR\remote.cmd}
- Break
- }
- # Since we know we can connect now, let's finish setting up the FTP session.
- $FTPSession = Get-FTPConnection -Session AndroidFF7SyncSession
- # Download Remote Save to Save Sync Directory and create working copy of backup
- Get-FTPItem -Session $FTPSession -Path $ANDROIDSAVE -LocalPath $WORKINGANDROIDSAVE | Out-Null
- Copy-Item $WORKINGANDROIDSAVE $WORKINGANDROIDBACKUP
- # Check Timestamp of Remote Save vs. Local Save
- $RemoteTimestamp = [datetime](Get-FTPChildItem -Session $FTPSession -Path /mnt/external_sd/MEMCARDS/ff7_pc.mcd).ModifiedDate
- $LocalTimestamp = [datetime](Get-ItemProperty -Path $WORKINGLOCALSAVE -Name LastWriteTime).lastwritetime
- # If Remote Save is newer, back up local save and replace
- If ($RemoteTimestamp -gt $LocalTimestamp) {
- If (Test-Path $LOCALBACKUP){Remove-Item $LOCALBACKUP}
- Copy-Item $WORKINGLOCALSAVE $LOCALBACKUP
- CMD /C "remote.cmd" | Out-Null # Convert .MCD format FF7 save to .FF7 save format save
- Remove-Item $LOCALSAVE ## Replace save00.ff7
- Copy-Item $WORKINGLOCALSAVE $LOCALSAVE
- Write-Host "Remote save was newer. Local save has been backed up and replaced."
- # Generate MD5 "salt" (not really a salt, but whatever) for Metadata.XML generation later
- $PROFILEID = $SAVEDIRS[1].Name
- $PROFILEID = $PROFILEID.Trim("user_")
- $PROFILEIDARRAY = $PROFILEID.ToCharArray();
- $BYTES = New-Object Byte[] 7
- $HEXARRAY=[int[]][char[]]$PROFILEIDARRAY
- $LOOP=0
- ForEach ($HEXLINE in $HEXARRAY){
- $BYTES[$LOOP]=$HEXLINE
- $LOOP = $LOOP + 1
- }
- [IO.File]::WriteAllBytes($MD5SALT,$BYTES)
- # Metadata.XML support code
- $COPYCMD = "copy /b save" + $SAVESLOT + ".ff7+md5salt.bin save" + $SAVESLOT + ".ff7.md5"
- CMD /C $COPYCMD | Out-Null # Concatenate existing save file w/ md5 "salt" to allow signature generation
- $MD5HASH = (Get-FileHash -Path "save" + $SAVESLOT + ".ff7.md5" -Algorithm md5).Hash
- $PREPPEDHASH = " <signature>"+$MD5HASH.ToLower()+"</signature>"
- $METADATAContent = Get-Content -Path $METADATAXML
- $METADATAContent[$XMLLINE] = $PREPPEDHASH # This value is only accurate if you're targeting the first save file.
- Copy-Item $METADATAXML $METADATABACKUP
- $METADATAContent | Set-Content -Path $METADATAXML
- # Re-set proper modified time, as downloading it changed it to 'right now'.
- $MODIFIEDSAVE = Get-Item $LOCALSAVE
- $MODIFIEDSAVE.LastWriteTime = $RemoteTimestamp
- }
- # If Local save is newer, back up remote save and replace
- If ($LocalTimestamp -gt $RemoteTimestamp) {
- # Re-upload downloaded ff7_pc.mcd to ff7_pc.mcd backup
- Add-FTPItem -Session $FTPSession -Path $ANDROIDDIR -LocalPath $WORKINGANDROIDBACKUP -Overwrite | Out-Null
- CMD /C "local.cmd" | Out-Null # Convert .ff7 format FF7 save to .MCD save format save
- Add-FTPItem -Session $FTPSession -Path $ANDROIDDIR -LocalPath $WORKINGANDROIDSAVE -Overwrite | Out-Null
- Write-Host "Local save was newer. Remote save has been backed up and replaced."
- }
- If ($LocalTimestamp -eq $RemoteTimestamp) {
- Write-Host "Timestamps the same, sync complete."
- }
- # Clean up after ourselves.
- If (Test-Path $WORKINGLOCALSAVE){Remove-Item $WORKINGLOCALSAVE}
- If (Test-Path $WORKINGANDROIDSAVE){Remove-Item $WORKINGANDROIDSAVE}
- If (Test-Path $WORKINGANDROIDBACKUP){Remove-Item $WORKINGANDROIDBACKUP}
- If (Test-Path $WORKINGLOCALMD5){Remove-Item $WORKINGLOCALMD5}
- If (Test-Path $WORKINGDIR\local.txt){Remove-Item $WORKINGDIR\local.txt}
- If (Test-Path $WORKINGDIR\remote.txt){Remove-Item $WORKINGDIR\remote.txt}
- If (Test-Path $WORKINGDIR\local.cmd){Remove-Item $WORKINGDIR\local.cmd}
- If (Test-Path $WORKINGDIR\remote.cmd){Remove-Item $WORKINGDIR\remote.cmd}
Add Comment
Please, Sign In to add comment