diff --git a/xbmc/platform/darwin/tvos/PreflightHandler.mm b/xbmc/platform/darwin/tvos/PreflightHandler.mm new file mode 100644 index 0000000000000..7aa067bc37f6b --- /dev/null +++ b/xbmc/platform/darwin/tvos/PreflightHandler.mm @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2016 Team MrMC + * https://github.com/MrMC + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MrMC; see the file COPYING. If not, see + * . + * + */ + +#import "PreflightHandler.h" + +#include "URL.h" +#include "filesystem/File.h" +#include "utils/log.h" + +#import "platform/darwin/NSLogDebugHelpers.h" +#import "platform/darwin/ios-common/DarwinNSUserDefaults.h" +#import "platform/darwin/tvos/filesystem/TVOSFile.h" +#import "platform/darwin/tvos/filesystem/TVOSFileUtils.h" +#include "platform/posix/filesystem/PosixFile.h" + +#import + +uint64_t CPreflightHandler::NSUserDefaultsSize() +{ + auto libraryDir = + NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0]; + auto filepath = [libraryDir + stringByAppendingPathComponent:[NSString + stringWithFormat:@"/Preferences/%@.plist", + [NSBundle mainBundle].bundleIdentifier]]; + auto fileSize = + [[[NSFileManager defaultManager] attributesOfItemAtPath:filepath error:nil][NSFileSize] + unsignedLongLongValue]; + + return fileSize; +} + +void CPreflightHandler::NSUserDefaultsPurge(const char* prefix) +{ + auto defaults = [NSUserDefaults standardUserDefaults]; + NSDictionary* dict = [defaults dictionaryRepresentation]; + NSString* aKey; + for (aKey in dict.allKeys) + { + if ([aKey hasPrefix:@(prefix)]) + { + [defaults removeObjectForKey:aKey]; + LOG(@"nsuserdefaults: removing %@", aKey); + } + } +} + +void CPreflightHandler::CheckForRemovedCacheFolder() +{ + // if already migrated, "UserdataMigrated" key will be in UserDefaults + // if user home directory does not exist, Apple deleted cache from under us. + // we will mark it for possible restore if the backup exist + auto defaults = [NSUserDefaults standardUserDefaults]; + auto migration_key = @"UserdataMigrated"; + if (![defaults objectForKey:migration_key]) + { + // no existing data, no need to check any further. + return; + } + + //!@todo: implement some sort of backup/restore if folder no longer exists? +} + +void CPreflightHandler::MigrateUserdataXMLToNSUserDefaults() +{ + LOG(@"MigrateUserdataXMLToNSUserDefaults: NSUserDefaultsSize(%llu)", NSUserDefaultsSize()); + + auto defaults = [NSUserDefaults standardUserDefaults]; + + // if already migrated, we are done. + auto migration_key = @"UserdataMigrated"; + if ([defaults objectForKey:migration_key]) + { + // If we require forced update of data from cache check for migration_key value + return; + } + + LOG(@"MigrateUserdataXMLToNSUserDefaults: migration starting"); + + NSUserDefaultsPurge("/userdata"); + // now search for all xxx.xml files in the + // user home directory and copy them into NSUserDefaults + auto userHome = CTVOSFileUtils::GetUserHomeDirectory(); + auto nsPath = @(userHome); + + NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:nsPath]; + NSString* file; + while (file = [enumerator nextObject]) + { + auto fullPath = [nsPath stringByAppendingPathComponent:file]; + + // skip the Thumbnails directory, there are no xml files present + // and it can be very large. + if ([fullPath containsString:@"Thumbnails"]) + continue; + + // check if it's a directory + BOOL isDirectory = NO; + [[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:&isDirectory]; + if (isDirectory) + continue; + // check if the file extension is 'xml' + if ([file.pathExtension isEqualToString:@"xml"]) + { + // log what we are doing + LOG(@"MigrateUserdataXMLToNSUserDefaults: Found -> %@", fullPath); + + // we cannot use a Cfile for src, it will get mapped into a CTVOSFile + const CURL srcUrl(fullPath.UTF8String); + XFILE::CPosixFile srcfile; + if (!srcfile.Open(srcUrl)) + { + LOG(@"MigrateUserdataXMLToNSUserDefaults: Failed opening file %s", srcUrl.Get().c_str()); + continue; + } + + const CURL dtsUrl(fullPath.UTF8String); + XFILE::CTVOSFile dstfile; + if (dstfile.OpenForWrite(dtsUrl, true)) + { + auto iBufferSize = 128 * 1024; + XFILE::auto_buffer buffer(iBufferSize); + + while (true) + { + // read data + auto iread = srcfile.Read(buffer.get(), iBufferSize); + if (iread == 0) + break; + else if (iread < 0) + { + LOG(@"MigrateUserdataXMLToNSUserDefaults: Failed read from file %s", + srcUrl.Get().c_str()); + break; + } + + // write data and make sure we write it all + auto iwrite = 0; + while (iwrite < iread) + { + auto iwrite2 = dstfile.Write(buffer.get() + iwrite, iread - iwrite); + if (iwrite2 <= 0) + break; + iwrite += iwrite2; + } + + if (iwrite != iread) + { + LOG(@"MigrateUserdataXMLToNSUserDefaults: Failed write to file %s", + dtsUrl.Get().c_str()); + break; + } + } + dstfile.Close(); + srcfile.Close(); + srcfile.Delete(srcUrl); + } + } + } + + // set the migration_key to a known value and write it. + [defaults setObject:@"1" forKey:migration_key]; + [defaults synchronize]; + + LOG(@"MigrateUserdataXMLToNSUserDefaults: migration finished"); + LOG(@"MigrateUserdataXMLToNSUserDefaults: NSUserDefaultsSize(%llu)", NSUserDefaultsSize()); +}