如今,很难找到有关S3PI的可靠信息和真正的帮助,如果您尝试使用S3PI作为库来与SIMS软件包文件进行交互,则更是如此。此外,S3PI库不在github上,这里没有存储库。
由于这些原因,我决定创建此存储库以整体存储S3PI库,并提供有关如何正确使用它的代码示例和提示。此外,我在这里汇编了与S3PI相关的Internet上发现的所有有用信息!
如果您想贡献更多信息,请创建拉动请求或通过“问题”选项卡进行报告。我将尽力保留尽可能多的信息和示例,但是如果S3PI的作者要求它,我将脱机此存储库。
重要的
记住,创建S3PI图书馆的所有学分都可以转到令人难以置信的“ Peter L Jones”!
“ SIMS 3软件包接口”提供了“理解” SIMS3游戏软件包的便携式代码的核心库。请注意,(一些次要调整)核心库代码还了解其他游戏程序包格式(例如Simcity Online,SIMS4)。
与核心库一起,还有许多提供项目主要部分的“包装器”。这些处理包装中数据(或任何其他源)中数据的序列化和序列化。
进一步的工具可以使用此库和包装器来操纵SIMS游戏的软件包文件的数据内容。
请注意,此图书馆的开发以及此处提供的包装纸现已完成。
“ S3PI”是“ SIMS3™软件包接口”的首字母缩写词。它提供了支持,以访问电子Arts SIMS3™游戏使用的单个“软件包”文件中的数据。 “软件包”是一个通常“ .package”的文件扩展名的磁盘文件(但使用其他扩展名)。但是,S3PI使用的主要识别功能是文件开头的四个字节魔法曲奇,必须是“ DBPF”。此外,对于SIMS 3™,文件格式版本编号必须为2(SIMS City 5™,几乎不支持SIMS City 5™)。
请注意,不支持“受保护的”软件包(带有“ DBPP”的魔法曲奇)。请注意,当前不支持“临时”软件包(带有“ DBBF”的魔法曲奇)。
S3PI是一组.NET组件(以下记录并在此处可用),该组件实现了Sims 3 Wiki的描述(此处可作为Wiki-Mirror提供)。请注意,如果您不确定某些事情应该如何工作,或者您认为您在S3PI中发现问题,这总是值得检查的,这仍然很可能。 Wiki和图书馆都不是正确的 - 实际上,两者都以不同的方式是错误的。
S3PI库带有一个编译的帮助文件(.CHM),该文件描述了您需要知道的许多内容。您也可以在此处访问此版本。此页面试图快速概述,因为库非常广泛,并且可以弄清楚您需要知道的内容可能很棘手。
此页面分为剩下的三个部分。
一旦您了解了如何使用包装器访问软件包中的数据,则应查阅SIMS3 Wiki的文件类型列表,以查看受支持的文件以及如何获得支持(如果不是S3PI分发的一部分)。
提示
如果您有未解决的问题,请随时在论坛/讨论中发布。很高兴提供帮助!
该库是一组.NET组件DLL。如果您可能使用库从一个以上的项目进行工作,我强烈建议将它们放在与项目和解决方案工作区分开的文件夹中 - 但在您的文件夹结构中附近。这样,不必更新每个项目就很容易替换它们。
要使用新项目中的库,您首先需要知道要使用的库中哪些部分。我故意将依赖性部件分开,以便您的项目只需要引用所需的DLL,从而使您的项目规模降低。缺点是有很多DLL!
要注意的第一件事是,其中许多是“包装纸” - 任何名字都像SomethingResource.DLL 。这些包含代码,以了解SIMS 3软件包文件中一个或多个资源的内容。
首先,让我们看一下每个剩余的组件。之后,我将提供有关如何启动新项目的概述。我会以一些关于组织您的工作实践的想法。
笔记
在分发代码之前,您必须同意GPLV3许可证。请注意,当您链接到GPLV3库时,您本质上是重新使用它,并且必须按照等效的条款分发代码。您可以在免费软件基金会网站上找到更多详细信息。
何时包含它:总是 - 图书馆的其他几个部分都需要。
摘要:包含许多与“ Sims 3”直接相关的类,并且没有对任何s3pi.Interfaces类型的引用。可以想象,它们可以用于不使用其余S3PI库的项目(因此将其分开)。
进一步阅读:通过引用此组件,您将获得以下内容...
System.Collections.Generic.AHandlerList<T> - List<T>通过提供的EventHandler提供列表更新的反馈。System.ArgumentLengthException表示参数长度的错误。System.Extensions LINQ未提供的有用扩展方法(并且没有延期执行)。 (班级名称可疑,可能会改变...)System.Security.Cryptography.FNV32 -FNV32哈希例程System.Security.Cryptography.FNV64 -FNV64哈希例程System.Text.SevenBitString在从或到Stream的给定Encoding中,读写一个七位的编码长度排名的字符串。System.Security.Cryptography.Sims3PackCRC计算存储在sims3pack文件中的数据块的CRC。 (好吧,可以说这没有理由在这里,但是就代码依赖而言,这是有道理的!) 何时包括:总是。
摘要:最初旨在允许通过替换DLL更改库的各种设置。这从未发生过。
进一步阅读:什么都没有。
何时包括:总是。图书馆本身不仅需要它,而且定义了公共API。
摘要:提供了整个库和包装器中使用的许多接口,抽象类和辅助类别,这些类别定义了各种库类提供的公共方法。
进一步阅读:
s3pi.Interfaces名称空间文档。请注意,名称空间是由s3pi.GenericRCOL类“污染”的。纯粹主义者也可能会考虑一些助手类“污染” ...
何时包含它:使用SIMS 3软件包文件时。
摘要:提供s3pi.Interfaces中定义的抽象类和接口的具体实现。
进一步阅读:什么都没有。
何时包含它:使用资源包装器时。
摘要:如果您正在创建新的资源或从软件包中读取资源,这是推荐的机制。但是,确实存在替代方案。
进一步阅读:
s3pi.WrapperDealer.WrapperDealer负责将IResourceIndexEntry中的ResourceType与理解它或默认包装器的特定类(A wrapper')关联。 s3pi.DefaultResource和s3pi.GenericRCOLResource提供了一旦参考的基础,以了解如何使用资源。
何时包括:如果您想要社区标准文件名。
摘要:在《模拟人生3》开始时,修改社区同意在包装文件外部如何命名包装资源的设定格式。该组件为S3PI库提供了实现。
进一步读取: s3pi.Extensions名称空间。图书馆的该区域仍然需要正确记录。
何时包含它:当您需要CopyAbleMessageBox(或ISSEATEXCEPTION)时。
摘要:此组件提供了一种显示消息的方法,使用户可以轻松复制内容。将来,其他一般控制可能会出现在这里。
进一步读取: CopyableMessageBox类, CopyableMessageBoxButtons枚举和CopyableMessageBoxIcon枚举。
何时包括:当您想要一个自定义控件之一时。
摘要:此组件提供了与S3PI中数据类型相关的自定义控件。
进一步阅读: ResourceTypeCombo类, TGIBlockCombo类和TGIBlockListEditor类。图书馆的该区域仍然需要正确记录。
何时包含它:当您使用DDS映像并想要一种在Winforms应用程序中显示它们的方法。
摘要:此组件提供了自定义控件和DDS资源支持。
进一步阅读: DDSPanel类和DDSPanel.MaskChannel枚举。
何时包含它:在为S3PE编写助手时可能很有用。
摘要:提供将资源映射到可能对此类资源感兴趣的一个或多个程序的支持。
进一步阅读: s3pi.Helpers名称空间。图书馆的该区域仍然需要正确记录。
基于上一节System.Custom , s3pi.Settings , s3pi.Interfaces等设置您需要的引用。此外,您将需要考虑是否要参考特定的包装程序集。在大多数情况下,这将是适当的方法。默认情况下,您的引用组件将与您的程序一起复制到项目输出文件夹。
请注意,还有某些其他组件,例如配置文件,您还需要安排项目的构建(如果更新)到输出文件夹。
您可能想查看S3OC和S3PE Visual Studio Solutions,以了解我的完成方式。
S3PI提供了许多C#类,以协助想要访问SIMS 3个软件包文件以及存储在其中的资源的程序。 “核心库”仅了解包装容器本身 - 它对资源的内容不了解。那是委派给“包装纸”的。包装器通过声明其支持的ResourceTypes清单与资源相关联。核心库将学术资源类的实例返回到库“客户端”。
因此,包装器具有两个主要目的:提供对一种或多种资源类型的内容的“理解”,并让核心库知道他们理解的哪些资源类型。包装器可以在认为合适的情况下提供一个或多个资源处理程序,但鼓励作者将不同的担忧分为不同的包装器。
核心库通过在S3PI库文件夹中的组件中搜索具有适当接口的人来标识包装器。
WrapperDealer具有一个接口,可让客户端应用程序启用和禁用ResourceType和Warpper的特定组合。
我现在在S3PI库文档中包括了DefaultResource 。
最简单的示例是源分布中的DefaultResource中给出的示例。它“无所作为”超出对任何包装器的必要意义。
它定义了两个类:
public class DefaultResource : AResource { }
public class DefaultResourceHandler : AResourceHandler { }包含包装器的组件必须包含实现AResourceHandler类。此类提供IDictionary<Type, List<string>>在实现AResource类和包含ResourceType值的字符串列表之间查找。 (所使用的字符串是从ResourceType TypedValue到字符串的值,即十六进制字符串。)
DefaultResource使用“*”,让WrapperDealer知道拿走一切很乐意。不要在包装纸中使用它!
然后, DefaultResource 。
强烈建议您使用const Int32 recommendedApiVersion = 1;在班级的顶部,以使以后的兼容性。如果需要,这确实允许您“版本”包装器的API。不过,在实践中可能没有那么有用。
您必须拥有属性AApiVersionedFields RecommendedApiVersion 。
DefaultResource的构造函数证明了一个重要的一点 - 新资源是不了解的。它可以通过检查传递给构造函数的流是否为空,可以检查它是新的。然后,它应该创建一个MemoryStream ,并使用资源的最小有效数据内容填充它。
就是这样!您无需做任何其他事情。当然,您还没有为任何人提供使用包装纸的理由...
笔记
经过一番思考, ImageResource和TextResource被认为是贬低的。但是,在可预见的将来,它们将留在图书馆。尽管下面写了什么,但我不再认为拥有一个仅是字节流的资源的包装器是一个好主意。
DDS资源采用的方法被认为更正确。下面直接指出的是:此处的设计受S3PE的影响,而不是针对数据本身的结构实施任何内容,这是包装器的目的。这应该以类似于DDS资源的方式处理。
进一步的推出是在质量检查周期内的添加,然后去除_VID资源包装器,当时发现内容纯粹是自定义电子艺术视听编解码器的输出,而不是具有任何可有效的解密内容。
总之:“值”应该只是一个字符串。通常,内置格式化器会从您的公共物业中构建合适的建筑。
Imageresource是一个稍微复杂的例子。它坚持两个类模型:
public class ImageResource : AResource { }
public class ImageResourceHandler : AResourceHandler { }此包装器处理图像 - 许多不同的资源类型只是存储的PNG89文件。
ImageResourceHandler的静态构造函数读取了所有已知图像资源类型的列表(来自汇编所在的文件夹中的文件)。然后,它用于填充列表IDictionary<Type, List<string>>实例构造函数中的列表,然后由WrapperDealer使用。这意味着将新的资源类型添加到该包装支持的列表中很容易 - 只需编辑文本文件并添加它们,而无需重新编译即可。 (这是一个很好的模式,我应该使重复使用更容易...)
ImageResource类仍然相当简单。它的构造函数可确保如果通过了空流,则有一个有效的PNG89图像。它提供了一个称为“ value”的单个属性,该属性返回从PNG89数据创建的System.Drawing.Image 。它不支持将图像保存回现有资源。
值得进一步提及的Value属性 - S3PI演示前端(现在称为S3PE)检查资源是否具有Value属性,如果是的,则是否具有图像或字符串数据。它知道如何展示这两个(目前)。返回适当的一个对于调试可能很有用。
您会注意到流到使用前将流定位设置为零 - 始终假设它处于未知状态。
TextResource包装器非常相似 - 文件中定义了文本资源;它具有“值”属性,该属性将资源作为字符串值返回。此外,它具有一些特定于文本的属性,包括将数据访问为XML。 (将XML包装器作为文本包装器的扩展程序,并且仅处理已知的XML文件是更好的设计...)
该包装器处理单个资源类型 - 软件包名称映射。它提供了IDictionary<ulong, string>接口,允许阅读和更新地图。这是如何处理更新的一个非常简单的示例。
这是它的工作方式。这是一个简单的示例,但模式可以扩展到更复杂的需求。
这项工作是在许多地方完成的:
Stream属性检查资源是否已被污垢(即更改)。如果是这样,它会丢弃当前流并调用UnParse() 。
实例构造函数检查空流,并调用UnParse()以构建最小有效资源。
Parse(Stream s)方法将数据读取到用于操纵它的数据结构中 - 在这里用Dictionary<ulong, string> 。由于这是允许插入数据的不同长度条目的重复结构,因此在流中使用数据并不有效。
UnParse()方法将数据结构导出回新流,在需要时创建新对象。
如上所述, Stream属性需要知道资源是否已被弄脏。 IDictionary<ulong, string>接口的实现通过致电OnResourceChanged(this, EventArgs.Empty)来解决此问题。这是在AResource上提供的,并将资源设置为Dirty,并拨打了聆听该活动的任何内容的ResourceChanged处理程序。
Catalogresource确实将这些想法进一步提高了。它具有用于一组相关资源的抽象类。我试图保持一致的编码模式,以帮助了解正在发生的事情。我将其作为练习,向读者进行实施,以了解所有类的交互方式。希望这应该有意义! (如果没有,当错误出现时,我会很难!!)
这具有最近的好处,尽管相对简单,但确实有一些有趣的位。它也是我在编写新的包装纸中最经常咨询自己的包装纸之一。
RCOL资源是一种普通资源,如上所述 - 基本差异是它是其他“资源”的容器,称为RCOL块。每个块的格式由四个字符代码(“ fourcc”或tag)识别;这些块还具有资源类型。 RCOL资源(在软件包中)具有与(资源中)中的第一个RCOL块相同的类型,并且该资源以第一个RCOL块命名。一些RCOL资源仅包含一个RCOL块;其他包含多个RCOL块。
基本支持由s3pi.GenericRCOLResource提供。除了读取块并将块写入软件包外,资源包装器还具有支持RCOL格式的其他方法,并且还有RCOL块处理程序的注册表。
(请注意,术语“块”宽松地用于参考RCOL块...)
有一个抽象类, ARCOLBlock ,它定义了基本面。有一个默认的实现, DefaultRCOL ,因为当注册表中未定义其他匹配的RCOL块处理程序时,可以提供最小的支持。
除了扩展ARCOLBlock而不是AResource之外,编写RCOL块处理程序的任务与编写资源包装器非常相似。
但是,电子艺术 /马克西斯最近开始使生活更加艰难,因为某些单rcol容器并不完全符合对容器应如何工作的原始理解。在阅读代码或自己编写时要注意这一点。
首先,您需要有经验,并且已经知道如何使用S3PE软件编辑SIMS软件包文件。 S3PE基本上是S3PI的图形接口,可以使用普通用户。因此,可以肯定地说S3PI能够做S3PE可以做的一切,甚至更多。
笔记
S3PE也可以在此存储库中下载。其可执行文件及其C#源代码和Visual Studio解决方案均包括。
知道这一点,要在程序中实现S3PI,您需要代码,就好像您的程序使用S3PE执行操作一样。
假设您要从包文件中删除某个资源。如果使用S3PE,则首先打开该软件包文件,搜索删除资源。然后,您将按DEL键删除该文件,然后保存。包装文件保存后,您将关闭它。
如果您使用S3PE执行此操作,则如果您使用S3PI执行相同的操作,则您的程序应执行相同的步骤。请参阅下面的示例代码,执行删除资源的操作“ 0x00B2D882-0X000000000000-0X0A12300000FF0000”,以示例...
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 ) ; 重要的
完成处理后,始终关闭打开的包装文件非常重要。
笔记
Dream Launcher是由“ Marcos4503”创建的《模拟人生3》的启动器。 Dream Launcher利用S3PI来实现各种功能,例如合并软件包,清理储蓄和其他功能。您可以按照此链接查看Dream Launcher存储库,并查看源代码以获取更多S3PI使用示例。 Dream Launcher是使用C#作为编程语言创建的。
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 ) ; ***请注意,在这里,我们可以使用WrapperDealer类,以便S3PI自动为我们使用的资源提供正确的包装器。如果我们在那里使用WrapperDealer ,S3PI会自动为我们带来ImageResource包装器,但是,众所周知, WrapperDealer与某些.NET框架(例如WPF本身)不相容,从而导致崩溃。因此,当使用包装文件中的资源时,始终建议直接使用包装器。如果我们选择使用WrapperDealer类获取图像,那么代码段将看起来像这样...
//...
//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 ) ; 笔记
在这里,我们使用WrapperDealer访问CASP资源,但是我们也可以直接使用包装器CASPartResource访问CASP资源。
您可能已经了解,SIMS软件包文件就像一个“ zip文件”,其中包含其中的其他几个文件。软件包文件中的每个文件/资源都具有与之关联的TGI。
TGI基本上是类型,组和实例。类型和组为8位十六进制,而实例是16位十六位。为了避免游戏加载时资源之间的冲突,游戏加载的所有软件包中存在的每个资源都必须具有唯一的TGI组合。
当您使用S3PE打开软件包文件时,您可以轻松地看到打开软件包中每个资源的类型,组和实例。现在,您已经知道了这一点,请记住,在编辑SIMS软件包文件时,您应始终确保插入包装文件中的资源必须始终具有唯一的TGI。
如果您对此有任何进一步的疑问,则可以阅读本文,其中谈论并很好地解释了Mod的冲突,它们是如何发生的,如何解决这些冲突等等。
如果您阅读了这么远,则应该对S3PI是什么以及如何使用它有一个体面的了解。如果要访问旧的官方S3PI存储库,则可以使用此链接。记住所有因创建S3PI库的荣誉都是彼得·L·琼斯(Peter L Jones)。
由Marcos Tomaz创建的存储库