diff --git a/GenHub/GenHub.Core/Assets/Registries/Generals-1.08.csv b/GenHub/GenHub.Core/Assets/Registries/Generals-1.08.csv
new file mode 100644
index 000000000..ab1d6be0c
--- /dev/null
+++ b/GenHub/GenHub.Core/Assets/Registries/Generals-1.08.csv
@@ -0,0 +1,165 @@
+relativePath,size,md5,sha256,gameType,language,isRequired,metadata,downloadUrl
+00000000.016,153716,aebed2f8fa6f42b8c76929dfc8f90a00,ef61474057b21db70ae4356c3f22c088e583b3fd00c0b21da0c298949e8c3d62,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+00000000.256,308276,e8caed1e8eb521287cad86cbcb6edf5c,2d1f66e232557e775e636f8b8976422e330223e70a50f0535cc6b2e5ea03903f,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Audio.big,127940044,c4d80fc87dc13eacd9dbf2a981b194a3,d522df264149e3e27fd46f027548cc38bd60614a43666555219b7b1d45fb3bad,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+AudioEnglish.big,104744592,fc015ddbe16ac6b4d39a85f5612d7233,39d67dba96111178fcceefae2bedb2dc65b55b968741162c714b333c0b0f5f2e,Generals,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+BINKW32.DLL,358963,e58a20c9e7b342d5ca1f5ba75f1d1108,892a51c4056efcb22297a3b44a3491e3f5888f28b08ed1b17030f24acffedb44,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+BrowserEngine.dll,356352,df2ad4b243d68d74d6c35525d24c56ed,3653210a9e021be8c28144db1420320bc999a1c9ca13084a056f4b51ff03bd8a,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCAttack_S.ani,1666,548a3180cc4fadcdf669ea455e6e1921,8e1a57bd031bc8565775f78a162eb0b2f81444d464466e57d43838a116b38ab2,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/sccattack.ani,6398,6d899deaefe2228081f28073a9485ade,fa13992a0603390b0c8fa4200cfca7ee9eb0744cb55b87f49aebee55e5711216,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCAttMov_S.ani,1670,3bd9ee4b858660bbf45fa393dc1c04c5,b9c38e1c1a9294b3afcd029162a79bbbfc285770b5e1ff82998a604755ad7e19,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCAttMov.ani,5644,f06b765c53fda761aebe4f8b4045cd08,c6b2ac92c9d8d8a15bd411dc3e3cf6e276b081cef20035ca82647f0168085714,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCCashHack.ani,7896,19efb6c96fb3b30adfcf6d8ad6fa7981,b41565b664457a00d87efbd24b5681155967620df5ae64a54d28aa9780279385,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCEnter_S.ani,2446,f48af1584768ade129a8a6c4f049b452,858479b74fafd998f1496b69edf2e44245a0c2f8bc8dc6c7ecb9b90d62c7370f,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCEnter.ani,2440,20f9bc7c814947b4cec1349c4bb8bfb7,037ab0691bebf9b4a985d194d0619e9fefa0330a07d4c85bb5047c26d10c5787,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCExit.ani,2436,acf7171dfbe1f72d64c9c08a6a190469,590d419191ec781d97fa7e375498eb99270eeccf8d9ee75bb5c3c67e9dcb620a,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCFriendly_S.ani,6358,5578eda24455b8416ac223d29b4ce475,488b83907f2b12c35d1e3d688a3564acd309d4f2afa1185cade157aee40fc238,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCFriendly.ani,6344,9f7a4f16f5cafbf36e865285ec360638,f7b424afb5575067fffe02cf1afdeb53f62f81ee85a12d581e2a76b5ab418652,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCGuard.ani,1616,1b24328b2926ca7bede732be68b4a4c1,fa8c51b348106c3ede587fc0a13deb63aa54a5db38ce8f582f03b761d57f6150,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCHeal.ani,2478,d12a7573375158375a33131339598d51,32997e09311a684687ab8a339d63d3f075eab98def7e8e0c47b4519b9763a695,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCHostile_S.ani,1674,aa715bed9e28d9dd5a13a56b8fec2e90,73f2fb4c8871afbaa49d6a5cbd34c53e4f29cead68f571d7aab5a5b406c893ea,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCHostile.ani,3270,bf03d453be48a42b218c084cbe19e578,78133602f6b1eceb6257895e9eaeadeb07ddbc9e969f6ac69b6bcbe16b5244ed,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCHostile2.ani,8634,f46b0d70e7c29ddb6b24a14dc053fb95,69a82c2f23d5608f039ce47cde2fe955ec1e4dc9971082ef2c24035f04d0738c,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCHostile3.ani,25792,92918392b8969da0d4562b5636df397c,b86c5be45dbf421172de0d12f4f1b237ce1844ad67ce614620490d60a5cbc5b5,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCKnifeAttack.ani,3966,eb68b3991a575c6ee2247c66b9ff01da,922ba292078be4b9f697450bcf68e23b6bcd9344f433b64641c856f15cc040a1,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCMove_S.ani,1664,3b33efc0b64e1b35f07afb8e098d5cf8,0a60c8f9b6da230767f10275ca97a57347d65a39468f953db122e0f93b07793c,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/sccmove.ani,5638,728cd67cd5a9c8c133f4a05d0a7424ea,edcc9a98a24e0e21bd89e45db2fcdf04c5d3ac359616db96ea6c33407df04c82,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCNoAction_S.ani,892,d9612607ce26ba18da3c98187ba8f60a,2cfa77a51eb15ab54359c13c27b926d7b0f948f2c91d4a1928d139d70ceacb02,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCNoAction.ani,886,cc4e179474a670631835d81725830882,116f51bd9073187b7b2fba0ce08bbe3417cd5daacec7b2af6b78a60afd07d6ff,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCNoBomb.ani,886,48237559686d7aaf721d2cb5109efa67,03e4cd887ab8283a8594b4ae54c0f73b159dce2ebf2df069923531de89be0d7a,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCNoEntry_S.ani,1682,121ffb04e7785ef03dba6de7930d5899,2b76be67e3d75d0cc428efb1563c08c0a6f717d95ffdeb1696c70ba446c92c09,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCNoEntry.ani,1676,bf2c1b618dff02c56bed38e7c69b19ba,ab8fed4bf8bf5bf6ff8e79a40bc89afe9c6bc7c13e2b1f5577506bb48d017803,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCNoKnife.ani,886,973eebf5e3893f3e1ce04ebab5f2b5eb,49d8a05655d9af4bf5c6ab3c7f15e2c86dc1037cc2c016c093d22eada35d75f0,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCOutrange.ani,16460,f18891ca6f6be7f2bc8feeeda196ac80,8539c4a441775dd7ccf5c1faf0cb79fdd5fb6c6797f7d7d3ca98768348429741,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCPlace.ani,1680,dc039f3410c790f3666ac7dc611ef801,a149ac8f358c82b6fd89e2e3fef32b6b1d11adcfd846c3560fd388d75c166e0b,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCPlaceBeacon.ani,7078,82c144ddda013cf670954944a79b2196,d32b4336cbe21101cb872fe9e650f0cad9c77f86fdc26286484448c3fc00b8a6,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/sccpointer.ani,884,54199240e4efefa204418c6c973221b2,ad3829b3e6262f8881ad2a3ddc90e7add848ca5b6bb8fda76facd6ea3a98df00,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCRallyPnt_S.ani,1668,1b3d007599e9307b758c28748321c10b,cfb1cd7baed30b94d5b47a659296738376cafeb4dc492dda6b2c9e5de534d1b9,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCRallyPnt.ani,4064,cec0a2074598b34bebe51d14f7783d31,9d15def280ae76cc3aff9f5ae61547f035b9841ed42e647d13d4cae596a2065d,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCRemoteChg.ani,9412,f8a2450fc0f82f28f584af001dbfbc30,d60b49ac06ae56b56fc03e6cbbf6d1bdbf0bc66eaacec621d7cd0caffd300d6d,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCRepair.ani,5640,a7a77357ec53203c1af9164acf6abff7,baaaa22fb2b331660321833659967d8d057cb50ab31cd2b927459ff0c548ecb6,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCResumeC.ani,17970,a5941a513b20dc2165e7ed9fd2cfdf12,cea73ea5cf0dc34d8299d42ec965eaf2c98a7a501ab1bd5599407f3af91f7e85,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/sccscroll0.ani,888,69a1fbc2773ec932f6a5a7fb37f7f12f,0560079fe4782ac89942eb45e94de7011eb1f4ebb39230890e4917d612bc5d01,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/sccscroll1.ani,888,6aacbae0874a6c9aa335e35947a8ebd0,c48c8691866637e0fa9fc98bb40e2d8240370b4cafd7b767c1595b58789eb06c,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/sccscroll2.ani,888,43f6019d566d0aa8759a42d0a5bec5cd,e62fe7da074898004ee7ef81626830fa94332b3a9d4fe8987e948cea0e2ac1f9,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/sccscroll3.ani,888,6f2e50e14ff2776eae76d961a4a5bfc7,f9c2a000c946a6f6bb420d9671c9ba21a9fc14821a5692a5f2e5b4755fdebb8d,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCScroll4.ani,888,a169748d79da826c10d7e5149c7fd41e,ec77136c9768ca3c50216ae92269aa5fae4f29197a13a355c6f92fcf53ecb279,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCScroll5.ani,888,f17a6fc61a7b944bf5eed66d8242d5ca,2ccb9140e23faf6cc98560e545a54b83efce54dd1bfb01ea84119732d0f04433,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCScroll6.ani,888,33618b4ed32fcec63df6fea44b98bac8,46186f3e8e9243edda6e0baf00e0a0fcaaf5e5d9b4d4531bf13393fdda67acc9,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCScroll7.ani,888,eaae99658bb1a5d14ea89ae89aaf32e0,00904ceec2de7e828c1a5fbe224b1ebf6cb19543f3d24381b64d4329a510e46b,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCSDIUplink.ani,16460,1443e1a4d8c5665e6a88288e1b03e7b0,11de6fad707df80023f91c011569de86705b2770cd1df5bb0394481bf5794fb5,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCSelect.ani,6388,21d58f3402a7f33ca5422fc865d2d602,42840130f31fa90c880ba2855fecff0c4a1cd3e903e6eea862ab95b8dc876427,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCSell.ani,1672,21f1c178ab89de9b364cc8709ad0dce8,3a9dcadef511ad9a2dd1ca9b31eac33a7aa58b652554cd1d18c628c87fab1a4d,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCSniper.ani,15636,29c81451b838097f4baed9b00c3f2873,258ce022aa05087bd0772484d3aceff49b9beb532cd8a8d524f9eb97be57de80,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCSpyDrone.ani,1632,b30aaf4f9ef73831165b1e280050da14,6a4302a4a897d6ded38ee0216bc45975406d40a73270eed12d13cb73c868ba34,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCStop.ani,882,28eb1d384c4ff53a0b44352fd62455b3,ebd5c2968465f5e3898d38d3a737f2bda387be6be6ee246712a5081d703150a1,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCTimedChg.ani,8634,c84d13defbb211c95a8015cfc743320c,727c0863496e777929a86425fcc849e7041b77e3209151f566fe233bf8a2bce6,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCTNTAttack.ani,4744,23569a40a3dbe637a6fdd839d25f58ec,e5c03a381c120e26d04c5c7495c88bf43224f51d89e9ca9d7dea84a5903285fd,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCWaypoint_S.ani,1682,b7566c5d6d3bc8e604f6c07b9dedf4ff,1c585e4c98e05ad6fa916f6d496dcac8673ee651a8cd6743319850e3d14bd4bc,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Cursors/SCCWaypoint.ani,1676,c76b8d01c707588e02aea0c9ae052c54,6335fdebaa2ce98e17fec156d75ff9b089ab0f42b70170e5f18383abc73f1e88,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/english/Movies/EA_LOGO.bik,1480980,adbd4c3a5abce41bb190430acb4ff29e,f8beb9cbc902cdd90f94563df1246d4df5769f8335a2dcc1f85916d446e3a8c0,Generals,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/english/Movies/EA_LOGO640.bik,1480980,adbd4c3a5abce41bb190430acb4ff29e,f8beb9cbc902cdd90f94563df1246d4df5769f8335a2dcc1f85916d446e3a8c0,Generals,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/english/Movies/sizzle_review.bik,19887184,a5da392e70910f56ffa563e8720296d6,9bed2259d7088cc4cfa437e3b0ac54449a2069956e3f033ca37d0c2c88274869,Generals,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/english/Movies/sizzle_review640.bik,15345648,1b9993508acd143a86d8e0682db4ca99,0709f9a04903ae915eed68e7bf883b51c0fa22b50096ba9c334e4e0b113087a4,Generals,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/CHINA_end.bik,21096880,131d68acde339d1f204de92733c2d5ae,63553a1c27e4677e381b222fd711a71f72e8289ceeaf0ac0ac07a90a7b4a883a,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/CHINA_end640.bik,16030972,e9356c5ba8b2f15c8eb05a300851fb50,886ad6a33edff45b0f41e3060904dd4e4df7bc9bbf08933fc86b34a1d47183f0,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/China01_Final_00s.bik,9462664,d2ad7d6128e139113c364d26da6f85e0,a183045298d219626b3681adeb77a26b74ca0ecf7ce3daf4a7aa41a11178e237,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/China02_Final_00s.bik,9416184,eabc0320e25a3214936fe7a664a5ac28,2332508999e8ba62c531d3763c9b2cc5627fa059f7bb504b419f089ef2d8dfff,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/China03_Final_00s.bik,9466800,4983a2ca739a4869f60346855a5e4b85,f6554d863a1e47d374e490161c39200df6786478674a9bb4c4a8542362b3a259,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/China04_Final_00s.bik,9304904,989929b5f0f10a943162e6d7659382cd,b28c8a1f63a5e01df3189d6b537fab52cf7a3ff209d845256f3b9fef21d2315a,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/China05_Final_00s.bik,9434164,e90bb8585ec81807290394c39ab66dca,7c520491d39aa3e50a895e8e34bace4dbdf2cd424b507461b3f1959e3c2224e8,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/China06_Final_00s.bik,9484312,a298248160b6a42b7fc4cfa8e2615dad,e232468337b3573151ffa953c5e357f8fc144951114a8db1441dec41414d3c5a,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/China07_Final_00s.bik,9480224,b1dc505e4e57322c651d5ec8071dbf12,aca8ce932d965cd7514a3d5ec50c6a5bf0673409ab316d6b6a429d1f09e6f8b2,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA_end.bik,18464604,705f46b48604eb83418f51f233d3fa5c,141553b0ff8f27dccd6a36008bf4cf547117da09e3b13de29e56b55a7f3b5bf3,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA_end640.bik,14030116,28bc6598a0816b49270dc3374e2fda7b,266765c6f199a662c3bcb4b9c1bbb6564d25766b77b164c4bc3d014e593375f7,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA01_Final_00s.bik,9380716,9c8328840cab3d83a10cfd9b5f2b2d0d,985d0e9c3e1d2f1e37fc3842827763f584955ca1e929b308f78a92c9d074faf7,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA02_Final_00s.bik,9434596,6fe1a7e3518f9b4d9d4d63cad0b1c243,bf95e38a1d1bc15ef9457b461f80d840a9ad019d45ade6dc2a3070c4a6d24655,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA03_Final_00s.bik,9366612,947d5947b7657072219e9b07137eb678,f9ad9839558a23d34e56a34167773ab4e00ffaf5bcb4b34017408f13449a3fb8,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA04_Final_00s.bik,9479028,ce76409306e2450955170ac4f6cc9ad4,60a3713905f240a645aaa9d28f95141876afd2d2c13db66a3ac6449a0c1aef13,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA05_Final_00s.bik,9476400,438e155a4fdf8b6a337e806eaae946b6,6cb3a3a63b0970b72151d88c8635713bb6c279747fdc829647a98d3b869d9655,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA06_Final_00s.bik,9380596,9b2c80a2d661edc49cabdb2f9f34376f,2a2574025f5dc703026d94f5fb373721cd9440c71ae0a5ac6930a4a3151c36e5,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA07_Final_00s.bik,9314264,9e6f4ca61154a9ce1bf654e8abeec032,613de8f96bca17a350e6f8a2abd902e6d8bfcd6b1f0ceac299baf086d90922aa,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/GLA08_Final_00s.bik,9454488,9c20c612eaccc90324b01b1263b533d9,25ede4b757b4aadae00dcd7fd3e7632b553dc9443894be0363e3f23ed49638be,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/Training_Final_00s.bik,9497308,c97f7aea6379f1abb2fa1352dda02cbf,5a46ab6aa21bb760faf3f6d8172d7ab75c009ad9adc95e01c40ebd81d76aeeb9,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/USA_end.bik,20857148,8b13a3c745df210b2df2147750922414,e2149c34fca288f8f86b43466a4a6aa8cb33a97d4ca8075628d77191c08051cf,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/USA_end640.bik,15464260,49b90621d514e06334573c87fafa9e10,f39bc09b30d23ab47b52fa0c97d07375bcfcc75a5097cd45a23c4f1262891682,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/USA01_Final_00s.bik,9440016,bc0908dbfd543f6f816146f4d4794e2e,416f0acdf3a36b4009389e55e8a27001df336bf40921413892f40e923bd78cf4,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/USA02_Final_00s.bik,9484920,99f59a5108b72b46afd223d3bd3b1439,ce76cc66cbdf911e1e6283673b4c26b38196ee0c9e05274f26e5c92f1e363456,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/USA03_Final_00s.bik,9358748,5367c7faac2a8639cb5101f969c82442,51a7f8c59cb3f4cee9c2af1e670549907eb81e8806a5023db20d6a3c211f8a46,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/USA04_Final_00s.bik,9416664,5f5ecb41dcaeb833dbdc82790ee4f7cf,3b183b059e43c1d1184f7206d0d542d94ae58a736035e12cd943c470038ca5c2,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/USA06_Final_00s.bik,9258892,05880cd511c0658537edb5238e7e6e31,45fdd0345c8461c6ae8eae614e540ccd37b3ce6a2345cc9a7ae0e0dea760b0d9,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/USA07_Final_00s.bik,9466124,31977bea9ee3ebd1c5fa0f5d612074da,ddbbe1be231a7ffdaa3235981bb1936e4c1c5dcd08284f0b262095010970806e,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Movies/USA08_Final_00s.bik,9420052,20c9e77027b4a0e245bcbebaac11fff3,59e6ede70c0f8c0f8557d063356f6ccc61cbc829adb1f619ce9aaec0d4d98eb6,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Scripts/MultiplayerScripts.scb,6915,1c9afbc46dad13f55c317a162255d6ae,86c6a3b82a4188ba3c7abf1388f9d5ee503f06634466be095256a2aefb4ae06b,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/Scripts/SkirmishScripts.scb,467525,c8e1e11e697da5adf6abdcdf1290c578,9eedadc0d8d9deb241d56a3db148720029e119c0cfc03a3ef74cade9da3106a5,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust00.tga,16428,9f2d9b6133b6e4d78b409ca8303cab85,d4b2fa073a52734658fed6b780de379fe866b956aec8c95525457232e7d1b636,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust01.tga,16428,5b257180aef56e05b007be93eb64e527,67116bb0c18491cf403d5706e4d6c005d43ea77e034cb889d60465501d504156,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust02.tga,16428,6802068b10f1f8ac64646d54124b7e96,2718869ffb24f789a912b5fe0dcd94067db1a06aa144e1a1ac0d81cd6b5c8fbe,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust03.tga,16428,af760ffba0eff9fe49ace5e8e82de122,6e7c3733f937534800dfc1ac4632c34dd4c873d6d9e55f13dcedf738462d470d,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust04.tga,16428,3068c2e091b0c52c93b0d83d18b04ea3,7123520dc1d22b0279e24f50f4dd5c62d54b2185883acadfd723e19eda7d5c0e,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust05.tga,16428,4e6a03367c3282c939f1002f826c6549,7a44d215ad75268319abcef7cd2ac89dfc68cf7a4ff090cb354db287971764a4,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust06.tga,16428,daefb72301f19803058795c9d4eeb833,c1fba0eef7d968bdd1866fdc13495e0c3ef5f5171f6a6c70f4e73e7097dc6d52,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust07.tga,16428,8b8ccacbcdf9cdef9481cd9428760c44,b84e11ade33a67bce27c7c2dfcbcc569045e41609332f6f62ef12c675db7baa8,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust08.tga,16428,0def5b045f0e0ddabf002d68efb8d347,a1e37542342ecb0d7c482512b3f98195b1f17706a0f1579221fa78f3644d64ba,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust09.tga,16428,f89c4c80fb4d69d23c0755787d18f12b,48608dff4b30b2b25a5fa0c450f744c56e37f007f01b351018c82df7e7198ea4,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust10.tga,16428,44baf98408c4f629a79495ca88669bec,c2e1c7d5594681269641e08442334eee3d0da26b7c385036427dd33aa56ecedb,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust11.tga,16428,db0ad392bd51d1fe89ee050e7e9a430b,1bb9d5b5edd128e315bba2f24ed00be12e4df7ce8ca37f7c7d84ef666e8ac72c,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust12.tga,16428,0416996a4b5ba4c7a60f0456cf1c5a76,dfa05cbcf7d32b8057bf57fa0c7b05e24839205845e66ce06d612130a3f31376,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust13.tga,16428,9016d60dfcd7fa826edd07464a1cc1cb,5baa929913b5ce1a1cbec97f6cd9090dfa3d78ebc0e000d1ee6b8e0f45591255,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust14.tga,16428,deb81cd26d0d67043fd6338c691338f3,29c941490d668adb8cff8927ca5e6694847dffc4198739e88956356e6f2a3094,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust15.tga,16428,d10b07457cc172d05b8508b427e24187,f87addd5360edc1bd5822bcc2818dda27eb727054783b346ddbea26b78e82ff2,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust16.tga,16428,d75dfff45be2c47b5f3c115f9d78bfe2,90dee0bb7198e675772d81e30327531f8bbb535d299b8dd613466c44cd7235f7,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust17.tga,16428,bc83a41cb453dfe12d378ecb8c799d49,f053a13e73c8ce938938feea573141a0e22b50eeaec7f101f4636004f8ba554f,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust18.tga,16428,040c0932fd2705ea0a37e360c57b1cec,c26f3a040e1284a99e06b18953df40efbeabe1e0947783c797b363364ebb99f1,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust19.tga,16428,85e1680cea2bb20578e4f1aa19dfa67e,04915f82f986d72f6c999583b957bb735ba221890bec61aeeb1a16abb1d4352e,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust20.tga,16428,d5ac8f99b83fc92ba4e2f356c6816c21,1a27a58af21b14b8da96728ba62725626085f6b50399d99ff990fd2bcde3e725,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust21.tga,16428,ab1231ec264d2b93cfca0cadc52554f4,8c0e3d19126da509f7cdea23e98142b489479019095ae7cfc00024921523ad5f,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust22.tga,16428,f349cba69868d4da452fa37ec21f860e,fbdbef23579e743b027ce6177e3e15edd3df2794ae49bfc6144c81b777d395a0,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust23.tga,16428,79cb087169f45f2d2b121762b0175b5e,478d7d5e09a60624104647106b05c02d6831a68a7aa14590359d3d00fa02accc,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust24.tga,16428,64e7f0a3fafb344ff8772d2b47aabdb2,e19606c3677d773c9f39681bc0a03c49376292d7f0bc5ce340ccab19c1f09166,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust25.tga,16428,255ca010b8657f14c85bc72028472bf6,b29b4df4052bfde94ce82617f647c02e3864c1f730e9d91f880bb43bbb325f5e,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust26.tga,16428,e79bc7f009b31a48137b5e59fb704ffd,4fd4406232083d148b97cb1de257f5674023c5ecb92b4418291f3ab6667a8c69,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust27.tga,16428,4f7c2763c2f1b4ea4ef99c2bf6496404,becbb96d56fb5a22bc809c2100ee7beecab9470593feec4bf82354d81aeccbc5,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust28.tga,16428,9f399179b32616aebff3a1638973de15,2f0f5c933ff40b9c959c484a317a2e0568392fb2347e76bdbaac2e850e1de642,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust29.tga,16428,cc2998d2d0044e1762d794ed38930cc2,7437ed2c75b3f791372ed914c692e0cb36387d4586089a818c9f33be38a41c32,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust30.tga,16428,88da7064ccdee97387ee50c6c678ce7b,11a82a9b483dcf824e8e86997decc89286e6cc5af71624c1186a186aa5721a35,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Data/WaterPlane/caust31.tga,16428,418b9f65207e4d7321c5e87f98f3084d,6b49361ac4c0709a3dbf15e3ec537aa616e0c324bfa6bc490d28cee5c3fa7cd6,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+dbghelp.dll,163088,13fbc2e8b37ddf28181dd6d8081c2b8e,a29056a9810ff08c708505f1ac20d0263d5d894a223696e20217c0e9d132bf84,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+DrvMgt.dll,23552,cfac34e9b742612844204f42fe76baa4,1366302382f84813e3f6d097e8e401b17ac3887ca7bd9f71827763aa4fc23916,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+English.big,2774948,a12512c67dac5bcc81a2be26461001b5,218440f15bd2f718c6897f631eeacb8a42378679bcc0a6aa8a9cf83d0798f543,Generals,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+game.dat,5701632,a7cc739a1657edd542312a4db719b7ab,a2c697ba74f1ab224a72e0f175b0a8e924a5bf15b4d3c0d1a8a312e68c04b8dd,Generals,All,True,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Generals.dat,56,55645a20899cb8cb37449f3f989e6127,ef6a721d84a7cac5afaf7bfe36402ef07764560215f68b45d7e1f5d8c7ef5aab,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+generals.exe,57392,fd658a62512db722448f4924259d0a0a,e253361f457f2ec3290ccf4088aa5c4022fc4772a769fff5fb2fa8b9e5df842d,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Generals.ico,21630,29f24ae15cc2c4c1deff5a9904000637,e2f762b9279981c2021e891bd11677caf447e11ca3d039a5e0b9173418aceac6,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+generals.lcf,144,2438db32c55a1551d1daba2e9adc0f40,49ac9bf59939df878d2ad3a130bb2ae6ea6f3c6ada56d0bd35d5aa891853de0f,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+gensec.big,787464,ac9e5838f587d75f40b0c064cceeef05,99c6f200392488aeace0ecf7a0833c9426f068b698b5fb3e70ecd1b0d410913f,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+INI.big,7607479,ecdd6e48060398b207f70a6d40917e97,bff8d621088b25fd8b041c8acca020a020fabc66f972ab2bd131fc67d905a72c,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Install_Final.bmp,1440056,ff426b5b7fb72dcb3e0d38846f7ac128,05fe660e4794ce97752e15074ad61f0ef5508484a776be97bd1f0d46f4786baa,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+langdata.dat,25398,2e7d20210b21b5fe40e1bb44af63c1c2,b964985085af30ff170d25cdbf97a000db1f52d99e9e4cda293a7761cc5a2616,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+launcher.bmp,39608,4ad9b62f4ab1fababf35c616b6e7285f,4d61a1b74377b4a42353d17b10b72a1757f6da4efae3b79a355f38a500051922,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Launcher.txt,22690,9df9e806744c78e0d860c2cc0e6ec1f6,0065e33171eac7670d506b1c2b3c1bfdb5c4ab121fb3b5e465d3481376d37f13,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+maps.big,23554152,728bf83395aa64dce52d8d27b5f88a30,8a241df0c87ea47f6ad992984ea73dabf4f2ac5f96f8924f30bb0bf5fc4ac4d1,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+MSS/mssa3d.m3d,83456,e089ce52b0617a6530069f22e0bdba2a,41ccd5e30475ef7b40e68aa8c5c0ce18e804179fcaa77ba42e6ffa4f438d9a24,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+MSS/mssds3d.m3d,70656,85267776d45dbf5475c7d9882f08117c,a2926f4e2a094a99508c05adaf86c5710ad3cff8bbcf247821feb0e6977f547c,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+MSS/mssdsp.flt,93696,cb71b1791009eca618e9b1ad4baa4fa9,e035db7c2a4a2378156f096a1450faec425fd8b89bffb886f68c655480bfff52,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+MSS/mssdx7.m3d,80896,2727e2671482a55b2f1f16aa88d2780f,e6c928729db1d7c62d684962f4ecfd6bb039504897af53b72b2a66d32f1bc6b0,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+MSS/msseax.m3d,103424,788bd950efe89fa5166292bd6729fa62,62e0e34435b9705eedc73660e64564138d8276dcf7b08dfbaac05c592b67e6d3,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+MSS/mssmp3.asi,125952,189576dfe55af3b70db7e3e2312cd0fd,121be91fd21c80396cb5cc46c245d9b3f67a26f8cec4d0ebd03f17cd13508b0d,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+MSS/mssrsx.m3d,354816,7fae15b559eb91f491a5f75cfa103cd4,f983c72977f19fb7bdfeaec4db1ee1e169a18cc58499452a5bab9fa2447f68ce,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+MSS/msssoft.m3d,67072,bdc9ad58ade17dbd939522eee447416f,5dcbf188c30ae1ac6a3d5b7fac4a25e831c9a495683b044f5141c4bdbc83f607,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+MSS/mssvoice.asi,197120,3d5342edebe722748ace78c930f4d8a5,72bac1b0d0d3bfcc235a74c06c3fc62043f197a2bd8ebcf8a89652d78f23157b,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+mss32.dll,349696,6400e224b8b44ece59a992e6d8233719,441b290e7dc6334eb5023cd9b7937739298fdd66c104d4c96e5edcf642ae912d,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Music.big,158818808,5b65772e6097d206c0fe326452624637,c1e162b8a7575d98d9e20c7ef582e3edc96b35e3d071d821b01c5426d3c55450,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+P2XDLL.DLL,519168,f8e5e9d283c5f7ca528777ddbb5d6e48,15dad960f53ba3238564a10678b993ddbe9964b4c5033d905337e0bfb79039d2,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Patch.big,1462590,7ab5492ae04a8657feb4ec83aae80280,28dc194412f96dc1f66412430cf74f2d89ad0cdabf70d2c8d1179d8e51743494,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+patchget.dat,122880,13282bada64a35b59f9281ab73932ca0,ab1a8576ce19b00bad8988212d54a9f3da1b30c5ceed38807fed315670b3a252,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+patchw32.dll,185344,6ec517e866e476401755281837295579,0ec6e25234ad74489eb1890d4de57bb6140bb8196bdc4a5dcac90dd9d16eb2dd,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+SECDRV.SYS,12464,890cada2ab7acf53a5f9cce7515522a2,78f1de7b1f3fbae009fe818d5bf3c4e0f109c4c8dff87a385921575c133b4b25,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+shaders.big,1200,b587ef873d3f5cd475b156d4236c1e5e,b982d3a99c8fae32a6d07ab0994274c1754a0fb08f69646f96d698b3986fe2a5,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Speech.big,13479230,0866ad595a5098345653097dca1afda6,5106e92a91b1159fd861d5e4475beb6e7e05b0d5ac43d65520e02d89e39e33d4,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+SpeechEnglish.big,269104188,ead55e76d6b86944aed95b94f631c8d6,b48ede709de86437a9cff23bd27586a03f86e2b009e0773816af48496bb10ae2,Generals,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Terrain.big,48342856,9fa5a8c692d6122a20032f3a59a70d2c,4c203b31ccbf7f4a41ca0288d3e782a356a0f75f3315c05a83d7364e19f86f71,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Textures.big,333031108,90a934df85a1d628fc79f440068ce0d5,1303e92c57c9cf4e24bf85b342bd58799924565796f3a4aef65dd4a9967aad5b,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+W3D.big,184549391,3e1ddef647cf5b590d2290f797401b15,87727b698089cdc32bc378b1746bf315c2a920d5df33cace0d7085e027b67d36,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+Window.big,7962700,6681234d7a863f5f2841efe1b0e3b773,344f830ce00eabc247524b5e8b1305f10fe8c742a667e238d3a9c1c63fbe6479,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
+WorldBuilder.exe,6885376,b63f9648c5373b0868611931a88a7721,1b5c2c634c5b2f1c1ec53d49ae8a35da5d397c4566c42b406920cada0b7368d9,Generals,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv
diff --git a/GenHub/GenHub.Core/Assets/Registries/ZeroHour-1.04.csv b/GenHub/GenHub.Core/Assets/Registries/ZeroHour-1.04.csv
new file mode 100644
index 000000000..20d71ca5e
--- /dev/null
+++ b/GenHub/GenHub.Core/Assets/Registries/ZeroHour-1.04.csv
@@ -0,0 +1,176 @@
+relativePath,size,md5,sha256,gameType,language,isRequired,metadata,downloadUrl
+00000000.016,153720,614156f3dc3ada5d21a5f0cb60ce5bf1,a85138edc09cd8c51b337674999b9cea16ef1a25259c7f4f7810edb3add55a0b,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+00000000.256,308276,5d8160359b90e0dd13d191e87fd84010,6b4db9327a858fd6a8ee77362b7fc0b6457279a57d942d5143fb7a0230e0b7e3,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+AudioEnglishZH.big,58326522,5e6d3b22c4dc45b421417feb21a89427,85109b5cb4a5ef75c5fdd1fe1a66957d951f98f028e10e3729dddc2309402914,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+AudioZH.big,23965542,03edcec3e410dd2e6bfb99f92087ad39,6fbd05e43491bfd5f56c9250c3f9c30fc3061cd5867cb912c46486fc78f5e8c7,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+BINKW32.DLL,358963,e58a20c9e7b342d5ca1f5ba75f1d1108,892a51c4056efcb22297a3b44a3491e3f5888f28b08ed1b17030f24acffedb44,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCAttack_S.ani,1666,548a3180cc4fadcdf669ea455e6e1921,8e1a57bd031bc8565775f78a162eb0b2f81444d464466e57d43838a116b38ab2,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/sccattack.ani,6398,6d899deaefe2228081f28073a9485ade,fa13992a0603390b0c8fa4200cfca7ee9eb0744cb55b87f49aebee55e5711216,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCAttMov_S.ani,1670,3bd9ee4b858660bbf45fa393dc1c04c5,b9c38e1c1a9294b3afcd029162a79bbbfc285770b5e1ff82998a604755ad7e19,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCAttMov.ani,5644,f06b765c53fda761aebe4f8b4045cd08,c6b2ac92c9d8d8a15bd411dc3e3cf6e276b081cef20035ca82647f0168085714,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCCashHack.ani,7896,19efb6c96fb3b30adfcf6d8ad6fa7981,b41565b664457a00d87efbd24b5681155967620df5ae64a54d28aa9780279385,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCEnter_S.ani,2446,f48af1584768ade129a8a6c4f049b452,858479b74fafd998f1496b69edf2e44245a0c2f8bc8dc6c7ecb9b90d62c7370f,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCEnter.ani,2440,20f9bc7c814947b4cec1349c4bb8bfb7,037ab0691bebf9b4a985d194d0619e9fefa0330a07d4c85bb5047c26d10c5787,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCExit.ani,2436,acf7171dfbe1f72d64c9c08a6a190469,590d419191ec781d97fa7e375498eb99270eeccf8d9ee75bb5c3c67e9dcb620a,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCFriendly_S.ani,6358,5578eda24455b8416ac223d29b4ce475,488b83907f2b12c35d1e3d688a3564acd309d4f2afa1185cade157aee40fc238,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCFriendly.ani,6344,9f7a4f16f5cafbf36e865285ec360638,f7b424afb5575067fffe02cf1afdeb53f62f81ee85a12d581e2a76b5ab418652,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCGuard.ani,1616,1b24328b2926ca7bede732be68b4a4c1,fa8c51b348106c3ede587fc0a13deb63aa54a5db38ce8f582f03b761d57f6150,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCHeal.ani,2478,d12a7573375158375a33131339598d51,32997e09311a684687ab8a339d63d3f075eab98def7e8e0c47b4519b9763a695,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCHostile_S.ani,1674,aa715bed9e28d9dd5a13a56b8fec2e90,73f2fb4c8871afbaa49d6a5cbd34c53e4f29cead68f571d7aab5a5b406c893ea,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCHostile.ani,3270,bf03d453be48a42b218c084cbe19e578,78133602f6b1eceb6257895e9eaeadeb07ddbc9e969f6ac69b6bcbe16b5244ed,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCHostile2.ani,8634,f46b0d70e7c29ddb6b24a14dc053fb95,69a82c2f23d5608f039ce47cde2fe955ec1e4dc9971082ef2c24035f04d0738c,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCHostile3.ani,25792,92918392b8969da0d4562b5636df397c,b86c5be45dbf421172de0d12f4f1b237ce1844ad67ce614620490d60a5cbc5b5,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCKnifeAttack.ani,3966,eb68b3991a575c6ee2247c66b9ff01da,922ba292078be4b9f697450bcf68e23b6bcd9344f433b64641c856f15cc040a1,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCMove_S.ani,1664,3b33efc0b64e1b35f07afb8e098d5cf8,0a60c8f9b6da230767f10275ca97a57347d65a39468f953db122e0f93b07793c,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/sccmove.ani,5638,728cd67cd5a9c8c133f4a05d0a7424ea,edcc9a98a24e0e21bd89e45db2fcdf04c5d3ac359616db96ea6c33407df04c82,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCNoAction_S.ani,892,d9612607ce26ba18da3c98187ba8f60a,2cfa77a51eb15ab54359c13c27b926d7b0f948f2c91d4a1928d139d70ceacb02,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCNoAction.ani,886,cc4e179474a670631835d81725830882,116f51bd9073187b7b2fba0ce08bbe3417cd5daacec7b2af6b78a60afd07d6ff,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCNoBomb.ani,886,48237559686d7aaf721d2cb5109efa67,03e4cd887ab8283a8594b4ae54c0f73b159dce2ebf2df069923531de89be0d7a,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCNoEntry_S.ani,1682,121ffb04e7785ef03dba6de7930d5899,2b76be67e3d75d0cc428efb1563c08c0a6f717d95ffdeb1696c70ba446c92c09,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCNoEntry.ani,1676,bf2c1b618dff02c56bed38e7c69b19ba,ab8fed4bf8bf5bf6ff8e79a40bc89afe9c6bc7c13e2b1f5577506bb48d017803,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCNoKnife.ani,886,973eebf5e3893f3e1ce04ebab5f2b5eb,49d8a05655d9af4bf5c6ab3c7f15e2c86dc1037cc2c016c093d22eada35d75f0,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCOutrange.ani,16460,f18891ca6f6be7f2bc8feeeda196ac80,8539c4a441775dd7ccf5c1faf0cb79fdd5fb6c6797f7d7d3ca98768348429741,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCPlace.ani,1680,dc039f3410c790f3666ac7dc611ef801,a149ac8f358c82b6fd89e2e3fef32b6b1d11adcfd846c3560fd388d75c166e0b,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCPlaceBeacon.ani,7078,82c144ddda013cf670954944a79b2196,d32b4336cbe21101cb872fe9e650f0cad9c77f86fdc26286484448c3fc00b8a6,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/sccpointer.ani,884,54199240e4efefa204418c6c973221b2,ad3829b3e6262f8881ad2a3ddc90e7add848ca5b6bb8fda76facd6ea3a98df00,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCRallyPnt_S.ani,1668,1b3d007599e9307b758c28748321c10b,cfb1cd7baed30b94d5b47a659296738376cafeb4dc492dda6b2c9e5de534d1b9,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCRallyPnt.ani,4064,cec0a2074598b34bebe51d14f7783d31,9d15def280ae76cc3aff9f5ae61547f035b9841ed42e647d13d4cae596a2065d,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCRemoteChg.ani,9412,f8a2450fc0f82f28f584af001dbfbc30,d60b49ac06ae56b56fc03e6cbbf6d1bdbf0bc66eaacec621d7cd0caffd300d6d,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCRepair.ani,5640,a7a77357ec53203c1af9164acf6abff7,baaaa22fb2b331660321833659967d8d057cb50ab31cd2b927459ff0c548ecb6,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCResumeC.ani,17970,a5941a513b20dc2165e7ed9fd2cfdf12,cea73ea5cf0dc34d8299d42ec965eaf2c98a7a501ab1bd5599407f3af91f7e85,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/sccscroll0.ani,888,69a1fbc2773ec932f6a5a7fb37f7f12f,0560079fe4782ac89942eb45e94de7011eb1f4ebb39230890e4917d612bc5d01,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/sccscroll1.ani,888,6aacbae0874a6c9aa335e35947a8ebd0,c48c8691866637e0fa9fc98bb40e2d8240370b4cafd7b767c1595b58789eb06c,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/sccscroll2.ani,888,43f6019d566d0aa8759a42d0a5bec5cd,e62fe7da074898004ee7ef81626830fa94332b3a9d4fe8987e948cea0e2ac1f9,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/sccscroll3.ani,888,6f2e50e14ff2776eae76d961a4a5bfc7,f9c2a000c946a6f6bb420d9671c9ba21a9fc14821a5692a5f2e5b4755fdebb8d,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCScroll4.ani,888,a169748d79da826c10d7e5149c7fd41e,ec77136c9768ca3c50216ae92269aa5fae4f29197a13a355c6f92fcf53ecb279,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCScroll5.ani,888,f17a6fc61a7b944bf5eed66d8242d5ca,2ccb9140e23faf6cc98560e545a54b83efce54dd1bfb01ea84119732d0f04433,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCScroll6.ani,888,33618b4ed32fcec63df6fea44b98bac8,46186f3e8e9243edda6e0baf00e0a0fcaaf5e5d9b4d4531bf13393fdda67acc9,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCScroll7.ani,888,eaae99658bb1a5d14ea89ae89aaf32e0,00904ceec2de7e828c1a5fbe224b1ebf6cb19543f3d24381b64d4329a510e46b,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCSDIUplink.ani,16460,1443e1a4d8c5665e6a88288e1b03e7b0,11de6fad707df80023f91c011569de86705b2770cd1df5bb0394481bf5794fb5,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCSelect.ani,6388,21d58f3402a7f33ca5422fc865d2d602,42840130f31fa90c880ba2855fecff0c4a1cd3e903e6eea862ab95b8dc876427,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCSell.ani,1672,21f1c178ab89de9b364cc8709ad0dce8,3a9dcadef511ad9a2dd1ca9b31eac33a7aa58b652554cd1d18c628c87fab1a4d,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCSniper.ani,15636,29c81451b838097f4baed9b00c3f2873,258ce022aa05087bd0772484d3aceff49b9beb532cd8a8d524f9eb97be57de80,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCSpyDrone.ani,1632,b30aaf4f9ef73831165b1e280050da14,6a4302a4a897d6ded38ee0216bc45975406d40a73270eed12d13cb73c868ba34,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCStop.ani,882,28eb1d384c4ff53a0b44352fd62455b3,ebd5c2968465f5e3898d38d3a737f2bda387be6be6ee246712a5081d703150a1,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCTimedChg.ani,8634,c84d13defbb211c95a8015cfc743320c,727c0863496e777929a86425fcc849e7041b77e3209151f566fe233bf8a2bce6,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCTNTAttack.ani,4744,23569a40a3dbe637a6fdd839d25f58ec,e5c03a381c120e26d04c5c7495c88bf43224f51d89e9ca9d7dea84a5903285fd,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCWaypoint_S.ani,1682,b7566c5d6d3bc8e604f6c07b9dedf4ff,1c585e4c98e05ad6fa916f6d496dcac8673ee651a8cd6743319850e3d14bd4bc,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Cursors/SCCWaypoint.ani,1676,c76b8d01c707588e02aea0c9ae052c54,6335fdebaa2ce98e17fec156d75ff9b089ab0f42b70170e5f18383abc73f1e88,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_AirGen_000.bik,346496,13ea362d7b47cd7247af5e433e31f16b,2d40193202eb94c4b61d504541e914453c72734ab69777f8805c900607d9b44e,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_AirGen_inv_000.bik,151536,e72d42f551369f6f827009696642e7e2,9eaeb8644884408b0b12f4f635939d013baea7d17813794a8760c81bab10e375,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_BossGen_000.bik,346964,06e4f22ea5b688b3b8704dd9a2224484,2c5b1fc04a58ab23a140ef5f604370200754db649726fb1f07ed51fd1e34f91a,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_BossGen_inv_000.bik,346988,26a765a7dbe257ff065b6a1db6c215a9,113b95f527ab763bb48d598095c0dd570dff925f097ea231d3f18b696d06c620,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_DemolGen_000.bik,346204,79f69494f98c180251dc20d4942d3693,43bff2a3b1b88e986038e497080f153a2aa3cfa8ca858bd4c179433ea71a9820,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_DemolGen_inv_000.bik,346196,50f505f6a145b77b922c1ce1fc121fec,787436021a38fc4390ef1d9f4672eb05cc5d1da07df81f4989f8bea0289565c8,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_InfantryGen_000.bik,346660,909cd4a92955a09cfa5a5d757986cba6,c5dec5ffc859340b06b97bc6e3f50ed48d3aee4022808dae26f52dd32566ce25,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_InfantryGen_inv_000.bik,346644,beb578d242b377c7ad85772888c5f3e6,f9c6a21d40ecce522b25e31e3759b75c5d0b2aa9654e89cb922b8109b1ccd259,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_LaserGen_000.bik,346720,def7d95800704d912857326956621e34,52c33f472f294b243051d7a8f7e16f84256ef5f83e669ecbd63d6e779cf80289,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_LaserGen_inv_000.bik,152152,c6b2fd8c8584e726eb22c83ce47f0bbf,4db8ef4958b397ce3ff6d70bd5559fe52dcae55e5cae11ec383ba678c166a134,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_NukeGen_000.bik,346788,0980302323d3e57b0e8a10a13b2d0484,e42916ce2dbc58c626e8faf4d71ed870d000e0c31e3cacca3eed9a213bf5ccfa,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_NukeGen_inv_000.bik,346844,8091a036c4013b40914e3e353ec3066b,c2221846962aa329fe184867effd9b58a755b4ef088c799235ce2a6cfeb5a92d,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_StealthGen_000.bik,346780,a2805a2af305c3dd696034ec3092757e,b98d23db78e1c5fcb4a4c821210e0209aba982f567c5dd1169d75138df8b0ad8,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_StealthGen_inv_000.bik,346576,95f63b145cfbea47c8b2b43dcc5e6e6c,13367541c8eef647f944b689027de3b9e05527fb0c01fea3db76442e96c5594b,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_SuperGen_000.bik,346716,c41c7c55a307b7348816c65ab8615318,48f5c1b8e1a4b8385742b9f4186ef3245d081c6d81a57309f9d6dd787adae5e2,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_SuperGen_inv_000.bik,346760,99f88fab06fbf9450a78a9d769c52a12,4c532ac82135eb891aace70cbad07de9e0dbb8b829b4c855122cf0024fd49c06,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_TankGen_000.bik,346692,fa21d37a99b75bd6f05999bf8b54e075,d38eda028458e75806d52aad0d8d91a6f9a5de9ecd61ddb41914be89aa06c9ee,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_TankGen_inv_000.bik,346824,854ca0f56855864f577bdd0e32bf0eb9,39cd6967d21b3480fbd87750052ffe9305adde460a2c66cfdc199b4adec1ac8b,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_ThraxGen_000.bik,346192,73cb03cab7bd8e822d46f61250865d77,78995480ccf11157e5a65fb553371683de8b6a34dbba7cc756e55fd1bdf39311,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/Comp_ThraxGen_inv_000.bik,346212,c52f4f03b949d321e126104500167a3a,f33251a5fa071bfc0326b4cbe7ba5c423c8de886f98dc7fe22659e247ed99725,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/EA_LOGO.BIK,1480980,adbd4c3a5abce41bb190430acb4ff29e,f8beb9cbc902cdd90f94563df1246d4df5769f8335a2dcc1f85916d446e3a8c0,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/EA_LOGO640.BIK,1480980,adbd4c3a5abce41bb190430acb4ff29e,f8beb9cbc902cdd90f94563df1246d4df5769f8335a2dcc1f85916d446e3a8c0,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_China01_0.bik,18455164,58b447f059cc41672f4463533cedf269,1c46249e9145606f09346dff88616e0ac95d3d40d193d53e134594e4693553b5,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_China02_0.bik,13208784,7ca5f9a4e00e740d8bafbf8a9f7b5a79,09696f151d47db3c8f99fa1f35e862f803e6b1f7b77613a32fc1d674ae589e62,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_China03_0.bik,14858580,abe073e844cda10163b6b3c8f15bcd80,192ca2cb20e4d7f7b37234a61146854dbae50dedf3405f2bfa89c0d304239c2c,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_China04_0.bik,16527788,78e2d3e6562d093e2bb3e906f2cd0a45,43f130f39cded46e84b49bff079f791e8e3566e95037547618da0cb9bb89de7c,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_China05_0.bik,15937612,8de7f4742ea9b8a7e124a664dff9898e,2341a9531021808560fdac241d14ecc2a678e86c013af74161a484cd5a05713c,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_GLA01_0.bik,15523480,40e2374e0e7b19d54ce433ccbb3b2dea,da4e5a0d5aad89aed9b0643d66c6533fac1e127a3f20e37595223797603f2d57,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_GLA02_0.bik,13984196,7b94d3111786099a03ada61269c6e40a,55ab92bf9ed77be8c6ad7c167634714634d2b5ba156e5a276d6cc3fb181a91f6,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_GLA03_0.bik,15558184,ebcb01cfbbd41687a93c5192dcdbdad6,aef36d070fa054a9a595cb3579c93131c2a7f936208b817313d4170c0903665c,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_GLA04_0.bik,13278492,e1e64e5524e143db38565f92ba95a4e6,dc2aec472a42a85d0307855cfb53e374f81f814db1cc25ae005d3d148874ecc6,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_GLA05_0.bik,18456064,609304c5ac3e35097c11c45d99a5d391,90a51d388e7662488b236833382e342a5c77db218ef02709ddb6a7f21c178a3d,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_USA01_0.bik,23077588,ab98eb72484f7380cba94368394a08fc,81810d0e1505611aad402c6ba02e8f18f1224fa5fa7bde40cf769a5d15f3de1d,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_USA02_0.bik,15983780,d63e78fd37fc7b0788a40fedc537c625,01f8b53b61aa9009c4a8b9092ffdac874c2c280023e2a9311fd358e5b455bf4d,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_USA03_0.bik,13706924,921b27dd9f2deb9317a377aa9d3d7f26,5471d66f13206ac39bd435d53a2c924eb4cf38af9e06a22f4ca17e142f885173,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_USA04_0.bik,16062316,0534b9b0ae2407cb501e3c0696b489ea,1a46805c6a117ffc1d14843d5073f5b246924d413cdbd6448141e1b1ed1941ea,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/MD_USA05_0.bik,20291044,a6e969dd759dea02a3b7609d1a5cef38,97abc54134f15db7359d9509dfbde9e6c9d6382edc5979e66da8dc763119f74c,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/sizzle_review.bik,23891876,4238a81d8ccbaf8324bdf1994ddeade7,66a52a06953255390ca584c385e1f5524864ee48d206713577c172a5aacf1ab6,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/English/Movies/sizzle_review640.bik,17220424,e464f33ae298e75d3439756819786136,01ba21833f311e39660b704d9fbe88cc4cc60c641d1f0a699035647c8007c3f5,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Movies/GC_Background.bik,149700,bd8fb9e5d3982d9c86c817512bcf17eb,fd997a3b763c8a6e3a5655eaaa9d105b88471487ee639c71cff23bc3d62e8acb,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Movies/VS_small.bik,310128,6b56f1c09cdba7a363c66873ba385cb4,f0e4c07b1041dd535298a42eb64b8f734560e33d76902f340d4e820704baf993,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Scripts/MultiplayerScripts.scb,10381,2938ee364c4fc60f608d223046ae20f6,86d6bd295dd56dc17c6c1289f9a530506c755b0dbc3e868448d93dd468738ab6,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Scripts/Scripts.ini,68743,e37952e22da90832511f99dbcea732f5,2c72d91a77929fd5f2b6f590b4af5ef1c099379bf9c6e39b0f249022a54b7554,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/Scripts/SkirmishScripts.scb,2272629,0635cfc535efd6f0a09495326bf5d8e5,8f93862b751f289b052206b87170cc840044cb66660fbf6ae30d5782c1d73776,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust00.tga,16428,9f2d9b6133b6e4d78b409ca8303cab85,d4b2fa073a52734658fed6b780de379fe866b956aec8c95525457232e7d1b636,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust01.tga,16428,5b257180aef56e05b007be93eb64e527,67116bb0c18491cf403d5706e4d6c005d43ea77e034cb889d60465501d504156,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust02.tga,16428,6802068b10f1f8ac64646d54124b7e96,2718869ffb24f789a912b5fe0dcd94067db1a06aa144e1a1ac0d81cd6b5c8fbe,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust03.tga,16428,af760ffba0eff9fe49ace5e8e82de122,6e7c3733f937534800dfc1ac4632c34dd4c873d6d9e55f13dcedf738462d470d,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust04.tga,16428,3068c2e091b0c52c93b0d83d18b04ea3,7123520dc1d22b0279e24f50f4dd5c62d54b2185883acadfd723e19eda7d5c0e,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust05.tga,16428,4e6a03367c3282c939f1002f826c6549,7a44d215ad75268319abcef7cd2ac89dfc68cf7a4ff090cb354db287971764a4,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust06.tga,16428,daefb72301f19803058795c9d4eeb833,c1fba0eef7d968bdd1866fdc13495e0c3ef5f5171f6a6c70f4e73e7097dc6d52,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust07.tga,16428,8b8ccacbcdf9cdef9481cd9428760c44,b84e11ade33a67bce27c7c2dfcbcc569045e41609332f6f62ef12c675db7baa8,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust08.tga,16428,0def5b045f0e0ddabf002d68efb8d347,a1e37542342ecb0d7c482512b3f98195b1f17706a0f1579221fa78f3644d64ba,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust09.tga,16428,f89c4c80fb4d69d23c0755787d18f12b,48608dff4b30b2b25a5fa0c450f744c56e37f007f01b351018c82df7e7198ea4,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust10.tga,16428,44baf98408c4f629a79495ca88669bec,c2e1c7d5594681269641e08442334eee3d0da26b7c385036427dd33aa56ecedb,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust11.tga,16428,db0ad392bd51d1fe89ee050e7e9a430b,1bb9d5b5edd128e315bba2f24ed00be12e4df7ce8ca37f7c7d84ef666e8ac72c,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust12.tga,16428,0416996a4b5ba4c7a60f0456cf1c5a76,dfa05cbcf7d32b8057bf57fa0c7b05e24839205845e66ce06d612130a3f31376,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust13.tga,16428,9016d60dfcd7fa826edd07464a1cc1cb,5baa929913b5ce1a1cbec97f6cd9090dfa3d78ebc0e000d1ee6b8e0f45591255,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust14.tga,16428,deb81cd26d0d67043fd6338c691338f3,29c941490d668adb8cff8927ca5e6694847dffc4198739e88956356e6f2a3094,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust15.tga,16428,d10b07457cc172d05b8508b427e24187,f87addd5360edc1bd5822bcc2818dda27eb727054783b346ddbea26b78e82ff2,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust16.tga,16428,d75dfff45be2c47b5f3c115f9d78bfe2,90dee0bb7198e675772d81e30327531f8bbb535d299b8dd613466c44cd7235f7,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust17.tga,16428,bc83a41cb453dfe12d378ecb8c799d49,f053a13e73c8ce938938feea573141a0e22b50eeaec7f101f4636004f8ba554f,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust18.tga,16428,040c0932fd2705ea0a37e360c57b1cec,c26f3a040e1284a99e06b18953df40efbeabe1e0947783c797b363364ebb99f1,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust19.tga,16428,85e1680cea2bb20578e4f1aa19dfa67e,04915f82f986d72f6c999583b957bb735ba221890bec61aeeb1a16abb1d4352e,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust20.tga,16428,d5ac8f99b83fc92ba4e2f356c6816c21,1a27a58af21b14b8da96728ba62725626085f6b50399d99ff990fd2bcde3e725,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust21.tga,16428,ab1231ec264d2b93cfca0cadc52554f4,8c0e3d19126da509f7cdea23e98142b489479019095ae7cfc00024921523ad5f,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust22.tga,16428,f349cba69868d4da452fa37ec21f860e,fbdbef23579e743b027ce6177e3e15edd3df2794ae49bfc6144c81b777d395a0,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust23.tga,16428,79cb087169f45f2d2b121762b0175b5e,478d7d5e09a60624104647106b05c02d6831a68a7aa14590359d3d00fa02accc,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust24.tga,16428,64e7f0a3fafb344ff8772d2b47aabdb2,e19606c3677d773c9f39681bc0a03c49376292d7f0bc5ce340ccab19c1f09166,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust25.tga,16428,255ca010b8657f14c85bc72028472bf6,b29b4df4052bfde94ce82617f647c02e3864c1f730e9d91f880bb43bbb325f5e,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust26.tga,16428,e79bc7f009b31a48137b5e59fb704ffd,4fd4406232083d148b97cb1de257f5674023c5ecb92b4418291f3ab6667a8c69,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust27.tga,16428,4f7c2763c2f1b4ea4ef99c2bf6496404,becbb96d56fb5a22bc809c2100ee7beecab9470593feec4bf82354d81aeccbc5,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust28.tga,16428,9f399179b32616aebff3a1638973de15,2f0f5c933ff40b9c959c484a317a2e0568392fb2347e76bdbaac2e850e1de642,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust29.tga,16428,cc2998d2d0044e1762d794ed38930cc2,7437ed2c75b3f791372ed914c692e0cb36387d4586089a818c9f33be38a41c32,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust30.tga,16428,88da7064ccdee97387ee50c6c678ce7b,11a82a9b483dcf824e8e86997decc89286e6cc5af71624c1186a186aa5721a35,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Data/WaterPlane/caust31.tga,16428,418b9f65207e4d7321c5e87f98f3084d,6b49361ac4c0709a3dbf15e3ec537aa616e0c324bfa6bc490d28cee5c3fa7cd6,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+dbghelp.dll,163088,13fbc2e8b37ddf28181dd6d8081c2b8e,a29056a9810ff08c708505f1ac20d0263d5d894a223696e20217c0e9d132bf84,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+DrvMgt.dll,41472,b725c0fc7139ba5e16759cffd44c8944,cbae2ee6166028bcd26768e0ab375e29b007b06208291347cd80c0e676ca4570,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+EnglishZH.big,80472928,b88cc553608289160da1cc7af4829a82,d3904216ac210a363d9e0e46980c6ef70232046ba713ea137e8a87cbd57a32d6,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+game.dat,6483968,0fafcfa2cfbcff3c5ed5b209c306d64d,3ca248389ab7b562a1f7e99875af4b8282a5b3e459945e3b1b9a0fdbacbd282c,ZeroHour,All,True,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Generals.dat,56,bec512bdd269c5541c20f96c7cb1b930,2fb113e18fa5ff72885c5c9a94d95b3ce739088cea95582096b93c9ff3a0b6e4,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+generals.exe,6480431,328413da608c14b7f402ec61a0067956,ad4ef0c12ab41d6534a3ac0ee1364e4c1be93eaad6f2c5bf559869abd642594c,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Generals.ico,21630,1e0020da079a9aaf973a0cd622ff4bb7,dabd138c378c1dea55f3019420682789a68f4fc515cfa37c6cd665d8e413e504,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+generals.lcf,267,9648766e3d377cc7f544fab225c3da15,9b30cc82856514f1af345701f317b84481a6fa877751e7cd71fd5e60c462a790,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+GeneralsZH.ico,21630,1e0020da079a9aaf973a0cd622ff4bb7,dabd138c378c1dea55f3019420682789a68f4fc515cfa37c6cd665d8e413e504,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+GensecZH.big,787464,c375e701ef869d86e936f01df365c49f,ac2aaf5536a2f748dc99178aa431280f26d9d85179e6c2abedf5adfabc971b15,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+INIZH.big,18764687,aeec332104db082612d162ec41913986,1a6d41a7a2cb31e67ad2f868aca9264ad069c275e0074f8a0d970a336071e9a0,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Install_Final.bmp,1440056,3ae2a6b7a00941e85d979c5842ed8d95,d2bad77cdec7269346dbf2b7c2c06505f220670352bddb242f6a839f0f1f2401,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+langdata.dat,25398,2e7d20210b21b5fe40e1bb44af63c1c2,b964985085af30ff170d25cdbf97a000db1f52d99e9e4cda293a7761cc5a2616,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+launcher.bmp,39604,bb58f29acaeee51836e0261f03ebd576,9c2c1a24c2972f239adf53f96aa53e4e3288bd37d42531fd4d37d39ab84b2df6,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Launcher.txt,13028,ddd7d6df5ad443d3a4bebacf16b225df,8aadc18dc84587cdea6dca1b75a71be3836b0a3dd2259d39c4ff38f57e91d551,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MapsZH.big,39749312,ba7e68e1c67416fa651f74a3f099245a,35cc8947f34f363d69f5045b9ac65f6ee164b8d5a382a1f7af213dbc2a744b96,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MSS/mssa3d.m3d,83456,e089ce52b0617a6530069f22e0bdba2a,41ccd5e30475ef7b40e68aa8c5c0ce18e804179fcaa77ba42e6ffa4f438d9a24,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MSS/mssds3d.m3d,70656,85267776d45dbf5475c7d9882f08117c,a2926f4e2a094a99508c05adaf86c5710ad3cff8bbcf247821feb0e6977f547c,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MSS/mssdsp.flt,93696,cb71b1791009eca618e9b1ad4baa4fa9,e035db7c2a4a2378156f096a1450faec425fd8b89bffb886f68c655480bfff52,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MSS/mssdx7.m3d,80896,2727e2671482a55b2f1f16aa88d2780f,e6c928729db1d7c62d684962f4ecfd6bb039504897af53b72b2a66d32f1bc6b0,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MSS/msseax.m3d,103424,788bd950efe89fa5166292bd6729fa62,62e0e34435b9705eedc73660e64564138d8276dcf7b08dfbaac05c592b67e6d3,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MSS/mssmp3.asi,125952,189576dfe55af3b70db7e3e2312cd0fd,121be91fd21c80396cb5cc46c245d9b3f67a26f8cec4d0ebd03f17cd13508b0d,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MSS/mssrsx.m3d,354816,7fae15b559eb91f491a5f75cfa103cd4,f983c72977f19fb7bdfeaec4db1ee1e169a18cc58499452a5bab9fa2447f68ce,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MSS/msssoft.m3d,67072,bdc9ad58ade17dbd939522eee447416f,5dcbf188c30ae1ac6a3d5b7fac4a25e831c9a495683b044f5141c4bdbc83f607,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MSS/mssvoice.asi,197120,3d5342edebe722748ace78c930f4d8a5,72bac1b0d0d3bfcc235a74c06c3fc62043f197a2bd8ebcf8a89652d78f23157b,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+mss32.dll,349696,6400e224b8b44ece59a992e6d8233719,441b290e7dc6334eb5023cd9b7937739298fdd66c104d4c96e5edcf642ae912d,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+Music.big,786724,54d9b5b40a97e9770670ec5a9e0cafad,3f2af9c6dcc2b35852556bdc020c95e43c2f43127c50dbff018e12a2bed6116f,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+MusicZH.big,34741916,17c178f302bba43ca5c66efa7e14efab,5aa7b8408e5cf61fd53633f6e6310c76d79e74d75dd3286b6e9b9c718d9c5b5e,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+P2XDLL.DLL,519168,f8e5e9d283c5f7ca528777ddbb5d6e48,15dad960f53ba3238564a10678b993ddbe9964b4c5033d905337e0bfb79039d2,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+patchget.dat,122880,cb197b8fa2ba2bde0eb382f862d8e3d5,fd84c2ce09d574653ade6aae0081b3ea033b230e5e841d6c45fd98d10d47a9f7,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+patchw32.dll,185344,6ec517e866e476401755281837295579,0ec6e25234ad74489eb1890d4de57bb6140bb8196bdc4a5dcac90dd9d16eb2dd,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+PatchZH.big,118822,c190bbaa94d8f940259b6bdc9d611deb,450276fbabd19f79dc0143f70fe755e44a99b22c552bc00c5854fc810e615722,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+SECDRV.SYS,12400,ba0d892d2f786bcebdf03b0a252b47f3,4ed103bd45ece4d2b6029c36d0e209c8a6f1c34e0f72b01553742773cb1f43a1,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+ShadersZH.big,996,299c959957eac0d8348ad96479ea00c8,6246a6906f93261669c8c19e021c8d3484a6f7449bd5e253c8e998c1a9d31d6e,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+SpeechEnglishZH.big,254275694,42dece807234bacad9c58328e37af0ce,5b3c8b1819b1ddfeaf6045a4a5dfb9cb47ad16a508dbf17c317e11b9d310a14f,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+SpeechZH.big,6174552,f01b53214e75950749c1e1e0d1dfa105,4498097a3c294bc638e72e09e967bb417b0c9b171b4eee95e2c6c0bffef68fa2,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+TerrainZH.big,8660432,cd7d1e946232307d7b1c089e3af8708e,501ef71d12e7a1398800231f2526d47e8e583607f843a2ebcb0c622e26fc73b3,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+TexturesZH.big,222753036,8be93eafc51909aec552827000a940f1,aa975400abd70e45e13eacf4ab22505ea322941dfc492097c68245cd35d4b791,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+W3DEnglishZH.big,2696872,31d49330fb96abd8efadb7f713c8e238,f5d164d1e294f26744e2b87ecfbe13e68affaf5ceb4cf12f43005beb8f54defd,ZeroHour,EN,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+W3DZH.big,189741064,39a344cef260aeb608c49b0739bfafd4,e308c3aeb49d023ffd98b50400f24643a782a68ac4d2e53171c17fc45d73fdc6,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+WindowZH.big,8493653,11ba0050700bd5f91a146acb8c0293bb,68b519f28d012cd297fae2663d431a4b0e6e416aa7bb1a57a79e5500ebcc14b4,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
+WorldBuilder.exe,10604603,1b5e7d5779ceb561e4d69b17a42d38e1,2ce0541cf2510713b7e98873d1f0fac0301cdb770df0d95bbfabd55155a96ddd,ZeroHour,All,False,"{""category"":""other""}",https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv
diff --git a/GenHub/GenHub.Core/Assets/Registries/index.json b/GenHub/GenHub.Core/Assets/Registries/index.json
new file mode 100644
index 000000000..7de03bcff
--- /dev/null
+++ b/GenHub/GenHub.Core/Assets/Registries/index.json
@@ -0,0 +1,39 @@
+{
+ "version": "1.0.0",
+ "lastUpdated": "2026-08-30T17:40:00Z",
+ "description": "Index of CSV registries for Command & Conquer Generals and Zero Hour validation",
+ "registries": [
+ {
+ "id": "generals-1.08",
+ "gameType": "Generals",
+ "version": "1.08",
+ "url": "https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv",
+ "fileCount": 164,
+ "totalSizeBytes": 48166,
+ "languages": ["All", "EN", "DE", "FR", "ES", "IT", "KO", "PL", "PT-BR", "ZH-CN", "ZH-TW"],
+ "checksum": {
+ "md5": "41e3f06a608156eaea960d432d6be682",
+ "sha256": "97c72beab9b92918ccf2629cf104034007337873783f2f7f03855e9857fa6267"
+ },
+ "generatedAt": "2025-09-17T09:15:00Z",
+ "generatorVersion": "1.0.0",
+ "isActive": true
+ },
+ {
+ "id": "zerohour-1.04",
+ "gameType": "ZeroHour",
+ "version": "1.04",
+ "url": "https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv",
+ "fileCount": 175,
+ "totalSizeBytes": 51722,
+ "languages": ["All", "EN", "DE", "FR", "ES", "IT", "KO", "PL", "PT-BR", "ZH-CN", "ZH-TW"],
+ "checksum": {
+ "md5": "f2dc5255bc48636449e146666fb1b53a",
+ "sha256": "41bd40d8f477eeba73c5c7543a902542394034d995c8efc27467d5938b9836c6"
+ },
+ "generatedAt": "2025-09-17T09:20:00Z",
+ "generatorVersion": "1.0.0",
+ "isActive": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/GenHub/GenHub.Core/Constants/CsvConstants.cs b/GenHub/GenHub.Core/Constants/CsvConstants.cs
index 65f6407c0..ec7be534a 100644
--- a/GenHub/GenHub.Core/Constants/CsvConstants.cs
+++ b/GenHub/GenHub.Core/Constants/CsvConstants.cs
@@ -127,4 +127,39 @@ public static class CsvConstants
/// Canonical language code for Traditional Chinese.
///
public const string LanguageZhTw = "ZH-TW";
+
+ ///
+ /// File name for Generals 1.08 authoritative CSV registry.
+ ///
+ public const string GeneralsCsvFileName = "Generals-1.08.csv";
+
+ ///
+ /// File name for Zero Hour 1.04 authoritative CSV registry.
+ ///
+ public const string ZeroHourCsvFileName = "ZeroHour-1.04.csv";
+
+ ///
+ /// File name for game installation files index JSON.
+ ///
+ public const string RegistryIndexFileName = "index.json";
+
+ ///
+ /// Documentation folder containing game installation files registry.
+ ///
+ public const string RegistryDocsFolder = "GameInstallationFilesRegistry";
+
+ ///
+ /// Embedded resource namespace in GenHub.Core containing authoritative registries.
+ ///
+ public const string EmbeddedResourceNamespace = "GenHub.Core.Assets.Registries";
+
+ ///
+ /// Default remote URL for Generals 1.08 CSV.
+ ///
+ public const string DefaultGeneralsCsvUrl = "https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/Generals-1.08.csv";
+
+ ///
+ /// Default remote URL for Zero Hour 1.04 CSV.
+ ///
+ public const string DefaultZeroHourCsvUrl = "https://raw.githubusercontent.com/community-outpost/GenHub/main/docs/GameInstallationFilesRegistry/ZeroHour-1.04.csv";
}
diff --git a/GenHub/GenHub.Core/GenHub.Core.csproj b/GenHub/GenHub.Core/GenHub.Core.csproj
index 2dd9fe5dc..bbd7d8006 100644
--- a/GenHub/GenHub.Core/GenHub.Core.csproj
+++ b/GenHub/GenHub.Core/GenHub.Core.csproj
@@ -1,4 +1,4 @@
-
+
net8.0
enable
@@ -18,5 +18,7 @@
+
+
diff --git a/GenHub/GenHub.Core/Interfaces/Manifest/IContentManifestBuilder.cs b/GenHub/GenHub.Core/Interfaces/Manifest/IContentManifestBuilder.cs
index 58985d1e0..ec01a5563 100644
--- a/GenHub/GenHub.Core/Interfaces/Manifest/IContentManifestBuilder.cs
+++ b/GenHub/GenHub.Core/Interfaces/Manifest/IContentManifestBuilder.cs
@@ -167,8 +167,18 @@ IContentManifestBuilder AddDependency(
/// The source path of the file in the game installation.
/// Whether the file is executable.
/// File permissions.
+ /// Optional pre-computed SHA256 content hash.
+ /// Optional file size in bytes.
+ /// Whether the file is required.
/// A task that yields the instance for chaining upon completion.
- Task AddGameInstallationFileAsync(string relativePath, string sourcePath, bool isExecutable = false, FilePermissions? permissions = null);
+ Task AddGameInstallationFileAsync(
+ string relativePath,
+ string sourcePath,
+ bool isExecutable = false,
+ FilePermissions? permissions = null,
+ string? hash = null,
+ long? size = null,
+ bool isRequired = true);
///
/// Adds a content-addressable file from the CAS system.
diff --git a/GenHub/GenHub.Core/Interfaces/Manifest/IManifestGenerationService.cs b/GenHub/GenHub.Core/Interfaces/Manifest/IManifestGenerationService.cs
index 280d43697..f15f6a9cd 100644
--- a/GenHub/GenHub.Core/Interfaces/Manifest/IManifestGenerationService.cs
+++ b/GenHub/GenHub.Core/Interfaces/Manifest/IManifestGenerationService.cs
@@ -15,8 +15,14 @@ public interface IManifestGenerationService
/// The game type (Generals, ZeroHour).
/// The installation type (Steam, EaApp).
/// The manifest version (e.g., "1.08", "1.04", or integer like 0, 1, 2). If null, defaults to 0.
+ /// Optional explicit language code (e.g., "EN", "DE"). If null, language is detected automatically.
/// A that returns a configured manifest builder.
- Task CreateGameInstallationManifestAsync(string gameInstallationPath, GameType gameType, GameInstallationType installationType, string? manifestVersion = null);
+ Task CreateGameInstallationManifestAsync(
+ string gameInstallationPath,
+ GameType gameType,
+ GameInstallationType installationType,
+ string? manifestVersion = null,
+ string? language = null);
///
/// Creates a manifest builder for a game installation with integer version.
@@ -25,8 +31,14 @@ public interface IManifestGenerationService
/// The game type (Generals, ZeroHour).
/// The installation type (Steam, EaApp).
/// The manifest version (e.g., 1, 2, 20). Defaults to 0 for first version.
+ /// Optional explicit language code (e.g., "EN", "DE"). If null, language is detected automatically.
/// A that returns a configured manifest builder.
- Task CreateGameInstallationManifestAsync(string gameInstallationPath, GameType gameType, GameInstallationType installationType, int manifestVersion = 0);
+ Task CreateGameInstallationManifestAsync(
+ string gameInstallationPath,
+ GameType gameType,
+ GameInstallationType installationType,
+ int manifestVersion = 0,
+ string? language = null);
///
/// Creates a manifest builder for any content type (mod, patch, addon, etc).
diff --git a/GenHub/GenHub.Tests/GenHub.Tests.Core/Features/Manifest/ManifestGenerationServiceTests.cs b/GenHub/GenHub.Tests/GenHub.Tests.Core/Features/Manifest/ManifestGenerationServiceTests.cs
index 094c1c886..bad46ee3c 100644
--- a/GenHub/GenHub.Tests/GenHub.Tests.Core/Features/Manifest/ManifestGenerationServiceTests.cs
+++ b/GenHub/GenHub.Tests/GenHub.Tests.Core/Features/Manifest/ManifestGenerationServiceTests.cs
@@ -260,7 +260,7 @@ public async Task CreateGameInstallationManifestAsync_UsesCsvWhenAvailableAsync(
var installationPath = Path.Combine(_tempDirectory, "GeneralsInstall");
Directory.CreateDirectory(installationPath);
- // Create some files that are in the generals.csv
+ // Create some files that are in the Generals registry
await File.WriteAllTextAsync(Path.Combine(installationPath, "generals.exe"), "dummy");
await File.WriteAllTextAsync(Path.Combine(installationPath, "AudioEnglish.big"), "dummy");
@@ -275,6 +275,167 @@ public async Task CreateGameInstallationManifestAsync_UsesCsvWhenAvailableAsync(
Assert.Contains(manifest.Files, f => f.RelativePath == "AudioEnglish.big");
}
+ ///
+ /// Tests that CreateGameInstallationManifestAsync generates authoritative manifest for Generals with SHA256 hashes and proper source type.
+ ///
+ /// A representing the asynchronous test operation.
+ [Fact]
+ public async Task CreateGameInstallationManifestAsync_Generals_PopulatesAuthoritativeMetadataAsync()
+ {
+ // Arrange
+ var installationPath = Path.Combine(_tempDirectory, "GeneralsAuthoritative");
+ Directory.CreateDirectory(installationPath);
+
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "generals.exe"), "executable binary");
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "binkw32.dll"), "bink dll");
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "Audio.big"), "audio archive");
+
+ // Act
+ var builder = await _service.CreateGameInstallationManifestAsync(
+ installationPath, GameType.Generals, GameInstallationType.Steam, "1.08", "EN");
+ var manifest = builder.Build();
+
+ // Assert
+ Assert.NotNull(manifest);
+ var exeFile = manifest.Files.FirstOrDefault(f => f.RelativePath == "generals.exe");
+ Assert.NotNull(exeFile);
+ Assert.True(exeFile.IsExecutable);
+ Assert.Equal(GenHub.Core.Models.Enums.ContentSourceType.GameInstallation, exeFile.SourceType);
+ Assert.Equal("e253361f457f2ec3290ccf4088aa5c4022fc4772a769fff5fb2fa8b9e5df842d", exeFile.Hash);
+
+ var dllFile = manifest.Files.FirstOrDefault(f => f.RelativePath.Equals("binkw32.dll", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(dllFile);
+ Assert.False(dllFile.IsExecutable);
+ Assert.Equal("892a51c4056efcb22297a3b44a3491e3f5888f28b08ed1b17030f24acffedb44", dllFile.Hash);
+ }
+
+ ///
+ /// Tests that CreateGameInstallationManifestAsync generates authoritative manifest for Zero Hour.
+ ///
+ /// A representing the asynchronous test operation.
+ [Fact]
+ public async Task CreateGameInstallationManifestAsync_ZeroHour_ResolvesAuthoritativeFilesAsync()
+ {
+ // Arrange
+ var installationPath = Path.Combine(_tempDirectory, "ZeroHourInstall");
+ Directory.CreateDirectory(installationPath);
+
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "generals.exe"), "zh exe");
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "AudioZH.big"), "zh audio");
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "SpeechZH.big"), "zh speech");
+
+ // Act
+ var builder = await _service.CreateGameInstallationManifestAsync(
+ installationPath, GameType.ZeroHour, GameInstallationType.EaApp, "1.04", "EN");
+ var manifest = builder.Build();
+
+ // Assert
+ Assert.NotNull(manifest);
+ Assert.Contains(manifest.Files, f => f.RelativePath == "generals.exe");
+ Assert.Contains(manifest.Files, f => f.RelativePath == "AudioZH.big");
+ Assert.Contains(manifest.Files, f => f.RelativePath == "SpeechZH.big");
+ }
+
+ ///
+ /// Tests that CreateGameInstallationManifestAsync filters language files according to requested language.
+ ///
+ /// A representing the asynchronous test operation.
+ [Fact]
+ public async Task CreateGameInstallationManifestAsync_LanguageFiltering_IncludesMatchingLanguageOnlyAsync()
+ {
+ // Arrange
+ var installationPath = Path.Combine(_tempDirectory, "LanguageGenerals");
+ Directory.CreateDirectory(installationPath);
+
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "generals.exe"), "exe");
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "AudioEnglish.big"), "english audio");
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "English.big"), "english text");
+
+ // Act 1: Request English (EN)
+ var enBuilder = await _service.CreateGameInstallationManifestAsync(
+ installationPath, GameType.Generals, GameInstallationType.Steam, "1.08", "EN");
+ var enManifest = enBuilder.Build();
+
+ // Assert 1: English files are included
+ Assert.NotNull(enManifest);
+ Assert.Contains(enManifest.Files, f => f.RelativePath == "AudioEnglish.big");
+ Assert.Contains(enManifest.Files, f => f.RelativePath == "English.big");
+
+ // Act 2: Request German (DE) for the same folder
+ var deBuilder = await _service.CreateGameInstallationManifestAsync(
+ installationPath, GameType.Generals, GameInstallationType.Steam, "1.08", "DE");
+ var deManifest = deBuilder.Build();
+
+ // Assert 2: English-specific files are excluded when German is requested
+ Assert.NotNull(deManifest);
+ Assert.DoesNotContain(deManifest.Files, f => f.RelativePath == "AudioEnglish.big");
+ Assert.DoesNotContain(deManifest.Files, f => f.RelativePath == "English.big");
+ }
+
+ ///
+ /// Tests that CreateGameInstallationManifestAsync excludes extra non-vanilla files from the core manifest.
+ ///
+ /// A representing the asynchronous test operation.
+ [Fact]
+ public async Task CreateGameInstallationManifestAsync_ExcludesExtraNonVanillaFilesAsync()
+ {
+ // Arrange
+ var installationPath = Path.Combine(_tempDirectory, "ModdedGenerals");
+ Directory.CreateDirectory(installationPath);
+
+ // Vanilla files
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "generals.exe"), "exe");
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "AudioEnglish.big"), "english audio");
+
+ // Extra non-vanilla mod files
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "!Gentool.dll"), "gentool mod dll");
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "CustomShockwave.big"), "mod archive");
+ await File.WriteAllTextAsync(Path.Combine(installationPath, "test_custom_config.ini"), "custom ini");
+
+ // Act
+ var builder = await _service.CreateGameInstallationManifestAsync(
+ installationPath, GameType.Generals, GameInstallationType.Steam, "1.08", "EN");
+ var manifest = builder.Build();
+
+ // Assert
+ Assert.NotNull(manifest);
+ Assert.Contains(manifest.Files, f => f.RelativePath == "generals.exe");
+ Assert.Contains(manifest.Files, f => f.RelativePath == "AudioEnglish.big");
+
+ // Untracked/mod files should NOT be in the pristine vanilla manifest
+ Assert.DoesNotContain(manifest.Files, f => f.RelativePath == "!Gentool.dll");
+ Assert.DoesNotContain(manifest.Files, f => f.RelativePath == "CustomShockwave.big");
+ Assert.DoesNotContain(manifest.Files, f => f.RelativePath == "test_custom_config.ini");
+ }
+
+ ///
+ /// Tests that CreateGameInstallationManifestAsync uses backup (.bak) file if available.
+ ///
+ /// A representing the asynchronous test operation.
+ [Fact]
+ public async Task CreateGameInstallationManifestAsync_UsesBackupFileWhenPresentAsync()
+ {
+ // Arrange
+ var installationPath = Path.Combine(_tempDirectory, "BackupTest");
+ Directory.CreateDirectory(installationPath);
+
+ var originalExe = Path.Combine(installationPath, "generals.exe");
+ var backupExe = Path.Combine(installationPath, "generals.exe.bak");
+ await File.WriteAllTextAsync(originalExe, "modified exe");
+ await File.WriteAllTextAsync(backupExe, "pristine original backup exe");
+
+ // Act
+ var builder = await _service.CreateGameInstallationManifestAsync(
+ installationPath, GameType.Generals, GameInstallationType.Steam, "1.08", "EN");
+ var manifest = builder.Build();
+
+ // Assert
+ Assert.NotNull(manifest);
+ var exeFile = manifest.Files.FirstOrDefault(f => f.RelativePath == "generals.exe");
+ Assert.NotNull(exeFile);
+ Assert.Equal(backupExe, exeFile.SourcePath);
+ }
+
///
/// Cleans up temporary test files.
///
diff --git a/GenHub/GenHub/Features/Manifest/ContentManifestBuilder.cs b/GenHub/GenHub/Features/Manifest/ContentManifestBuilder.cs
index 828107c9c..0be224c51 100644
--- a/GenHub/GenHub/Features/Manifest/ContentManifestBuilder.cs
+++ b/GenHub/GenHub/Features/Manifest/ContentManifestBuilder.cs
@@ -504,19 +504,34 @@ public async Task AddRemoteFileAsync(
/// The source path of the file in the game installation.
/// Whether the file is executable.
/// File permissions.
+ /// Optional pre-computed SHA256 content hash.
+ /// Optional file size in bytes.
+ /// Whether the file is required.
/// A task that yields the instance for chaining upon completion.
public async Task AddGameInstallationFileAsync(
string relativePath,
string sourcePath,
bool isExecutable = false,
- FilePermissions? permissions = null)
+ FilePermissions? permissions = null,
+ string? hash = null,
+ long? size = null,
+ bool isRequired = true)
{
if (string.IsNullOrEmpty(sourcePath))
{
throw new ArgumentException("sourcePath cannot be null or empty for game installation files.", nameof(sourcePath));
}
- return await AddFileAsync(relativePath, sourcePath, ContentSourceType.GameInstallation, string.Empty, isExecutable, permissions);
+ return await AddFileAsync(
+ relativePath,
+ sourcePath,
+ ContentSourceType.GameInstallation,
+ string.Empty,
+ isExecutable,
+ permissions,
+ hash,
+ size,
+ isRequired);
}
///
@@ -865,6 +880,9 @@ private ContentInstallTarget DetermineInstallTarget(string relativePath)
/// Download URL.
/// Is executable.
/// File permissions.
+ /// Optional pre-computed SHA256 content hash.
+ /// Optional file size in bytes.
+ /// Whether the file is required.
/// The builder instance.
private async Task AddFileAsync(
string relativePath,
@@ -872,7 +890,10 @@ private async Task AddFileAsync(
ContentSourceType sourceType = ContentSourceType.ContentAddressable,
string downloadUrl = "",
bool isExecutable = false,
- FilePermissions? permissions = null)
+ FilePermissions? permissions = null,
+ string? hash = null,
+ long? size = null,
+ bool isRequired = true)
{
var installTarget = DetermineInstallTarget(relativePath);
@@ -884,19 +905,29 @@ private async Task AddFileAsync(
IsExecutable = isExecutable,
DownloadUrl = downloadUrl,
InstallTarget = installTarget,
+ IsRequired = isRequired,
Permissions = permissions ?? new FilePermissions { UnixPermissions = isExecutable ? "755" : "644", },
};
- var shouldComputeHash = false;
+ if (size.HasValue)
+ {
+ manifestFile.Size = size.Value;
+ }
+
+ if (!string.IsNullOrEmpty(hash))
+ {
+ manifestFile.Hash = hash;
+ }
+
if (!string.IsNullOrEmpty(sourcePath) && File.Exists(sourcePath))
{
var fileInfo = new FileInfo(sourcePath);
- manifestFile.Size = fileInfo.Length;
+ if (!size.HasValue)
+ {
+ manifestFile.Size = fileInfo.Length;
+ }
- // Always compute hash for executable files (critical for GameClient integrity validation)
- // For non-executable GameInstallation files, skip hash (CSV-based authority from GitHub planned)
- shouldComputeHash = isExecutable || sourceType != ContentSourceType.GameInstallation;
- if (shouldComputeHash)
+ if (string.IsNullOrEmpty(manifestFile.Hash))
{
manifestFile.Hash = await _hashProvider.ComputeFileHashAsync(sourcePath);
}
@@ -913,7 +944,7 @@ private async Task AddFileAsync(
}
_manifest.Files.Add(manifestFile);
- logger.LogDebug("Added file: {RelativePath} (Source: {SourceType}, Hashed: {Hashed})", relativePath, sourceType, shouldComputeHash);
+ logger.LogDebug("Added file: {RelativePath} (Source: {SourceType}, Hash: {Hash})", relativePath, sourceType, manifestFile.Hash);
return this;
}
}
diff --git a/GenHub/GenHub/Features/Manifest/ManifestGenerationService.cs b/GenHub/GenHub/Features/Manifest/ManifestGenerationService.cs
index ea609583a..ff1e1cf2d 100644
--- a/GenHub/GenHub/Features/Manifest/ManifestGenerationService.cs
+++ b/GenHub/GenHub/Features/Manifest/ManifestGenerationService.cs
@@ -1,18 +1,25 @@
using CsvHelper;
using CsvHelper.Configuration;
using GenHub.Core.Constants;
+using GenHub.Core.Features.GameInstallations;
using GenHub.Core.Interfaces.Common;
+using GenHub.Core.Interfaces.Content;
using GenHub.Core.Interfaces.Manifest;
using GenHub.Core.Interfaces.Tools;
+using GenHub.Core.Models.Content;
using GenHub.Core.Models.Enums;
using GenHub.Core.Models.Manifest;
+using GenHub.Core.Models.Results.Content;
using GenHub.Core.Utilities;
+using GenHub.Features.Content.Services.ContentResolvers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
+using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Net.Http;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
@@ -31,17 +38,29 @@ public class ManifestGenerationService(
IFileHashProvider hashProvider,
IManifestIdService manifestIdService,
IDownloadService downloadService,
- IConfigurationProviderService configurationProvider) : IManifestGenerationService
+ IConfigurationProviderService configurationProvider,
+ ILanguageDetector? languageDetector = null,
+ CsvResolver? csvResolver = null) : IManifestGenerationService
{
- private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
+ private static readonly CsvConfiguration CsvConfig = new(CultureInfo.InvariantCulture)
+ {
+ HasHeaderRecord = true,
+ MissingFieldFound = null,
+ HeaderValidated = null,
+ BadDataFound = null,
+ };
+
+ private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
- private static readonly string[] SupportedLanguages = ["EN", "DE", "FR", "ES", "IT", "KO", "PL", "PT-BR", "ZH-CN", "ZH-TW"];
+ private readonly ILanguageDetector _languageDetector = languageDetector ?? new LanguageDetector();
+
+ private readonly CsvResolver? _resolvedCsvResolver = csvResolver;
- private int _fileCount = 0;
+ private int _fileCount;
///
/// Creates a manifest builder for a game installation with string version normalization.
@@ -50,12 +69,14 @@ public class ManifestGenerationService(
/// The game type (Generals, ZeroHour).
/// The installation type (Steam, EaApp).
/// The manifest version (e.g., "1.08", "1.04", or integer like 0, 1, 2). If null, defaults to 0.
+ /// Optional explicit language code (e.g., "EN", "DE"). If null, language is detected automatically.
/// A that returns a configured manifest builder.
public async Task CreateGameInstallationManifestAsync(
string gameInstallationPath,
GameType gameType,
GameInstallationType installationType,
- string? manifestVersion = null)
+ string? manifestVersion = null,
+ string? language = null)
{
try
{
@@ -81,7 +102,7 @@ public async Task CreateGameInstallationManifestAsync(
builder.WithPublisher(publisher.Name, publisher.Website, publisher.SupportUrl, string.Empty, publisher.PublisherType);
// Add essential game files
- await AddGameFilesToManifest(builder, gameInstallationPath, gameType);
+ await AddGameFilesToManifest(builder, gameInstallationPath, gameType, manifestVersion, language);
logger.LogInformation(
"Created GameInstallation manifest for {InstallationType} {GameType} (Publisher: {PublisherName})",
@@ -109,14 +130,16 @@ public async Task CreateGameInstallationManifestAsync(
/// The game type (Generals, ZeroHour).
/// The installation type (Steam, EaApp).
/// The manifest version (e.g., 1, 2, 20). Defaults to 0 for first version.
+ /// Optional explicit language code (e.g., "EN", "DE"). If null, language is detected automatically.
/// A that returns a configured manifest builder.
public async Task CreateGameInstallationManifestAsync(
string gameInstallationPath,
GameType gameType,
GameInstallationType installationType,
- int manifestVersion = 0)
+ int manifestVersion = 0,
+ string? language = null)
{
- return await CreateGameInstallationManifestAsync(gameInstallationPath, gameType, installationType, manifestVersion.ToString());
+ return await CreateGameInstallationManifestAsync(gameInstallationPath, gameType, installationType, manifestVersion.ToString(), language);
}
///
@@ -333,7 +356,7 @@ public async Task SaveManifestAsync(ContentManifest manifest, string outputPath)
Directory.CreateDirectory(directory);
}
- var options = _jsonSerializerOptions;
+ var options = JsonOptions;
await using var stream = File.Create(outputPath);
await JsonSerializer.SerializeAsync(stream, manifest, options);
@@ -498,136 +521,286 @@ private static bool ShouldSkipFile(string relativePath)
relativePath.EndsWith(SteamConstants.ProxyLauncherFileName, StringComparison.OrdinalIgnoreCase);
}
+ private static string? GetCatalogFileName(GameType gameType, string version)
+ {
+ if (gameType == GameType.Generals)
+ {
+ return version switch
+ {
+ "1.08" or "1.8" => CsvConstants.GeneralsCsvFileName,
+ _ => null,
+ };
+ }
+
+ if (gameType == GameType.ZeroHour)
+ {
+ return version switch
+ {
+ "1.04" or "1.4" => CsvConstants.ZeroHourCsvFileName,
+ _ => null,
+ };
+ }
+
+ return null;
+ }
+
+ private static List FilterEntriesByGameAndLanguage(
+ IEnumerable records,
+ string targetGame,
+ string targetLanguage)
+ {
+ var result = new List();
+ foreach (var record in records)
+ {
+ if (string.IsNullOrWhiteSpace(record.RelativePath))
+ {
+ continue;
+ }
+
+ if (!string.IsNullOrWhiteSpace(targetGame) &&
+ !string.Equals(record.GameType, targetGame, StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ if (MatchesLanguage(record.Language, targetLanguage))
+ {
+ result.Add(record);
+ }
+ }
+
+ return result;
+ }
+
+ private static bool MatchesLanguage(string? entryLanguage, string targetLanguage)
+ {
+ if (string.Equals(targetLanguage, CsvConstants.AllLanguagesFilter, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ if (string.IsNullOrWhiteSpace(entryLanguage) ||
+ string.Equals(entryLanguage, CsvConstants.AllLanguagesFilter, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ var normalizedEntryLang = ContentSearchQuery.NormalizeLanguage(entryLanguage);
+ return string.Equals(normalizedEntryLang, targetLanguage, StringComparison.OrdinalIgnoreCase);
+ }
+
///
- /// Adds game files to a manifest builder.
+ /// Finds a file in the installation directory using case-insensitive path resolution.
///
- /// The manifest builder.
- /// The installation path.
- /// The game type.
- /// A task representing the asynchronous operation.
- ///
- /// Scans the game installation directory and adds all files to the manifest,
- /// excluding known non-game directories (RedistInstallers, Manuals, etc.).
- /// This will be used for manual content packaging of game installations until we have CSV feature implemented that will act as a source of truth to generate the manifest.
- ///
- private async Task AddGameFilesToManifest(IContentManifestBuilder builder, string installationPath, GameType gameType)
+ private static string? FindFileCaseInsensitive(string installationPath, string relativePath)
{
- try
+ if (string.IsNullOrWhiteSpace(relativePath) || Path.IsPathRooted(relativePath))
{
- // Reset file counter for this manifest generation
- _fileCount = 0;
+ return null;
+ }
- logger.LogInformation("Starting manifest generation for {GameType} at {InstallationPath}", gameType, installationPath);
+ // Prevent directory traversal
+ var normalizedRelative = relativePath.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
+ if (normalizedRelative.Contains(".." + Path.DirectorySeparatorChar) || normalizedRelative.StartsWith("..", StringComparison.Ordinal))
+ {
+ return null;
+ }
- // Add essential executable files
- var executableName = gameType == GameType.Generals ? GameClientConstants.GeneralsExecutable : GameClientConstants.ZeroHourExecutable;
- var executablePath = Path.Combine(installationPath, executableName);
+ var fullInstallationPath = Path.GetFullPath(installationPath);
+ var exactPath = Path.GetFullPath(Path.Combine(fullInstallationPath, normalizedRelative));
- if (File.Exists(executablePath))
+ // Ensure target is strictly within the installation directory
+ if (!exactPath.StartsWith(fullInstallationPath, StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ if (File.Exists(exactPath))
+ {
+ return exactPath;
+ }
+
+ var segments = relativePath.Split(['/', '\\'], StringSplitOptions.RemoveEmptyEntries);
+ var currentDir = fullInstallationPath;
+
+ for (int i = 0; i < segments.Length - 1; i++)
+ {
+ if (!Directory.Exists(currentDir))
{
- var sourcePath = ResolveSourcePathWithBackup(executablePath, executableName);
- await builder.AddGameInstallationFileAsync(executableName, sourcePath, isExecutable: true);
+ return null;
}
- // Add common game files including DLLs and .big archives which are required for the game to run
- var commonFiles = new[]
+ var segment = segments[i];
+ if (segment == "." || segment == "..")
{
- "*.exe",
- "*.dll",
- "*.dat",
- "*.ini",
- "*.cfg",
- "*.big", // Essential: Archive files containing game assets, textures, audio, etc.
- "*.txt", // Essential: Text files like steam_appid.txt
- };
+ return null;
+ }
+
+ var matchingDir = Directory.EnumerateDirectories(currentDir)
+ .FirstOrDefault(d => string.Equals(Path.GetFileName(d), segment, StringComparison.OrdinalIgnoreCase));
- foreach (var pattern in commonFiles)
+ if (matchingDir == null)
{
- try
- {
- var files = Directory.GetFiles(installationPath, pattern, SearchOption.TopDirectoryOnly);
- foreach (var file in files)
- {
- var relativePath = Path.GetFileName(file);
+ return null;
+ }
- // Skip backup files and the proxy launcher itself
- if (ShouldSkipFile(relativePath))
- {
- continue;
- }
+ currentDir = matchingDir;
+ }
- // Skip the main executable as it was already added with backup handling
- if (relativePath.Equals(executableName, StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
+ if (!Directory.Exists(currentDir))
+ {
+ return null;
+ }
- await builder.AddGameInstallationFileAsync(relativePath, file);
- }
+ var targetFileName = segments[^1];
+ return Directory.EnumerateFiles(currentDir)
+ .FirstOrDefault(f => string.Equals(Path.GetFileName(f), targetFileName, StringComparison.OrdinalIgnoreCase));
+ }
+
+ ///
+ /// Counts extra non-vanilla files in the installation directory that are excluded from the manifest.
+ ///
+ private static int CountExtraNonVanillaFiles(string installationPath, ISet authoritativePaths)
+ {
+ try
+ {
+ var localFiles = Directory.GetFiles(installationPath, "*", SearchOption.AllDirectories);
+ int extraCount = 0;
+
+ foreach (var localFile in localFiles)
+ {
+ var relPath = Path.GetRelativePath(installationPath, localFile).Replace('\\', '/');
+ if (ShouldSkipFile(relPath))
+ {
+ continue;
}
- catch (Exception ex)
+
+ if (!authoritativePaths.Contains(relPath))
{
- logger.LogWarning(ex, "Failed to enumerate files with pattern {Pattern} at {InstallationPath}", pattern, installationPath);
+ extraCount++;
}
}
- // Add all subdirectories except known non-game directories
- // PRIORITY: Use CSV-based manifest generation if available
- await AddFilesFromCsvAsync(builder, installationPath, gameType);
-
- logger.LogInformation("Completed manifest generation for {GameType}: {TotalFiles} files added", gameType, _fileCount);
- logger.LogDebug("Added game files to manifest for {GameType} at {InstallationPath}", gameType, installationPath);
+ return extraCount;
}
- catch (Exception ex)
+ catch (IOException)
{
- logger.LogError(ex, "Error adding game files to manifest");
+ return 0;
+ }
+ catch (UnauthorizedAccessException)
+ {
+ return 0;
}
}
///
- /// Recursively adds all files from a directory to the manifest.
+ /// Adds authoritative vanilla game files to a manifest builder using the CSV catalog authority.
///
/// The manifest builder.
- /// The root installation path.
- /// The directory to scan.
+ /// The installation path.
+ /// The game type.
+ /// Optional manifest version.
+ /// Optional explicit language code.
/// A task representing the asynchronous operation.
- private async Task AddDirectoryFilesRecursivelyAsync(IContentManifestBuilder builder, string installationPath, string directoryPath)
+ private async Task AddGameFilesToManifest(
+ IContentManifestBuilder builder,
+ string installationPath,
+ GameType gameType,
+ string? manifestVersion,
+ string? language)
{
- try
+ _fileCount = 0;
+
+ var detectedLanguage = string.IsNullOrWhiteSpace(language)
+ ? await _languageDetector.DetectAsync(installationPath)
+ : language;
+
+ var normalizedLanguage = ContentSearchQuery.NormalizeLanguage(detectedLanguage);
+ string version;
+ if (!string.IsNullOrWhiteSpace(manifestVersion) && manifestVersion != "0")
{
- // Add all files in this directory (run synchronously on background thread)
- var files = await Task.Run(() => Directory.GetFiles(directoryPath, "*.*", SearchOption.TopDirectoryOnly));
- foreach (var file in files)
- {
- var relativePath = Path.GetRelativePath(installationPath, file);
+ version = manifestVersion;
+ }
+ else
+ {
+ version = gameType == GameType.Generals
+ ? ManifestConstants.GeneralsManifestVersion
+ : ManifestConstants.ZeroHourManifestVersion;
+ }
- // Skip backup files and the proxy launcher itself
- if (ShouldSkipFile(relativePath))
- {
- continue;
- }
+ logger.LogInformation(
+ "Starting authoritative manifest generation for {GameType} v{Version} ({Language}) at {InstallationPath}",
+ gameType,
+ version,
+ normalizedLanguage,
+ installationPath);
- await builder.AddGameInstallationFileAsync(relativePath, file);
+ var authoritativeEntries = await GetAuthoritativeEntriesAsync(gameType, version, normalizedLanguage);
- // Report progress every 50 files
- _fileCount++;
- if (_fileCount % 50 == 0)
- {
- logger.LogInformation("Scanning game files: {FileCount} files processed...", _fileCount);
- }
+ if (authoritativeEntries.Count == 0)
+ {
+ logger.LogWarning(
+ "No authoritative CSV entries found for {GameType} v{Version} ({Language})",
+ gameType,
+ version,
+ normalizedLanguage);
+ return;
+ }
+
+ var authoritativePathSet = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var entry in authoritativeEntries)
+ {
+ if (string.IsNullOrWhiteSpace(entry.RelativePath))
+ {
+ continue;
}
- // Recursively process subdirectories (run synchronously on background thread)
- var subdirectories = await Task.Run(() => Directory.GetDirectories(directoryPath, "*", SearchOption.TopDirectoryOnly));
- foreach (var subdir in subdirectories)
+ authoritativePathSet.Add(entry.RelativePath);
+
+ var resolvedFilePath = FindFileCaseInsensitive(installationPath, entry.RelativePath);
+ if (resolvedFilePath == null || !File.Exists(resolvedFilePath))
{
- await AddDirectoryFilesRecursivelyAsync(builder, installationPath, subdir);
+ if (entry.IsRequired)
+ {
+ logger.LogWarning(
+ "Required vanilla file missing from installation: {RelativePath}",
+ entry.RelativePath);
+ }
+
+ continue;
}
+
+ var sourcePath = ResolveSourcePathWithBackup(resolvedFilePath, entry.RelativePath);
+ var isExecutable = ExecutableFileClassifier.RequiresExecutePermission(entry.RelativePath, sourcePath);
+
+ var catalogHash = !string.IsNullOrWhiteSpace(entry.Sha256)
+ ? entry.Sha256
+ : await hashProvider.ComputeFileHashAsync(sourcePath);
+
+ var catalogSize = entry.Size > 0
+ ? entry.Size
+ : new FileInfo(sourcePath).Length;
+
+ await builder.AddGameInstallationFileAsync(
+ entry.RelativePath,
+ sourcePath,
+ isExecutable,
+ permissions: null,
+ hash: catalogHash,
+ size: catalogSize,
+ isRequired: entry.IsRequired);
+
+ _fileCount++;
}
- catch (Exception ex)
- {
- logger.LogWarning(ex, "Failed to recursively add files from {DirectoryPath}", directoryPath);
- }
+
+ // Exclude extra/non-vanilla files from the core manifest to keep it completely pristine
+ var extraFileCount = CountExtraNonVanillaFiles(installationPath, authoritativePathSet);
+ logger.LogInformation(
+ "Completed authoritative manifest generation for {GameType}: {TotalFiles} vanilla files added, {ExtraCount} extra non-vanilla files excluded",
+ gameType,
+ _fileCount,
+ extraFileCount);
}
///
@@ -760,7 +933,7 @@ private async Task AddGeneralsOnlineClientFilesToManifest(IContentManifestBuilde
/// The installation path.
/// The game type.
/// The full path to the game executable.
- /// The publisher name.
+ /// The publisher name (for publisher-specific logic).
/// A task representing the asynchronous operation.
private async Task AddClientFilesToManifest(IContentManifestBuilder builder, string installationPath, GameType gameType, string executablePath, string publisherName)
{
@@ -881,98 +1054,169 @@ private async Task AddClientFilesToManifest(IContentManifestBuilder builder, str
}
///
- /// Adds files to the manifest using a CSV source of truth.
+ /// Resolves authoritative CSV entries for the specified game type, version, and language.
///
- private async Task AddFilesFromCsvAsync(IContentManifestBuilder builder, string installationPath, GameType gameType)
+ private async Task> GetAuthoritativeEntriesAsync(
+ GameType gameType,
+ string version,
+ string language)
{
- try
+ var gameTypeStr = gameType == GameType.ZeroHour ? CsvConstants.ZeroHourGameType : CsvConstants.GeneralsGameType;
+ var csvFileName = GetCatalogFileName(gameType, version);
+ if (string.IsNullOrEmpty(csvFileName))
{
- var csvResourceName = gameType == GameType.Generals ? "GenHub.Core.Assets.Manifests.generals.csv" : "GenHub.Core.Assets.Manifests.zerohour.csv";
- var assembly = Assembly.Load("GenHub.Core");
- using var stream = assembly.GetManifestResourceStream(csvResourceName);
-
- if (stream == null)
- {
- logger.LogWarning("Embedded resource {ResourceName} not found", csvResourceName);
- return false;
- }
-
- using var reader = new StreamReader(stream);
- var config = new CsvConfiguration(CultureInfo.InvariantCulture)
- {
- HasHeaderRecord = true,
- };
- using var csv = new CsvReader(reader, config);
-
- var records = csv.GetRecords().ToList();
- var installationFiles = Directory.GetFiles(installationPath, "*", SearchOption.AllDirectories)
- .Select(f => Path.GetRelativePath(installationPath, f))
- .ToHashSet(StringComparer.OrdinalIgnoreCase);
+ logger.LogWarning("No authoritative CSV catalog configured for {GameType} version {Version}", gameType, version);
+ return [];
+ }
- logger.LogInformation("Processing {Count} entries from CSV for {GameType}", records.Count, gameType);
+ var csvRemoteUrl = gameType == GameType.ZeroHour ? CsvConstants.DefaultZeroHourCsvUrl : CsvConstants.DefaultGeneralsCsvUrl;
- foreach (var record in records)
+ // 1. Try CSV resolver if available
+ if (_resolvedCsvResolver != null)
+ {
+ try
{
- if (string.IsNullOrEmpty(record.RelativePath)) continue;
-
- var finalPath = record.RelativePath;
- var found = false;
-
- // 1. Check if the exact file exists
- if (installationFiles.Contains(finalPath))
+ var searchResult = new ContentSearchResult
{
- found = true;
- }
+ Id = $"csvregistry.gameinstallation.{gameTypeStr.ToLowerInvariant()}{version.Replace(".", string.Empty)}{language.ToLowerInvariant()}",
+ Name = $"{gameTypeStr} {version} ({language})",
+ Version = version,
+ TargetGame = gameType,
+ ContentType = ContentType.GameInstallation,
+ SourceUrl = csvRemoteUrl,
+ ResolverId = CsvConstants.ResolverId,
+ ResolverMetadata =
+ {
+ [CsvConstants.GameTypeMetadataKey] = gameTypeStr,
+ [CsvConstants.VersionMetadataKey] = version,
+ [CsvConstants.LanguageMetadataKey] = language,
+ [CsvConstants.CsvUrlMetadataKey] = csvFileName,
+ },
+ };
- // 2. If it's language-specific and exact file NOT found, try to resolve other language variants
- else if (!string.IsNullOrEmpty(record.Language))
+ var resolveResult = await _resolvedCsvResolver.ResolveAsync(searchResult);
+ if (resolveResult.Success && resolveResult.Data?.Files != null && resolveResult.Data.Files.Count > 0)
{
- // Attempt to find any language-pivoted version of this file
- foreach (var lang in SupportedLanguages)
+ logger.LogDebug(
+ "Resolved {Count} authoritative files via CSV resolver for {GameType} v{Version} ({Language})",
+ resolveResult.Data.Files.Count,
+ gameType,
+ version,
+ language);
+
+ return resolveResult.Data.Files.Select(f => new CsvCatalogEntry
{
- var pivotedPath = record.RelativePath.Replace(record.Language, lang, StringComparison.OrdinalIgnoreCase);
- if (installationFiles.Contains(pivotedPath))
- {
- finalPath = pivotedPath;
- found = true;
- logger.LogDebug("Resolved language file {Original} to {Pivoted}", record.RelativePath, pivotedPath);
- break;
- }
- }
+ RelativePath = f.RelativePath,
+ Size = f.Size,
+ Sha256 = f.Hash,
+ GameType = gameTypeStr,
+ Language = language,
+ IsRequired = f.IsRequired,
+ DownloadUrl = f.DownloadUrl,
+ }).ToList();
}
+ }
+ catch (HttpRequestException ex)
+ {
+ logger.LogWarning(ex, "Failed to resolve CSV catalog via HTTP for {GameType} ({Language}), falling back to local/embedded registry", gameType, language);
+ }
+ catch (IOException ex)
+ {
+ logger.LogWarning(ex, "Failed to resolve CSV catalog via I/O for {GameType} ({Language}), falling back to local/embedded registry", gameType, language);
+ }
+ catch (InvalidOperationException ex)
+ {
+ logger.LogWarning(ex, "Failed to resolve CSV catalog via resolver for {GameType} ({Language}), falling back to local/embedded registry", gameType, language);
+ }
+ }
- if (found)
- {
- var fullPath = Path.Combine(installationPath, finalPath);
-
- fullPath = ResolveSourcePathWithBackup(fullPath, finalPath);
+ // 2. Fallback to embedded assembly or local file
+ return LoadAuthoritativeEntriesFromFallback(gameType, version, language, csvFileName);
+ }
- // .dat is data, not code. It was previously marked executable because
- // the Steam layout launches game.dat through a proxy, which is a launch
- // strategy rather than a property of the file, and it forced
- // SteamManifestPatcher to keep flipping the flag by hand.
- var isExecutable = ExecutableFileClassifier.RequiresExecutePermission(finalPath, fullPath);
+ ///
+ /// Loads authoritative CSV entries from embedded assembly assets or local registry files.
+ ///
+ private IReadOnlyList LoadAuthoritativeEntriesFromFallback(
+ GameType gameType,
+ string version,
+ string language,
+ string csvFileName)
+ {
+ var gameTypeStr = gameType == GameType.ZeroHour ? CsvConstants.ZeroHourGameType : CsvConstants.GeneralsGameType;
- await builder.AddGameInstallationFileAsync(finalPath, fullPath, isExecutable);
- _fileCount++;
+ // Try embedded resource from GenHub.Core
+ try
+ {
+ var assembly = typeof(CsvConstants).Assembly;
+ var resourceName = $"{CsvConstants.EmbeddedResourceNamespace}.{csvFileName}";
+ using var stream = assembly.GetManifestResourceStream(resourceName);
+ if (stream != null)
+ {
+ using var reader = new StreamReader(stream);
+ using var csv = new CsvReader(reader, CsvConfig);
+ var records = csv.GetRecords().ToList();
+ var filtered = FilterEntriesByGameAndLanguage(records, gameTypeStr, language);
+ if (filtered.Count > 0)
+ {
+ logger.LogDebug("Loaded {Count} authoritative entries from embedded resource {Resource}", filtered.Count, resourceName);
+ return filtered;
}
- else
+ }
+ }
+ catch (IOException ex)
+ {
+ logger.LogWarning(ex, "I/O failure loading authoritative CSV from embedded resource for {GameType}", gameType);
+ }
+ catch (FormatException ex)
+ {
+ logger.LogWarning(ex, "Format error loading authoritative CSV from embedded resource for {GameType}", gameType);
+ }
+ catch (CsvHelperException ex)
+ {
+ logger.LogWarning(ex, "CSV parse error loading authoritative CSV from embedded resource for {GameType}", gameType);
+ }
+
+ // Try local disk path if running from repo or development tree
+ try
+ {
+ var possiblePaths = new[]
+ {
+ Path.Combine(AppContext.BaseDirectory, "docs", CsvConstants.RegistryDocsFolder, csvFileName),
+ Path.Combine(Directory.GetCurrentDirectory(), "docs", CsvConstants.RegistryDocsFolder, csvFileName),
+ Path.Combine(AppContext.BaseDirectory, csvFileName),
+ };
+
+ foreach (var path in possiblePaths)
+ {
+ if (File.Exists(path))
{
- // If it's a core file (no language), log as missing
- if (string.IsNullOrEmpty(record.Language))
+ using var reader = new StreamReader(path);
+ using var csv = new CsvReader(reader, CsvConfig);
+ var records = csv.GetRecords().ToList();
+ var filtered = FilterEntriesByGameAndLanguage(records, gameTypeStr, language);
+ if (filtered.Count > 0)
{
- logger.LogDebug("Core file {File} missing from installation", finalPath);
+ logger.LogDebug("Loaded {Count} authoritative entries from local path {Path}", filtered.Count, path);
+ return filtered;
}
}
}
-
- return true;
}
- catch (Exception ex)
+ catch (IOException ex)
+ {
+ logger.LogWarning(ex, "I/O failure loading authoritative CSV from local disk for {GameType}", gameType);
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ logger.LogWarning(ex, "Access denied loading authoritative CSV from local disk for {GameType}", gameType);
+ }
+ catch (CsvHelperException ex)
{
- logger.LogError(ex, "Failed to add files from CSV for {GameType}", gameType);
- return false;
+ logger.LogWarning(ex, "CSV parse error loading authoritative CSV from local disk for {GameType}", gameType);
}
+
+ return [];
}
///
@@ -987,22 +1231,13 @@ private string ResolveSourcePathWithBackup(string filePath, string manifestFileN
return backupPath;
}
- return filePath;
- }
+ var legacyBackupPath = filePath + ".bak";
+ if (File.Exists(legacyBackupPath))
+ {
+ logger.LogInformation("Using backup file {Backup} as source for {File} in manifest", Path.GetFileName(legacyBackupPath), manifestFileName);
+ return legacyBackupPath;
+ }
- ///
- /// Represents a file entry in the manifest CSV.
- ///
- private class ManifestFileEntry
- {
- ///
- /// Gets or sets the relative path of the file.
- ///
- public string RelativePath { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the language of the file (optional).
- ///
- public string Language { get; set; } = string.Empty;
+ return filePath;
}
}