De nos jours, il est très difficile de trouver des informations solides et une aide réelle concernant S3PI, encore plus si vous essayez de créer un programme utilisant S3PI comme bibliothèque pour interagir avec les fichiers de package SIMS. De plus, la bibliothèque S3PI n'est pas sur GitHub et n'a pas de référentiel ici.
Pour ces raisons, j'ai décidé de créer ce référentiel pour stocker la bibliothèque S3PI dans son intégralité, avec des exemples de code et des conseils sur la façon de l'utiliser correctement. De plus, j'ai compilé ici toutes les informations utiles que j'ai trouvées sur Internet liées à S3PI!
Si vous souhaitez contribuer plus d'informations, veuillez créer une demande de traction ou les signaler via l'onglet "Issues". Je ferai de mon mieux pour conserver ce référentiel avec autant d'informations et d'exemples que possible, mais si l'auteur de S3PI le demande, je vais mettre ce référentiel hors ligne.
Important
Rappelant que tous les crédits pour la création de la bibliothèque S3PI vont à l'incroyable "Peter L Jones"!
L'interface du package SIMS 3 "fournit une bibliothèque de base de code portable qui" comprend "les packages de jeux SIMS3. Notez que (avec quelques ajustements mineurs), le code de la bibliothèque de base comprend également d'autres formats de package de jeu (par exemple SimCity Online, SIMS4).
Parallèlement à la bibliothèque de base, un certain nombre de "emballages" qui fournissent la partie principale du projet. Ceux-ci gèrent la désérialisation et la sérialisation des données dans le package (ou toute autre source).
D'autres outils peuvent utiliser cette bibliothèque et des emballages pour manipuler le contenu de données des fichiers de package des jeux SIMS.
Notez que le développement de cette bibliothèque et des emballages fournis ici est maintenant terminé.
Le "S3PI" est un acronyme de "SIMS3 ™ Package Interface". Il prend en charge l'accès aux données dans les fichiers "package" individuels utilisés par le jeu Electronic Arts SIMS3 ™. Un "package" est un fichier de disque avec une extension de fichier généralement de ".package" (mais d'autres extensions sont utilisées). Cependant, la principale fonction d'identification utilisée par S3PI est le cookie magique de quatre octets au début du fichier, qui doit être "DBPF". De plus, le numéro de version du format de fichier doit être 2 pour SIMS 3 ™ (ou 3 pour SIM City 5 ™, qui est à peine pris en charge).
Notez que les packages "protégés" (avec un cookie magique de "DBPP") ne sont pas pris en charge. Notez que les packages "temporaires" (avec un cookie magique de "DBBF") ne sont pas actuellement pris en charge.
Le S3PI est un ensemble d'assemblages .NET (documentés ci-dessous et disponibles ici) qui implémentent les descriptions du Wiki Sims 3 (avec ces pages alternativement disponibles ici sous le nom de wiki-mirror). Notez que cela vaut toujours la peine de vérifier là-bas si vous n'êtes pas sûr de quelque chose, soit sur le fonctionnement de quelque chose, soit si vous pensez que vous avez trouvé un problème dans S3PI, ce qui reste tout à fait possible. Ni le wiki ni la bibliothèque n'ont toujours raison - en effet, il peut se faire que les deux sont faux de différentes manières.
La bibliothèque S3PI est livrée avec un fichier d'aide compilé (.chm) qui décrit une grande partie de ce que vous devez savoir. Vous pouvez également accéder à une version de ceci, ici. Cette page essaie de donner un aperçu rapide, car la bibliothèque est assez étendue et il peut être difficile de déterminer ce que vous devez savoir.
Cette page est divisée en trois sections restantes.
Une fois que vous comprenez comment utiliser un wrapper pour accéder aux données dans un package, vous devez consulter la liste Wiki SIMS3 des types de fichiers pour voir lesquelles sont prises en charge et comment obtenir l'assistance (si cela ne fait pas partie de la distribution S3PI).
Conseil
Si vous avez des questions sans réponse, n'hésitez pas à poster ici dans le forum / les discussions. Ce sera un plaisir d'aider!
La bibliothèque est un ensemble de DLL d'assemblage .NET. Si vous êtes susceptible de travailler sur plus d'un projet en utilisant la bibliothèque, je conseille fortement de les mettre dans un dossier distinct de votre projet et de vos espaces de travail de solution - mais à proximité dans votre structure de dossier. De cette façon, il est facile de les remplacer tous sans avoir à mettre à jour chaque projet.
Pour utiliser la bibliothèque à partir d'un nouveau projet, vous devez d'abord savoir quelles parties de la bibliothèque vous allez utiliser. J'ai délibérément gardé les pièces dépendantes séparées afin que votre projet n'ait que référencer les DLL dont elle a besoin, en gardant ainsi votre projet en taille. L'inconvénient est qu'il y a beaucoup de DLL!
La première chose à remarquer est que beaucoup d'entre eux sont des "emballages" - tout ce qui avec un nom comme SomethingResource.DLL . Ceux-ci contiennent le code pour comprendre le contenu d'une ou plusieurs ressources dans un fichier de package SIMS 3.
Tout d'abord, jetons un coup d'œil à chacun des assemblages restants. Après cela, je vais fournir un aperçu de la façon de démarrer un nouveau projet. Je finirai par quelques réflexions sur l'organisation de vos pratiques de travail.
Note
Vous devez accepter la licence GPLV3 avant de distribuer votre code. Notez que, lorsque vous vous liez à une bibliothèque GPLV3, vous le réutilisez essentiellement et devez distribuer votre code en termes équivalents. Vous pouvez trouver plus de détails sur le site de la Free Software Foundation.
Quand l'inclure: toujours - il est requis par plusieurs autres parties de la bibliothèque.
Résumé: contient un certain nombre de classes qui ne se rapportent pas directement à "les Sims 3" et n'ont aucune référence à aucun des types s3pi.Interfaces . Ils pourraient, en théorie, des projets n'utilisant pas le reste de la bibliothèque S3PI (ce qui le gardait séparé).
Lire plus approfondie: En faisant référence à cet assemblage, vous obtenez ce qui suit ...
System.Collections.Generic.AHandlerList<T> - Extension abstraite de List<T> Fournissant des commentaires sur les mises à jour de la liste via un EventHandler fourni.System.ArgumentLengthException - représente une erreur dans la longueur d'un argument à une méthode.System.Extensions - Méthodes d'extension utiles non fournies par LINQ (et sans exécution différée). (Le nom de classe est douteux et peut changer ...)System.Security.Cryptography.FNV32 - Routine de hachage FNV32System.Security.Cryptography.FNV64 - Routine de hachage FNV64System.Text.SevenBitString - Lisez et écrivez une chaîne encotée en longueur en sept bits dans un Encoding donné d'un flux ou d'un Stream .System.Security.Cryptography.Sims3PackCRC - Calculez le CRC d'un morceau de données stocké dans un fichier SIMS3PACK. (OK, sans doute cela n'a aucune raison d'être ici mais en termes de dépendances de code, cela a du sens!) Quand l'inclure: toujours.
Résumé: Initialement destiné à permettre à divers paramètres de modification de la bibliothèque en remplaçant la DLL. Cela ne s'est jamais produit.
Lire plus approfondie: rien.
Quand l'inclure: toujours. Non seulement il est requis par la bibliothèque elle-même, mais elle définit l'API publique.
Résumé: fournit un certain nombre d'interfaces, de classes abstraites et de classes d'assistance utilisée dans la bibliothèque et les emballages, qui définissent les méthodes publiques fournies par les différentes classes de bibliothèque.
Lire plus approfondie:
s3pi.Interfaces Documentation de l'espace de noms. Notez que l'espace de noms est "pollué" par les classes de s3pi.GenericRCOL . Les puristes pourraient également considérer certaines des classes d'assistance "pollution" ...
Quand l'inclure: lorsque vous travaillez avec des fichiers de package SIMS 3.
Résumé: Fournit la mise en œuvre concrète des classes et des interfaces abstraites définies dans s3pi.Interfaces .
Lire plus approfondie: rien.
Quand l'inclure: lorsque vous travaillez avec des emballages de ressources.
Résumé: Si vous créez de nouveaux ressources ou des ressources de lecture à partir d'un package, c'est le mécanisme recommandé. Cependant, des alternatives existent.
Lire plus approfondie:
s3pi.WrapperDealer.WrapperDealer - Responsable de l'association ResourceType dans le IResourceIndexEntry à une classe particulière (un "wrapper") qui le comprend ou le wrapper par défaut. Le s3pi.DefaultResource et s3pi.GenericRCOLResource fournissent les bases pour comprendre comment utiliser une ressource une fois que vous y avez une référence.
Quand l'inclure: si vous voulez des noms de fichiers standard communautaires.
Résumé: Au début des Sims 3, la communauté de modding a convenu d'un format défini pour la façon dont une ressource emballée doit être nommée en dehors d'un fichier de package. Cet assemblage fournit la mise en œuvre de la bibliothèque S3PI.
Lire plus approfondie: Espace de noms s3pi.Extensions . Cette zone de la bibliothèque doit encore être correctement documentée.
Quand l'inclure: lorsque vous souhaitez CopyableMessageBox (ou IssueException).
Résumé: Cet assemblage fournit un moyen d'afficher des messages qui permet à l'utilisateur de copier facilement le contenu. À l'avenir, d'autres contrôles généraux peuvent apparaître ici.
Lire plus approfondie: CopyableMessageBox Class, CopyableMessageBoxButtons Enumeration et CopyableMessageBoxIcon Enumeration.
Quand l'inclure: lorsque vous voulez l'un des contrôles personnalisés.
Résumé: Cet assemblage fournit des contrôles personnalisés liés aux types de données dans S3PI.
Lire plus approfondie: Classe ResourceTypeCombo , classe TGIBlockCombo et classe TGIBlockListEditor . Cette zone de la bibliothèque doit encore être correctement documentée.
Quand l'inclure: lorsque vous utilisez des images DDS et que vous souhaitez les afficher dans une application WinForms.
Résumé: Cet assemblage fournit un contrôle personnalisé et une prise en charge des ressources DDS.
Lire plus approfondie: Classe DDSPanel et énumération DDSPanel.MaskChannel .
Quand l'inclure: peut être utile lors de l'écriture d'une aide pour S3PE.
Résumé: fournit un soutien à la cartographie d'une ressource à un ou plusieurs programmes qui peuvent être intéressés par ce type de ressource.
Lire plus approfondie: s3pi.Helpers Espace de noms. Cette zone de la bibliothèque doit encore être correctement documentée.
Configurez les références dont vous avez besoin en fonction de la section précédente System.Custom , s3pi.Settings , s3pi.Interfaces et tout autre. De plus, vous devrez déterminer si vous souhaitez référencer des assemblages de wrapper spécifiques. Dans la plupart des cas, ce sera l'approche appropriée. Vos assemblages référencés seront, par défaut, copiés dans le dossier de sortie du projet, ainsi que votre programme.
Notez qu'il existe certains composants supplémentaires utilisés, tels que les fichiers de configuration, que vous devrez également organiser la création de votre projet à copier (si plus récent) dans le dossier de sortie.
Vous voudrez peut-être regarder les solutions S3OC et S3PE Visual Studio pour voir comment je l'ai fait.
S3PI fournit un certain nombre de classes C # pour aider les programmes souhaitant accéder aux fichiers de package SIMS 3 et les ressources stockées en eux. La "bibliothèque de base" ne comprend que le conteneur de package lui-même - il n'a aucune compréhension du contenu des ressources. Cela est délégué à des "emballages". Les emballages sont associés aux ressources en déclarant la liste des ResourceTypes qu'ils soutiennent. La bibliothèque de base renvoie une instance de la classe de ressources approcheuse à la bibliothèque "client".
Ainsi, les emballages ont deux objectifs principaux: fournir une «compréhension» du contenu d'un ou plusieurs types de ressources et de faire savoir à la bibliothèque de base quels types de ressources ils comprennent. Un wrapper peut fournir un ou plusieurs gestionnaires de ressources tels qu'il le semble, mais les auteurs sont encouragés à garder différentes préoccupations séparées en différents emballages.
La bibliothèque de base identifie les emballages en recherchant dans les assemblages du dossier de la bibliothèque S3PI pour ceux qui ont l'interface appropriée.
Le WrapperDealer a une interface qui permet aux applications client d'activer et de désactiver des combinaisons particulières de ResourceType et de wrapper.
J'ai maintenant inclus DefaultResource dans la documentation de la bibliothèque S3PI.
L'exemple le plus simple est celui donné dans DefaultResource dans la distribution de la source. Il "ne fait rien" au-delà de ce qui est essentiel pour tout emballage.
Il définit deux classes:
public class DefaultResource : AResource { }
public class DefaultResourceHandler : AResourceHandler { } Un assemblage contenant un wrapper doit contenir une classe implémentant AResourceHandler . Cette classe fournit une recherche IDictionary<Type, List<string>> entre une classe qui implémente AResource et une liste de chaînes contenant des valeurs ResourceType . (Les chaînes utilisées sont les valeurs du ResourceType TypedValue à une chaîne, c'est-à-dire une chaîne hexagonale.)
DefaultResource utilise "*" pour faire savoir WrapperDealer qu'il est heureux de tout prendre. Ne l'utilisez pas dans vos emballages!
DefaultResource , alors.
Il est fortement recommandé d'avoir const Int32 recommendedApiVersion = 1; Au sommet de votre classe pour une compatibilité future. Cela vous permet de "version" l'API de votre emballage si vous en avez besoin. Mais probablement pas utile dans la pratique.
Vous devez avoir la propriété AApiVersionedFields RecommendedApiVersion .
Le constructeur de DefaultResource démontre un point important - une nouvelle ressource est créée en ne sachant rien . Il peut vérifier qu'il est nouveau en vérifiant que le flux passé au constructeur est nul. Il devrait ensuite créer un MemoryStream et le remplir avec le contenu de données minimum valide pour la ressource.
C'est ça! Il n'y a rien d'autre que vous devez faire. Bien sûr, vous n'avez pas encore fourni une raison d'utiliser votre emballage ...
Note
Après certains, ImageResource et TextResource sont considérés comme dépréciés. Cependant, ils resteront dans la bibliothèque dans un avenir prévisible. Malgré ce qui est écrit ci-dessous, je ne le considère plus comme une bonne idée d'avoir un wrapper pour une ressource qui n'est qu'un flux d'octets.
L'approche adoptée pour les ressources DDS est considérée comme plus correcte. Un coroll est directement énoncé ci-dessous: La conception ici a été influencée par S3PE plutôt que de mettre en œuvre quoi que ce soit concernant la structure des données elle-même, qui est le but d'un wrapper. Cela aurait dû être géré de manière similaire aux ressources DDS.
Un autre coroll a été l'ajout, puis la suppression de l'emballage des ressources _VID pendant un cycle QA lorsqu'il a été découvert que le contenu était purement la sortie d'un codec audiovisuel artistique électronique personnalisé, plutôt que d'avoir un contenu utilement déchiffrable.
En conclusion: la "valeur" devrait être une chaîne. Normalement, le formateur intégré en construit un approprié de vos propriétés publiques.
Un exemple légèrement plus complexe est ImageResource. Il colle au modèle à deux classes:
public class ImageResource : AResource { }
public class ImageResourceHandler : AResourceHandler { }Cet wrapper gère les images - de nombreux types de ressources différents sont simplement des fichiers PNG89 stockés.
Le constructeur statique pour ImageResourceHandler lit une liste de tous les types de ressources d'image connus (à partir d'un fichier dans le dossier où se trouve l'assemblage). Ceci est ensuite utilisé pour remplir la liste de liste IDictionary<Type, List<string>> dans le constructeur d'instance, qui est ensuite utilisé par WrapperDealer . Cela signifie qu'il est facile d'ajouter de nouveaux types de ressources à la liste des prises en charge - il suffit de modifier le fichier texte et de les ajouter, pas besoin de se recompiler. (C'est un si bon modèle, j'aurais dû faciliter la réutilisation ...)
La classe ImageResource reste assez simple. Son constructeur garantit qu'il y a une image PNG89 valide si elle a passé un flux nul. Et il fournit une seule propriété appelée "valeur" qui renvoie un System.Drawing.Image créé à partir des données PNG89. Il ne prend pas en charge la sauvegarde d'images à une ressource existante.
La propriété Value mérite d'être mentionnée davantage - la démo S3PI (maintenant connue sous le nom de S3PE) vérifie si une ressource a une propriété Value et, dans l'affirmative, qu'elle ait un type de données d'image ou de chaîne. Il sait comment afficher ces deux (et ces deux seuls, actuellement). Le retour de celui approprié peut être utile dans le débogage.
Vous remarquerez que la position du flux est défini sur zéro avant utilisation - supposons toujours qu'elle est dans un état inconnu.
Le wrapper TextResource est très similaire - les ressources textuelles sont définies dans un fichier; Il a une propriété "valeur" renvoyant la ressource en tant que valeur de chaîne. De plus, il a des propriétés spécifiques au texte, y compris l'accès aux données sous forme de XML. (Ce serait mieux le design d'avoir l'emballage XML comme extension de l'emballage de texte et de gérer uniquement les fichiers XML connus ...)
Ce wrapper gère un seul type de ressource - la carte du nom du package. Il fournit une interface IDictionary<ulong, string> , permettant à la carte d'être lue et mise à jour. C'est un exemple très simple de la façon de gérer les mises à jour.
Voici comment cela fonctionne. Ceci est un exemple simple, mais le motif peut être étendu à des besoins plus complexes.
Le travail se fait dans un certain nombre d'endroits:
La propriété Stream vérifie si la ressource a été sale (c'est-à-dire changé). Si c'est le cas, il rejette le flux actuel et appelle UnParse() .
Le constructeur d'instance vérifie un flux nul et appelle UnParse() pour construire la ressource valide minimale.
La méthode Parse(Stream s) lit les données dans la structure de données utilisées pour les manipuler - ici un Dictionary<ulong, string> . Comme il s'agit d'une structure répétitive des entrées de longueur variables qui permet d'insérer les données, il n'est pas efficace d'utiliser les données du flux.
La méthode UnParse() exporte la structure de données vers un nouveau flux, créant de nouveaux objets si nécessaire.
Comme mentionné ci-dessus, la propriété Stream doit savoir si la ressource a été sali. La mise en œuvre de l'interface IDictionary<ulong, string> s'en occupe en appelant OnResourceChanged(this, EventArgs.Empty) pour les opérations de mise à jour. Ceci est fourni sur AResource et définit la ressource pour sale, ainsi que pour appeler les gestionnaires ResourceChanged de tout ce qui écoute l'événement.
CatalResource prend vraiment les idées dans NameMapResource davantage. Il a une classe abstraite pour un ensemble de ressources connexes. J'ai essayé de garder un modèle de codage cohérent pour aider à comprendre ce qui se passe. Je le laisse comme un exercice au lecteur de travailler à travers la mise en œuvre pour voir comment toutes les classes interagissent. J'espère que cela devrait avoir un sens! (Sinon, je serai perplexe quand un bug apparaît !!)
Cela a l'avantage d'être récent et, bien que relativement simple, a des bits intéressants. C'est aussi l'un des emballages que je me consulte le plus souvent en écrivant un nouveau.
Une ressource RCOL est une ressource normale, comme décrit ci-dessus - avec la différence fondamentale qu'il s'agit d'un conteneur pour d'autres "ressources", appelées blocs RCOL. Chaque bloc a un format identifié par un code de quatre caractères ("quatrecc" ou tag); Les blocs ont également des types de ressources. La ressource RCOL (dans le package) a le même type que le premier bloc RCOL (dans la ressource) et la ressource porte le nom de ce premier bloc RCOL. Certaines ressources RCOL ne contiennent qu'un seul bloc RCOL; D'autres contiennent plusieurs blocs RCOL.
Le support de base est fourni par s3pi.GenericRCOLResource . En plus de lire des blocs et d'écrire des blocs au package, l'emballage des ressources a des méthodes supplémentaires pour prendre en charge le format RCOL et il existe un registre des gestionnaires de blocs RCOL.
(Notez que le terme "morceau" est utile pour se référer à un bloc RCOL parfois ...)
Il existe une classe abstraite, ARCOLBlock , qui définit les fondamentaux. Il y a une implémentation par défaut, DefaultRCOL , car lorsqu'aucun autre gestionnaire de blocs RCOL correspondant n'est défini dans le registre, ce qui fournit un support minimal.
Outre l'extension ARCOLBlock plutôt AResource , la tâche d'écrire un gestionnaire de blocs RCOL est très similaire à l'écriture d'un wrapper de ressources.
Cependant, les arts électroniques / Maxis ont récemment commencé à rendre la vie un peu plus difficile dans la mesure où certains conteneurs à RCOL ne sont pas exactement conformes à la compréhension originale du fonctionnement du conteneur. Soyez conscient de cela lorsque vous lisez le code ou en écrivant le vôtre.
Tout d'abord, vous devez avoir de l'expérience et savoir déjà comment modifier les fichiers de package SIMS, à l'aide du logiciel S3PE. S3PE fonctionne essentiellement comme une interface graphique pour S3PI, que les utilisateurs courants peuvent utiliser. Par conséquent, il est sûr de dire que S3PI est capable de faire tout ce que S3PE peut faire, et encore plus.
Note
Le S3PE est également disponible en téléchargement dans ce référentiel. À la fois son fichier exécutable et son code source C # et sa solution Visual Studio.
Sachant cela, pour implémenter S3PI dans votre programme, vous devez coder comme si votre programme utilisait S3PE pour effectuer l'action.
Disons que vous souhaitez supprimer une certaine ressource d'un fichier de package. Si vous utilisiez S3PE, vous ouvrez d'abord ce fichier de package, recherchez la ressource à supprimer. Ensuite, vous appuyez sur la touche DEL pour supprimer ce fichier, puis enregistrez. Une fois le fichier de package enregistré, vous le fermerez.
Si vous le faites en utilisant S3PE, votre programme doit effectuer les mêmes étapes si vous utilisez S3PI pour effectuer cette même action. Voir l'exemple de code ci-dessous en effectuant l'action de la suppression de la ressource "0x00b2d882-0x00000000-0x0a12300000ff0000" à partir d'un fichier de package ouvert, par exemple ...
using s3pi ;
using s3pi . Interfaces ;
using s3pi . Package ;
//Open the package
IPackage package = Package . OpenPackage ( 0 , "C:/Folder/someFile.package" , true ) ;
//Search the resource "0x00B2D882-0x00000000-0x0A12300000FF0000" inside the package
foreach ( IResourceIndexEntry item in package . GetResourceList )
{
//Get current entrie resource TGI
string typeHex = GetLongConvertedToHexStr ( item . ResourceType , 8 ) ;
string groupHex = GetLongConvertedToHexStr ( item . ResourceGroup , 8 ) ;
string instanceHex = GetLongConvertedToHexStr ( item . Instance , 16 ) ;
//If is the target resource, delete it
if ( typeHex == "0x00B2D882" && groupHex == "0x00000000" && instanceHex == "0x0A12300000FF0000" )
package . DeleteResource ( item ) ;
}
//Save the changes
package . SavePackage ( ) ;
//Close the package
Package . ClosePackage ( 0 , package ) ; Important
Il est très important que vous fermiez toujours un fichier de package ouvert après avoir fini de travailler dessus.
Note
Le lanceur de rêves est un lanceur pour les Sims 3 créés par "Marcos4503". Le lanceur de rêves utilise S3PI pour implémenter diverses fonctionnalités, telles que la fusion des packages, le nettoyage des sauvegardes et d'autres fonctions. Vous pouvez suivre ce lien pour consulter le référentiel de lanceur de rêves et jeter un œil au code source pour plus d'exemples d'utilisation de S3PI. Le lanceur de rêves a été créé en utilisant C # comme langage de programmation.
using s3pi ;
using s3pi . Interfaces ;
using s3pi . Package ;
//Creates a new Package that will receive the resources from 2 other Packages
IPackage finalPackage = Package . NewPackage ( 0 ) ;
//Open the Package 1 and copy all resources to the final package
IPackage package1 = Package . OpenPackage ( 0 , "C:/Folder/package1.package" , false ) ;
foreach ( IResourceIndexEntry item in package1 . GetResourceList )
finalPackage . AddResource ( item , ( package1 as APackage ) . GetResource ( item ) , true ) ;
Package . ClosePackage ( 0 , package1 ) ;
//Open the Package 2 and copy all resources to the final package
IPackage package2 = Package . OpenPackage ( 0 , "C:/Folder/package2.package" , false ) ;
foreach ( IResourceIndexEntry item in package2 . GetResourceList )
finalPackage . AddResource ( item , ( package2 as APackage ) . GetResource ( item ) , true ) ;
Package . ClosePackage ( 0 , package2 ) ;
//Enable compression for all viable resources of final merged package (the same way S3PE does)
foreach ( IResourceIndexEntry item in finalPackage . GetResourceList )
item . Compressed = ( ushort ) ( ( item . Filesize != item . Memsize ) ? 0xFFFF : 0x0000 ) ;
//Saves the final Package, result of the merge
finalPackage . SaveAs ( "C:/Folder/finalFile.package" ) ;
Package . ClosePackage ( 0 , finalPackage ) ; using s3pi ;
using s3pi . Interfaces ;
using s3pi . Package ;
//Open a .nhd save file
IPackage nhdSaveFile = Package . OpenPackage ( 0 , "C:/Folder/saveFile.nhd" , false ) ;
//Search inside the package, by the first thumbnail of type "SNAP" (or hex type "0x6B6D837E")
foreach ( IResourceIndexEntry item in nhdSaveFile . GetResourceList )
if ( GetLongConvertedToHexStr ( item . ResourceType , 8 ) == "0x6B6D837E" )
{
//Get the base stream for this resource
Stream aPackageStream = ( nhdSaveFile as APackage ) . GetResource ( item ) ;
//Get the base resource using the "ImageResource" s3pi wrapper***
IResource baseResource = ( IResource ) ( new ImageResource . ImageResource ( 0 , aPackageStream ) ) ;
//Get the bitmap from base resource stream
BitmapImage bitmapImage = new BitmapImage ( ) ;
bitmapImage . BeginInit ( ) ;
bitmapImage . StreamSource = baseResource . Stream ;
bitmapImage . CacheOption = BitmapCacheOption . OnLoad ;
bitmapImage . EndInit ( ) ;
bitmapImage . Freeze ( ) ;
//... continue ...//
//Cancel the search
break ;
}
//Close the save file
Package . ClosePackage ( 0 , nhdSaveFile ) ; *** Notez que ici, nous pourrions utiliser la classe WrapperDealer afin que S3PI nous fournisse automatiquement le wrapper pour la ressource avec laquelle nous travaillons. Si nous y utilisons WrapperDealer , S3PI nous apporterait automatiquement le wrapper ImageResource , cependant, WrapperDealer est connu pour être incompatible avec certains frameworks .NET, tels que WPF lui-même, provoquant des accidents. Pour cette raison, il est toujours recommandé d'utiliser directement l'emballage, lorsque vous travaillez avec une ressource dans le fichier de package. Si nous choisissons d'utiliser la classe WrapperDealer pour obtenir l'image, l'extrait de code ressemblerait à ceci ...
//...
//Get the resource using WrapperDealer
IResource resource = WrapperDealer . GetResource ( 0 , nhdSaveFile , item , true ) ;
//Get the bitmap from base resource stream
BitmapImage bitmapImage = new BitmapImage ( ) ;
bitmapImage . BeginInit ( ) ;
bitmapImage . StreamSource = resource . Stream ;
bitmapImage . CacheOption = BitmapCacheOption . OnLoad ;
bitmapImage . EndInit ( ) ;
bitmapImage . Freeze ( ) ;
//... using s3pi ;
using s3pi . Interfaces ;
using s3pi . Package ;
//Open a package that contains a CASP resource
IPackage openedPackage = Package . OpenPackage ( 0 , "C:/Folder/clothes.package" , true ) ;
//Search the first CASP (or hex type 0x034AEECB) resource inside the package
foreach ( IResourceIndexEntry item in openedPackage . GetResourceList )
if ( GetLongConvertedToHexStr ( item . ResourceType , 8 ) == "0x034AEECB" )
{
//Get the CASP stream
Stream caspStream = WrapperDealer . GetResource ( 1 , openedPackage , item , true ) . Stream ;
//Get the CASP resource
CASPartResource . CASPartResource sourceCASpart = new CASPartResource . CASPartResource ( 1 , caspStream ) ;
//Allow this CASP for Random Sims
sourceCaspart . ClothingCategory |= CASPartResource . ClothingCategoryFlags . ValidForRandom ;
//Disallow this CASP for Random Sims
sourceCaspart . ClothingCategory &= ~ CASPartResource . ClothingCategoryFlags . ValidForRandom ;
//Delete the old CASP resource
openedPackage . DeleteResource ( item ) ;
//Add the new modified resource
openedPackage . AddResource ( ( ( IResourceKey ) item ) , ( ( AResource ) sourceCaspart ) . Stream , true ) ;
//Release streams
caspStream . Dispose ( ) ;
caspStream . Close ( ) ;
( ( AResource ) sourceCaspart ) . Stream . Dispose ( ) ;
( ( AResource ) sourceCaspart ) . Stream . Close ( ) ;
}
//Save the package and close it
openedPackage . SavePackage ( ) ;
Package . ClosePackage ( 0 , openedPackage ) ; Note
Ici, nous utilisons WrapperDealer pour accéder à la ressource CASP, mais nous pourrions également utiliser le wrapper CASPartResource directement pour accéder à la ressource CASP.
Comme vous le comprenez probablement déjà, les fichiers de package SIMS sont comme un "fichier zip", contenant plusieurs autres fichiers à l'intérieur. Chaque fichier / ressource dans un fichier de package a un TGI associé.
Le TGI est essentiellement le type, le groupe et l'instance. Le type et le groupe sont hexadécimaux à 8 chiffres, tandis que l'instance est un hexadécimal à 16 chiffres. Afin d'éviter les conflits entre les ressources lorsqu'ils sont chargés par le jeu, chaque ressource présente dans tous les packages chargés par le jeu doit avoir une combinaison TGI unique.
Lorsque vous ouvrez un fichier de package avec S3PE, vous pouvez facilement voir le type, le groupe et l'instance de chaque ressource présente dans le package ouvert. Maintenant que vous le savez, gardez à l'esprit que lors de la modification des fichiers de package SIMS, vous devez toujours vous assurer que les ressources que vous insérez dans les fichiers de package doivent toujours avoir un TGI unique.
Si vous avez d'autres questions à ce sujet, vous pouvez lire cet article, qui parle et explique très bien ce que sont les conflits de mod, comment ils se produisent, comment les résoudre, etc.
Si vous avez lu jusqu'ici, vous devriez avoir une compréhension décente de ce qu'est S3PI et comment l'utiliser. Si vous souhaitez accéder à l'ancien référentiel officiel S3PI, vous pouvez utiliser ce lien. Se souvenir que tout crédit pour la création de la bibliothèque S3PI va à Peter L Jones.
Référentiel créé avec Marcos Tomaz