├── Runtime ├── DataTransform.meta ├── Extensions.cs.meta ├── FileStorage.cs.meta ├── Serialization.meta ├── FileStorageAsync.cs.meta ├── FileStorageSync.cs.meta ├── IDataTransform.cs.meta ├── IFileProvider.cs.meta ├── IFileStorage.cs.meta ├── Compression │ ├── Compress.cs.meta │ ├── GZipDataTransform.cs.meta │ ├── DeflateDataTransform.cs.meta │ ├── GZipDataTransform.cs │ ├── DeflateDataTransform.cs │ └── Compress.cs ├── IPlayerPrefsStorage.cs.meta ├── PlayerPrefsStorage.cs.meta ├── ISerializationProvider.cs.meta ├── PlayerPrefsStorageAsync.cs.meta ├── PlayerPrefsStorageSync.cs.meta ├── Encryption │ ├── IEncryptionSettings.cs.meta │ ├── AesEncryptionDataTransform.cs.meta │ ├── IEncryptionSettings.cs │ ├── AesEncryption.cs.meta │ ├── AesEncryptionDataTransform.cs │ └── AesEncryption.cs ├── Serialization │ ├── JsonSerialize.cs.meta │ ├── DataTransformSerializationProvider.cs.meta │ ├── DataTransformSerializationProviderAsync.cs.meta │ ├── DataTransformSerializationProviderSync.cs.meta │ ├── UnityJsonSerializationProvider.cs.meta │ ├── NewtonsoftJsonSerializationProvider.cs.meta │ ├── JsonSerialize.cs │ ├── DataTransformSerializationProviderSync.cs │ ├── UnityJsonSerializationProvider.cs │ ├── NewtonsoftJsonSerializationProvider.cs │ ├── DataTransformSerializationProviderAsync.cs │ └── DataTransformSerializationProvider.cs ├── DataTransform │ ├── CombinedDataTransform.cs.meta │ ├── CombinedDataTransformAsync.cs.meta │ ├── CombinedDataTransformSync.cs.meta │ ├── CombinedDataTransformSync.cs │ ├── CombinedDataTransformAsync.cs │ └── CombinedDataTransform.cs ├── Compression.meta ├── Encryption.meta ├── Dre0Dru.LocalStorage.asmdef.meta ├── FileProvider.cs.meta ├── IDataTransform.cs ├── ISerializationProvider.cs ├── IFileStorage.cs ├── Dre0Dru.LocalStorage.asmdef ├── IFileProvider.cs ├── IPlayerPrefsStorage.cs ├── FileStorageSync.cs ├── FileStorageAsync.cs ├── FileStorage.cs ├── PlayerPrefsStorageSync.cs ├── Extensions.cs ├── PlayerPrefsStorageAsync.cs ├── PlayerPrefsStorage.cs └── FileProvider.cs ├── Tests ├── Runtime │ ├── PlayerPrefsStorageTests.cs.meta │ ├── Dre0Dru.LocalStorage.Tests.asmdef.meta │ ├── Setup.cs.meta │ ├── Constants.cs.meta │ ├── Extensions.cs.meta │ ├── FileProviderTests.cs.meta │ ├── FileStorageTests.cs.meta │ ├── GZipDataTransformTests.cs.meta │ ├── CombinedDataTransformTests.cs.meta │ ├── DeflateDataTransformTests.cs.meta │ ├── AesEncryptionDataTransformTests.cs.meta │ ├── UnityJsonSerializationProviderTests.cs.meta │ ├── DataTransformSerializationProviderTests.cs.meta │ ├── Dre0Dru.LocalStorage.Tests.asmdef │ ├── Setup.cs │ ├── Extensions.cs │ ├── GZipDataTransformTests.cs │ ├── DeflateDataTransformTests.cs │ ├── CombinedDataTransformTests.cs │ ├── AesEncryptionDataTransformTests.cs │ ├── FileProviderTests.cs │ ├── DataTransformSerializationProviderTests.cs │ ├── UnityJsonSerializationProviderTests.cs │ ├── FileStorageTests.cs │ ├── PlayerPrefsStorageTests.cs │ └── Constants.cs └── Runtime.meta ├── CHANGELOG.md.meta ├── LICENSE.md.meta ├── README.md.meta ├── package.json.meta ├── Runtime.meta ├── Tests.meta ├── package.json ├── .github └── workflows │ ├── publish_upm_release.yml │ └── semantic_release_dry_run.yml ├── .releaserc.json ├── LICENSE.md ├── CHANGELOG.md └── README.md /Runtime/DataTransform.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e09c4ce6ac444730852a14a791f43e0f 3 | timeCreated: 1624987855 -------------------------------------------------------------------------------- /Runtime/Extensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: be7cecbccf2b4d05ae20a4a2e8ac5009 3 | timeCreated: 1624983896 -------------------------------------------------------------------------------- /Runtime/FileStorage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4208b062776b4ed78fbc0f70eb637fb1 3 | timeCreated: 1616095485 -------------------------------------------------------------------------------- /Runtime/Serialization.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 47e93e5751074debb3e5893a545baa37 3 | timeCreated: 1624986752 -------------------------------------------------------------------------------- /Runtime/FileStorageAsync.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 86a5f15aab064eeea9b2647e0b46a96a 3 | timeCreated: 1624988231 -------------------------------------------------------------------------------- /Runtime/FileStorageSync.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 445d54397ede44449ce9d68afbf0f81b 3 | timeCreated: 1624988163 -------------------------------------------------------------------------------- /Runtime/IDataTransform.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 48279c24d9bc43f1a3de0a139f103df6 3 | timeCreated: 1621097640 -------------------------------------------------------------------------------- /Runtime/IFileProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 26fea329792f43d38f9ec3617206a73c 3 | timeCreated: 1616097749 -------------------------------------------------------------------------------- /Runtime/IFileStorage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: beedce3361354021a81961cfbf5e3a93 3 | timeCreated: 1620475690 -------------------------------------------------------------------------------- /Runtime/Compression/Compress.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc61be6c3d5a4c88a18fa0973c788a36 3 | timeCreated: 1620327799 -------------------------------------------------------------------------------- /Runtime/IPlayerPrefsStorage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0c773217d7c442f0bbe091d60741efcb 3 | timeCreated: 1621337931 -------------------------------------------------------------------------------- /Runtime/PlayerPrefsStorage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7a1e37bf359c4dff80ae77cfbb8c9adc 3 | timeCreated: 1621338502 -------------------------------------------------------------------------------- /Runtime/ISerializationProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e9b4a687473e4bcaba9bf7c83f8749cb 3 | timeCreated: 1616096814 -------------------------------------------------------------------------------- /Runtime/PlayerPrefsStorageAsync.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 449f6d83b7074156aa43f4ee468b5efa 3 | timeCreated: 1624988570 -------------------------------------------------------------------------------- /Runtime/PlayerPrefsStorageSync.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c139b9c99f5441594c3cdc35d3b9031 3 | timeCreated: 1624988475 -------------------------------------------------------------------------------- /Runtime/Compression/GZipDataTransform.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ab6cd5a5554b4a5ba29339928f82dd40 3 | timeCreated: 1621174412 -------------------------------------------------------------------------------- /Runtime/Encryption/IEncryptionSettings.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cd28ba529bf740dc97e00b736f791d3f 3 | timeCreated: 1616159884 -------------------------------------------------------------------------------- /Runtime/Serialization/JsonSerialize.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 22ee8c0c2f6b472cab3867123b0aa192 3 | timeCreated: 1624986753 -------------------------------------------------------------------------------- /Tests/Runtime/PlayerPrefsStorageTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 823b89c6aefe4a7bba1e99f71393c722 3 | timeCreated: 1621342655 -------------------------------------------------------------------------------- /Runtime/Compression/DeflateDataTransform.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de82c8d02bfc4025b329d971688d6547 3 | timeCreated: 1621174554 -------------------------------------------------------------------------------- /Runtime/DataTransform/CombinedDataTransform.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2c2ea5501d554092826ff3bc3cf0d7f0 3 | timeCreated: 1621176331 -------------------------------------------------------------------------------- /Runtime/DataTransform/CombinedDataTransformAsync.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 490e9089b6424f019e6d6aec7a0fdf9c 3 | timeCreated: 1624988021 -------------------------------------------------------------------------------- /Runtime/DataTransform/CombinedDataTransformSync.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b61fc837dc694ffabf6b193da260280b 3 | timeCreated: 1624987953 -------------------------------------------------------------------------------- /Runtime/Encryption/AesEncryptionDataTransform.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: efaabd887cfd496491d611bc376e299b 3 | timeCreated: 1621103066 -------------------------------------------------------------------------------- /Runtime/Serialization/DataTransformSerializationProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5f7161d4d7b64b239f125f3b06652630 3 | timeCreated: 1621097812 -------------------------------------------------------------------------------- /Runtime/Serialization/DataTransformSerializationProviderAsync.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fd27b209595b4967b65748dc39297a71 3 | timeCreated: 1624987682 -------------------------------------------------------------------------------- /Runtime/Serialization/DataTransformSerializationProviderSync.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4dcdb5f1d0204761963d4dc73489a61c 3 | timeCreated: 1624987477 -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 660b3e6c9afbd774f9c16cf7f5c92601 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7fb4c452372be3048bcc73e503f6b6ae 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d405f4fc7444d7d478497a8e2e4222b8 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b7a0818a007bb5446964427bf25285d4 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f83f902e325345f46b7e1b0bdfde5a53 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Tests.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a91d5550073369a48b9b1ea30112a236 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Tests/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ce4e623aa697b6045b0bc779f3e881e6 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Compression.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3a0e8a8e8e91d7940a639387fe5cc448 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Encryption.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b1dcd2c69b422a447922f75ec3688af1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Dre0Dru.LocalStorage.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9549a63ebfcdeef4aa33998d26c2b07c 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Encryption/IEncryptionSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Dre0Dru.LocalStorage.Encryption 2 | { 3 | public interface IEncryptionSettings 4 | { 5 | byte[] Key { get; } 6 | 7 | byte[] InitializationVector { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /Tests/Runtime/Dre0Dru.LocalStorage.Tests.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 70f2df3a2fa922b4f8ac0c45a7ee84f3 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/FileProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a8ec479cb4884223be0358d0d34a3afe 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/Setup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc1ee8819c10db7499fe098bc8b36613 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/Constants.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0b281029161b73446ad8d1e8b647e96a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/Extensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c47c8dd5d9ea3564b96e9c89f479946d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/FileProviderTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 598500b7efcff8846bec855939b65f8f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/FileStorageTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 20524cbb0cb9da84eb58d6f4b9cbf1d4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Encryption/AesEncryption.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1be31a7d56127b14a87a06878e4c867b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/GZipDataTransformTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d0d7a361e3997f2439b1456c7cb352e0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/CombinedDataTransformTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 138db42136e52b848a6b2731b29964aa 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/DeflateDataTransformTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 62b48d328a8a4c743b49f092897bc9c9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/AesEncryptionDataTransformTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 816dcbecdfedf1a438b2a282726ca464 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/UnityJsonSerializationProviderTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 93c322a36902ec34595a2e53a08fc958 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Serialization/UnityJsonSerializationProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 51c49afba89946c195d35d00876bb714 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Runtime/DataTransformSerializationProviderTests.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c09c26d95bcd80149b94b76e19018ac3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Serialization/NewtonsoftJsonSerializationProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 88b76feb1174bf245b5bf034cf775fa5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.dre0dru.localstorage", 3 | "version": "5.0.0", 4 | "displayName": "Local Storage", 5 | "description": "Utility classes for managing local data saved on device", 6 | "unity": "2020.1", 7 | "unityRelease": "0f1", 8 | "keywords": [ 9 | "Storage" 10 | ], 11 | "author": { 12 | "name": "Dre0Dru", 13 | "url": "https://github.com/dre0dru" 14 | }, 15 | "type": "library" 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/publish_upm_release.yml: -------------------------------------------------------------------------------- 1 | name: Publish UPM release 2 | on: [workflow_dispatch] 3 | jobs: 4 | release: 5 | name: release 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | with: 10 | fetch-depth: 0 11 | - name: Semantic release 12 | id: semantic 13 | uses: cycjimmy/semantic-release-action@v2 14 | with: 15 | extra_plugins: | 16 | @semantic-release/changelog 17 | @semantic-release/git 18 | branch: master 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tagFormat": "v${version}", 3 | "plugins": [ 4 | ["@semantic-release/commit-analyzer", { "preset": "angular" }], 5 | "@semantic-release/release-notes-generator", 6 | ["@semantic-release/changelog", { "preset": "angular", "changelogFile": "CHANGELOG.md" }], 7 | ["@semantic-release/npm", { "npmPublish": false, "pkgRoot": "./" }], 8 | ["@semantic-release/git", { 9 | "assets": ["package.json", "CHANGELOG.md"], 10 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 11 | }], 12 | "@semantic-release/github" 13 | ] 14 | } -------------------------------------------------------------------------------- /Tests/Runtime/Dre0Dru.LocalStorage.Tests.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dre0Dru.LocalStorage.Tests", 3 | "rootNamespace": "Dre0Dru.LocalStorage.Tests", 4 | "references": [ 5 | "UnityEngine.TestRunner", 6 | "UnityEditor.TestRunner", 7 | "UniTask", 8 | "Dre0Dru.LocalStorage" 9 | ], 10 | "includePlatforms": [], 11 | "excludePlatforms": [], 12 | "allowUnsafeCode": false, 13 | "overrideReferences": true, 14 | "precompiledReferences": [ 15 | "nunit.framework.dll" 16 | ], 17 | "autoReferenced": false, 18 | "defineConstraints": [ 19 | "UNITY_INCLUDE_TESTS" 20 | ], 21 | "versionDefines": [], 22 | "noEngineReferences": false 23 | } -------------------------------------------------------------------------------- /Runtime/Serialization/JsonSerialize.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using UnityEngine; 3 | 4 | namespace Dre0Dru.LocalStorage.Serialization 5 | { 6 | public static class JsonSerialize 7 | { 8 | public static byte[] ToUnityJsonBytes(T data, bool prettyPrint = false) => 9 | JsonUtility.ToJson(data, prettyPrint).StringToBytes(); 10 | 11 | public static T FromUnityJsonBytes(byte[] data) => 12 | JsonUtility.FromJson(data.BytesToString()); 13 | 14 | private static byte[] StringToBytes(this string str) => 15 | Encoding.UTF8.GetBytes(str); 16 | 17 | private static string BytesToString(this byte[] bytes) => 18 | Encoding.UTF8.GetString(bytes); 19 | } 20 | } -------------------------------------------------------------------------------- /.github/workflows/semantic_release_dry_run.yml: -------------------------------------------------------------------------------- 1 | name: Semantic Release Dry Run 2 | on: [workflow_dispatch] 3 | jobs: 4 | release: 5 | name: semantic_release_dry_run 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | with: 10 | fetch-depth: 0 11 | - name: Semantic release 12 | id: semantic 13 | uses: cycjimmy/semantic-release-action@v2 14 | with: 15 | dry_run: true 16 | extra_plugins: | 17 | @semantic-release/changelog 18 | @semantic-release/git 19 | branch: master 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | - name: Output info 23 | run: | 24 | echo ${{ steps.semantic.outputs.new_release_published }} 25 | echo ${{ steps.semantic.outputs.new_release_version }} 26 | -------------------------------------------------------------------------------- /Runtime/IDataTransform.cs: -------------------------------------------------------------------------------- 1 | namespace Dre0Dru.LocalStorage 2 | { 3 | public interface IDataTransform : IDataTransformSync, IDataTransformAsync 4 | { 5 | } 6 | 7 | public interface IDataTransformSync 8 | { 9 | byte[] Apply(byte[] data); 10 | 11 | byte[] Reverse(byte[] data); 12 | } 13 | 14 | public interface IDataTransformAsync 15 | { 16 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 17 | Cysharp.Threading.Tasks.UniTask ApplyAsync(byte[] data); 18 | #else 19 | System.Threading.Tasks.Task ApplyAsync(byte[] data); 20 | #endif 21 | 22 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 23 | Cysharp.Threading.Tasks.UniTask ReverseAsync(byte[] data); 24 | #else 25 | System.Threading.Tasks.Task ReverseAsync(byte[] data); 26 | #endif 27 | } 28 | } -------------------------------------------------------------------------------- /Runtime/ISerializationProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Dre0Dru.LocalStorage 2 | { 3 | public interface ISerializationProvider : ISerializationProviderSync, ISerializationProviderAsync 4 | { 5 | } 6 | 7 | public interface ISerializationProviderSync 8 | { 9 | byte[] Serialize(T data); 10 | 11 | T Deserialize(byte[] data); 12 | } 13 | 14 | public interface ISerializationProviderAsync 15 | { 16 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 17 | Cysharp.Threading.Tasks.UniTask SerializeAsync(T data); 18 | #else 19 | System.Threading.Tasks.Task SerializeAsync(T data); 20 | #endif 21 | 22 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 23 | Cysharp.Threading.Tasks.UniTask DeserializeAsync(byte[] data); 24 | #else 25 | System.Threading.Tasks.Task DeserializeAsync(byte[] data); 26 | #endif 27 | } 28 | } -------------------------------------------------------------------------------- /Runtime/IFileStorage.cs: -------------------------------------------------------------------------------- 1 | namespace Dre0Dru.LocalStorage 2 | { 3 | public interface IFileStorage : IFileStorageSync, IFileStorageAsync 4 | { 5 | } 6 | 7 | public interface IFileStorageSync : IFileProviderCommon 8 | { 9 | void Save(TData data, string fileName); 10 | 11 | TData Load(string fileName); 12 | } 13 | 14 | public interface IFileStorageAsync : IFileProviderCommon 15 | { 16 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 17 | Cysharp.Threading.Tasks.UniTask SaveAsync(TData data, string fileName); 18 | #else 19 | System.Threading.Tasks.Task SaveAsync(TData data, string fileName); 20 | #endif 21 | 22 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 23 | Cysharp.Threading.Tasks.UniTask LoadAsync(string fileName); 24 | #else 25 | System.Threading.Tasks.Task LoadAsync(string fileName); 26 | #endif 27 | } 28 | } -------------------------------------------------------------------------------- /Runtime/Dre0Dru.LocalStorage.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dre0Dru.LocalStorage", 3 | "rootNamespace": "Dre0Dru.LocalStorage", 4 | "references": [ 5 | "GUID:f51ebe6a0ceec4240a699833d6309b23" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [ 15 | { 16 | "name": "com.cysharp.unitask", 17 | "expression": "", 18 | "define": "UNITASK_SUPPORT" 19 | }, 20 | { 21 | "name": "jillejr.newtonsoft.json-for-unity", 22 | "expression": "", 23 | "define": "NEWTONSOFT_JSON_SUPPORT" 24 | }, 25 | { 26 | "name": "com.unity.nuget.newtonsoft-json", 27 | "expression": "", 28 | "define": "NEWTONSOFT_JSON_SUPPORT" 29 | } 30 | ], 31 | "noEngineReferences": false 32 | } -------------------------------------------------------------------------------- /Runtime/DataTransform/CombinedDataTransformSync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dre0Dru.LocalStorage.DataTransform 4 | { 5 | public class CombinedDataTransformSync : IDataTransformSync 6 | { 7 | private readonly IDataTransformSync _firstTransform; 8 | private readonly IDataTransformSync _secondTransform; 9 | 10 | #if UNITY_2020_3_OR_NEWER 11 | [UnityEngine.Scripting.RequiredMember] 12 | #endif 13 | public CombinedDataTransformSync(IDataTransformSync firstTransform, IDataTransformSync secondTransform) 14 | { 15 | _firstTransform = firstTransform ?? 16 | throw new ArgumentNullException(nameof(firstTransform)); 17 | _secondTransform = secondTransform ?? 18 | throw new ArgumentNullException(nameof(secondTransform)); 19 | } 20 | 21 | public byte[] Apply(byte[] data) => 22 | _secondTransform.Apply(_firstTransform.Apply(data)); 23 | 24 | public byte[] Reverse(byte[] data) => 25 | _firstTransform.Reverse(_secondTransform.Reverse(data)); 26 | } 27 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-2022 Dre0Dru. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Runtime/IFileProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Dre0Dru.LocalStorage 2 | { 3 | public interface IFileProvider : IFileProviderSync, IFileProviderAsync 4 | { 5 | } 6 | 7 | public interface IFileProviderSync : IFileProviderCommon 8 | { 9 | void Write(byte[] output, string fileName); 10 | 11 | byte[] Read(string fileName); 12 | } 13 | 14 | public interface IFileProviderAsync : IFileProviderCommon 15 | { 16 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 17 | Cysharp.Threading.Tasks.UniTask WriteAsync(byte[] output, string fileName); 18 | #else 19 | System.Threading.Tasks.Task WriteAsync(byte[] output, string fileName); 20 | #endif 21 | 22 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 23 | Cysharp.Threading.Tasks.UniTask ReadAsync(string fileName); 24 | #else 25 | System.Threading.Tasks.Task ReadAsync(string fileName); 26 | #endif 27 | } 28 | 29 | public interface IFileProviderCommon 30 | { 31 | bool Delete(string fileName); 32 | 33 | string GetFilePath(string fileName); 34 | 35 | bool FileExists(string fileName); 36 | } 37 | } -------------------------------------------------------------------------------- /Runtime/Serialization/DataTransformSerializationProviderSync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dre0Dru.LocalStorage.Serialization 4 | { 5 | public class DataTransformSerializationProviderSync : ISerializationProviderSync 6 | { 7 | private readonly ISerializationProviderSync _baseProvider; 8 | 9 | private readonly IDataTransformSync _dataTransform; 10 | 11 | #if UNITY_2020_3_OR_NEWER 12 | [UnityEngine.Scripting.RequiredMember] 13 | #endif 14 | public DataTransformSerializationProviderSync(ISerializationProviderSync baseProvider, 15 | IDataTransformSync dataTransform) 16 | { 17 | _baseProvider = baseProvider ?? 18 | throw new ArgumentNullException(nameof(baseProvider)); 19 | _dataTransform = dataTransform ?? 20 | throw new ArgumentNullException(nameof(dataTransform)); 21 | } 22 | 23 | public byte[] Serialize(T data) => 24 | _dataTransform.Apply(_baseProvider.Serialize(data)); 25 | 26 | public T Deserialize(byte[] data) => 27 | _baseProvider.Deserialize(_dataTransform.Reverse(data)); 28 | } 29 | } -------------------------------------------------------------------------------- /Tests/Runtime/Setup.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using UnityEngine; 3 | 4 | namespace LocalStorage.PlayModeTests 5 | { 6 | public static class Setup 7 | { 8 | public static void CreateEmptyFile(string filePath) 9 | { 10 | File.Create(filePath).Dispose(); 11 | } 12 | 13 | public static void DeleteFile(string filePath) 14 | { 15 | File.Delete(filePath); 16 | } 17 | 18 | public static bool FileExists(string filePath) 19 | { 20 | return File.Exists(filePath); 21 | } 22 | 23 | public static byte[] ReadFromFile(string filePath) 24 | { 25 | return File.ReadAllBytes(filePath); 26 | } 27 | 28 | public static void WriteToFile(string filePath, byte[] data) 29 | { 30 | File.WriteAllBytes(filePath, data); 31 | } 32 | 33 | public static void ClearPlayerPrefs() 34 | { 35 | PlayerPrefs.DeleteAll(); 36 | PlayerPrefs.Save(); 37 | } 38 | 39 | public static void PlayerPrefsCreateKey(string key) 40 | { 41 | PlayerPrefs.SetString(key, "string"); 42 | PlayerPrefs.Save(); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Runtime/Compression/GZipDataTransform.cs: -------------------------------------------------------------------------------- 1 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 2 | using Cysharp.Threading.Tasks; 3 | #else 4 | using System.Threading.Tasks; 5 | #endif 6 | 7 | namespace Dre0Dru.LocalStorage.Compression 8 | { 9 | public class GZipDataTransform : IDataTransform 10 | { 11 | public byte[] Apply(byte[] data) 12 | { 13 | return Compress.WriteGZip(data); 14 | } 15 | 16 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 17 | public UniTask ApplyAsync(byte[] data) 18 | { 19 | return Compress.WriteGZipAsync(data).AsUniTask(); 20 | } 21 | #else 22 | public Task ApplyAsync(byte[] data) 23 | { 24 | return Compress.WriteGZipAsync(data); 25 | } 26 | #endif 27 | 28 | public byte[] Reverse(byte[] data) 29 | { 30 | return Compress.ReadGZip(data); 31 | } 32 | 33 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 34 | public UniTask ReverseAsync(byte[] data) 35 | { 36 | return Compress.ReadGZipAsync(data).AsUniTask(); 37 | } 38 | #else 39 | public Task ReverseAsync(byte[] data) 40 | { 41 | return Compress.ReadGZipAsync(data); 42 | } 43 | #endif 44 | } 45 | } -------------------------------------------------------------------------------- /Runtime/Compression/DeflateDataTransform.cs: -------------------------------------------------------------------------------- 1 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 2 | using Cysharp.Threading.Tasks; 3 | #else 4 | using System.Threading.Tasks; 5 | #endif 6 | 7 | namespace Dre0Dru.LocalStorage.Compression 8 | { 9 | public class DeflateDataTransform : IDataTransform 10 | { 11 | public byte[] Apply(byte[] data) 12 | { 13 | return Compress.WriteDeflate(data); 14 | } 15 | 16 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 17 | public UniTask ApplyAsync(byte[] data) 18 | { 19 | return Compress.WriteDeflateAsync(data).AsUniTask(); 20 | } 21 | #else 22 | public Task ApplyAsync(byte[] data) 23 | { 24 | return Compress.WriteDeflateAsync(data); 25 | } 26 | #endif 27 | 28 | public byte[] Reverse(byte[] data) 29 | { 30 | return Compress.ReadDeflate(data); 31 | } 32 | 33 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 34 | public UniTask ReverseAsync(byte[] data) 35 | { 36 | return Compress.ReadDeflateAsync(data).AsUniTask(); 37 | } 38 | #else 39 | public Task ReverseAsync(byte[] data) 40 | { 41 | return Compress.ReadDeflateAsync(data); 42 | } 43 | #endif 44 | } 45 | } -------------------------------------------------------------------------------- /Runtime/IPlayerPrefsStorage.cs: -------------------------------------------------------------------------------- 1 | namespace Dre0Dru.LocalStorage 2 | { 3 | public interface IPlayerPrefsStorage : IPlayerPrefsStorageSync, IPlayerPrefsStorageAsync 4 | { 5 | } 6 | 7 | public interface IPlayerPrefsStorageSync : IPlayerPrefsStorageCommon 8 | { 9 | void SetData(string key, T data); 10 | 11 | T GetData(string key); 12 | } 13 | 14 | public interface IPlayerPrefsStorageAsync : IPlayerPrefsStorageCommon 15 | { 16 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 17 | Cysharp.Threading.Tasks.UniTask SetDataAsync(string key, T data); 18 | #else 19 | System.Threading.Tasks.Task SetDataAsync(string key, T data); 20 | #endif 21 | 22 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 23 | Cysharp.Threading.Tasks.UniTask GetDataAsync(string key); 24 | #else 25 | System.Threading.Tasks.Task GetDataAsync(string key); 26 | #endif 27 | } 28 | 29 | public interface IPlayerPrefsStorageCommon 30 | { 31 | void SetFloat(string key, float value); 32 | 33 | float GetFloat(string key); 34 | 35 | void SetInt(string key, int value); 36 | 37 | int GetInt(string key); 38 | 39 | void SetString(string key, string value); 40 | 41 | string GetString(string key); 42 | 43 | bool HasKey(string key); 44 | 45 | void Save(); 46 | 47 | void DeleteKey(string key); 48 | 49 | void DeleteAll(); 50 | } 51 | } -------------------------------------------------------------------------------- /Runtime/FileStorageSync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dre0Dru.LocalStorage 4 | { 5 | public class FileStorageSync : IFileStorageSync 6 | { 7 | private readonly ISerializationProviderSync _serializationProvider; 8 | private readonly IFileProviderSync _fileProvider; 9 | 10 | #if UNITY_2020_3_OR_NEWER 11 | [UnityEngine.Scripting.RequiredMember] 12 | #endif 13 | public FileStorageSync(ISerializationProviderSync serializationProvider, 14 | IFileProviderSync fileProvider) 15 | { 16 | _serializationProvider = serializationProvider ?? 17 | throw new ArgumentNullException(nameof(serializationProvider)); 18 | _fileProvider = fileProvider ?? 19 | throw new ArgumentNullException(nameof(fileProvider)); 20 | } 21 | 22 | public void Save(TData data, string fileName) => 23 | _fileProvider.Write(_serializationProvider.Serialize(data), 24 | GetFilePath(fileName)); 25 | 26 | public TData Load(string fileName) => 27 | _serializationProvider 28 | .Deserialize(_fileProvider.Read(GetFilePath(fileName))); 29 | 30 | public bool Delete(string fileName) => 31 | _fileProvider.Delete(fileName); 32 | 33 | public string GetFilePath(string fileName) => 34 | _fileProvider.GetFilePath(fileName); 35 | 36 | public bool FileExists(string fileName) => 37 | _fileProvider.FileExists(fileName); 38 | } 39 | } -------------------------------------------------------------------------------- /Runtime/Serialization/UnityJsonSerializationProvider.cs: -------------------------------------------------------------------------------- 1 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 2 | using Cysharp.Threading.Tasks; 3 | #else 4 | using System.Threading.Tasks; 5 | #endif 6 | 7 | namespace Dre0Dru.LocalStorage.Serialization 8 | { 9 | public class UnityJsonSerializationProvider : ISerializationProvider 10 | { 11 | private readonly bool _prettyPrint; 12 | 13 | #if UNITY_2020_3_OR_NEWER 14 | [UnityEngine.Scripting.RequiredMember] 15 | #endif 16 | public UnityJsonSerializationProvider() : this(false) 17 | { 18 | } 19 | 20 | public UnityJsonSerializationProvider(bool prettyPrint) 21 | { 22 | _prettyPrint = prettyPrint; 23 | } 24 | 25 | public byte[] Serialize(T data) => 26 | JsonSerialize.ToUnityJsonBytes(data, _prettyPrint); 27 | 28 | public T Deserialize(byte[] data) => 29 | JsonSerialize.FromUnityJsonBytes(data); 30 | 31 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 32 | public UniTask SerializeAsync(T data) => 33 | this.SerializeFakeAsync(data); 34 | #else 35 | public Task SerializeAsync(T data) => 36 | this.SerializeFakeAsync(data); 37 | #endif 38 | 39 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 40 | public UniTask DeserializeAsync(byte[] data) => 41 | this.DeserializeFakeAsync(data); 42 | #else 43 | public Task DeserializeAsync(byte[] data) => 44 | this.DeserializeFakeAsync(data); 45 | #endif 46 | } 47 | } -------------------------------------------------------------------------------- /Runtime/Serialization/NewtonsoftJsonSerializationProvider.cs: -------------------------------------------------------------------------------- 1 | #if NEWTONSOFT_JSON_SUPPORT 2 | 3 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 4 | using Cysharp.Threading.Tasks; 5 | #else 6 | using System.Threading.Tasks; 7 | #endif 8 | using System.Text; 9 | using Newtonsoft.Json; 10 | 11 | namespace Dre0Dru.LocalStorage.Serialization 12 | { 13 | public class NewtonsoftJsonSerializationProvider : ISerializationProvider 14 | { 15 | private readonly JsonSerializerSettings _serializerSettings; 16 | 17 | public NewtonsoftJsonSerializationProvider(JsonSerializerSettings serializerSettings) 18 | { 19 | _serializerSettings = serializerSettings; 20 | } 21 | 22 | public byte[] Serialize(T data) => 23 | Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data, _serializerSettings)); 24 | 25 | public T Deserialize(byte[] data) => 26 | JsonConvert.DeserializeObject(Encoding.UTF8.GetString(data)); 27 | 28 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 29 | public UniTask SerializeAsync(T data) => 30 | this.SerializeFakeAsync(data); 31 | #else 32 | public Task SerializeAsync(T data) => 33 | this.SerializeFakeAsync(data); 34 | #endif 35 | 36 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 37 | public UniTask DeserializeAsync(byte[] data) => 38 | this.DeserializeFakeAsync(data); 39 | #else 40 | public Task DeserializeAsync(byte[] data) => 41 | this.DeserializeFakeAsync(data); 42 | #endif 43 | } 44 | } 45 | #endif 46 | -------------------------------------------------------------------------------- /Tests/Runtime/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using LocalStorage.Compression; 3 | using LocalStorage.Encryption; 4 | using UnityEngine; 5 | 6 | namespace LocalStorage.PlayModeTests 7 | { 8 | public static class Extensions 9 | { 10 | public static string FromBytes(this byte[] bytes) 11 | { 12 | return Encoding.UTF8.GetString(bytes); 13 | } 14 | 15 | public static byte[] ToBytes(this string str) 16 | { 17 | return Encoding.UTF8.GetBytes(str); 18 | } 19 | 20 | public static string ToJson(this T obj) 21 | { 22 | return JsonUtility.ToJson(obj); 23 | } 24 | 25 | public static byte[] Encrypt(this byte[] bytes) 26 | { 27 | return AesEncryption.Encrypt(bytes, Constants.Instances.Es.Key, Constants.Instances.Es.InitializationVector); 28 | } 29 | 30 | public static byte[] Decrypt(this byte[] bytes) 31 | { 32 | return AesEncryption.Decrypt(bytes, Constants.Instances.Es.Key, Constants.Instances.Es.InitializationVector); 33 | } 34 | 35 | public static byte[] WriteGZip(this byte[] bytes) 36 | { 37 | return Compress.WriteGZip(bytes); 38 | } 39 | 40 | public static byte[] ReadGzip(this byte[] bytes) 41 | { 42 | return Compress.ReadGZip(bytes); 43 | } 44 | 45 | public static byte[] WriteDeflate(this byte[] bytes) 46 | { 47 | return Compress.WriteDeflate(bytes); 48 | } 49 | 50 | public static byte[] ReadDeflate(this byte[] bytes) 51 | { 52 | return Compress.ReadDeflate(bytes); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Tests/Runtime/GZipDataTransformTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using Cysharp.Threading.Tasks; 3 | using NUnit.Framework; 4 | using UnityEngine.TestTools; 5 | using static LocalStorage.PlayModeTests.Constants.Instances; 6 | using static LocalStorage.PlayModeTests.Constants.Data; 7 | 8 | namespace LocalStorage.PlayModeTests 9 | { 10 | [TestFixture] 11 | public class GZipDataTransformTests 12 | { 13 | [Test] 14 | [TestCaseSource(typeof(Constants.Data), nameof(TestsByteData))] 15 | public void DataTransform_Apply(byte[] data) 16 | { 17 | var result = GZipDT.Apply(data); 18 | 19 | Assert.AreEqual(data, result.ReadGzip()); 20 | } 21 | 22 | [UnityTest] 23 | public IEnumerator DataTransform_ApplyAsync() 24 | => UniTask.ToCoroutine(async () => 25 | { 26 | var result = await GZipDT.ApplyAsync(TestByteData); 27 | 28 | Assert.AreEqual(TestByteData, result.ReadGzip()); 29 | }); 30 | 31 | [Test] 32 | [TestCaseSource(typeof(Constants.Data), nameof(TestsByteData))] 33 | public void DataTransform_Reverse(byte[] data) 34 | { 35 | var compressed = data.WriteGZip(); 36 | 37 | var result = GZipDT.Reverse(compressed); 38 | 39 | Assert.AreEqual(data, result); 40 | } 41 | 42 | [UnityTest] 43 | public IEnumerator DataTransform_ReverseAsync() 44 | => UniTask.ToCoroutine(async () => 45 | { 46 | var compressed = TestByteData.WriteGZip(); 47 | 48 | var result = await GZipDT.ReverseAsync(compressed); 49 | 50 | Assert.AreEqual(TestByteData, result); 51 | }); 52 | } 53 | } -------------------------------------------------------------------------------- /Tests/Runtime/DeflateDataTransformTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using Cysharp.Threading.Tasks; 3 | using NUnit.Framework; 4 | using UnityEngine.TestTools; 5 | using static LocalStorage.PlayModeTests.Constants.Instances; 6 | using static LocalStorage.PlayModeTests.Constants.Data; 7 | 8 | namespace LocalStorage.PlayModeTests 9 | { 10 | [TestFixture] 11 | public class DeflateDataTransformTests 12 | { 13 | [Test] 14 | [TestCaseSource(typeof(Constants.Data), nameof(Constants.Data.TestsByteData))] 15 | public void DataTransform_Apply(byte[] data) 16 | { 17 | var result = DeflateDT.Apply(data); 18 | 19 | Assert.AreEqual(data, result.ReadDeflate()); 20 | } 21 | 22 | [UnityTest] 23 | public IEnumerator DataTransform_ApplyAsync() 24 | => UniTask.ToCoroutine(async () => 25 | { 26 | var result = await DeflateDT.ApplyAsync(TestByteData); 27 | 28 | Assert.AreEqual(TestByteData, result.ReadDeflate()); 29 | }); 30 | 31 | [Test] 32 | [TestCaseSource(typeof(Constants.Data), nameof(Constants.Data.TestsByteData))] 33 | public void DataTransform_Reverse(byte[] data) 34 | { 35 | var compressed = data.WriteDeflate(); 36 | 37 | var result = DeflateDT.Reverse(compressed); 38 | 39 | Assert.AreEqual(data, result); 40 | } 41 | 42 | [UnityTest] 43 | public IEnumerator DataTransform_ReverseAsync() 44 | => UniTask.ToCoroutine(async () => 45 | { 46 | var compressed = TestByteData.WriteDeflate(); 47 | 48 | var result = await DeflateDT.ReverseAsync(compressed); 49 | 50 | Assert.AreEqual(TestByteData, result); 51 | }); 52 | } 53 | } -------------------------------------------------------------------------------- /Runtime/DataTransform/CombinedDataTransformAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 3 | using Cysharp.Threading.Tasks; 4 | #else 5 | using System.Threading.Tasks; 6 | #endif 7 | 8 | namespace Dre0Dru.LocalStorage.DataTransform 9 | { 10 | public class CombinedDataTransformAsync : IDataTransformAsync 11 | { 12 | private readonly IDataTransformAsync _firstTransform; 13 | private readonly IDataTransformAsync _secondTransform; 14 | 15 | #if UNITY_2020_3_OR_NEWER 16 | [UnityEngine.Scripting.RequiredMember] 17 | #endif 18 | public CombinedDataTransformAsync(IDataTransformAsync firstTransform, IDataTransformAsync secondTransform) 19 | { 20 | _firstTransform = firstTransform ?? 21 | throw new ArgumentNullException(nameof(firstTransform)); 22 | _secondTransform = secondTransform ?? 23 | throw new ArgumentNullException(nameof(secondTransform)); 24 | } 25 | 26 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 27 | public UniTask ApplyAsync(byte[] data) => 28 | _firstTransform.ApplyAsync(data) 29 | .ContinueWith(bytes => _secondTransform.ApplyAsync(bytes)); 30 | #else 31 | public Task ApplyAsync(byte[] data) => 32 | _firstTransform.ApplyAsync(data) 33 | .ContinueWith(task => _secondTransform.ApplyAsync(task.Result)) 34 | .Unwrap(); 35 | #endif 36 | 37 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 38 | public UniTask ReverseAsync(byte[] data) => 39 | _secondTransform.ReverseAsync(data) 40 | .ContinueWith(bytes => _firstTransform.ReverseAsync(bytes)); 41 | #else 42 | public Task ReverseAsync(byte[] data) => 43 | _secondTransform.ReverseAsync(data) 44 | .ContinueWith(task => _firstTransform.ReverseAsync(task.Result)) 45 | .Unwrap(); 46 | #endif 47 | } 48 | } -------------------------------------------------------------------------------- /Tests/Runtime/CombinedDataTransformTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Linq; 3 | using Cysharp.Threading.Tasks; 4 | using LocalStorage.DataTransform; 5 | using NUnit.Framework; 6 | using UnityEngine.TestTools; 7 | using static LocalStorage.PlayModeTests.Constants.Instances; 8 | using static LocalStorage.PlayModeTests.Constants.Data; 9 | 10 | namespace LocalStorage.PlayModeTests 11 | { 12 | [TestFixture] 13 | public class CombinedDataTransformTests 14 | { 15 | private static object[] _dataTransforms = 16 | { 17 | new object[] {new CombinedDataTransform(AesDT, DeflateDT)}, 18 | new object[] {new CombinedDataTransform(DeflateDT, AesDT)}, 19 | new object[] {new CombinedDataTransform(AesDT, GZipDT)}, 20 | new object[] {new CombinedDataTransform(GZipDT, AesDT)}, 21 | }; 22 | 23 | [Test] 24 | [TestCaseSource(nameof(_dataTransforms))] 25 | public void DataTransform_ApplyReverse(IDataTransform dataTransform) 26 | { 27 | var applied = dataTransform.Apply(TestByteData); 28 | var result = dataTransform.Reverse(applied); 29 | 30 | Assert.AreEqual(TestByteData, result); 31 | } 32 | 33 | [UnityTest] 34 | public IEnumerator DataTransform_ApplyReverseAsync() 35 | => UniTask.ToCoroutine(async () => 36 | { 37 | async UniTask Test(byte[] data, IDataTransform dataTransform) 38 | { 39 | var applied = await dataTransform.ApplyAsync(data); 40 | var result = await dataTransform.ReverseAsync(applied); 41 | 42 | Assert.AreEqual(data, result); 43 | } 44 | 45 | foreach (var serializationProvider in _dataTransforms 46 | .Select(o => (object[]) o) 47 | .Select(objects => (IDataTransform) objects[0])) 48 | { 49 | await Test(TestByteData, serializationProvider); 50 | } 51 | }); 52 | } 53 | } -------------------------------------------------------------------------------- /Runtime/Serialization/DataTransformSerializationProviderAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 3 | using Cysharp.Threading.Tasks; 4 | #else 5 | using System.Threading.Tasks; 6 | #endif 7 | 8 | namespace Dre0Dru.LocalStorage.Serialization 9 | { 10 | public class DataTransformSerializationProviderAsync : ISerializationProviderAsync 11 | { 12 | private readonly ISerializationProviderAsync _baseProvider; 13 | 14 | private readonly IDataTransformAsync _dataTransform; 15 | 16 | #if UNITY_2020_3_OR_NEWER 17 | [UnityEngine.Scripting.RequiredMember] 18 | #endif 19 | public DataTransformSerializationProviderAsync(ISerializationProviderAsync baseProvider, 20 | IDataTransformAsync dataTransform) 21 | { 22 | _baseProvider = baseProvider ?? 23 | throw new ArgumentNullException(nameof(baseProvider)); 24 | _dataTransform = dataTransform ?? 25 | throw new ArgumentNullException(nameof(dataTransform)); 26 | } 27 | 28 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 29 | public UniTask SerializeAsync(T data) => 30 | _baseProvider.SerializeAsync(data) 31 | .ContinueWith(bytes => _dataTransform.ApplyAsync(bytes)); 32 | #else 33 | public Task SerializeAsync(T data) => 34 | _baseProvider.SerializeAsync(data) 35 | .ContinueWith(task => _dataTransform.ApplyAsync(task.Result)) 36 | .Unwrap(); 37 | #endif 38 | 39 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 40 | public UniTask DeserializeAsync(byte[] data) => 41 | _dataTransform.ReverseAsync(data) 42 | .ContinueWith(bytes => _baseProvider.DeserializeAsync(bytes)); 43 | #else 44 | public Task DeserializeAsync(byte[] data) => 45 | _dataTransform.ReverseAsync(data) 46 | .ContinueWith(task => _baseProvider.DeserializeAsync(task.Result)) 47 | .Unwrap(); 48 | #endif 49 | } 50 | } -------------------------------------------------------------------------------- /Tests/Runtime/AesEncryptionDataTransformTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using Cysharp.Threading.Tasks; 4 | using LocalStorage.Encryption; 5 | using NUnit.Framework; 6 | using UnityEngine.TestTools; 7 | using static LocalStorage.PlayModeTests.Constants.Instances; 8 | using static LocalStorage.PlayModeTests.Constants.Data; 9 | 10 | namespace LocalStorage.PlayModeTests 11 | { 12 | [TestFixture] 13 | public class AesEncryptionDataTransformTests 14 | { 15 | [Test] 16 | public void DataTransform_ThrowsArgumentNullException() 17 | { 18 | Assert.Throws(() => 19 | { 20 | var transform = new AesEncryptionDataTransform(null); 21 | }); 22 | } 23 | 24 | [Test] 25 | [TestCaseSource(typeof(Constants.Data), nameof(Constants.Data.TestsByteData))] 26 | public void DataTransform_Apply(byte[] data) 27 | { 28 | var result = AesDT.Apply(data); 29 | 30 | Assert.AreEqual(data, result.Decrypt()); 31 | } 32 | 33 | [UnityTest] 34 | public IEnumerator DataTransform_ApplyAsync() 35 | => UniTask.ToCoroutine(async () => 36 | { 37 | var result = await AesDT.ApplyAsync(TestByteData); 38 | 39 | Assert.AreEqual(TestByteData, result.Decrypt()); 40 | }); 41 | 42 | [Test] 43 | [TestCaseSource(typeof(Constants.Data), nameof(Constants.Data.TestsByteData))] 44 | public void DataTransform_Reverse(byte[] data) 45 | { 46 | var encrypted = data.Encrypt(); 47 | 48 | var result = AesDT.Reverse(encrypted); 49 | 50 | Assert.AreEqual(data, result); 51 | } 52 | 53 | [UnityTest] 54 | public IEnumerator DataTransform_ReverseAsync() 55 | => UniTask.ToCoroutine(async () => 56 | { 57 | var encrypted = TestByteData.Encrypt(); 58 | 59 | var result = await AesDT.ReverseAsync(encrypted); 60 | 61 | Assert.AreEqual(TestByteData, result); 62 | }); 63 | } 64 | } -------------------------------------------------------------------------------- /Runtime/DataTransform/CombinedDataTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 3 | using Cysharp.Threading.Tasks; 4 | #else 5 | using System.Threading.Tasks; 6 | #endif 7 | 8 | namespace Dre0Dru.LocalStorage.DataTransform 9 | { 10 | public class CombinedDataTransform : IDataTransform 11 | { 12 | private readonly IDataTransform _firstTransform; 13 | private readonly IDataTransform _secondTransform; 14 | 15 | #if UNITY_2020_3_OR_NEWER 16 | [UnityEngine.Scripting.RequiredMember] 17 | #endif 18 | public CombinedDataTransform(IDataTransform firstTransform, IDataTransform secondTransform) 19 | { 20 | _firstTransform = firstTransform ?? 21 | throw new ArgumentNullException(nameof(firstTransform)); 22 | _secondTransform = secondTransform ?? 23 | throw new ArgumentNullException(nameof(secondTransform)); 24 | } 25 | 26 | public byte[] Apply(byte[] data) => 27 | _secondTransform.Apply(_firstTransform.Apply(data)); 28 | 29 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 30 | public UniTask ApplyAsync(byte[] data) => 31 | _firstTransform.ApplyAsync(data) 32 | .ContinueWith(bytes => _secondTransform.ApplyAsync(bytes)); 33 | #else 34 | public Task ApplyAsync(byte[] data) => 35 | _firstTransform.ApplyAsync(data) 36 | .ContinueWith(task => _secondTransform.ApplyAsync(task.Result)) 37 | .Unwrap(); 38 | #endif 39 | 40 | public byte[] Reverse(byte[] data) => 41 | _firstTransform.Reverse(_secondTransform.Reverse(data)); 42 | 43 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 44 | public UniTask ReverseAsync(byte[] data) => 45 | _secondTransform.ReverseAsync(data) 46 | .ContinueWith(bytes => _firstTransform.ReverseAsync(bytes)); 47 | #else 48 | public Task ReverseAsync(byte[] data) => 49 | _secondTransform.ReverseAsync(data) 50 | .ContinueWith(task => _firstTransform.ReverseAsync(task.Result)) 51 | .Unwrap(); 52 | #endif 53 | } 54 | } -------------------------------------------------------------------------------- /Runtime/Serialization/DataTransformSerializationProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 3 | using Cysharp.Threading.Tasks; 4 | #else 5 | using System.Threading.Tasks; 6 | #endif 7 | 8 | namespace Dre0Dru.LocalStorage.Serialization 9 | { 10 | public class DataTransformSerializationProvider : ISerializationProvider 11 | { 12 | private readonly ISerializationProvider _baseProvider; 13 | 14 | private readonly IDataTransform _dataTransform; 15 | 16 | #if UNITY_2020_3_OR_NEWER 17 | [UnityEngine.Scripting.RequiredMember] 18 | #endif 19 | public DataTransformSerializationProvider(ISerializationProvider baseProvider, 20 | IDataTransform dataTransform) 21 | { 22 | _baseProvider = baseProvider ?? 23 | throw new ArgumentNullException(nameof(baseProvider)); 24 | _dataTransform = dataTransform ?? 25 | throw new ArgumentNullException(nameof(dataTransform)); 26 | } 27 | 28 | public byte[] Serialize(T data) => 29 | _dataTransform.Apply(_baseProvider.Serialize(data)); 30 | 31 | public T Deserialize(byte[] data) => 32 | _baseProvider.Deserialize(_dataTransform.Reverse(data)); 33 | 34 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 35 | public UniTask SerializeAsync(T data) => 36 | _baseProvider.SerializeAsync(data) 37 | .ContinueWith(bytes => _dataTransform.ApplyAsync(bytes)); 38 | #else 39 | public Task SerializeAsync(T data) => 40 | _baseProvider.SerializeAsync(data) 41 | .ContinueWith(task => _dataTransform.ApplyAsync(task.Result)) 42 | .Unwrap(); 43 | #endif 44 | 45 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 46 | public UniTask DeserializeAsync(byte[] data) => 47 | _dataTransform.ReverseAsync(data) 48 | .ContinueWith(bytes => _baseProvider.DeserializeAsync(bytes)); 49 | #else 50 | public Task DeserializeAsync(byte[] data) => 51 | _dataTransform.ReverseAsync(data) 52 | .ContinueWith(task => _baseProvider.DeserializeAsync(task.Result)) 53 | .Unwrap(); 54 | #endif 55 | } 56 | } -------------------------------------------------------------------------------- /Runtime/Encryption/AesEncryptionDataTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 3 | using Cysharp.Threading.Tasks; 4 | #else 5 | using System.Threading.Tasks; 6 | #endif 7 | 8 | namespace Dre0Dru.LocalStorage.Encryption 9 | { 10 | public class AesEncryptionDataTransform : IDataTransform 11 | { 12 | private readonly IEncryptionSettings _encryptionSettings; 13 | 14 | #if UNITY_2020_3_OR_NEWER 15 | [UnityEngine.Scripting.RequiredMember] 16 | #endif 17 | public AesEncryptionDataTransform(IEncryptionSettings encryptionSettings) 18 | { 19 | _encryptionSettings = encryptionSettings ?? 20 | throw new ArgumentNullException(nameof(encryptionSettings)); 21 | } 22 | 23 | public byte[] Apply(byte[] data) 24 | { 25 | return AesEncryption.Encrypt(data, _encryptionSettings.Key, 26 | _encryptionSettings.InitializationVector); 27 | } 28 | 29 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 30 | public UniTask ApplyAsync(byte[] data) 31 | { 32 | return AesEncryption.EncryptAsync(data, _encryptionSettings.Key, 33 | _encryptionSettings.InitializationVector).AsUniTask(); 34 | } 35 | #else 36 | public Task ApplyAsync(byte[] data) 37 | { 38 | return AesEncryption.EncryptAsync(data, _encryptionSettings.Key, 39 | _encryptionSettings.InitializationVector); 40 | } 41 | #endif 42 | 43 | public byte[] Reverse(byte[] data) 44 | { 45 | return AesEncryption.Decrypt(data, _encryptionSettings.Key, 46 | _encryptionSettings.InitializationVector); 47 | } 48 | 49 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 50 | public UniTask ReverseAsync(byte[] data) 51 | { 52 | return AesEncryption.DecryptAsync(data, _encryptionSettings.Key, 53 | _encryptionSettings.InitializationVector).AsUniTask(); 54 | } 55 | #else 56 | public Task ReverseAsync(byte[] data) 57 | { 58 | return AesEncryption.DecryptAsync(data, _encryptionSettings.Key, 59 | _encryptionSettings.InitializationVector); 60 | } 61 | #endif 62 | } 63 | } -------------------------------------------------------------------------------- /Runtime/FileStorageAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 3 | using Cysharp.Threading.Tasks; 4 | #else 5 | using System.Threading.Tasks; 6 | #endif 7 | 8 | namespace Dre0Dru.LocalStorage 9 | { 10 | public class FileStorageAsync : IFileStorageAsync 11 | { 12 | private readonly ISerializationProviderAsync _serializationProvider; 13 | private readonly IFileProviderAsync _fileProvider; 14 | 15 | #if UNITY_2020_3_OR_NEWER 16 | [UnityEngine.Scripting.RequiredMember] 17 | #endif 18 | public FileStorageAsync(ISerializationProviderAsync serializationProvider, 19 | IFileProviderAsync fileProvider) 20 | { 21 | _serializationProvider = serializationProvider ?? 22 | throw new ArgumentNullException(nameof(serializationProvider)); 23 | _fileProvider = fileProvider ?? 24 | throw new ArgumentNullException(nameof(fileProvider)); 25 | } 26 | 27 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 28 | public UniTask SaveAsync(TData data, string fileName) => 29 | _serializationProvider.SerializeAsync(data) 30 | .ContinueWith(bytes => 31 | _fileProvider.WriteAsync(bytes, GetFilePath(fileName))); 32 | #else 33 | public Task SaveAsync(TData data, string fileName) => 34 | _serializationProvider.SerializeAsync(data) 35 | .ContinueWith(task => 36 | _fileProvider.WriteAsync(task.Result, GetFilePath(fileName))); 37 | #endif 38 | 39 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 40 | public UniTask LoadAsync(string fileName) 41 | { 42 | return _fileProvider.ReadAsync(GetFilePath(fileName)) 43 | .ContinueWith(bytes => _serializationProvider.DeserializeAsync(bytes)); 44 | } 45 | #else 46 | public Task LoadAsync(string fileName) 47 | { 48 | return _fileProvider.ReadAsync(GetFilePath(fileName)) 49 | .ContinueWith(task => _serializationProvider.DeserializeAsync(task.Result)).Unwrap(); 50 | } 51 | #endif 52 | 53 | public bool Delete(string fileName) => 54 | _fileProvider.Delete(fileName); 55 | 56 | public string GetFilePath(string fileName) => 57 | _fileProvider.GetFilePath(fileName); 58 | 59 | public bool FileExists(string fileName) => 60 | _fileProvider.FileExists(fileName); 61 | } 62 | } -------------------------------------------------------------------------------- /Runtime/FileStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 3 | using Cysharp.Threading.Tasks; 4 | #else 5 | using System.Threading.Tasks; 6 | #endif 7 | 8 | namespace Dre0Dru.LocalStorage 9 | { 10 | public class FileStorage : IFileStorage 11 | { 12 | private readonly ISerializationProvider _serializationProvider; 13 | private readonly IFileProvider _fileProvider; 14 | 15 | #if UNITY_2020_3_OR_NEWER 16 | [UnityEngine.Scripting.RequiredMember] 17 | #endif 18 | public FileStorage(ISerializationProvider serializationProvider, 19 | IFileProvider fileProvider) 20 | { 21 | _serializationProvider = serializationProvider ?? 22 | throw new ArgumentNullException(nameof(serializationProvider)); 23 | _fileProvider = fileProvider ?? 24 | throw new ArgumentNullException(nameof(fileProvider)); 25 | } 26 | 27 | public void Save(TData data, string fileName) => 28 | _fileProvider.Write(_serializationProvider.Serialize(data), 29 | GetFilePath(fileName)); 30 | 31 | public TData Load(string fileName) => 32 | _serializationProvider 33 | .Deserialize(_fileProvider.Read(GetFilePath(fileName))); 34 | 35 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 36 | public UniTask SaveAsync(TData data, string fileName) => 37 | _serializationProvider.SerializeAsync(data) 38 | .ContinueWith(bytes => 39 | _fileProvider.WriteAsync(bytes, GetFilePath(fileName))); 40 | #else 41 | public Task SaveAsync(TData data, string fileName) => 42 | _serializationProvider.SerializeAsync(data) 43 | .ContinueWith(task => 44 | _fileProvider.WriteAsync(task.Result, GetFilePath(fileName))); 45 | #endif 46 | 47 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 48 | public UniTask LoadAsync(string fileName) 49 | { 50 | return _fileProvider.ReadAsync(GetFilePath(fileName)) 51 | .ContinueWith(bytes => _serializationProvider.DeserializeAsync(bytes)); 52 | } 53 | #else 54 | public Task LoadAsync(string fileName) 55 | { 56 | return _fileProvider.ReadAsync(GetFilePath(fileName)) 57 | .ContinueWith(task => _serializationProvider.DeserializeAsync(task.Result)).Unwrap(); 58 | } 59 | #endif 60 | 61 | public bool Delete(string fileName) => 62 | _fileProvider.Delete(fileName); 63 | 64 | public string GetFilePath(string fileName) => 65 | _fileProvider.GetFilePath(fileName); 66 | 67 | public bool FileExists(string fileName) => 68 | _fileProvider.FileExists(fileName); 69 | } 70 | } -------------------------------------------------------------------------------- /Runtime/PlayerPrefsStorageSync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace Dre0Dru.LocalStorage 5 | { 6 | public class PlayerPrefsStorageSync : IPlayerPrefsStorageSync 7 | { 8 | private readonly ISerializationProviderSync _serializationProvider; 9 | private readonly bool _autoSaveEnabled; 10 | 11 | #if UNITY_2020_3_OR_NEWER 12 | [UnityEngine.Scripting.RequiredMember] 13 | #endif 14 | public PlayerPrefsStorageSync(ISerializationProviderSync serializationProvider) 15 | : this(serializationProvider, false) 16 | { 17 | } 18 | 19 | public PlayerPrefsStorageSync(ISerializationProviderSync serializationProvider, bool autoSaveEnabled) 20 | { 21 | _serializationProvider = serializationProvider ?? 22 | throw new ArgumentNullException(nameof(serializationProvider)); 23 | _autoSaveEnabled = autoSaveEnabled; 24 | } 25 | 26 | public void SetData(string key, T data) => 27 | SetBytes(key, _serializationProvider.Serialize(data)); 28 | 29 | public T GetData(string key) => 30 | _serializationProvider.Deserialize(GetBytes(key)); 31 | 32 | public void SetFloat(string key, float value) 33 | { 34 | PlayerPrefs.SetFloat(key, value); 35 | AttemptAutoSave(); 36 | } 37 | 38 | public float GetFloat(string key) => 39 | PlayerPrefs.GetFloat(key); 40 | 41 | public void SetInt(string key, int value) 42 | { 43 | PlayerPrefs.SetInt(key, value); 44 | AttemptAutoSave(); 45 | } 46 | 47 | public int GetInt(string key) => 48 | PlayerPrefs.GetInt(key); 49 | 50 | public void SetString(string key, string value) 51 | { 52 | PlayerPrefs.SetString(key, value); 53 | AttemptAutoSave(); 54 | } 55 | 56 | public string GetString(string key) => 57 | PlayerPrefs.GetString(key); 58 | 59 | public bool HasKey(string key) => 60 | PlayerPrefs.HasKey(key); 61 | 62 | public void Save() => 63 | PlayerPrefs.Save(); 64 | 65 | public void DeleteKey(string key) 66 | { 67 | PlayerPrefs.DeleteKey(key); 68 | AttemptAutoSave(); 69 | } 70 | 71 | public void DeleteAll() 72 | { 73 | PlayerPrefs.DeleteAll(); 74 | AttemptAutoSave(); 75 | } 76 | 77 | private void AttemptAutoSave() 78 | { 79 | if (_autoSaveEnabled) 80 | { 81 | Save(); 82 | } 83 | } 84 | 85 | private void SetBytes(string key, byte[] data) => 86 | SetString(key, Convert.ToBase64String(data)); 87 | 88 | private byte[] GetBytes(string key) => 89 | Convert.FromBase64String(GetString(key)); 90 | } 91 | } -------------------------------------------------------------------------------- /Tests/Runtime/FileProviderTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using Cysharp.Threading.Tasks; 3 | using NUnit.Framework; 4 | using UnityEngine.TestTools; 5 | using static LocalStorage.PlayModeTests.Constants.Instances; 6 | using static LocalStorage.PlayModeTests.Constants.Data; 7 | 8 | namespace LocalStorage.PlayModeTests 9 | { 10 | [TestFixture] 11 | public class FileProviderTests 12 | { 13 | [SetUp] 14 | public virtual void SetUp() 15 | { 16 | Setup.DeleteFile(FilePath); 17 | } 18 | 19 | [TearDown] 20 | public virtual void TearDown() 21 | { 22 | Setup.DeleteFile(FilePath); 23 | } 24 | 25 | [Test] 26 | public void FileProvider_Delete() 27 | { 28 | Setup.CreateEmptyFile(FilePath); 29 | 30 | FP.Delete(FileName); 31 | 32 | Assert.IsFalse(Setup.FileExists(FilePath)); 33 | } 34 | 35 | [Test] 36 | public void FileProvider_GetFilePath() 37 | { 38 | Assert.AreEqual(FilePath, FP.GetFilePath(FileName)); 39 | } 40 | 41 | [Test] 42 | public void FileProvider_FileExists() 43 | { 44 | Assert.IsFalse(FP.FileExists(FileName)); 45 | 46 | Setup.CreateEmptyFile(FilePath); 47 | 48 | Assert.IsTrue(FP.FileExists(FileName)); 49 | } 50 | 51 | [Test] 52 | [TestCaseSource(typeof(Constants.Data), nameof(TestsByteData))] 53 | public void FileProvider_Write(byte[] data) 54 | { 55 | Assert.IsFalse(Setup.FileExists(FilePath)); 56 | 57 | FP.Write(data, FileName); 58 | 59 | Assert.IsTrue(Setup.FileExists(FilePath)); 60 | Assert.AreEqual(data, Setup.ReadFromFile(FilePath)); 61 | } 62 | 63 | [UnityTest] 64 | public IEnumerator FileProvider_WriteAsync() 65 | => UniTask.ToCoroutine(async () => 66 | { 67 | Assert.IsFalse(Setup.FileExists(FilePath)); 68 | 69 | await FP.WriteAsync(TestByteData, FileName); 70 | 71 | Assert.IsTrue(Setup.FileExists(FilePath)); 72 | Assert.AreEqual(TestByteData, Setup.ReadFromFile(FilePath)); 73 | }); 74 | 75 | [Test] 76 | [TestCaseSource(typeof(Constants.Data), nameof(TestsByteData))] 77 | public void FileProvider_Read(byte[] data) 78 | { 79 | Setup.WriteToFile(FilePath, data); 80 | 81 | Assert.AreEqual(data, FP.Read(FileName)); 82 | } 83 | 84 | [UnityTest] 85 | public IEnumerator FileProvider_ReadAsync() 86 | => UniTask.ToCoroutine(async () => 87 | { 88 | Setup.WriteToFile(FilePath, TestByteData); 89 | 90 | var result = await FP.ReadAsync(FileName); 91 | 92 | Assert.AreEqual(TestByteData, result); 93 | }); 94 | } 95 | } -------------------------------------------------------------------------------- /Tests/Runtime/DataTransformSerializationProviderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using Cysharp.Threading.Tasks; 5 | using LocalStorage.Serialization; 6 | using NUnit.Framework; 7 | using UnityEngine.TestTools; 8 | using static LocalStorage.PlayModeTests.Constants.Instances; 9 | using static LocalStorage.PlayModeTests.Constants.Data; 10 | 11 | namespace LocalStorage.PlayModeTests 12 | { 13 | [TestFixture] 14 | public class DataTransformSerializationProviderTests 15 | { 16 | 17 | private static object[] _serializationProviders = 18 | { 19 | new object[] {new DataTransformSerializationProvider(UnityJsonSP, AesDT)}, 20 | new object[] {new DataTransformSerializationProvider(UnityJsonSP, DeflateDT)}, 21 | new object[] {new DataTransformSerializationProvider(UnityJsonSP, GZipDT)}, 22 | }; 23 | 24 | private static object[] _argumentNullExceptionCases = 25 | { 26 | new object[] {null, null}, 27 | new object[] {UnityJsonSP, null}, 28 | new object[] {null, AesDT}, 29 | }; 30 | 31 | [Test] 32 | [TestCaseSource(nameof(_argumentNullExceptionCases))] 33 | public void SerializationProvider_ThrowsArgumentNullException(ISerializationProvider serializationProvider, 34 | IDataTransform dataTransform) 35 | { 36 | Assert.Throws(() => 37 | { 38 | var result = new DataTransformSerializationProvider(serializationProvider, dataTransform); 39 | }); 40 | } 41 | 42 | [Test] 43 | [TestCaseSource(nameof(_serializationProviders))] 44 | public void SerializationProvider_SerializeDeserialize(ISerializationProvider serializationProvider) 45 | { 46 | void Test(T data) 47 | { 48 | var serialized = serializationProvider.Serialize(data); 49 | var deserialized = serializationProvider.Deserialize(serialized); 50 | 51 | Assert.AreEqual(data, deserialized); 52 | } 53 | 54 | Test(GenericDataVector); 55 | Test(GenericDataStruct); 56 | } 57 | 58 | [UnityTest] 59 | public IEnumerator SerializationProvider_SerializeDeserializeAsync() 60 | => UniTask.ToCoroutine(async () => 61 | { 62 | async UniTask Test(T data, ISerializationProvider serializationProvider) 63 | { 64 | var serialized =await serializationProvider.SerializeAsync(data); 65 | var deserialized = await serializationProvider.DeserializeAsync(serialized); 66 | 67 | Assert.AreEqual(data, deserialized); 68 | } 69 | 70 | foreach (var serializationProvider in _serializationProviders 71 | .Select(o => (object[]) o) 72 | .Select(objects => (ISerializationProvider) objects[0])) 73 | { 74 | await Test(GenericDataVector, serializationProvider); 75 | await Test(GenericDataStruct, serializationProvider); 76 | } 77 | }); 78 | } 79 | } -------------------------------------------------------------------------------- /Tests/Runtime/UnityJsonSerializationProviderTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Threading.Tasks; 3 | using Cysharp.Threading.Tasks; 4 | using NUnit.Framework; 5 | using UnityEngine.TestTools; 6 | using static LocalStorage.PlayModeTests.Constants.Instances; 7 | using static LocalStorage.PlayModeTests.Constants.Data; 8 | 9 | namespace LocalStorage.PlayModeTests 10 | { 11 | [TestFixture] 12 | public class UnityJsonSerializationProviderTests 13 | { 14 | [Test] 15 | public void SerializationProvider_Serialize() 16 | { 17 | //Can't use [TestCase] or [TestCaseSource] 18 | //because of IL2CPP AOT compilation 19 | void Test(T data) 20 | { 21 | var result = UnityJsonSP.Serialize(data); 22 | 23 | Assert.AreEqual(data.ToJson().ToBytes(), result); 24 | 25 | var resultJson = result.FromBytes(); 26 | 27 | Assert.AreEqual(data.ToJson(), resultJson); 28 | } 29 | 30 | Test(GenericDataVector); 31 | Test(GenericDataStruct); 32 | } 33 | 34 | [UnityTest] 35 | public IEnumerator SerializationProvider_SerializeAsync() 36 | => UniTask.ToCoroutine(async () => 37 | { 38 | //Can't use [TestCase] or [TestCaseSource] 39 | //because of IL2CPP AOT compilation 40 | async Task Test(T data) 41 | { 42 | var result = await UnityJsonSP.SerializeAsync(data); 43 | 44 | Assert.AreEqual(data.ToJson().ToBytes(), result); 45 | 46 | var resultJson = result.FromBytes(); 47 | 48 | Assert.AreEqual(data.ToJson(), resultJson); 49 | } 50 | 51 | await Test(GenericDataVector); 52 | await Test(GenericDataStruct); 53 | }); 54 | 55 | [Test] 56 | public void SerializationProvider_Deserialize() 57 | { 58 | //Can't use [TestCase] or [TestCaseSource] 59 | //because of IL2CPP AOT compilation 60 | void Test(T data) 61 | { 62 | var json = data.ToJson(); 63 | var bytes = json.ToBytes(); 64 | 65 | var result = UnityJsonSP.Deserialize(bytes); 66 | 67 | Assert.AreEqual(data, result); 68 | } 69 | 70 | Test(GenericDataVector); 71 | Test(GenericDataStruct); 72 | } 73 | 74 | [UnityTest] 75 | public IEnumerator SerializationProvider_DeserializeAsync() 76 | => UniTask.ToCoroutine(async () => 77 | { 78 | //Can't use [TestCase] or [TestCaseSource] 79 | //because of IL2CPP AOT compilation 80 | async Task Test(T data) 81 | { 82 | var json = data.ToJson(); 83 | var bytes = json.ToBytes(); 84 | 85 | var result = await UnityJsonSP.DeserializeAsync(bytes); 86 | 87 | Assert.AreEqual(data, result); 88 | } 89 | 90 | await Test(GenericDataVector); 91 | await Test(GenericDataStruct); 92 | }); 93 | } 94 | } -------------------------------------------------------------------------------- /Tests/Runtime/FileStorageTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using Cysharp.Threading.Tasks; 5 | using LocalStorage.Serialization; 6 | using NUnit.Framework; 7 | using UnityEngine.TestTools; 8 | using static LocalStorage.PlayModeTests.Constants.Instances; 9 | using static LocalStorage.PlayModeTests.Constants.Data; 10 | 11 | namespace LocalStorage.PlayModeTests 12 | { 13 | [TestFixture] 14 | public class FileStorageTests 15 | { 16 | private static readonly object[] ArgumentNullExceptionCases = 17 | { 18 | new object[] {null, null}, 19 | new object[] {UnityJsonSP, null}, 20 | new object[] {null, FP}, 21 | }; 22 | 23 | private static readonly object[] StorageInstances = 24 | { 25 | new object[] {new FileStorage(UnityJsonSP, FP)}, 26 | new object[] {new FileStorage(new DataTransformSerializationProvider(UnityJsonSP, AesDT), FP)}, 27 | new object[] {new FileStorage(new DataTransformSerializationProvider(UnityJsonSP, DeflateDT), FP)}, 28 | new object[] {new FileStorage(new DataTransformSerializationProvider(UnityJsonSP, GZipDT), FP)}, 29 | }; 30 | 31 | [Test] 32 | [TestCaseSource(nameof(ArgumentNullExceptionCases))] 33 | public void Storage_ThrowsArgumentNullException( 34 | ISerializationProvider serializationProvider, IFileProvider fileProvider) 35 | { 36 | Assert.Throws(() => 37 | { 38 | var storage = new FileStorage(serializationProvider, fileProvider); 39 | }); 40 | } 41 | 42 | [Test] 43 | [TestCaseSource(nameof(StorageInstances))] 44 | public void Storage_SaveLoad(IFileStorage storage) 45 | { 46 | //Can't use [TestCase] or [TestCaseSource] 47 | //because of IL2CPP AOT compilation 48 | void Test(T data) 49 | { 50 | Setup.DeleteFile(FilePath); 51 | 52 | Assert.IsFalse(Setup.FileExists(FilePath)); 53 | 54 | storage.Save(data, FileName); 55 | 56 | Assert.IsTrue(Setup.FileExists(FilePath)); 57 | 58 | Assert.AreEqual(data, storage.Load(FileName)); 59 | } 60 | 61 | Test(GenericDataVector); 62 | Test(GenericDataStruct); 63 | 64 | Setup.DeleteFile(FilePath); 65 | } 66 | 67 | [UnityTest] 68 | public IEnumerator Storage_SaveLoadAsync() 69 | => UniTask.ToCoroutine(async () => 70 | { 71 | //Can't use [TestCase] or [TestCaseSource] 72 | //because of IL2CPP AOT compilation 73 | async UniTask Test(T data, IFileStorage storage) 74 | { 75 | Setup.DeleteFile(FilePath); 76 | 77 | Assert.IsFalse(Setup.FileExists(FilePath)); 78 | 79 | await storage.SaveAsync(data, FileName); 80 | 81 | Assert.IsTrue(Setup.FileExists(FilePath)); 82 | 83 | var result = await storage.LoadAsync(FileName); 84 | 85 | Assert.AreEqual(data, result); 86 | 87 | Setup.DeleteFile(FilePath); 88 | } 89 | 90 | foreach (var storage in StorageInstances 91 | .Select(o => (object[]) o) 92 | .Select(objects => (IFileStorage) objects[0])) 93 | { 94 | await Test(GenericDataVector, storage); 95 | await Test(GenericDataStruct, storage); 96 | } 97 | }); 98 | } 99 | } -------------------------------------------------------------------------------- /Runtime/Extensions.cs: -------------------------------------------------------------------------------- 1 | namespace Dre0Dru.LocalStorage 2 | { 3 | public static class Extensions 4 | { 5 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 6 | public static Cysharp.Threading.Tasks.UniTask SerializeFakeAsync( 7 | this ISerializationProviderSync provider, T data) => 8 | Cysharp.Threading.Tasks.UniTask.FromResult(provider.Serialize(data)); 9 | #else 10 | public static System.Threading.Tasks.Task SerializeFakeAsync( 11 | this ISerializationProviderSync provider, T data) => 12 | System.Threading.Tasks.Task.FromResult(provider.Serialize(data)); 13 | #endif 14 | 15 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 16 | public static Cysharp.Threading.Tasks.UniTask DeserializeFakeAsync( 17 | this ISerializationProviderSync provider, byte[] data) => 18 | Cysharp.Threading.Tasks.UniTask.FromResult(provider.Deserialize(data)); 19 | #else 20 | public static System.Threading.Tasks.Task DeserializeFakeAsync(this ISerializationProviderSync provider, 21 | byte[] data) => 22 | System.Threading.Tasks.Task.FromResult(provider.Deserialize(data)); 23 | #endif 24 | 25 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 26 | public static Cysharp.Threading.Tasks.UniTask WriteFakeAsync(this IFileProviderSync provider, byte[] output, 27 | string fileName) 28 | { 29 | provider.Write(output, fileName); 30 | return Cysharp.Threading.Tasks.UniTask.CompletedTask; 31 | } 32 | #else 33 | public static System.Threading.Tasks.Task WriteFakeAsync(this IFileProviderSync provider, byte[] output, 34 | string fileName) 35 | { 36 | provider.Write(output, fileName); 37 | return System.Threading.Tasks.Task.CompletedTask; 38 | } 39 | #endif 40 | 41 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 42 | public static Cysharp.Threading.Tasks.UniTask ReadFakeAsync(this IFileProviderSync provider, 43 | string fileName) => 44 | Cysharp.Threading.Tasks.UniTask.FromResult(provider.Read(fileName)); 45 | #else 46 | public static System.Threading.Tasks.Task ReadFakeAsync(this IFileProviderSync provider, 47 | string fileName) => 48 | System.Threading.Tasks.Task.FromResult(provider.Read(fileName)); 49 | #endif 50 | 51 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 52 | public static Cysharp.Threading.Tasks.UniTask ApplyFakeAsync(this IDataTransformSync dataTransform, 53 | byte[] data) => 54 | Cysharp.Threading.Tasks.UniTask.FromResult(dataTransform.Apply(data)); 55 | #else 56 | public static System.Threading.Tasks.Task ApplyFakeAsync(this IDataTransformSync dataTransform, 57 | byte[] data) => 58 | System.Threading.Tasks.Task.FromResult(dataTransform.Apply(data)); 59 | #endif 60 | 61 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 62 | public static Cysharp.Threading.Tasks.UniTask ReverseFakeAsync(this IDataTransformSync dataTransform, 63 | byte[] data) => 64 | Cysharp.Threading.Tasks.UniTask.FromResult(dataTransform.Reverse(data)); 65 | #else 66 | public static System.Threading.Tasks.Task ReverseFakeAsync(this IDataTransformSync dataTransform, 67 | byte[] data) => 68 | System.Threading.Tasks.Task.FromResult(dataTransform.Reverse(data)); 69 | #endif 70 | } 71 | } -------------------------------------------------------------------------------- /Runtime/PlayerPrefsStorageAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 3 | using Cysharp.Threading.Tasks; 4 | #else 5 | using System.Threading.Tasks; 6 | #endif 7 | using UnityEngine; 8 | 9 | namespace Dre0Dru.LocalStorage 10 | { 11 | public class PlayerPrefsStorageAsync : IPlayerPrefsStorageAsync 12 | { 13 | private readonly ISerializationProviderAsync _serializationProvider; 14 | private readonly bool _autoSaveEnabled; 15 | 16 | #if UNITY_2020_3_OR_NEWER 17 | [UnityEngine.Scripting.RequiredMember] 18 | #endif 19 | public PlayerPrefsStorageAsync(ISerializationProviderAsync serializationProvider) 20 | : this(serializationProvider, false) 21 | { 22 | } 23 | 24 | public PlayerPrefsStorageAsync(ISerializationProviderAsync serializationProvider, bool autoSaveEnabled) 25 | { 26 | _serializationProvider = serializationProvider ?? 27 | throw new ArgumentNullException(nameof(serializationProvider)); 28 | _autoSaveEnabled = autoSaveEnabled; 29 | } 30 | 31 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 32 | public UniTask SetDataAsync(string key, T data) => 33 | _serializationProvider.SerializeAsync(data) 34 | .ContinueWith(bytes => SetBytes(key, bytes)); 35 | #else 36 | public Task SetDataAsync(string key, T data) => 37 | _serializationProvider.SerializeAsync(data) 38 | .ContinueWith(task => SetBytes(key, task.Result), TaskScheduler.FromCurrentSynchronizationContext()); 39 | #endif 40 | 41 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 42 | public UniTask GetDataAsync(string key) => 43 | _serializationProvider.DeserializeAsync(GetBytes(key)); 44 | #else 45 | public Task GetDataAsync(string key) => 46 | _serializationProvider.DeserializeAsync(GetBytes(key)); 47 | #endif 48 | 49 | public void SetFloat(string key, float value) 50 | { 51 | PlayerPrefs.SetFloat(key, value); 52 | AttemptAutoSave(); 53 | } 54 | 55 | public float GetFloat(string key) => 56 | PlayerPrefs.GetFloat(key); 57 | 58 | public void SetInt(string key, int value) 59 | { 60 | PlayerPrefs.SetInt(key, value); 61 | AttemptAutoSave(); 62 | } 63 | 64 | public int GetInt(string key) => 65 | PlayerPrefs.GetInt(key); 66 | 67 | public void SetString(string key, string value) 68 | { 69 | PlayerPrefs.SetString(key, value); 70 | AttemptAutoSave(); 71 | } 72 | 73 | public string GetString(string key) => 74 | PlayerPrefs.GetString(key); 75 | 76 | public bool HasKey(string key) => 77 | PlayerPrefs.HasKey(key); 78 | 79 | public void Save() => 80 | PlayerPrefs.Save(); 81 | 82 | public void DeleteKey(string key) 83 | { 84 | PlayerPrefs.DeleteKey(key); 85 | AttemptAutoSave(); 86 | } 87 | 88 | public void DeleteAll() 89 | { 90 | PlayerPrefs.DeleteAll(); 91 | AttemptAutoSave(); 92 | } 93 | 94 | private void AttemptAutoSave() 95 | { 96 | if (_autoSaveEnabled) 97 | { 98 | Save(); 99 | } 100 | } 101 | 102 | private void SetBytes(string key, byte[] data) => 103 | SetString(key, Convert.ToBase64String(data)); 104 | 105 | private byte[] GetBytes(string key) => 106 | Convert.FromBase64String(GetString(key)); 107 | } 108 | } -------------------------------------------------------------------------------- /Runtime/PlayerPrefsStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 3 | using Cysharp.Threading.Tasks; 4 | #else 5 | using System.Threading.Tasks; 6 | #endif 7 | using UnityEngine; 8 | 9 | namespace Dre0Dru.LocalStorage 10 | { 11 | public class PlayerPrefsStorage : IPlayerPrefsStorage 12 | { 13 | private readonly ISerializationProvider _serializationProvider; 14 | private readonly bool _autoSaveEnabled; 15 | 16 | #if UNITY_2020_3_OR_NEWER 17 | [UnityEngine.Scripting.RequiredMember] 18 | #endif 19 | public PlayerPrefsStorage(ISerializationProvider serializationProvider) 20 | : this(serializationProvider, false) 21 | { 22 | } 23 | 24 | public PlayerPrefsStorage(ISerializationProvider serializationProvider, bool autoSaveEnabled) 25 | { 26 | _serializationProvider = serializationProvider ?? 27 | throw new ArgumentNullException(nameof(serializationProvider)); 28 | _autoSaveEnabled = autoSaveEnabled; 29 | } 30 | 31 | public void SetData(string key, T data) => 32 | SetBytes(key, _serializationProvider.Serialize(data)); 33 | 34 | public T GetData(string key) => 35 | _serializationProvider.Deserialize(GetBytes(key)); 36 | 37 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 38 | public UniTask SetDataAsync(string key, T data) => 39 | _serializationProvider.SerializeAsync(data) 40 | .ContinueWith(bytes => SetBytes(key, bytes)); 41 | #else 42 | public Task SetDataAsync(string key, T data) => 43 | _serializationProvider.SerializeAsync(data) 44 | .ContinueWith(task => SetBytes(key, task.Result), TaskScheduler.FromCurrentSynchronizationContext()); 45 | #endif 46 | 47 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 48 | public UniTask GetDataAsync(string key) => 49 | _serializationProvider.DeserializeAsync(GetBytes(key)); 50 | #else 51 | public Task GetDataAsync(string key) => 52 | _serializationProvider.DeserializeAsync(GetBytes(key)); 53 | #endif 54 | 55 | public void SetFloat(string key, float value) 56 | { 57 | PlayerPrefs.SetFloat(key, value); 58 | AttemptAutoSave(); 59 | } 60 | 61 | public float GetFloat(string key) => 62 | PlayerPrefs.GetFloat(key); 63 | 64 | public void SetInt(string key, int value) 65 | { 66 | PlayerPrefs.SetInt(key, value); 67 | AttemptAutoSave(); 68 | } 69 | 70 | public int GetInt(string key) => 71 | PlayerPrefs.GetInt(key); 72 | 73 | public void SetString(string key, string value) 74 | { 75 | PlayerPrefs.SetString(key, value); 76 | AttemptAutoSave(); 77 | } 78 | 79 | public string GetString(string key) => 80 | PlayerPrefs.GetString(key); 81 | 82 | public bool HasKey(string key) => 83 | PlayerPrefs.HasKey(key); 84 | 85 | public void Save() => 86 | PlayerPrefs.Save(); 87 | 88 | public void DeleteKey(string key) 89 | { 90 | PlayerPrefs.DeleteKey(key); 91 | AttemptAutoSave(); 92 | } 93 | 94 | public void DeleteAll() 95 | { 96 | PlayerPrefs.DeleteAll(); 97 | AttemptAutoSave(); 98 | } 99 | 100 | private void AttemptAutoSave() 101 | { 102 | if (_autoSaveEnabled) 103 | { 104 | Save(); 105 | } 106 | } 107 | 108 | private void SetBytes(string key, byte[] data) => 109 | SetString(key, Convert.ToBase64String(data)); 110 | 111 | private byte[] GetBytes(string key) => 112 | Convert.FromBase64String(GetString(key)); 113 | } 114 | } -------------------------------------------------------------------------------- /Runtime/Encryption/AesEncryption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Cryptography; 4 | using System.Threading.Tasks; 5 | 6 | namespace Dre0Dru.LocalStorage.Encryption 7 | { 8 | public static class AesEncryption 9 | { 10 | public static byte[] Encrypt(byte[] dataToEncrypt, byte[] key, byte[] initializationVector) 11 | { 12 | if (dataToEncrypt == null) 13 | { 14 | throw new ArgumentNullException(nameof(dataToEncrypt)); 15 | } 16 | 17 | var encryptor = CreateEncryptor(key, initializationVector); 18 | 19 | return PerformCryptography(dataToEncrypt, encryptor); 20 | } 21 | 22 | public static Task EncryptAsync(byte[] dataToEncrypt, byte[] key, byte[] initializationVector) 23 | { 24 | if (dataToEncrypt == null) 25 | { 26 | throw new ArgumentNullException(nameof(dataToEncrypt)); 27 | } 28 | 29 | var encryptor = CreateEncryptor(key, initializationVector); 30 | 31 | return PerformCryptographyAsync(dataToEncrypt, encryptor); 32 | } 33 | 34 | public static byte[] Decrypt(byte[] encryptedData, byte[] key, byte[] initializationVector) 35 | { 36 | if (encryptedData == null) 37 | { 38 | throw new ArgumentNullException(nameof(encryptedData)); 39 | } 40 | 41 | var decryptor = CreateDecryptor(key, initializationVector); 42 | 43 | return PerformCryptography(encryptedData, decryptor); 44 | } 45 | 46 | public static Task DecryptAsync(byte[] encryptedData, byte[] key, byte[] initializationVector) 47 | { 48 | if (encryptedData == null) 49 | { 50 | throw new ArgumentNullException(nameof(encryptedData)); 51 | } 52 | 53 | var decryptor = CreateDecryptor(key, initializationVector); 54 | 55 | return PerformCryptographyAsync(encryptedData, decryptor); 56 | } 57 | 58 | private static ICryptoTransform CreateEncryptor(byte[] key, byte[] initializationVector) 59 | { 60 | using var aes = CreateAes(key, initializationVector); 61 | 62 | return aes.CreateEncryptor(aes.Key, aes.IV); 63 | } 64 | 65 | private static ICryptoTransform CreateDecryptor(byte[] key, byte[] initializationVector) 66 | { 67 | using var aes = CreateAes(key, initializationVector); 68 | 69 | return aes.CreateDecryptor(aes.Key, aes.IV); 70 | } 71 | 72 | private static Aes CreateAes(byte[] key, byte[] initializationVector) 73 | { 74 | var aes = Aes.Create(); 75 | aes.Key = key; 76 | aes.IV = initializationVector; 77 | return aes; 78 | } 79 | 80 | private static byte[] PerformCryptography(byte[] data, ICryptoTransform cryptoTransform) 81 | { 82 | using (cryptoTransform) 83 | using (var msDecrypt = new MemoryStream()) 84 | using (var csDecrypt = new CryptoStream(msDecrypt, cryptoTransform, CryptoStreamMode.Write)) 85 | { 86 | csDecrypt.Write(data, 0, data.Length); 87 | csDecrypt.FlushFinalBlock(); 88 | 89 | return msDecrypt.ToArray(); 90 | } 91 | } 92 | 93 | private static Task PerformCryptographyAsync(byte[] data, ICryptoTransform cryptoTransform) 94 | { 95 | var msDecrypt = new MemoryStream(); 96 | var csDecrypt = new CryptoStream(msDecrypt, cryptoTransform, CryptoStreamMode.Write); 97 | return csDecrypt.WriteAsync(data, 0, data.Length) 98 | .ContinueWith(task => 99 | { 100 | using (csDecrypt) 101 | using (cryptoTransform) 102 | using (msDecrypt) 103 | { 104 | csDecrypt.FlushFinalBlock(); 105 | return msDecrypt.ToArray(); 106 | } 107 | }); 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /Runtime/FileProvider.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using UnityEngine; 3 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 4 | using Cysharp.Threading.Tasks; 5 | #else 6 | using System.Threading.Tasks; 7 | #endif 8 | 9 | namespace Dre0Dru.LocalStorage 10 | { 11 | public class FileProvider : IFileProvider 12 | { 13 | private readonly string _path; 14 | 15 | #if UNITY_2020_3_OR_NEWER 16 | [UnityEngine.Scripting.RequiredMember] 17 | #endif 18 | public FileProvider() : this(null) 19 | { 20 | } 21 | 22 | public FileProvider(string path) 23 | { 24 | _path = string.IsNullOrEmpty(path) 25 | ? Application.persistentDataPath 26 | : Path.Combine(Application.persistentDataPath, path); 27 | 28 | if (Directory.Exists(_path) == false) 29 | { 30 | Directory.CreateDirectory(_path); 31 | } 32 | } 33 | 34 | public void Write(byte[] output, string fileName) => 35 | File.WriteAllBytes(GetFilePath(fileName), output); 36 | 37 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 38 | public UniTask WriteAsync(byte[] output, string fileName) 39 | { 40 | var fileStream = CreateFileStream(fileName, FileMode.Create, 41 | FileAccess.Write, FileShare.None); 42 | 43 | return fileStream.WriteAsync(output, 0, output.Length) 44 | .ContinueWith(task => fileStream.Dispose()).AsUniTask(); 45 | } 46 | #else 47 | public Task WriteAsync(byte[] output, string fileName) 48 | { 49 | var fileStream = CreateFileStream(fileName, FileMode.Create, 50 | FileAccess.Write, FileShare.None); 51 | 52 | return fileStream.WriteAsync(output, 0, output.Length) 53 | .ContinueWith(task => fileStream.Dispose()); 54 | } 55 | #endif 56 | 57 | public byte[] Read(string fileName) => 58 | File.ReadAllBytes(GetFilePath(fileName)); 59 | 60 | #if !DISABLE_UNITASK_SUPPORT && UNITASK_SUPPORT 61 | public UniTask ReadAsync(string fileName) 62 | { 63 | var fileStream = CreateFileStream(fileName, FileMode.Open, 64 | FileAccess.Read, FileShare.Read); 65 | 66 | var buffer = new byte[fileStream.Length]; 67 | //Assuming data length is < int.MaxValue 68 | return fileStream.ReadAsync(buffer, 0, buffer.Length) 69 | .ContinueWith(task => 70 | { 71 | fileStream.Dispose(); 72 | return buffer; 73 | }).AsUniTask(); 74 | } 75 | #else 76 | public Task ReadAsync(string fileName) 77 | { 78 | var fileStream = CreateFileStream(fileName, FileMode.Open, 79 | FileAccess.Read, FileShare.Read); 80 | 81 | var buffer = new byte[fileStream.Length]; 82 | //Assuming data length is < int.MaxValue 83 | return fileStream.ReadAsync(buffer, 0, buffer.Length) 84 | .ContinueWith(task => 85 | { 86 | fileStream.Dispose(); 87 | return buffer; 88 | }); 89 | } 90 | #endif 91 | 92 | public bool Delete(string fileName) 93 | { 94 | if (FileExists(fileName)) 95 | { 96 | File.Delete(GetFilePath(fileName)); 97 | return true; 98 | } 99 | 100 | return false; 101 | } 102 | 103 | public string GetFilePath(string fileName) => 104 | Path.Combine(_path, fileName); 105 | 106 | public bool FileExists(string fileName) => 107 | File.Exists(GetFilePath(fileName)); 108 | 109 | private FileStream CreateFileStream(string fileName, FileMode fileMode, 110 | FileAccess fileAccess, FileShare fileShare) 111 | { 112 | return new FileStream( 113 | GetFilePath(fileName), 114 | fileMode, fileAccess, fileShare, 115 | 4096, true); 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /Tests/Runtime/PlayerPrefsStorageTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using Cysharp.Threading.Tasks; 4 | using NUnit.Framework; 5 | using UnityEngine.TestTools; 6 | using static LocalStorage.PlayModeTests.Constants.Instances; 7 | using static LocalStorage.PlayModeTests.Constants.Data; 8 | 9 | namespace LocalStorage.PlayModeTests 10 | { 11 | [TestFixture] 12 | public class PlayerPrefsStorageTests 13 | { 14 | private static readonly IPlayerPrefsStorage Storage = 15 | new PlayerPrefsStorage(UnityJsonSP, true); 16 | 17 | [SetUp] 18 | public virtual void SetUp() 19 | { 20 | Setup.ClearPlayerPrefs(); 21 | } 22 | 23 | [TearDown] 24 | public virtual void TearDown() 25 | { 26 | Setup.ClearPlayerPrefs(); 27 | } 28 | 29 | [Test] 30 | public void Storage_ThrowsArgumentNullException() 31 | { 32 | Assert.Throws(() => 33 | { 34 | var storage = new PlayerPrefsStorage(null); 35 | }); 36 | } 37 | 38 | [Test] 39 | public void Storage_GetSetData() 40 | { 41 | //Can't use [TestCase] or [TestCaseSource] 42 | //because of IL2CPP AOT compilation 43 | void Test(T data) 44 | { 45 | Setup.ClearPlayerPrefs(); 46 | 47 | Assert.IsFalse(Storage.HasKey(DataKey)); 48 | 49 | Storage.SetData(DataKey, data); 50 | 51 | Assert.IsTrue(Storage.HasKey(DataKey)); 52 | Assert.AreEqual(data, Storage.GetData(DataKey)); 53 | 54 | Setup.ClearPlayerPrefs(); 55 | } 56 | 57 | Test(GenericDataVector); 58 | Test(GenericDataStruct); 59 | } 60 | 61 | [UnityTest] 62 | public IEnumerator Storage_GetSetDataAsync() => 63 | UniTask.ToCoroutine(async () => 64 | { 65 | //Can't use [ValueSource] 66 | async UniTask Test(T data) 67 | { 68 | Setup.ClearPlayerPrefs(); 69 | 70 | Assert.IsFalse(Storage.HasKey(DataKey)); 71 | 72 | await Storage.SetDataAsync(DataKey, data); 73 | var result = await Storage.GetDataAsync(DataKey); 74 | 75 | Assert.IsTrue(Storage.HasKey(DataKey)); 76 | Assert.AreEqual(data, result); 77 | 78 | Setup.ClearPlayerPrefs(); 79 | } 80 | 81 | await Test(GenericDataVector); 82 | await Test(GenericDataStruct); 83 | }); 84 | 85 | [Test] 86 | [TestCase(1.0f)] 87 | public void Storage_GetSetFloat(float data) 88 | { 89 | Assert.IsFalse(Storage.HasKey(DataKey)); 90 | 91 | Storage.SetFloat(DataKey, data); 92 | 93 | Assert.IsTrue(Storage.HasKey(DataKey)); 94 | Assert.AreEqual(data, Storage.GetFloat(DataKey)); 95 | } 96 | 97 | [Test] 98 | [TestCase(1)] 99 | public void Storage_GetSetInt(int data) 100 | { 101 | Assert.IsFalse(Storage.HasKey(DataKey)); 102 | 103 | Storage.SetInt(DataKey, data); 104 | 105 | Assert.IsTrue(Storage.HasKey(DataKey)); 106 | Assert.AreEqual(data, Storage.GetInt(DataKey)); 107 | } 108 | 109 | [Test] 110 | [TestCase("string")] 111 | public void Storage_GetSetString(string data) 112 | { 113 | Assert.IsFalse(Storage.HasKey(DataKey)); 114 | 115 | Storage.SetString(DataKey, data); 116 | 117 | Assert.IsTrue(Storage.HasKey(DataKey)); 118 | Assert.AreEqual(data, Storage.GetString(DataKey)); 119 | } 120 | 121 | [Test] 122 | public void Storage_DeleteKey() 123 | { 124 | Setup.PlayerPrefsCreateKey(DataKey); 125 | Assert.IsTrue(Storage.HasKey(DataKey)); 126 | 127 | Storage.DeleteKey(DataKey); 128 | 129 | Assert.IsFalse(Storage.HasKey(DataKey)); 130 | } 131 | 132 | [Test] 133 | public void Storage_DeleteAll() 134 | { 135 | Setup.PlayerPrefsCreateKey(DataKey); 136 | Assert.IsTrue(Storage.HasKey(DataKey)); 137 | 138 | Storage.DeleteAll(); 139 | 140 | Assert.IsFalse(Storage.HasKey(DataKey)); 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [5.0.0](https://github.com/dre0dru/LocalStorage/compare/v4.1.0...v5.0.0) (2022-11-29) 2 | 3 | 4 | ### chore 5 | 6 | * updated release action, package at root ([c81ecc6](https://github.com/dre0dru/LocalStorage/commit/c81ecc6cc0c840d96da0c735d871879a2b364b85)) 7 | 8 | 9 | ### BREAKING CHANGES 10 | 11 | * namespaces changes 12 | 13 | # [4.1.0](https://github.com/dre0dru/LocalStorage/compare/v4.0.1...v4.1.0) (2021-12-19) 14 | 15 | 16 | ### Features 17 | 18 | * added newtonsoft json serializer ([3e71001](https://github.com/dre0dru/LocalStorage/commit/3e7100166a2c2e523c174fca9c114e10401f19ba)) 19 | 20 | ## [4.0.1](https://github.com/dre0dru/LocalStorage/compare/v4.0.0...v4.0.1) (2021-08-01) 21 | 22 | 23 | ### Bug Fixes 24 | 25 | * wrapped `RequiredMember` attribute in preprocessor directive ([b9370bf](https://github.com/dre0dru/LocalStorage/commit/b9370bfc11826e54a04703d561585c093f3fffed)) 26 | 27 | # [4.0.0](https://github.com/dre0dru/LocalStorage/compare/v3.1.0...v4.0.0) (2021-06-29) 28 | 29 | 30 | ### Features 31 | 32 | * split async and sync API into different interfaces ([21a4e84](https://github.com/dre0dru/LocalStorage/commit/21a4e848d80e92402f6c3ad8aeeda2e5691d20bb)) 33 | 34 | 35 | ### BREAKING CHANGES 36 | 37 | * core interfaces changed, serialization classes moved into separate namespace, introduced sync/async only implementations for several facades, removed generic storage interfaces and implementations 38 | 39 | # [3.1.0](https://github.com/dre0dru/LocalStorage/compare/v3.0.0...v3.1.0) (2021-05-21) 40 | 41 | 42 | ### Features 43 | 44 | * added optional `UniTask` support for async API ([bd3ed83](https://github.com/dre0dru/LocalStorage/commit/bd3ed83dbc2b4846fd30203d83cf46b76e3b8ded)) 45 | 46 | # [3.0.0](https://github.com/dre0dru/LocalStorage/compare/v2.4.0...v3.0.0) (2021-05-19) 47 | 48 | 49 | ### Features 50 | 51 | * misspelled footer in commits, need to bump version ([53bece0](https://github.com/dre0dru/LocalStorage/commit/53bece0575dfe033bb9c844d3793afcebd230836)) 52 | 53 | 54 | ### BREAKING CHANGES 55 | 56 | * bump package version 57 | 58 | # [2.4.0](https://github.com/dre0dru/LocalStorage/compare/v2.3.0...v2.4.0) (2021-05-19) 59 | 60 | 61 | ### Features 62 | 63 | * added `IPlayerPrefsStorage` and its implementation ([310d9e9](https://github.com/dre0dru/LocalStorage/commit/310d9e99dd5f6032764b6b89958386eac555dfa4)) 64 | * added new `async` APIs for encryption and compression, removed `async` keyword and replaced with task chaining ([549e7b7](https://github.com/dre0dru/LocalStorage/commit/549e7b77ee6360a5853fab1075d87e3ae221c4b7)) 65 | * changed `IFileStorage` generic interface declaration to use only `ISerializationProvider` as generic constraint ([683e57f](https://github.com/dre0dru/LocalStorage/commit/683e57fe0bb42c2701d1a1e83b3148f5c0f58300)) 66 | * introduced `IDataTransform` and `DataTransformSerializationProvider` ([145b6f4](https://github.com/dre0dru/LocalStorage/commit/145b6f4b73da206d4d530479e2c236a1d327f7c4)) 67 | * preparing for adding player prefs storage ([aa1750c](https://github.com/dre0dru/LocalStorage/commit/aa1750c427dd0a9b87eb20a7d48d40e0aa05d229)) 68 | 69 | # [2.3.0](https://github.com/dre0dru/LocalStorage/compare/v2.2.0...v2.3.0) (2021-05-15) 70 | 71 | 72 | ### Features 73 | 74 | * added async methods to `ISerializationProvider`, removed async keyword from `Storage`, returning chained tasks instead ([dce37ff](https://github.com/dre0dru/LocalStorage/commit/dce37ff34a6c9f6170456fb31422764196bc6276)) 75 | 76 | # [2.2.0](https://github.com/dre0dru/LocalStorage/compare/v2.1.0...v2.2.0) (2021-05-08) 77 | 78 | 79 | ### Features 80 | 81 | * reintroduced `IStorage` interface for service abstraction ([8153927](https://github.com/dre0dru/LocalStorage/commit/8153927ea81bf3244e648e88854c96172a611c3d)) 82 | 83 | # [2.1.0](https://github.com/dre0dru/LocalStorage/compare/v2.0.0...v2.1.0) (2021-05-07) 84 | 85 | 86 | ### Features 87 | 88 | * added compressed data provider ([e7d3ed7](https://github.com/dre0dru/LocalStorage/commit/e7d3ed7f3524f2b279ec9295b2c6e27555f2ffbb)) 89 | 90 | # [2.0.0](https://github.com/dre0dru/LocalStorage/compare/v1.0.0...v2.0.0) (2021-04-02) 91 | 92 | 93 | ### Bug Fixes 94 | 95 | * `IOException` for custom path if directory does not exist ([47862e4](https://github.com/dre0dru/LocalStorage/commit/47862e47c0b89b521ba2628729e735c9eeb40737)) 96 | * fixed parameter name in `FileProvider` ([577c8c1](https://github.com/dre0dru/LocalStorage/commit/577c8c141272888d554da5264d282e47947dd7ca)) 97 | 98 | 99 | ### Features 100 | 101 | * added generic storage ([d0f203f](https://github.com/dre0dru/LocalStorage/commit/d0f203f747f1ab6804121a05826fc509ae5ab6b8)) 102 | * default path for `FileProvider` is `Application.persistentDataPath` ([885b7db](https://github.com/dre0dru/LocalStorage/commit/885b7db8c519431ebd94f6072cc29a2d1db94d9a)) 103 | * moved file manipulation from storage to file provider, added custom file paths ([90c62b1](https://github.com/dre0dru/LocalStorage/commit/90c62b12f4995db66168a745691cc9083697d005)) 104 | 105 | 106 | ### BREAKING CHANGES 107 | 108 | * file provider now uses `fileName` as parameter instead of `filePath` 109 | 110 | # 1.0.0 (2021-03-22) 111 | 112 | 113 | ### Features 114 | 115 | * removed storage interface - it is unnecessary abstraction ([1a1ff37](https://github.com/dre0dru/LocalStorage/commit/1a1ff377d2c2d236264a67c0efbe54aa37287012)) 116 | -------------------------------------------------------------------------------- /Tests/Runtime/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Cryptography; 4 | using LocalStorage.Compression; 5 | using LocalStorage.Encryption; 6 | using LocalStorage.Serialization; 7 | using UnityEngine; 8 | 9 | namespace LocalStorage.PlayModeTests 10 | { 11 | public static class Constants 12 | { 13 | public static class Instances 14 | { 15 | public class EncryptionSettings : IEncryptionSettings 16 | { 17 | public byte[] Key { get; } 18 | public byte[] InitializationVector { get; } 19 | 20 | public EncryptionSettings() 21 | { 22 | using var aes = Aes.Create(); 23 | Key = aes.Key; 24 | InitializationVector = aes.IV; 25 | } 26 | } 27 | 28 | public static readonly IEncryptionSettings Es = new EncryptionSettings(); 29 | public static readonly IDataTransform AesDT = new AesEncryptionDataTransform(Es); 30 | public static readonly IDataTransform DeflateDT = new DeflateDataTransform(); 31 | public static readonly IDataTransform GZipDT = new GZipDataTransform(); 32 | 33 | public static readonly ISerializationProvider UnityJsonSP 34 | = new UnityJsonSerializationProvider(); 35 | 36 | public static readonly IFileProvider FP = new FileProvider(); 37 | } 38 | 39 | public static class Data 40 | { 41 | public const string FileName = "file.test"; 42 | public const string DataKey = "key"; 43 | 44 | public static string FilePath => Path.Combine(Application.persistentDataPath, FileName); 45 | 46 | public const string LoremIpsum = 47 | @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec condimentum tortor ut risus accumsan, quis egestas diam placerat. Aliquam fermentum sit amet nunc in fermentum. Vivamus cursus volutpat pulvinar. Donec sagittis fermentum lacus non facilisis. Donec efficitur nulla metus, nec euismod nisl pretium sed. Sed quis metus vel tortor sollicitudin volutpat vel eget libero. Pellentesque blandit laoreet tincidunt. Ut bibendum, ipsum a viverra semper, nulla orci sodales nisi, quis blandit mauris elit vel nisi. Etiam laoreet mattis vestibulum. Quisque id magna lectus. 48 | Duis eleifend eros sit amet augue eleifend consectetur. Integer id eros et lorem consectetur convallis. Nunc nec porttitor ligula, et lobortis sapien. Nullam vel tortor vitae dui scelerisque condimentum. Pellentesque erat mauris, ultricies a sapien eu, blandit pellentesque dui. Donec accumsan nisi non venenatis tincidunt. Nam porttitor sodales porttitor. Fusce quis quam nulla. Pellentesque lobortis tristique neque, sed porttitor felis auctor vel. Fusce sodales turpis erat, vitae varius elit sollicitudin eu. 49 | Phasellus non sem sapien. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur vitae ligula est. Vestibulum cursus ac felis quis accumsan. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aliquam congue ullamcorper neque, sit amet eleifend magna tincidunt eget. Donec magna lectus, suscipit ut efficitur nec, maximus sit amet eros. Maecenas varius imperdiet elit, vel iaculis leo ultrices quis. Fusce cursus lorem et ligula efficitur, et molestie turpis vulputate. Curabitur interdum imperdiet suscipit. 50 | Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus pellentesque convallis commodo. Vestibulum eleifend mauris id efficitur pulvinar. Vestibulum hendrerit auctor nisi quis luctus. Aenean consequat justo ut quam tincidunt, non tempus nunc efficitur. Nulla pulvinar aliquam mi quis varius. Vestibulum nec purus et nunc tempus sagittis id vitae risus. In pulvinar ligula et scelerisque vehicula. 51 | Pellentesque sem ex, pellentesque ac neque quis, vulputate sollicitudin mi. Morbi a urna sed nisl pretium lobortis a ac ex. Aenean vel lectus eget dolor auctor tempus. Sed quis aliquam ligula, et aliquet dui. Curabitur sodales convallis sem, eu aliquet quam sodales a. Praesent feugiat nec ligula ultricies vestibulum. Nam tristique vel metus ut pulvinar. Cras commodo ipsum dui, condimentum gravida felis sollicitudin id. Phasellus fringilla ligula lorem, vel viverra sapien posuere sit amet."; 52 | 53 | public static readonly object[] TestsByteData = 54 | { 55 | new object[] {TestByteData} 56 | }; 57 | 58 | public static byte[] TestByteData => LoremIpsum.ToBytes(); 59 | 60 | [Serializable] 61 | public struct TestSerializableStruct 62 | { 63 | public string String; 64 | public float Float; 65 | } 66 | 67 | public static readonly object[] TestGenericData = 68 | { 69 | new object[] {new Vector2(10, 20)}, 70 | new object[] 71 | { 72 | new TestSerializableStruct() 73 | { 74 | String = "String", 75 | Float = 33.33f 76 | } 77 | }, 78 | }; 79 | 80 | public static readonly Vector2 GenericDataVector = new Vector2(10, 20); 81 | 82 | public static readonly TestSerializableStruct GenericDataStruct = new TestSerializableStruct() 83 | { 84 | String = "String", 85 | Float = 33.33f 86 | }; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /Runtime/Compression/Compress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Threading.Tasks; 5 | 6 | namespace Dre0Dru.LocalStorage.Compression 7 | { 8 | public static class Compress 9 | { 10 | public static byte[] WriteGZip(byte[] data) 11 | { 12 | if (data == null) 13 | { 14 | throw new ArgumentNullException(nameof(data)); 15 | } 16 | 17 | using var dataStream = new MemoryStream(); 18 | using (var compressionStream = new GZipStream(dataStream, CompressionMode.Compress)) 19 | { 20 | compressionStream.Write(data, 0, data.Length); 21 | } 22 | 23 | return dataStream.ToArray(); 24 | } 25 | 26 | public static Task WriteGZipAsync(byte[] data) 27 | { 28 | if (data == null) 29 | { 30 | throw new ArgumentNullException(nameof(data)); 31 | } 32 | 33 | var dataStream = new MemoryStream(); 34 | var compressionStream = new GZipStream(dataStream, CompressionMode.Compress); 35 | 36 | return compressionStream.WriteAsync(data, 0, data.Length) 37 | .ContinueWith(task => 38 | { 39 | using (dataStream) 40 | { 41 | //Must manually dispose before returning 42 | //https://github.com/dotnet/runtime/issues/15371 43 | compressionStream.Dispose(); 44 | return dataStream.ToArray(); 45 | } 46 | }); 47 | } 48 | 49 | public static byte[] ReadGZip(byte[] data) 50 | { 51 | if (data == null) 52 | { 53 | throw new ArgumentNullException(nameof(data)); 54 | } 55 | 56 | using var dataStream = new MemoryStream(data); 57 | using var compressionStream = new GZipStream(dataStream, CompressionMode.Decompress); 58 | using var decompressedStream = new MemoryStream(); 59 | compressionStream.CopyTo(decompressedStream); 60 | 61 | return decompressedStream.ToArray(); 62 | } 63 | 64 | public static Task ReadGZipAsync(byte[] data) 65 | { 66 | if (data == null) 67 | { 68 | throw new ArgumentNullException(nameof(data)); 69 | } 70 | 71 | var dataStream = new MemoryStream(data); 72 | var compressionStream = new GZipStream(dataStream, CompressionMode.Decompress); 73 | var decompressedStream = new MemoryStream(); 74 | 75 | return compressionStream.CopyToAsync(decompressedStream) 76 | .ContinueWith(task => 77 | { 78 | using (dataStream) 79 | using (compressionStream) 80 | using (decompressedStream) 81 | { 82 | return decompressedStream.ToArray(); 83 | } 84 | }); 85 | } 86 | 87 | public static byte[] WriteDeflate(byte[] data) 88 | { 89 | if (data == null) 90 | { 91 | throw new ArgumentNullException(nameof(data)); 92 | } 93 | 94 | using var dataStream = new MemoryStream(); 95 | using (var compressionStream = new DeflateStream(dataStream, CompressionMode.Compress)) 96 | { 97 | compressionStream.Write(data, 0, data.Length); 98 | } 99 | 100 | return dataStream.ToArray(); 101 | } 102 | 103 | public static Task WriteDeflateAsync(byte[] data) 104 | { 105 | if (data == null) 106 | { 107 | throw new ArgumentNullException(nameof(data)); 108 | } 109 | 110 | var dataStream = new MemoryStream(); 111 | var compressionStream = new DeflateStream(dataStream, CompressionMode.Compress); 112 | 113 | return compressionStream.WriteAsync(data, 0, data.Length) 114 | .ContinueWith(task => 115 | { 116 | using (dataStream) 117 | { 118 | //Must manually dispose before returning 119 | //https://github.com/dotnet/runtime/issues/15371 120 | compressionStream.Dispose(); 121 | return dataStream.ToArray(); 122 | } 123 | }); 124 | } 125 | 126 | public static byte[] ReadDeflate(byte[] data) 127 | { 128 | if (data == null) 129 | { 130 | throw new ArgumentNullException(nameof(data)); 131 | } 132 | 133 | using var dataStream = new MemoryStream(data); 134 | using var compressionStream = new DeflateStream(dataStream, CompressionMode.Decompress); 135 | using var decompressedStream = new MemoryStream(); 136 | compressionStream.CopyTo(decompressedStream); 137 | 138 | return decompressedStream.ToArray(); 139 | } 140 | 141 | public static Task ReadDeflateAsync(byte[] data) 142 | { 143 | if (data == null) 144 | { 145 | throw new ArgumentNullException(nameof(data)); 146 | } 147 | 148 | var dataStream = new MemoryStream(data); 149 | var compressionStream = new DeflateStream(dataStream, CompressionMode.Decompress); 150 | var decompressedStream = new MemoryStream(); 151 | 152 | return compressionStream.CopyToAsync(decompressedStream) 153 | .ContinueWith(task => 154 | { 155 | using (dataStream) 156 | using (compressionStream) 157 | using (decompressedStream) 158 | { 159 | return decompressedStream.ToArray(); 160 | } 161 | }); 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![openupm](https://img.shields.io/npm/v/com.dre0dru.localstorage?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/com.dre0dru.localstorage/) 2 | # Description 3 | Configurable generic classes for managing local data saved on device. 4 | Unity 2020.1+ 5 | 6 | ## Features 7 | - Two generic classes with configuration options for data read/write and serialization/deserialization via file system or player prefs. 8 | - Simple file provider that just reads/writes data. 9 | - Data transformations that allow to preprocess data on read/write: 10 | - `IDataTransform` that uses `AES` encryption to encrypt/decrypt data. 11 | - `IDataTransform` that uses `GZip` or `Deflate` to compress/decompress data. 12 | - Json serialization provider that uses `JsonUtility` for serialization. 13 | - File storage saves data to `Application.persistentDataPath`. 14 | - Player prefs storage saves data to `PlayerPrefs`. Data location depends on device, refer to [Unity](https://docs.unity3d.com/ScriptReference/PlayerPrefs.html) documentation. 15 | - Easy to use and understand abstractions that allow to create custom serialization/deserialization and data transformation processes. 16 | - Async/sync API. 17 | - Optional [UniTask](https://github.com/Cysharp/UniTask) support for async API. 18 | # Installation 19 | This package can be installed as unity module directly from git url in two ways: 20 | - By adding following line in `Packages/manifest.json`: 21 | ``` 22 | "com.dre0dru.localstorage": "https://github.com/dre0dru/LocalStorage.git#upm", 23 | ``` 24 | - By using `Window/Package Manager/Add package from git URL...` in Unity: 25 | ``` 26 | https://github.com/dre0dru/LocalStorage.git#upm 27 | ``` 28 | - The package is also available on the [openupm registry](https://openupm.com/packages/com.dre0dru.localstorage/). You can install it via [openupm-cli](https://github.com/openupm/openupm-cli): 29 | ``` 30 | openupm add com.dre0dru.localstorage 31 | ``` 32 | ## Optional UniTask support 33 | [UniTask](https://github.com/Cysharp/UniTask) package can be installed to convert async API from using `Task` to `UniTask`. No further actions are required after installation. 34 | 35 | `UniTask` support can be disabled by using `DISABLE_UNITASK_SUPPORT` define. 36 | # Usage 37 | ## FileStorage usage 38 | ```c# 39 | //Serialization/deserialization implementation 40 | ISerializationProvider serializationProvider = new UnityJsonSerializationProvider(); 41 | 42 | //Path to save/load from (optional) 43 | string path = "dataFolder"; //Resulting path will be Application.persistentDataPath/dataFolder 44 | //File save/load implementation 45 | IFileProvider fileProvider = new FileProvider(path); 46 | 47 | IFileStorage storage = new FileStorage(serializationProvider, fileProvider); 48 | 49 | string fileName = "fileName.extension"; 50 | 51 | //Resulting path will be Application.persistentDataPath/dataFolder/fileName.extension 52 | string filePath = storage.GetFilePath(fileName); 53 | 54 | //Serializes data then saves file 55 | storage.Save(new Vector2(1.0f, 1.0f), fileName); 56 | 57 | //Async saving 58 | await storage.SaveAsync(new Vector2(1.0f, 1.0f), fileName); 59 | 60 | //Check if file exists 61 | bool exists = storage.FileExists(fileName); 62 | 63 | //Loads file then deserializes data 64 | Vector2 deserialized = storage.Load(fileName); 65 | 66 | //Async loading 67 | Vector2 deserialized = await storage.LoadAsync(fileName); 68 | 69 | //Deletes file if present 70 | storage.Delete(fileName); 71 | ``` 72 | ## PlayerPrefsStorage usage 73 | ```c# 74 | //Serialization/deserialization implementation 75 | ISerializationProvider serializationProvider = new UnityJsonSerializationProvider(); 76 | 77 | IPlayerPrefsStorage storage = new PlayerPrefsStorage(serializationProvider); 78 | 79 | string dataKey = "key"; 80 | 81 | //Serializes data using ISerializationProvider then puts it into PlayerPrefs under provided key 82 | storage.SetData(dataKey, new Vector2(1, 1)); 83 | 84 | //Async saving 85 | await storage.SetDataAsync(dataKey, new Vector2(1, 1)); 86 | 87 | //Loads data from PlayerPrefs by key then deserializes using ISerializationProvider 88 | Vector2 deserialized = storage.GetData(dataKey); 89 | 90 | //Async loading 91 | Vector2 deserialized = await storage.GetDataAsync(dataKey); 92 | ``` 93 | The rest of `IPlayerPrefsStorage` API mimics [Unity](https://docs.unity3d.com/ScriptReference/PlayerPrefs.html) `PlayerPrefs` API. 94 | 95 | **Warning!** `PlayerPrefsStorage` is not thread safe. 96 | ## Data transformation usage 97 | `IDataTransform` implementations can be used to preprocess data during serialization/deserialization process. 98 | Available data transformations are: 99 | - `AesEncryptionDataTransform` 100 | - `DeflateDataTransform` 101 | - `GZipDataTransform` 102 | ```c# 103 | //This is just an example, don't generate new key/IV every time 104 | public class ExampleEncryptionSettings : IEncryptionSettings 105 | { 106 | public byte[] Key { get; private set; } 107 | public byte[] InitializationVector { get; private set; } 108 | 109 | public ExampleEncryptionSettings() 110 | { 111 | //Create AES instance 112 | using var aes = Aes.Create(); 113 | //Save AES key somewhere 114 | Key = aes.Key; 115 | //Save AES IV somewhere 116 | InitializationVector = aes.IV; 117 | } 118 | } 119 | 120 | //Encryption settings for AES 121 | IEncryptionSettings encryptionSettings = new ExampleEncryptionSettings(); 122 | 123 | //Setup desired IDataTransform implementation 124 | IDataTransform encryptionDataTransform = new AesEncryptionDataTransform(encryptionSettings); 125 | 126 | //Base ISerializationProvider that will be used before/after data transform is applied 127 | ISerializationProvider baseSP = new UnityJsonSerializationProvider(); 128 | 129 | //Setup ISerializationProvider that will use target data transformation during serialization/deserialization process 130 | ISerializationProvider transformSerializationProvider = 131 | new DataTransformSerializationProvider(baseSP, encryptionDataTransform); 132 | 133 | //Use DataTransformSerializationProvider with FileStorage 134 | IFileProvider fp = new FileProvider(); 135 | IFileStorage fileStorage = new FileStorage(transformSerializationProvider, fp); 136 | 137 | //Or with PlayerPrefsStorage 138 | IPlayerPrefsStorage playerPrefsStorage = new PlayerPrefsStorage(transformSerializationProvider); 139 | ``` 140 | You can combine multiple `IDataTransform` to create chained data transformations: 141 | ```c# 142 | IEncryptionSettings encryptionSettings = new ExampleEncryptionSettings(); 143 | 144 | //Encryption data transform 145 | IDataTransform encryptionDataTransform = new AesEncryptionDataTransform(encryptionSettings); 146 | 147 | //Compression data transform 148 | IDataTransform compressionDataTransform = new GZipDataTransform(); 149 | 150 | //Create combined data transform 151 | //First it will encrypt data, then it will compress the result 152 | IDataTransform combinedDataTransform = new CombinedDataTransform(encryptionDataTransform, compressionDataTransform); 153 | 154 | //Pass it to DataTransformSerializationProvider implementation 155 | ISerializationProvider serializationProvider = 156 | new DataTransformSerializationProvider(new UnityJsonSerializationProvider(), 157 | combinedDataTransform); 158 | 159 | //Data transformation chains can be created indefinitely 160 | IDataTransform customDataTransform = new CustomDataTransform(); 161 | 162 | //This will result in applying CustomDataTransform data transformations first, 163 | //then applying data transformations specified by combinedDataTransform instance 164 | IDataTransform multipleCombinedDataTransform = 165 | new CombinedDataTransform(customDataTransform, combinedDataTransform); 166 | ``` 167 | ## Sync/async API 168 | All interfaces come in 3 variants: 169 | - Sync API 170 | - Async API 171 | - Both sync and async 172 | 173 | Implementations that depends on mentioned interfaces have same 3 variants, e.g. `FileStorage` for both sync and async API, `FileStorageSync` for sync API only and `FileStorageAsync` for async API only. 174 | 175 | # License 176 | The software released under the terms of the [MIT license](./LICENSE.md). --------------------------------------------------------------------------------