├── .gitattributes ├── .gitignore ├── .gitmodules ├── 010 ├── CsvResource.bt ├── ForgeMidi.bt ├── ForgeTex.bt ├── ForgeTypes.bt ├── HxMesh.bt ├── Milo.bt ├── RBReloadedBlob.bt ├── bink.bt ├── common.bt ├── dtb.bt ├── entityResource.bt ├── lipsync.bt ├── rbsong.bt └── songdta.bt ├── CHANGES.md ├── Dependencies ├── LICENSE └── MidiCS.dll ├── LICENSE.txt ├── LibForge ├── .editorconfig ├── .gitignore ├── ForgeTool │ ├── ForgeTool.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── app.config ├── ForgeToolGUI │ ├── ErrorWindow.Designer.cs │ ├── ErrorWindow.cs │ ├── ErrorWindow.resx │ ├── ForgeBrowser.Designer.cs │ ├── ForgeBrowser.cs │ ├── ForgeBrowser.resx │ ├── ForgeToolGUI.csproj │ ├── Inspectors │ │ ├── ConversionInspector.Designer.cs │ │ ├── ConversionInspector.cs │ │ ├── ConversionInspector.resx │ │ ├── FuserInspector.Designer.cs │ │ ├── FuserInspector.cs │ │ ├── FuserInspector.resx │ │ ├── Inspector.cs │ │ ├── MeshInspector.Designer.cs │ │ ├── MeshInspector.cs │ │ ├── MeshInspector.resx │ │ ├── ObjectInspector.Designer.cs │ │ ├── ObjectInspector.cs │ │ ├── ObjectInspector.resx │ │ ├── PropertyInspector.Designer.cs │ │ ├── PropertyInspector.cs │ │ ├── PropertyInspector.resx │ │ ├── RBMidiInspector.Designer.cs │ │ ├── RBMidiInspector.cs │ │ ├── RBMidiInspector.resx │ │ ├── SongDataInspector.Designer.cs │ │ ├── SongDataInspector.cs │ │ ├── SongDataInspector.resx │ │ ├── StartupInspector.Designer.cs │ │ ├── StartupInspector.cs │ │ ├── StartupInspector.resx │ │ ├── StringInspector.Designer.cs │ │ ├── StringInspector.cs │ │ ├── StringInspector.resx │ │ ├── TextureInspector.Designer.cs │ │ ├── TextureInspector.cs │ │ ├── TextureInspector.resx │ │ ├── VRConversionInspector.Designer.cs │ │ ├── VRConversionInspector.cs │ │ └── VRConversionInspector.resx │ ├── OpenTK.dll.config │ ├── Program.cs │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── anvil.ico │ ├── anvil.xcf │ ├── app.config │ └── packages.config ├── LibForge.sln ├── LibForge │ ├── Ark │ │ ├── Archive.cs │ │ ├── ArkBuilder.cs │ │ ├── FileEntry.cs │ │ └── Hash.cs │ ├── CSV │ │ └── CsvData.cs │ ├── DLCSong.cs │ ├── Engine │ │ ├── Component.cs │ │ ├── DataTypes.cs │ │ ├── Entity.cs │ │ ├── EntityResource.cs │ │ ├── GameObject.cs │ │ └── Resource.cs │ ├── Extensions │ │ └── StreamExtensions.cs │ ├── Fuser │ │ ├── FuserAsset.cs │ │ ├── FusionPatch.cs │ │ ├── FusionPatchResource.cs │ │ ├── MidiFileResource.cs │ │ ├── MidiMusicResource.cs │ │ ├── MoggSampleResource.cs │ │ ├── ResourceFile.cs │ │ └── UnknownResource.cs │ ├── LibForge.csproj │ ├── Lipsync │ │ ├── FrameGroup.cs │ │ ├── KeyFrame.cs │ │ ├── Lipsync.cs │ │ ├── LipsyncConverter.cs │ │ ├── LipsyncReader.cs │ │ ├── LipsyncWriter.cs │ │ └── VisemeEvent.cs │ ├── Mesh │ │ ├── HxMesh.cs │ │ ├── HxMeshConverter.cs │ │ ├── HxMeshReader.cs │ │ └── HxMeshWriter.cs │ ├── Meta.cs │ ├── Midi │ │ ├── MidiFileResource.cs │ │ ├── MidiFileResourceReader.cs │ │ ├── MidiFileResourceWriter.cs │ │ ├── MidiHelper.cs │ │ ├── RBMid.cs │ │ ├── RBMidConverter.cs │ │ ├── RBMidReader.cs │ │ └── RBMidWriter.cs │ ├── Milo │ │ ├── BlockStructure.cs │ │ ├── CharLipSync.cs │ │ ├── IMiloEntry.cs │ │ └── MiloFile.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── RBSong │ │ ├── PropAnimResource.cs │ │ ├── RBSongConverter.cs │ │ ├── RBSongResource.cs │ │ └── RBSongResourceWriter.cs │ ├── SongData │ │ ├── SongData.cs │ │ ├── SongDataConverter.cs │ │ ├── SongDataReader.cs │ │ └── SongDataWriter.cs │ ├── Texture │ │ ├── Texture.cs │ │ ├── TextureConverter.cs │ │ ├── TextureReader.cs │ │ └── TextureWriter.cs │ └── Util │ │ ├── BinReader.cs │ │ ├── BinWriter.cs │ │ ├── EncryptedReadStream.cs │ │ ├── EncryptedWriteStream.cs │ │ ├── IDataSerializable.cs │ │ ├── PkgCreator.cs │ │ ├── ReaderBase.cs │ │ └── WriterBase.cs └── LibForgeTests │ ├── LibForgeTests.csproj │ ├── MidiConverterTests.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── RBMidiFileTests.cs │ ├── TestData │ ├── hopos.mid │ └── pro_markers.mid │ └── packages.config ├── README.md ├── appveyor.yml ├── files-in-DLC-packages.md ├── fuser_songfmt.md └── rbsong_contents.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | # Declare files that will always have CRLF line endings on checkout. 7 | *.sln text eol=crlf 8 | 9 | ############################################################################### 10 | # Set default behavior for command prompt diff. 11 | # 12 | # This is need for earlier builds of msysgit that does not have it on by 13 | # default for csharp files. 14 | # Note: This is only used by command line 15 | ############################################################################### 16 | #*.cs diff=csharp 17 | 18 | ############################################################################### 19 | # Set the merge driver for project and solution files 20 | # 21 | # Merging from the command prompt will add diff markers to the files if there 22 | # are conflicts (Merging from VS is not affected by the settings below, in VS 23 | # the diff markers are never inserted). Diff markers may cause the following 24 | # file extensions to fail to load in VS. An alternative would be to treat 25 | # these files as binary and thus will always conflict and require user 26 | # intervention with every merge. To do so, just uncomment the entries below 27 | ############################################################################### 28 | #*.sln merge=binary 29 | #*.csproj merge=binary 30 | #*.vbproj merge=binary 31 | #*.vcxproj merge=binary 32 | #*.vcproj merge=binary 33 | #*.dbproj merge=binary 34 | #*.fsproj merge=binary 35 | #*.lsproj merge=binary 36 | #*.wixproj merge=binary 37 | #*.modelproj merge=binary 38 | #*.sqlproj merge=binary 39 | #*.wwaproj merge=binary 40 | 41 | ############################################################################### 42 | # behavior for image files 43 | # 44 | # image files are treated as binary by default. 45 | ############################################################################### 46 | #*.jpg binary 47 | #*.png binary 48 | #*.gif binary 49 | 50 | ############################################################################### 51 | # diff behavior for common document formats 52 | # 53 | # Convert binary document formats to text before diffing them. This feature 54 | # is only available from the command line. Turn it on by uncommenting the 55 | # entries below. 56 | ############################################################################### 57 | #*.doc diff=astextplain 58 | #*.DOC diff=astextplain 59 | #*.docx diff=astextplain 60 | #*.DOCX diff=astextplain 61 | #*.dot diff=astextplain 62 | #*.DOT diff=astextplain 63 | #*.pdf diff=astextplain 64 | #*.PDF diff=astextplain 65 | #*.rtf diff=astextplain 66 | #*.RTF diff=astextplain 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Dependencies/GameArchives"] 2 | path = Dependencies/GameArchives 3 | url = https://github.com/maxton/GameArchives.git 4 | [submodule "Dependencies/LibOrbisPkg"] 5 | path = Dependencies/LibOrbisPkg 6 | url = https://github.com/maxton/LibOrbisPkg.git 7 | [submodule "Dependencies/DtxCS"] 8 | path = Dependencies/DtxCS 9 | url = https://github.com/InvoxiPlayGames/DtxCS.git 10 | -------------------------------------------------------------------------------- /010/CsvResource.bt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------ 2 | //--- 010 Editor v7.0.2 Binary Template 3 | // 4 | // File: 5 | // Authors: 6 | // Version: 7 | // Purpose: 8 | // Category: 9 | // File Mask: 10 | // ID Bytes: 11 | // History: 12 | //------------------------------------------------ 13 | 14 | typedef int value; 15 | string ValueRead(value v) { 16 | local string x; 17 | local int i = v; 18 | while(strings[i] != 0) { 19 | x += strings[i]; 20 | i += 1; 21 | } 22 | return x; 23 | } 24 | 25 | typedef struct { 26 | int numColumns; 27 | value columns[numColumns]; 28 | } row; 29 | 30 | int csvresource_revision; 31 | int two; 32 | char csvdata; 33 | int stringTableSize; 34 | char strings[stringTableSize]; 35 | row headerRow; 36 | int numRows; 37 | row bodyRows[numRows]; -------------------------------------------------------------------------------- /010/ForgeTex.bt: -------------------------------------------------------------------------------- 1 | // HMX Forge Texture format (.png_platform, .bmp_platform) 2 | LittleEndian(); 3 | typedef struct { 4 | int Width; 5 | int Height; 6 | int Flag; 7 | if(Version == 0xC) { 8 | int unk[2]; 9 | } 10 | } MipmapInfo; 11 | typedef struct { 12 | int Size; 13 | byte Data[Size]; 14 | } MipmapData; 15 | 16 | int Magic; 17 | int Version; 18 | Assert(Magic == 6 || Magic == 4, "Magic number == 6 or 4"); 19 | if (Magic == 6 && Version != 0xC) { 20 | int Unknown; 21 | int Unknown; 22 | int UnknownSixteen; 23 | int Unknown; 24 | int Unknown; 25 | int UnknownFlag; 26 | int Unknown; 27 | int UnknownFour; 28 | int Unknown; 29 | int UnknownThirtyTwo; 30 | int Unk[12]; 31 | int UnknownSix; 32 | int UnknownSixteen2; 33 | int Unknown; 34 | // these MIGHT be related to texture compression 35 | // 1 = DXT1 ?? 36 | // 3 = DXT5 ?? 37 | int UnknownCompressionFormat; 38 | int UnknownCompressionFormat; 39 | int Unknown; 40 | int Negative; 41 | int Negative; 42 | int BitsPerPixel; // Seen: 4 and 8 43 | int UnknownTwo; 44 | int UnknownOne; 45 | int UnknownTwo; 46 | int UnknownTwo; 47 | int Unknown; 48 | int UnknownTwo; 49 | int UnknownThree; 50 | int Unk2[5]; 51 | } 52 | else if (Magic == 6 && Version == 0xC) { 53 | FSeek(0x84); 54 | } 55 | else { 56 | FSeek(0xAC); 57 | } 58 | 59 | int MipmapLevels; 60 | MipmapInfo info[MipmapLevels]; 61 | int Unknown6; 62 | // Image Data for each mipmap level 63 | MipmapData ImageData[MipmapLevels]; 64 | int Unknown; 65 | int Unknown; 66 | float Unknown; 67 | float Unknown; 68 | float Unknown; 69 | float Unknown; 70 | int Unknown; -------------------------------------------------------------------------------- /010/HxMesh.bt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------ 2 | //--- 010 Editor v7.0.2 Binary Template 3 | // 4 | // File: 5 | // Authors: 6 | // Version: 7 | // Purpose: 8 | // Category: 9 | // File Mask: 10 | // ID Bytes: 11 | // History: 12 | //------------------------------------------------ 13 | LittleEndian(); 14 | typedef enum { 15 | Invalid = -1, 16 | Color = 0, 17 | ColorTex = 2, 18 | Unskinned = 3, 19 | Skinned = 4, 20 | PosOnly = 5, 21 | Particle = 6, 22 | UnskinnedCompressed = 7, 23 | SkinnedCompressed = 8 24 | } VertexType; 25 | 26 | typedef struct { 27 | 28 | float x; 29 | float y; 30 | float z; 31 | 32 | char unk; 33 | float unk; 34 | char unk; 35 | hfloat unk; 36 | int unk; 37 | int unk; 38 | int unk; 39 | hfloat unk; // Not sure about this one 40 | hfloat unk; 41 | hfloat unk; 42 | hfloat unk; 43 | hfloat unk; 44 | hfloat unk; 45 | // Repeated UV coordinates? 46 | hfloat u1; 47 | hfloat v1; 48 | hfloat u2; 49 | hfloat v2; 50 | if(vertexType == ColorTex) { 51 | byte unkColorTex[80 - 52]; 52 | } 53 | else if(vertexType == UnskinnedCompressed) 54 | { 55 | short unk; 56 | short unk; 57 | int unk; 58 | int unk; 59 | } 60 | } Point; 61 | 62 | typedef struct { 63 | int v1, v2, v3; 64 | } Triangle; 65 | 66 | char ID[8]; 67 | int endian; 68 | int version; 69 | VertexType vertexType; 70 | 71 | int num_verts; 72 | int num_tris; 73 | if(version >= 0xC) 74 | char bools[2]; 75 | if(version >= 0xD) 76 | char bools_2[2]; 77 | if(version > 3) 78 | char keepMeshData; 79 | uint vertexUsageFlags; 80 | uint faceUsageFlags; 81 | if(version > 0xA) 82 | uint unk; 83 | float unkFloats[4]; 84 | Point vertices[num_verts]; 85 | Triangle tris[num_tris]; -------------------------------------------------------------------------------- /010/Milo.bt: -------------------------------------------------------------------------------- 1 | LittleEndian(); 2 | 3 | enum MiloType 4 | { 5 | MILO_A = 0xCABEDEAF, 6 | MILO_B = 0xCBBEDEAF, 7 | MILO_C = 0xCCBEDEAF, 8 | MILO_D = 0xCDBEDEAF 9 | }; 10 | 11 | int structureType; 12 | int offset; 13 | int blockCount; 14 | int maxUncompressSize; 15 | 16 | local int i = 0; 17 | if(structureType == MILO_A) 18 | { 19 | int blockSize[blockCount]; 20 | } 21 | else 22 | { 23 | struct 24 | { 25 | int size : 24; 26 | int uncompressed : 8; 27 | }blockSize[blockCount]; 28 | FSeek(offset); 29 | for(i = 0; i < blockCount; i++) 30 | struct { char data[blockSize[i].size]; } data; 31 | } -------------------------------------------------------------------------------- /010/RBReloadedBlob.bt: -------------------------------------------------------------------------------- 1 | LittleEndian(); 2 | 3 | typedef struct { 4 | short len; 5 | wchar_t str[len]; 6 | } UStr; 7 | string USTR_READ(UStr& s){ 8 | return s.str; 9 | } 10 | 11 | 12 | int id; // must be 0x2F18EC 13 | int unk1; // must be 3 14 | int unk2; // 1? 15 | int songId; 16 | UStr name; 17 | UStr artist; 18 | UStr credit1; 19 | UStr credit2; 20 | UStr credit3; 21 | UStr credit4; 22 | UStr credit5; 23 | int difficultyLevel; 24 | int origin; 25 | int previewStart; 26 | int previewLength; 27 | int songLength; 28 | char keys[144]; -------------------------------------------------------------------------------- /010/bink.bt: -------------------------------------------------------------------------------- 1 | // BINK 2 | LittleEndian(); 3 | typedef struct { 4 | char MAGIC[3]; 5 | char rev; 6 | uint32 size; 7 | uint32 nFrames; 8 | uint32 largestFrame; 9 | uint32 nFrames2; 10 | uint32 vw; 11 | uint32 vh; 12 | uint32 fpsNum; 13 | uint32 fpsDenom; 14 | uint32 vFlags; 15 | uint32 nAudioTracks; 16 | struct { 17 | uint16 unk; 18 | uint16 numChannels; 19 | } audioChannelInfo[nAudioTracks]; 20 | struct { 21 | uint16 sampleRate; 22 | char flagsLower; 23 | char flagsUpper; 24 | } audioInfo[nAudioTracks]; 25 | uint32 trackIds[nAudioTracks]; 26 | } BIKHDR; 27 | 28 | BIKHDR header; 29 | int frameOffsets[header.nFrames + 1]; 30 | 31 | local int i = 0; 32 | local int pos, nPos; 33 | for(i = 0; i < header.nFrames; i++) 34 | { 35 | pos = frameOffsets[i + 1] & (~0x1); 36 | nPos = frameOffsets[i + 2] & (~0x1); 37 | FSeek(pos); 38 | struct { 39 | struct { 40 | int length; 41 | if(length > 0) 42 | { 43 | int samples; 44 | byte packet[length-4]; 45 | } 46 | } audio[header.nAudioTracks]; 47 | struct { 48 | byte vData[nPos - FTell()]; 49 | } video; 50 | } frame; 51 | } -------------------------------------------------------------------------------- /010/common.bt: -------------------------------------------------------------------------------- 1 | // Common definitions for Forge stuff 2 | 3 | // pascal string (32-bit length prefixed ASCII) 4 | typedef struct { 5 | uint strlen; 6 | char name[strlen]; 7 | } PSTR; 8 | string PSTR_READ(PSTR&st){ 9 | if(st.strlen == 0) return ""; 10 | return st.name; 11 | } -------------------------------------------------------------------------------- /010/dtb.bt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------ 2 | //--- 010 Editor v7.0.2 Binary Template 3 | // 4 | // File: 5 | // Authors: 6 | // Version: 7 | // Purpose: 8 | // Category: 9 | // File Mask: 10 | // ID Bytes: 11 | // History: 12 | //------------------------------------------------ 13 | #include "common.bt" 14 | 15 | typedef enum 16 | { 17 | DATAINT = 0x00, 18 | DATAFLOAT = 0x01, 19 | VARIABLE = 0x02, 20 | SYMBOL = 0x05, 21 | EMPTY = 0x06, 22 | IFDEF = 0x07, 23 | ELSE = 0x08, 24 | ENDIF = 0x09, 25 | ARRAY = 0x10, 26 | COMMAND = 0x11, 27 | STRING = 0x12, 28 | MACRO = 0x13, 29 | DEFINE = 0x20, 30 | INCLUDE = 0x21, 31 | MERGE = 0x22, 32 | IFNDEF = 0x23 33 | } DataType; 34 | 35 | 36 | byte id; 37 | 38 | struct DataArray; 39 | typedef struct DataNode { 40 | }; 41 | 42 | void ReadElement() { 43 | local DataType type = ReadInt(); 44 | FSeek(FTell() + 4); 45 | switch(type) { 46 | case DATAINT: int value; break; 47 | case DATAFLOAT: float value; break; 48 | case SYMBOL: case STRING: PSTR value; break; 49 | case EMPTY: break; 50 | case ARRAY: DataArray value; break; 51 | }; 52 | } 53 | 54 | typedef struct DataArray { 55 | int version; 56 | short count; 57 | short unk; 58 | local int i = 0; 59 | while(i++ < count) { 60 | ReadElement(); 61 | } 62 | }; 63 | 64 | DataArray root; -------------------------------------------------------------------------------- /010/lipsync.bt: -------------------------------------------------------------------------------- 1 | #include "common.bt" 2 | LittleEndian(); 3 | 4 | int version; 5 | int subtype; 6 | float frameRate; 7 | struct { 8 | int num_strings; 9 | PSTR strings[num_strings]; 10 | } visemes; 11 | struct { 12 | int num_strings; 13 | PSTR strings[num_strings]; 14 | } instruments; 15 | int count_off; 16 | int offset_data[count_off]; // Offsets for following data 17 | 18 | typedef struct { 19 | ubyte visemeIndex; 20 | 21 | if (visemeIndex != 0xFF) 22 | ubyte weight; 23 | } visemeChange; 24 | 25 | string VC_READ(visemeChange &vc){ 26 | if (vc.visemeIndex == 0xFF) 27 | return "0xFF"; 28 | return visemes.strings[vc.visemeIndex].name; 29 | } 30 | 31 | int getSize(int off, int max) { 32 | if (max <= 0) return 0; 33 | 34 | // Reads in size until terminating character 35 | local int i; 36 | for (i = 0; i < max; i += 2) { 37 | if (ReadUByte(off + i) == 0xFF) return i + 1; 38 | } 39 | 40 | return max; 41 | } 42 | 43 | // Calculates lengths for interlaced lipsync data 44 | local int i, size, keySize; 45 | for (i = 0; i < count_off - 1; i++) { 46 | size = offset_data[i+1]-offset_data[i]; 47 | if (size == 0) continue; 48 | 49 | struct { 50 | keySize = getSize(FTell(), size); 51 | 52 | // Part 1 53 | if (keySize > 0) { 54 | struct { 55 | visemeChange changes[(keySize >> 1) + (keySize % 2)]; 56 | } lipsync1; 57 | } 58 | size -= keySize; 59 | keySize = getSize(FTell(), size); 60 | 61 | // Part 2 62 | if (keySize > 0) { 63 | struct { 64 | visemeChange changes[(keySize >> 1) + (keySize % 2)]; 65 | } lipsync2; 66 | } 67 | size -= keySize; 68 | keySize = getSize(FTell(), size); 69 | 70 | // Part 3 71 | if (keySize > 0) { 72 | struct { 73 | visemeChange changes[(keySize >> 1) + (keySize % 2)]; 74 | } lipsync3; 75 | } 76 | size -= keySize; 77 | keySize = getSize(FTell(), size); 78 | 79 | // Part 4 - Not observed yet 80 | if (keySize > 0) { 81 | struct { 82 | visemeChange changes[(keySize >> 1) + (keySize % 2)]; 83 | } lipsync4; 84 | } 85 | } key_frame_data; 86 | } -------------------------------------------------------------------------------- /010/songdta.bt: -------------------------------------------------------------------------------- 1 | // Forge .songdta file 2 | 3 | LittleEndian(); 4 | 5 | uint songdta_type; 6 | uint song_id; 7 | short version; 8 | char game_origin[18]; 9 | float preview_start; 10 | float preview_end; 11 | char name[256]; 12 | char artist[256]; 13 | char album_name[256]; 14 | short album_track_number; 15 | FSeek(FTell()+2); 16 | int album_year; 17 | int original_year; 18 | char genre[64]; 19 | float song_length; 20 | float guitar; 21 | float bass; 22 | float vocals; 23 | float drum; 24 | float band; 25 | float keys; 26 | float real_keys; 27 | byte tutorial; 28 | byte album_art; 29 | byte cover; 30 | enum { 31 | Male = 1, 32 | Female = 2 33 | } vocal_gender; 34 | char anim_tempo[16]; 35 | char has_markup; 36 | FSeek(FTell()+3); 37 | int vocal_parts; // not certain about this one 38 | struct { 39 | int unk_0 : 1; 40 | int vocals : 1; 41 | int unk_2 : 1; 42 | int unk_3 : 1; 43 | int unk_4 : 1; 44 | int unk_5 : 1; 45 | } solos; 46 | char fake; 47 | char shortname[256]; -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # 20220228 2 | 3 | * (hopefully) fix converting pack CONs to RBVR with ForgeTool (@InvoxiPlayGames) 4 | 5 | # 20220225 6 | 7 | Rock Band VR support by InvoxiPlayGames. 8 | 9 | * Adds "con2rbvr" verb to ForgeTool, which converts a CON custom into an RBVR custom. 10 | * Changes DtxCS to use my fork which adds DTB serialization. 11 | * Writes `{split_ark}` DTA command to arkorders obtained using the "arkorder" verb. 12 | * Adds file flags to the output arkorders and adjusts ARK building procedure to take flags into account. 13 | * Allows specifying xor value for HDR encryption during ARK building. 14 | * RBMid "PART_KEYS" parsing is not included in debug builds as they still cause crashes. 15 | * Adds information on "con2rbvr", "arkorder" and "arkbuild" verbs in ForgeTool's usage section. 16 | 17 | # 20220115 18 | 19 | Minor bug fixes by Onyxite. 20 | 21 | * Fix cases where an unintended kick lane would be created on drums 22 | * Fix cases where the fake and crowd channel indices aren't assigned correctly 23 | * Don't require a numerically sorted list of channel indices in the input 24 | -------------------------------------------------------------------------------- /Dependencies/MidiCS.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtolly/LibForge/31208f52f6460e64bdf42893ed2157f895b0baae/Dependencies/MidiCS.dll -------------------------------------------------------------------------------- /LibForge/.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = space 3 | indent_size = 2 4 | end_of_line = crlf 5 | -------------------------------------------------------------------------------- /LibForge/.gitignore: -------------------------------------------------------------------------------- 1 | TempData/* -------------------------------------------------------------------------------- /LibForge/ForgeTool/ForgeTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {CB848B8C-A1B6-4777-A674-D3E5C255AAC1} 8 | Exe 9 | ForgeTool 10 | ForgeTool 11 | v4.7.1 12 | 512 13 | 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | 38 | ..\..\Dependencies\MidiCS.dll 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {8651b5c6-b8f9-4a30-a4e6-37d6ca367306} 56 | DtxCS 57 | 58 | 59 | {906748f0-3a55-4b20-bccb-9dc7187f1d5e} 60 | GameArchives 61 | 62 | 63 | {3684b7e6-0978-487a-895c-d0ed8f6b7b9a} 64 | LibForge 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /LibForge/ForgeTool/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ForgeTool")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ForgeTool")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("cb848b8c-a1b6-4777-a674-d3e5c255aac1")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("0.0.1.*")] 36 | [assembly: AssemblyFileVersion("0.0.1.0")] 37 | -------------------------------------------------------------------------------- /LibForge/ForgeTool/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/ErrorWindow.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ForgeToolGUI 2 | { 3 | partial class ErrorWindow 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.label1 = new System.Windows.Forms.Label(); 32 | this.textBox1 = new System.Windows.Forms.TextBox(); 33 | this.SuspendLayout(); 34 | // 35 | // label1 36 | // 37 | this.label1.Location = new System.Drawing.Point(12, 9); 38 | this.label1.Name = "label1"; 39 | this.label1.Size = new System.Drawing.Size(514, 79); 40 | this.label1.TabIndex = 0; 41 | this.label1.Text = "label1"; 42 | // 43 | // textBox1 44 | // 45 | this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 46 | | System.Windows.Forms.AnchorStyles.Left) 47 | | System.Windows.Forms.AnchorStyles.Right))); 48 | this.textBox1.Location = new System.Drawing.Point(12, 91); 49 | this.textBox1.Multiline = true; 50 | this.textBox1.Name = "textBox1"; 51 | this.textBox1.ReadOnly = true; 52 | this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; 53 | this.textBox1.Size = new System.Drawing.Size(514, 169); 54 | this.textBox1.TabIndex = 1; 55 | // 56 | // ErrorWindow 57 | // 58 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 59 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 60 | this.ClientSize = new System.Drawing.Size(538, 272); 61 | this.Controls.Add(this.textBox1); 62 | this.Controls.Add(this.label1); 63 | this.Name = "ErrorWindow"; 64 | this.ShowIcon = false; 65 | this.Text = "ErrorWindow"; 66 | this.ResumeLayout(false); 67 | this.PerformLayout(); 68 | 69 | } 70 | 71 | #endregion 72 | 73 | private System.Windows.Forms.Label label1; 74 | private System.Windows.Forms.TextBox textBox1; 75 | } 76 | } -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/ErrorWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace ForgeToolGUI 11 | { 12 | public partial class ErrorWindow : Form 13 | { 14 | public ErrorWindow(string message = "", string title = "Error", string details = "") 15 | { 16 | InitializeComponent(); 17 | Text = title; 18 | label1.Text = message; 19 | textBox1.Text = details; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/ErrorWindow.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/ConversionInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.Data; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | using GameArchives.STFS; 10 | using LibForge.Util; 11 | using System.Text.RegularExpressions; 12 | 13 | namespace ForgeToolGUI.Inspectors 14 | { 15 | public partial class ConversionInspector : Inspector 16 | { 17 | public ConversionInspector() 18 | { 19 | InitializeComponent(); 20 | } 21 | 22 | void ClearState() 23 | { 24 | listBox1.Items.Clear(); 25 | dtas.Clear(); 26 | UpdateState(); 27 | } 28 | void UpdateState() 29 | { 30 | if (dtas.Count == 0) 31 | { 32 | groupBox2.Enabled = false; 33 | groupBox3.Enabled = false; 34 | idBox.Text = ""; 35 | descriptionBox.Text = ""; 36 | return; 37 | } 38 | var dtaList = dtas.Values.SelectMany(x => x).ToList(); 39 | dtaList.Sort((a, b) => a.Shortname.CompareTo(b.Shortname)); 40 | groupBox2.Enabled = true; 41 | idBox.Text = PkgCreator.GenId(dtaList); 42 | descriptionBox.Text = PkgCreator.GenDesc(dtaList); 43 | } 44 | 45 | private void pickFileButton_Click(object sender, EventArgs e) 46 | { 47 | using (var ofd = new OpenFileDialog() { Multiselect = true }) 48 | { 49 | if(ofd.ShowDialog() == DialogResult.OK) 50 | { 51 | LoadCons(ofd.FileNames); 52 | } 53 | } 54 | } 55 | 56 | Dictionary> dtas = new Dictionary>(); 57 | private bool LoadCon(string filename) 58 | { 59 | using (var con = STFSPackage.OpenFile(GameArchives.Util.LocalFile(filename))) 60 | { 61 | var datas = PkgCreator.GetSongMetadatas(con.RootDirectory.GetDirectory("songs")); 62 | if (datas.Count > 0) 63 | { 64 | dtas[filename] = datas; 65 | listBox1.Items.Add(filename); 66 | } 67 | } 68 | return true; 69 | } 70 | private void LoadCons(string[] filenames) 71 | { 72 | foreach (var filename in filenames) 73 | { 74 | try 75 | { 76 | LoadCon(filename); 77 | } 78 | catch (Exception e) 79 | { 80 | logBox.AppendText($"Error loading {filename}: {e.Message}" + Environment.NewLine); 81 | } 82 | } 83 | UpdateState(); 84 | } 85 | void RemoveCon(string filename) 86 | { 87 | listBox1.Items.Remove(filename); 88 | dtas.Remove(filename); 89 | } 90 | 91 | private void idBox_TextChanged(object sender, EventArgs e) 92 | { 93 | updateContentId(); 94 | } 95 | 96 | private void updateContentId() 97 | { 98 | var txt = new Regex("[^a-zA-Z0-9]").Replace(idBox.Text, "").ToUpper(); 99 | contentIdTextBox.Text = euCheckBox.Checked ? $"EP8802-CUSA02901_00-{txt}" : $"UP8802-CUSA02084_00-{txt}"; 100 | groupBox3.Enabled = contentIdTextBox.Text.Length == 36; 101 | } 102 | 103 | private void euCheckBox_CheckedChanged(object sender, EventArgs e) 104 | { 105 | updateContentId(); 106 | } 107 | 108 | private void buildButton_Click(object sender, EventArgs e) 109 | { 110 | using (var sfd = new SaveFileDialog() { FileName = contentIdTextBox.Text + ".pkg" }) 111 | { 112 | if (sfd.ShowDialog() == DialogResult.OK) 113 | { 114 | Action log = x => logBox.AppendText(x + Environment.NewLine); 115 | log("Converting DLC files..."); 116 | var cons = listBox1.Items.OfType().Select(f => STFSPackage.OpenFile(GameArchives.Util.LocalFile(f))).ToList(); 117 | var songs = new List(); 118 | foreach (var con in cons) 119 | { 120 | songs.AddRange(PkgCreator.ConvertDLCPackage( 121 | con.RootDirectory.GetDirectory("songs"), 122 | volumeAdjustCheckBox.Checked, 123 | s => log($"Warning ({con.FileName}): " + s))); 124 | } 125 | log("Building PKG..."); 126 | PkgCreator.BuildPkg(songs, contentIdTextBox.Text, descriptionBox.Text, euCheckBox.Checked, sfd.FileName, log); 127 | foreach(var con in cons) 128 | { 129 | con.Dispose(); 130 | } 131 | } 132 | } 133 | } 134 | 135 | private void listBox1_DragEnter(object sender, DragEventArgs e) 136 | { 137 | e.Effect = DragDropEffects.Copy; 138 | } 139 | 140 | private void listBox1_DragDrop(object sender, DragEventArgs e) 141 | { 142 | if (e.Data.GetData(DataFormats.FileDrop) is string[] files) 143 | { 144 | LoadCons(files); 145 | } 146 | } 147 | 148 | private void clearButton_Click(object sender, EventArgs e) 149 | { 150 | ClearState(); 151 | } 152 | 153 | private void listBox1_KeyUp(object sender, KeyEventArgs e) 154 | { 155 | if (e.KeyCode == Keys.Delete || e.KeyCode == Keys.Back) 156 | { 157 | RemoveCon(listBox1.SelectedItem as string); 158 | UpdateState(); 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/FuserInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Windows.Forms; 10 | using LibForge.Fuser; 11 | using LibForge.Util; 12 | 13 | namespace ForgeToolGUI.Inspectors 14 | { 15 | public partial class FuserInspector : Inspector 16 | { 17 | class ListItem 18 | { 19 | public ResourceFile file; 20 | public override string ToString() 21 | { 22 | return $"{file.Filename} ({file.Type})"; 23 | } 24 | } 25 | public FuserInspector(FuserAsset asset = null) 26 | { 27 | InitializeComponent(); 28 | if (asset != null) 29 | { 30 | foreach (var resource in asset.ResourceFiles) 31 | { 32 | listBox1.Items.Add(new ListItem { file = resource }); 33 | } 34 | } 35 | } 36 | 37 | private void listBox1_SelectedIndexChanged(object sender, EventArgs e) 38 | { 39 | exportMidiButton.Enabled = listBox1.SelectedItem != null && (listBox1.SelectedItem as ListItem).file is MidiFileResource; 40 | exportMoggButton.Enabled = listBox1.SelectedItem != null && (listBox1.SelectedItem as ListItem).file is MoggSampleResource; 41 | button1.Enabled = exportFusionButton.Enabled = listBox1.SelectedItem != null && (listBox1.SelectedItem as ListItem).file is FusionPatchResource; 42 | if (exportFusionButton.Enabled) 43 | { 44 | var r = (listBox1.SelectedItem as ListItem).file as FusionPatchResource; 45 | var array = DtxCS.DTX.FromDtaString(r.data); 46 | propertyGrid1.SelectedObject = array.Deserialize(); 47 | } 48 | } 49 | private void SaveFile(Action writer) where T : ResourceFile 50 | { 51 | if (listBox1.SelectedItem != null && (listBox1.SelectedItem as ListItem).file is T value) 52 | { 53 | using (var sfd = new SaveFileDialog() { FileName = value.Filename }) 54 | { 55 | if (sfd.ShowDialog() == DialogResult.OK) 56 | { 57 | using (var s = File.OpenWrite(sfd.FileName)) 58 | { 59 | writer(value, s); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | private void exportMidiButton_Click(object sender, EventArgs e) 66 | { 67 | SaveFile((mfr, s) => 68 | { 69 | var midiFile = new MidiCS.MidiFile(MidiCS.MidiFormat.MultiTrack, mfr.MidiFile.MidiTracks.ToList(), 480); 70 | MidiCS.MidiFileWriter.WriteSMF(midiFile, s); 71 | }); 72 | } 73 | private void exportMoggButton_Click(object sender, EventArgs e) 74 | { 75 | SaveFile((msr, s) => 76 | { 77 | s.Write(msr.MoggFileData, 0, msr.MoggFileData.Length); 78 | }); 79 | } 80 | private void exportFusionButton_Click(object sender, EventArgs e) 81 | { 82 | SaveFile((fpr, s) => 83 | { 84 | using (var tw = new StreamWriter(s)) 85 | { 86 | tw.Write(fpr.data); 87 | } 88 | }); 89 | } 90 | 91 | private void button1_Click(object sender, EventArgs e) 92 | { 93 | SaveFile((fpr, s) => 94 | { 95 | var array = DtxCS.DTX.FromDtaString(fpr.data); 96 | var patch = array.Deserialize(); 97 | var serialized = patch.Serialize().ToFileString(); 98 | using (var tw = new StreamWriter(s)) 99 | { 100 | tw.Write(serialized); 101 | } 102 | }); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/Inspector.cs: -------------------------------------------------------------------------------- 1 | using LibForge.CSV; 2 | using LibForge.Lipsync; 3 | using LibForge.Mesh; 4 | using LibForge.Midi; 5 | using LibForge.Milo; 6 | using LibForge.RBSong; 7 | using LibForge.SongData; 8 | using LibForge.Texture; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Text; 13 | 14 | 15 | namespace ForgeToolGUI 16 | { 17 | static class InspectorFactory 18 | { 19 | /// 20 | /// Returns an inspector for the object or null if there isn't one. 21 | /// 22 | /// 23 | /// 24 | public static Inspector GetInspector(object obj) 25 | { 26 | switch (obj) 27 | { 28 | case Texture i: 29 | return new ImageInspector(TextureConverter.ToBitmap(i, 0)); 30 | case string s: 31 | return new StringInspector(s); 32 | case SongData d: 33 | return new SongDataInspector(d); 34 | case RBMid m: 35 | return new RBMidiInspector(m); 36 | case HxMesh m: 37 | return new MeshInspector(m); 38 | case MiloFile mf: 39 | return new PropertyInspector(mf); 40 | case LibForge.Fuser.FuserAsset a: 41 | return new Inspectors.FuserInspector(a); 42 | case object o: 43 | return new ObjectInspector(obj); 44 | } 45 | return null; 46 | } 47 | 48 | 49 | public static object LoadObject(GameArchives.IFile i) 50 | { 51 | if (i.Name.Contains(".bmp_") || i.Name.Contains(".png_")) 52 | { 53 | using (var s = i.GetStream()) 54 | { 55 | try 56 | { 57 | return TextureReader.ReadStream(s); 58 | } 59 | catch (Exception ex) 60 | { 61 | System.Windows.Forms.MessageBox.Show("Couldn't load texture: " + ex.Message); 62 | return null; 63 | } 64 | } 65 | } 66 | else if (i.Name.Contains("_dta_") || i.Name.EndsWith(".dtb")) 67 | { 68 | using (var s = i.GetStream()) 69 | { 70 | var data = DtxCS.DTX.FromDtb(s); 71 | var sb = new StringBuilder(); 72 | foreach (var x in data.Children) 73 | { 74 | sb.AppendLine(x.ToString(0)); 75 | } 76 | return sb.ToString(); 77 | } 78 | } 79 | else if (i.Name.EndsWith(".dta") || i.Name.EndsWith(".moggsong")) 80 | { 81 | using (var s = i.GetStream()) 82 | using (var r = new System.IO.StreamReader(s)) 83 | { 84 | return r.ReadToEnd(); 85 | } 86 | } 87 | else if (i.Name.Contains(".songdta")) 88 | { 89 | using (var s = i.GetStream()) 90 | { 91 | var songData = SongDataReader.ReadStream(s); 92 | return songData; 93 | } 94 | } 95 | else if (i.Name.Contains(".fbx")) 96 | { 97 | using (var s = i.GetStream()) 98 | { 99 | return HxMeshReader.ReadStream(s); 100 | } 101 | } 102 | else if (i.Name.Contains(".rbmid_")) 103 | { 104 | using (var s = i.GetStream()) 105 | { 106 | return RBMidReader.ReadStream(s); 107 | } 108 | } 109 | else if (i.Name.Contains(".lipsync")) 110 | { 111 | using (var s = i.GetStream()) 112 | { 113 | return new LipsyncReader(s).Read(); 114 | } 115 | } 116 | else if (i.Name.Contains(".rbsong")) 117 | { 118 | using (var s = i.GetStream()) 119 | { 120 | var rbsong = new RBSongResource(); 121 | rbsong.Load(s); 122 | return rbsong; 123 | } 124 | } 125 | else if (i.Name.Contains(".gp4")) 126 | { 127 | using (var s = i.GetStream()) 128 | { 129 | return LibOrbisPkg.GP4.Gp4Project.ReadFrom(s); 130 | } 131 | } 132 | else if (i.Name.Contains(".pkg")) 133 | { 134 | using (var s = i.GetStream()) 135 | { 136 | return new LibOrbisPkg.PKG.PkgReader(s).ReadHeader(); 137 | } 138 | } 139 | else if(i.Name.EndsWith(".milo_xbox")) 140 | { 141 | using (var s = i.GetStream()) 142 | { 143 | return MiloFile.ReadFromStream(s); 144 | } 145 | } 146 | else if(i.Name.Contains(".csv")) 147 | { 148 | using (var s = i.GetStream()) 149 | { 150 | 151 | return CsvData.LoadFile(s).ToString(); 152 | } 153 | } 154 | else if (i.Name.EndsWith(".uexp")) 155 | { 156 | using (var s = i.GetStream()) 157 | { 158 | return LibForge.Fuser.FuserAsset.Read(s); 159 | } 160 | } 161 | else 162 | { 163 | return null; 164 | } 165 | } 166 | } 167 | 168 | public class Inspector : System.Windows.Forms.UserControl 169 | { 170 | protected ForgeBrowser fb; 171 | public void SetBrowser(ForgeBrowser f) 172 | { 173 | fb = f; 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/MeshInspector.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/ObjectInspector.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ForgeToolGUI 2 | { 3 | partial class ObjectInspector 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.treeView1 = new System.Windows.Forms.TreeView(); 32 | this.SuspendLayout(); 33 | // 34 | // treeView1 35 | // 36 | this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill; 37 | this.treeView1.HideSelection = false; 38 | this.treeView1.Location = new System.Drawing.Point(0, 0); 39 | this.treeView1.Name = "treeView1"; 40 | this.treeView1.Size = new System.Drawing.Size(531, 502); 41 | this.treeView1.TabIndex = 1; 42 | this.treeView1.AfterExpand += new System.Windows.Forms.TreeViewEventHandler(this.TreeView1_AfterExpand); 43 | // 44 | // ObjectInspector 45 | // 46 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 47 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 48 | this.Controls.Add(this.treeView1); 49 | this.Name = "ObjectInspector"; 50 | this.Size = new System.Drawing.Size(531, 502); 51 | this.ResumeLayout(false); 52 | 53 | } 54 | 55 | #endregion 56 | 57 | private System.Windows.Forms.TreeView treeView1; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/ObjectInspector.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/PropertyInspector.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ForgeToolGUI 2 | { 3 | partial class PropertyInspector 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.propertyGrid1 = new System.Windows.Forms.PropertyGrid(); 32 | this.SuspendLayout(); 33 | // 34 | // propertyGrid1 35 | // 36 | this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill; 37 | this.propertyGrid1.Location = new System.Drawing.Point(0, 0); 38 | this.propertyGrid1.Name = "propertyGrid1"; 39 | this.propertyGrid1.Size = new System.Drawing.Size(531, 502); 40 | this.propertyGrid1.TabIndex = 0; 41 | // 42 | // PropertyInspector 43 | // 44 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 45 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 46 | this.Controls.Add(this.propertyGrid1); 47 | this.Name = "PropertyInspector"; 48 | this.Size = new System.Drawing.Size(531, 502); 49 | this.ResumeLayout(false); 50 | 51 | } 52 | 53 | #endregion 54 | 55 | private System.Windows.Forms.PropertyGrid propertyGrid1; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/PropertyInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.Data; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | using LibForge.RBSong; 10 | using LibForge.Midi; 11 | using System.Collections; 12 | 13 | namespace ForgeToolGUI 14 | { 15 | public partial class PropertyInspector : Inspector 16 | { 17 | public PropertyInspector(object obj) 18 | { 19 | InitializeComponent(); 20 | ObjectPreview(obj); 21 | } 22 | 23 | object previewObj; 24 | void ObjectPreview(object obj) 25 | { 26 | propertyGrid1.SelectedObject = obj; 27 | previewObj = obj; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/PropertyInspector.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/SongDataInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.Data; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace ForgeToolGUI 11 | { 12 | public partial class SongDataInspector : Inspector 13 | { 14 | public SongDataInspector(LibForge.SongData.SongData data) 15 | { 16 | InitializeComponent(); 17 | if(data != null) 18 | UpdateValues(data); 19 | } 20 | public void UpdateValues(LibForge.SongData.SongData data) 21 | { 22 | songIdTextBox.Text = data.SongId.ToString(); 23 | gameOriginTextBox.Text = data.GameOrigin; 24 | versionTextBox.Text = data.Version.ToString(); 25 | nameTextBox.Text = data.Name; 26 | artistTextBox.Text = data.Artist; 27 | albumTextBox.Text = data.AlbumName; 28 | yearTextBox.Text = data.AlbumYear.ToString(); 29 | origYearTextBox.Text = data.OriginalYear.ToString(); 30 | genreTextBox.Text = data.Genre; 31 | trackTextBox.Text = data.AlbumTrackNumber.ToString(); 32 | songLengthTextBox.Text = data.SongLength.ToString(); 33 | previewStartTextBox.Text = data.PreviewStart.ToString(); 34 | previewEndTextBox.Text = data.PreviewEnd.ToString(); 35 | guitarRankTextBox.Text= data.GuitarRank.ToString(); 36 | bassRankTextBox.Text = data.BassRank.ToString(); 37 | voxRankTextBox.Text = data.VocalsRank.ToString(); 38 | drumsRankTextBox.Text = data.DrumRank.ToString(); 39 | bandRankTextBox.Text = data.BandRank.ToString(); 40 | tutorialCheckBox.Checked = data.Tutorial; 41 | artCheckBox.Checked = data.AlbumArt; 42 | coverCheckBox.Checked = data.Cover; 43 | vocalPartsTextBox.Text = data.VocalParts.ToString(); 44 | vocalGenderTextBox.Text = data.VocalGender.ToString(); 45 | mediumTextBox.Text = data.Medium; 46 | freestyleCheckBox.Checked = data.HasFreestyleVocals; 47 | fakeCheckBox.Checked = data.Fake; 48 | shortnameTextBox.Text = data.Shortname; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/StartupInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | using GameArchives; 12 | using GameArchives.STFS; 13 | using DtxCS; 14 | using DtxCS.DataTypes; 15 | using LibForge; 16 | using LibForge.Ark; 17 | using LibForge.CSV; 18 | using LibForge.Lipsync; 19 | using LibForge.Mesh; 20 | using LibForge.Midi; 21 | using LibForge.Milo; 22 | using LibForge.RBSong; 23 | using LibForge.SongData; 24 | using LibForge.Texture; 25 | using LibForge.Util; 26 | 27 | namespace ForgeToolGUI.Inspectors 28 | { 29 | public partial class StartupInspector : Inspector 30 | { 31 | public StartupInspector() 32 | { 33 | InitializeComponent(); 34 | } 35 | 36 | private void button1_Click(object sender, EventArgs e) 37 | { 38 | fb.OpenConverter(); 39 | } 40 | 41 | private void openFileButton_Click(object sender, EventArgs e) 42 | { 43 | fb.openFile_Click(sender, e); 44 | } 45 | 46 | private void openPackageButton_Click(object sender, EventArgs e) 47 | { 48 | fb.openPackage_Click(sender, e); 49 | } 50 | 51 | private void openFolderButton_Click(object sender, EventArgs e) 52 | { 53 | fb.openFolder_Click(sender, e); 54 | } 55 | 56 | private void button1_Click_1(object sender, EventArgs e) 57 | { 58 | } 59 | 60 | private void button2_Click(object sender, EventArgs e) 61 | { 62 | fb.VRConverter(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/StringInspector.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ForgeToolGUI 2 | { 3 | partial class StringInspector 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.textBox = new System.Windows.Forms.TextBox(); 32 | this.SuspendLayout(); 33 | // 34 | // textBox 35 | // 36 | this.textBox.Dock = System.Windows.Forms.DockStyle.Fill; 37 | this.textBox.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 38 | this.textBox.Location = new System.Drawing.Point(0, 0); 39 | this.textBox.Multiline = true; 40 | this.textBox.Name = "textBox"; 41 | this.textBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; 42 | this.textBox.Size = new System.Drawing.Size(445, 266); 43 | this.textBox.TabIndex = 1; 44 | // 45 | // StringInspector 46 | // 47 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 48 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 49 | this.Controls.Add(this.textBox); 50 | this.Name = "StringInspector"; 51 | this.Size = new System.Drawing.Size(445, 266); 52 | this.ResumeLayout(false); 53 | this.PerformLayout(); 54 | 55 | } 56 | 57 | #endregion 58 | 59 | private System.Windows.Forms.TextBox textBox; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/StringInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.Data; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace ForgeToolGUI 11 | { 12 | public partial class StringInspector : Inspector 13 | { 14 | public StringInspector(string str = "") 15 | { 16 | InitializeComponent(); 17 | textBox.Text = str; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/TextureInspector.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ForgeToolGUI 2 | { 3 | partial class ImageInspector 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.pictureBox1 = new System.Windows.Forms.PictureBox(); 32 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); 33 | this.SuspendLayout(); 34 | // 35 | // pictureBox1 36 | // 37 | this.pictureBox1.Location = new System.Drawing.Point(0, 0); 38 | this.pictureBox1.Name = "pictureBox1"; 39 | this.pictureBox1.Size = new System.Drawing.Size(91, 70); 40 | this.pictureBox1.TabIndex = 3; 41 | this.pictureBox1.TabStop = false; 42 | // 43 | // TextureInspector 44 | // 45 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 46 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 47 | this.AutoScroll = true; 48 | this.Controls.Add(this.pictureBox1); 49 | this.Name = "TextureInspector"; 50 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); 51 | this.ResumeLayout(false); 52 | 53 | } 54 | 55 | #endregion 56 | 57 | private System.Windows.Forms.PictureBox pictureBox1; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Inspectors/TextureInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.Data; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace ForgeToolGUI 11 | { 12 | public partial class ImageInspector : Inspector 13 | { 14 | public ImageInspector(Image i) 15 | { 16 | InitializeComponent(); 17 | pictureBox1.Width = i.Width; 18 | pictureBox1.Height = i.Height; 19 | pictureBox1.Image = i; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/OpenTK.dll.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace ForgeToolGUI 7 | { 8 | static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | Application.EnableVisualStyles(); 17 | Application.SetCompatibleTextRenderingDefault(false); 18 | Application.Run(new ForgeBrowser()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ForgeToolGUI")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ForgeToolGUI")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("233099f7-0471-4064-b000-f6dde21e64e6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ForgeToolGUI.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ForgeToolGUI.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ForgeToolGUI.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/anvil.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtolly/LibForge/31208f52f6460e64bdf42893ed2157f895b0baae/LibForge/ForgeToolGUI/anvil.ico -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/anvil.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtolly/LibForge/31208f52f6460e64bdf42893ed2157f895b0baae/LibForge/ForgeToolGUI/anvil.xcf -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LibForge/ForgeToolGUI/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /LibForge/LibForge.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32014.148 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibForge", "LibForge\LibForge.csproj", "{3684B7E6-0978-487A-895C-D0ED8F6B7B9A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ForgeTool", "ForgeTool\ForgeTool.csproj", "{CB848B8C-A1B6-4777-A674-D3E5C255AAC1}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ForgeToolGUI", "ForgeToolGUI\ForgeToolGUI.csproj", "{233099F7-0471-4064-B000-F6DDE21E64E6}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibForgeTests", "LibForgeTests\LibForgeTests.csproj", "{DB683EFE-A4DE-4A8C-BD78-30B84787A027}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameArchives", "..\Dependencies\GameArchives\Library\GameArchives.csproj", "{906748F0-3A55-4B20-BCCB-9DC7187F1D5E}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibOrbisPkg", "..\Dependencies\LibOrbisPkg\LibOrbisPkg\LibOrbisPkg.csproj", "{B053F491-FF0F-4CBB-B03B-520591BB0441}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DtxCS", "..\Dependencies\DtxCS\Library\DtxCS.csproj", "{8651B5C6-B8F9-4A30-A4E6-37D6CA367306}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | Release-minimal|Any CPU = Release-minimal|Any CPU 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {3684B7E6-0978-487A-895C-D0ED8F6B7B9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {3684B7E6-0978-487A-895C-D0ED8F6B7B9A}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {3684B7E6-0978-487A-895C-D0ED8F6B7B9A}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {3684B7E6-0978-487A-895C-D0ED8F6B7B9A}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {3684B7E6-0978-487A-895C-D0ED8F6B7B9A}.Release-minimal|Any CPU.ActiveCfg = Release|Any CPU 32 | {3684B7E6-0978-487A-895C-D0ED8F6B7B9A}.Release-minimal|Any CPU.Build.0 = Release|Any CPU 33 | {CB848B8C-A1B6-4777-A674-D3E5C255AAC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {CB848B8C-A1B6-4777-A674-D3E5C255AAC1}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {CB848B8C-A1B6-4777-A674-D3E5C255AAC1}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {CB848B8C-A1B6-4777-A674-D3E5C255AAC1}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {CB848B8C-A1B6-4777-A674-D3E5C255AAC1}.Release-minimal|Any CPU.ActiveCfg = Release|Any CPU 38 | {CB848B8C-A1B6-4777-A674-D3E5C255AAC1}.Release-minimal|Any CPU.Build.0 = Release|Any CPU 39 | {233099F7-0471-4064-B000-F6DDE21E64E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {233099F7-0471-4064-B000-F6DDE21E64E6}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {233099F7-0471-4064-B000-F6DDE21E64E6}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {233099F7-0471-4064-B000-F6DDE21E64E6}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {233099F7-0471-4064-B000-F6DDE21E64E6}.Release-minimal|Any CPU.ActiveCfg = Release|Any CPU 44 | {233099F7-0471-4064-B000-F6DDE21E64E6}.Release-minimal|Any CPU.Build.0 = Release|Any CPU 45 | {DB683EFE-A4DE-4A8C-BD78-30B84787A027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {DB683EFE-A4DE-4A8C-BD78-30B84787A027}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {DB683EFE-A4DE-4A8C-BD78-30B84787A027}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {DB683EFE-A4DE-4A8C-BD78-30B84787A027}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {DB683EFE-A4DE-4A8C-BD78-30B84787A027}.Release-minimal|Any CPU.ActiveCfg = Release|Any CPU 50 | {DB683EFE-A4DE-4A8C-BD78-30B84787A027}.Release-minimal|Any CPU.Build.0 = Release|Any CPU 51 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Release-minimal|Any CPU.ActiveCfg = Release-minimal|Any CPU 56 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Release-minimal|Any CPU.Build.0 = Release-minimal|Any CPU 57 | {B053F491-FF0F-4CBB-B03B-520591BB0441}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {B053F491-FF0F-4CBB-B03B-520591BB0441}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {B053F491-FF0F-4CBB-B03B-520591BB0441}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {B053F491-FF0F-4CBB-B03B-520591BB0441}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {B053F491-FF0F-4CBB-B03B-520591BB0441}.Release-minimal|Any CPU.ActiveCfg = Release|Any CPU 62 | {B053F491-FF0F-4CBB-B03B-520591BB0441}.Release-minimal|Any CPU.Build.0 = Release|Any CPU 63 | {8651B5C6-B8F9-4A30-A4E6-37D6CA367306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {8651B5C6-B8F9-4A30-A4E6-37D6CA367306}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {8651B5C6-B8F9-4A30-A4E6-37D6CA367306}.Release|Any CPU.ActiveCfg = Release|Any CPU 66 | {8651B5C6-B8F9-4A30-A4E6-37D6CA367306}.Release|Any CPU.Build.0 = Release|Any CPU 67 | {8651B5C6-B8F9-4A30-A4E6-37D6CA367306}.Release-minimal|Any CPU.ActiveCfg = Release|Any CPU 68 | {8651B5C6-B8F9-4A30-A4E6-37D6CA367306}.Release-minimal|Any CPU.Build.0 = Release|Any CPU 69 | EndGlobalSection 70 | GlobalSection(SolutionProperties) = preSolution 71 | HideSolutionNode = FALSE 72 | EndGlobalSection 73 | GlobalSection(ExtensibilityGlobals) = postSolution 74 | SolutionGuid = {361789AF-FA0C-458A-8AE3-2AC3F559CBC0} 75 | EndGlobalSection 76 | EndGlobal 77 | -------------------------------------------------------------------------------- /LibForge/LibForge/Ark/Archive.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Extensions; 2 | using LibForge.Util; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace LibForge.Ark 10 | { 11 | /// 12 | /// Version 10 archive file class. 13 | /// 14 | public class Archive 15 | { 16 | public FileEntry[] fileEntries; 17 | public uint[] arkSizes; 18 | public int[] hashTable; 19 | public string[] arkNames; 20 | public byte[] guid; 21 | public string[][] strings; 22 | public Archive(string hdrFilename) 23 | { 24 | using (var f = File.OpenRead(hdrFilename)) 25 | { 26 | LoadHdr(f); 27 | } 28 | } 29 | 30 | public void WriteArkorder(TextWriter sw) 31 | { 32 | long[] arkOffsets = new long[arkSizes.Length]; 33 | long totalOffset = 0; 34 | for (int i = 0; i < arkSizes.Length; i++) 35 | { 36 | arkOffsets[i] = totalOffset; 37 | totalOffset += arkSizes[i]; 38 | } 39 | int nextArk = 1; 40 | foreach(var entry in fileEntries.OrderBy(x => x.Offset)) 41 | { 42 | if (arkSizes.Length > nextArk && entry.Offset == arkOffsets[nextArk]) 43 | { 44 | sw.WriteLine("{split_ark}"); 45 | nextArk++; 46 | } 47 | sw.WriteLine("(\"{0}\" {1})", entry.Path, entry.Flags); 48 | } 49 | } 50 | 51 | private void LoadHdr(Stream file) 52 | { 53 | var cryptStream = new EncryptedReadStream(file, 0); 54 | var r = new BinReader(cryptStream); 55 | int version = r.Int(); 56 | if (version == -11) 57 | cryptStream.xor = 0xFF; 58 | else if (version != 10) 59 | throw new Exception("Only version 10 arks are supported"); 60 | int unk = r.Check(r.Int(), 1); 61 | guid = cryptStream.ReadBytes(16); 62 | int numArks = r.Int(); 63 | arkSizes = r.Arr(r.UInt); 64 | arkNames = r.Arr(r.String); 65 | strings = r.Arr(() => r.Arr(r.String)); 66 | fileEntries = r.Arr(() => { var e = new FileEntry(); e.Load(cryptStream); return e; }); 67 | hashTable = r.Arr(r.Int); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /LibForge/LibForge/Ark/ArkBuilder.cs: -------------------------------------------------------------------------------- 1 | using GameArchives; 2 | using LibForge.Extensions; 3 | using LibForge.Util; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | 10 | namespace LibForge.Ark 11 | { 12 | public class ArkBuilder 13 | { 14 | private string name; 15 | private string[] arkNames; 16 | private uint[] arkSizes; 17 | private int[] hashTable; 18 | private List>> layout; 19 | private FileEntry[] entries; 20 | 21 | /// 22 | /// Constructs a new Ark Builder. 23 | /// 24 | /// Name of the archive files. Example: "main_pc" 25 | /// Ordered list of ark files; each of which is a list of ark path / file pairs. 26 | public ArkBuilder(string name, List>> ArkFiles) 27 | { 28 | this.name = name; 29 | layout = ArkFiles; 30 | arkNames = new string[layout.Count]; 31 | arkSizes = new uint[layout.Count]; 32 | entries = new FileEntry[layout.Sum(x => x.Count)]; 33 | int i = 0; 34 | int entryIndex = 0; 35 | long arkOffset = 0; 36 | foreach (var ark in layout) 37 | { 38 | foreach(var (path,file,flags) in layout[i]) 39 | { 40 | entries[entryIndex++] = new FileEntry() 41 | { 42 | Flags = flags, 43 | Size = (uint)file.Size, 44 | Offset = arkOffset, 45 | Path = path 46 | }; 47 | arkOffset += file.Size; 48 | arkSizes[i] += (uint)file.Size; 49 | } 50 | arkNames[i] = $"{name}_{i++}.ark"; 51 | } 52 | //Array.Sort(entries, (x, y) => x.Path.CompareTo(y.Path)); 53 | hashTable = new int[entries.Length]; 54 | for (i = 0; i < hashTable.Length; i++) hashTable[i] = -1; 55 | for(i = 0; i < entries.Length; i++) 56 | { 57 | long bin = ((uint)Hash.Compute(Encoding.UTF8.GetBytes(entries[i].Path))) % hashTable.Length; 58 | hashTable[bin] = i; 59 | } 60 | } 61 | 62 | public void Save(string outPath, byte xor = 0xFF) 63 | { 64 | var hdrPath = Path.Combine(outPath, name + ".hdr"); 65 | using (var s = File.Create(hdrPath)) 66 | { 67 | WriteHdr(s, xor); 68 | } 69 | 70 | for(int i = 0; i < arkNames.Length; i++) 71 | { 72 | using (var s = File.Create(Path.Combine(outPath, arkNames[i]))) 73 | { 74 | WriteArk(i, s); 75 | } 76 | } 77 | } 78 | 79 | public void WriteHdr(Stream output, byte xor) 80 | { 81 | var cryptStream = new EncryptedWriteStream(output, new Random().Next(), xor); 82 | var w = new BinWriter(cryptStream); 83 | w.Write(10); // version 84 | w.Write(1); // unknown 85 | var guid = Guid.NewGuid().ToByteArray(); 86 | cryptStream.Write(guid, 0, guid.Length); 87 | w.Write(arkSizes.Length); 88 | w.Write(arkSizes, w.Write); 89 | w.Write(arkNames, w.Write); 90 | //TODO: What are these strings - "markers"? 91 | w.Write(1); 92 | w.Write(0); 93 | w.Write(entries, x => x.Save(cryptStream)); 94 | w.Write(hashTable, w.Write); 95 | } 96 | 97 | public void WriteArk(int arkIndex, Stream output) 98 | { 99 | if (arkIndex < 0 || arkIndex > layout.Count) throw new ArgumentOutOfRangeException(nameof(arkIndex)); 100 | foreach(var (_,file,_) in layout[arkIndex]) 101 | { 102 | using (var s = file.GetStream()) 103 | { 104 | s.CopyTo(output); 105 | } 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /LibForge/LibForge/Ark/FileEntry.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Ark 9 | { 10 | public class FileEntry 11 | { 12 | public long Offset; 13 | public string Path; 14 | public int Flags; 15 | public uint Size; 16 | 17 | public void Load(Stream s) 18 | { 19 | var r = new BinReader(s); 20 | Offset = r.Long(); 21 | Path = r.String(); 22 | Flags = r.Int(); 23 | Size = r.UInt(); 24 | } 25 | 26 | public void Save(Stream s) 27 | { 28 | var w = new BinWriter(s); 29 | w.Write(Offset); 30 | w.Write(Path); 31 | w.Write(Flags); 32 | w.Write(Size); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LibForge/LibForge/Ark/Hash.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace LibForge.Ark 8 | { 9 | // Public domain from https://gist.github.com/automatonic/3725443 10 | public static class Hash 11 | { 12 | //Change to suit your needs 13 | const uint seed = 0x9520e3c4; 14 | 15 | public static int Compute(byte[] data) 16 | { 17 | const uint c1 = 0xcc9e2d51; 18 | const uint c2 = 0x1b873593; 19 | 20 | uint h1 = seed; 21 | uint k1 = 0; 22 | uint streamLength = 0; 23 | 24 | using (MemoryStream s = new MemoryStream(data)) 25 | using (BinaryReader reader = new BinaryReader(s)) 26 | { 27 | byte[] chunk = reader.ReadBytes(4); 28 | while (chunk.Length > 0) 29 | { 30 | streamLength += (uint)chunk.Length; 31 | switch (chunk.Length) 32 | { 33 | case 4: 34 | /* Get four bytes from the input into an uint */ 35 | k1 = (uint) 36 | (chunk[0] 37 | | chunk[1] << 8 38 | | chunk[2] << 16 39 | | chunk[3] << 24); 40 | 41 | /* bitmagic hash */ 42 | k1 *= c1; 43 | k1 = rotl32(k1, 15); 44 | k1 *= c2; 45 | 46 | h1 ^= k1; 47 | h1 = rotl32(h1, 13); 48 | h1 = h1 * 5 + 0xe6546b64; 49 | break; 50 | case 3: 51 | k1 = (uint) 52 | (chunk[0] 53 | | chunk[1] << 8 54 | | chunk[2] << 16); 55 | k1 *= c1; 56 | k1 = rotl32(k1, 15); 57 | k1 *= c2; 58 | h1 ^= k1; 59 | break; 60 | case 2: 61 | k1 = (uint) 62 | (chunk[0] 63 | | chunk[1] << 8); 64 | k1 *= c1; 65 | k1 = rotl32(k1, 15); 66 | k1 *= c2; 67 | h1 ^= k1; 68 | break; 69 | case 1: 70 | k1 = (uint)(chunk[0]); 71 | k1 *= c1; 72 | k1 = rotl32(k1, 15); 73 | k1 *= c2; 74 | h1 ^= k1; 75 | break; 76 | 77 | } 78 | chunk = reader.ReadBytes(4); 79 | } 80 | } 81 | 82 | // finalization, magic chants to wrap it all up 83 | h1 ^= streamLength; 84 | h1 = fmix(h1); 85 | 86 | unchecked //ignore overflow 87 | { 88 | return (int)h1; 89 | } 90 | } 91 | 92 | private static uint rotl32(uint x, byte r) 93 | { 94 | return (x << r) | (x >> (32 - r)); 95 | } 96 | 97 | private static uint fmix(uint h) 98 | { 99 | h ^= h >> 16; 100 | h *= 0x85ebca6b; 101 | h ^= h >> 13; 102 | h *= 0xc2b2ae35; 103 | h ^= h >> 16; 104 | return h; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /LibForge/LibForge/CSV/CsvData.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Extensions; 2 | using LibForge.Util; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace LibForge.CSV 10 | { 11 | public class CsvData 12 | { 13 | public string[] headerRow; 14 | public string[][] rows; 15 | 16 | public static CsvData LoadFile(Stream s) 17 | { 18 | CsvData c = new CsvData(); 19 | c.Read(s); 20 | return c; 21 | } 22 | 23 | public override string ToString() 24 | { 25 | var sb = new StringBuilder(); 26 | if (headerRow.Length > 0) 27 | { 28 | sb.Append(headerRow[0]); 29 | for (int i = 1; i < headerRow.Length; i++) 30 | { 31 | sb.Append(',').Append(headerRow[i]); 32 | } 33 | sb.AppendLine(); 34 | } 35 | if (rows.Length > 0) 36 | { 37 | for(int i = 0; i < rows.Length; i++) 38 | { 39 | if (rows[i].Length == 0) continue; 40 | sb.Append(rows[i][0]); 41 | for (int j = 1; j < rows[i].Length; j++) 42 | { 43 | sb.Append(',').Append(rows[i][j]); 44 | } 45 | sb.AppendLine(); 46 | } 47 | } 48 | return sb.ToString(); 49 | } 50 | 51 | public void Read(Stream s) 52 | { 53 | var r = new BinReader(s); 54 | r.Check(r.Int(), 1); 55 | r.Check(r.Int(), 2); 56 | r.Check(r.Byte(), 0x2C); 57 | var tableSize = r.Int(); 58 | if (tableSize + s.Position >= s.Length) 59 | { 60 | throw new InvalidDataException("String table exceeded the length of the file."); 61 | } 62 | var stringTable = s.ReadBytes(tableSize); 63 | var stringcache = new Dictionary(); 64 | string GetString(int index) 65 | { 66 | if (stringcache.ContainsKey(index)) return stringcache[index]; 67 | int length = 0; 68 | while (stringTable[index + length] != 0 && ++length < stringTable.Length) ; 69 | return stringcache[index] = Encoding.UTF8.GetString(stringTable, index, length); 70 | } 71 | var headerColumns = r.Int(); 72 | headerRow = new string[headerColumns]; 73 | for(int i = 0; i < headerColumns; i++) 74 | { 75 | headerRow[i] = GetString(r.Int()); 76 | } 77 | var numRows = r.Int(); 78 | rows = new string[numRows][]; 79 | for(int i = 0; i < numRows; i++) 80 | { 81 | var numColumns = r.Int(); 82 | rows[i] = new string[numColumns]; 83 | for (int j = 0; j < numColumns; j++) 84 | { 85 | rows[i][j] = GetString(r.Int()); 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /LibForge/LibForge/DLCSong.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using DtxCS.DataTypes; 6 | 7 | namespace LibForge 8 | { 9 | public class DLCSong 10 | { 11 | public SongData.SongData SongData; 12 | public Lipsync.Lipsync Lipsync; 13 | public Midi.RBMid RBMidi; 14 | public Texture.Texture Artwork; 15 | public GameArchives.IFile Mogg; 16 | public DataArray MoggSong; 17 | public DataArray MoggDta; 18 | public RBSong.RBSongResource RBSong; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LibForge/LibForge/Engine/Component.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Engine 9 | { 10 | public class Component 11 | { 12 | public string Name1; 13 | public string Name2; 14 | public int Rev; 15 | public long Unknown2; 16 | public Property[] Props; 17 | 18 | public void Load(Stream s, int objRev, int layerVersion) 19 | { 20 | var r = new BinReader(s); 21 | Name1 = r.String(); 22 | if (objRev >= 2) 23 | Name2 = r.String(); 24 | Rev = r.Int(); 25 | Unknown2 = r.Long(); 26 | if (layerVersion >= 0xE) 27 | { 28 | Props = new Property[r.Int()]; 29 | for (int i = 0; i < Props.Length; i++) 30 | { 31 | Props[i] = Property.Read(s); 32 | } 33 | foreach (var prop in Props) 34 | { 35 | prop.Value = Value.Read(s, prop.Type); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LibForge/LibForge/Engine/Entity.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Engine 9 | { 10 | public class EntityLayer 11 | { 12 | public int Version; 13 | public int fileSlotIndex; 14 | public short TotalObjectLayers; 15 | public GameObject[] Objects; 16 | 17 | public void Load(Stream s, int index, int version) 18 | { 19 | var r = new BinReader(s); 20 | Version = r.Int(); 21 | if (Version < 8) 22 | throw new InvalidDataException("Entity layer version should be > 8"); 23 | if (Version >= 0x17) 24 | { 25 | r.Int(); // unknown 26 | } 27 | fileSlotIndex = r.Int(); 28 | if (fileSlotIndex != index) 29 | { 30 | //throw new InvalidDataException("Entity layer failed to load (serialized to different slot)"); 31 | } 32 | TotalObjectLayers = r.Short(); 33 | Objects = new GameObject[r.Int()]; 34 | for (int i = 0; i < Objects.Length; i++) 35 | { 36 | Objects[i] = new GameObject(); 37 | Objects[i].Load(s, Version); 38 | if (Objects[i] != null && Objects[i].Rev >= 3 && Objects[i].Name.Length == 0) 39 | { 40 | // It seems like maybe these are "ghost objects" that should be overwritten??? Need more RE 41 | i--; 42 | } 43 | } 44 | } 45 | } 46 | 47 | public class Entity 48 | { 49 | public const int MaxVersion = 0x1E; 50 | public int Version; 51 | public EntityLayer[] Layers; 52 | 53 | public void Load(Stream s, EntityResource rsrc) 54 | { 55 | var r = new BinReader(s); 56 | Version = r.Int(); 57 | if (Version > MaxVersion) 58 | { 59 | throw new Exception("Can't handle entity version " + Version); 60 | } 61 | if (Version <= 1) 62 | { 63 | throw new InvalidDataException("Entity version must be > 1"); 64 | } 65 | if (Version <= 4) 66 | { 67 | r.String(); 68 | } 69 | int numLayers = 1; 70 | if (Version >= 9) 71 | { 72 | numLayers = r.Int(); 73 | } 74 | else 75 | { 76 | int rootId = r.Int(); 77 | if (Version > 6) 78 | { 79 | numLayers = r.Int(); 80 | } 81 | else 82 | { 83 | short arraySize = r.Short(); 84 | } 85 | } 86 | for (int li = 0; li < numLayers; li++) 87 | { 88 | if (Version < 8) 89 | { 90 | if (Version >= 7) 91 | r.Short(); // layer_field_28 92 | if (numLayers != 1) 93 | throw new Exception("Num layers should be 1 for version <8"); 94 | var propArrayBaseSize = r.Int(); 95 | if (propArrayBaseSize > 0) 96 | { 97 | throw new NotImplementedException("Version < 8 should load objs here"); 98 | } 99 | } 100 | else if (Version <= 11) 101 | { 102 | r.String(); // layer_name 103 | } 104 | } 105 | if (Version < 8) 106 | { 107 | throw new NotImplementedException("Version < 8 not implemented"); 108 | } 109 | Layers = new EntityLayer[numLayers]; 110 | LoadLayer(s, 0, rsrc); 111 | for (int i = 1; i < numLayers; i++) 112 | { 113 | LoadLayer(s, i, rsrc); 114 | } 115 | } 116 | private void LoadLayer(Stream s, int layerIndex, EntityResource rsrc) 117 | { 118 | Layers[layerIndex] = new EntityLayer(); 119 | Layers[layerIndex].Load(s, layerIndex, Version); 120 | if (Version >= 0xC) 121 | { 122 | rsrc.LoadInlineResources(s, layerIndex); 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /LibForge/LibForge/Engine/EntityResource.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Engine 9 | { 10 | public class EntityResource : Resource 11 | { 12 | public EntityResource() { Type = "EntityResource"; } 13 | public const int MaxVersion = 0x11; 14 | public int Version; 15 | public string[] InlineLayerNames; 16 | public Entity Entity; 17 | public Resource[][] InlineResourceLayers; 18 | 19 | public override void Load(Stream s) 20 | { 21 | var r = new BinReader(s); 22 | Version = r.Int(); 23 | if (Version < 0xC || Version > MaxVersion) 24 | { 25 | throw new InvalidDataException("Can't handle EntityResource version " + Version); 26 | } 27 | InlineLayerNames = r.Arr(r.String); 28 | InlineResourceLayers = new Resource[InlineLayerNames.Length][]; 29 | if (Version >= 0xF) 30 | { 31 | var unk0 = r.Int(); 32 | if (Version >= 0x10) 33 | { 34 | var unk1 = r.Int(); 35 | } 36 | 37 | } 38 | Entity = new Entity(); 39 | Entity.Load(s, this); 40 | } 41 | public void LoadInlineResources(Stream s, int layerIndex) 42 | { 43 | var r = new BinReader(s); 44 | if (layerIndex > InlineResourceLayers.Length) 45 | throw new InvalidDataException("More inline resource layers than entity resource layers"); 46 | int version = r.Int(); 47 | // HACK: Allows reading codemonkey_rbn.rbsong 48 | if (version == 1) 49 | { 50 | r.Int(); 51 | r.Int(); 52 | r.Int(); 53 | version = r.Int(); 54 | } 55 | int count = r.Int(); 56 | var layer = InlineResourceLayers[layerIndex] = new Resource[count]; 57 | for (int i = 0; i < count; i++) 58 | { 59 | var type = r.String(); 60 | var inline = layer[i] = Resource.Create(type); 61 | inline.Path = r.String(); 62 | inline.Load(s); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /LibForge/LibForge/Engine/GameObject.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Engine 9 | { 10 | public struct GameObjectId 11 | { 12 | public short Layer; 13 | public int Index; 14 | } 15 | public class GameObject 16 | { 17 | public const int MaxVersion = 0x4; 18 | public GameObjectId Id; 19 | public int Rev; 20 | public string Name; 21 | public Component[] Components; 22 | 23 | public void Load(Stream s, int layerVersion) 24 | { 25 | var r = new BinReader(s); 26 | var id = r.Int(); 27 | if (id == -1) return; 28 | Id = new GameObjectId 29 | { 30 | Index = id & 0xFFF, 31 | Layer = (short)(id >> 16), // Some places say this should be >> 12, but that doesn't make sense with any files 32 | }; 33 | Rev = r.Int(); 34 | if (Rev < 0) 35 | { 36 | Name = new string((char)r.Byte(), 1); 37 | } 38 | else 39 | { 40 | Name = r.String(); 41 | } 42 | 43 | if (Rev >= 3 && Name.Length == 0) 44 | { 45 | // TODO: Newer GameObject unknown stuff 46 | r.Int(); 47 | r.Int(); 48 | if (Rev >= 4) 49 | { 50 | r.Int(); 51 | } 52 | Components = new Component[] { }; 53 | return; 54 | } 55 | var numChildren = r.Int(); 56 | Components = new Component[numChildren]; 57 | for (int i = 0; i < numChildren; i++) 58 | { 59 | Components[i] = new Component(); 60 | Components[i].Load(s, Rev, layerVersion); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /LibForge/LibForge/Engine/Resource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using LibForge.RBSong; 7 | using LibForge.Extensions; 8 | using LibForge.Util; 9 | 10 | namespace LibForge.Engine 11 | { 12 | public abstract class Resource 13 | { 14 | public string Type; 15 | public string Path; 16 | public static Resource Create(string type) 17 | { 18 | switch (type) 19 | { 20 | case "PropAnimResource": 21 | return new PropAnimResource(); 22 | case "RBSongResource": 23 | return new RBSongResource(); 24 | case "EntityResource": 25 | return new EntityResource(); 26 | default: 27 | throw new NotImplementedException("Unimplemented resource type: " + type); 28 | } 29 | } 30 | 31 | public virtual void Load(Stream s) 32 | { 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LibForge/LibForge/Fuser/FuserAsset.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Fuser 9 | { 10 | public class FuserAsset 11 | { 12 | public long Version; 13 | public long Version2; 14 | public int Unk3; 15 | public long FilenameHash; 16 | public string Filename; 17 | public int Unk4; 18 | public int Unk5; 19 | 20 | public ResourceFile[] ResourceFiles; 21 | public static FuserAsset Read(Stream s) 22 | { 23 | var ret = new FuserAsset(); 24 | ret.Load(s); 25 | return ret; 26 | } 27 | private FuserAsset() { } 28 | private void Load(Stream s) 29 | { 30 | var b = new BinReader(s); 31 | Version = b.Check(b.Long(), 7); 32 | Version2 = b.Long(); 33 | if (Version2 == 14 || Version2 == 12) 34 | { 35 | // Bunch of unknown values here. 36 | s.Position += 45; 37 | } 38 | else if (Version2 != 7) 39 | { 40 | throw new InvalidDataException("Unknown value at 0x8: " + Version2); 41 | } 42 | Unk3 = b.Int(); 43 | FilenameHash = b.Long(); 44 | Filename = b.UE4String(); 45 | Unk4 = b.Int(); 46 | Unk5 = b.Int(); 47 | if (Unk4 == 1 && Unk5 == 0) 48 | { 49 | ResourceFiles = new ResourceFile[1]; 50 | } 51 | else 52 | { 53 | ResourceFiles = new ResourceFile[b.Long()]; 54 | } 55 | for (int i = 0; i < ResourceFiles.Length; i++) 56 | { 57 | ResourceFiles[i] = ResourceFile.Read(s); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /LibForge/LibForge/Fuser/FusionPatchResource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using LibForge.Util; 7 | 8 | namespace LibForge.Fuser 9 | { 10 | public class FusionPatchResource : ResourceFile 11 | { 12 | public string data; 13 | public override void Load(Stream s) 14 | { 15 | var b = new BinReader(s); 16 | data = b.String((int)Size); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LibForge/LibForge/Fuser/MidiFileResource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace LibForge.Fuser 8 | { 9 | public class MidiFileResource : ResourceFile 10 | { 11 | public LibForge.Midi.MidiFileResource MidiFile; 12 | public override void Load(Stream s) 13 | { 14 | MidiFile = new LibForge.Midi.MidiFileResourceReader(s).Read(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LibForge/LibForge/Fuser/MidiMusicResource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace LibForge.Fuser 8 | { 9 | public class MidiMusicResource : ResourceFile 10 | { 11 | public override void Load(Stream s) 12 | { 13 | throw new NotImplementedException(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LibForge/LibForge/Fuser/MoggSampleResource.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Fuser 9 | { 10 | public class MoggSampleResource : ResourceFile 11 | { 12 | public int SampleRate; 13 | public int NumberOfSamples; 14 | public byte[] MoggFileData; 15 | public override void Load(Stream s) 16 | { 17 | var b = new BinReader(s); 18 | b.Check(b.FixedString(4), "mogs"); 19 | b.Check(b.Int(), 1); 20 | SampleRate = b.Int(); 21 | b.Check(b.Int(), 2); 22 | NumberOfSamples = b.Int(); 23 | b.Check(b.Int(), 3); 24 | b.Check(b.Int(), 2); 25 | MoggFileData = new byte[b.Int()]; 26 | s.Read(MoggFileData, 0, MoggFileData.Length); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LibForge/LibForge/Fuser/ResourceFile.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Fuser 9 | { 10 | public abstract class ResourceFile 11 | { 12 | public int Unk1; 13 | public string Filename; 14 | public int Unk2; 15 | public string Type; 16 | public long Size; 17 | 18 | public static ResourceFile Read(Stream s) 19 | { 20 | var b = new BinReader(s); 21 | var unk1 = b.Int(); 22 | var filename = b.UE4String(); 23 | var unk2 = b.Int(); 24 | var type = b.UE4String(); 25 | var size = b.Long(); 26 | ResourceFile ret; 27 | switch(type) 28 | { 29 | case "FusionPatchResource": 30 | ret = new FusionPatchResource(); 31 | break; 32 | case "MidiFileResource": 33 | ret = new MidiFileResource(); 34 | break; 35 | //case "MidiMusicResource": 36 | // ret = new MidiMusicResource(); 37 | // break; 38 | case "MoggSampleResource": 39 | ret = new MoggSampleResource(); 40 | break; 41 | default: 42 | ret = new UnknownResource(); 43 | break; 44 | } 45 | ret.Unk1 = unk1; 46 | ret.Filename = filename; 47 | ret.Unk2 = unk2; 48 | ret.Type = type; 49 | ret.Size = size; 50 | ret.Load(s); 51 | return ret; 52 | } 53 | 54 | public abstract void Load(Stream s); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /LibForge/LibForge/Fuser/UnknownResource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace LibForge.Fuser 8 | { 9 | public class UnknownResource : ResourceFile 10 | { 11 | public byte[] data; 12 | public override void Load(Stream s) 13 | { 14 | data = new byte[Size]; 15 | s.Read(data, 0, data.Length); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LibForge/LibForge/Lipsync/FrameGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Lipsync 7 | { 8 | public class FrameGroup 9 | { 10 | public KeyFrame[] Frames { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /LibForge/LibForge/Lipsync/KeyFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Lipsync 7 | { 8 | public struct KeyFrame 9 | { 10 | public List Events { get; set; } 11 | public override string ToString() => string.Join(", ", Events.Select(x => x.ToString())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LibForge/LibForge/Lipsync/Lipsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Lipsync 7 | { 8 | public class Lipsync 9 | { 10 | public int Version; 11 | public int Subtype; 12 | public float FrameRate; 13 | // Names for the viseme key names 14 | public string[] Visemes; 15 | // Players with visemes 16 | public string[] Players; 17 | public uint[] FrameIndices; 18 | public byte[] FrameData; 19 | //public FrameGroup[] Groups; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LibForge/LibForge/Lipsync/LipsyncConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using LibForge.Extensions; 7 | using LibForge.Milo; 8 | 9 | namespace LibForge.Lipsync 10 | { 11 | public class LipsyncConverter 12 | { 13 | private static string[] partMapping = new string[] { "mic", "guitar", "bass", "drums" }; 14 | 15 | public static Lipsync FromMilo(MiloFile milo) 16 | { 17 | var miloLips = milo.Entries 18 | .Where(x => x is CharLipSync) 19 | .Select(y => y as CharLipSync) 20 | .ToList(); 21 | 22 | miloLips.Sort((x, y) => 23 | { 24 | if (x.Name.Equals("song.lipsync", StringComparison.CurrentCultureIgnoreCase)) 25 | return -1; 26 | else if (y.Name.Equals("song.lipsync", StringComparison.CurrentCultureIgnoreCase)) 27 | return 1; 28 | 29 | return string.Compare(x.Name, y.Name); 30 | }); 31 | 32 | int totalFrames = miloLips.Select(x => x.Frames.Length).DefaultIfEmpty(0).Max(); 33 | uint[] offsets = new uint[totalFrames + 1]; 34 | 35 | long currentOffset = 0; 36 | var visemes = miloLips.SelectMany(x => x.Frames.SelectMany(y => y.Events)) 37 | .Select(z => z.VisemeName) 38 | .Distinct() 39 | .ToDictionary(a => a, b => (int)(currentOffset++)); 40 | 41 | using (MemoryStream ms = new MemoryStream()) 42 | { 43 | for (int i = 0; i < totalFrames; i++) 44 | { 45 | currentOffset = ms.Position; 46 | var data = GetBytes(miloLips, visemes, i); 47 | ms.Write(data, 0, data.Length); 48 | 49 | offsets[i + 1] = (uint)ms.Position; 50 | } 51 | 52 | return new Lipsync 53 | { 54 | Version = 0, 55 | Subtype = 0, 56 | FrameRate = 30, 57 | Visemes = visemes.Select((x, y) => x.Key).ToArray(), 58 | Players = partMapping.Take(miloLips.Count).ToArray(), 59 | FrameIndices = offsets, 60 | FrameData = ms.ToArray() 61 | }; 62 | 63 | } 64 | } 65 | 66 | private static byte[] GetBytes(List lips, Dictionary visemes, int fo) 67 | { 68 | byte[] GetBytes(CharLipSync lip) 69 | { 70 | byte[] data = new byte[(lip.Frames.Length <= fo) ? 0 : lip.Frames[fo].Events.Count * 2]; 71 | if (data.Length <= 0) return data; 72 | 73 | var events = lip.Frames[fo].Events; 74 | 75 | for (int i = 0; i < events.Count; i++) 76 | { 77 | data[i * 2] = (byte)visemes[events[i].VisemeName]; 78 | data[(i * 2) + 1] = (byte)events[i].Weight; 79 | } 80 | 81 | return data; 82 | } 83 | 84 | // Calculates max interlaced frames 85 | int max = lips.Count; 86 | while (max > 0) 87 | { 88 | if (fo < lips[max - 1].Frames.Length 89 | && lips[max - 1] 90 | .Frames[fo] 91 | .Events.Count > 0) 92 | break; 93 | 94 | max--; 95 | } 96 | 97 | if (max == 0) 98 | return new byte[0]; 99 | 100 | using (MemoryStream ms = new MemoryStream()) 101 | { 102 | var buffer = GetBytes(lips.First()); 103 | ms.Write(buffer, 0, buffer.Length); 104 | 105 | foreach(var lip in lips.Take(max).Skip(1)) 106 | { 107 | ms.WriteByte(0xFF); 108 | 109 | buffer = GetBytes(lip); 110 | ms.Write(buffer, 0, buffer.Length); 111 | } 112 | 113 | return ms.ToArray(); 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /LibForge/LibForge/Lipsync/LipsyncReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using LibForge.Util; 6 | 7 | namespace LibForge.Lipsync 8 | { 9 | public class LipsyncReader : ReaderBase 10 | { 11 | public LipsyncReader(System.IO.Stream s) : base(s) { } 12 | public override Lipsync Read() 13 | { 14 | var l = new Lipsync(); 15 | l.Version = Check(Int(), 0); 16 | l.Subtype = Check(Int(), 0); 17 | l.FrameRate = Check(Float(), 30f); 18 | l.Visemes = Arr(String); 19 | l.Players = Arr(String); 20 | l.FrameIndices = Arr(UInt); 21 | l.FrameData = FixedArr(Byte, l.FrameIndices.LastOrDefault()); 22 | return l; 23 | /* 24 | l.Groups = new FrameGroup[l.Players.Length]; 25 | 26 | uint[] offsets = Arr(UInt); // Key frame offsets 27 | 28 | // Inits groups 29 | for (int i = 0; i < l.Groups.Length; i++) 30 | { 31 | l.Groups[i] = new FrameGroup(); 32 | l.Groups[i].Frames = new KeyFrame[offsets.Length - 1]; 33 | 34 | } 35 | 36 | 37 | for (int i = 0; i < offsets.Length - 1; i++) 38 | { 39 | int groupSize = (int)(offsets[i + 1] - offsets[i]); 40 | 41 | int j = 0; 42 | while (j < l.Groups.Length) 43 | { 44 | int keySize = GetKeySize(s, groupSize); 45 | if (keySize == 0) break; 46 | 47 | var events = l.Groups[i].Frames[j]; 48 | 49 | groupSize -= keySize; 50 | j++; 51 | } 52 | } 53 | */ 54 | } 55 | 56 | private static int GetKeySize(System.IO.Stream s, int max) 57 | { 58 | if (max <= 0) return 0; 59 | 60 | long start = s.Position; 61 | int i = 0; 62 | 63 | // Reads in size until terminating character 64 | while (i < max) 65 | { 66 | i++; 67 | 68 | if (s.ReadByte() == 0xFF) 69 | break; 70 | 71 | i++; 72 | } 73 | 74 | s.Seek(start, System.IO.SeekOrigin.Begin); 75 | return max; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /LibForge/LibForge/Lipsync/LipsyncWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Lipsync 7 | { 8 | public class LipsyncWriter : Util.WriterBase 9 | { 10 | public LipsyncWriter(System.IO.Stream s) : base(s) { } 11 | public override void WriteStream(Lipsync v) 12 | { 13 | Write(v.Version); 14 | Write(v.Subtype); 15 | Write(v.FrameRate); 16 | Write(v.Visemes, Write); 17 | Write(v.Players, Write); 18 | Write(v.FrameIndices, Write); 19 | s.Write(v.FrameData, 0, v.FrameData.Length); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LibForge/LibForge/Lipsync/VisemeEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Lipsync 7 | { 8 | public struct VisemeEvent 9 | { 10 | public VisemeEvent(string name, byte weight) 11 | { 12 | VisemeName = name; 13 | Weight = weight; 14 | } 15 | 16 | public string VisemeName; 17 | public byte Weight; 18 | 19 | public override string ToString() => $"{VisemeName} ({Weight})"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LibForge/LibForge/Mesh/HxMesh.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Mesh 7 | { 8 | public class HxMesh 9 | { 10 | public struct Point 11 | { 12 | public float X, Y, Z; 13 | 14 | // Texture coordinates 15 | public float U1, V1; 16 | public float U2, V2; 17 | // other stuff TBD 18 | } 19 | public class Triangle 20 | { 21 | public int V1, V2, V3; 22 | } 23 | public Point[] Points; 24 | public Triangle[] Triangles; 25 | public float Unknown1; 26 | public float Unknown2; 27 | public float Unknown3; 28 | public float Unknown4; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LibForge/LibForge/Mesh/HxMeshConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Mesh 7 | { 8 | public class HxMeshConverter 9 | { 10 | public static string ToObj(HxMesh mesh) 11 | { 12 | var sb = new StringBuilder(); 13 | foreach (var p in mesh.Points) 14 | { 15 | sb.AppendLine($"v {p.X} {p.Y} {p.Z}"); 16 | } 17 | foreach (var p in mesh.Points) 18 | { 19 | sb.AppendLine($"vt {p.U1} {1 - p.V1}"); 20 | } 21 | foreach (var t in mesh.Triangles) 22 | { 23 | // Writes vert/uv 24 | sb.AppendLine($"f {t.V1 + 1}/{t.V1 + 1} {t.V2 + 1}/{t.V2 + 1} {t.V3 + 1}/{t.V3 + 1}"); 25 | } 26 | return sb.ToString(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LibForge/LibForge/Mesh/HxMeshReader.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Mesh 9 | { 10 | public class HxMeshReader : ReaderBase 11 | { 12 | public static HxMesh ReadStream(Stream s) 13 | { 14 | return new HxMeshReader(s).Read(); 15 | } 16 | public HxMeshReader(Stream s) : base(s) 17 | { 18 | } 19 | const int kMeshFileVersion = 0xF; // 0xE in blu-ray version 20 | public override HxMesh Read() 21 | { 22 | // Note: the string doesn't have a null terminator 23 | Check(Encoding.UTF8.GetString(FixedArr(Byte, 8)), "HXMESH\r\n"); 24 | // This appears to be used to verify endianness 25 | Check(Int(), 1); 26 | var version = Int(); 27 | if (version > kMeshFileVersion) 28 | throw new Exception($"Unknown mesh version 0x{version:X}"); 29 | var vertexType = Int(); 30 | var numVerts = UInt(); 31 | var numTris = UInt(); 32 | if(version >= 0xC) 33 | { 34 | var unkFlag1 = Bool(); 35 | var unkFlag2 = Bool(); 36 | if (version >= 0xD) 37 | { 38 | var unkFlag3 = Bool(); 39 | var unkFlag4 = Bool(); 40 | } 41 | } 42 | if(version >= 0x3) 43 | { 44 | var keepMeshData = Bool(); 45 | } 46 | var vertexUsageFlags = UInt(); 47 | var faceUsageFlags = UInt(); 48 | if (version > 0xA) 49 | { 50 | var unk = UInt(); 51 | } 52 | return new HxMesh 53 | { 54 | Unknown1 = Float(), 55 | Unknown2 = Float(), 56 | Unknown3 = Float(), 57 | Unknown4 = Float(), 58 | Points = FixedArr(() => new HxMesh.Point 59 | { 60 | X = Float(), 61 | Y = Float(), 62 | Z = Float().Then(Skip(32)), // TODO: what are these bytes 63 | U1 = Half(), 64 | V1 = Half(), 65 | U2 = Half(), 66 | V2 = Half().Then(Skip(vertexType == 7 ? 12 : 0)) // TODO: what are these bytes 67 | }, numVerts), 68 | Triangles = FixedArr(() => new HxMesh.Triangle 69 | { 70 | V1 = Int(), 71 | V2 = Int(), 72 | V3 = Int() 73 | }, numTris) 74 | }; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /LibForge/LibForge/Mesh/HxMeshWriter.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Mesh 9 | { 10 | public class HxMeshWriter : WriterBase 11 | { 12 | public HxMeshWriter(Stream s) : base(s) 13 | { 14 | } 15 | 16 | public override void WriteStream(HxMesh v) 17 | { 18 | throw new NotImplementedException(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LibForge/LibForge/Meta.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge 9 | { 10 | public static class Meta 11 | { 12 | public static string BuildString => Assembly.GetAssembly(typeof(Meta)).GetName().Version.ToString(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /LibForge/LibForge/Midi/MidiFileResource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Midi 7 | { 8 | public class MidiFileResource 9 | { 10 | public class TEMPO 11 | { 12 | public float StartMillis; 13 | public uint StartTick; 14 | public int Tempo; 15 | } 16 | public struct TIMESIG 17 | { 18 | public int Measure; 19 | public uint Tick; 20 | public short Numerator; 21 | public short Denominator; 22 | } 23 | public struct BEAT 24 | { 25 | public uint Tick; 26 | public bool Downbeat; 27 | } 28 | public struct FUSER_DATA 29 | { 30 | public byte[] data; 31 | } 32 | 33 | public int MidiSongResourceMagic; 34 | public uint LastTrackFinalTick; 35 | public MidiCS.MidiTrack[] MidiTracks; 36 | public int? FuserRevision; 37 | public uint FinalTick; 38 | public uint Measures; 39 | public uint[] Unknown; 40 | public uint FinalTickMinusOne; 41 | public float[] UnknownFloats; 42 | public TEMPO[] Tempos; 43 | public TIMESIG[] TimeSigs; 44 | public BEAT[] Beats; 45 | public int UnknownZero; 46 | 47 | public int? FuserRevision2; 48 | public FUSER_DATA[] FuserData; 49 | public string[] MidiTrackNames; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /LibForge/LibForge/Midi/MidiFileResourceReader.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using MidiCS; 3 | using MidiCS.Events; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | 10 | namespace LibForge.Midi 11 | { 12 | public class MidiFileResourceReader : ReaderBase 13 | { 14 | public MidiFileResourceReader(Stream s) : base(s) 15 | { 16 | } 17 | 18 | public override MidiFileResource Read() 19 | { 20 | var r = new MidiFileResource(); 21 | Read(r); 22 | return r; 23 | } 24 | 25 | public void Read(MidiFileResource r) 26 | { 27 | r.MidiSongResourceMagic = Check(Int(), 2); 28 | r.LastTrackFinalTick = UInt(); 29 | r.MidiTracks = Arr(ReadMidiTrack); 30 | var finalTickOrRev = UInt(); 31 | if (finalTickOrRev == 0x56455223) // '#REV' 32 | { 33 | r.FuserRevision = Int(); 34 | r.FinalTick = UInt(); 35 | } else 36 | { 37 | r.FinalTick = finalTickOrRev; 38 | } 39 | r.Measures = UInt(); 40 | r.Unknown = FixedArr(UInt, 6); 41 | r.FinalTickMinusOne = Check(UInt(), r.FinalTick - 1); 42 | r.UnknownFloats = FixedArr(Float, 4); 43 | r.Tempos = Arr(ReadTempo); 44 | r.TimeSigs = Arr(ReadTimesig); 45 | r.Beats = Arr(ReadBeat); 46 | r.UnknownZero = Check(Int(), 0); 47 | if (r.FuserRevision == 2) 48 | { 49 | r.FuserRevision2 = Int(); 50 | r.FuserData = Arr(ReadFuserData); 51 | } 52 | r.MidiTrackNames = CheckedArr(String, (uint)r.MidiTracks.Length); 53 | } 54 | 55 | private uint midiTick; 56 | private string trackName; 57 | private string[] trackStrings; 58 | private MidiTrack ReadMidiTrack() 59 | { 60 | var unk = Byte(); 61 | var unk2 = Int(); 62 | var num_events = UInt(); 63 | midiTick = 0; 64 | trackName = ""; 65 | var start = s.Position; 66 | s.Position += num_events * 8; 67 | trackStrings = Arr(String); 68 | var end = s.Position; 69 | s.Position = start; 70 | var msgs = new List(FixedArr(ReadMessage, num_events)); 71 | msgs.Add(new EndOfTrackEvent(0)); 72 | s.Position = end; 73 | return new MidiTrack(msgs, midiTick, trackName); 74 | } 75 | private IMidiMessage ReadMessage() 76 | { 77 | var tick = UInt(); 78 | var deltaTime = tick - midiTick; 79 | midiTick = tick; 80 | var kind = Byte(); 81 | switch (kind) 82 | { 83 | // Midi Messages 84 | case 1: 85 | var tc = Byte(); 86 | var channel = (byte)(tc & 0xF); 87 | var type = tc >> 4; 88 | var note = Byte(); 89 | var velocity = Byte(); 90 | switch (type) 91 | { 92 | case 8: 93 | return new NoteOffEvent(deltaTime, channel, note, velocity); 94 | case 9: 95 | return new NoteOnEvent(deltaTime, channel, note, velocity); 96 | case 11: // seen in touchofgrey and others, assuming ctrl chg 97 | return new ControllerEvent(deltaTime, channel, note, velocity); 98 | case 12: // seen in foreplaylongtime, assuming prgmchg 99 | return new ProgramChgEvent(deltaTime, channel, note); 100 | case 13: // seen in huckleberrycrumble, assuming channel pressure 101 | return new ChannelPressureEvent(deltaTime, channel, note); 102 | case 14: // seen in theballadofirahayes, assuming pitch bend 103 | return new PitchBendEvent(deltaTime, channel, (ushort)(note | (velocity << 8))); 104 | default: 105 | throw new NotImplementedException($"Message type {type}"); 106 | } 107 | // Tempo 108 | case 2: 109 | var tempo_msb = (uint)Byte(); 110 | var tempo_lsb = UShort(); 111 | return new TempoEvent(deltaTime, tempo_msb << 16 | tempo_lsb); 112 | // Time Signature 113 | case 4: 114 | var num = Byte(); 115 | var denom = Byte(); 116 | var denom_pow2 = (byte)Math.Log(denom, 2); 117 | Skip(1)(); 118 | return new TimeSignature(deltaTime, num, denom_pow2, 24, 8); 119 | // Text 120 | case 8: 121 | var ttype = Byte(); 122 | var txt = trackStrings[Short()]; 123 | switch (ttype) 124 | { 125 | case 1: 126 | return new TextEvent(deltaTime, txt); 127 | case 2: 128 | return new CopyrightNotice(deltaTime, txt); 129 | case 3: 130 | trackName = txt; 131 | return new TrackName(deltaTime, txt); 132 | case 5: 133 | return new Lyric(deltaTime, txt); 134 | default: 135 | throw new NotImplementedException($"Text event {ttype} not implemented"); 136 | } 137 | default: 138 | throw new NotImplementedException($"Message kind {kind} not yet known"); 139 | } 140 | } 141 | private MidiFileResource.TEMPO ReadTempo() => new RBMid.TEMPO 142 | { 143 | StartMillis = Float(), 144 | StartTick = UInt(), 145 | Tempo = Int() 146 | }; 147 | private MidiFileResource.TIMESIG ReadTimesig() => new RBMid.TIMESIG 148 | { 149 | Measure = Int(), 150 | Tick = UInt(), 151 | Numerator = Short(), 152 | Denominator = Short() 153 | }; 154 | private MidiFileResource.BEAT ReadBeat() => new RBMid.BEAT 155 | { 156 | Tick = UInt(), 157 | Downbeat = Int() != 0 158 | }; 159 | private MidiFileResource.FUSER_DATA ReadFuserData() 160 | { 161 | var unk_count = UInt(); 162 | return new MidiFileResource.FUSER_DATA() 163 | { 164 | data = FixedArr(Byte, unk_count + 8) 165 | }; 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /LibForge/LibForge/Midi/MidiFileResourceWriter.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Util; 2 | using MidiCS; 3 | using MidiCS.Events; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | 10 | namespace LibForge.Midi 11 | { 12 | public class MidiFileResourceWriter : WriterBase 13 | { 14 | public static void WriteStream(MidiFileResource r, Stream s) 15 | { 16 | new MidiFileResourceWriter(s).WriteStream(r); 17 | } 18 | private MidiFileResourceWriter(Stream s) : base(s) { } 19 | public override void WriteStream(MidiFileResource r) 20 | { 21 | Write(r.MidiSongResourceMagic); 22 | Write(r.LastTrackFinalTick); 23 | Write(r.MidiTracks, WriteMidiTrack); 24 | if (r.FuserRevision != null) 25 | { 26 | Write(r.FuserRevision.Value); 27 | } 28 | Write(r.FinalTick); 29 | Write(r.Measures); 30 | Array.ForEach(r.Unknown, Write); 31 | Write(r.FinalTickMinusOne); 32 | Array.ForEach(r.UnknownFloats, Write); 33 | Write(r.Tempos, WriteTempo); 34 | Write(r.TimeSigs, WriteTimesig); 35 | Write(r.Beats, WriteBeat); 36 | Write(r.UnknownZero); 37 | if (r.FuserRevision != null) 38 | { 39 | Write(r.FuserRevision2.Value); 40 | Write(r.FuserData, WriteFuserData); 41 | } 42 | Write(r.MidiTrackNames, Write); 43 | } 44 | private bool first_track = true; 45 | private List track_strings; 46 | private void WriteMidiTrack(MidiTrack obj) 47 | { 48 | track_strings = new List(); 49 | 50 | Write((byte)1); 51 | if (first_track || obj.Name == "EVENTS") 52 | Write(-1); 53 | else 54 | Write(0); 55 | first_track = false; 56 | // Subtract 1 for the end-of-track event 57 | Write(obj.Messages.Count - 1); 58 | uint ticks = 0; 59 | foreach (var m in obj.Messages) 60 | { 61 | byte kind, d1, d2, d3; 62 | switch (m) 63 | { 64 | case NoteOffEvent e: 65 | kind = 1; 66 | d1 = (byte)(0x80 | e.Channel); 67 | d2 = e.Key; 68 | d3 = e.Velocity; 69 | break; 70 | case NoteOnEvent e: 71 | kind = 1; 72 | d1 = (byte)(0x90 | e.Channel); 73 | d2 = e.Key; 74 | d3 = e.Velocity; 75 | break; 76 | case ControllerEvent e: 77 | kind = 1; 78 | d1 = (byte)(0xB0 | e.Channel); 79 | d2 = e.Controller; 80 | d3 = e.Value; 81 | break; 82 | case ProgramChgEvent e: 83 | kind = 1; 84 | d1 = (byte)(0xC0 | e.Channel); 85 | d2 = e.Program; 86 | d3 = 0; 87 | break; 88 | case ChannelPressureEvent e: 89 | kind = 1; 90 | d1 = (byte)(0xD0 | e.Channel); 91 | d2 = e.Pressure; 92 | d3 = 0; 93 | break; 94 | case PitchBendEvent e: 95 | kind = 1; 96 | d1 = (byte)(0xE0 | e.Channel); 97 | d2 = (byte)(e.Bend & 0xFF); 98 | d3 = (byte)(e.Bend >> 8); 99 | break; 100 | case TempoEvent e: 101 | kind = 2; 102 | d1 = (byte)(e.MicrosPerQn >> 16); 103 | d2 = (byte)(e.MicrosPerQn & 0xFFU); 104 | d3 = (byte)((e.MicrosPerQn >> 8) & 0xFFU); 105 | break; 106 | case TimeSignature e: 107 | kind = 4; 108 | d1 = e.Numerator; 109 | d2 = (byte)(1 << e.Denominator); 110 | d3 = 0; 111 | break; 112 | case MetaTextEvent e: 113 | kind = 8; 114 | var idx = GetString(e.Text); 115 | d2 = (byte)(idx & 0xFF); 116 | d3 = (byte)(idx >> 8); 117 | switch (e) 118 | { 119 | case TextEvent x: 120 | d1 = 1; 121 | break; 122 | case TrackName x: 123 | d1 = 3; 124 | break; 125 | case Lyric x: 126 | d1 = 5; 127 | break; 128 | default: 129 | d1 = 1; 130 | break; 131 | } 132 | break; 133 | case EndOfTrackEvent e: 134 | continue; 135 | default: 136 | throw new Exception("Unknown Midi Message type"); 137 | } 138 | ticks += m.DeltaTime; 139 | Write(ticks); 140 | Write(kind); 141 | Write(d1); 142 | Write(d2); 143 | Write(d3); 144 | } 145 | Write(track_strings.Count); 146 | track_strings.ForEach(Write); 147 | } 148 | private int GetString(string s) 149 | { 150 | var idx = track_strings.Count; 151 | track_strings.Add(s); 152 | return idx; 153 | } 154 | private void WriteTempo(MidiFileResource.TEMPO obj) 155 | { 156 | Write(obj.StartMillis); 157 | Write(obj.StartTick); 158 | Write(obj.Tempo); 159 | } 160 | private void WriteTimesig(MidiFileResource.TIMESIG obj) 161 | { 162 | Write(obj.Measure); 163 | Write(obj.Tick); 164 | Write(obj.Numerator); 165 | Write(obj.Denominator); 166 | } 167 | private void WriteBeat(MidiFileResource.BEAT obj) 168 | { 169 | Write(obj.Tick); 170 | Write(obj.Downbeat ? 1 : 0); 171 | } 172 | 173 | private void WriteFuserData(MidiFileResource.FUSER_DATA obj) 174 | { 175 | Write(obj.data.Length - 8); 176 | s.Write(obj.data, 0, obj.data.Length); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /LibForge/LibForge/Milo/BlockStructure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Milo 7 | { 8 | public enum BlockStructure : uint 9 | { 10 | /// 11 | /// Structured as milo (No compression) 12 | /// 13 | MILO_A = 0xCABEDEAF, 14 | /// 15 | /// Structured as milo (Compressed with ZLib) 16 | /// 17 | MILO_B = 0xCBBEDEAF, 18 | /// 19 | /// Structured as milo (Compressed with GZip) 20 | /// 21 | MILO_C = 0xCCBEDEAF, 22 | /// 23 | /// Structured as milo (Compressed with ZLib) 24 | /// 25 | MILO_D = 0xCDBEDEAF 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LibForge/LibForge/Milo/CharLipSync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using LibForge.Extensions; 7 | using LibForge.Lipsync; 8 | 9 | namespace LibForge.Milo 10 | { 11 | public class CharLipSync : IMiloEntry 12 | { 13 | private string _name; 14 | 15 | public static CharLipSync FromStream(Stream stream, string name) 16 | { 17 | // Does not include DTA parsing so DC lipsync files are not supported at this time 18 | CharLipSync lipsync = new CharLipSync(); 19 | lipsync._name = name; 20 | 21 | lipsync.Version = stream.ReadInt32BE(); 22 | lipsync.SubVersion = stream.ReadInt32BE(); 23 | lipsync.DTAImport = stream.ReadLengthUTF8(true); 24 | 25 | int dtb = stream.ReadByte(); 26 | if (dtb != 0) 27 | throw new Exception("Parsing of LipSync files with embedded DTB is not currently supported"); 28 | 29 | stream.Position += 4; // Skips zeros 30 | int visemeCount = stream.ReadInt32BE(); 31 | lipsync.Visemes = Enumerable.Range(0, visemeCount).Select(x => stream.ReadLengthUTF8(true)).ToArray(); 32 | 33 | int keyFrameCount = stream.ReadInt32BE(); 34 | stream.Position += 4; // Skips total size of following data 35 | 36 | lipsync.Frames = new KeyFrame[keyFrameCount]; 37 | for (int i = 0; i < keyFrameCount; i++) 38 | { 39 | int eventCount = stream.ReadByte(); 40 | 41 | lipsync.Frames[i] = new KeyFrame(); 42 | lipsync.Frames[i].Events = new List(); 43 | 44 | for (int j = 0; j < eventCount; j++) 45 | { 46 | lipsync.Frames[i].Events.Add(new VisemeEvent(lipsync.Visemes[stream.ReadByte()], (byte)stream.ReadByte())); 47 | } 48 | } 49 | 50 | // There's some other data ofter this sometimes but it's not needed 51 | 52 | return lipsync; 53 | } 54 | 55 | public int Version { get; set; } 56 | public int SubVersion { get; set; } 57 | public string DTAImport { get; set; } 58 | 59 | public string[] Visemes { get; set; } 60 | public KeyFrame[] Frames { get; set; } // Sampled at 30 Hz 61 | 62 | public string Name => _name; 63 | public string Type => "CharLipSync"; 64 | 65 | public override string ToString() => $"{Name} ({Frames.Length} key frames)"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /LibForge/LibForge/Milo/IMiloEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Milo 7 | { 8 | public interface IMiloEntry 9 | { 10 | string Name { get; } 11 | string Type { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LibForge/LibForge/Milo/MiloFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using LibForge.Extensions; 7 | 8 | namespace LibForge.Milo 9 | { 10 | // Subset of code taken from Mackiloha library 11 | public class MiloFile 12 | { 13 | private const uint ADDE_PADDING = 0xADDEADDE; 14 | 15 | private MiloFile(string name, string type) 16 | { 17 | Name = name; 18 | Type = type; 19 | Entries = new List(); 20 | } 21 | 22 | public static MiloFile ReadFromStream(Stream stream) 23 | { 24 | long startingOffset = stream.Position; // You might not be starting at 0x0 25 | var structureType = (BlockStructure)stream.ReadUInt32LE(); 26 | 27 | if (!(structureType == BlockStructure.MILO_A || structureType == BlockStructure.MILO_D)) 28 | throw new Exception("Unsupported milo compression"); 29 | 30 | int offset = stream.ReadInt32LE(); // Start of blocks 31 | int blockCount = stream.ReadInt32LE(); 32 | stream.Seek(4, SeekOrigin.Current); // Skips max uncompressed size 33 | 34 | // Reads block sizes 35 | int totalSize; 36 | if (structureType == BlockStructure.MILO_A) 37 | { 38 | totalSize = Enumerable.Range(0, blockCount) 39 | .Select(x => stream.ReadInt32LE()) 40 | .Sum(); 41 | } 42 | else // Milo_D 43 | { 44 | // TODO: Implement zlib block inflation 45 | totalSize = Enumerable.Range(0, blockCount) 46 | .Select(x => 47 | { 48 | var blockSize = stream.ReadInt32LE(); 49 | var compressed = (blockSize & 0x01_00_00_00) == 0; 50 | 51 | if (compressed) 52 | throw new NotImplementedException("Zlib block inflation not implemented yet"); 53 | 54 | return blockSize & 0xFF_FF_FF; 55 | }) 56 | .Sum(); 57 | } 58 | 59 | // Jumps to first block offset 60 | stream.Position = startingOffset + offset; 61 | 62 | using (var ms = new MemoryStream()) 63 | { 64 | // Copies raw milo data 65 | stream.CopyTo(ms, totalSize); 66 | ms.Seek(0, SeekOrigin.Begin); 67 | return ParseDirectory(ms); 68 | } 69 | } 70 | 71 | private static MiloFile ParseDirectory(Stream stream) 72 | { 73 | int version = stream.ReadInt32BE(); 74 | 75 | if (!(version == 25 || version == 28)) // RBN1 and RBN2 milos 76 | throw new NotSupportedException($"Milo directory version of {version} is not supported"); 77 | 78 | string dirType = stream.ReadLengthUTF8(true), dirName = stream.ReadLengthUTF8(true); 79 | MiloFile milo = new MiloFile(dirType, dirName); 80 | 81 | stream.Position += 8; // Skips string count + total length 82 | 83 | // Reads entry types/names 84 | int count = stream.ReadInt32BE(); 85 | string[] types = new string[count]; 86 | string[] names = new string[count]; 87 | 88 | for (int i = 0; i < count; i++) 89 | { 90 | types[i] = stream.ReadLengthUTF8(true); 91 | names[i] = stream.ReadLengthUTF8(true); 92 | } 93 | 94 | // Skips unknown data 95 | var next = FindNext(stream, ADDE_PADDING); 96 | stream.Seek(next + 4, SeekOrigin.Current); 97 | 98 | // Reads each file 99 | for (int i = 0; i < names.Length; i++) 100 | { 101 | long start = stream.Position; 102 | int size = FindNext(stream, ADDE_PADDING); 103 | byte[] data = stream.ReadBytes(size); 104 | stream.Position += 4; 105 | 106 | //milo.Entries.Add(new MiloEntry(names[i], types[i], stream.ReadBytes(size))); 107 | 108 | if (types[i].Equals("CharLipSync", StringComparison.CurrentCultureIgnoreCase)) 109 | { 110 | // Parse LipSync data 111 | using (var ms = new MemoryStream(data)) 112 | { 113 | CharLipSync lipsync = CharLipSync.FromStream(ms, names[i]); 114 | milo.Entries.Add(lipsync); 115 | } 116 | } 117 | } 118 | 119 | return milo; 120 | } 121 | 122 | private static int FindNext(Stream stream, uint magic) 123 | { 124 | long start = stream.Position, currentPosition = stream.Position; 125 | uint currentMagic = 0; 126 | 127 | while (magic != currentMagic) 128 | { 129 | if (stream.Position == stream.Length) 130 | { 131 | // Couldn't find it 132 | stream.Seek(start, SeekOrigin.Begin); 133 | return -1; 134 | } 135 | 136 | currentMagic = (uint)((currentMagic << 8) | stream.ReadByte()); 137 | currentPosition++; 138 | } 139 | 140 | stream.Seek(start, SeekOrigin.Begin); 141 | return (int)((currentPosition - 4) - start); 142 | } 143 | 144 | public string Name { get; } 145 | public string Type { get; } 146 | 147 | public List Entries { get; } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /LibForge/LibForge/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("LibForge")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("LibForge")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3684b7e6-0978-487a-895c-d0ed8f6b7b9a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("0.0.1.*")] 36 | [assembly: AssemblyFileVersion("0.0.1.0")] 37 | -------------------------------------------------------------------------------- /LibForge/LibForge/RBSong/PropAnimResource.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Engine; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace LibForge.RBSong 8 | { 9 | public class PropAnimResource : EntityResource 10 | { 11 | public PropAnimResource() { Type = "PropAnimResource"; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LibForge/LibForge/RBSong/RBSongResource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using DtxCS.DataTypes; 7 | using LibForge.Engine; 8 | 9 | namespace LibForge.RBSong 10 | { 11 | public class RBSongResource : EntityResource 12 | { 13 | public RBSongResource() { Type = "RBSongResource"; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LibForge/LibForge/RBSong/RBSongResourceWriter.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Engine; 2 | using LibForge.Util; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace LibForge.RBSong 10 | { 11 | public class RBSongResourceWriter : WriterBase 12 | { 13 | public RBSongResourceWriter(Stream s) : base(s) { } 14 | public override void WriteStream(RBSongResource v) 15 | { 16 | WriteEntityResource(v); 17 | } 18 | public void WriteEntityResource(EntityResource v) 19 | { 20 | Write(v.Version); 21 | Write(v.InlineLayerNames, Write); 22 | WriteEntity(v); 23 | } 24 | public void WriteInlineResources(EntityResource er, int layerIndex) 25 | { 26 | Write(er.Version); 27 | Write(er.InlineResourceLayers[layerIndex].Length); 28 | for(int i = 0; i < er.InlineResourceLayers[layerIndex].Length; i++) 29 | { 30 | var resource = er.InlineResourceLayers[layerIndex][i]; 31 | Write(resource.Type); 32 | Write(resource.Path); 33 | WriteEntityResource(er.InlineResourceLayers[layerIndex][i] as EntityResource); 34 | } 35 | } 36 | public void WriteEntity(EntityResource rsrc) 37 | { 38 | if(rsrc.Version > 0xE) { Write(0); } 39 | Write(rsrc.Entity.Version); 40 | Write(rsrc.Entity.Layers.Length); 41 | for(int i = 0; i < rsrc.Entity.Layers.Length; i++) 42 | { 43 | WriteEntityLayer(rsrc.Entity.Layers[i]); 44 | WriteInlineResources(rsrc, i); 45 | } 46 | } 47 | public void WriteEntityLayer(EntityLayer e) 48 | { 49 | Write(e.Version); 50 | Write(e.fileSlotIndex); 51 | Write(e.TotalObjectLayers); 52 | Write(e.Objects, WriteGameObject); 53 | } 54 | public void WriteGameObject(GameObject go) 55 | { 56 | if(go == null) 57 | { 58 | Write(-1); 59 | return; 60 | } 61 | var id = go.Id.Index | (go.Id.Layer << 16); 62 | Write(id); 63 | Write(go.Rev); 64 | Write(go.Name); 65 | Write(go.Components, WriteComponent); 66 | } 67 | public void WriteComponent(Component c) 68 | { 69 | Write(c.Name1); 70 | Write(c.Name2); 71 | Write(c.Rev); 72 | Write(c.Unknown2); 73 | Write(c.Props, WritePropDef); 74 | Array.ForEach(c.Props, p => WriteValue(p.Value)); 75 | } 76 | public void WritePropDef(PropertyDef p) 77 | { 78 | Write(p.Name); 79 | WriteType(p.Type); 80 | } 81 | public void WriteType(Engine.Type t) 82 | { 83 | Write((int)t.InternalType); 84 | if(t is ArrayType) 85 | { 86 | var at = t as ArrayType; 87 | WriteType(at.ElementType); 88 | } 89 | else if(t is StructType) 90 | { 91 | var st = t as StructType; 92 | Write(st.Refcount); 93 | Write(st.Properties, WritePropDef); 94 | } 95 | } 96 | public void WriteValue(Value v) 97 | { 98 | switch (v) 99 | { 100 | case FloatValue x: Write(x.Data); break; 101 | case IntValue x: Write(x.Data); break; 102 | case ByteValue x: Write(x.Data); break; 103 | case UIntValue x: Write(x.Data); break; 104 | case LongValue x: Write(x.Data); break; 105 | case BoolValue x: Write(x.Data); break; 106 | case SymbolValue x: Write(x.Data); break; 107 | case ResourcePathValue x: 108 | Write(x.Prefix); 109 | Write(x.Data); 110 | break; 111 | case DrivenProp x: 112 | Write(x.Unknown1); 113 | Write(x.Unknown2); 114 | Write(x.ClassName); 115 | Write(x.Unknown3); 116 | Write(x.Unknown4); 117 | Write(x.PropertyName); 118 | break; 119 | case ArrayValue x: Write(x.Data, WriteValue); break; 120 | case StructValue x: Array.ForEach(x.Props, p => WriteValue(p.Value)); break; 121 | default: 122 | throw new Exception("This is an exhaustive switch"); 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /LibForge/LibForge/SongData/SongData.cs: -------------------------------------------------------------------------------- 1 | namespace LibForge.SongData 2 | { 3 | public class SongData 4 | { 5 | public uint Type; 6 | public uint SongId; 7 | public short Version; 8 | public string GameOrigin; 9 | public float PreviewStart; 10 | public float PreviewEnd; 11 | public string Name; 12 | public string Artist; 13 | public string AlbumName; 14 | public short AlbumTrackNumber; 15 | public int AlbumYear; 16 | public int OriginalYear; 17 | public string Genre; 18 | public float SongLength; 19 | public float GuitarRank; 20 | public float BassRank; 21 | public float VocalsRank; 22 | public float DrumRank; 23 | public float BandRank; 24 | public float KeysRank; 25 | public float RealKeysRank; 26 | public bool Tutorial; 27 | public bool AlbumArt; 28 | public bool Cover; 29 | public byte VocalGender; 30 | public string Medium; 31 | public bool HasFreestyleVocals; 32 | public int VocalParts; 33 | public int Flags; 34 | public bool Fake; 35 | public string Shortname; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LibForge/LibForge/SongData/SongDataConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using DtxCS.DataTypes; 6 | 7 | namespace LibForge.SongData 8 | { 9 | public class SongDataConverter 10 | { 11 | public static SongData ToSongData(DataArray songDta) 12 | { 13 | var songId = songDta.Array("song_id"); 14 | var art = songDta.Array("album_art").Any(1); 15 | var shortName = songDta.Array("song").Array("name").Any(1).Split('/').Last(); 16 | var songIdNum = (shortName.GetHashCode() & 0xFFFFFF) + 90000000; 17 | return new SongData 18 | { 19 | AlbumArt = art == "1" || art == "TRUE", 20 | AlbumName = songDta.Array("album_name")?.String(1) ?? "", 21 | AlbumTrackNumber = (short)(songDta.Array("album_track_number")?.Int(1) ?? 0), 22 | AlbumYear = songDta.Array("year_released")?.Int(1) ?? 0, 23 | Artist = songDta.Array("artist").String(1), 24 | BandRank = songDta.Array("rank").Array("band").Int(1), 25 | BassRank = songDta.Array("rank").Array("bass").Int(1), 26 | DrumRank = songDta.Array("rank").Array("drum").Int(1), 27 | GuitarRank = songDta.Array("rank").Array("guitar").Int(1), 28 | KeysRank = songDta.Array("rank").Array("keys")?.Int(1) ?? 0, 29 | RealKeysRank = songDta.Array("rank").Array("real_keys")?.Int(1) ?? 0, 30 | VocalsRank = songDta.Array("rank").Array("vocals").Int(1), 31 | Cover = false, 32 | Fake = false, 33 | Flags = 0, 34 | GameOrigin = songDta.Array("game_origin")?.Any(1) ?? "ugc_plus", 35 | Genre = songDta.Array("genre").Symbol(1).ToString(), 36 | HasFreestyleVocals = false, 37 | Medium = "", 38 | Name = songDta.Array("name").String(1), 39 | OriginalYear = songDta.Array("year_released").Int(1), 40 | Tutorial = false, 41 | Type = 11, 42 | Version = -1, 43 | VocalGender = (byte)((songDta.Array("vocal_gender")?.Any(1) ?? "male") == "male" ? 1 : 2), 44 | VocalParts = songDta.Array("song").Array("vocal_parts")?.Int(1) ?? 1, 45 | Shortname = shortName.ToLowerInvariant(), 46 | SongId = (uint)songIdNum, 47 | SongLength = songDta.Array("song_length").Int(1), 48 | PreviewStart = songDta.Array("preview").Int(1), 49 | PreviewEnd = songDta.Array("preview").Int(2), 50 | }; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /LibForge/LibForge/SongData/SongDataReader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using LibForge.Util; 3 | 4 | namespace LibForge.SongData 5 | { 6 | public class SongDataReader : ReaderBase 7 | { 8 | public static SongData ReadStream(Stream s) 9 | { 10 | return new SongDataReader(s).Read(); 11 | } 12 | public SongDataReader(Stream s) : base(s) { } 13 | 14 | public override SongData Read() 15 | { 16 | return new SongData 17 | { 18 | Type = UInt(), 19 | SongId = UInt(), 20 | Version = Short(), 21 | GameOrigin = String(18), 22 | PreviewStart = Float(), 23 | PreviewEnd = Float(), 24 | Name = String(256), 25 | Artist = String(256), 26 | AlbumName = String(256), 27 | AlbumTrackNumber = Short().Then(Skip(2)), 28 | AlbumYear = Int(), 29 | OriginalYear = Int(), 30 | Genre = String(64), 31 | SongLength = Float(), 32 | GuitarRank = Float(), 33 | BassRank = Float(), 34 | VocalsRank = Float(), 35 | DrumRank = Float(), 36 | BandRank = Float(), 37 | KeysRank = Float(), 38 | RealKeysRank = Float(), 39 | Tutorial = Bool(), 40 | AlbumArt = Bool(), 41 | Cover = Bool(), 42 | VocalGender = Byte(), 43 | Medium = String(16), 44 | HasFreestyleVocals = Bool().Then(Skip(3)), 45 | VocalParts = Int(), 46 | Flags = Int(), 47 | Fake = Bool(), 48 | Shortname = String(256) 49 | }; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /LibForge/LibForge/SongData/SongDataWriter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using LibForge.Util; 3 | 4 | namespace LibForge.SongData 5 | { 6 | public class SongDataWriter : WriterBase 7 | { 8 | public static void WriteStream(SongData r, Stream s) 9 | { 10 | new SongDataWriter(s).WriteStream(r); 11 | } 12 | private SongDataWriter(Stream s) : base(s) { } 13 | public override void WriteStream(SongData r) 14 | { 15 | Write(r.Type); 16 | Write(r.SongId); 17 | Write(r.Version); 18 | Write(r.GameOrigin, 18); 19 | Write(r.PreviewStart); 20 | Write(r.PreviewEnd); 21 | Write(r.Name, 256); 22 | Write(r.Artist, 256); 23 | Write(r.AlbumName, 256); 24 | Write(r.AlbumTrackNumber); 25 | s.Position += 2; 26 | Write(r.AlbumYear); 27 | Write(r.OriginalYear); 28 | Write(r.Genre, 64); 29 | Write(r.SongLength); 30 | Write(r.GuitarRank); 31 | Write(r.BassRank); 32 | Write(r.VocalsRank); 33 | Write(r.DrumRank); 34 | Write(r.BandRank); 35 | Write(r.KeysRank); 36 | Write(r.RealKeysRank); 37 | Write(r.Tutorial); 38 | Write(r.AlbumArt); 39 | Write(r.Cover); 40 | Write(r.VocalGender); 41 | Write(r.Medium, 16); 42 | Write(r.HasFreestyleVocals); 43 | s.Position += 3; 44 | Write(r.VocalParts); 45 | Write(r.Flags); 46 | Write(r.Fake); 47 | Write(r.Shortname, 256); 48 | s.WriteByte(0); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /LibForge/LibForge/Texture/Texture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace LibForge.Texture 7 | { 8 | public class Texture 9 | { 10 | public class Mipmap 11 | { 12 | public int Width; 13 | public int Height; 14 | public int Flags; 15 | public byte[] Data; 16 | } 17 | public int Version; 18 | public Mipmap[] Mipmaps; 19 | public byte[] HeaderData; 20 | public byte[] FooterData; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LibForge/LibForge/Texture/TextureReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using LibForge.Util; 7 | 8 | namespace LibForge.Texture 9 | { 10 | public class TextureReader : ReaderBase 11 | { 12 | public static Texture ReadStream(Stream s) 13 | { 14 | return new TextureReader(s).Read(); 15 | } 16 | public TextureReader(Stream s) : base(s) { } 17 | 18 | public override Texture Read() 19 | { 20 | var magic = Int(); 21 | if(magic != 6 && magic != 4 && magic != 3) 22 | { 23 | throw new Exception($"Unknown texture magic {magic}"); 24 | } 25 | var version = Int(); 26 | var hdrData = magic == 6 ? FixedArr(Byte, version == 0xC ? 0x7Cu : 0xACu) 27 | : magic == 3 ? FixedArr(Byte, 0x8Cu) 28 | : FixedArr(Byte, 0xA4u); 29 | var MipmapLevels = UInt(); 30 | var Mipmaps = FixedArr(() => new Texture.Mipmap 31 | { 32 | Width = Int(), 33 | Height = Int(), 34 | Flags = version == 0xC ? Int().Then(Skip(8)) : Int() 35 | }, MipmapLevels); 36 | UInt(); 37 | for(var i = 0; i < Mipmaps.Length; i++) 38 | { 39 | if (magic == 3) 40 | { 41 | Mipmaps[i].Data = FixedArr(Byte, (uint) (Mipmaps[i].Width * Mipmaps[i].Height) / 2); 42 | } 43 | else 44 | { 45 | Mipmaps[i].Data = Arr(Byte); 46 | } 47 | } 48 | var footerData = FixedArr(Byte, 0x1C); 49 | return new Texture 50 | { 51 | Version = magic, 52 | Mipmaps = Mipmaps, 53 | HeaderData = hdrData, 54 | FooterData = footerData 55 | }; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /LibForge/LibForge/Texture/TextureWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using LibForge.Util; 7 | 8 | namespace LibForge.Texture 9 | { 10 | public class TextureWriter : WriterBase 11 | { 12 | public static void WriteStream(Texture r, Stream s) 13 | { 14 | new TextureWriter(s).WriteStream(r); 15 | } 16 | private TextureWriter(Stream s) : base(s) { } 17 | public override void WriteStream(Texture r) 18 | { 19 | Write(r.Version); 20 | s.Write(r.HeaderData, 0, r.HeaderData.Length); 21 | Write(r.Mipmaps, level => 22 | { 23 | Write(level.Width); 24 | Write(level.Height); 25 | Write(level.Flags); 26 | }); 27 | Write(6); 28 | foreach(var map in r.Mipmaps) 29 | { 30 | Write(map.Data.Length); 31 | s.Write(map.Data, 0, map.Data.Length); 32 | } 33 | s.Write(r.FooterData, 0, r.FooterData.Length); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LibForge/LibForge/Util/BinReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using LibForge.Extensions; 7 | 8 | namespace LibForge.Util 9 | { 10 | public class BinReader 11 | { 12 | protected Stream s; 13 | public BinReader(Stream stream) 14 | { 15 | s = stream; 16 | } 17 | public T Object() 18 | { 19 | T ret = default; 20 | foreach (var field in typeof(T).GetFields()) 21 | { 22 | var type = field.FieldType; 23 | field.SetValue(ret, Read(field.FieldType)); 24 | } 25 | return default; 26 | } 27 | public object Read(Type t) 28 | { 29 | var readers = 30 | new Dictionary> { 31 | { typeof(int), () => Int() }, 32 | { typeof(uint), () => UInt() }, 33 | { typeof(long), () => Long() }, 34 | { typeof(ulong), () => ULong() }, 35 | { typeof(float), () => Float() }, 36 | { typeof(short), () => Short() }, 37 | { typeof(ushort), () => UShort() }, 38 | { typeof(byte), () => Byte() }, 39 | { typeof(string), () => String() }, 40 | { typeof(bool), () => Bool() }, 41 | }; 42 | if (readers.ContainsKey(t)) 43 | return readers[t](); 44 | var method = GetType().GetMethod(nameof(Object)).MakeGenericMethod(t); 45 | return method.Invoke(this, new object[] { }); 46 | } 47 | public static Func Seq(Action a, Func v) 48 | { 49 | return () => 50 | { 51 | a(); 52 | return v(); 53 | }; 54 | } 55 | public T Check(T v, T expected, string where = null) 56 | { 57 | if (v.Equals(expected)) 58 | return v; 59 | throw new Exception($"Invalid data encountered at {s.Position:X} {where}: expected {expected}, got {v}"); 60 | } 61 | 62 | public byte CheckRange(byte v, byte minimum, byte maximum) 63 | { 64 | if (minimum <= v && maximum >= v) 65 | return v; 66 | throw new Exception($"Range of {minimum} -> {maximum} exceeded at {s.Position:X}: got {v} "); 67 | } 68 | public int CheckRange(int v, int minimum, int maximum) 69 | { 70 | if (minimum <= v && maximum >= v) 71 | return v; 72 | throw new Exception($"Range of {minimum} -> {maximum} exceeded at {s.Position:X}: got {v} "); 73 | } 74 | // For reading a fixed size array of something 75 | public T[] FixedArr(Func constructor, uint size) 76 | { 77 | if (size > s.Length - s.Position) 78 | { 79 | throw new Exception($"Invalid array size {size:X} encountered at {s.Position:X}. File is corrupt or not understood."); 80 | } 81 | var arr = new T[size]; 82 | for (var i = 0; i < size; i++) 83 | arr[i] = constructor(); 84 | 85 | return arr; 86 | } 87 | // For reading a length-prefixed array of something 88 | public T[] Arr(Func constructor, uint maxSize = 0) 89 | { 90 | var size = UInt(); 91 | if (maxSize != 0 && size > maxSize) 92 | throw new Exception($"Array was too big ({size} > {maxSize}) at {s.Position:X}"); 93 | return FixedArr(constructor, size); 94 | } 95 | public T[] CheckedArr(Func constructor, uint size) 96 | { 97 | var fileSize = UInt(); 98 | if (fileSize != size) 99 | throw new Exception($"Invalid array size ({fileSize} != {size}) at {s.Position:X}"); 100 | return FixedArr(constructor, size); 101 | } 102 | // For skipping unknown data 103 | public Action Skip(int count) => () => s.Position += count; 104 | // For reading simple types 105 | public int Int() => s.ReadInt32LE(); 106 | public uint UInt() => s.ReadUInt32LE(); 107 | public long Long() => s.ReadInt64LE(); 108 | public ulong ULong() => s.ReadUInt64LE(); 109 | public float Half() => s.ReadHalfFloat(); 110 | public float Float() => s.ReadFloat(); 111 | public short Short() => s.ReadInt16LE(); 112 | public ushort UShort() => s.ReadUInt16LE(); 113 | public byte Byte() => (byte)s.ReadByte(); 114 | public string String() => s.ReadLengthPrefixedString(Encoding.UTF8); 115 | public string String(int length) => s.ReadFixedLengthNullTerminatedString(length); 116 | public string FixedString(int length) => s.ReadFixedLengthString(length); 117 | public string UE4String() => String(Int()); 118 | public uint UInt24() => s.ReadUInt24LE(); 119 | /// 120 | /// Reads a byte as a boolean, throwing if it's not 1 or 0 121 | /// 122 | public bool Bool() => CheckRange(Byte(), (byte)0, (byte)1) != 0; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /LibForge/LibForge/Util/BinWriter.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Extensions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Util 9 | { 10 | public class BinWriter 11 | { 12 | protected Stream s; 13 | public BinWriter(Stream s) 14 | { 15 | this.s = s; 16 | } 17 | public void Write(byte v) => s.WriteByte(v); 18 | public void Write(short v) => s.WriteInt16LE(v); 19 | public void Write(ushort v) => s.WriteUInt16LE(v); 20 | public void Write(int v) => s.WriteInt32LE(v); 21 | public void Write(uint v) => s.WriteUInt32LE(v); 22 | public void Write(long v) => s.WriteInt64LE(v); 23 | public void Write(ulong v) => s.WriteUInt64LE(v); 24 | public void Write(float v) => s.Write(BitConverter.GetBytes(v), 0, 4); 25 | public void Write(bool v) => s.WriteByte((byte)(v ? 1 : 0)); 26 | public void Write(string v) 27 | { 28 | var bytes = Encoding.UTF8.GetBytes(v); 29 | s.WriteInt32LE(bytes.Length); 30 | s.Write(bytes, 0, bytes.Length); 31 | } 32 | public void Write(string v, int length) 33 | { 34 | var bytes = Encoding.UTF8.GetBytes(v); 35 | s.Write(bytes, 0, bytes.Length); 36 | s.WriteByte(0); 37 | s.Position += length - bytes.Length - 1; 38 | } 39 | public void Write(T[] arr, Action writer) 40 | { 41 | // Treat uninitialized arrays as empty ones 42 | if (arr == null) 43 | { 44 | Write(0); 45 | return; 46 | } 47 | Write(arr.Length); 48 | foreach (var x in arr) 49 | { 50 | writer(x); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /LibForge/LibForge/Util/EncryptedReadStream.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * EncryptedReadStream.cs 3 | * 4 | * Copyright (c) 2015,2016,2017 maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.IO; 22 | using LibForge.Extensions; 23 | 24 | namespace LibForge.Util 25 | { 26 | class EncryptedReadStream : Stream 27 | { 28 | private long position; 29 | private int key; 30 | private int curKey; 31 | private long keypos; 32 | private Stream file; 33 | public byte xor; 34 | 35 | internal EncryptedReadStream(Stream file, byte xor = 0) 36 | { 37 | file.Position = 0; 38 | // The initial key is found in the first 4 bytes. 39 | this.key = cryptRound(file.ReadInt32LE()); 40 | this.position = 0; 41 | this.keypos = 0; 42 | this.curKey = this.key; 43 | this.file = file; 44 | this.Length = file.Length - 4; 45 | this.xor = xor; 46 | } 47 | 48 | public override bool CanRead => position < Length && position >= 0; 49 | public override bool CanSeek => true; 50 | public override bool CanWrite => false; 51 | public override long Length { get; } 52 | 53 | public override long Position 54 | { 55 | get 56 | { 57 | return position; 58 | } 59 | 60 | set 61 | { 62 | Seek(value, SeekOrigin.Begin); 63 | } 64 | } 65 | 66 | private void updateKey() 67 | { 68 | if (keypos == position) 69 | return; 70 | if (keypos > position) // reset key 71 | { 72 | keypos = 0; 73 | curKey = key; 74 | } 75 | while (keypos < position) // don't think there's a faster way to do this 76 | { 77 | curKey = cryptRound(curKey); 78 | keypos++; 79 | } 80 | } 81 | 82 | private int cryptRound(int key) 83 | { 84 | int ret = (key - ((key / 0x1F31D) * 0x1F31D)) * 0x41A7 - (key / 0x1F31D) * 0xB14; 85 | if (ret <= 0) 86 | ret += 0x7FFFFFFF; 87 | return ret; 88 | } 89 | 90 | public override int Read(byte[] buffer, int offset, int count) 91 | { 92 | // ensure file is at correct offset 93 | file.Seek(this.position + 4, SeekOrigin.Begin); 94 | if (offset + count > buffer.Length) 95 | { 96 | throw new IndexOutOfRangeException("Attempt to fill buffer past its end"); 97 | } 98 | if (this.Position == this.Length || this.Position + count > this.Length) 99 | { 100 | count = (int)(this.Length - this.Position); 101 | //throw new System.IO.EndOfStreamException("Cannot read past end of file."); 102 | } 103 | 104 | int bytesRead = file.Read(buffer, offset, count); 105 | 106 | for (uint i = 0; i < bytesRead; i++) 107 | { 108 | buffer[offset + i] ^= (byte)(this.curKey ^ xor); 109 | this.position++; 110 | updateKey(); 111 | } 112 | return bytesRead; 113 | } 114 | 115 | public override long Seek(long offset, SeekOrigin origin) 116 | { 117 | int adjust = origin == SeekOrigin.Current ? 0 : 4; 118 | this.position = file.Seek(offset + adjust, origin) - 4; 119 | updateKey(); 120 | return position; 121 | } 122 | 123 | #region Not Used 124 | 125 | public override void Flush() 126 | { 127 | throw new NotSupportedException(); 128 | } 129 | 130 | public override void SetLength(long value) 131 | { 132 | throw new NotSupportedException(); 133 | } 134 | 135 | public override void Write(byte[] buffer, int offset, int count) 136 | { 137 | throw new NotSupportedException(); 138 | } 139 | 140 | #endregion 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /LibForge/LibForge/Util/EncryptedWriteStream.cs: -------------------------------------------------------------------------------- 1 | using LibForge.Extensions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace LibForge.Util 9 | { 10 | public class EncryptedWriteStream : Stream 11 | { 12 | private long position; 13 | private int key; 14 | private int curKey; 15 | private long keypos; 16 | private Stream file; 17 | public byte xor; 18 | 19 | 20 | internal EncryptedWriteStream(Stream file, int key, byte xor = 0) 21 | { 22 | file.Position = 0; 23 | file.WriteInt32LE(key); 24 | position = 0; 25 | keypos = 0; 26 | // The initial key is found in the first 4 bytes. 27 | this.key = cryptRound(key); 28 | this.curKey = this.key; 29 | this.file = file; 30 | this.xor = xor; 31 | } 32 | 33 | public override bool CanRead => false; 34 | public override bool CanSeek => true; 35 | public override bool CanWrite => file.CanWrite; 36 | public override long Length => file.Length - 4; 37 | 38 | public override long Position 39 | { 40 | get 41 | { 42 | return position; 43 | } 44 | 45 | set 46 | { 47 | Seek(value, SeekOrigin.Begin); 48 | } 49 | } 50 | 51 | private void updateKey() 52 | { 53 | if (keypos == position) 54 | return; 55 | if (keypos > position) // reset key 56 | { 57 | keypos = 0; 58 | curKey = key; 59 | } 60 | while (keypos < position) // don't think there's a faster way to do this 61 | { 62 | curKey = cryptRound(curKey); 63 | keypos++; 64 | } 65 | } 66 | 67 | private int cryptRound(int key) 68 | { 69 | int ret = (key - ((key / 0x1F31D) * 0x1F31D)) * 0x41A7 - (key / 0x1F31D) * 0xB14; 70 | if (ret <= 0) 71 | ret += 0x7FFFFFFF; 72 | return ret; 73 | } 74 | 75 | public override int Read(byte[] buffer, int offset, int count) 76 | { 77 | throw new NotImplementedException(); 78 | } 79 | 80 | public override long Seek(long offset, SeekOrigin origin) 81 | { 82 | int adjust = origin == SeekOrigin.Current ? 0 : 4; 83 | this.position = file.Seek(offset + adjust, origin) - 4; 84 | updateKey(); 85 | return position; 86 | } 87 | 88 | #region Not Used 89 | 90 | public override void Flush() 91 | { 92 | throw new NotSupportedException(); 93 | } 94 | 95 | public override void SetLength(long value) 96 | { 97 | throw new NotSupportedException(); 98 | } 99 | 100 | public override void Write(byte[] buffer, int offset, int count) 101 | { 102 | if (offset + count > buffer.Length) 103 | { 104 | throw new IndexOutOfRangeException("Attempt to read buffer past its end"); 105 | } 106 | updateKey(); 107 | var copy = new byte[count]; 108 | Buffer.BlockCopy(buffer, offset, copy, 0, count); 109 | for (uint i = 0; i < count; i++) 110 | { 111 | copy[i] ^= (byte)(this.curKey ^ xor); 112 | position++; 113 | updateKey(); 114 | } 115 | // ensure file is at correct offset 116 | file.Seek(this.position + 4 - count, SeekOrigin.Begin); 117 | file.Write(copy, 0, count); 118 | } 119 | 120 | #endregion 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /LibForge/LibForge/Util/ReaderBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using LibForge.Extensions; 5 | 6 | namespace LibForge.Util 7 | { 8 | static class ReaderExtensions 9 | { 10 | // For doing side effects else fluently in an expression 11 | public static T Then(this T v, Action a) 12 | { 13 | a(); 14 | return v; 15 | } 16 | public static T Then(this T v, Action a) 17 | { 18 | a(v); 19 | return v; 20 | } 21 | } 22 | public abstract class ReaderBase : BinReader 23 | { 24 | public ReaderBase(System.IO.Stream s) : base(s) { } 25 | public virtual D Read() => (D)Read(typeof(D)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LibForge/LibForge/Util/WriterBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using LibForge.Extensions; 4 | 5 | namespace LibForge.Util 6 | { 7 | public abstract class WriterBase : BinWriter 8 | { 9 | public WriterBase(System.IO.Stream s) : base(s) { } 10 | public abstract void WriteStream(D v); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /LibForge/LibForgeTests/LibForgeTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {DB683EFE-A4DE-4A8C-BD78-30B84787A027} 8 | Library 9 | Properties 10 | LibForgeTests 11 | LibForgeTests 12 | v4.7.1 13 | 512 14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 15.0 16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 18 | False 19 | UnitTest 20 | 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | 42 | ..\packages\MSTest.TestFramework.1.2.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll 43 | 44 | 45 | ..\packages\MSTest.TestFramework.1.2.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll 46 | 47 | 48 | ..\..\Dependencies\MidiCS.dll 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | {3684b7e6-0978-487a-895c-d0ed8f6b7b9a} 64 | LibForge 65 | 66 | 67 | 68 | 69 | 70 | Always 71 | 72 | 73 | Always 74 | 75 | 76 | 77 | 78 | 79 | 80 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /LibForge/LibForgeTests/MidiConverterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using MidiCS; 6 | using LibForge.Midi; 7 | 8 | namespace LibForgeTests 9 | { 10 | [TestClass] 11 | public class MidiConverterTests 12 | { 13 | static System.IO.Stream GetFile(string name) => System.IO.File.OpenRead("TestData\\"+name); 14 | static RBMid GetMid(string name) => RBMidConverter.ToRBMid(MidiFileReader.FromStream(GetFile(name))); 15 | [TestMethod] 16 | public void TestHOPOs() 17 | { 18 | var rbmid = GetMid("hopos.mid"); 19 | var expected = 20 | "nnnnnynynynnnynnnynynnnynnnynynnynyyyynn" + 21 | "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn" + 22 | "nnnnnnnnnnnnnnnnnnnnnnnnnnnynnynnynnynnn" + 23 | "nnnnnnnnnnnnnnyynynnnnnnynynynnynn"; 24 | var actual = new string(rbmid.GemTracks[1].Gems[3].Select(g => g.IsHopo ? 'y' : 'n').ToArray()); 25 | Assert.AreEqual(expected, actual); 26 | } 27 | 28 | [TestMethod] 29 | public void TestProMarkers() 30 | { 31 | var rbmid = GetMid("pro_markers.mid"); 32 | var expected = 33 | "TTCCC" + 34 | "TTTCC" + 35 | "TTCTC" + 36 | "TTCCT" + 37 | "TTTTC" + 38 | "TTCTT" + 39 | "TTTCT" + 40 | "TTTTT" + 41 | "TTCTTCCTC"; 42 | var actual = new string(rbmid.GemTracks[0].Gems[3].Select(g => g.ProCymbal == 1 ? 'C' : 'T').ToArray()); 43 | Assert.AreEqual(expected, actual); 44 | 45 | var expected_2 = "012436571313260"; 46 | var actual_2 = rbmid.ProMarkers[0].Markers.Select(m => ((int)m.Flags / 4).ToString()).Aggregate(string.Concat); 47 | Assert.AreEqual(expected_2, actual_2); 48 | } 49 | 50 | [TestMethod] 51 | public void TestProMarkersGuitar() 52 | { 53 | // These are probably useless on guitar, but let's just test it for completion's sake 54 | var rbmid = GetMid("pro_markers.mid"); 55 | var expected = 56 | "TTCCC" + 57 | "TTCC" + 58 | "TTC" + 59 | "TTC" + 60 | "TT"; 61 | var actual = new string(rbmid.GemTracks[1].Gems[3].Select(g => g.ProCymbal == 1 ? 'C' : 'T').ToArray()); 62 | Assert.AreEqual(expected, actual); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /LibForge/LibForgeTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("LibForgeTests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("LibForgeTests")] 10 | [assembly: AssemblyCopyright("Copyright © 2018")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("db683efe-a4de-4a8c-bd78-30b84787a027")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /LibForge/LibForgeTests/RBMidiFileTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using LibForge.Midi; 8 | using MidiCS; 9 | using System.IO; 10 | 11 | namespace LibForgeTests 12 | { 13 | [TestClass] 14 | public class RBMidiFileTests 15 | { 16 | static System.IO.Stream GetFile(string name) => System.IO.File.OpenRead("TestData\\" + name); 17 | static RBMid GetMid(string name) { using (var s = GetFile(name)) return RBMidConverter.ToRBMid(MidiFileReader.FromStream(GetFile(name))); } 18 | [TestMethod] 19 | public void TestRBMidiFileRoundTrip() 20 | { 21 | RBMid rbmid = GetMid("hopos.mid"); 22 | try 23 | { 24 | using (var ms = new MemoryStream()) 25 | { 26 | RBMidWriter.WriteStream(rbmid, ms); 27 | ms.Position = 0; 28 | var rbmid2 = new RBMidReader(ms).Read(); 29 | var diff = rbmid.Compare(rbmid2); 30 | Assert.AreEqual(null, diff, "RBMid differed after round-trip at element " + diff); 31 | } 32 | } 33 | catch(Exception e) 34 | { 35 | Assert.Fail("Exception occurred writing or reading an RBMid: " + e.Message); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LibForge/LibForgeTests/TestData/hopos.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtolly/LibForge/31208f52f6460e64bdf42893ed2157f895b0baae/LibForge/LibForgeTests/TestData/hopos.mid -------------------------------------------------------------------------------- /LibForge/LibForgeTests/TestData/pro_markers.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mtolly/LibForge/31208f52f6460e64bdf42893ed2157f895b0baae/LibForge/LibForgeTests/TestData/pro_markers.mid -------------------------------------------------------------------------------- /LibForge/LibForgeTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a fork of LibForge/ForgeTool by Maxton, with continuing work to fix bugs and add features. 2 | 3 | Download the latest build from [Appveyor](https://ci.appveyor.com/project/LlysiX/libforge/build/artifacts) or from the [releases page](https://github.com/mtolly/LibForge/releases) if the artifacts have expired 4 | 5 | # Original readme follows 6 | 7 | ## About This Repository 8 | 9 | I am keeping track of my research into RB4 customs here. 10 | 11 | I am working out the structure of the files in Rock Band 4. These are being documented in the form of 010 Editor Template files, which are in the `010` directory. 12 | 13 | ## Latest Build 14 | 15 | Can be downloaded at [Appveyor](https://ci.appveyor.com/project/maxton/libforge/branch/master/artifacts) 16 | 17 | ## LibForge 18 | 19 | This is a library I'm working on that handles reading, writing, and converting for formats in the Forge engine used by Rock Band 4 and Rock Band VR. 20 | 21 | It is licensed under the GNU LGPLv3 and includes two frontends at the moment: 22 | 23 | ### ForgeTool 24 | 25 | This is a command line tool that does file conversions. 26 | 27 | ``` 28 | Usage: ForgeTool.exe [options] 29 | Verbs: 30 | version 31 | - Prints the version number and exits 32 | rbmid2mid 33 | - converts a Forge midi to a Standard Midi File 34 | reprocess 35 | - converts a Forge midi to a Forge midi 36 | mid2rbmid 37 | - converts a Standard Midi File to a Forge midi 38 | tex2png 39 | - converts a Forge texture to PNG 40 | mesh2obj 41 | - converts a Forge mesh to OBJ 42 | con2gp4 [--scee] [--id 16CHARIDENTIFIER] [--desc "Package Description"] 43 | - converts a CON custom to a .gp4 project in the given output directory 44 | --scee : make an EU package 45 | --id <16CHARIDENTIFIER> : set the customizable part of the Package ID/Filename 46 | --desc "Package Description" : set the description of the package 47 | con2pkg [--scee] [--id 16CHARIDENTIFIER] [--desc "Package Description"] 48 | - converts a CON custom to a PS4 PKG custom in the given output directory 49 | --scee : make an EU package 50 | --id <16CHARIDENTIFIER> : set the customizable part of the Package ID/Filename 51 | --desc "Package Description" : set the description of the package 52 | con2rbvr [--noark] [--prefix PREFIX] 53 | - converts a CON custom to a RBVR custom in the given game directory 54 | --noark : creates a songs folder to be used with patchcreator in arkhelper instead of a DLC ARK 55 | --prefix : sets a custom prefix to better organize your converted song files 56 | arkorder 57 | - outputs the arkorder DTA of a given ARK HDR file 58 | arkbuild 59 | - creates an ARK file using files with the specified arkorder in the output directory 60 | milo2lipsync 61 | - converts an uncompressed milo archive to forge lipsync file 62 | csv2txt 63 | - decodes a csv file 64 | dta2b 65 | - converts text dta into encoded dta 66 | ``` 67 | 68 | ### ForgeToolGUI 69 | 70 | This has an ark/PFS/folder browser with support for previewing the following: 71 | - Textures (.png_pc, .png_ps4, .bmp_pc, .bmp_ps4, etc) 72 | - Models (a 3d preview with optional wireframe) 73 | - .songdta files 74 | - Data (.dta/.dtb/.\*\_dta\_\*/.moggsong) files 75 | - RBmid files 76 | - RBsong files 77 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.1.{build} 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | image: Visual Studio 2019 8 | 9 | configuration: Release 10 | 11 | platform: Any CPU 12 | assembly_info: 13 | patch: true 14 | file: 'LibForge\*\Properties\AssemblyInfo.*' 15 | assembly_version: '{version}.0' 16 | assembly_file_version: '{version}.0' 17 | assembly_informational_version: '{version}.0' 18 | 19 | install: 20 | - cmd: git submodule update --init --recursive 21 | 22 | before_build: 23 | - cmd: nuget restore LibForge/LibForge.sln 24 | 25 | build: 26 | project: LibForge/LibForge.sln 27 | parallel: true 28 | verbosity: minimal 29 | 30 | after_build: 31 | - > 32 | 7z a LibForge-%APPVEYOR_BUILD_VERSION%.zip 33 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeTool\bin\Release\LibForge.dll 34 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeTool\bin\Release\MidiCS.dll 35 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\GameArchives.dll 36 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\LibOrbisPkg.dll 37 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\DtxCS.dll 38 | %APPVEYOR_BUILD_FOLDER%\README.md 39 | %APPVEYOR_BUILD_FOLDER%\LICENSE.txt 40 | - > 41 | 7z a ForgeTool-%APPVEYOR_BUILD_VERSION%.zip 42 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeTool\bin\Release\ForgeTool.exe 43 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeTool\bin\Release\LibForge.dll 44 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeTool\bin\Release\MidiCS.dll 45 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\GameArchives.dll 46 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\LibOrbisPkg.dll 47 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\DtxCS.dll 48 | %APPVEYOR_BUILD_FOLDER%\README.md 49 | %APPVEYOR_BUILD_FOLDER%\LICENSE.txt 50 | - > 51 | 7z a ForgeToolGUI-%APPVEYOR_BUILD_VERSION%.zip 52 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeTool\bin\Release\ForgeTool.exe 53 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeTool\bin\Release\LibForge.dll 54 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeTool\bin\Release\MidiCS.dll 55 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\GameArchives.dll 56 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\LibOrbisPkg.dll 57 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\OpenTK.dll 58 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\OpenTK.GLControl.dll 59 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\DtxCS.dll 60 | %APPVEYOR_BUILD_FOLDER%\LibForge\ForgeToolGUI\bin\Release\ForgeToolGUI.exe 61 | %APPVEYOR_BUILD_FOLDER%\README.md 62 | %APPVEYOR_BUILD_FOLDER%\LICENSE.txt 63 | artifacts: 64 | - path: LibForge-%APPVEYOR_BUILD_VERSION%.zip 65 | - path: ForgeTool-%APPVEYOR_BUILD_VERSION%.zip 66 | - path: ForgeToolGUI-%APPVEYOR_BUILD_VERSION%.zip -------------------------------------------------------------------------------- /files-in-DLC-packages.md: -------------------------------------------------------------------------------- 1 | DLC Packages on the PS4 have a `songs` directory, within which is a directory 2 | for each song in the package. There is no per-package songs.dta to describe 3 | every song in the package, they are just enumerated based on the folders in the 4 | `songs` folder. 5 | 6 | ``` 7 | Image0 Root 8 | |-- sce_sys 9 | | `-- param.sfo 10 | `-- songs 11 | `-- shortname 12 | |-- shortname.lipsync_ps4 13 | |-- shortname.mogg 14 | |-- shortname.mogg.dta 15 | |-- shortname.moggsong 16 | |-- shortname.png_ps4 17 | |-- shortname.rbmid_ps4 18 | |-- shortname.rbsong 19 | `-- shortname.songdta_ps4 20 | ``` 21 | 22 | Within each song's folder, there are 8 files. 23 | 24 | ## shortname.lipsync_ps4 25 | This file seems to contain the facial animation data. Its structure is unknown. 26 | 27 | ## shortname.mogg 28 | This file has the multitrack audio data for the song. 29 | 30 | ## shortname.mogg.dta 31 | This file has the audio mixing data for the song (this used to be in songs.dta) 32 | It is a plaintext-encoded DTA file. 33 | 34 | ## shortname.moggsong 35 | This file seems to tell the game what the .mogg and .rbmid files are named. It 36 | is a plaintext-encoded DTA file. 37 | 38 | ## shortname.png_ps4 39 | This file has the album artwork for the song. 40 | 41 | ## shortname.rbmid_ps4 42 | This file has the instrument authoring data (tempo, notes, lyrics, markup). 43 | See `010/ForgeMidi.bt` for an 010 Editor template describing the known data structures in this file. 44 | 45 | ## shortname.rbsong 46 | This file seems to contain the venue authoring data. 47 | 48 | ## shortname.songdta_ps4 49 | This file has the song metadata (song id, name, artist, year, length, etc). 50 | This information also used to be in the songs.dta file. 51 | See `010/songdta.bt` for an 010 Editor template describing the known data structures in this file. -------------------------------------------------------------------------------- /fuser_songfmt.md: -------------------------------------------------------------------------------- 1 | # FUSER Song Formats 2 | 3 | DLC Songs are stored in the path /Game/DLC/Songs 4 | 5 | Each song has its own directory with the song's shortname. 6 | This has the metadata and textures. 7 | In each subfolder, the instrument data is stored. 8 | 9 | ``` 10 | `-- shortname 11 | |-- Meta_shortname.[uasset|uexp] 12 | |-- T_ShortName_Large.[uasset|uexp] 13 | |-- T_ShortName_Small.[uasset|uexp] 14 | `-- shortname[bt|bs|ld|lp] 15 | `-- [...] 16 | ``` 17 | 18 | Most songs have 4 instruments: bt (beat), bs (bass), ld (lead), lp (loop). 19 | Beat is generally drums, bass is generally bass, lead is generally vocals, 20 | and loop is generally guitars/synths. 21 | 22 | ## Instrument 23 | 24 | Let's look at the directory structure for an instrument in detail. 25 | Each instrument has a "normal" variation and a "trans" variation (for transitioning between songs?) 26 | 27 | ``` 28 | `-- shortnamebt 29 | |-- Meta_shortnamebt.[uasset|uexp] 30 | |-- Meta_shortnamebt_trans.[uasset|uexp] 31 | |-- shortnamebt_midisong.[uasset|uexp] 32 | |-- shortnamebt_trans_midisong.[uasset|uexp] 33 | |-- midi 34 | | |-- ShortName_INST_Key_Tempo_mid.[uasset|uexp] 35 | | |-- ShortName_INST_Key2_Tempo_mid.[uasset|uexp] 36 | | |-- trans_ShortName_INST_Key_Tempo_mid.[uasset|uexp] 37 | | `-- trans_ShortName_INST_Key2_Tempo_mid.[uasset|uexp] 38 | `-- patches 39 | |-- ShortName_INST_Key_Tempo_fusion.[uasset|uexp] 40 | `-- trans_ShortName_INST_Key_Tempo_fusion.[uasset|uexp] 41 | ``` 42 | 43 | It looks like a lot but really it's kind of simple, and similar to 44 | other games with Harmonix's audio engine, just wrapped in Unreal formats 45 | rather than Ark and DTA files. 46 | 47 | ## Unreal Assets 48 | Aside from the meta assets, there's really only 3 kinds of asset, and 49 | they are pretty much just containers for Resource files. 50 | 51 | ### HmxMidiSongAsset 52 | Links Midi files and tracks with their corresponding Fusion patches. 53 | #### MidiMusicResource 54 | 55 | ### HmxMidiFileAsset 56 | Contains MIDI sequence data. 57 | #### MidiFileResource 58 | 59 | ### HmxFusionAsset 60 | Contains audio samples and Fusion patches (which contain key-maps for the samples, 61 | as well as optional effects for the audio engine to apply). 62 | #### MoggSampleResource 63 | #### FusionPatchResource 64 | --------------------------------------------------------------------------------