mirror of
https://github.com/RaindropViewer/RaindropViewer.git
synced 2026-08-14 00:57:55 +00:00
Changed all Bitmap -> Texture2D.
Make demo scene to browse tga files in openmetaverse_data added BetterStreamingAssets to read from SA some refactoring.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class AndroidCopyStreamingAssetsToPersistentDataPath : MonoBehaviour
|
||||
{
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
public void init()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c516553780c52c448827236a5a5ae788
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+1
-3
@@ -1,7 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91b0b4c77fdd1d24aa1af646e9d29960
|
||||
timeCreated: 1536994288
|
||||
licenseType: Store
|
||||
guid: 9df6cd2f9439dd04fb0d7a5aeb12e189
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
+2
-3
@@ -1,7 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aebd7ef925dd4fe4690c3238e879f914
|
||||
timeCreated: 1537606270
|
||||
licenseType: Store
|
||||
guid: 4c30698f6ca5f244eb5984ca1a9eae57
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
@@ -0,0 +1,652 @@
|
||||
// Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Better;
|
||||
using Better.StreamingAssets;
|
||||
using Better.StreamingAssets.ZipArchive;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using BetterStreamingAssetsImp = BetterStreamingAssets.EditorImpl;
|
||||
#elif UNITY_ANDROID
|
||||
using BetterStreamingAssetsImp = BetterStreamingAssets.ApkImpl;
|
||||
#else
|
||||
using BetterStreamingAssetsImp = BetterStreamingAssets.LooseFilesImpl;
|
||||
#endif
|
||||
|
||||
public static class BetterStreamingAssets
|
||||
{
|
||||
internal struct ReadInfo
|
||||
{
|
||||
public string readPath;
|
||||
public long size;
|
||||
public long offset;
|
||||
public uint crc32;
|
||||
}
|
||||
|
||||
public static string Root
|
||||
{
|
||||
get { return BetterStreamingAssetsImp.s_root; }
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
BetterStreamingAssetsImp.Initialize(Application.dataPath, Application.streamingAssetsPath);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static void InitializeWithExternalApk(string apkPath)
|
||||
{
|
||||
BetterStreamingAssetsImp.ApkMode = true;
|
||||
BetterStreamingAssetsImp.Initialize(apkPath, "jar:file://" + apkPath + "!/assets/");
|
||||
}
|
||||
|
||||
public static void InitializeWithExternalDirectories(string dataPath, string streamingAssetsPath)
|
||||
{
|
||||
BetterStreamingAssetsImp.ApkMode = false;
|
||||
BetterStreamingAssetsImp.Initialize(dataPath, streamingAssetsPath);
|
||||
}
|
||||
#endif
|
||||
|
||||
public static bool FileExists(string path)
|
||||
{
|
||||
ReadInfo info;
|
||||
return BetterStreamingAssetsImp.TryGetInfo(path, out info);
|
||||
}
|
||||
|
||||
public static bool DirectoryExists(string path)
|
||||
{
|
||||
return BetterStreamingAssetsImp.DirectoryExists(path);
|
||||
}
|
||||
|
||||
public static AssetBundleCreateRequest LoadAssetBundleAsync(string path, uint crc = 0)
|
||||
{
|
||||
var info = GetInfoOrThrow(path);
|
||||
return AssetBundle.LoadFromFileAsync(info.readPath, crc, (ulong)info.offset);
|
||||
}
|
||||
|
||||
public static AssetBundle LoadAssetBundle(string path, uint crc = 0)
|
||||
{
|
||||
var info = GetInfoOrThrow(path);
|
||||
return AssetBundle.LoadFromFile(info.readPath, crc, (ulong)info.offset);
|
||||
}
|
||||
|
||||
public static System.IO.Stream OpenRead(string path)
|
||||
{
|
||||
if ( path == null )
|
||||
throw new ArgumentNullException("path");
|
||||
if ( path.Length == 0 )
|
||||
throw new ArgumentException("Empty path", "path");
|
||||
|
||||
return BetterStreamingAssetsImp.OpenRead(path);
|
||||
}
|
||||
|
||||
public static System.IO.StreamReader OpenText(string path)
|
||||
{
|
||||
Stream str = OpenRead(path);
|
||||
try
|
||||
{
|
||||
return new StreamReader(str);
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
if (str != null)
|
||||
str.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReadAllText(string path)
|
||||
{
|
||||
using ( var sr = OpenText(path) )
|
||||
{
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public static string[] ReadAllLines(string path)
|
||||
{
|
||||
string line;
|
||||
var lines = new List<string>();
|
||||
|
||||
using ( var sr = OpenText(path) )
|
||||
{
|
||||
while ( ( line = sr.ReadLine() ) != null )
|
||||
{
|
||||
lines.Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
return lines.ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ReadAllBytes(string path)
|
||||
{
|
||||
if ( path == null )
|
||||
throw new ArgumentNullException("path");
|
||||
if ( path.Length == 0 )
|
||||
throw new ArgumentException("Empty path", "path");
|
||||
|
||||
return BetterStreamingAssetsImp.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
return BetterStreamingAssetsImp.GetFiles(path, searchPattern, searchOption);
|
||||
}
|
||||
|
||||
public static string[] GetFiles(string path)
|
||||
{
|
||||
return GetFiles(path, null);
|
||||
}
|
||||
|
||||
public static string[] GetFiles(string path, string searchPattern)
|
||||
{
|
||||
return GetFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
private static ReadInfo GetInfoOrThrow(string path)
|
||||
{
|
||||
ReadInfo result;
|
||||
if ( !BetterStreamingAssetsImp.TryGetInfo(path, out result) )
|
||||
ThrowFileNotFound(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void ThrowFileNotFound(string path)
|
||||
{
|
||||
throw new FileNotFoundException("File not found", path);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal static class EditorImpl
|
||||
{
|
||||
public static bool ApkMode = false;
|
||||
|
||||
public static string s_root
|
||||
{
|
||||
get { return ApkMode ? ApkImpl.s_root : LooseFilesImpl.s_root; }
|
||||
}
|
||||
|
||||
internal static void Initialize(string dataPath, string streamingAssetsPath)
|
||||
{
|
||||
if ( ApkMode )
|
||||
{
|
||||
ApkImpl.Initialize(dataPath, streamingAssetsPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
LooseFilesImpl.Initialize(dataPath, streamingAssetsPath);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryGetInfo(string path, out ReadInfo info)
|
||||
{
|
||||
if ( ApkMode )
|
||||
return ApkImpl.TryGetInfo(path, out info);
|
||||
else
|
||||
return LooseFilesImpl.TryGetInfo(path, out info);
|
||||
}
|
||||
|
||||
internal static bool DirectoryExists(string path)
|
||||
{
|
||||
if ( ApkMode )
|
||||
return ApkImpl.DirectoryExists(path);
|
||||
else
|
||||
return LooseFilesImpl.DirectoryExists(path);
|
||||
}
|
||||
|
||||
internal static Stream OpenRead(string path)
|
||||
{
|
||||
if ( ApkMode )
|
||||
return ApkImpl.OpenRead(path);
|
||||
else
|
||||
return LooseFilesImpl.OpenRead(path);
|
||||
}
|
||||
|
||||
internal static byte[] ReadAllBytes(string path)
|
||||
{
|
||||
if ( ApkMode )
|
||||
return ApkImpl.ReadAllBytes(path);
|
||||
else
|
||||
return LooseFilesImpl.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
internal static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
if ( ApkMode )
|
||||
return ApkImpl.GetFiles(path, searchPattern, searchOption);
|
||||
else
|
||||
return LooseFilesImpl.GetFiles(path, searchPattern, searchOption);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR || !UNITY_ANDROID
|
||||
internal static class LooseFilesImpl
|
||||
{
|
||||
public static string s_root;
|
||||
private static string[] s_emptyArray = new string[0];
|
||||
|
||||
public static void Initialize(string dataPath, string streamingAssetsPath)
|
||||
{
|
||||
s_root = Path.GetFullPath(streamingAssetsPath).Replace('\\', '/').TrimEnd('/');
|
||||
}
|
||||
|
||||
public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
if (!Directory.Exists(s_root))
|
||||
return s_emptyArray;
|
||||
|
||||
// this will throw if something is fishy
|
||||
path = PathUtil.NormalizeRelativePath(path, forceTrailingSlash : true);
|
||||
|
||||
Debug.Assert(s_root.Last() != '\\' && s_root.Last() != '/' && path.StartsWith("/"));
|
||||
|
||||
var files = Directory.GetFiles(s_root + path, searchPattern ?? "*", searchOption);
|
||||
|
||||
for ( int i = 0; i < files.Length; ++i )
|
||||
{
|
||||
Debug.Assert(files[i].StartsWith(s_root));
|
||||
files[i] = files[i].Substring(s_root.Length + 1).Replace('\\', '/');
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// purge meta files
|
||||
{
|
||||
int j = 0;
|
||||
for ( int i = 0; i < files.Length; ++i )
|
||||
{
|
||||
if ( !files[i].EndsWith(".meta") )
|
||||
{
|
||||
files[j++] = files[i];
|
||||
}
|
||||
}
|
||||
Array.Resize(ref files, j);
|
||||
}
|
||||
|
||||
#endif
|
||||
return files;
|
||||
}
|
||||
|
||||
public static bool TryGetInfo(string path, out ReadInfo info)
|
||||
{
|
||||
path = PathUtil.NormalizeRelativePath(path);
|
||||
|
||||
info = new ReadInfo();
|
||||
|
||||
var fullPath = s_root + path;
|
||||
if ( !File.Exists(fullPath) )
|
||||
return false;
|
||||
|
||||
info.readPath = fullPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DirectoryExists(string path)
|
||||
{
|
||||
var normalized = PathUtil.NormalizeRelativePath(path);
|
||||
return Directory.Exists(s_root + normalized);
|
||||
}
|
||||
|
||||
public static byte[] ReadAllBytes(string path)
|
||||
{
|
||||
ReadInfo info;
|
||||
|
||||
if ( !TryGetInfo(path, out info) )
|
||||
ThrowFileNotFound(path);
|
||||
|
||||
return File.ReadAllBytes(info.readPath);
|
||||
}
|
||||
|
||||
public static System.IO.Stream OpenRead(string path)
|
||||
{
|
||||
ReadInfo info;
|
||||
if ( !TryGetInfo(path, out info) )
|
||||
ThrowFileNotFound(path);
|
||||
|
||||
Stream fileStream = File.OpenRead(info.readPath);
|
||||
try
|
||||
{
|
||||
return new SubReadOnlyStream(fileStream, leaveOpen: false);
|
||||
}
|
||||
catch ( System.Exception )
|
||||
{
|
||||
fileStream.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR || UNITY_ANDROID
|
||||
internal static class ApkImpl
|
||||
{
|
||||
private static string[] s_paths;
|
||||
private static PartInfo[] s_streamingAssets;
|
||||
public static string s_root;
|
||||
|
||||
private struct PartInfo
|
||||
{
|
||||
public long size;
|
||||
public long offset;
|
||||
public uint crc32;
|
||||
}
|
||||
|
||||
public static void Initialize(string dataPath, string streamingAssetsPath)
|
||||
{
|
||||
s_root = dataPath;
|
||||
|
||||
List<string> paths = new List<string>();
|
||||
List<PartInfo> parts = new List<PartInfo>();
|
||||
|
||||
GetStreamingAssetsInfoFromJar(s_root, paths, parts);
|
||||
|
||||
if (paths.Count == 0 && !Application.isEditor && Path.GetFileName(dataPath) != "base.apk")
|
||||
{
|
||||
// maybe split?
|
||||
var newDataPath = Path.GetDirectoryName(dataPath) + "/base.apk";
|
||||
if (File.Exists(newDataPath))
|
||||
{
|
||||
s_root = newDataPath;
|
||||
GetStreamingAssetsInfoFromJar(newDataPath, paths, parts);
|
||||
}
|
||||
}
|
||||
|
||||
s_paths = paths.ToArray();
|
||||
s_streamingAssets = parts.ToArray();
|
||||
}
|
||||
|
||||
public static bool TryGetInfo(string path, out ReadInfo info)
|
||||
{
|
||||
path = PathUtil.NormalizeRelativePath(path);
|
||||
info = new ReadInfo();
|
||||
|
||||
var index = Array.BinarySearch(s_paths, path, StringComparer.OrdinalIgnoreCase);
|
||||
if ( index < 0 )
|
||||
return false;
|
||||
|
||||
var dataInfo = s_streamingAssets[index];
|
||||
info.crc32 = dataInfo.crc32;
|
||||
info.offset = dataInfo.offset;
|
||||
info.size = dataInfo.size;
|
||||
info.readPath = s_root;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DirectoryExists(string path)
|
||||
{
|
||||
var normalized = PathUtil.NormalizeRelativePath(path, forceTrailingSlash : true);
|
||||
var dirIndex = GetDirectoryIndex(normalized);
|
||||
return dirIndex >= 0 && dirIndex < s_paths.Length;
|
||||
}
|
||||
|
||||
public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
if ( path == null )
|
||||
throw new ArgumentNullException("path");
|
||||
|
||||
var actualDirPath = PathUtil.NormalizeRelativePath(path, forceTrailingSlash : true);
|
||||
|
||||
// find first file there
|
||||
var index = GetDirectoryIndex(actualDirPath);
|
||||
if ( index < 0 )
|
||||
throw new IOException();
|
||||
if ( index == s_paths.Length )
|
||||
throw new DirectoryNotFoundException();
|
||||
|
||||
Predicate<string> filter;
|
||||
if ( string.IsNullOrEmpty(searchPattern) || searchPattern == "*" )
|
||||
{
|
||||
filter = null;
|
||||
}
|
||||
else if ( searchPattern.IndexOf('*') >= 0 || searchPattern.IndexOf('?') >= 0 )
|
||||
{
|
||||
var regex = PathUtil.WildcardToRegex(searchPattern);
|
||||
filter = (x) => regex.IsMatch(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
filter = (x) => string.Compare(x, searchPattern, true) == 0;
|
||||
}
|
||||
|
||||
List<string> results = new List<string>();
|
||||
string fixedPath = null;
|
||||
|
||||
for ( int i = index; i < s_paths.Length; ++i )
|
||||
{
|
||||
var filePath = s_paths[i];
|
||||
|
||||
if ( !filePath.StartsWith(actualDirPath) )
|
||||
break;
|
||||
|
||||
string fileName;
|
||||
|
||||
var dirSeparatorIndex = filePath.LastIndexOf('/', filePath.Length - 1, filePath.Length - actualDirPath.Length);
|
||||
if ( dirSeparatorIndex >= 0 )
|
||||
{
|
||||
if ( searchOption == SearchOption.TopDirectoryOnly )
|
||||
continue;
|
||||
|
||||
fileName = filePath.Substring(dirSeparatorIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileName = filePath.Substring(actualDirPath.Length);
|
||||
}
|
||||
|
||||
// now do a match
|
||||
if ( filter == null || filter(fileName) )
|
||||
{
|
||||
var normalizedPart = filePath.Substring(actualDirPath.Length);
|
||||
|
||||
if ( fixedPath == null )
|
||||
{
|
||||
fixedPath = PathUtil.FixTrailingDirectorySeparators(path);
|
||||
if ( fixedPath == "/" )
|
||||
fixedPath = string.Empty;
|
||||
}
|
||||
|
||||
var result = PathUtil.CombineSlash(fixedPath, normalizedPart);
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ReadAllBytes(string path)
|
||||
{
|
||||
ReadInfo info;
|
||||
if ( !TryGetInfo(path, out info) )
|
||||
ThrowFileNotFound(path);
|
||||
|
||||
byte[] buffer;
|
||||
using ( var fileStream = File.OpenRead(info.readPath) )
|
||||
{
|
||||
if ( info.offset != 0 )
|
||||
{
|
||||
if ( fileStream.Seek(info.offset, SeekOrigin.Begin) != info.offset )
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
if ( info.size > (long)int.MaxValue )
|
||||
throw new IOException();
|
||||
|
||||
int count = (int)info.size;
|
||||
int offset = 0;
|
||||
|
||||
buffer = new byte[count];
|
||||
while ( count > 0 )
|
||||
{
|
||||
int num = fileStream.Read(buffer, offset, count);
|
||||
if ( num == 0 )
|
||||
throw new EndOfStreamException();
|
||||
offset += num;
|
||||
count -= num;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static System.IO.Stream OpenRead(string path)
|
||||
{
|
||||
ReadInfo info;
|
||||
if ( !TryGetInfo(path, out info) )
|
||||
ThrowFileNotFound(path);
|
||||
|
||||
Stream fileStream = File.OpenRead(info.readPath);
|
||||
try
|
||||
{
|
||||
return new SubReadOnlyStream(fileStream, info.offset, info.size, leaveOpen : false);
|
||||
}
|
||||
catch ( System.Exception )
|
||||
{
|
||||
fileStream.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetDirectoryIndex(string path)
|
||||
{
|
||||
Debug.Assert(s_paths != null);
|
||||
|
||||
// find first file there
|
||||
var index = Array.BinarySearch(s_paths, path, StringComparer.OrdinalIgnoreCase);
|
||||
if ( index >= 0 )
|
||||
return ~index;
|
||||
|
||||
// if the end, no such directory exists
|
||||
index = ~index;
|
||||
if ( index == s_paths.Length )
|
||||
return index;
|
||||
|
||||
for ( int i = index; i < s_paths.Length && s_paths[i].StartsWith(path); ++i )
|
||||
{
|
||||
// because otherwise there would be a match
|
||||
Debug.Assert(s_paths[i].Length > path.Length);
|
||||
|
||||
if ( path[path.Length - 1] == '/' )
|
||||
return i;
|
||||
|
||||
if ( s_paths[i][path.Length] == '/' )
|
||||
return i;
|
||||
}
|
||||
|
||||
return s_paths.Length;
|
||||
}
|
||||
|
||||
private static void GetStreamingAssetsInfoFromJar(string apkPath, List<string> paths, List<PartInfo> parts)
|
||||
{
|
||||
using ( var stream = File.OpenRead(apkPath) )
|
||||
using ( var reader = new BinaryReader(stream) )
|
||||
{
|
||||
if ( !stream.CanRead )
|
||||
throw new ArgumentException();
|
||||
if ( !stream.CanSeek )
|
||||
throw new ArgumentException();
|
||||
|
||||
long expectedNumberOfEntries;
|
||||
long centralDirectoryStart;
|
||||
ZipArchiveUtils.ReadEndOfCentralDirectory(stream, reader, out expectedNumberOfEntries, out centralDirectoryStart);
|
||||
|
||||
try
|
||||
{
|
||||
stream.Seek(centralDirectoryStart, SeekOrigin.Begin);
|
||||
|
||||
long numberOfEntries = 0;
|
||||
|
||||
ZipCentralDirectoryFileHeader header;
|
||||
|
||||
const int prefixLength = 7;
|
||||
const string prefix = "assets/";
|
||||
const string assetsPrefix = "assets/bin/";
|
||||
Debug.Assert(prefixLength == prefix.Length);
|
||||
|
||||
while ( ZipCentralDirectoryFileHeader.TryReadBlock(reader, out header) )
|
||||
{
|
||||
if ( header.CompressedSize != header.UncompressedSize )
|
||||
{
|
||||
#if UNITY_ASSERTIONS
|
||||
var fileName = Encoding.UTF8.GetString(header.Filename);
|
||||
if (fileName.StartsWith(prefix) && !fileName.StartsWith(assetsPrefix))
|
||||
{
|
||||
Debug.LogAssertionFormat("BetterStreamingAssets: file {0} seems to be a Streaming Asset, but is compressed. If this is a App Bundle build, see README for a possible workaround.", fileName);
|
||||
}
|
||||
#endif
|
||||
// we only want uncompressed files
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileName = Encoding.UTF8.GetString(header.Filename);
|
||||
|
||||
if (fileName.EndsWith("/"))
|
||||
{
|
||||
// there's some strangeness when it comes to OBB: directories are listed as files
|
||||
// simply ignoring them should be enough
|
||||
Debug.Assert(header.UncompressedSize == 0);
|
||||
}
|
||||
else if ( fileName.StartsWith(prefix) )
|
||||
{
|
||||
// ignore normal assets...
|
||||
if ( fileName.StartsWith(assetsPrefix) )
|
||||
{
|
||||
// Note: if you put bin directory in your StreamingAssets you will get false negative here
|
||||
}
|
||||
else
|
||||
{
|
||||
var relativePath = fileName.Substring(prefixLength - 1);
|
||||
var entry = new PartInfo()
|
||||
{
|
||||
crc32 = header.Crc32,
|
||||
offset = header.RelativeOffsetOfLocalHeader, // this offset will need fixing later on
|
||||
size = header.UncompressedSize
|
||||
};
|
||||
|
||||
var index = paths.BinarySearch(relativePath, StringComparer.OrdinalIgnoreCase);
|
||||
if ( index >= 0 )
|
||||
throw new System.InvalidOperationException("Paths duplicate! " + fileName);
|
||||
|
||||
paths.Insert(~index, relativePath);
|
||||
parts.Insert(~index, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
numberOfEntries++;
|
||||
}
|
||||
|
||||
if ( numberOfEntries != expectedNumberOfEntries )
|
||||
throw new ZipArchiveException("Number of entries does not match");
|
||||
|
||||
}
|
||||
catch ( EndOfStreamException ex )
|
||||
{
|
||||
throw new ZipArchiveException("CentralDirectoryInvalid", ex);
|
||||
}
|
||||
|
||||
// now fix offsets
|
||||
for ( int i = 0; i < parts.Count; ++i )
|
||||
{
|
||||
var entry = parts[i];
|
||||
stream.Seek(entry.offset, SeekOrigin.Begin);
|
||||
|
||||
if ( !ZipLocalFileHeader.TrySkipBlock(reader) )
|
||||
throw new ZipArchiveException("Local file header corrupt");
|
||||
|
||||
entry.offset = stream.Position;
|
||||
|
||||
parts[i] = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12bc032fc47a6c34fb90f0f4a48f4441
|
||||
timeCreated: 1506516969
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,163 @@
|
||||
// Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Better.StreamingAssets
|
||||
{
|
||||
public static partial class PathUtil
|
||||
{
|
||||
private enum NormalizeState
|
||||
{
|
||||
PrevSlash,
|
||||
PrevDot,
|
||||
PrevDoubleDot,
|
||||
NothingSpecial,
|
||||
}
|
||||
|
||||
public static bool IsDirectorySeparator(char c)
|
||||
{
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
public static string FixTrailingDirectorySeparators(string path)
|
||||
{
|
||||
if ( path.Length >= 2 )
|
||||
{
|
||||
var lastChar = path[path.Length - 1];
|
||||
var prevChar = path[path.Length - 2];
|
||||
if ( PathUtil.IsDirectorySeparator(lastChar) && PathUtil.IsDirectorySeparator(prevChar) )
|
||||
{
|
||||
return path.TrimEnd('\\', '/') + lastChar;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string CombineSlash(string a, string b)
|
||||
{
|
||||
if ( a == null )
|
||||
throw new ArgumentNullException("a");
|
||||
if ( b == null )
|
||||
throw new ArgumentNullException("b");
|
||||
|
||||
if ( string.IsNullOrEmpty(b) )
|
||||
return a;
|
||||
if ( string.IsNullOrEmpty(a) )
|
||||
return b;
|
||||
|
||||
if (b[0] == '/')
|
||||
return b;
|
||||
|
||||
if ( IsDirectorySeparator(a[a.Length -1]) )
|
||||
return a + b;
|
||||
else
|
||||
return a + '/' + b;
|
||||
}
|
||||
|
||||
public static string NormalizeRelativePath(string relative, bool forceTrailingSlash = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(relative))
|
||||
throw new System.ArgumentException("Empty or null", "relative");
|
||||
|
||||
StringBuilder output = new StringBuilder(relative.Length);
|
||||
|
||||
NormalizeState state = NormalizeState.PrevSlash;
|
||||
output.Append('/');
|
||||
|
||||
int startIndex = 0;
|
||||
int lastIndexPlus1 = relative.Length;
|
||||
|
||||
if ( relative[0] == '"' && relative.Length > 2 && relative[relative.Length - 1] == '"')
|
||||
{
|
||||
startIndex++;
|
||||
lastIndexPlus1--;
|
||||
}
|
||||
|
||||
for ( int i = startIndex; i <= lastIndexPlus1; ++i )
|
||||
{
|
||||
if (i == lastIndexPlus1 || relative[i] == Path.DirectorySeparatorChar || relative[i] == Path.AltDirectorySeparatorChar)
|
||||
{
|
||||
if ( state == NormalizeState.PrevSlash || state == NormalizeState.PrevDot )
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if ( state == NormalizeState.PrevDoubleDot )
|
||||
{
|
||||
if ( output.Length == 1 )
|
||||
throw new System.IO.IOException("AAA");
|
||||
|
||||
// on level up!
|
||||
int j;
|
||||
for ( j = output.Length - 2; j >= 0 && output[j] != '/'; --j)
|
||||
{
|
||||
}
|
||||
|
||||
output.Remove(j + 1, output.Length - j - 1);
|
||||
}
|
||||
else if ( i < lastIndexPlus1 || forceTrailingSlash )
|
||||
{
|
||||
output.Append('/');
|
||||
}
|
||||
|
||||
state = NormalizeState.PrevSlash;
|
||||
}
|
||||
else if ( relative[i] == '.' )
|
||||
{
|
||||
if ( state == NormalizeState.PrevSlash )
|
||||
{
|
||||
state = NormalizeState.PrevDot;
|
||||
}
|
||||
else if ( state == NormalizeState.PrevDot )
|
||||
{
|
||||
state = NormalizeState.PrevDoubleDot;
|
||||
}
|
||||
else if ( state == NormalizeState.PrevDoubleDot )
|
||||
{
|
||||
state = NormalizeState.NothingSpecial;
|
||||
output.Append("...");
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Append('.');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( state == NormalizeState.PrevDot )
|
||||
{
|
||||
output.Append('.');
|
||||
}
|
||||
else if ( state == NormalizeState.PrevDoubleDot )
|
||||
{
|
||||
output.Append("..");
|
||||
}
|
||||
|
||||
if (!IsValidCharacter(relative[i]))
|
||||
throw new System.IO.IOException("Invalid characters");
|
||||
|
||||
output.Append(relative[i]);
|
||||
state = NormalizeState.NothingSpecial;
|
||||
}
|
||||
}
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
public static bool IsValidCharacter(char c)
|
||||
{
|
||||
if (c == '\"' || c == '<' || c == '>' || c == '|' || c < 32 || c == ':' || c == '*' || c == '?')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Regex WildcardToRegex(string pattern)
|
||||
{
|
||||
return new Regex("^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$", RegexOptions.IgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34f6b4de9962c114299b3b9bfdd590d5
|
||||
timeCreated: 1506546248
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,168 @@
|
||||
// Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Better.StreamingAssets
|
||||
{
|
||||
internal class SubReadOnlyStream: Stream
|
||||
{
|
||||
private readonly long m_offset;
|
||||
private readonly bool m_leaveOpen;
|
||||
|
||||
private long? m_length;
|
||||
private Stream m_actualStream;
|
||||
private long m_position;
|
||||
|
||||
public SubReadOnlyStream(Stream actualStream, bool leaveOpen = false)
|
||||
{
|
||||
if (actualStream == null)
|
||||
throw new ArgumentNullException("superStream");
|
||||
|
||||
m_actualStream = actualStream;
|
||||
m_leaveOpen = leaveOpen;
|
||||
}
|
||||
|
||||
public SubReadOnlyStream(Stream actualStream, long offset, long length, bool leaveOpen = false)
|
||||
: this(actualStream, leaveOpen)
|
||||
{
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException("offset");
|
||||
|
||||
if (length < 0)
|
||||
throw new ArgumentOutOfRangeException("length");
|
||||
|
||||
Debug.Assert(offset <= actualStream.Length);
|
||||
Debug.Assert(actualStream.Length >= length);
|
||||
Debug.Assert(offset + length <= actualStream.Length);
|
||||
|
||||
m_offset = offset;
|
||||
m_position = offset;
|
||||
m_length = length;
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (!m_length.HasValue)
|
||||
m_length = m_actualStream.Length - m_offset;
|
||||
|
||||
return m_length.Value; ;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return m_position - m_offset;
|
||||
}
|
||||
set
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return m_actualStream.CanRead; } }
|
||||
|
||||
public override bool CanSeek { get { return m_actualStream.CanSeek; } }
|
||||
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
ThrowIfCantRead();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if ( m_actualStream.Position != m_position )
|
||||
m_actualStream.Seek(m_position, SeekOrigin.Begin);
|
||||
|
||||
if ( m_length.HasValue )
|
||||
{
|
||||
var endPosition = m_offset + m_length.Value;
|
||||
if (m_position + count > endPosition)
|
||||
{
|
||||
count = (int)(endPosition - m_position);
|
||||
}
|
||||
}
|
||||
|
||||
int bytesRead = m_actualStream.Read(buffer, offset, count);
|
||||
m_position += bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if ( origin == SeekOrigin.Begin )
|
||||
{
|
||||
m_position = m_actualStream.Seek(m_offset + offset, SeekOrigin.Begin);
|
||||
}
|
||||
else if ( origin == SeekOrigin.End )
|
||||
{
|
||||
m_position = m_actualStream.Seek(m_offset + Length + offset, SeekOrigin.End);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_position = m_actualStream.Seek(offset, SeekOrigin.Current);
|
||||
}
|
||||
return m_position;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
// Close the stream for reading. Note that this does NOT close the superStream (since
|
||||
// the substream is just 'a chunk' of the super-stream
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if ( disposing )
|
||||
{
|
||||
if (m_actualStream != null)
|
||||
{
|
||||
if (!m_leaveOpen)
|
||||
m_actualStream.Dispose();
|
||||
|
||||
m_actualStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (m_actualStream == null)
|
||||
throw new ObjectDisposedException(GetType().ToString(), "");
|
||||
}
|
||||
|
||||
private void ThrowIfCantRead()
|
||||
{
|
||||
if (!CanRead)
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23985058976064b4aa47f955e25b32fb
|
||||
timeCreated: 1506207467
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,510 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 8
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_IndirectSpecularColor: {r: 0.44657892, g: 0.4964128, b: 0.5748171, a: 1}
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 11
|
||||
m_GIWorkflowMode: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_TemporalCoherenceThreshold: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 1
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 9
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_TextureWidth: 1024
|
||||
m_TextureHeight: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_FinalGather: 0
|
||||
m_FinalGatherFiltering: 1
|
||||
m_FinalGatherRayCount: 256
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 0
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 500
|
||||
m_PVRBounces: 2
|
||||
m_PVRFiltering: 0
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 5
|
||||
m_PVRFilteringGaussRadiusAO: 2
|
||||
m_PVRFilteringAtrousColorSigma: 1
|
||||
m_PVRFilteringAtrousNormalSigma: 1
|
||||
m_PVRFilteringAtrousPositionSigma: 1
|
||||
m_LightingDataAsset: {fileID: 0}
|
||||
m_UseShadowmask: 1
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 2
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
accuratePlacement: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &70076753
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
serializedVersion: 5
|
||||
m_Component:
|
||||
- component: {fileID: 70076757}
|
||||
- component: {fileID: 70076756}
|
||||
- component: {fileID: 70076755}
|
||||
- component: {fileID: 70076754}
|
||||
m_Layer: 5
|
||||
m_Name: Canvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &70076754
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 70076753}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &70076755
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 70076753}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
--- !u!223 &70076756
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 70076753}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!224 &70076757
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 70076753}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_Children:
|
||||
- {fileID: 1916147957}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 2
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!1 &329145529
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
serializedVersion: 5
|
||||
m_Component:
|
||||
- component: {fileID: 329145535}
|
||||
- component: {fileID: 329145534}
|
||||
- component: {fileID: 329145533}
|
||||
- component: {fileID: 329145532}
|
||||
- component: {fileID: 329145531}
|
||||
- component: {fileID: 329145530}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &329145530
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 329145529}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e254ae11057fcdf4a894dcf319700de7, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
InProgressText: {fileID: 1916147958}
|
||||
EditorApkPath: BetterStreamingAssetsTest.apk
|
||||
RepetitionCount: 10
|
||||
LogToFile: 0
|
||||
--- !u!81 &329145531
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 329145529}
|
||||
m_Enabled: 1
|
||||
--- !u!124 &329145532
|
||||
Behaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 329145529}
|
||||
m_Enabled: 1
|
||||
--- !u!92 &329145533
|
||||
Behaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 329145529}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &329145534
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 329145529}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 2
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
m_StereoMirrorMode: 0
|
||||
--- !u!4 &329145535
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 329145529}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1496189342
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
serializedVersion: 5
|
||||
m_Component:
|
||||
- component: {fileID: 1496189345}
|
||||
- component: {fileID: 1496189344}
|
||||
- component: {fileID: 1496189343}
|
||||
m_Layer: 0
|
||||
m_Name: EventSystem
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1496189343
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 1496189342}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_HorizontalAxis: Horizontal
|
||||
m_VerticalAxis: Vertical
|
||||
m_SubmitButton: Submit
|
||||
m_CancelButton: Cancel
|
||||
m_InputActionsPerSecond: 10
|
||||
m_RepeatDelay: 0.5
|
||||
m_ForceModuleActive: 0
|
||||
--- !u!114 &1496189344
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 1496189342}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_FirstSelected: {fileID: 0}
|
||||
m_sendNavigationEvents: 1
|
||||
m_DragThreshold: 5
|
||||
--- !u!4 &1496189345
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 1496189342}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 3
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1570752177
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
serializedVersion: 5
|
||||
m_Component:
|
||||
- component: {fileID: 1570752179}
|
||||
- component: {fileID: 1570752178}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &1570752178
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 1570752177}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 8
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_CookieSize: 10
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_Lightmapping: 4
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
--- !u!4 &1570752179
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 1570752177}
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!1 &1916147956
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
serializedVersion: 5
|
||||
m_Component:
|
||||
- component: {fileID: 1916147957}
|
||||
- component: {fileID: 1916147959}
|
||||
- component: {fileID: 1916147958}
|
||||
m_Layer: 5
|
||||
m_Name: Text
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1916147957
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 1916147956}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 70076757}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1916147958
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 1916147956}
|
||||
m_Enabled: 0
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
|
||||
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
|
||||
m_FontData:
|
||||
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_FontSize: 30
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 10
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 4
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: Testing in progress...
|
||||
--- !u!222 &1916147959
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 1916147956}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8645273165cb7f54290d6eaa1e10ab37
|
||||
timeCreated: 1461878212
|
||||
guid: 2bef88fd675ce3f4fa61ff5f18aa8242
|
||||
timeCreated: 1506210412
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,515 @@
|
||||
// Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using UnityEngine.Profiling;
|
||||
using System.Collections;
|
||||
using Stopwatch = System.Diagnostics.Stopwatch;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Better.StreamingAssets
|
||||
{
|
||||
public class BSA_TestSceneGUI : MonoBehaviour
|
||||
{
|
||||
public UnityEngine.UI.Text InProgressText;
|
||||
public string EditorApkPath = "BetterStreamingAssetsTest.apk";
|
||||
public int RepetitionCount = 10;
|
||||
public bool LogToFile = false;
|
||||
|
||||
private class CoroutineHost : MonoBehaviour { }
|
||||
|
||||
private class TestInfo
|
||||
{
|
||||
public ReadMode readMode;
|
||||
public TestType testType;
|
||||
public string path;
|
||||
public int attempts;
|
||||
public Exception error;
|
||||
public TimeSpan duration;
|
||||
public long bytesRead;
|
||||
public long memoryPeak;
|
||||
public long maxMemoryPeak;
|
||||
}
|
||||
|
||||
|
||||
private delegate void TestResultDelegate(TimeSpan avgDuration, long avgBytesRead, long avgMemoryPeak, long maxMemoryPeak, string[] assetNames);
|
||||
|
||||
[Flags]
|
||||
private enum ReadMode
|
||||
{
|
||||
BSA = 1 << 0,
|
||||
WWW = 1 << 1,
|
||||
Direct = 1 << 5,
|
||||
UnityWebRequest = 1 << 6
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum TestType
|
||||
{
|
||||
CheckIfExists = 1 << 0,
|
||||
LoadBytes = 1 << 1,
|
||||
}
|
||||
|
||||
private string m_status = string.Empty;
|
||||
private TestType m_testModes = TestType.CheckIfExists;
|
||||
private ReadMode m_readModes = ReadMode.WWW;
|
||||
private CoroutineHost coroutineHost;
|
||||
|
||||
private Vector2 m_assetsScroll;
|
||||
private Vector2 m_resultsScroll;
|
||||
|
||||
private string[] m_allStreamingAssets = new string[0];
|
||||
private List<TestInfo> m_results = new List<TestInfo>();
|
||||
private HashSet<string> m_selectedPaths = new HashSet<string>();
|
||||
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
InProgressText.enabled = false;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
InProgressText.enabled = true;
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
using (new GUILayout.AreaScope(new Rect(0, 0, Screen.width, Screen.height)))
|
||||
{
|
||||
if (string.IsNullOrEmpty(BetterStreamingAssets.Root))
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("APK path");
|
||||
EditorApkPath = GUILayout.TextField(EditorApkPath);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Use APK (Android like)"))
|
||||
{
|
||||
BetterStreamingAssets.InitializeWithExternalApk(EditorApkPath);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Use Assets/StreamingAssets directory (iOS/Standalone like)"))
|
||||
{
|
||||
BetterStreamingAssets.Initialize();
|
||||
Initialize();
|
||||
}
|
||||
return;
|
||||
#else
|
||||
BetterStreamingAssets.Initialize();
|
||||
Initialize();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (m_allStreamingAssets.Length == 0)
|
||||
{
|
||||
GUILayout.Label("No streaming assets found in " + BetterStreamingAssets.Root);
|
||||
return;
|
||||
}
|
||||
|
||||
GUILayout.Label("Using " + BetterStreamingAssets.Root);
|
||||
|
||||
GUILayout.Label("Discovered streaming assets:");
|
||||
using (var scope = new GUILayout.ScrollViewScope(m_assetsScroll, GUILayout.MaxHeight(300)))
|
||||
{
|
||||
m_assetsScroll = scope.scrollPosition;
|
||||
foreach (var path in m_allStreamingAssets)
|
||||
{
|
||||
var wasSelected = m_selectedPaths.Contains(path);
|
||||
if (GUILayout.Toggle(wasSelected, path))
|
||||
{
|
||||
if (!wasSelected)
|
||||
m_selectedPaths.Add(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wasSelected)
|
||||
m_selectedPaths.Remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const float VerticalSpace = 10.0f;
|
||||
GUILayout.Space(VerticalSpace);
|
||||
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("Repetition count: " + RepetitionCount, GUILayout.Width(150.0f));
|
||||
RepetitionCount = Mathf.RoundToInt(GUILayout.HorizontalSlider((float)RepetitionCount, 1.0f, 20.0f));
|
||||
}
|
||||
|
||||
LogToFile = GUILayout.Toggle(LogToFile, "Log results to file");
|
||||
|
||||
GUILayout.Space(VerticalSpace);
|
||||
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("Test modes: ", GUILayout.Width(150.0f));
|
||||
DoTestTypeToggle(TestType.CheckIfExists);
|
||||
DoTestTypeToggle(TestType.LoadBytes);
|
||||
}
|
||||
|
||||
GUILayout.Space(VerticalSpace);
|
||||
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("Read modes: ", GUILayout.Width(150.0f));
|
||||
DoReadModeToggle(ReadMode.BSA);
|
||||
DoReadModeToggle(ReadMode.WWW);
|
||||
DoReadModeToggle(ReadMode.UnityWebRequest);
|
||||
#if !UNITY_ANDROID || UNITY_EDITOR
|
||||
DoReadModeToggle(ReadMode.Direct);
|
||||
#endif
|
||||
}
|
||||
|
||||
GUI.enabled = m_selectedPaths.Count > 0;
|
||||
if (GUILayout.Button("Test Selected Paths (" + m_selectedPaths.Count + ")"))
|
||||
{
|
||||
coroutineHost.StartCoroutine(TestAllCoroutine(m_selectedPaths.ToArray(), RepetitionCount, m_readModes, m_testModes, m_results));
|
||||
}
|
||||
GUI.enabled = true;
|
||||
|
||||
GUILayout.Box(m_status);
|
||||
|
||||
using (var scroll = new GUILayout.ScrollViewScope(m_resultsScroll))
|
||||
{
|
||||
m_resultsScroll = scroll.scrollPosition;
|
||||
|
||||
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
|
||||
GUI.skin.label.clipping = TextClipping.Clip;
|
||||
|
||||
foreach (var result in m_results)
|
||||
{
|
||||
using (var layout = new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(result.path, GUILayout.Width(200));
|
||||
GUILayout.Label(result.readMode.ToString(), GUILayout.Width(160));
|
||||
GUILayout.Label(result.testType.ToString(), GUILayout.Width(160));
|
||||
|
||||
if (result.error != null)
|
||||
{
|
||||
GUILayout.Label(result.error.GetType().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label(result.duration.ToString());
|
||||
GUILayout.Label((result.memoryPeak / 1024.0 / 1024.0).ToString("F2") + " MB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUI.skin.label.clipping = TextClipping.Overflow;
|
||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string StreamingAssetsPath
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (BetterStreamingAssets.Root == EditorApkPath)
|
||||
{
|
||||
return "jar:" + new Uri(EditorApkPath).AbsoluteUri + "!/assets";
|
||||
}
|
||||
#endif
|
||||
return Application.streamingAssetsPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
m_allStreamingAssets = BetterStreamingAssets.GetFiles("/", "*", SearchOption.AllDirectories);
|
||||
|
||||
coroutineHost = gameObject.AddComponent<CoroutineHost>();
|
||||
|
||||
// allocate something for mono heap to grow
|
||||
var bytes = new byte[200 * 1024 * 1024];
|
||||
Debug.LogFormat("Allocated {0}, mono heap size: {1}", bytes.Length, Profiler.GetMonoHeapSizeLong());
|
||||
}
|
||||
|
||||
private void DoTestTypeToggle(TestType testMode)
|
||||
{
|
||||
bool wasSet = (m_testModes & testMode) == testMode;
|
||||
if (GUILayout.Toggle(wasSet, testMode.ToString()))
|
||||
{
|
||||
m_testModes |= testMode;
|
||||
}
|
||||
else if (wasSet)
|
||||
{
|
||||
if (m_testModes != testMode)
|
||||
m_testModes &= ~testMode;
|
||||
}
|
||||
}
|
||||
|
||||
private void DoReadModeToggle(ReadMode readMode)
|
||||
{
|
||||
bool wasSet = (m_readModes & readMode) == readMode;
|
||||
if (GUILayout.Toggle(wasSet, readMode.ToString()))
|
||||
{
|
||||
m_readModes |= readMode;
|
||||
}
|
||||
else if (wasSet)
|
||||
{
|
||||
if (m_readModes != readMode)
|
||||
m_readModes &= ~readMode;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator TestAllCoroutine(IEnumerable<string> paths, int attempts, ReadMode readModes, TestType testTypes, List<TestInfo> results)
|
||||
{
|
||||
LogWorkProgress("starting...");
|
||||
|
||||
string logPath = Path.Combine(Application.persistentDataPath, "BSA_test_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + "_.csv");
|
||||
|
||||
enabled = false;
|
||||
results.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
foreach (ReadMode readMode in Enum.GetValues(typeof(ReadMode)))
|
||||
{
|
||||
if ((readMode & readModes) != readMode)
|
||||
continue;
|
||||
|
||||
foreach (TestType testType in Enum.GetValues(typeof(TestType)))
|
||||
{
|
||||
if ((testType & testTypes) != testType)
|
||||
continue;
|
||||
|
||||
var testInfo = new TestInfo()
|
||||
{
|
||||
readMode = readMode,
|
||||
testType = testType,
|
||||
path = path,
|
||||
attempts = attempts,
|
||||
};
|
||||
|
||||
yield return coroutineHost.StartCoroutine(ErrorCatchingCoroutine(TestHarness(readMode, path, testType, attempts,
|
||||
(duration, bytes, memory, maxMemory, names) =>
|
||||
{
|
||||
testInfo.duration = duration;
|
||||
testInfo.bytesRead = bytes;
|
||||
testInfo.memoryPeak = memory;
|
||||
testInfo.maxMemoryPeak = maxMemory;
|
||||
}),
|
||||
ex =>
|
||||
{
|
||||
testInfo.error = ex;
|
||||
}
|
||||
));
|
||||
|
||||
results.Add(testInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
enabled = true;
|
||||
|
||||
if (LogToFile)
|
||||
{
|
||||
using (var writer = File.CreateText(logPath))
|
||||
{
|
||||
foreach (var result in results)
|
||||
{
|
||||
string errorMessage = string.Empty;
|
||||
if (result.error != null)
|
||||
errorMessage = result.error.ToString().Replace(Environment.NewLine, ";");
|
||||
|
||||
writer.WriteLine("\"{0}\"\t{1}\t{2}\t{3}\t{4}\t\"{5}\"", result.path, result.readMode, result.testType, result.duration, result.memoryPeak, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
LogWorkProgress("Logged at: " + logPath);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void LogWorkProgress(string status)
|
||||
{
|
||||
Debug.Log("WORK PROGRESS: " + status);
|
||||
if (string.IsNullOrEmpty(m_status))
|
||||
m_status = status;
|
||||
else
|
||||
m_status += "\n" + status;
|
||||
}
|
||||
|
||||
private IEnumerator ErrorCatchingCoroutine(IEnumerator inner, Action<System.Exception> onError)
|
||||
{
|
||||
m_status = string.Empty;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
bool next = false;
|
||||
try
|
||||
{
|
||||
next = inner.MoveNext();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
onError(ex);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!next)
|
||||
break;
|
||||
|
||||
yield return inner.Current;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator TestHarness(ReadMode readMode, string path, TestType testType, int attempts, TestResultDelegate callback)
|
||||
{
|
||||
var stopwatch = new Stopwatch();
|
||||
|
||||
string[] assetNames = null;
|
||||
|
||||
var streamingAssetsUrl = Path.Combine(StreamingAssetsPath, path.TrimStart('/')).Replace('\\', '/');
|
||||
|
||||
long bytesRead = 0;
|
||||
long maxMemoryPeak = 0;
|
||||
long totalMemoryPeaks = 0;
|
||||
|
||||
for (int i = 0; i < attempts; ++i)
|
||||
{
|
||||
IDisposable toDispose = null;
|
||||
|
||||
yield return Resources.UnloadUnusedAssets();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
yield return null;
|
||||
|
||||
var memoryUnityBefore = Profiler.GetTotalAllocatedMemoryLong();
|
||||
//var memoryMonoBefore = Profiler.GetMonoUsedSizeLong();
|
||||
stopwatch.Start();
|
||||
|
||||
if (readMode == ReadMode.WWW)
|
||||
{
|
||||
#pragma warning disable 0618 // Type or member is obsolete
|
||||
var www = new WWW(streamingAssetsUrl);
|
||||
#pragma warning restore 0618 // Type or member is obsolete
|
||||
toDispose = www;
|
||||
{
|
||||
yield return www;
|
||||
|
||||
Profiler.BeginSample(testType.ToString());
|
||||
|
||||
switch (testType)
|
||||
{
|
||||
case TestType.CheckIfExists:
|
||||
if (!string.IsNullOrEmpty(www.error))
|
||||
throw new System.Exception(www.error);
|
||||
break;
|
||||
case TestType.LoadBytes:
|
||||
bytesRead += www.bytes.Length;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
else if (readMode == ReadMode.BSA)
|
||||
{
|
||||
Profiler.BeginSample(testType.ToString());
|
||||
|
||||
switch (testType)
|
||||
{
|
||||
case TestType.CheckIfExists:
|
||||
if (!BetterStreamingAssets.FileExists(path))
|
||||
throw new System.InvalidOperationException();
|
||||
break;
|
||||
case TestType.LoadBytes:
|
||||
bytesRead += BetterStreamingAssets.ReadAllBytes(path).Length;
|
||||
break;
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
else if (readMode == ReadMode.Direct)
|
||||
{
|
||||
var p = streamingAssetsUrl;
|
||||
Profiler.BeginSample(testType.ToString());
|
||||
|
||||
switch (testType)
|
||||
{
|
||||
case TestType.CheckIfExists:
|
||||
if (!File.Exists(p))
|
||||
throw new System.InvalidOperationException();
|
||||
break;
|
||||
case TestType.LoadBytes:
|
||||
bytesRead += File.ReadAllBytes(p).Length;
|
||||
break;
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
else if (readMode == ReadMode.UnityWebRequest)
|
||||
{
|
||||
var www = UnityEngine.Networking.UnityWebRequest.Get(streamingAssetsUrl);
|
||||
toDispose = www;
|
||||
#pragma warning disable 0618 // Type or member is obsolete
|
||||
yield return www.Send();
|
||||
#pragma warning restore 0618 // Type or member is obsolete
|
||||
|
||||
Profiler.BeginSample(testType.ToString());
|
||||
|
||||
switch (testType)
|
||||
{
|
||||
case TestType.CheckIfExists:
|
||||
if (!string.IsNullOrEmpty(www.error))
|
||||
throw new System.Exception(www.error);
|
||||
break;
|
||||
case TestType.LoadBytes:
|
||||
bytesRead += (int)www.downloadedBytes;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
var memoryPeak = Math.Max(0, Profiler.GetTotalAllocatedMemoryLong() - memoryUnityBefore);
|
||||
// + Math.Max(0, Profiler.GetMonoUsedSizeLong() - memoryMonoBefore);
|
||||
|
||||
maxMemoryPeak = System.Math.Max(memoryPeak, maxMemoryPeak);
|
||||
totalMemoryPeaks += memoryPeak;
|
||||
|
||||
yield return null;
|
||||
|
||||
if (toDispose != null)
|
||||
toDispose.Dispose();
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
yield return Resources.UnloadUnusedAssets();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
yield return null;
|
||||
|
||||
callback(new TimeSpan(stopwatch.ElapsedTicks / attempts), bytesRead / attempts, totalMemoryPeaks / attempts, maxMemoryPeak, assetNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e254ae11057fcdf4a894dcf319700de7
|
||||
timeCreated: 1506685124
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,649 @@
|
||||
// Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
|
||||
// Bits below are copied from or inspired by System.IO.Compression.dll; leaving comments from
|
||||
// original source code and attaching license
|
||||
|
||||
// The MIT License(MIT)
|
||||
//
|
||||
// Copyright(c) .NET Foundation and Contributors
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Better.StreamingAssets.ZipArchive
|
||||
{
|
||||
// All blocks.TryReadBlock do a check to see if signature is correct. Generic extra field is slightly different
|
||||
// all of the TryReadBlocks will throw if there are not enough bytes in the stream
|
||||
|
||||
internal struct ZipGenericExtraField
|
||||
{
|
||||
private const int SizeOfHeader = 4;
|
||||
|
||||
private ushort _tag;
|
||||
private ushort _size;
|
||||
private byte[] _data;
|
||||
|
||||
public ushort Tag { get { return _tag; } }
|
||||
// returns size of data, not of the entire block
|
||||
public ushort Size { get { return _size; } }
|
||||
public byte[] Data { get { return _data; } }
|
||||
|
||||
// shouldn't ever read the byte at position endExtraField
|
||||
// assumes we are positioned at the beginning of an extra field subfield
|
||||
public static bool TryReadBlock(BinaryReader reader, long endExtraField, out ZipGenericExtraField field)
|
||||
{
|
||||
field = new ZipGenericExtraField();
|
||||
|
||||
// not enough bytes to read tag + size
|
||||
if ( endExtraField - reader.BaseStream.Position < 4 )
|
||||
return false;
|
||||
|
||||
field._tag = reader.ReadUInt16();
|
||||
field._size = reader.ReadUInt16();
|
||||
|
||||
// not enough bytes to read the data
|
||||
if ( endExtraField - reader.BaseStream.Position < field._size )
|
||||
return false;
|
||||
|
||||
field._data = reader.ReadBytes(field._size);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct Zip64ExtraField
|
||||
{
|
||||
// Size is size of the record not including the tag or size fields
|
||||
// If the extra field is going in the local header, it cannot include only
|
||||
// one of uncompressed/compressed size
|
||||
|
||||
public const int OffsetToFirstField = 4;
|
||||
private const ushort TagConstant = 1;
|
||||
|
||||
private ushort _size;
|
||||
private long? _uncompressedSize;
|
||||
private long? _compressedSize;
|
||||
private long? _localHeaderOffset;
|
||||
private int? _startDiskNumber;
|
||||
|
||||
|
||||
public long? UncompressedSize
|
||||
{
|
||||
get { return _uncompressedSize; }
|
||||
set { _uncompressedSize = value; UpdateSize(); }
|
||||
}
|
||||
public long? CompressedSize
|
||||
{
|
||||
get { return _compressedSize; }
|
||||
set { _compressedSize = value; UpdateSize(); }
|
||||
}
|
||||
public long? LocalHeaderOffset
|
||||
{
|
||||
get { return _localHeaderOffset; }
|
||||
set { _localHeaderOffset = value; UpdateSize(); }
|
||||
}
|
||||
public int? StartDiskNumber { get { return _startDiskNumber; } }
|
||||
|
||||
private void UpdateSize()
|
||||
{
|
||||
_size = 0;
|
||||
if ( _uncompressedSize != null ) _size += 8;
|
||||
if ( _compressedSize != null ) _size += 8;
|
||||
if ( _localHeaderOffset != null ) _size += 8;
|
||||
if ( _startDiskNumber != null ) _size += 4;
|
||||
}
|
||||
|
||||
// There is a small chance that something very weird could happen here. The code calling into this function
|
||||
// will ask for a value from the extra field if the field was masked with FF's. It's theoretically possible
|
||||
// that a field was FF's legitimately, and the writer didn't decide to write the corresponding extra field.
|
||||
// Also, at the same time, other fields were masked with FF's to indicate looking in the zip64 record.
|
||||
// Then, the search for the zip64 record will fail because the expected size is wrong,
|
||||
// and a nulled out Zip64ExtraField will be returned. Thus, even though there was Zip64 data,
|
||||
// it will not be used. It is questionable whether this situation is possible to detect
|
||||
|
||||
// unlike the other functions that have try-pattern semantics, these functions always return a
|
||||
// Zip64ExtraField. If a Zip64 extra field actually doesn't exist, all of the fields in the
|
||||
// returned struct will be null
|
||||
//
|
||||
// If there are more than one Zip64 extra fields, we take the first one that has the expected size
|
||||
//
|
||||
public static Zip64ExtraField GetJustZip64Block(Stream extraFieldStream,
|
||||
bool readUncompressedSize, bool readCompressedSize,
|
||||
bool readLocalHeaderOffset, bool readStartDiskNumber)
|
||||
{
|
||||
Zip64ExtraField zip64Field;
|
||||
using ( BinaryReader reader = new BinaryReader(extraFieldStream) )
|
||||
{
|
||||
ZipGenericExtraField currentExtraField;
|
||||
while ( ZipGenericExtraField.TryReadBlock(reader, extraFieldStream.Length, out currentExtraField) )
|
||||
{
|
||||
if ( TryGetZip64BlockFromGenericExtraField(currentExtraField, readUncompressedSize,
|
||||
readCompressedSize, readLocalHeaderOffset, readStartDiskNumber, out zip64Field) )
|
||||
{
|
||||
return zip64Field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zip64Field = new Zip64ExtraField();
|
||||
|
||||
zip64Field._compressedSize = null;
|
||||
zip64Field._uncompressedSize = null;
|
||||
zip64Field._localHeaderOffset = null;
|
||||
zip64Field._startDiskNumber = null;
|
||||
|
||||
return zip64Field;
|
||||
}
|
||||
|
||||
private static bool TryGetZip64BlockFromGenericExtraField(ZipGenericExtraField extraField,
|
||||
bool readUncompressedSize, bool readCompressedSize,
|
||||
bool readLocalHeaderOffset, bool readStartDiskNumber,
|
||||
out Zip64ExtraField zip64Block)
|
||||
{
|
||||
zip64Block = new Zip64ExtraField();
|
||||
|
||||
zip64Block._compressedSize = null;
|
||||
zip64Block._uncompressedSize = null;
|
||||
zip64Block._localHeaderOffset = null;
|
||||
zip64Block._startDiskNumber = null;
|
||||
|
||||
if ( extraField.Tag != TagConstant )
|
||||
return false;
|
||||
|
||||
// this pattern needed because nested using blocks trigger CA2202
|
||||
MemoryStream ms = null;
|
||||
try
|
||||
{
|
||||
ms = new MemoryStream(extraField.Data);
|
||||
using ( BinaryReader reader = new BinaryReader(ms) )
|
||||
{
|
||||
ms = null;
|
||||
|
||||
zip64Block._size = extraField.Size;
|
||||
|
||||
ushort expectedSize = 0;
|
||||
|
||||
if ( readUncompressedSize ) expectedSize += 8;
|
||||
if ( readCompressedSize ) expectedSize += 8;
|
||||
if ( readLocalHeaderOffset ) expectedSize += 8;
|
||||
if ( readStartDiskNumber ) expectedSize += 4;
|
||||
|
||||
// if it is not the expected size, perhaps there is another extra field that matches
|
||||
if ( expectedSize != zip64Block._size )
|
||||
return false;
|
||||
|
||||
if ( readUncompressedSize ) zip64Block._uncompressedSize = reader.ReadInt64();
|
||||
if ( readCompressedSize ) zip64Block._compressedSize = reader.ReadInt64();
|
||||
if ( readLocalHeaderOffset ) zip64Block._localHeaderOffset = reader.ReadInt64();
|
||||
if ( readStartDiskNumber ) zip64Block._startDiskNumber = reader.ReadInt32();
|
||||
|
||||
// original values are unsigned, so implies value is too big to fit in signed integer
|
||||
if ( zip64Block._uncompressedSize < 0 ) throw new ZipArchiveException("FieldTooBigUncompressedSize");
|
||||
if ( zip64Block._compressedSize < 0 ) throw new ZipArchiveException("FieldTooBigCompressedSize");
|
||||
if ( zip64Block._localHeaderOffset < 0 ) throw new ZipArchiveException("FieldTooBigLocalHeaderOffset");
|
||||
if ( zip64Block._startDiskNumber < 0 ) throw new ZipArchiveException("FieldTooBigStartDiskNumber");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if ( ms != null )
|
||||
ms.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal struct Zip64EndOfCentralDirectoryLocator
|
||||
{
|
||||
public const uint SignatureConstant = 0x07064B50;
|
||||
public const int SizeOfBlockWithoutSignature = 16;
|
||||
|
||||
public uint NumberOfDiskWithZip64EOCD;
|
||||
public ulong OffsetOfZip64EOCD;
|
||||
public uint TotalNumberOfDisks;
|
||||
|
||||
public static bool TryReadBlock(BinaryReader reader, out Zip64EndOfCentralDirectoryLocator zip64EOCDLocator)
|
||||
{
|
||||
zip64EOCDLocator = new Zip64EndOfCentralDirectoryLocator();
|
||||
|
||||
if ( reader.ReadUInt32() != SignatureConstant )
|
||||
return false;
|
||||
|
||||
zip64EOCDLocator.NumberOfDiskWithZip64EOCD = reader.ReadUInt32();
|
||||
zip64EOCDLocator.OffsetOfZip64EOCD = reader.ReadUInt64();
|
||||
zip64EOCDLocator.TotalNumberOfDisks = reader.ReadUInt32();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal struct Zip64EndOfCentralDirectoryRecord
|
||||
{
|
||||
private const uint SignatureConstant = 0x06064B50;
|
||||
private const ulong NormalSize = 0x2C; // the size of the data excluding the size/signature fields if no extra data included
|
||||
|
||||
public ulong SizeOfThisRecord;
|
||||
public ushort VersionMadeBy;
|
||||
public ushort VersionNeededToExtract;
|
||||
public uint NumberOfThisDisk;
|
||||
public uint NumberOfDiskWithStartOfCD;
|
||||
public ulong NumberOfEntriesOnThisDisk;
|
||||
public ulong NumberOfEntriesTotal;
|
||||
public ulong SizeOfCentralDirectory;
|
||||
public ulong OffsetOfCentralDirectory;
|
||||
|
||||
public static bool TryReadBlock(BinaryReader reader, out Zip64EndOfCentralDirectoryRecord zip64EOCDRecord)
|
||||
{
|
||||
zip64EOCDRecord = new Zip64EndOfCentralDirectoryRecord();
|
||||
|
||||
if ( reader.ReadUInt32() != SignatureConstant )
|
||||
return false;
|
||||
|
||||
zip64EOCDRecord.SizeOfThisRecord = reader.ReadUInt64();
|
||||
zip64EOCDRecord.VersionMadeBy = reader.ReadUInt16();
|
||||
zip64EOCDRecord.VersionNeededToExtract = reader.ReadUInt16();
|
||||
zip64EOCDRecord.NumberOfThisDisk = reader.ReadUInt32();
|
||||
zip64EOCDRecord.NumberOfDiskWithStartOfCD = reader.ReadUInt32();
|
||||
zip64EOCDRecord.NumberOfEntriesOnThisDisk = reader.ReadUInt64();
|
||||
zip64EOCDRecord.NumberOfEntriesTotal = reader.ReadUInt64();
|
||||
zip64EOCDRecord.SizeOfCentralDirectory = reader.ReadUInt64();
|
||||
zip64EOCDRecord.OffsetOfCentralDirectory = reader.ReadUInt64();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ZipLocalFileHeader
|
||||
{
|
||||
public const uint DataDescriptorSignature = 0x08074B50;
|
||||
public const uint SignatureConstant = 0x04034B50;
|
||||
public const int OffsetToCrcFromHeaderStart = 14;
|
||||
public const int OffsetToBitFlagFromHeaderStart = 6;
|
||||
public const int SizeOfLocalHeader = 30;
|
||||
|
||||
|
||||
// will not throw end of stream exception
|
||||
public static bool TrySkipBlock(BinaryReader reader)
|
||||
{
|
||||
const int OffsetToFilenameLength = 22; // from the point after the signature
|
||||
|
||||
if ( reader.ReadUInt32() != SignatureConstant )
|
||||
return false;
|
||||
|
||||
|
||||
if ( reader.BaseStream.Length < reader.BaseStream.Position + OffsetToFilenameLength )
|
||||
return false;
|
||||
|
||||
reader.BaseStream.Seek(OffsetToFilenameLength, SeekOrigin.Current);
|
||||
|
||||
ushort filenameLength = reader.ReadUInt16();
|
||||
ushort extraFieldLength = reader.ReadUInt16();
|
||||
|
||||
if ( reader.BaseStream.Length < reader.BaseStream.Position + filenameLength + extraFieldLength )
|
||||
return false;
|
||||
|
||||
reader.BaseStream.Seek(filenameLength + extraFieldLength, SeekOrigin.Current);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ZipCentralDirectoryFileHeader
|
||||
{
|
||||
public const uint SignatureConstant = 0x02014B50;
|
||||
public byte VersionMadeByCompatibility;
|
||||
public byte VersionMadeBySpecification;
|
||||
public ushort VersionNeededToExtract;
|
||||
public ushort GeneralPurposeBitFlag;
|
||||
public ushort CompressionMethod;
|
||||
public uint LastModified; // convert this on the fly
|
||||
public uint Crc32;
|
||||
public long CompressedSize;
|
||||
public long UncompressedSize;
|
||||
public ushort FilenameLength;
|
||||
public ushort ExtraFieldLength;
|
||||
public ushort FileCommentLength;
|
||||
public int DiskNumberStart;
|
||||
public ushort InternalFileAttributes;
|
||||
public uint ExternalFileAttributes;
|
||||
public long RelativeOffsetOfLocalHeader;
|
||||
|
||||
public byte[] Filename;
|
||||
public byte[] FileComment;
|
||||
public List<ZipGenericExtraField> ExtraFields;
|
||||
|
||||
// if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null
|
||||
// in either case, the zip64 extra field info will be incorporated into other fields
|
||||
public static bool TryReadBlock(BinaryReader reader, out ZipCentralDirectoryFileHeader header)
|
||||
{
|
||||
header = new ZipCentralDirectoryFileHeader();
|
||||
|
||||
if ( reader.ReadUInt32() != SignatureConstant )
|
||||
return false;
|
||||
header.VersionMadeBySpecification = reader.ReadByte();
|
||||
header.VersionMadeByCompatibility = reader.ReadByte();
|
||||
header.VersionNeededToExtract = reader.ReadUInt16();
|
||||
header.GeneralPurposeBitFlag = reader.ReadUInt16();
|
||||
header.CompressionMethod = reader.ReadUInt16();
|
||||
header.LastModified = reader.ReadUInt32();
|
||||
header.Crc32 = reader.ReadUInt32();
|
||||
uint compressedSizeSmall = reader.ReadUInt32();
|
||||
uint uncompressedSizeSmall = reader.ReadUInt32();
|
||||
header.FilenameLength = reader.ReadUInt16();
|
||||
header.ExtraFieldLength = reader.ReadUInt16();
|
||||
header.FileCommentLength = reader.ReadUInt16();
|
||||
ushort diskNumberStartSmall = reader.ReadUInt16();
|
||||
header.InternalFileAttributes = reader.ReadUInt16();
|
||||
header.ExternalFileAttributes = reader.ReadUInt32();
|
||||
uint relativeOffsetOfLocalHeaderSmall = reader.ReadUInt32();
|
||||
|
||||
header.Filename = reader.ReadBytes(header.FilenameLength);
|
||||
|
||||
bool uncompressedSizeInZip64 = uncompressedSizeSmall == ZipHelper.Mask32Bit;
|
||||
bool compressedSizeInZip64 = compressedSizeSmall == ZipHelper.Mask32Bit;
|
||||
bool relativeOffsetInZip64 = relativeOffsetOfLocalHeaderSmall == ZipHelper.Mask32Bit;
|
||||
bool diskNumberStartInZip64 = diskNumberStartSmall == ZipHelper.Mask16Bit;
|
||||
|
||||
Zip64ExtraField zip64;
|
||||
|
||||
long endExtraFields = reader.BaseStream.Position + header.ExtraFieldLength;
|
||||
using ( Stream str = new SubReadOnlyStream(reader.BaseStream, reader.BaseStream.Position, header.ExtraFieldLength, leaveOpen: true) )
|
||||
{
|
||||
header.ExtraFields = null;
|
||||
zip64 = Zip64ExtraField.GetJustZip64Block(str,
|
||||
uncompressedSizeInZip64, compressedSizeInZip64,
|
||||
relativeOffsetInZip64, diskNumberStartInZip64);
|
||||
}
|
||||
|
||||
// There are zip files that have malformed ExtraField blocks in which GetJustZip64Block() silently bails out without reading all the way to the end
|
||||
// of the ExtraField block. Thus we must force the stream's position to the proper place.
|
||||
reader.BaseStream.AdvanceToPosition(endExtraFields);
|
||||
|
||||
reader.BaseStream.Position += header.FileCommentLength;
|
||||
header.FileComment = null;
|
||||
|
||||
header.UncompressedSize = zip64.UncompressedSize == null
|
||||
? uncompressedSizeSmall
|
||||
: zip64.UncompressedSize.Value;
|
||||
header.CompressedSize = zip64.CompressedSize == null
|
||||
? compressedSizeSmall
|
||||
: zip64.CompressedSize.Value;
|
||||
header.RelativeOffsetOfLocalHeader = zip64.LocalHeaderOffset == null
|
||||
? relativeOffsetOfLocalHeaderSmall
|
||||
: zip64.LocalHeaderOffset.Value;
|
||||
header.DiskNumberStart = zip64.StartDiskNumber == null
|
||||
? diskNumberStartSmall
|
||||
: zip64.StartDiskNumber.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ZipEndOfCentralDirectoryBlock
|
||||
{
|
||||
public const uint SignatureConstant = 0x06054B50;
|
||||
public const int SizeOfBlockWithoutSignature = 18;
|
||||
public uint Signature;
|
||||
public ushort NumberOfThisDisk;
|
||||
public ushort NumberOfTheDiskWithTheStartOfTheCentralDirectory;
|
||||
public ushort NumberOfEntriesInTheCentralDirectoryOnThisDisk;
|
||||
public ushort NumberOfEntriesInTheCentralDirectory;
|
||||
public uint SizeOfCentralDirectory;
|
||||
public uint OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
|
||||
public byte[] ArchiveComment;
|
||||
|
||||
|
||||
public static bool TryReadBlock(BinaryReader reader, out ZipEndOfCentralDirectoryBlock eocdBlock)
|
||||
{
|
||||
eocdBlock = new ZipEndOfCentralDirectoryBlock();
|
||||
if ( reader.ReadUInt32() != SignatureConstant )
|
||||
return false;
|
||||
|
||||
eocdBlock.Signature = SignatureConstant;
|
||||
eocdBlock.NumberOfThisDisk = reader.ReadUInt16();
|
||||
eocdBlock.NumberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16();
|
||||
eocdBlock.NumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt16();
|
||||
eocdBlock.NumberOfEntriesInTheCentralDirectory = reader.ReadUInt16();
|
||||
eocdBlock.SizeOfCentralDirectory = reader.ReadUInt32();
|
||||
eocdBlock.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt32();
|
||||
|
||||
ushort commentLength = reader.ReadUInt16();
|
||||
eocdBlock.ArchiveComment = reader.ReadBytes(commentLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class ZipHelper
|
||||
{
|
||||
internal const uint Mask32Bit = 0xFFFFFFFF;
|
||||
internal const ushort Mask16Bit = 0xFFFF;
|
||||
|
||||
private const int BackwardsSeekingBufferSize = 32;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads exactly bytesToRead out of stream, unless it is out of bytes
|
||||
/// </summary>
|
||||
internal static void ReadBytes(Stream stream, byte[] buffer, int bytesToRead)
|
||||
{
|
||||
int bytesLeftToRead = bytesToRead;
|
||||
|
||||
int totalBytesRead = 0;
|
||||
|
||||
while (bytesLeftToRead > 0)
|
||||
{
|
||||
int bytesRead = stream.Read(buffer, totalBytesRead, bytesLeftToRead);
|
||||
if (bytesRead == 0) throw new IOException();
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
bytesLeftToRead -= bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// assumes all bytes of signatureToFind are non zero, looks backwards from current position in stream,
|
||||
// if the signature is found then returns true and positions stream at first byte of signature
|
||||
// if the signature is not found, returns false
|
||||
internal static bool SeekBackwardsToSignature(Stream stream, uint signatureToFind)
|
||||
{
|
||||
int bufferPointer = 0;
|
||||
uint currentSignature = 0;
|
||||
byte[] buffer = new byte[BackwardsSeekingBufferSize];
|
||||
|
||||
bool outOfBytes = false;
|
||||
bool signatureFound = false;
|
||||
|
||||
while (!signatureFound && !outOfBytes)
|
||||
{
|
||||
outOfBytes = SeekBackwardsAndRead(stream, buffer, out bufferPointer);
|
||||
|
||||
Debug.Assert(bufferPointer < buffer.Length);
|
||||
|
||||
while (bufferPointer >= 0 && !signatureFound)
|
||||
{
|
||||
currentSignature = (currentSignature << 8) | ((uint)buffer[bufferPointer]);
|
||||
if (currentSignature == signatureToFind)
|
||||
{
|
||||
signatureFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferPointer--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!signatureFound)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.Seek(bufferPointer, SeekOrigin.Current);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip to a further position downstream (without relying on the stream being seekable)
|
||||
internal static void AdvanceToPosition(this Stream stream, long position)
|
||||
{
|
||||
long numBytesLeft = position - stream.Position;
|
||||
Debug.Assert(numBytesLeft >= 0);
|
||||
while (numBytesLeft != 0)
|
||||
{
|
||||
const int throwAwayBufferSize = 64;
|
||||
int numBytesToSkip = (numBytesLeft > throwAwayBufferSize) ? throwAwayBufferSize : (int)numBytesLeft;
|
||||
int numBytesActuallySkipped = stream.Read(new byte[throwAwayBufferSize], 0, numBytesToSkip);
|
||||
if (numBytesActuallySkipped == 0)
|
||||
throw new IOException();
|
||||
numBytesLeft -= numBytesActuallySkipped;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if we are out of bytes
|
||||
private static bool SeekBackwardsAndRead(Stream stream, byte[] buffer, out int bufferPointer)
|
||||
{
|
||||
if (stream.Position >= buffer.Length)
|
||||
{
|
||||
stream.Seek(-buffer.Length, SeekOrigin.Current);
|
||||
ReadBytes(stream, buffer, buffer.Length);
|
||||
stream.Seek(-buffer.Length, SeekOrigin.Current);
|
||||
bufferPointer = buffer.Length - 1;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int bytesToRead = (int)stream.Position;
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
ReadBytes(stream, buffer, bytesToRead);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
bufferPointer = bytesToRead - 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ZipArchiveException : Exception
|
||||
{
|
||||
public ZipArchiveException(string msg) : base(msg)
|
||||
{ }
|
||||
|
||||
public ZipArchiveException(string msg, Exception inner)
|
||||
: base(msg, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class ZipArchiveUtils
|
||||
{
|
||||
public static void ReadEndOfCentralDirectory(Stream stream, BinaryReader reader, out long expectedNumberOfEntries, out long centralDirectoryStart)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this seeks to the start of the end of central directory record
|
||||
stream.Seek(-ZipEndOfCentralDirectoryBlock.SizeOfBlockWithoutSignature, SeekOrigin.End);
|
||||
if (!ZipHelper.SeekBackwardsToSignature(stream, ZipEndOfCentralDirectoryBlock.SignatureConstant))
|
||||
throw new ZipArchiveException("SignatureConstant");
|
||||
|
||||
long eocdStart = stream.Position;
|
||||
|
||||
// read the EOCD
|
||||
ZipEndOfCentralDirectoryBlock eocd;
|
||||
bool eocdProper = ZipEndOfCentralDirectoryBlock.TryReadBlock(reader, out eocd);
|
||||
Debug.Assert(eocdProper); // we just found this using the signature finder, so it should be okay
|
||||
|
||||
if (eocd.NumberOfThisDisk != eocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory)
|
||||
throw new ZipArchiveException("SplitSpanned");
|
||||
|
||||
centralDirectoryStart = eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
|
||||
if (eocd.NumberOfEntriesInTheCentralDirectory != eocd.NumberOfEntriesInTheCentralDirectoryOnThisDisk)
|
||||
throw new ZipArchiveException("SplitSpanned");
|
||||
expectedNumberOfEntries = eocd.NumberOfEntriesInTheCentralDirectory;
|
||||
|
||||
|
||||
// only bother looking for zip64 EOCD stuff if we suspect it is needed because some value is FFFFFFFFF
|
||||
// because these are the only two values we need, we only worry about these
|
||||
// if we don't find the zip64 EOCD, we just give up and try to use the original values
|
||||
if (eocd.NumberOfThisDisk == ZipHelper.Mask16Bit ||
|
||||
eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == ZipHelper.Mask32Bit ||
|
||||
eocd.NumberOfEntriesInTheCentralDirectory == ZipHelper.Mask16Bit)
|
||||
{
|
||||
// we need to look for zip 64 EOCD stuff
|
||||
// seek to the zip 64 EOCD locator
|
||||
stream.Seek(eocdStart - Zip64EndOfCentralDirectoryLocator.SizeOfBlockWithoutSignature, SeekOrigin.Begin);
|
||||
// if we don't find it, assume it doesn't exist and use data from normal eocd
|
||||
if (ZipHelper.SeekBackwardsToSignature(stream, Zip64EndOfCentralDirectoryLocator.SignatureConstant))
|
||||
{
|
||||
// use locator to get to Zip64EOCD
|
||||
Zip64EndOfCentralDirectoryLocator locator;
|
||||
bool zip64eocdLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(reader, out locator);
|
||||
Debug.Assert(zip64eocdLocatorProper); // we just found this using the signature finder, so it should be okay
|
||||
|
||||
if (locator.OffsetOfZip64EOCD > long.MaxValue)
|
||||
throw new ZipArchiveException("FieldTooBigOffsetToZip64EOCD");
|
||||
long zip64EOCDOffset = (long)locator.OffsetOfZip64EOCD;
|
||||
|
||||
stream.Seek(zip64EOCDOffset, SeekOrigin.Begin);
|
||||
|
||||
// read Zip64EOCD
|
||||
Zip64EndOfCentralDirectoryRecord record;
|
||||
if (!Zip64EndOfCentralDirectoryRecord.TryReadBlock(reader, out record))
|
||||
throw new ZipArchiveException("Zip64EOCDNotWhereExpected");
|
||||
|
||||
if (record.NumberOfEntriesTotal > long.MaxValue)
|
||||
throw new ZipArchiveException("FieldTooBigNumEntries");
|
||||
if (record.OffsetOfCentralDirectory > long.MaxValue)
|
||||
throw new ZipArchiveException("FieldTooBigOffsetToCD");
|
||||
if (record.NumberOfEntriesTotal != record.NumberOfEntriesOnThisDisk)
|
||||
throw new ZipArchiveException("SplitSpanned");
|
||||
|
||||
expectedNumberOfEntries = (long)record.NumberOfEntriesTotal;
|
||||
centralDirectoryStart = (long)record.OffsetOfCentralDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
if (centralDirectoryStart > stream.Length)
|
||||
{
|
||||
throw new ZipArchiveException("FieldTooBigOffsetToCD");
|
||||
}
|
||||
}
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
throw new ZipArchiveException("CDCorrupt", ex);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new ZipArchiveException("CDCorrupt", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ae0668083250b649a64c7836ec20f11
|
||||
timeCreated: 1506207467
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "Better"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11f3455556175aa41b2b4d4f2ec8b146
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,117 @@
|
||||
README - Better Streaming Assets
|
||||
--------------------------------
|
||||
Many thanks for downloading! Before getting your hands on the tool please have a read.
|
||||
|
||||
Better Streaming Assets is a plugin that lets you access Streaming Assets directly in an uniform and
|
||||
thread-safe way, with neglectible overhead. Mostly beneficial for Android projects, where the
|
||||
alternatives are to use archaic and hugely inefficient WWW or embed data in Asset Bundles. API is
|
||||
based on System.IO.File and System.IO.Directory classes.
|
||||
|
||||
Contact / Support:
|
||||
------------------
|
||||
Support/Feedback: support@dmprog.pl
|
||||
Twitter: @gwiazdorrr
|
||||
Support Page: http://dmprog.pl/unity-plugins/
|
||||
|
||||
Note on Android & App Bundles
|
||||
------------------
|
||||
App Bundles (.aab) builds are bugged when it comes to Streaming Assets. See https://github.com/gwiazdorrr/BetterStreamingAssets/issues/10 for details. The bottom line is:
|
||||
!!! Keep all file names in Streaming Assets lowercase! !!!
|
||||
|
||||
Usage:
|
||||
------
|
||||
Check examples below. Note that all the paths are relative to StreamingAssets directory. That is, if you have files
|
||||
|
||||
<project>/Assets/StreamingAssets/foo.bar
|
||||
<project>/Assets/StreamingAssets/dir/foo.bar
|
||||
|
||||
You are expected to use following paths:
|
||||
|
||||
foo.bar (or /foo.bar)
|
||||
dir/foo.bar (or /dir/foo.bar)
|
||||
|
||||
Examples:
|
||||
---------
|
||||
Initialization (before first use, needs to be called on main thread):
|
||||
|
||||
BetterStreaminAssets.Initialize();
|
||||
|
||||
Typical scenario, deserializing from Xml:
|
||||
|
||||
public static Foo ReadFromXml(string path)
|
||||
{
|
||||
if ( !BetterStreamingAssets.FileExists(path) )
|
||||
{
|
||||
Debug.LogErrorFormat("Streaming asset not found: {0}", path);
|
||||
return null;
|
||||
}
|
||||
|
||||
using ( var stream = BetterStreamingAssets.OpenRead(path) )
|
||||
{
|
||||
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
|
||||
return (Foo)serializer.Deserialize(stream);
|
||||
}
|
||||
}
|
||||
|
||||
Note that ReadFromXml can be called from any thread, as long as Foo's constructor doesn't make any
|
||||
UnityEngine calls.
|
||||
|
||||
Listing all Streaming Assets in with .xml extension:
|
||||
|
||||
// all the xmls
|
||||
string[] paths = BetterStreamingAssets.GetFiles("\", "*.xml", SearchOption.AllDirectories);
|
||||
// just xmls in Config directory (and nested)
|
||||
string[] paths = BetterStreamingAssets.GetFiles("Config", "*.xml", SearchOption.AllDirectories);
|
||||
|
||||
Checking if a directory exists:
|
||||
|
||||
Debug.Asset( BetterStreamingAssets.DirectoryExists("Config") );
|
||||
|
||||
Ways of reading a file:
|
||||
|
||||
// all at once
|
||||
byte[] data = BetterStreamingAssets.ReadAllBytes("Foo/bar.data");
|
||||
|
||||
// as stream, last 10 bytes
|
||||
byte[] footer = new byte[10];
|
||||
using (var stream = BetterStreamingAssets.OpenRead("Foo/bar.data"))
|
||||
{
|
||||
stream.Seek(footer.Length, SeekOrigin.End);
|
||||
stream.Read(footer, 0, footer.Length);
|
||||
}
|
||||
|
||||
Asset bundles (again, main thread only):
|
||||
|
||||
// synchronous
|
||||
var bundle = BetterStreamingAssets.LoadAssetBundle(path);
|
||||
// async
|
||||
var bundleOp = BetterStreamingAssets.LoadAssetBundleAsync(path);
|
||||
|
||||
|
||||
Legal Stuff / Licensing:
|
||||
------------------------
|
||||
Code uses MIT license, as follows:
|
||||
|
||||
The MIT License(MIT)
|
||||
|
||||
Copyright(c) .NET Foundation and Contributors
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c59a049ac5cdb4a48996edcefd9ac9c7
|
||||
timeCreated: 1512873914
|
||||
licenseType: Store
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -348,7 +348,7 @@ namespace OpenMetaverse.Imaging
|
||||
{
|
||||
try
|
||||
{
|
||||
Bitmap bitmap = null;
|
||||
Texture2D bitmap = null;
|
||||
lock (ResourceSync)
|
||||
{
|
||||
using (Stream stream = Helpers.GetResourceStream(fileName, Settings.RESOURCE_DIR))
|
||||
@@ -356,8 +356,8 @@ namespace OpenMetaverse.Imaging
|
||||
if (stream != null)
|
||||
{
|
||||
|
||||
var tex = LoadTGAClass.LoadTGA(stream);
|
||||
bitmap = new Bitmap(tex);
|
||||
bitmap = LoadTGAClass.LoadTGA(stream);
|
||||
//bitmap = new Texture2D(tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,14 +129,14 @@ namespace OpenMetaverse.Imaging
|
||||
/// <param name="bitmap"></param>
|
||||
///
|
||||
//apparently this method only loads the image (.tga) as a bitmap, then returns it as a managed image. the bitmap is not used. perhaps we can skip this intermediary?
|
||||
public ManagedImage(Bitmap tex)
|
||||
public ManagedImage(Texture2D tex)
|
||||
{
|
||||
Width = tex.Width;
|
||||
Height = tex.Height;
|
||||
Width = tex.width;
|
||||
Height = tex.height;
|
||||
|
||||
int pixelCount = Width * Height;
|
||||
|
||||
if (tex.Format == TextureFormat.ARGB32) //PixelFormat.Format32bppArgb --- 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue
|
||||
if (tex.format == TextureFormat.ARGB32) //PixelFormat.Format32bppArgb --- 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue
|
||||
{
|
||||
Channels = ImageChannels.Alpha | ImageChannels.Color;
|
||||
Red = new byte[pixelCount];
|
||||
@@ -180,7 +180,7 @@ namespace OpenMetaverse.Imaging
|
||||
|
||||
// throw new NotImplementedException("16bpp grayscale image support is incomplete");
|
||||
//}
|
||||
else if (tex.Format == TextureFormat.RGB24) //== PixelFormat.Format24bppRgb)
|
||||
else if (tex.format == TextureFormat.RGB24) //== PixelFormat.Format24bppRgb)
|
||||
{
|
||||
Channels = ImageChannels.Color;
|
||||
Red = new byte[pixelCount];
|
||||
@@ -207,8 +207,8 @@ namespace OpenMetaverse.Imaging
|
||||
|
||||
for (int i = 0; i < pixelCount; i++)
|
||||
{
|
||||
int _x = i % Width;
|
||||
int _y = i / Width;
|
||||
//int _x = i % Width;
|
||||
//int _y = i / Width;
|
||||
Color32 bit = tex.GetPixel(i % Width, i / Width);
|
||||
Blue[i] = bit.b;
|
||||
Green[i] = bit.g;
|
||||
@@ -216,7 +216,7 @@ namespace OpenMetaverse.Imaging
|
||||
|
||||
}
|
||||
}
|
||||
else if (tex.Format == TextureFormat.RGB24) // PixelFormat.Format32bppRgb) --- The remaining 8 bits are not used.
|
||||
else if (tex.format == TextureFormat.RGB24) // PixelFormat.Format32bppRgb) --- The remaining 8 bits are not used.
|
||||
{
|
||||
Channels = ImageChannels.Color;
|
||||
Red = new byte[pixelCount];
|
||||
@@ -253,7 +253,7 @@ namespace OpenMetaverse.Imaging
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Unrecognized pixel format: " + tex.Format.ToString());
|
||||
throw new NotSupportedException("Unrecognized pixel format: " + tex.format.ToString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -519,19 +519,19 @@ namespace OpenMetaverse.Imaging
|
||||
/// <param name="bitmap">The source <seealso cref="System.Drawing.Bitmap"/> object to encode</param>
|
||||
/// <param name="lossless">true to enable lossless decoding</param>
|
||||
/// <returns>A byte array containing the source Bitmap object</returns>
|
||||
public unsafe static byte[] EncodeFromImage(Bitmap bitmap, bool lossless)
|
||||
public unsafe static byte[] EncodeFromImage(Texture2D bitmap, bool lossless)
|
||||
{
|
||||
NativeArray<Color32> bd = bitmap.getAsNativeArray();
|
||||
NativeArray<Color32> bd = bitmap.GetRawTextureData<Color32>();//bitmap.getAsNativeArray();
|
||||
|
||||
ManagedImage decoded;
|
||||
|
||||
int bitmapWidth = bitmap.Width;
|
||||
int bitmapHeight = bitmap.Height;
|
||||
int bitmapWidth = bitmap.width;
|
||||
int bitmapHeight = bitmap.height;
|
||||
int pixelCount = bitmapWidth * bitmapHeight;
|
||||
int i;
|
||||
|
||||
//if ((bitmap.Format & PixelFormat.Alpha) != 0 || (bitmap.PixelFormat & PixelFormat.PAlpha) != 0)
|
||||
if (bitmap.Format.Equals(TextureFormat.ARGB32) )
|
||||
if (bitmap.format.Equals(TextureFormat.ARGB32) )
|
||||
{
|
||||
// Four layers, RGBA
|
||||
decoded = new ManagedImage(bitmapWidth, bitmapHeight,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
// */
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
//using System.Drawing;
|
||||
//using System.Drawing.Imaging;
|
||||
//using Catnip.Drawing;
|
||||
@@ -129,7 +130,13 @@ namespace OpenMetaverse.Imaging
|
||||
}
|
||||
}
|
||||
|
||||
public static Texture2D LoadTGA(System.IO.Stream source)
|
||||
public static bool isRLE(string path)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static Texture2D LoadTGAOld(System.IO.Stream source)
|
||||
{
|
||||
byte[] buffer = new byte[source.Length];
|
||||
source.Read(buffer, 0, buffer.Length);
|
||||
@@ -147,23 +154,60 @@ namespace OpenMetaverse.Imaging
|
||||
header.ImageSpec.PixelDepth != 32)
|
||||
throw new ArgumentException("Not a supported tga file.");
|
||||
|
||||
if (header.ImageSpec.PixelDepth == 8 ||
|
||||
header.ImageSpec.PixelDepth != 16)
|
||||
throw new ArgumentException("TGA texture had either 8 or 16 bit depth.");
|
||||
//if (header.ImageSpec.PixelDepth == 8)
|
||||
// throw new ArgumentException("TGA texture had 8 bit depth.");
|
||||
//if (header.ImageSpec.PixelDepth == 16)
|
||||
// throw new ArgumentException("TGA texture had 16 bit depth.");
|
||||
|
||||
if (header.ImageSpec.AlphaBits > 8)
|
||||
throw new ArgumentException("Not a supported tga file.");
|
||||
throw new ArgumentException("Not a supported tga file: too many Alpha bits");
|
||||
|
||||
if (header.ImageSpec.Width > 4096 ||
|
||||
header.ImageSpec.Height > 4096)
|
||||
throw new ArgumentException("Image too large.");
|
||||
|
||||
|
||||
long len = br.BaseStream.Length;
|
||||
//if (header.ImageSpec.PixelDepth == 24)
|
||||
//{
|
||||
// long expectedBytes = header.ImageSpec.Width * header.ImageSpec.Height * 3;
|
||||
// if (len < expectedBytes)
|
||||
// {
|
||||
// //throw new ArgumentException("TGA texture has 24 bit depth, height and width of " +header.ImageSpec.Width+ " " + header.ImageSpec.Height+ " but the number of bytes in file is " + len);
|
||||
// throw new ArgumentException("the 24bit TGA file is smaller than expected");
|
||||
// }
|
||||
|
||||
//}
|
||||
//if (header.ImageSpec.PixelDepth == 32)
|
||||
//{
|
||||
// long expectedBytes = header.ImageSpec.Width * header.ImageSpec.Height * 4;
|
||||
// if (len < expectedBytes)
|
||||
// {
|
||||
// //throw new ArgumentException("TGA texture has 32 bit depth, height and width of " +header.ImageSpec.Width+ " " + header.ImageSpec.Height+ " but the number of bytes in file is " + len);
|
||||
// throw new ArgumentException("the 32bit TGA file is smaller than expected");
|
||||
// }
|
||||
|
||||
//}
|
||||
//if (header.ImageSpec.PixelDepth == 8)
|
||||
//{
|
||||
// long expectedBytes = header.ImageSpec.Width * header.ImageSpec.Height * 1;
|
||||
// if (len < expectedBytes)
|
||||
// {
|
||||
// //throw new ArgumentException("TGA texture has 32 bit depth, height and width of " +header.ImageSpec.Width+ " " + header.ImageSpec.Height+ " but the number of bytes in file is " + len);
|
||||
// throw new ArgumentException("the 8bit TGA file is smaller than expected");
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
|
||||
Texture2D b;
|
||||
int width = header.ImageSpec.Width;
|
||||
int height = header.ImageSpec.Height;
|
||||
Color32[] pulledColors = new Color32[width * height];
|
||||
//BitmapData bd;
|
||||
|
||||
//NOTE NON COMPRESSED textures only!!!
|
||||
|
||||
// Create a bitmap for the image.
|
||||
// Only include an alpha layer when the image requires one.
|
||||
if (header.ImageSpec.AlphaBits > 0 ||
|
||||
@@ -180,15 +224,10 @@ namespace OpenMetaverse.Imaging
|
||||
//bd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
|
||||
// ImageLockMode.WriteOnly,
|
||||
// PixelFormat.Format32bppPArgb);
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
byte red = br.ReadByte();
|
||||
byte green = br.ReadByte();
|
||||
byte blue = br.ReadByte();
|
||||
byte alpha = br.ReadByte();
|
||||
|
||||
pulledColors[i] = new Color32(blue, green, red, alpha);
|
||||
}
|
||||
//Debug.Log("Debug the color: " + pulledColors[5]); //ok we know these are correct.
|
||||
//Debug.Log("Debug the color: " + pulledColors[500]);
|
||||
//Debug.Log("Debug the color: " + pulledColors[50000]);
|
||||
}
|
||||
else
|
||||
{ // Image does not need an alpha layer, so do not include one.
|
||||
@@ -202,6 +241,24 @@ namespace OpenMetaverse.Imaging
|
||||
//bd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
|
||||
// ImageLockMode.WriteOnly,
|
||||
// PixelFormat.Format32bppRgb);
|
||||
|
||||
}
|
||||
|
||||
if (header.ImageSpec.PixelDepth == 8)
|
||||
{
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
byte red = br.ReadByte();
|
||||
|
||||
pulledColors[i] = new Color32(0, 0, 0, red);
|
||||
}
|
||||
|
||||
} else if (header.ImageSpec.PixelDepth == 16)
|
||||
{
|
||||
throw new ArgumentException("TGA texture had 16 bit depth.");
|
||||
|
||||
} else if (header.ImageSpec.PixelDepth == 24) //TODO: handle the case of 'alpha channel is present'!
|
||||
{
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
byte red = br.ReadByte();
|
||||
@@ -211,8 +268,19 @@ namespace OpenMetaverse.Imaging
|
||||
pulledColors[i] = new Color32(blue, green, red, 1);
|
||||
}
|
||||
|
||||
}
|
||||
} else if (header.ImageSpec.PixelDepth == 32)
|
||||
{
|
||||
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
byte red = br.ReadByte();
|
||||
byte green = br.ReadByte();
|
||||
byte blue = br.ReadByte();
|
||||
byte alpha = br.ReadByte();
|
||||
|
||||
pulledColors[i] = new Color32(blue, green, red, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
//switch (header.ImageSpec.PixelDepth)
|
||||
//{
|
||||
@@ -241,6 +309,287 @@ namespace OpenMetaverse.Imaging
|
||||
//}
|
||||
|
||||
//b.UnlockBits(bd);
|
||||
b.SetPixels32(pulledColors);
|
||||
b.Apply();
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
static void decodeStandard8(
|
||||
Color32[] b, int texWidth, int texHeight,
|
||||
tgaHeader hdr,
|
||||
System.IO.BinaryReader br)
|
||||
{
|
||||
tgaCD cd = new tgaCD();
|
||||
cd.RMask = 0x000000ff;
|
||||
cd.GMask = 0x000000ff;
|
||||
cd.BMask = 0x000000ff;
|
||||
cd.AMask = 0x000000ff;
|
||||
cd.RShift = 0;
|
||||
cd.GShift = 0;
|
||||
cd.BShift = 0;
|
||||
cd.AShift = 0;
|
||||
cd.FinalOr = 0x00000000;
|
||||
if (hdr.RleEncoded)
|
||||
decodeRle(b, texWidth, texHeight, 1, cd, br, hdr.ImageSpec.BottomUp);
|
||||
else
|
||||
decodePlain(b, texWidth, texHeight, 1, cd, br, hdr.ImageSpec.BottomUp);
|
||||
}
|
||||
|
||||
static void decodeSpecial16(
|
||||
Color32[] b, int texWidth, int texHeight, tgaHeader hdr, System.IO.BinaryReader br)
|
||||
{
|
||||
// i must convert the input stream to a sequence of uint values
|
||||
// which I then unpack.
|
||||
tgaCD cd = new tgaCD();
|
||||
cd.RMask = 0x00f00000;
|
||||
cd.GMask = 0x0000f000;
|
||||
cd.BMask = 0x000000f0;
|
||||
cd.AMask = 0xf0000000;
|
||||
cd.RShift = 12;
|
||||
cd.GShift = 8;
|
||||
cd.BShift = 4;
|
||||
cd.AShift = 16;
|
||||
cd.FinalOr = 0;
|
||||
|
||||
if (hdr.RleEncoded)
|
||||
decodeRle(b, texWidth, texHeight, 2, cd, br, hdr.ImageSpec.BottomUp);
|
||||
else
|
||||
decodePlain(b, texWidth, texHeight, 2, cd, br, hdr.ImageSpec.BottomUp);
|
||||
}
|
||||
|
||||
static void decodeStandard16(
|
||||
Color32[] b, int texWidth, int texHeight,
|
||||
tgaHeader hdr,
|
||||
System.IO.BinaryReader br)
|
||||
{
|
||||
// i must convert the input stream to a sequence of uint values
|
||||
// which I then unpack.
|
||||
tgaCD cd = new tgaCD();
|
||||
cd.RMask = 0x00f80000; // from 0xF800
|
||||
cd.GMask = 0x0000fc00; // from 0x07E0
|
||||
cd.BMask = 0x000000f8; // from 0x001F
|
||||
cd.AMask = 0x00000000;
|
||||
cd.RShift = 8;
|
||||
cd.GShift = 5;
|
||||
cd.BShift = 3;
|
||||
cd.AShift = 0;
|
||||
cd.FinalOr = 0xff000000;
|
||||
|
||||
if (hdr.RleEncoded)
|
||||
decodeRle(b, texWidth, texHeight, 2, cd, br, hdr.ImageSpec.BottomUp);
|
||||
else
|
||||
decodePlain(b, texWidth, texHeight, 2, cd, br, hdr.ImageSpec.BottomUp);
|
||||
}
|
||||
|
||||
|
||||
static void decodeSpecial24(Color32[] b, int texWidth, int texHeight,
|
||||
tgaHeader hdr, System.IO.BinaryReader br)
|
||||
{
|
||||
// i must convert the input stream to a sequence of uint values
|
||||
// which I then unpack.
|
||||
tgaCD cd = new tgaCD();
|
||||
cd.RMask = 0x00f80000;
|
||||
cd.GMask = 0x0000fc00;
|
||||
cd.BMask = 0x000000f8;
|
||||
cd.AMask = 0xff000000;
|
||||
cd.RShift = 8;
|
||||
cd.GShift = 5;
|
||||
cd.BShift = 3;
|
||||
cd.AShift = 8;
|
||||
cd.FinalOr = 0;
|
||||
|
||||
if (hdr.RleEncoded)
|
||||
decodeRle(b, texWidth, texHeight, 3, cd, br, hdr.ImageSpec.BottomUp);
|
||||
else
|
||||
decodePlain(b, texWidth, texHeight, 3, cd, br, hdr.ImageSpec.BottomUp);
|
||||
}
|
||||
|
||||
static void decodeStandard24(Color32[] b, int texWidth, int texHeight,
|
||||
tgaHeader hdr, System.IO.BinaryReader br)
|
||||
{
|
||||
// i must convert the input stream to a sequence of uint values
|
||||
// which I then unpack.
|
||||
tgaCD cd = new tgaCD();
|
||||
cd.RMask = 0x00ff0000;
|
||||
cd.GMask = 0x0000ff00;
|
||||
cd.BMask = 0x000000ff;
|
||||
cd.AMask = 0x00000000;
|
||||
cd.RShift = 0;
|
||||
cd.GShift = 0;
|
||||
cd.BShift = 0;
|
||||
cd.AShift = 0;
|
||||
cd.FinalOr = 0xff000000;
|
||||
|
||||
if (hdr.RleEncoded)
|
||||
decodeRle(b, texWidth, texHeight, 3, cd, br, hdr.ImageSpec.BottomUp);
|
||||
else
|
||||
decodePlain(b, texWidth, texHeight, 3, cd, br, hdr.ImageSpec.BottomUp);
|
||||
}
|
||||
|
||||
static void decodeStandard32(Color32[] b, int texWidth, int texHeight,
|
||||
tgaHeader hdr, System.IO.BinaryReader br)
|
||||
{
|
||||
// i must convert the input stream to a sequence of uint values
|
||||
// which I then unpack.
|
||||
tgaCD cd = new tgaCD();
|
||||
cd.RMask = 0x00ff0000;
|
||||
cd.GMask = 0x0000ff00;
|
||||
cd.BMask = 0x000000ff;
|
||||
cd.AMask = 0xff000000;
|
||||
cd.RShift = 0;
|
||||
cd.GShift = 0;
|
||||
cd.BShift = 0;
|
||||
cd.AShift = 0;
|
||||
cd.FinalOr = 0x00000000;
|
||||
cd.NeedNoConvert = true;
|
||||
|
||||
if (hdr.RleEncoded)
|
||||
decodeRle(b, texWidth, texHeight, 4, cd, br, hdr.ImageSpec.BottomUp);
|
||||
else
|
||||
decodePlain(b, texWidth, texHeight, 4, cd, br, hdr.ImageSpec.BottomUp);
|
||||
}
|
||||
|
||||
//load the tga and get a texture using a TGA byte array.
|
||||
public static Texture2D LoadTGA(byte[] source)
|
||||
{
|
||||
|
||||
System.IO.MemoryStream stream = new System.IO.MemoryStream();
|
||||
stream.Write(source, 0, source.Length);
|
||||
stream.Seek(0, SeekOrigin.Begin); //FUCKKK
|
||||
|
||||
return LoadTGA(stream); // better refactor this mess later
|
||||
|
||||
}
|
||||
public static Texture2D LoadTGA(System.IO.Stream source)
|
||||
{
|
||||
//byte[] buffer = new byte[source.Length];
|
||||
//int _readbytes = source.Read(buffer, 0, buffer.Length);
|
||||
|
||||
//System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer);
|
||||
|
||||
using (System.IO.BinaryReader br = new System.IO.BinaryReader(source))
|
||||
{
|
||||
tgaHeader header = new tgaHeader();
|
||||
header.Read(br);
|
||||
|
||||
if (header.ImageSpec.PixelDepth != 8 &&
|
||||
header.ImageSpec.PixelDepth != 16 &&
|
||||
header.ImageSpec.PixelDepth != 24 &&
|
||||
header.ImageSpec.PixelDepth != 32)
|
||||
throw new ArgumentException("Not a supported tga file.");
|
||||
|
||||
//if (header.ImageSpec.PixelDepth == 8)
|
||||
// throw new ArgumentException("TGA texture had 8 bit depth.");
|
||||
if (header.ImageSpec.PixelDepth == 16)
|
||||
throw new ArgumentException("TGA texture had 16 bit depth.");
|
||||
|
||||
if (header.ImageSpec.AlphaBits > 8)
|
||||
throw new ArgumentException("Not a supported tga file: too many Alpha bits");
|
||||
|
||||
if (header.ImageSpec.Width > 4096 ||
|
||||
header.ImageSpec.Height > 4096)
|
||||
throw new ArgumentException("Image too large.");
|
||||
|
||||
|
||||
//long len = br.BaseStream.Length;
|
||||
//if (header.ImageSpec.PixelDepth == 24)
|
||||
//{
|
||||
// long expectedBytes = header.ImageSpec.Width * header.ImageSpec.Height * 3;
|
||||
// if (len < expectedBytes)
|
||||
// {
|
||||
// //throw new ArgumentException("TGA texture has 24 bit depth, height and width of " +header.ImageSpec.Width+ " " + header.ImageSpec.Height+ " but the number of bytes in file is " + len);
|
||||
// throw new ArgumentException("the 24bit TGA file is smaller than expected");
|
||||
// }
|
||||
|
||||
//}
|
||||
//if (header.ImageSpec.PixelDepth == 32)
|
||||
//{
|
||||
// long expectedBytes = header.ImageSpec.Width * header.ImageSpec.Height * 4;
|
||||
// if (len < expectedBytes)
|
||||
// {
|
||||
// //throw new ArgumentException("TGA texture has 32 bit depth, height and width of " +header.ImageSpec.Width+ " " + header.ImageSpec.Height+ " but the number of bytes in file is " + len);
|
||||
// throw new ArgumentException("the 32bit TGA file is smaller than expected");
|
||||
// }
|
||||
|
||||
//}
|
||||
//if (header.ImageSpec.PixelDepth == 8)
|
||||
//{
|
||||
// long expectedBytes = header.ImageSpec.Width * header.ImageSpec.Height * 1;
|
||||
// if (len < expectedBytes)
|
||||
// {
|
||||
// //throw new ArgumentException("TGA texture has 32 bit depth, height and width of " +header.ImageSpec.Width+ " " + header.ImageSpec.Height+ " but the number of bytes in file is " + len);
|
||||
// throw new ArgumentException("the 8bit TGA file is smaller than expected");
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
|
||||
Texture2D b;
|
||||
int width = header.ImageSpec.Width;
|
||||
int height = header.ImageSpec.Height;
|
||||
Color32[] pulledColors = new Color32[width * height];
|
||||
|
||||
//should support compressed texture too!
|
||||
|
||||
// Create a bitmap for the image.
|
||||
// Only include an alpha layer when the image requires one.
|
||||
if (header.ImageSpec.AlphaBits > 0 ||
|
||||
header.ImageSpec.PixelDepth == 8 || // Assume 8 bit images are alpha only
|
||||
header.ImageSpec.PixelDepth == 32) // Assume 32 bit images are ARGB
|
||||
{ // Image needs an alpha layer
|
||||
b = new Texture2D(
|
||||
header.ImageSpec.Width,
|
||||
header.ImageSpec.Height,
|
||||
TextureFormat.ARGB32,
|
||||
false
|
||||
/*PixelFormat.Format32bppArgb*/);
|
||||
|
||||
//Debug.Log("Debug the color: " + pulledColors[5]); //ok we know these are correct.
|
||||
//Debug.Log("Debug the color: " + pulledColors[500]);
|
||||
//Debug.Log("Debug the color: " + pulledColors[50000]);
|
||||
}
|
||||
else
|
||||
{ // Image does not need an alpha layer, so do not include one.
|
||||
b = new Texture2D(
|
||||
header.ImageSpec.Width,
|
||||
header.ImageSpec.Height,
|
||||
TextureFormat.RGB24,
|
||||
false
|
||||
/*PixelFormat.Format32bppRgb*/);
|
||||
|
||||
}
|
||||
|
||||
switch (header.ImageSpec.PixelDepth)
|
||||
{
|
||||
case 8:
|
||||
decodeStandard8(pulledColors, width,height, header, br);
|
||||
break;
|
||||
case 16:
|
||||
if (header.ImageSpec.AlphaBits > 0)
|
||||
decodeSpecial16(pulledColors, width, height, header, br);
|
||||
else
|
||||
decodeStandard16(pulledColors, width, height, header, br);
|
||||
break;
|
||||
case 24:
|
||||
if (header.ImageSpec.AlphaBits > 0)
|
||||
decodeSpecial24(pulledColors, width, height, header, br);
|
||||
else
|
||||
decodeStandard24(pulledColors, width, height, header, br);
|
||||
break;
|
||||
case 32:
|
||||
decodeStandard32(pulledColors, width, height, header, br);
|
||||
break;
|
||||
default:
|
||||
//b.UnlockBits(bd);
|
||||
//b.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
b.SetPixels32(pulledColors);
|
||||
b.Apply();
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
||||
@@ -267,6 +616,187 @@ namespace OpenMetaverse.Imaging
|
||||
return null; // file not found
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct tgaCD
|
||||
{
|
||||
public uint RMask, GMask, BMask, AMask;
|
||||
public byte RShift, GShift, BShift, AShift;
|
||||
public uint FinalOr;
|
||||
public bool NeedNoConvert;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// decodes the non-RLE version of tga files.
|
||||
/// </summary>
|
||||
/// <param name="b">output reference to texture/bmp</param>
|
||||
/// <param name="byp">bytes-per-pixel</param>
|
||||
/// <param name="cd">???</param>
|
||||
/// <param name="br">binary reader that is reading the data file </param>
|
||||
/// <param name="bottomUp">if the image sh. </param>
|
||||
static void decodePlain(
|
||||
Color32[] b, int texWidth, int texHeight,
|
||||
int byp, tgaCD cd, System.IO.BinaryReader br, bool bottomUp)
|
||||
{
|
||||
int w = texWidth;
|
||||
byte[] linebuffer = new byte[w * byp];
|
||||
|
||||
for (int j = 0; j < texHeight; ++j)
|
||||
{
|
||||
br.Read(linebuffer, 0, w * byp);
|
||||
|
||||
if (!bottomUp)
|
||||
decodeLine(b, texWidth, texHeight, j, byp, linebuffer, ref cd);
|
||||
else
|
||||
decodeLine(b, texWidth, texHeight, texHeight - j - 1, byp, linebuffer, ref cd);
|
||||
}
|
||||
}
|
||||
|
||||
static void decodeRle(
|
||||
Color32[] b, int texWidth, int texHeight,
|
||||
int byp, tgaCD cd, System.IO.BinaryReader br, bool bottomUp)
|
||||
{
|
||||
try
|
||||
{
|
||||
int w = texWidth;
|
||||
// make buffer larger, so in case of emergency I can decode
|
||||
// over line ends.
|
||||
byte[] linebuffer = new byte[(w + 128) * byp];
|
||||
int maxindex = w * byp;
|
||||
int index = 0;
|
||||
|
||||
for (int j = 0; j < texHeight; ++j)
|
||||
{
|
||||
while (index < maxindex)
|
||||
{
|
||||
byte blocktype = br.ReadByte(); //MSB of blocktype: 1 means Raw packet(non-RLE), 0 means RLE packet
|
||||
|
||||
int bytestoread;
|
||||
int bytestocopy;
|
||||
|
||||
if (blocktype >= 0x80) // run-length packet. - read 1 color and replicate them by bytestocopy
|
||||
{
|
||||
int pixel_count_minus_1 = (blocktype - 0x80);
|
||||
bytestoread = byp; // pixel data
|
||||
bytestocopy = byp * pixel_count_minus_1;
|
||||
}
|
||||
else //raw packet (non run-lenght encoding.)
|
||||
{
|
||||
bytestoread = byp * (blocktype + 1);
|
||||
bytestocopy = 0;
|
||||
}
|
||||
|
||||
//if (index + bytestoread > maxindex)
|
||||
// throw new System.ArgumentException ("Corrupt TGA");
|
||||
|
||||
br.Read(linebuffer, index, bytestoread);
|
||||
index += bytestoread;
|
||||
|
||||
for (int i = 0; i != bytestocopy; ++i)
|
||||
{
|
||||
linebuffer[index + i] = linebuffer[index + i - bytestoread];
|
||||
}
|
||||
index += bytestocopy;
|
||||
}
|
||||
if (!bottomUp)
|
||||
decodeLine(b, texWidth, texHeight, j, byp, linebuffer, ref cd);
|
||||
else
|
||||
decodeLine(b, texWidth, texHeight, texHeight - j - 1, byp, linebuffer, ref cd);
|
||||
|
||||
if (index > maxindex)
|
||||
{
|
||||
Array.Copy(linebuffer, maxindex, linebuffer, 0, index - maxindex);
|
||||
index -= maxindex;
|
||||
}
|
||||
else
|
||||
index = 0;
|
||||
|
||||
}
|
||||
}
|
||||
catch (System.IO.EndOfStreamException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
static public Color32 makeColor32fromUInt(uint x)
|
||||
{
|
||||
return new Color32( (byte)( (x & 0x00FF0000) >> 16),
|
||||
(byte)( (x & 0x0000FF00) >> 8),
|
||||
(byte)(( x & 0x000000FF) >> 0),
|
||||
(byte)((x & 0xFF000000) >> 24)); //alpha
|
||||
}
|
||||
|
||||
static uint UnpackColor(
|
||||
uint sourceColor, ref tgaCD cd)
|
||||
{
|
||||
if (cd.RMask == 0xFF && cd.GMask == 0xFF && cd.BMask == 0xFF)
|
||||
{
|
||||
// Special case to deal with 8-bit TGA files that we treat as alpha masks
|
||||
return sourceColor << 24;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint rpermute = (sourceColor << cd.RShift) | (sourceColor >> (32 - cd.RShift));
|
||||
uint gpermute = (sourceColor << cd.GShift) | (sourceColor >> (32 - cd.GShift));
|
||||
uint bpermute = (sourceColor << cd.BShift) | (sourceColor >> (32 - cd.BShift));
|
||||
uint apermute = (sourceColor << cd.AShift) | (sourceColor >> (32 - cd.AShift));
|
||||
uint result =
|
||||
(rpermute & cd.RMask) | (gpermute & cd.GMask)
|
||||
| (bpermute & cd.BMask) | (apermute & cd.AMask) | cd.FinalOr;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static unsafe void decodeLine(
|
||||
Color32[] b, int texWidth, int texHeight,
|
||||
int line,
|
||||
int byp,
|
||||
byte[] data,
|
||||
ref tgaCD cd)
|
||||
{
|
||||
if (cd.NeedNoConvert)
|
||||
{
|
||||
// fast copy
|
||||
uint offset_colorArray_scanline = (uint)(line * texWidth + 0); //should be large enough?
|
||||
//uint* linep = (uint*)((byte*)b.Scan0.ToPointer() + line * b.Stride);
|
||||
fixed (byte* ptr = data)
|
||||
{
|
||||
uint* sptr = (uint*)ptr;
|
||||
for (int i = 0; i < texWidth; ++i)
|
||||
{
|
||||
b[i + offset_colorArray_scanline] = makeColor32fromUInt(sptr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//byte* linep = (byte*)b.Scan0.ToPointer() + line * b.Stride;
|
||||
uint offset_colorArray_scanline = (uint)(line * texWidth + 0);
|
||||
|
||||
//uint* up = (uint*)linep;
|
||||
|
||||
int rdi = 0;
|
||||
|
||||
fixed (byte* ptr = data)
|
||||
{
|
||||
for (int i = 0; i < texWidth; ++i)
|
||||
{
|
||||
uint x = 0;
|
||||
for (int j = 0; j < byp; ++j)
|
||||
{
|
||||
x |= ((uint)ptr[rdi]) << (j << 3); //load all bytes that represent the pixel's color into register x.
|
||||
++rdi;
|
||||
}
|
||||
uint unpackedColoByte = UnpackColor(x, ref cd);
|
||||
b[i+line*texWidth] = makeColor32fromUInt(unpackedColoByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,6 +28,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Catnip.Drawing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenMetaverse.Rendering
|
||||
{
|
||||
@@ -68,7 +69,7 @@ namespace OpenMetaverse.Rendering
|
||||
/// <param name="sculptTexture">Sculpt texture</param>
|
||||
/// <param name="lod">Level of detail to generate the mesh at</param>
|
||||
/// <returns>The generated mesh</returns>
|
||||
SimpleMesh GenerateSimpleSculptMesh(Primitive prim, Bitmap sculptTexture, DetailLevel lod);
|
||||
SimpleMesh GenerateSimpleSculptMesh(Primitive prim, Texture2D sculptTexture, DetailLevel lod);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a series of faces, each face containing a mesh and
|
||||
@@ -87,7 +88,7 @@ namespace OpenMetaverse.Rendering
|
||||
/// <param name="sculptTexture">Sculpt texture</param>
|
||||
/// <param name="lod">Level of detail to generate the mesh at</param>
|
||||
/// <returns>The generated mesh</returns>
|
||||
FacetedMesh GenerateFacetedSculptMesh(Primitive prim, Bitmap sculptTexture, DetailLevel lod);
|
||||
FacetedMesh GenerateFacetedSculptMesh(Primitive prim, Texture2D sculptTexture, DetailLevel lod);
|
||||
|
||||
/// <summary>
|
||||
/// Apply texture coordinate modifications from a
|
||||
|
||||
@@ -40,6 +40,7 @@ using LibreMetaverse.PrimMesher;
|
||||
using OMV = OpenMetaverse;
|
||||
using OMVR = OpenMetaverse.Rendering;
|
||||
using Catnip.Drawing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenMetaverse.Rendering
|
||||
{
|
||||
@@ -93,7 +94,7 @@ namespace OpenMetaverse.Rendering
|
||||
/// <param name="sculptTexture">Sculpt texture</param>
|
||||
/// <param name="lod">Level of detail to generate the mesh at</param>
|
||||
/// <returns>The generated mesh or null on failure</returns>
|
||||
public SimpleMesh GenerateSimpleSculptMesh(Primitive prim, Bitmap sculptTexture, DetailLevel lod)
|
||||
public SimpleMesh GenerateSimpleSculptMesh(Primitive prim, Texture2D sculptTexture, DetailLevel lod)
|
||||
{
|
||||
var faceted = GenerateFacetedSculptMesh(prim, sculptTexture, lod);
|
||||
|
||||
@@ -188,7 +189,7 @@ namespace OpenMetaverse.Rendering
|
||||
/// routine since all the context for finding teh texture is elsewhere.
|
||||
/// </summary>
|
||||
/// <returns>The faceted mesh or null if can't do it</returns>
|
||||
public OMVR.FacetedMesh GenerateFacetedSculptMesh(Primitive prim, Bitmap scupltTexture, DetailLevel lod)
|
||||
public OMVR.FacetedMesh GenerateFacetedSculptMesh(Primitive prim, Texture2D scupltTexture, DetailLevel lod)
|
||||
{
|
||||
LibreMetaverse.PrimMesher.SculptMesh.SculptType smSculptType;
|
||||
switch (prim.Sculpt.Type)
|
||||
|
||||
@@ -115,9 +115,9 @@ namespace OpenMetaverse
|
||||
/// <param name="pos">Starting position of the UUID in the byte array</param>
|
||||
public void FromBytes(byte[] source, int pos)
|
||||
{
|
||||
int a = (source[pos + 0] << 24) | (source[pos + 1] << 16) | (source[pos + 2] << 8) | source[pos + 3];
|
||||
short b = (short)((source[pos + 4] << 8) | source[pos + 5]);
|
||||
short c = (short)((source[pos + 6] << 8) | source[pos + 7]);
|
||||
int a = (source[pos + 0] << 24) | (source[pos + 1] << 16) | (source[pos + 2] << 8) | source[pos + 3]; //4 bytes aka 32bits aka int
|
||||
short b = (short)((source[pos + 4] << 8) | source[pos + 5]); // 2 bytes aka 16 bits
|
||||
short c = (short)((source[pos + 6] << 8) | source[pos + 7]); // 2 bytes aka 16 bits
|
||||
|
||||
Guid = new Guid(a, b, c, source[pos + 8], source[pos + 9], source[pos + 10], source[pos + 11],
|
||||
source[pos + 12], source[pos + 13], source[pos + 14], source[pos + 15]);
|
||||
|
||||
@@ -1084,7 +1084,7 @@ namespace OpenMetaverse
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
// actually perform the login.
|
||||
private void BeginLogin()
|
||||
{
|
||||
LoginParams loginParams = CurrentContext;
|
||||
|
||||
@@ -30,6 +30,7 @@ using System.Collections.Generic;
|
||||
//using System.Drawing;
|
||||
//using System.Drawing.Drawing2D;
|
||||
using Catnip.Drawing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LibreMetaverse.PrimMesher
|
||||
{
|
||||
@@ -45,10 +46,10 @@ namespace LibreMetaverse.PrimMesher
|
||||
{
|
||||
}
|
||||
|
||||
public SculptMap(Bitmap bm, int lod)
|
||||
public SculptMap(Texture2D bm, int lod)
|
||||
{
|
||||
var bmW = bm.Width;
|
||||
var bmH = bm.Height;
|
||||
var bmW = bm.width;
|
||||
var bmH = bm.height;
|
||||
|
||||
if (bmW == 0 || bmH == 0)
|
||||
throw new Exception("SculptMap: bitmap has no data");
|
||||
@@ -72,7 +73,7 @@ namespace LibreMetaverse.PrimMesher
|
||||
try
|
||||
{
|
||||
if (needsScaling)
|
||||
bm.resize(width,height);
|
||||
bm.Resize(width,height);
|
||||
//bm = ScaleImage(bm, width, height,
|
||||
// InterpolationMode.NearestNeighbor);
|
||||
}
|
||||
@@ -101,7 +102,7 @@ namespace LibreMetaverse.PrimMesher
|
||||
for (var y = 0; y < height; y++)
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
var c = bm.GetPixel(x, y);
|
||||
Color32 c = bm.GetPixel(x, y);
|
||||
|
||||
redBytes[byteNdx] = c.r;
|
||||
greenBytes[byteNdx] = c.g;
|
||||
@@ -113,7 +114,7 @@ namespace LibreMetaverse.PrimMesher
|
||||
for (var y = 0; y <= height; y++)
|
||||
for (var x = 0; x <= width; x++)
|
||||
{
|
||||
var c = bm.GetPixel(x < width ? x * 2 : x * 2 - 1,
|
||||
Color32 c = bm.GetPixel(x < width ? x * 2 : x * 2 - 1,
|
||||
y < height ? y * 2 : y * 2 - 1);
|
||||
|
||||
redBytes[byteNdx] = c.r;
|
||||
|
||||
@@ -55,22 +55,22 @@ namespace LibreMetaverse.PrimMesher
|
||||
|
||||
public List<ViewerFace> viewerFaces;
|
||||
|
||||
//<Deprecated : Sculptie from image file>
|
||||
//public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert)
|
||||
//{
|
||||
// //var bitmap = (Bitmap) Image.FromFile(fileName);
|
||||
// var myreader = new BMPLoader();
|
||||
// BMPImage myimg = myreader.LoadBMP(fileName);
|
||||
// Texture2D tex = myimg.ToTexture2D();
|
||||
// Texture2D fakebmp = new Texture2D(tex);
|
||||
|
||||
public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert)
|
||||
{
|
||||
//var bitmap = (Bitmap) Image.FromFile(fileName);
|
||||
var myreader = new BMPLoader();
|
||||
BMPImage myimg = myreader.LoadBMP(fileName);
|
||||
Texture2D tex = myimg.ToTexture2D();
|
||||
Bitmap fakebmp = new Bitmap(tex);
|
||||
// _SculptMesh(fakebmp, (SculptType) sculptType, lod, viewerMode != 0, mirror != 0, invert != 0);
|
||||
// //bitmap.Dispose();
|
||||
|
||||
_SculptMesh(fakebmp, (SculptType) sculptType, lod, viewerMode != 0, mirror != 0, invert != 0);
|
||||
//bitmap.Dispose();
|
||||
|
||||
fakebmp.delete();
|
||||
// fakebmp.delete();
|
||||
|
||||
|
||||
}
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications
|
||||
@@ -179,12 +179,12 @@ namespace LibreMetaverse.PrimMesher
|
||||
calcVertexNormals(SculptType.plane, numXElements, numYElements);
|
||||
}
|
||||
|
||||
public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode)
|
||||
public SculptMesh(Texture2D sculptBitmap, SculptType sculptType, int lod, bool viewerMode)
|
||||
{
|
||||
_SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false);
|
||||
}
|
||||
|
||||
public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror,
|
||||
public SculptMesh(Texture2D sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror,
|
||||
bool invert)
|
||||
{
|
||||
_SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert);
|
||||
@@ -308,7 +308,7 @@ namespace LibreMetaverse.PrimMesher
|
||||
//}
|
||||
|
||||
|
||||
private void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror,
|
||||
private void _SculptMesh(Texture2D sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror,
|
||||
bool invert)
|
||||
{
|
||||
_SculptMesh(new SculptMap(sculptBitmap, lod).ToRows(mirror), sculptType, viewerMode, mirror, invert);
|
||||
|
||||
@@ -383,7 +383,7 @@ namespace OpenMetaverse
|
||||
{
|
||||
EconomyDataPacket econ = (EconomyDataPacket)e.Packet;
|
||||
|
||||
priceUpload = econ.Info.PriceUpload;
|
||||
priceUpload = econ.Info.PriceUpload; //TODO: is this thread safe? no lock
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -761,7 +761,7 @@ namespace OpenMetaverse
|
||||
task.TokenSource.Cancel();
|
||||
RemoveTransfer(task.RequestID);
|
||||
|
||||
_Client.Assets.Cache.SaveAssetToCache(task.RequestID, task.Transfer.AssetData);
|
||||
_Client.Assets.Cache.SaveAssetToCache(task.RequestID, task.Transfer.AssetData); //the image is saved to disk in whatever format that it arrived in the packet in.
|
||||
|
||||
foreach (var callback in task.Callbacks)
|
||||
callback(TextureRequestState.Finished, new AssetTexture(task.RequestID, task.Transfer.AssetData));
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Raindrop
|
||||
|
||||
public ChatManager(RaindropInstance instance)
|
||||
{
|
||||
this.instance = instance;
|
||||
instance = this.instance;
|
||||
|
||||
UnityEngine.Debug.Log("chatmanager being constructed");
|
||||
//setup
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Raindrop.Core
|
||||
/// </summary>
|
||||
public void BeginMonitoring()
|
||||
{
|
||||
var client = RaindropInstance.GlobalInstance.Client;
|
||||
var client = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>().Client;
|
||||
client.Inventory.Store.InventoryObjectAdded += Store_InventoryObjectAdded;
|
||||
client.Inventory.Store.InventoryObjectUpdated += Store_InventoryObjectUpdated;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Raindrop.Core
|
||||
/// </summary>
|
||||
public void StopMonitoring()
|
||||
{
|
||||
var client = RaindropInstance.GlobalInstance.Client;
|
||||
var client = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>().Client;
|
||||
client.Inventory.Store.InventoryObjectAdded -= Store_InventoryObjectAdded;
|
||||
client.Inventory.Store.InventoryObjectUpdated -= Store_InventoryObjectUpdated;
|
||||
}
|
||||
@@ -114,7 +114,7 @@ namespace Raindrop.Core
|
||||
private bool ProcessWord(string word, StringBuilder outString)
|
||||
{
|
||||
var possibleTriggers = new List<GestureTrigger>();
|
||||
var client = RaindropInstance.GlobalInstance.Client;
|
||||
var client = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>().Client;
|
||||
var lowerWord = word.ToLower();
|
||||
|
||||
client.Self.ActiveGestures.ForEach(pair =>
|
||||
@@ -169,7 +169,7 @@ namespace Raindrop.Core
|
||||
/// <param name="gesture">Gesture that was added or updated.</param>
|
||||
private void UpdateInventoryGesture(InventoryGesture gesture)
|
||||
{
|
||||
var client = RaindropInstance.GlobalInstance.Client;
|
||||
var client = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>().Client;
|
||||
|
||||
client.Assets.RequestAsset(gesture.AssetUUID, AssetType.Gesture, false, (_, asset) =>
|
||||
{
|
||||
|
||||
@@ -8,13 +8,12 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Zenject;
|
||||
|
||||
namespace Raindrop
|
||||
{
|
||||
class LoginUtils
|
||||
{
|
||||
|
||||
|
||||
public class SavedLogin
|
||||
{
|
||||
public string Username;
|
||||
@@ -59,7 +58,7 @@ namespace Raindrop
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
RaindropInstance instance = RaindropInstance.GlobalInstance;
|
||||
RaindropInstance instance = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
string gridName;
|
||||
if (GridID == "custom_login_uri")
|
||||
{
|
||||
|
||||
@@ -76,8 +76,8 @@ namespace Raindrop
|
||||
}
|
||||
Console.WriteLine();
|
||||
|
||||
if (RaindropInstance.GlobalInstance.GlobalLogFile != null && (!RaindropInstance.GlobalInstance.GlobalSettings.ContainsKey("log_to_file") || RaindropInstance.GlobalInstance.GlobalSettings["log_to_file"]))
|
||||
File.AppendAllText(RaindropInstance.GlobalInstance.GlobalLogFile, RenderLoggingEvent(le) + Environment.NewLine);
|
||||
//if (RaindropInstance.GlobalInstance.GlobalLogFile != null && (!RaindropInstance.GlobalInstance.GlobalSettings.ContainsKey("log_to_file") || RaindropInstance.GlobalInstance.GlobalSettings["log_to_file"]))
|
||||
// File.AppendAllText(RaindropInstance.GlobalInstance.GlobalLogFile, RenderLoggingEvent(le) + Environment.NewLine);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Raindrop
|
||||
/// <returns>Display text for URI</returns>
|
||||
public string GetLinkName(string uri)
|
||||
{
|
||||
if (!RaindropInstance.GlobalInstance.GlobalSettings["resolve_uris"])
|
||||
if (!ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>().GlobalSettings["resolve_uris"])
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
@@ -262,7 +262,7 @@ namespace Raindrop
|
||||
/// <returns>Name of agent on success, INCOMPLETE_NAME on failure or timeout</returns>
|
||||
private string GetAgentName(UUID agentID, ResolveType nameType)
|
||||
{
|
||||
RaindropInstance instance = RaindropInstance.GlobalInstance;
|
||||
RaindropInstance instance = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
string name = RaindropInstance.INCOMPLETE_NAME;
|
||||
|
||||
using (ManualResetEvent gotName = new ManualResetEvent(false))
|
||||
@@ -318,7 +318,7 @@ namespace Raindrop
|
||||
/// <returns>Name of the group on success, INCOMPLETE_NAME on failure or timeout</returns>
|
||||
private string GetGroupName(UUID groupID)
|
||||
{
|
||||
RaindropInstance instance = RaindropInstance.GlobalInstance;
|
||||
RaindropInstance instance = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
string name = RaindropInstance.INCOMPLETE_NAME;
|
||||
|
||||
using (ManualResetEvent gotName = new ManualResetEvent(false))
|
||||
@@ -356,7 +356,7 @@ namespace Raindrop
|
||||
/// <returns>Name of the parcel on success, INCOMPLETE_NAME on failure or timeout</returns>
|
||||
private string GetParcelName(UUID parcelID)
|
||||
{
|
||||
RaindropInstance instance = RaindropInstance.GlobalInstance;
|
||||
RaindropInstance instance = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
string name = RaindropInstance.INCOMPLETE_NAME;
|
||||
|
||||
using (ManualResetEvent gotName = new ManualResetEvent(false))
|
||||
@@ -629,7 +629,7 @@ namespace Raindrop
|
||||
#region Link Execution
|
||||
private void ExecuteLinkRegionUri(Match match)
|
||||
{
|
||||
RaindropInstance instance = RaindropInstance.GlobalInstance;
|
||||
RaindropInstance instance = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
|
||||
string name = HttpUtility.UrlDecode(match.Groups["region_name"].Value);
|
||||
int x = match.Groups["local_x"].Success ? int.Parse(match.Groups["local_x"].Value) : 128;
|
||||
@@ -643,7 +643,7 @@ namespace Raindrop
|
||||
|
||||
private void ExecuteLinkAgent(Match match)
|
||||
{
|
||||
RaindropInstance instance = RaindropInstance.GlobalInstance;
|
||||
RaindropInstance instance = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
UUID agentID = new UUID(match.Groups["agent_id"].Value);
|
||||
//string action = match.Groups["action"].Value;
|
||||
|
||||
@@ -679,7 +679,7 @@ namespace Raindrop
|
||||
|
||||
private void ExecuteLinkGroup(Match match)
|
||||
{
|
||||
RaindropInstance instance = RaindropInstance.GlobalInstance;
|
||||
RaindropInstance instance = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
string action = match.Groups["action"].Value;
|
||||
|
||||
switch (action)
|
||||
@@ -761,7 +761,7 @@ namespace Raindrop
|
||||
|
||||
private void ExecuteLinkWorldMap(Match match)
|
||||
{
|
||||
RaindropInstance instance = RaindropInstance.GlobalInstance;
|
||||
RaindropInstance instance = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
|
||||
string name = HttpUtility.UrlDecode(match.Groups["region_name"].Value);
|
||||
int x = match.Groups["local_x"].Success ? int.Parse(match.Groups["local_x"].Value) : 128;
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
////
|
||||
//// Radegast Metaverse Client
|
||||
//// Copyright (c) 2009-2014, Radegast Development Team
|
||||
//// All rights reserved.
|
||||
////
|
||||
//// Redistribution and use in source and binary forms, with or without
|
||||
//// modification, are permitted provided that the following conditions are met:
|
||||
////
|
||||
//// * Redistributions of source code must retain the above copyright notice,
|
||||
//// this list of conditions and the following disclaimer.
|
||||
//// * Redistributions in binary form must reproduce the above copyright
|
||||
//// notice, this list of conditions and the following disclaimer in the
|
||||
//// documentation and/or other materials provided with the distribution.
|
||||
//// * Neither the name of the application "Radegast", nor the names of its
|
||||
//// contributors may be used to endorse or promote products derived from
|
||||
//// this software without specific prior written permission.
|
||||
////
|
||||
//// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
//// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
//// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
//// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
//// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
//// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
//// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
//// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
//// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
//// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
////
|
||||
//// $Id$
|
||||
////
|
||||
//using OpenMetaverse;
|
||||
//using Raindrop.Media;
|
||||
//using Raindrop.Netcom;
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Threading;
|
||||
|
||||
//namespace Raindrop
|
||||
//{
|
||||
// public interface IRaindropService
|
||||
// {
|
||||
// //RD offers the underlying client by composition.
|
||||
// GridClient Client { get; }
|
||||
// string ClientDir { get; }
|
||||
// Settings ClientSettings { get; }
|
||||
// string GlobalLogFile { get; }
|
||||
// Settings GlobalSettings { get; }
|
||||
// GridManager GridManger { get; }
|
||||
// Dictionary<UUID, Group> Groups { get; }
|
||||
// string InventoryCacheFileName { get; }
|
||||
// MediaManager MediaManager { get; }
|
||||
// bool MonoRuntime { get; }
|
||||
// RaindropMovement Movement { get; }
|
||||
// NameManager Names { get; }
|
||||
// RaindropNetcom Netcom { get; }
|
||||
// StateManager State { get; }
|
||||
// string UserDir { get; }
|
||||
|
||||
// event EventHandler<ClientChangedEventArgs> ClientChanged;
|
||||
// event EventHandler<EventArgs> InventoryClipboardUpdated;
|
||||
|
||||
// bool AnotherInstanceRunning();
|
||||
// string ChatFileName(string session);
|
||||
// void CleanUp();
|
||||
// string ComputeCacheName(string cacheDir, UUID assetID);
|
||||
// string getAvatarName(UUID key);
|
||||
// string getAvatarName(UUID key, bool blocking);
|
||||
// LastExecStatus GetLastExecStatus();
|
||||
// //RD offers a few methods to obtain global state.
|
||||
// DateTime GetWorldTime();
|
||||
// void HandleThreadException(object sender, ThreadExceptionEventArgs e);
|
||||
// void LogClientMessage(string sessioName, string message);
|
||||
// void MarkEndExecution();
|
||||
// void MarkStartExecution();
|
||||
// //Convenience subroutine to call to attempt reconnection.
|
||||
// void Reconnect();
|
||||
// void SetClientTag();
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5dd741beb34a0a498739726ebb3023d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -39,7 +39,7 @@ using Raindrop.Media;
|
||||
using OpenMetaverse;
|
||||
using UnityEngine;
|
||||
using Logger = OpenMetaverse.Logger;
|
||||
using ServiceLocatorSample.ServiceLocator;
|
||||
using ServiceLocator;
|
||||
|
||||
namespace Raindrop
|
||||
{
|
||||
@@ -72,19 +72,7 @@ namespace Raindrop
|
||||
|
||||
// Singleton, there can be only one instance
|
||||
|
||||
private static RaindropInstance globalInstance = null;
|
||||
public static RaindropInstance GlobalInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (globalInstance == null)
|
||||
{
|
||||
globalInstance = new RaindropInstance(new GridClient());
|
||||
}
|
||||
return globalInstance;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// managed the chats that are loaded in memory. (including local chat.)
|
||||
//public ChatManager ChatManger { get { return chatManger; } }
|
||||
//private ChatManager chatManger;
|
||||
@@ -214,7 +202,7 @@ namespace Raindrop
|
||||
/// <summary>Manages default params for different grids</summary>
|
||||
public GridManager GridManger { get { return gridManager; } }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Current Outfit Folder (appearnce) manager
|
||||
/// </summary>
|
||||
@@ -290,10 +278,7 @@ namespace Raindrop
|
||||
|
||||
public RaindropInstance(GridClient client0)
|
||||
{
|
||||
//Logger.DebugLog("test");
|
||||
|
||||
// incase something else calls GlobalInstance while we are loading
|
||||
globalInstance = this;
|
||||
|
||||
app_data_dir = Application.persistentDataPath;
|
||||
streaming_assets_dir = Application.streamingAssetsPath;
|
||||
@@ -315,7 +300,7 @@ namespace Raindrop
|
||||
monoRuntime = Type.GetType("Mono.Runtime") != null;
|
||||
if (monoRuntime)
|
||||
{
|
||||
Logger.Log("Mono runtime is detected", Helpers.LogLevel.Debug);
|
||||
Logger.Log("Mono runtime is detected. This should not happen except in the editor.", Helpers.LogLevel.Warning);
|
||||
}
|
||||
|
||||
//Keyboard = new Keyboard();
|
||||
@@ -669,7 +654,7 @@ namespace Raindrop
|
||||
{
|
||||
try
|
||||
{
|
||||
userDir = Path.Combine(app_data_dir , PROGRAMNAME);
|
||||
userDir = Path.Combine(app_data_dir, PROGRAMNAME);
|
||||
if (!Directory.Exists(userDir))
|
||||
{
|
||||
Directory.CreateDirectory(userDir);
|
||||
|
||||
@@ -10,7 +10,11 @@
|
||||
"GUID:2665a8d13d1b3f18800f46e256720795",
|
||||
"GUID:82c7e6eac44d7e048b22ad98800dcfc7",
|
||||
"GUID:247a163e3cc6efb42bd22b9023b87ff3",
|
||||
"GUID:0c752da273b17c547ae705acf0f2adf2"
|
||||
"GUID:0c752da273b17c547ae705acf0f2adf2",
|
||||
"GUID:0d8beb7f090555447a6cf5ce9e54dbb4",
|
||||
"GUID:9e24947de15b9834991c9d8411ea37cf",
|
||||
"GUID:84651a3751eca9349aac36a66bba901b",
|
||||
"GUID:11f3455556175aa41b2b4d4f2ec8b146"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -435,7 +435,7 @@ namespace Raindrop.Rendering
|
||||
|
||||
try
|
||||
{
|
||||
string fname = RaindropInstance.GlobalInstance.ComputeCacheName(RaindropInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, textureID) + ".rzi";
|
||||
string fname = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>().ComputeCacheName(ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>().Client.Settings.ASSET_CACHE_DIR, textureID) + ".rzi";
|
||||
|
||||
using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
@@ -483,7 +483,7 @@ namespace Raindrop.Rendering
|
||||
{
|
||||
try
|
||||
{
|
||||
string fname = RaindropInstance.GlobalInstance.ComputeCacheName(RaindropInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, textureID) + ".rzi";
|
||||
string fname = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>().ComputeCacheName(ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>().Client.Settings.ASSET_CACHE_DIR, textureID) + ".rzi";
|
||||
|
||||
using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
using log4net.Config;
|
||||
using Raindrop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
//using log4net.Config;
|
||||
//using Raindrop;
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.IO;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
//using System.Threading.Tasks;
|
||||
//using UnityEngine;
|
||||
//using UnityEngine.SceneManagement;
|
||||
|
||||
namespace ServiceLocatorSample.ServiceLocator
|
||||
{
|
||||
//This is like the main() function. it runs before everything else.
|
||||
// inspired by https://medium.com/medialesson/simple-service-locator-for-your-unity-project-40e317aad307
|
||||
//namespace ServiceLocator
|
||||
//{
|
||||
// //This is like the main() function. it runs before everything else.
|
||||
// // inspired by https://medium.com/medialesson/simple-service-locator-for-your-unity-project-40e317aad307
|
||||
// //<Deprecated>
|
||||
// public static class Bootstrapper
|
||||
// {
|
||||
// //[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
// public static void Initiailze()
|
||||
// {
|
||||
// //if (!log4net.LogManager.GetRepository().Configured)
|
||||
// //{
|
||||
// // // log4net not configured
|
||||
// // foreach (log4net.Util.LogLog message in
|
||||
// // log4net.LogManager.GetRepository()
|
||||
// // .ConfigurationMessages
|
||||
// // .Cast < log4net.Util.LogLog())
|
||||
// // {
|
||||
// // // evaluate configuration message
|
||||
// // }
|
||||
// //}
|
||||
|
||||
public static class Bootstrapper
|
||||
{
|
||||
//[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
public static void Initiailze()
|
||||
{
|
||||
//if (!log4net.LogManager.GetRepository().Configured)
|
||||
//{
|
||||
// // log4net not configured
|
||||
// foreach (log4net.Util.LogLog message in
|
||||
// log4net.LogManager.GetRepository()
|
||||
// .ConfigurationMessages
|
||||
// .Cast < log4net.Util.LogLog())
|
||||
// {
|
||||
// // evaluate configuration message
|
||||
// }
|
||||
//}
|
||||
// //setup log4net
|
||||
// //XmlConfigurator.Configure(new FileInfo($"{Application.dataPath}/log4net.xml")); //this cause freeze
|
||||
// //Debug.Log("xmlconfigurator called.");
|
||||
// OpenMetaverse.Logger.Log("Logger.Log is working.", OpenMetaverse.Helpers.LogLevel.Info);
|
||||
|
||||
//setup log4net
|
||||
//XmlConfigurator.Configure(new FileInfo($"{Application.dataPath}/log4net.xml")); //this cause freeze
|
||||
//Debug.Log("xmlconfigurator called.");
|
||||
OpenMetaverse.Logger.Log("Logger.Log is working.", OpenMetaverse.Helpers.LogLevel.Info);
|
||||
// // Initialize default service locator.
|
||||
// ServiceLocator.Initiailze();
|
||||
|
||||
// Initialize default service locator.
|
||||
ServiceLocator.Initiailze();
|
||||
// // Register all your services next.
|
||||
// ServiceLocator.Instance.Register<RaindropInstance>(new RaindropInstance(new OpenMetaverse.GridClient()));
|
||||
// ServiceLocator.Instance.Register<UIService>(new UIService());
|
||||
|
||||
// Register all your services next.
|
||||
ServiceLocator.Current.Register<RaindropInstance>(new RaindropInstance(new OpenMetaverse.GridClient()));
|
||||
ServiceLocator.Current.Register<UIManager>(new UIManager());
|
||||
|
||||
// Application is ready to start, load your main scene.
|
||||
Scene[] currentScenes = SceneManager.GetAllScenes();
|
||||
bool loadUI = true;
|
||||
bool load3D = true;
|
||||
foreach(var scene in currentScenes)
|
||||
{
|
||||
if (scene.name == "UIscene")
|
||||
{
|
||||
loadUI = false;
|
||||
}
|
||||
if (scene.name == "3Dscene")
|
||||
{
|
||||
load3D = false;
|
||||
}
|
||||
}
|
||||
// // Application is ready to start, load your main scene.
|
||||
// Scene[] currentScenes = SceneManager.GetAllScenes();
|
||||
// bool loadUI = true;
|
||||
// bool load3D = true;
|
||||
// foreach(var scene in currentScenes)
|
||||
// {
|
||||
// if (scene.name == "UIscene")
|
||||
// {
|
||||
// loadUI = false;
|
||||
// }
|
||||
// if (scene.name == "3Dscene")
|
||||
// {
|
||||
// load3D = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (loadUI)
|
||||
SceneManager.LoadScene("UIscene", LoadSceneMode.Additive);
|
||||
if (load3D)
|
||||
SceneManager.LoadScene("3Dscene", LoadSceneMode.Additive);
|
||||
// if (loadUI)
|
||||
// SceneManager.LoadScene("UIscene", LoadSceneMode.Additive);
|
||||
// if (load3D)
|
||||
// SceneManager.LoadScene("3Dscene", LoadSceneMode.Additive);
|
||||
|
||||
Debug.Log("Bootstrap finished, all scenes loaded.!");
|
||||
// Debug.Log("Bootstrap finished, all scenes loaded.!");
|
||||
|
||||
ServiceLocatorSample.ServiceLocator.ServiceLocator.Current.Get<UIManager>().initialiseUI();
|
||||
// ServiceLocator.Instance.Get<UIService>().startUIInitialView();
|
||||
|
||||
Debug.Log("UI should be appeared in front of you");
|
||||
}
|
||||
// Debug.Log("UI should be appeared in front of you");
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
using log4net.Config;
|
||||
using Raindrop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace ServiceLocator
|
||||
{
|
||||
//This sets up and tears down the raindrop instance and other servies.
|
||||
//This is attached to a scene as the SOLE gameobject.
|
||||
// inspired by https://medium.com/medialesson/simple-service-locator-for-your-unity-project-40e317aad307
|
||||
|
||||
public class BootstrapperGO : MonoBehaviour
|
||||
{
|
||||
|
||||
private void Start()
|
||||
{
|
||||
|
||||
Debug.LogError("BootstrapperGO is probably not working well. please refactor or remove from scene.");
|
||||
//if (!log4net.LogManager.GetRepository().Configured)
|
||||
//{
|
||||
// // log4net not configured
|
||||
// foreach (log4net.Util.LogLog message in
|
||||
// log4net.LogManager.GetRepository()
|
||||
// .ConfigurationMessages
|
||||
// .Cast < log4net.Util.LogLog())
|
||||
// {
|
||||
// // evaluate configuration message
|
||||
// }
|
||||
//}
|
||||
|
||||
//setup log4net
|
||||
//XmlConfigurator.Configure(new FileInfo($"{Application.dataPath}/log4net.xml")); //this cause freeze
|
||||
//Debug.Log("xmlconfigurator called.");
|
||||
OpenMetaverse.Logger.Log("Logger.Log is working, as proven by the existence of this Log message.", OpenMetaverse.Helpers.LogLevel.Info);
|
||||
|
||||
// Initialize default service locator.
|
||||
//edit: move to ui scene - uibootstrapper.
|
||||
//ServiceLocator.Initiailze();
|
||||
|
||||
// Register all your services next.
|
||||
//edit: move to ui scene - uibootstrapper.
|
||||
//ServiceLocator.Instance.Register<RaindropInstance>(new RaindropInstance(new OpenMetaverse.GridClient()));
|
||||
//ServiceLocator.Instance.Register<UIService>(new UIService());
|
||||
|
||||
// Application is ready to start, load your main UI.
|
||||
//if (enableUI)
|
||||
SceneManager.LoadScene("UIscene", LoadSceneMode.Additive); //blocking load required as the UIService will be requested a few lines from now.
|
||||
//if (enable3D)
|
||||
SceneManager.LoadScene("3Dscene", LoadSceneMode.Additive);
|
||||
|
||||
Debug.Log("Bootstrap finished, all scenes loaded.!");
|
||||
|
||||
//edit: move to ui scene - uibootstrapper.
|
||||
//ServiceLocator.Instance.Get<UIService>().startUIInitialView();
|
||||
|
||||
//Debug.Log("UI should be appeared in front of you");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa61eabca29ec274e822d94f50702a99
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace ServiceLocatorSample.ServiceLocator
|
||||
namespace ServiceLocator
|
||||
{
|
||||
/// <summary>
|
||||
/// Base interface for our service locator to work with. Services implementing
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ServiceLocatorSample.ServiceLocator
|
||||
namespace ServiceLocator
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple service locator for <see cref="IGameService"/> instances.
|
||||
@@ -19,14 +19,14 @@ namespace ServiceLocatorSample.ServiceLocator
|
||||
/// <summary>
|
||||
/// Gets the currently active service locator instance.
|
||||
/// </summary>
|
||||
public static ServiceLocator Current { get; private set; }
|
||||
public static ServiceLocator Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initalizes the service locator with a new instance.
|
||||
/// </summary>
|
||||
public static void Initiailze()
|
||||
{
|
||||
Current = new ServiceLocator();
|
||||
Instance = new ServiceLocator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -45,6 +45,21 @@ namespace ServiceLocatorSample.ServiceLocator
|
||||
|
||||
return (T)services[key];
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns if there already registered the service instance of the given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the service to lookup.</typeparam>
|
||||
/// <returns>The service instance.</returns>
|
||||
public bool IsRegistered<T>() where T : IGameService
|
||||
{
|
||||
string key = typeof(T).Name;
|
||||
if (!services.ContainsKey(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the service with the current service locator.
|
||||
|
||||
@@ -6,24 +6,53 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using ServiceLocator;
|
||||
|
||||
namespace Raindrop
|
||||
{
|
||||
//get the UI manger and show the first UI panel to the user.
|
||||
class UIBootstrapper : MonoBehaviour
|
||||
{
|
||||
private RaindropInstance instance => Raindrop.RaindropInstance.GlobalInstance;
|
||||
private RaindropInstance instance => ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
|
||||
private RaindropNetcom netcom => instance.Netcom;
|
||||
|
||||
// bootstraps in order to obtain the canvasmanager instance.
|
||||
// bootstraps to find all instances of panels in the children tree.
|
||||
// has a funny role; in that it will register itself to the UIservice on start/awake. this should really be the other way round - that the UIservice creates/has dependency on the UIrootGO!!!
|
||||
private void Awake()
|
||||
{
|
||||
{
|
||||
ServiceLocator.ServiceLocator.Initiailze();
|
||||
|
||||
if (ServiceLocator.ServiceLocator.Instance.IsRegistered<UIService>())
|
||||
{
|
||||
Debug.LogError("Attempted to register UI Service again! ");
|
||||
return;
|
||||
}
|
||||
|
||||
if (! ServiceLocator.ServiceLocator.Instance.IsRegistered<RaindropInstance>())
|
||||
{
|
||||
Debug.LogWarning("UIBootstrapper creating and registering raindropinstance!");
|
||||
ServiceLocator.ServiceLocator.Instance.Register<RaindropInstance>(new RaindropInstance(new OpenMetaverse.GridClient()));
|
||||
//return;
|
||||
}
|
||||
|
||||
var cm = GetComponentInChildren<CanvasManager>();
|
||||
var mm = GetComponentInChildren<ModalManager>();
|
||||
|
||||
ServiceLocator.ServiceLocator.Instance.Register<UIService>(new UIService(cm,mm));
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
ServiceLocator.ServiceLocator.Instance.Get<UIService>().startUIInitialView();
|
||||
Debug.Log("UI should be appeared");
|
||||
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
Debug.Log("Application ending after " + Time.time + " seconds");
|
||||
Debug.Log("logging out");
|
||||
Debug.Log("Application ending after " + Time.time + " seconds");
|
||||
|
||||
//if (instance.GlobalSettings["confirm_exit"].AsBoolean())
|
||||
//{
|
||||
|
||||
@@ -8,21 +8,25 @@ using UnityEngine;
|
||||
//helper class that helps to pop, push, stack canvases.
|
||||
//singleton.
|
||||
//on awake, it searches children for all canvases.
|
||||
public class CanvasManager : Singleton<CanvasManager>
|
||||
public class CanvasManager : MonoBehaviour
|
||||
{
|
||||
List<CanvasIdentifier> canvasControllerList;
|
||||
//CanvasIdentifier lastActiveCanvas;
|
||||
[SerializeField]
|
||||
//public GameObject[] CanvasPrefabsList;
|
||||
|
||||
public List<CanvasIdentifier> canvasControllerList = new List<CanvasIdentifier>();
|
||||
public Stack<CanvasIdentifier> activeCanvasStack = new Stack<CanvasIdentifier>();
|
||||
|
||||
protected override void Awake()
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
canvasControllerList = FindObjectsOfType<CanvasIdentifier>().ToList();
|
||||
int childrenCount = transform.childCount;
|
||||
for (int i = 0; i < childrenCount ; i++)
|
||||
{
|
||||
//GameObject panelRoot = Instantiate(prefab) as GameObject;
|
||||
//panelRoot.transform.SetParent(this.transform);
|
||||
canvasControllerList.Add(transform.GetChild(i).GetComponent<CanvasIdentifier>());
|
||||
}
|
||||
canvasControllerList.ForEach(x => x.gameObject.SetActive(false));
|
||||
Debug.Log("Found " + canvasControllerList.Count + " canvas identifiers." );
|
||||
|
||||
|
||||
|
||||
Debug.Log("Found " + canvasControllerList.Count + " canvas identifiers.");
|
||||
}
|
||||
|
||||
public void resetToLoginScreen()
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43ef49fd5241c7245be207def9ef51c1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32a5b22a261aacc43885b5c39d2498fd
|
||||
guid: 3519366cc6f6159448e93458bcd34783
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 206ef8de4fd569e409b4104c18a8bb88
|
||||
guid: 48bab53868d70404f9e49730e1ce9a40
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
+4
-2
@@ -15,6 +15,7 @@ using System.Text.RegularExpressions;
|
||||
using Raindrop.Core;
|
||||
using Quaternion = UnityEngine.Quaternion;
|
||||
using Vector3 = UnityEngine.Vector3;
|
||||
using Zenject;
|
||||
|
||||
|
||||
//view(unitytext) -- presenter(this) -- controller(this?) -- model (raindropinstance singleton)
|
||||
@@ -26,7 +27,8 @@ namespace Raindrop.Presenters
|
||||
//left pane: scrollable list of chats.
|
||||
//right pane: the contents of the selected chat in the left pane.+
|
||||
// input bar of the text to send to said chat.
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
|
||||
private RaindropInstance instance;
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
|
||||
private GridClient client => instance.Client;
|
||||
@@ -253,7 +255,7 @@ namespace Raindrop.Presenters
|
||||
|
||||
private void OnCloseBtnClick()
|
||||
{
|
||||
var uimanager = ServiceLocatorSample.ServiceLocator.ServiceLocator.Current.Get<UIManager>();
|
||||
var uimanager = ServiceLocator.ServiceLocator.Instance.Get<UIService>();
|
||||
uimanager.canvasManager.popCanvas();
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cde782cfb154fd42898b6c662871547
|
||||
guid: cfc91f4fb31976b4b848da754e45cfee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
+43
-33
@@ -9,29 +9,50 @@ using OpenMetaverse.StructuredData;
|
||||
using OpenMetaverse.Assets;
|
||||
using Raindrop;
|
||||
using UnityEngine;
|
||||
using ServiceLocatorSample.ServiceLocator;
|
||||
using ServiceLocator;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Raindrop
|
||||
{
|
||||
public class UIManager : IGameService
|
||||
public class UIService : IGameService
|
||||
{
|
||||
//mainUImanager has responsibilities:
|
||||
// CanvasManager - stores and manages the pops, push of views onto the ui stack.
|
||||
// ModalManager - pops and shows modals.
|
||||
// LoadingCanvasPresenter - this particular modal/screen is tricky; it appears only when the scene is loading.
|
||||
//UI is a service. it will always be available.
|
||||
// note that presenters should register with the canvas manager. presenters themselves provide the logic of ui-traversal.
|
||||
// modals on the other hand are provided and popped in by the presenters themselves. for example a confirmation prompt for the user - obviously that should fall under the responsibility of the UI-logic layer.
|
||||
// UIservice contains:
|
||||
// CanvasManager - manages the UI stack. Access this to pop and push views onto the ui stack.
|
||||
// ModalManager - manages the modals. access this to pop and show modals.
|
||||
// <deprecated> LoadingCanvasPresenter - this particular modal/screen is tricky; it appears only when the scene is loading.
|
||||
|
||||
private RaindropInstance instance;
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
private GridClient client { get { return instance.Client; } }
|
||||
|
||||
//public CanvasManager canvasManager { get { return CanvasManager.GetInstance(); } }
|
||||
public CanvasManager canvasManager { get { return CanvasManager.GetInstance(); } }
|
||||
public ModalManager modalManager { get { return ModalManager.GetInstance(); } }
|
||||
// canvases are stack-based. only 1 is top-most and active at any time.
|
||||
private CanvasManager _canvasManager;
|
||||
public CanvasManager canvasManager { set { _canvasManager = value; } get { return _canvasManager; } }
|
||||
// modals are single-display. however, there is a modal queue, such that when the current modal is dismissed, the next-in-queue will appear.
|
||||
//care has to be taken not to spam the user with modals.
|
||||
private ModalManager _modalManager;
|
||||
public ModalManager modalManager { set { _modalManager = value; } get { return _modalManager; } }
|
||||
|
||||
//This was the old contructor used when UIManager was being created(constructed) in RaindropInstance
|
||||
public UIManager(/*RaindropInstance raindropInstance*/)
|
||||
public UIService(CanvasManager cm, ModalManager mm)
|
||||
{
|
||||
this.instance = RaindropInstance.GlobalInstance;
|
||||
canvasManager = cm;
|
||||
modalManager = mm;
|
||||
|
||||
// UI depends on raindrop business layer.
|
||||
try
|
||||
{
|
||||
this.instance = ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>();
|
||||
} catch (InvalidOperationException)
|
||||
{
|
||||
Debug.LogError("UIService failed to get raindrop service");
|
||||
//failed to find service.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Callbacks
|
||||
netcom.ClientLoginStatus += new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
|
||||
@@ -41,36 +62,25 @@ namespace Raindrop
|
||||
|
||||
RegisterClientEvents(client);
|
||||
|
||||
//canvasManager = new CanvasManager();
|
||||
|
||||
|
||||
}
|
||||
|
||||
//private void Awake()
|
||||
//{
|
||||
// Debug.Log("UIManager woken up");
|
||||
~UIService()
|
||||
{
|
||||
|
||||
// this.instance = RaindropInstance.GlobalInstance;
|
||||
netcom.ClientLoginStatus -= new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
|
||||
netcom.ClientLoggedOut -= new EventHandler(netcom_ClientLoggedOut);
|
||||
netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
|
||||
instance.Names.NameUpdated -= new EventHandler<UUIDNameReplyEventArgs>(Names_NameUpdated);
|
||||
|
||||
// // Callbacks
|
||||
// netcom.ClientLoginStatus += new EventHandler<LoginProgressEventArgs>(netcom_ClientLoginStatus);
|
||||
// netcom.ClientLoggedOut += new EventHandler(netcom_ClientLoggedOut);
|
||||
// netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(netcom_ClientDisconnected);
|
||||
// instance.Names.NameUpdated += new EventHandler<UUIDNameReplyEventArgs>(Names_NameUpdated);
|
||||
|
||||
// RegisterClientEvents(client);
|
||||
|
||||
// //canvasManager = new CanvasManager();
|
||||
|
||||
// initialiseUI();
|
||||
//}
|
||||
UnregisterClientEvents(client);
|
||||
}
|
||||
|
||||
|
||||
public void initialiseUI()
|
||||
|
||||
public void startUIInitialView()
|
||||
{
|
||||
canvasManager.pushCanvas(CanvasType.Welcome);
|
||||
modalManager.showSimpleModalBoxWithActionBtn("Disclaimer", "This software is a work in progress. There is no guarantee about its stability. ", "Accept");
|
||||
|
||||
}
|
||||
|
||||
public GameObject getCurrentForegroundPresenter()
|
||||
@@ -12,7 +12,7 @@ public class joystickToMovementBackend : MonoBehaviour
|
||||
{
|
||||
public LeanJoystick variableJoystick;
|
||||
public GameObject theJoystickInScene;
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
private GridClient client { get { return instance.Client; } }
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using UnityEngine.UI;
|
||||
//a monobehavior that makes a toggle toggle the eula acceptance in globalSettings
|
||||
public class EulaModalPresenter : MonoBehaviour
|
||||
{
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
//public string nameOfEulaToggleGO;
|
||||
private Toggle EulaToggle;
|
||||
public GameObject EulaToggleGO;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55529afe5fa7c8f4482ff13ff96fec4e
|
||||
guid: 66441a43fb359434c944c394b81c6f95
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -7,17 +7,20 @@ using UnityEngine;
|
||||
|
||||
//a singleton monobehavior that activates modals.
|
||||
//maintains reference to all modals.
|
||||
public class ModalManager : Singleton<ModalManager>
|
||||
public class ModalManager : MonoBehaviour
|
||||
{
|
||||
Thread mainThread;
|
||||
//pool of modals.
|
||||
public modalPresenter genericModal;
|
||||
private modalPresenter genericModalPresenter;
|
||||
public modalPresenter eulaModal;
|
||||
public modalPresenter loggingInStatusModal;
|
||||
|
||||
protected override void Awake()
|
||||
|
||||
|
||||
[SerializeField]
|
||||
public GameObject GenericModalPrefab;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
//find yo modals in scene.
|
||||
//foreach(modalPresenter _ in FindObjectsOfType<modalPresenter>())
|
||||
//{
|
||||
@@ -32,7 +35,12 @@ public class ModalManager : Singleton<ModalManager>
|
||||
// genericModal.closeModal();
|
||||
//}
|
||||
|
||||
if (genericModal == null)
|
||||
|
||||
GameObject GenericModal = Instantiate(GenericModalPrefab) as GameObject;
|
||||
GenericModal.transform.SetParent(this.transform);
|
||||
genericModalPresenter = GenericModal.GetComponent<modalPresenter>();
|
||||
|
||||
if (genericModalPresenter == null)
|
||||
{
|
||||
Debug.LogError("cannot find the gneric modal");
|
||||
}
|
||||
@@ -78,10 +86,10 @@ public class ModalManager : Singleton<ModalManager>
|
||||
{
|
||||
if (isOnMainThread())
|
||||
{
|
||||
if (genericModal != null)
|
||||
if (genericModalPresenter != null)
|
||||
{
|
||||
genericModal.setModal(title, content);
|
||||
genericModal.gameObject.SetActive(visibility);
|
||||
genericModalPresenter.setModal(title, content);
|
||||
genericModalPresenter.gameObject.SetActive(visibility);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -137,10 +145,10 @@ public class ModalManager : Singleton<ModalManager>
|
||||
|
||||
public void showSimpleModalBoxWithActionBtn(string title, string content, string Action)
|
||||
{
|
||||
if (genericModal != null)
|
||||
if (genericModalPresenter != null)
|
||||
{
|
||||
genericModal.setModal(title, content, Action);
|
||||
genericModal.gameObject.SetActive(true);
|
||||
genericModalPresenter.setModal(title, content, Action);
|
||||
genericModalPresenter.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3aa7a5a5fc2f14a4da5a09383ddb6712
|
||||
guid: 17ba502de0318504394f00ba4d1e3af2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d76a6d052750ed5489de979675b9f21a
|
||||
guid: 062fe3ce8e16c8e44a64b704ea6ff397
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95462efaa5b0f3a4cbdae982380e7a3b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b6aff116066ff44596caa8739b07594
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+2
-2
@@ -16,7 +16,7 @@ namespace Raindrop.Presenters
|
||||
public class DebugLogPresenter : MonoBehaviour
|
||||
{
|
||||
//main pane: a list of debug messages.
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
|
||||
private GridClient client => instance.Client;
|
||||
@@ -45,7 +45,7 @@ namespace Raindrop.Presenters
|
||||
|
||||
void RaindropAppender_Log(object sender, LogEventArgs e)
|
||||
{
|
||||
Debug.Log("yep the log function was called!");
|
||||
Debug.Log("debuglog presenter loggin method is working!");
|
||||
//if (!IsHandleCreated) return;
|
||||
|
||||
//if (InvokeRequired)
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 789e2a95a8c7030428b7932cfb90d3e5
|
||||
guid: e252eb323a73748459b245b4ab49ea92
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
+3
-3
@@ -24,11 +24,11 @@ namespace Raindrop.Presenters
|
||||
//2 . notifications - a dragdown list-like UI
|
||||
//3 . buttons to access other features.
|
||||
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
private GridClient client { get { return instance.Client; } }
|
||||
|
||||
private UIManager uimanager;
|
||||
private UIService uimanager;
|
||||
private Settings s;
|
||||
|
||||
bool Active => instance.Client.Network.Connected;
|
||||
@@ -80,7 +80,7 @@ namespace Raindrop.Presenters
|
||||
//simName.AsObservable().Subscribe(_ => UpdateSimLocDisplay(_));
|
||||
|
||||
//get uimanager service
|
||||
uimanager = ServiceLocatorSample.ServiceLocator.ServiceLocator.Current.Get<UIManager>();
|
||||
uimanager = ServiceLocator.ServiceLocator.Instance.Get<UIService>();
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace Raindrop.Presenters
|
||||
{
|
||||
public class LoadingCanvasPresenter : MonoBehaviour
|
||||
{
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
|
||||
// Start is called before the first frame update
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74f0a76d6b05c674c809ba6529de56c0
|
||||
guid: e117d10f9a19e14409d2e72c69c098a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
+6
-6
@@ -12,7 +12,7 @@ using UnityEngine.UI;
|
||||
using UniRx;
|
||||
using TMPro;
|
||||
using static Raindrop.LoginUtils;
|
||||
using ServiceLocatorSample.ServiceLocator;
|
||||
using ServiceLocator;
|
||||
|
||||
|
||||
//view(unitytext) -- presenter(this) -- controller(this?) -- model (raindropinstance singleton)
|
||||
@@ -30,9 +30,9 @@ namespace Raindrop.Presenters
|
||||
|
||||
public class LoginPresenter : MonoBehaviour
|
||||
{
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
private UIManager uimanager;
|
||||
private UIService uimanager;
|
||||
|
||||
|
||||
#region UI elements - the 'view' in MVP
|
||||
@@ -115,7 +115,7 @@ namespace Raindrop.Presenters
|
||||
AddNetcomEvents();
|
||||
|
||||
//get uimanager service
|
||||
uimanager = ServiceLocatorSample.ServiceLocator.ServiceLocator.Current.Get<UIManager>();
|
||||
uimanager = ServiceLocator.ServiceLocator.Instance.Get<UIService>();
|
||||
|
||||
}
|
||||
|
||||
@@ -438,7 +438,7 @@ namespace Raindrop.Presenters
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Settings
|
||||
|
||||
// this function appends (and saves) content of loginoptions to globalsettings file.
|
||||
// it is called when the user clicks the login button.
|
||||
@@ -516,7 +516,7 @@ namespace Raindrop.Presenters
|
||||
s["remember_login"] = isSaveCredentials; //OSD.FromBoolean (loginoptions.IsSaveCredentials);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2eeaa71c61350f46902f312854272c9
|
||||
guid: 4628244dfa91a494785c12f6098eedfd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
+1
-1
@@ -18,7 +18,7 @@ using TMPro;
|
||||
public class MapPresenter : MonoBehaviour
|
||||
{
|
||||
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
bool Active => instance.Client.Network.Connected;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fa61c7157c917847adf9560fc4b3fb1
|
||||
guid: 327f1fb8354daac46abcf57544d8c82e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71e488d444cef5f4199b21f3f31879e4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using UnityEngine.UI;
|
||||
|
||||
[RequireComponent(typeof(RawImage))]
|
||||
public class RawImageView : MonoBehaviour
|
||||
|
||||
{
|
||||
public void setRawImage(Texture2D img)
|
||||
{
|
||||
//hack: delete old texture before loading new one
|
||||
|
||||
if (this.GetComponent<RawImage>().texture != null)
|
||||
{
|
||||
Object.Destroy(this.GetComponent<RawImage>().texture);
|
||||
}
|
||||
this.GetComponent<RawImage>().texture = img;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 606f1f82e1856b94f99def2f63d3b15b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
// Class to help read the assets residing in the streamingassets folder; mostly binary and image/tga files.
|
||||
public class StreamAssetsReader
|
||||
{
|
||||
|
||||
//reads file at relative path into bytearray
|
||||
public static void read(string path, byte[] readInto)
|
||||
{
|
||||
// all at once
|
||||
byte[] data = BetterStreamingAssets.ReadAllBytes(path);
|
||||
|
||||
}
|
||||
|
||||
//callback when the web asset is loaded.
|
||||
//private static void StreamAssetsReader_completed(UnityWebRequestAsyncOperation obj)
|
||||
//{
|
||||
// byte[] result = obj.webRequest.downloadHandler.data;
|
||||
// File.WriteAllText(Application.persistentDataPath + relative_path, ((byte)obj.webRequest.result));
|
||||
// //throw new System.NotImplementedException();
|
||||
//}
|
||||
|
||||
|
||||
|
||||
//you can use the coroutine way, or you can use the callback function way.
|
||||
//private void OnLoadDone(UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<TextAsset> obj)
|
||||
//{
|
||||
// // In a production environment, you should add exception handling to catch scenarios such as a null result.
|
||||
// TextAsset data = obj.Result;
|
||||
// byte[] data_bytes = data.bytes;
|
||||
// var tex = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(data_bytes);
|
||||
|
||||
// float timeEnd = Time.realtimeSinceStartup;
|
||||
|
||||
// if (tex == null)
|
||||
// {
|
||||
// Debug.Log("reading of TGA addressable failed");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.Log("reading of TGA addressable SUCCESS! \nTook: " + (timeEnd - timeStart) + "seconds");
|
||||
// iv.setRawImage(tex);
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86909390f2f6895468c97692e53a00b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,90 @@
|
||||
//using System.Collections;
|
||||
//using System.Collections.Generic;
|
||||
//using UnityEngine;
|
||||
//using System.IO;
|
||||
|
||||
//public class TGAFileViewerAddressablesFail : MonoBehaviour
|
||||
//{
|
||||
|
||||
// [SerializeField]
|
||||
// public GameObject rawImageGO;
|
||||
// private RawImageView iv;
|
||||
// [SerializeField]
|
||||
// public GameObject textGO;
|
||||
|
||||
// //private List<FileInfo> fileList;
|
||||
// public int currentFileIndex = 0;
|
||||
// public bool useAddressables = true;
|
||||
// private float timeStart;
|
||||
|
||||
// private int imagesCount = 5;
|
||||
|
||||
// private void Start()
|
||||
// {
|
||||
// //get reference to the view.
|
||||
// iv = rawImageGO.GetComponent<RawImageView>();
|
||||
// if (iv == null)
|
||||
// {
|
||||
// throw new System.Exception("Imageview is fucked"); // fix exception type plz
|
||||
// }
|
||||
|
||||
|
||||
// if (useAddressables == true)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.LogError("TGA viewer by files/streamingassets is not implemented");
|
||||
// }
|
||||
|
||||
// ////get list of .tgas
|
||||
// //DirectoryInfo d = new DirectoryInfo(pathToTGAFolder);
|
||||
// //FileInfo[] fi = d.GetFiles("*.tga"); // LOL!!!!!! FUCK --- GC????
|
||||
// //fileList = new List<FileInfo>(fi);
|
||||
|
||||
// }
|
||||
|
||||
// public void onNextPicture()
|
||||
// {
|
||||
// ReadAndSetImageAddressable(currentFileIndex);
|
||||
|
||||
// return;
|
||||
// }
|
||||
// public void onPrevPicture()
|
||||
// {
|
||||
// ReadAndSetImageAddressable(currentFileIndex);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// //you can use the coroutine way, or you can use the callback function way.
|
||||
// private void OnLoadDone(UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<TextAsset> obj)
|
||||
// {
|
||||
// // In a production environment, you should add exception handling to catch scenarios such as a null result.
|
||||
// TextAsset data = obj.Result;
|
||||
// byte[] data_bytes = data.bytes;
|
||||
// var tex = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(data_bytes);
|
||||
|
||||
// float timeEnd = Time.realtimeSinceStartup;
|
||||
|
||||
// if (tex == null)
|
||||
// {
|
||||
// Debug.Log("reading of TGA addressable failed");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.Log("reading of TGA addressable SUCCESS! \nTook: " + (timeEnd - timeStart) + "seconds");
|
||||
// iv.setRawImage(tex);
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
|
||||
// private void ReadAndSetImageAddressable(int _currentFileIndex)
|
||||
// {
|
||||
// Debug.Log("reading of TGA addressable START");
|
||||
// timeStart = Time.realtimeSinceStartup;
|
||||
// Addressables.LoadAssetAsync<TextAsset>("Assets/BundleData/openmetaverse_data/blush_alpha.tga.bytes").Completed += OnLoadDone;
|
||||
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f27c82b52f4a63468bddc617f7318f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,138 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using Better.StreamingAssets;
|
||||
|
||||
public class TGAFileViewerStreamingAssets : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private string pathToTGAFolder;
|
||||
[SerializeField]
|
||||
public GameObject rawImageGO;
|
||||
private RawImageView iv;
|
||||
[SerializeField]
|
||||
public GameObject textGO;
|
||||
[SerializeField]
|
||||
public List<string> filesInStreamingAssets;//= new List<string> { "openmetaverse_data/blush_alpha.tga" };
|
||||
|
||||
//private List<FileInfo> fileList;
|
||||
public int currentFileIndex = 0;
|
||||
private float timeStart;
|
||||
private int imagesCount = 5;
|
||||
|
||||
private string[] paths;
|
||||
private byte[] poolItemBytes; //a object just to pool memory?
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
//in main thread, before all uses.
|
||||
BetterStreamingAssets.Initialize();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
//get reference to the view.
|
||||
iv = rawImageGO.GetComponent<RawImageView>();
|
||||
if (iv == null)
|
||||
{
|
||||
throw new System.Exception("Imageview is fucked"); // fix exception type plz
|
||||
}
|
||||
|
||||
//check if the path is exist.
|
||||
if (!BetterStreamingAssets.DirectoryExists(pathToTGAFolder))
|
||||
{
|
||||
Debug.LogErrorFormat("Streaming asset dir not found: {0}", pathToTGAFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
//get all files.
|
||||
|
||||
paths = BetterStreamingAssets.GetFiles(pathToTGAFolder, "*.tga", SearchOption.AllDirectories);
|
||||
|
||||
|
||||
////get list of .tgas
|
||||
//DirectoryInfo d = new DirectoryInfo(pathToTGAFolder);
|
||||
//FileInfo[] fi = d.GetFiles("*.tga"); // LOL!!!!!! FUCK --- GC????
|
||||
//fileList = new List<FileInfo>(fi);
|
||||
|
||||
}
|
||||
|
||||
public void onNextPicture()
|
||||
{
|
||||
|
||||
currentFileIndex++;
|
||||
currentFileIndex %= imagesCount;
|
||||
ReadAndSetImage(currentFileIndex);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
public void onPrevPicture()
|
||||
{
|
||||
|
||||
currentFileIndex--;
|
||||
currentFileIndex += imagesCount; //not sure if necessary -- prevent negative modulo
|
||||
currentFileIndex %= imagesCount;
|
||||
ReadAndSetImage(currentFileIndex);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void ReadAndSetImage(int _currentFileIndex)
|
||||
{
|
||||
string filepath = paths[_currentFileIndex];
|
||||
textGO.GetComponent<TextView>().setText(filepath);
|
||||
|
||||
float timeStart = Time.realtimeSinceStartup;
|
||||
Texture2D tex;
|
||||
using (var stream = BetterStreamingAssets.OpenRead(filepath))
|
||||
{
|
||||
tex = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(stream);
|
||||
}
|
||||
|
||||
//StreamAssetsReader.read(filepath, poolItemBytes); // new allocation deep inside here.
|
||||
//var tex = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(poolItemBytes);
|
||||
float timeEnd = Time.realtimeSinceStartup;
|
||||
if (tex == null)
|
||||
{
|
||||
Debug.Log("reading of TGA at path " + filepath + " failed");
|
||||
return;
|
||||
}
|
||||
Debug.Log("reading of TGA at path " + filepath + " SUCCESS! \nTook: " + (timeEnd - timeStart) + "seconds");
|
||||
|
||||
|
||||
iv.setRawImage(tex);
|
||||
}
|
||||
|
||||
|
||||
//IEnumerator work()
|
||||
//{
|
||||
|
||||
// Debug.Log("reading of TGA addressable START");
|
||||
// float timeStart = Time.realtimeSinceStartup;
|
||||
// //Debug.Log("reading of TGA addressable yield1");
|
||||
// //yield return asyncOp;
|
||||
// TextAsset data = asyncOp.Result;
|
||||
// byte[] data_bytes = data.bytes;
|
||||
// Debug.Log("loadTGA");
|
||||
// var tex = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(data_bytes);
|
||||
// float timeEnd = Time.realtimeSinceStartup;
|
||||
|
||||
// if (tex == null)
|
||||
// {
|
||||
// Debug.Log("reading of TGA addressable failed");
|
||||
// } else
|
||||
// {
|
||||
// Debug.Log("reading of TGA addressable SUCCESS! \nTook: " + (timeEnd - timeStart) + "seconds");
|
||||
// iv.setRawImage(tex);
|
||||
|
||||
// }
|
||||
|
||||
|
||||
//}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3daf7731a487bb48913667fe2861aad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(TMPro.TMP_Text))]
|
||||
public class TextView : MonoBehaviour
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void setText(string text)
|
||||
{
|
||||
this.GetComponent<TMPro.TMP_Text>().text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6105e38874b5073449c30ca3f57b4204
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Raindrop.Presenters
|
||||
{
|
||||
//all stack-based UI windows/views inherit this.
|
||||
public class BasePresenter : MonoBehaviour
|
||||
{
|
||||
private void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void registerWithUIService()
|
||||
{
|
||||
ServiceLocator.ServiceLocator.Instance.Get<UIService>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84beeb1ebdc9a7446b00e9ab7bd292c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12,7 +12,7 @@ namespace Raindrop.Presenters
|
||||
public class GenericDropdown : MonoBehaviour
|
||||
{
|
||||
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Raindrop.Unity3D
|
||||
//sets the attached gameobject to the location of the user in the sim.
|
||||
class AgentLocationUpdater : MonoBehaviour
|
||||
{
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
bool Active => instance.Client.Network.Connected;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Raindrop.Presenters
|
||||
{
|
||||
private Dictionary<uint, UnityEngine.GameObject> avatarsGO;
|
||||
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
//private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
bool Active => instance.Client.Network.Connected;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Raindrop.Presenters
|
||||
|
||||
private MapImageCameraPresenter cameraPresenter;
|
||||
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
private GridClient client { get { return instance.Client; } }
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Raindrop.Unity3D
|
||||
//sets the mesh of the gameobject to match the sim's shape.
|
||||
class TerrainMeshUpdater : MonoBehaviour
|
||||
{
|
||||
private RaindropInstance instance { get { return RaindropInstance.GlobalInstance; } }
|
||||
private RaindropInstance instance { get { return ServiceLocator.ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
||||
private GridClient Client { get { return instance.Client; } }
|
||||
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
||||
bool Active => instance.Client.Network.Connected;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd981829e31c35d4bbe895a05254cdb0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
//using System;
|
||||
//using Zenject;
|
||||
|
||||
//namespace Raindrop
|
||||
//{
|
||||
// class GameInstaller1 : MonoInstaller
|
||||
// {
|
||||
// public override void InstallBindings()
|
||||
// {
|
||||
// Container.Bind<IRaindropService>()
|
||||
// .To<RaindropInstance>()
|
||||
// .AsSingle();
|
||||
|
||||
// Container.Bind<UIInstance>().AsSingle().NonLazy();
|
||||
// Container.Bind<RaindropInstance>().AsSingle().NonLazy();
|
||||
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68e24ada4aef46747b70b5a7f07699a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user