Пример #1
0
        private unsafe byte ReadSqpackHandler(IntPtr pFileHandler, SeFileDescriptor *pFileDesc, int priority, bool isSync)
        {
            var gameFsPath = Marshal.PtrToStringAnsi(new IntPtr(pFileDesc->ResourceHandle->FileName));

            var isRooted = Path.IsPathRooted(gameFsPath);

            if (gameFsPath == null || gameFsPath.Length >= 260 || !isRooted)
            {
                return(ReadSqpackHook.OriginalFunction(pFileHandler, pFileDesc, priority, isSync));
            }

#if DEBUG
            PluginLog.Log("loading modded file: {GameFsPath}", gameFsPath);
#endif

            pFileDesc->FileMode = FileMode.LoadUnpackedResource;

            // note: must be utf16
            var utfPath = Encoding.Unicode.GetBytes(gameFsPath);

            Marshal.Copy(utfPath, 0, new IntPtr(&pFileDesc->UtfFileName), utfPath.Length);

            var fd = stackalloc byte[0x20 + utfPath.Length + 0x16];
            Marshal.Copy(utfPath, 0, new IntPtr(fd + 0x21), utfPath.Length);

            pFileDesc->FileDescriptor = fd;

            return(ReadFile(pFileHandler, pFileDesc, priority, isSync));
        }
Пример #2
0
    // We need to set the correct collection for the actual material path that is loaded
    // before actually loading the file.
    private bool MtrlLoadHandler(Utf8String split, Utf8String path, ResourceManager *resourceManager,
                                 SeFileDescriptor *fileDescriptor, int priority, bool isSync, out byte ret)
    {
        ret = 0;
        if (fileDescriptor->ResourceHandle->FileType != ResourceType.Mtrl)
        {
            return(false);
        }

        var lastUnderscore = split.LastIndexOf(( byte )'_');
        var name           = lastUnderscore == -1 ? split.ToString() : split.Substring(0, lastUnderscore).ToString();

        if (Penumbra.CollectionManager.ByName(name, out var collection))
        {
#if DEBUG
            PluginLog.Verbose("Using MtrlLoadHandler with collection {$Split:l} for path {$Path:l}.", name, path);
#endif
            SetCollection(path, collection);
        }
        else
        {
#if DEBUG
            PluginLog.Verbose("Using MtrlLoadHandler with no collection for path {$Path:l}.", path);
#endif
        }

        // Force isSync = true for this call. I don't really understand why,
        // or where the difference even comes from.
        // Was called with True on my client and with false on other peoples clients,
        // which caused problems.
        ret = Penumbra.ResourceLoader.DefaultLoadResource(path, resourceManager, fileDescriptor, priority, true);
        PathCollections.TryRemove(path, out _);
        return(true);
    }
Пример #3
0
    // Load the resource from a path on the users hard drives.
    private byte DefaultRootedResourceLoad(Utf8String gamePath, ResourceManager *resourceManager,
                                           SeFileDescriptor *fileDescriptor, int priority, bool isSync)
    {
        // Specify that we are loading unpacked files from the drive.
        // We need to copy the actual file path in UTF16 (Windows-Unicode) on two locations,
        // but since we only allow ASCII in the game paths, this is just a matter of upcasting.
        fileDescriptor->FileMode = FileMode.LoadUnpackedResource;

        var fd = stackalloc byte[0x20 + 2 * gamePath.Length + 0x16];

        fileDescriptor->FileDescriptor = fd;
        var fdPtr = ( char * )(fd + 0x21);

        for (var i = 0; i < gamePath.Length; ++i)
        {
            (&fileDescriptor->Utf16FileName)[i] = ( char )gamePath.Path[i];
            fdPtr[i] = ( char )gamePath.Path[i];
        }

        (&fileDescriptor->Utf16FileName)[gamePath.Length] = '\0';
        fdPtr[gamePath.Length] = '\0';

        // Use the SE ReadFile function.
        var ret = ReadFile(resourceManager, fileDescriptor, priority, isSync);

        FileLoaded?.Invoke(gamePath, ret != 0, true);
        return(ret);
    }
Пример #4
0
    // Load the resource from an SqPack and trigger the FileLoaded event.
    private byte DefaultResourceLoad(Utf8String path, ResourceManager *resourceManager,
                                     SeFileDescriptor *fileDescriptor, int priority, bool isSync)
    {
        var ret = Penumbra.ResourceLoader.ReadSqPackHook.Original(resourceManager, fileDescriptor, priority, isSync);

        FileLoaded?.Invoke(path, ret != 0, false);
        return(ret);
    }
Пример #5
0
    private byte ReadSqPackDetour(ResourceManager *resourceManager, SeFileDescriptor *fileDescriptor, int priority, bool isSync)
    {
        if (!DoReplacements)
        {
            return(ReadSqPackHook.Original(resourceManager, fileDescriptor, priority, isSync));
        }

        if (fileDescriptor == null || fileDescriptor->ResourceHandle == null)
        {
            PluginLog.Error("Failure to load file from SqPack: invalid File Descriptor.");
            return(ReadSqPackHook.Original(resourceManager, fileDescriptor, priority, isSync));
        }

        if (!Utf8GamePath.FromSpan(fileDescriptor->ResourceHandle->FileNameSpan(), out var gamePath, false) || gamePath.Length == 0)
        {
            return(ReadSqPackHook.Original(resourceManager, fileDescriptor, priority, isSync));
        }

        // Paths starting with a '|' are handled separately to allow for special treatment.
        // They are expected to also have a closing '|'.
        if (ResourceLoadCustomization == null || gamePath.Path[0] != ( byte )'|')
        {
            return(DefaultLoadResource(gamePath.Path, resourceManager, fileDescriptor, priority, isSync));
        }

        // Split the path into the special-treatment part (between the first and second '|')
        // and the actual path.
        byte ret   = 0;
        var  split = gamePath.Path.Split(( byte )'|', 3, false);

        fileDescriptor->ResourceHandle->FileNameData   = split[2].Path;
        fileDescriptor->ResourceHandle->FileNameLength = split[2].Length;

        var funcFound = ResourceLoadCustomization.GetInvocationList()
                        .Any(f => (( ResourceLoadCustomizationDelegate )f)
                             .Invoke(split[1], split[2], resourceManager, fileDescriptor, priority, isSync, out ret));

        if (!funcFound)
        {
            ret = DefaultLoadResource(split[2], resourceManager, fileDescriptor, priority, isSync);
        }

        // Return original resource handle path so that they can be loaded separately.
        fileDescriptor->ResourceHandle->FileNameData   = gamePath.Path.Path;
        fileDescriptor->ResourceHandle->FileNameLength = gamePath.Path.Length;
        return(ret);
    }
Пример #6
0
 // Load a resource by its path. If it is rooted, it will be loaded from the drive, otherwise from the SqPack.
 internal byte DefaultLoadResource(Utf8String gamePath, ResourceManager *resourceManager, SeFileDescriptor *fileDescriptor, int priority,
                                   bool isSync)
 => Utf8GamePath.IsRooted(gamePath)
         ? DefaultRootedResourceLoad(gamePath, resourceManager, fileDescriptor, priority, isSync)
         : DefaultResourceLoad(gamePath, resourceManager, fileDescriptor, priority, isSync);