-
Notifications
You must be signed in to change notification settings - Fork 112
Description
Heya! So I am currently trying to get a simple tool that will iterate through mod folders (named the same way as actual assets files they are trying to modify) and for each file inside, will try to replace it in the assets file. I went through the docs but it did not cover asset replacement unfortunately, and most projects that use AssetTools.NET seem to use 2.0 or whatever version still had AssetsReplacer class. I decided to start with audioclips cause with textures I'll have to care about formatting and so many more things and it all seems unnecessarily complicated.
The code below works (hooray!) but it corrupts the assets file in the process and neither the game nor stuff like AssetStudio can open it after that. And I've got no idea what I am doing wrong.
using AssetsTools.NET;
using AssetsTools.NET.Extra;
namespace P2ModLoader.Helper {
public static class AssetsFilePatcher {
public static bool PatchAssetsFile(string assetsFilePath, string modAssetsFolder) {
AssetsManager? manager = null;
AssetsFileInstance? assetsFileInstance = null;
// Create a copy for modification
var newFile = assetsFilePath + ".temp";
File.Copy(assetsFilePath, newFile, true);
try {
manager = new AssetsManager();
manager.LoadClassPackage("C:/classdata.tpk");
using var fs = new FileStream(
newFile,
FileMode.Open,
FileAccess.ReadWrite,
FileShare.None
);
assetsFileInstance = manager.LoadAssetsFile(fs, newFile);
var assetsFile = assetsFileInstance.file;
manager.LoadClassDatabaseFromPackage(assetsFile.Metadata.UnityVersion);
var modAssetFiles = Directory.GetFiles(modAssetsFolder, "*.*", SearchOption.AllDirectories);
foreach (var modAssetFile in modAssetFiles) {
var assetName = Path.GetFileNameWithoutExtension(modAssetFile);
var assetData = File.ReadAllBytes(modAssetFile);
var extension = Path.GetExtension(modAssetFile).ToLower();
switch (extension) {
case ".wav" or ".ogg" or ".mp3" or ".aif" or ".aiff": {
if (!ReplaceAudioAsset(manager, assetsFileInstance, assetName, assetData)) {
Console.WriteLine($"Failed to replace audio asset: {assetName}");
return false;
}
break;
}
default:
Console.WriteLine($"Unsupported asset type for file: {modAssetFile}");
break;
}
}
fs.Position = 0;
using (var writer = new AssetsFileWriter(fs)) {
assetsFileInstance.file.Write(writer, -1);
}
fs.Close();
// Copy over original.
File.Copy(newFile, assetsFilePath, true);
Console.WriteLine($"Successfully patched {assetsFilePath}");
return true;
} catch (Exception ex) {
Console.WriteLine($"Error patching assets file: {ex.Message} {ex.StackTrace}");
return false;
} finally {
File.Delete(newFile);
manager?.UnloadAll();
}
}
private static bool ReplaceAudioAsset(
AssetsManager manager,
AssetsFileInstance assetsFileInstance,
string assetName,
byte[] assetData) {
try {
var afile = assetsFileInstance.file;
var audioAssets = afile.GetAssetsOfType((int)AssetClassID.AudioClip);
foreach (var assetInfo in audioAssets) {
var baseField = manager.GetBaseField(assetsFileInstance, assetInfo);
var name = baseField["m_Name"].AsString;
if (name != assetName) continue;
var externalFilePath = assetsFileInstance.path.Replace(".temp", "") + ".resS";
// Backup external file.
BackupManager.CreateBackup(externalFilePath);
long newOffset;
using (var stream = new FileStream(
externalFilePath,
FileMode.Open,
FileAccess.ReadWrite,
FileShare.None)) {
newOffset = stream.Length;
stream.Position = newOffset;
stream.Write(assetData, 0, assetData.Length);
}
var resourceField = baseField["m_Resource"];
resourceField["m_Offset"].AsULong = (ulong)newOffset;
resourceField["m_Size"].AsUInt = (uint)assetData.Length;
var replacer = new ContentReplacerFromBuffer(baseField.WriteToByteArray());
assetInfo.Replacer = replacer;
Console.WriteLine($"Successfully replaced asset {assetName}");
return true;
}
Console.WriteLine($"Audio asset with name {assetName} not found.");
return false;
} catch (Exception ex) {
Console.WriteLine($"Error replacing audio asset: {ex.Message}");
return false;
}
}
}
}
I understand issues is probably not the best place to seek help but I've got no diea where to go. If I missed something in the doc or you have a working example with assets file patching I would really appreciate it.