From d05b5ca4b624108eb9fcac7b5f8b68626fd60df9 Mon Sep 17 00:00:00 2001 From: Nes370 Date: Mon, 22 Jul 2024 09:50:53 -0700 Subject: [PATCH] Added CSV files for track metadata --- pack_bgm.csv | 21 + pack_cheer.csv | 0 pack_dj_menu.csv | 1162 ++++++++++++++++++ src/goblincave/gitea/nes/AudioExtractor.java | 942 +++++++------- 4 files changed, 1693 insertions(+), 432 deletions(-) create mode 100644 pack_bgm.csv create mode 100644 pack_cheer.csv create mode 100644 pack_dj_menu.csv diff --git a/pack_bgm.csv b/pack_bgm.csv new file mode 100644 index 0000000..e5f9a6a --- /dev/null +++ b/pack_bgm.csv @@ -0,0 +1,21 @@ +Track Title Artist Album +1 Explorers Hiroshi Okubo Green Album +2 Valley of the Mind SamplingMasters MEGA Green Album +3 Nitro Mantra SamplingMasters AYA Green Album +4 I Want You SamplingMasters AYA Green Album +5 Road Mauler Rio Hamamoto Green Album +6 Floodlight sanodg White Album +7 Drift Psychosis Hiroshi Okubo White Album +8 Sue?o Del Mar Asuka Sakai White Album +9 Trail of Light J99 White Album +10 Ultra Cruise Tetsukazu Nakanishi White Album +11 Highway Fusion SamplingMasters MEGA Black Album +12 Acid Eutron #001 Acid Eutron Black Album +13 Radiance sanodg Black Album +14 Photon Field Koji Nakagawa Black Album +15 Galactic Life "Kohta ""SOLIDSTATE"" Takahashi" Black Album +16 Run PAC-MAN Run! Akitaka Tohyama Black Album +17 +18 +19 +20 diff --git a/pack_cheer.csv b/pack_cheer.csv new file mode 100644 index 0000000..e69de29 diff --git a/pack_dj_menu.csv b/pack_dj_menu.csv new file mode 100644 index 0000000..d97fca9 --- /dev/null +++ b/pack_dj_menu.csv @@ -0,0 +1,1162 @@ +Track,Speaker,Transcript,Context +1,Normal,, +2,Normal,, +3,Normal,, +4,Normal,, +5,Normal,, +6,Normal,, +7,Normal,, +8,Normal,, +9,Normal,, +10,Normal,, +11,Normal,, +12,Normal,, +13,Normal,, +14,Normal,, +15,Normal,, +16,Normal,, +17,Normal,, +18,Normal,, +19,Normal,, +20,Normal,, +21,Normal,, +22,Normal,, +23,Normal,, +24,Normal,, +25,Normal,, +26,Normal,, +27,Normal,, +28,Normal,, +29,Normal,, +30,Normal,, +31,Normal,, +32,Normal,, +33,Normal,, +34,Normal,, +35,Normal,, +36,Normal,, +37,Normal,, +38,Normal,, +39,Normal,, +40,Normal,, +41,Normal,, +42,Normal,, +43,Normal,, +44,Normal,, +45,Normal,, +46,Normal,, +47,Normal,, +48,Normal,, +49,Normal,, +50,Normal,, +51,Normal,, +52,Normal,, +53,Normal,, +54,Normal,, +55,Normal,, +56,Normal,, +57,Normal,, +58,Normal,, +59,Normal,, +60,Normal,, +61,Normal,, +62,Normal,, +63,Normal,, +64,Normal,, +65,Normal,, +66,Normal,, +67,Normal,, +68,Normal,, +69,Normal,, +70,Normal,, +71,Normal,, +72,Normal,, +73,Normal,, +74,Normal,, +75,Normal,, +76,Normal,, +77,Normal,, +78,Normal,, +79,Normal,, +80,Normal,, +81,Normal,, +82,Normal,, +83,Normal,, +84,Normal,, +85,Normal,, +86,Normal,, +87,Normal,, +88,Normal,, +89,Normal,, +90,Normal,, +91,Normal,, +92,Normal,, +93,Normal,, +94,Normal,, +95,Normal,, +96,Normal,, +97,Normal,, +98,Normal,, +99,Normal,, +100,Normal,, +101,Normal,, +102,Normal,, +103,Normal,, +104,Normal,, +105,Normal,, +106,Normal,, +107,Normal,, +108,Normal,, +109,Normal,, +110,Normal,, +111,Normal,, +112,Normal,, +113,Normal,, +114,Normal,, +115,Normal,, +116,Normal,, +117,Normal,, +118,Normal,, +119,Normal,, +120,Normal,, +121,Normal,, +122,Normal,, +123,Normal,, +124,Normal,, +125,Normal,, +126,Normal,, +127,Normal,, +128,Normal,, +129,Normal,, +130,Normal,, +131,Normal,, +132,Normal,, +133,Normal,, +134,Normal,, +135,Normal,, +136,Normal,, +137,Normal,, +138,Normal,, +139,Normal,, +140,Normal,, +141,Normal,, +142,Normal,, +143,Normal,, +144,Normal,, +145,Normal,, +146,Normal,, +147,Normal,, +148,Normal,, +149,Normal,, +150,Normal,, +151,Normal,, +152,Normal,, +153,Normal,, +154,Normal,, +155,Normal,, +156,Normal,, +157,Normal,, +158,Normal,, +159,Normal,, +160,Normal,, +161,Normal,, +162,Normal,, +163,Normal,, +164,Normal,, +165,Normal,, +166,Normal,, +167,Normal,, +168,Normal,, +169,Normal,, +170,Normal,, +171,Normal,, +172,Normal,, +173,Normal,, +174,Normal,, +175,Normal,, +176,Normal,, +177,Normal,, +178,Normal,, +179,Normal,, +180,Normal,, +181,Normal,, +182,Normal,, +183,Normal,, +184,Normal,, +185,Normal,, +186,Normal,, +187,Normal,, +188,Normal,, +189,Normal,, +190,Normal,, +191,Normal,, +192,Normal,, +193,Normal,, +194,Normal,, +195,Normal,, +196,Normal,, +197,Normal,, +198,Normal,, +199,Normal,, +200,Normal,, +201,Normal,, +202,Normal,, +203,Normal,, +204,Normal,, +205,Normal,, +206,Normal,, +207,Normal,, +208,Normal,, +209,Normal,, +210,Normal,, +211,Normal,, +212,Normal,, +213,Normal,, +214,Normal,, +215,Normal,, +216,Normal,, +217,Normal,, +218,Normal,, +219,Normal,, +220,Normal,, +221,Normal,, +222,Normal,, +223,Normal,, +224,Normal,, +225,Normal,, +226,Normal,, +227,Normal,, +228,Normal,, +229,Normal,, +230,Normal,, +231,Normal,, +232,Normal,, +233,Normal,, +234,Normal,, +235,Normal,, +236,Normal,, +237,Normal,, +238,Normal,, +239,Normal,, +240,Normal,, +241,Normal,, +242,Normal,, +243,Normal,, +244,Normal,, +245,Normal,, +246,Normal,, +247,Normal,, +248,Normal,, +249,Normal,, +250,Normal,, +251,Normal,, +252,Normal,, +253,Normal,, +254,Normal,, +255,Normal,, +256,Normal,, +257,Normal,, +258,Normal,, +259,Normal,, +260,Normal,, +261,Normal,, +262,Normal,, +263,Normal,, +264,Normal,, +265,Normal,, +266,Normal,, +267,Normal,, +268,Normal,, +269,Normal,, +270,Normal,, +271,Normal,, +272,Normal,, +273,Normal,, +274,Normal,, +275,Normal,, +276,Normal,, +277,Normal,, +278,Normal,, +279,Normal,, +280,Normal,, +281,Normal,, +282,Normal,, +283,Normal,, +284,Normal,, +285,Normal,, +286,Normal,, +287,Normal,, +288,Normal,, +289,Normal,, +290,Normal,, +291,Normal,, +292,Normal,, +293,Normal,, +294,Normal,, +295,Normal,, +296,Normal,, +297,Normal,, +298,Normal,, +299,Normal,, +300,Normal,, +301,Normal,, +302,Normal,, +303,Normal,, +304,Normal,, +305,Normal,, +306,Normal,, +307,Normal,, +308,Normal,, +309,Normal,, +310,Normal,, +311,Normal,, +312,Normal,, +313,Normal,, +314,Normal,, +315,Normal,, +316,Normal,, +317,Normal,, +318,Normal,, +319,Normal,, +320,Normal,, +321,Normal,, +322,Normal,, +323,Normal,, +324,Normal,, +325,Normal,, +326,Normal,, +327,Normal,, +328,Normal,, +329,Normal,, +330,Normal,, +331,Normal,, +332,Normal,, +333,Normal,, +334,Normal,, +335,Normal,, +336,Normal,, +337,Normal,, +338,Normal,, +339,Normal,, +340,Normal,, +341,Normal,, +342,Normal,, +343,Normal,, +344,Normal,, +345,Normal,, +346,Normal,, +347,Normal,, +348,Normal,, +349,Normal,, +350,Normal,, +351,Normal,, +352,Normal,, +353,Normal,, +354,Normal,, +355,Normal,, +356,Normal,, +357,Normal,, +358,Normal,, +359,Normal,, +360,Normal,, +361,Normal,, +362,Normal,, +363,Normal,, +364,Normal,, +365,Normal,, +366,Normal,, +367,Normal,, +368,Normal,, +369,Normal,, +370,Normal,, +371,Normal,, +372,Normal,, +373,Normal,, +374,Normal,, +375,Normal,, +376,Normal,, +377,Normal,, +378,Normal,, +379,Normal,, +380,Normal,, +381,Normal,, +382,Normal,, +383,Normal,, +384,Normal,, +385,Normal,, +386,Normal,, +387,Normal,, +388,Normal,, +389,Normal,, +390,Normal,, +391,Normal,, +392,Normal,, +393,Normal,, +394,Normal,, +395,Normal,, +396,Normal,, +397,Normal,, +398,Normal,, +399,Normal,, +400,Normal,, +401,Normal,, +402,Normal,, +403,Normal,, +404,Normal,, +405,Normal,, +406,Normal,, +407,Normal,, +408,Normal,, +409,Normal,, +410,Normal,, +411,Normal,, +412,Normal,, +413,Normal,, +414,Normal,, +415,Normal,, +416,Normal,, +417,Normal,, +418,Normal,, +419,Normal,, +420,Normal,, +421,Normal,, +422,Normal,, +423,Normal,, +424,Normal,, +425,Normal,, +426,Normal,, +427,Normal,, +428,Normal,, +429,Normal,, +430,Normal,, +431,Normal,, +432,Normal,, +433,Normal,, +434,Normal,, +435,Normal,, +436,Normal,, +437,Normal,, +438,Normal,, +439,Normal,, +440,Normal,, +441,Normal,, +442,Normal,, +443,Normal,, +444,Normal,, +445,Normal,, +446,Normal,, +447,Normal,, +448,Normal,, +449,Normal,, +450,Normal,, +451,Normal,, +452,Normal,, +453,Normal,, +454,Normal,, +455,Normal,, +456,Normal,, +457,Normal,, +458,Normal,, +459,Normal,, +460,Normal,, +461,Normal,, +462,Normal,, +463,Normal,, +464,Normal,, +465,Normal,, +466,Normal,, +467,Normal,, +468,Normal,, +469,Normal,, +470,Normal,, +471,Normal,, +472,Normal,, +473,Normal,, +474,Normal,, +475,Normal,, +476,Normal,, +477,Normal,, +478,Normal,, +479,Normal,, +480,Normal,, +481,Normal,, +482,Normal,, +483,Normal,, +484,Normal,, +485,Normal,, +486,Normal,, +487,Normal,, +488,Normal,, +489,Normal,, +490,Normal,, +491,Normal,, +492,Normal,, +493,Normal,, +494,Normal,, +495,Normal,, +496,Normal,, +497,Normal,, +498,Normal,, +499,Normal,, +500,Normal,, +501,Normal,, +502,Normal,, +503,Normal,, +504,Normal,, +505,Normal,, +506,Normal,, +507,Normal,, +508,Normal,, +509,Normal,, +510,Normal,, +511,Normal,, +512,Normal,, +513,Normal,, +514,Normal,, +515,Normal,, +516,Normal,, +517,Normal,, +518,Normal,, +519,Normal,, +520,Normal,, +521,Normal,, +522,Normal,, +523,Normal,, +524,Normal,, +525,Normal,, +526,Normal,, +527,Normal,, +528,Normal,, +529,Normal,, +530,Normal,, +531,Normal,, +532,Normal,, +533,Normal,, +534,Normal,, +535,Normal,, +536,Normal,, +537,Normal,, +538,Normal,, +539,Normal,, +540,Normal,, +541,Normal,, +542,Normal,, +543,Normal,, +544,Normal,, +545,Normal,, +546,Normal,, +547,Normal,, +548,Normal,, +549,Normal,, +550,Normal,, +551,Normal,, +552,Normal,, +553,Normal,, +554,Normal,, +555,Normal,, +556,Normal,, +557,Normal,, +558,Normal,, +559,Normal,, +560,Normal,, +561,Normal,, +562,Normal,, +563,Normal,, +564,Normal,, +565,Normal,, +566,Normal,, +567,Normal,, +568,Normal,, +569,Normal,, +570,Normal,, +571,Normal,, +572,Normal,, +573,Normal,, +574,Normal,, +575,Normal,, +576,Normal,, +577,Normal,, +578,Normal,, +579,Normal,, +580,Normal,, +581,Normal,, +582,Normal,, +583,Normal,, +584,Normal,, +585,Normal,, +586,Normal,, +587,Normal,, +588,Normal,, +589,Normal,, +590,Normal,, +591,Normal,, +592,Normal,, +593,Normal,, +594,Normal,, +595,Normal,, +596,Normal,, +597,Normal,, +598,Normal,, +599,Normal,, +600,Normal,, +601,Normal,, +602,Normal,, +603,Normal,, +604,Normal,, +605,Normal,, +606,Normal,, +607,Normal,, +608,Normal,, +609,Normal,, +610,Normal,, +611,Normal,, +612,Normal,, +613,Normal,, +614,Normal,, +615,Normal,, +616,Normal,, +617,Normal,, +618,Normal,, +619,Normal,, +620,Normal,, +621,Normal,, +622,Normal,, +623,Normal,, +624,Normal,, +625,Normal,, +626,Normal,, +627,Normal,, +628,Normal,, +629,Normal,, +630,Normal,, +631,Normal,, +632,Normal,, +633,Normal,, +634,Normal,, +635,Normal,, +636,Normal,, +637,Normal,, +638,Normal,, +639,Normal,, +640,Normal,, +641,Normal,, +642,Normal,, +643,Normal,, +644,Normal,, +645,Normal,, +646,Normal,, +647,Normal,, +648,Normal,, +649,Normal,, +650,Normal,, +651,Normal,, +652,Normal,, +653,Normal,, +654,Normal,, +655,Normal,, +656,Normal,, +657,Normal,, +658,Normal,, +659,Normal,, +660,Normal,, +661,Normal,, +662,Normal,, +663,Normal,, +664,Normal,, +665,Normal,, +666,Normal,, +667,Normal,, +668,Normal,, +669,Normal,, +670,Normal,, +671,Normal,, +672,Normal,, +673,Normal,, +674,Normal,, +675,Normal,, +676,Normal,, +677,Normal,, +678,Normal,, +679,Normal,, +680,Normal,, +681,Normal,, +682,Normal,, +683,Normal,, +684,Normal,, +685,Normal,, +686,Normal,, +687,Normal,, +688,Normal,, +689,Normal,, +690,Normal,, +691,Normal,, +692,Normal,, +693,Normal,, +694,Normal,, +695,Normal,, +696,Normal,, +697,Normal,, +698,Normal,, +699,Normal,, +700,Normal,, +701,Normal,, +702,Normal,, +703,Normal,, +704,Normal,, +705,Normal,, +706,Normal,, +707,Normal,, +708,Normal,, +709,Normal,, +710,Normal,, +711,Normal,, +712,Normal,, +713,Normal,, +714,Normal,, +715,Normal,, +716,Normal,, +717,Normal,, +718,Normal,, +719,Normal,, +720,Normal,, +721,Normal,, +722,Normal,, +723,Normal,, +724,Normal,, +725,Normal,, +726,Normal,, +727,Normal,, +728,Normal,, +729,Normal,, +730,Normal,, +731,Normal,, +732,Normal,, +733,Normal,, +734,Normal,, +735,Normal,, +736,Normal,, +737,Normal,, +738,Normal,, +739,Normal,, +740,Normal,, +741,Normal,, +742,Normal,, +743,Normal,, +744,Normal,, +745,Normal,, +746,Normal,, +747,Extra,Welcome to the world of Ridge Racer.,Main menu +748,Extra,Let's begin., +749,Extra,Are you ready?, +750,Extra,Ridge Racer,Title screen +751,Extra,Ridge Racer,Title screen +752,Extra,A new feature has been unlocked., +753,Extra,Ridge Racer World Xplorer,World Xplorer menu +754,Extra,Single Race,Single Race menu +755,Extra,Global Time Attack,Global Time Attack menu +756,Extra,Multi Battle,Multi Battle menu +757,Extra,Online Battle,Online Battle menu +758,Extra,Versus Battle,Versus Battle menu +759,Extra,Garage,Garage menu +760,Extra,Save and Load,Save and Load menu +761,Extra,Proceed with save?, +762,Extra,Proceed with load?, +763,Extra,Options,Options menu +764,Extra,Check, +765,Extra,Discovered new route., +766,Extra,Is this okay?, +767,Extra,Lakeshore Drive,Map selection +768,Extra,Highland Cliffs,Map selection +769,Extra,Laketop Parkway,Map selection +770,Extra,Harborline 765,Map selection +771,Extra,Midtown Parkway,Map selection +772,Extra,Island Circle,Map selection +773,Extra,Southbay Docks,Map selection +774,Extra,Aviator Loop,Map selection +775,Extra,Airport Lap,Map selection +776,Extra,Downtown Rave City,Map selection +777,Extra,Rave City Riverfront,Map selection +778,Extra,Crossbay Tunnel,Map selection +779,Extra,Seacrest District,Map selection +780,Extra,Surfside Resort,Map selection +781,Extra,Sunset Heights,Map selection +782,Extra,Kamata Fiera,Class 1 vehicle selection +783,Extra,Âge Abeille,Class 1 vehicle selection +784,Extra,Âge Prophetie,Class 1 vehicle selection +785,Extra,Terrazi Wild Gang,Class 1 vehicle selection +786,Extra,Danver Bayonet,Class 1 vehicle selection +787,Extra,Terrazi Starnose,Class 1 vehicle selection +788,Extra,Assoluto Fatalita,Class 2 vehicle selection +789,Extra,Assoluto Bisonte,Class 2 vehicle selection +790,Extra,Soldat Meltfire,Class 2 vehicle selection +791,Extra,Himmel EO,Class 2 vehicle selection +792,Extra,Gnade Esperanza,Class 2 vehicle selection +793,Extra,Soldat Raggio,Class 2 vehicle selection +794,Extra,Danver Hijack,Class 2 vehicle selection +795,Extra,Kamata Fiera,Class 3 vehicle selection +796,Extra,Kamata Angellus,Special Class vehicle selection +797,Extra,Soldat Crinale,Special Class vehicle selection +798,Extra,Namco Pac-Man,Special Class vehicle selection +799,Extra,Âge Ultranova,Special Class vehicle selection +800,Extra,Terrazi Terrajin,Special Class vehicle selection +801,Extra,Assoluto Pronzione,Special Class vehicle selection +802,Extra,Himmel 490B,Special Class vehicle selection +803,Extra,Gnade G00,Special Class vehicle selection +804,Extra,Danver Bass Cruiser,Special Class vehicle selection +805,Extra,Monstrous,Special Class vehicle selection +806,Extra,You've unlocked a new car!, +807,Extra,You've got a new car!, +808,Extra,Get ready to race., +809,Extra,An online battle notification has arrived., +810,Extra,A notification from a friend has arrived., +811,Extra,You have new mail., +812,Extra,Ridge Racer, +813,Extra,Ridge Racer, +814,Extra,Ridge Racer, +815,Extra,Ridge Racer, +816,Extra,Ridge Racer, +817,Extra,"Welcome to the AV player. Please enjoy great replays, and music.", +818,Extra,Next song, +819,Extra,Next song, +820,Extra,The race will commence at Lakeshore Drive. Are you ready?, +821,Extra,The race will commence at Highland Cliffs. Are you ready?, +822,Extra,The race will commence at Laketop Parkway. Are you ready?, +823,Extra,The race will commence at Harborline 765. Are you ready?, +824,Extra,The race will commence at Midtown Parkway. Are you ready?, +825,Extra,, +826,Extra,, +827,Extra,, +828,Extra,, +829,Extra,, +830,Extra,, +831,Extra,, +832,Extra,, +833,Extra,, +834,Extra,The race will commence at Sunset Heights. Are you ready?, +835,Extra,Three, +836,Extra,Two, +837,Extra,One, +838,Extra,Go, +839,Extra,Good start confirmed., +840,Extra,High speed start confirmed., +841,Extra,Please be careful., +842,Extra,Good luck coming in first., +843,Extra,Good luck achieving the fastest time., +844,Extra,Final lap., +845,Extra,Two laps to go., +846,Extra,Three laps to go., +847,Extra,You are currently in first place., +848,Extra,You are in first place. The second place driver is closing in fast., +849,Extra,You are currently in second place., +850,Extra,You are in second place. Closing in fast on first place., +851,Extra,You are in second place. Keep a close eye on the cars behind you., +852,Extra,You are currently in third place., +853,Extra,You are in third place. Closing in fast on second place., +854,Extra,You are in third place. But keep a close eye on the cars behind you., +855,Extra,You are currently in fourth place., +856,Extra,You are currently in fifth place., +857,Extra,You are currently in sixth place., +858,Extra,You are currently in seventh place., +859,Extra,You are currently in eighth place., +860,Extra,You are currently in ninth place., +861,Extra,You are currently in tenth place., +862,Extra,You are currently below tenth place., +863,Extra,A new record., +864,Extra,Firing nitrous., +865,Extra,Firing double nitrous., +866,Extra,Firing triple nitrous., +867,Extra,Another driver fired his nitrous., +868,Extra,Ready for nitrous., +869,Extra,Ready for double nitrous., +870,Extra,All nitrous are ready to go., +871,Extra,Another driver is closing in., +872,Extra,You have increased your standing., +873,Extra,Take the car ahead to take the lead., +874,Extra,Pass the car ahead to take the lead., +875,Extra,Rear-ended by another car., +876,Extra,Rear-ended at high speed., +877,Extra,Contact with another car., +878,Extra,System damage. System damage., +879,Extra,Contact with another car at high speed., +880,Extra,Confirming- current- con- di- tion…, +881,Extra,Confirming current condition…, +882,Extra,Good drifting confirmed., +883,Extra,Excellent drift., +884,Extra,Excellent drift., +885,Extra,Abnormal drifting detected., +886,Extra,"Nitrous activation during drift, confirmed.", +887,Extra,"360 drift, confirmed.", +888,Extra,Very long jump confirmed., +889,Extra,Coming up on final turn., +890,Extra,You are almost at the finish line., +891,Extra,Finish line ahead., +892,Extra,Finish., +893,Extra,First place. Congratulations!, +894,Extra,Second place., +895,Extra,Third place., +896,Extra,Fourth place., +897,Extra,Fifth place., +898,Extra,Sixth place., +899,Extra,Seventh place., +900,Extra,Eighth place., +901,Extra,Ninth place., +902,Extra,Tenth place., +903,Extra,Eleventh place., +904,Extra,Twelfth place., +905,Extra,Thirteenth place., +906,Extra,Fourteenth place., +907,Extra,Fifteenth place., +908,Extra,Sixteenth place., +909,Extra,Last place., +910,Extra,Proceeding to the next race., +911,Extra,Player One wins., +912,Extra,Player Two wins., +913,Extra,You won!, +914,Extra,You lost!, +915,Extra,Concluded., +916,Extra,A new record., +917,Extra,Replay, +918,Extra,One minute remaining., +919,Extra,Thirty seconds remaining., +920,Extra,Ten seconds remaining., +921,Extra,Five, +922,Extra,Three, +923,Extra,Two, +924,Extra,One, +925,Extra,Time up!, +926,Extra,Time's up!, +927,Extra,An online race notification has arrived., +928,Extra,A notification from a friend has arrived., +929,Extra,You have mail., +930,Extra,You are going the wrong way., +931,Extra,Detecting no response due to sleep or neglect. Are you alright?, +932,Extra,Detecting extreme decrease in motivation. Are you alright?, +933,Extra,You are slightly slower than the record time., +934,Extra,You are slower than the record time., +935,Extra,You are significantly slower than the record time., +936,Extra,Updating track record., +937,Extra,Ultimate charge detected., +938,Heihachi,リッジレーサー, +939,Heihachi,リッジレーサーや, +940,Heihachi,リッジレーサー, +941,Heihachi,リッジレーサー, +942,Heihachi,リッジレーサーじゃ, +943,Heihachi,リッジレーサー, +944,Heihachi,, +945,Heihachi,, +946,Heihachi,, +947,Heihachi,, +948,Heihachi,, +949,Heihachi,, +950,Heihachi,, +951,Heihachi,, +952,Heihachi,, +953,Heihachi,, +954,Heihachi,, +955,Heihachi,, +956,Heihachi,, +957,Heihachi,, +958,Heihachi,, +959,Heihachi,, +960,Heihachi,, +961,Heihachi,, +962,Heihachi,, +963,Heihachi,, +964,Heihachi,, +965,Heihachi,, +966,Heihachi,, +967,Heihachi,, +968,Heihachi,, +969,Heihachi,, +970,Heihachi,, +971,Heihachi,, +972,Heihachi,, +973,Heihachi,, +974,Heihachi,, +975,Heihachi,, +976,Heihachi,, +977,Heihachi,, +978,Heihachi,, +979,Heihachi,, +980,Heihachi,, +981,Heihachi,, +982,Heihachi,, +983,Heihachi,, +984,Heihachi,, +985,Heihachi,, +986,Heihachi,, +987,Heihachi,, +988,Heihachi,, +989,Heihachi,, +990,Heihachi,, +991,Heihachi,, +992,Heihachi,, +993,Heihachi,, +994,Heihachi,, +995,Heihachi,, +996,Heihachi,, +997,Heihachi,, +998,Heihachi,, +999,Heihachi,, +1000,Heihachi,, +1001,Heihachi,, +1002,Heihachi,, +1003,Heihachi,, +1004,Heihachi,, +1005,Heihachi,, +1006,Heihachi,, +1007,Heihachi,, +1008,Heihachi,, +1009,Heihachi,, +1010,Heihachi,, +1011,Heihachi,, +1012,Heihachi,, +1013,Heihachi,, +1014,Heihachi,, +1015,Heihachi,, +1016,Heihachi,, +1017,Heihachi,, +1018,Heihachi,, +1019,Heihachi,, +1020,Heihachi,, +1021,Heihachi,, +1022,Heihachi,, +1023,Heihachi,, +1024,Heihachi,, +1025,Heihachi,, +1026,Heihachi,, +1027,Heihachi,, +1028,Heihachi,, +1029,Heihachi,, +1030,Heihachi,, +1031,Heihachi,, +1032,Heihachi,, +1033,Heihachi,, +1034,Heihachi,, +1035,Heihachi,, +1036,Heihachi,, +1037,Heihachi,, +1038,Heihachi,, +1039,Heihachi,, +1040,Heihachi,, +1041,Heihachi,, +1042,Heihachi,, +1043,Heihachi,, +1044,Heihachi,, +1045,Heihachi,, +1046,Heihachi,, +1047,Heihachi,, +1048,Heihachi,, +1049,Heihachi,, +1050,Heihachi,, +1051,Heihachi,, +1052,Heihachi,, +1053,Heihachi,, +1054,Heihachi,, +1055,Heihachi,, +1056,Heihachi,, +1057,Heihachi,, +1058,Heihachi,, +1059,Heihachi,, +1060,Heihachi,, +1061,Heihachi,, +1062,Heihachi,, +1063,Heihachi,, +1064,Heihachi,, +1065,Heihachi,, +1066,Heihachi,, +1067,Heihachi,, +1068,Heihachi,, +1069,Heihachi,, +1070,Heihachi,, +1071,Heihachi,, +1072,Heihachi,, +1073,Heihachi,, +1074,Heihachi,, +1075,Heihachi,, +1076,Heihachi,, +1077,Heihachi,, +1078,Heihachi,, +1079,Heihachi,, +1080,Heihachi,, +1081,Heihachi,, +1082,Heihachi,, +1083,Heihachi,, +1084,Heihachi,, +1085,Heihachi,, +1086,Heihachi,, +1087,Heihachi,, +1088,Heihachi,, +1089,Heihachi,, +1090,Heihachi,, +1091,Heihachi,, +1092,Heihachi,, +1093,Heihachi,, +1094,Heihachi,, +1095,Heihachi,, +1096,Heihachi,, +1097,Heihachi,, +1098,Heihachi,, +1099,Heihachi,, +1100,Heihachi,, +1101,Heihachi,, +1102,Heihachi,, +1103,Heihachi,, +1104,Heihachi,, +1105,Heihachi,, +1106,Heihachi,, +1107,Heihachi,, +1108,Heihachi,, +1109,Heihachi,, +1110,Heihachi,, +1111,Heihachi,, +1112,Heihachi,, +1113,Heihachi,, +1114,Heihachi,, +1115,Heihachi,, +1116,Heihachi,, +1117,Heihachi,, +1118,Heihachi,, +1119,Heihachi,, +1120,Heihachi,, +1121,Heihachi,, +1122,Heihachi,, +1123,Heihachi,, +1124,Heihachi,, +1125,Heihachi,, +1126,Heihachi,, +1127,Heihachi,, +1128,Heihachi,, +1129,Heihachi,, +1130,Heihachi,, +1131,Heihachi,, +1132,Heihachi,, +1133,Heihachi,, +1134,Heihachi,, +1135,Heihachi,, +1136,Heihachi,, +1137,Heihachi,, +1138,Heihachi,, +1139,Heihachi,, +1140,Heihachi,, +1141,Heihachi,, +1142,Heihachi,, +1143,Heihachi,, +1144,Heihachi,, +1145,Heihachi,, +1146,Heihachi,, +1147,Heihachi,, +1148,Heihachi,, +1149,Heihachi,, +1150,Heihachi,, +1151,Heihachi,, +1152,Heihachi,, +1153,Heihachi,, +1154,Heihachi,, +1155,Heihachi,, +1156,Heihachi,, +1157,Heihachi,, +1158,Normal,, +1159,Normal,, +1160,Normal,, +1161,Normal,, diff --git a/src/goblincave/gitea/nes/AudioExtractor.java b/src/goblincave/gitea/nes/AudioExtractor.java index b9525c5..4d59f11 100644 --- a/src/goblincave/gitea/nes/AudioExtractor.java +++ b/src/goblincave/gitea/nes/AudioExtractor.java @@ -29,19 +29,38 @@ import com.diogonunes.jcolor.Attribute; */ public class AudioExtractor { - /** - * ANSI code to go up one line, followed by a carriage return. - * Useful for overwriting user input - */ - private final static String BACKLINE = "\033[F\r"; - private final static String PALE_YELLOW = "\u001B[93m"; - - private final static int RIFF = 0x52494646; /** * Activate alternate behavior for command line terminal. * Specifically prints things in a narrower width, and overwrites previous lines to add color. */ - private final static boolean CMD = false; + private final static boolean CMD_MODE = false; + + /** + * ANSI code to go up one line, followed by a carriage return. + * Useful for overwriting user input. + */ + private final static String ANSI_BACKLINE = "\033[F\r"; + /** + * ANSI code to switch to beige colored text. + * Useful for highlighting user input. + */ + private final static String ANSI_BEIGE = "\u001B[93m"; + + /** + * Hex value for the ASCII sequence "RIFF". + * Indicates the start of a RIFF header in WAV files. + */ + private final static int ASCII_RIFF = 0x5249_4646; + /** + * Hex value for the ASCII sequence "smpl". + * Indicates the start of a sample chunk in WAV files. + */ + private final static int ASCII_smpl = 0x736D_706C; + /** + * Hex value for the ASCII sequence "data". + * Indicates the start of a data chunk in WAV files. + */ + private final static int ASCII_data = 0x6461_7461; /** * BIN files must first be extracted from game disk to use this program. @@ -60,18 +79,24 @@ public class AudioExtractor { */ public static void main(String[] args) throws URISyntaxException, InterruptedException { - if(CMD) { + // Print Audio Extractor logo + if(CMD_MODE) { + // NARROW LOGO - String adjustment = "█████████ ████████████████████████████████████████████████████ ████████████████████████"; System.out.println(Ansi.colorize(logoFormat("Ridge", true, 8)/*.substring(0, 484) + adjustment*/, Attribute.BRIGHT_RED_TEXT())); Thread.sleep(500); - System.out.println(BACKLINE + BACKLINE + Ansi.colorize(adjustment, Attribute.BRIGHT_RED_TEXT())); + + String adjustment = "█████████ ████████████████████████████████████████████████████ ████████████████████████"; + System.out.println(ANSI_BACKLINE + ANSI_BACKLINE + Ansi.colorize(adjustment, Attribute.BRIGHT_RED_TEXT())); System.out.println(Ansi.colorize(logoFormat("Racer 6", true, 9), Attribute.BRIGHT_RED_TEXT())); Thread.sleep(500); + System.out.println(Ansi.colorize(logoFormat("Audio", true, 8), Attribute.YELLOW_TEXT())); Thread.sleep(500); + System.out.println(Ansi.colorize(logoFormat("Xtractr", true, 8), Attribute.YELLOW_TEXT())); Thread.sleep(500); + } else { // WIDE LOGO System.out.println(Ansi.colorize(logoFormat("Ridge Racer 6"), Attribute.BRIGHT_RED_TEXT())); @@ -80,11 +105,12 @@ public class AudioExtractor { Thread.sleep(500); } + // Print meta info System.out.println("Program:\tRidge Racer 6 Audio Extractor " + Ansi.colorize("v1.0", Attribute.BRIGHT_GREEN_TEXT()) + " by " + Ansi.colorize("Nes", Attribute.TEXT_COLOR(252, 42, 124))); System.out.println("Repository:\t" + Ansi.colorize("https://gitea.goblincave.synology.me/Nes/RR6AudioExtractor", Attribute.CYAN_TEXT())); - // Used for reading user input + // Initialize user input reader BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); // determine which operation to execute @@ -116,7 +142,7 @@ public class AudioExtractor { break; case "print": // print Attribute color = retrieveColor(reader); - String message = retrieveInput(reader, "Please enter your logo message: " + PALE_YELLOW); + String message = retrieveInput(reader, "Please enter your logo message: " + ANSI_BEIGE); System.out.println(Ansi.colorize(logoFormat(message), color)); break; case "exit": // exit @@ -204,418 +230,7 @@ public class AudioExtractor { else extract(packDirectory, extractDirectory, compressBGM); } - - /** - * Helper function to prompt user to select a WAV file. - * - * @param reader - * @return WAV file - */ - private static File retrieveWave(BufferedReader reader) { - - String prompt = "Please provide a WAV file: " + PALE_YELLOW; - - File waveFile = null; - boolean valid = false; - do { - - // Retrieve an audio file - waveFile = retrieveFile(reader, prompt, ".wav"); - - try { - // validate that chosen file is a proper Wave file - RandomAccessFile file = new RandomAccessFile(waveFile, "r"); - // first 4 bytes start with RIFF - if(file.readInt() == RIFF) { - // next 4 bytes contain the size of the file in little-endian representation - file.seek(4); - byte[] buffer = new byte[4]; - file.read(buffer); // write size bytes to buffer - - // calculate expected file size - int headerSize = 8 + buffer[3] - + buffer[2] * (int) Math.pow(16, 2) - + buffer[1] * (int) Math.pow(16, 4) - + buffer[0] * (int) Math.pow(16, 6); - - // file is valid if actual file length matches expected size - valid = headerSize == file.length(); - if(!valid) - System.out.println("File length mismatch. Expected " + headerSize + " bytes, but found " + file.length() + " bytes."); - - } else System.out.println("Invalid RIFF header."); - file.close(); - } catch (IOException e) { - System.out.println("Invalid file."); - } - - } while(!valid); - - return waveFile; - - } - - /** - * Helper function to prompt user to select a file. - * - * @param reader - * @return file - */ - private static File retrieveFile(BufferedReader reader, String prompt, String extension) { - - File file = null; - - String input = null; - boolean valid = false; - do { - System.out.print(prompt); - try { - input = reader.readLine(); - file = new File(input); - if(file.exists()) { - if(file.getName().toLowerCase().endsWith(extension)) { - valid = true; - } else System.out.println("File does not have " + extension.toUpperCase() + " file extension"); - } else System.out.println("File does not exist."); - } catch (IOException e) { - System.out.println(Ansi.colorize("Invalid file.")); - } - } while(!valid); - - return file; - - } - - /** - * Helper function to prompt user to select a color. - * - * @param reader - * @return color attribute - */ - private static Attribute retrieveColor(BufferedReader reader) { - - String prompt = "\nCommand parameters: " + Ansi.colorize("Print", Attribute.BRIGHT_BLUE_TEXT()) - + Ansi.colorize(" [color]", Attribute.BRIGHT_YELLOW_TEXT()) + Ansi.colorize(" [message]", Attribute.BRIGHT_RED_TEXT()) - + "\n" - + "\n" + Ansi.colorize("Colors", Attribute.BRIGHT_YELLOW_TEXT(), Attribute.BOLD()) + ":" - + "\n- " + Ansi.colorize("Default", Attribute.CLEAR()) - + "\n- " + Ansi.colorize("Black", Attribute.BLACK_TEXT()) - + ", " + Ansi.colorize("Gray", Attribute.BRIGHT_BLACK_TEXT()) - + ", " + Ansi.colorize("White", Attribute.BRIGHT_WHITE_TEXT()) - + "\n- " + Ansi.colorize("Indigo", Attribute.TEXT_COLOR(0x7D, 0x1A, 0xFF)) - + ", " + Ansi.colorize("Purple", Attribute.BRIGHT_MAGENTA_TEXT()) - + ", " + Ansi.colorize("Violet", Attribute.TEXT_COLOR(0xEE, 0x82, 0xEE)) - + "\n- " + Ansi.colorize("Red", Attribute.RED_TEXT()) - + ", " + Ansi.colorize("Cinnabar", Attribute.BRIGHT_RED_TEXT()) - + ", " + Ansi.colorize("Pink", Attribute.TEXT_COLOR(0xFF, 0xC0, 0xCB)) - + "\n- " + Ansi.colorize("Orange", Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F)) - + ", " + Ansi.colorize("Gold", Attribute.YELLOW_TEXT()) - + ", " + Ansi.colorize("Yellow", Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00)) - + "\n- " + Ansi.colorize("Green", Attribute.BRIGHT_GREEN_TEXT()) - + ", " + Ansi.colorize("Chartreuse", Attribute.TEXT_COLOR(0xD1, 0xEB, 0x27)) - + ", " + Ansi.colorize("Beige", Attribute.BRIGHT_YELLOW_TEXT()) - + "\n- " + Ansi.colorize("Blue", Attribute.BRIGHT_BLUE_TEXT()) - + ", " + Ansi.colorize("Teal", Attribute.TEXT_COLOR(0x00, 0x93, 0xCF)) - + ", " + Ansi.colorize("Cyan", Attribute.BRIGHT_CYAN_TEXT()) - + "\n" - + "\n" + Ansi.colorize("Please select a color:") + PALE_YELLOW + " "; - String[] values = {"Default", "Black", "Gray", "White", "Indigo", "Purple", "Violet", "Red", "Cinnabar", "Pink", "Orange", "Gold", - "Yellow", "Green", "Chartreuse", "Beige","Blue", "Teal", "Cyan"}; - - String input = retrieveInput(reader, prompt, values); - - Attribute color = null; - switch(input.toLowerCase()) { - case "default": color = Attribute.CLEAR(); break; - case "black": color = Attribute.BLACK_TEXT(); break; - case "gray": color = Attribute.BRIGHT_BLACK_TEXT(); break; - case "white": color = Attribute.BRIGHT_WHITE_TEXT(); break; - case "indigo": color = Attribute.TEXT_COLOR(0x7D, 0x1A, 0xFF); break; - case "purple": color = Attribute.BRIGHT_MAGENTA_TEXT(); break; - case "violet": color = Attribute.TEXT_COLOR(0xEE, 0x82, 0xEE); break; - case "red": color = Attribute.RED_TEXT(); break; - case "cinnabar": color = Attribute.BRIGHT_RED_TEXT(); break; - case "pink": color = Attribute.TEXT_COLOR(0xFF, 0xC0, 0xCB); break; - case "orange": color = Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F); break; - case "gold": color = Attribute.YELLOW_TEXT(); break; - case "yellow": color = Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00); break; - case "green": color = Attribute.BRIGHT_GREEN_TEXT(); break; - case "chartreuse": color = Attribute.TEXT_COLOR(0xD1, 0xEB, 0x27); break; - case "beige": color = Attribute.BRIGHT_YELLOW_TEXT(); break; - case "blue": color = Attribute.BRIGHT_BLUE_TEXT(); break; - case "teal": color = Attribute.TEXT_COLOR(0x00, 0x93, 0xCF); break; - case "cyan": color = Attribute.BRIGHT_CYAN_TEXT(); break; - } - - if(CMD) - System.out.println(BACKLINE + Ansi.colorize("Please select a color: ") + Ansi.colorize(input, color) + "\n"); - else System.out.println(Ansi.colorize("")); - - return color; - - } - - /** - * Helper function to prompt user to select a program task. - * - * @param reader - * @param input - * @return operation - */ - private static String retrieveOperation(BufferedReader reader, String input) { - - String operation = null; - String[] values = {"extract", "package", "patch", "print", "exit"}; - - if(input != null) - for(int i = 0; i < values.length; i++) - if(values[i].equalsIgnoreCase(input)) - operation = values[i]; - - if(operation == null) { - - String prompt = "\n" + Ansi.colorize("Operations", Attribute.BRIGHT_YELLOW_TEXT(), Attribute.BOLD()) + ":\n" - + "- " + Ansi.colorize("Extract", Attribute.BRIGHT_RED_TEXT()) + " audio tracks from package\n" - + "- " + Ansi.colorize("Package", Attribute.YELLOW_TEXT()) + " audio tracks into package\n" - + "- " + Ansi.colorize("Patch", Attribute.BRIGHT_GREEN_TEXT()) + " audio track to loop indefinitely\n" - + "- " + Ansi.colorize("Print", Attribute.BRIGHT_BLUE_TEXT()) + " Ridge Racer 6 style ASCII logo\n" - + "- " + Ansi.colorize("Exit", Attribute.BRIGHT_MAGENTA_TEXT()) + " program\n" - + "\nPlease select an operation: " + PALE_YELLOW; - - operation = retrieveInput(reader, prompt, values); - - if(CMD) { - - Attribute color = null; - switch(operation.toLowerCase()) { - case "extract": - color = Attribute.BRIGHT_RED_TEXT(); - break; - case "package": - color = Attribute.YELLOW_TEXT(); - break; - case "patch": - color = Attribute.BRIGHT_GREEN_TEXT(); - break; - case "print": - color = Attribute.BRIGHT_BLUE_TEXT(); - break; - case "exit": - color = Attribute.BRIGHT_MAGENTA_TEXT(); - break; - } - System.out.println(BACKLINE + Ansi.colorize("Please select an operation: ") + Ansi.colorize(operation, color)); - - } else System.out.print(Ansi.colorize("")); - - } - - return operation.toLowerCase(); - - } - - /** - * Prompts the user for input without strict validation. - * - * @param reader - * @param prompt - * @return input - */ - private static String retrieveInput(BufferedReader reader, String prompt) { - - String input = null; - - boolean valid = false; - do { - System.out.print(prompt); - try { - input = reader.readLine(); - valid = true; - } catch (IOException e) { - System.out.println(Ansi.colorize("Invalid input.")); - } - } while(!valid); - - return input; - - } - /** - * Prompts the user for input and validates that the input matches one of the provided values. - * - * @param reader - * @param prompt - * @param values - * @return input - */ - private static String retrieveInput(BufferedReader reader, String prompt, String[] values) { - - String input = null; - - boolean valid = false; - do { - System.out.print(prompt); - try { - input = reader.readLine(); - for(int i = 0; i < values.length; i++) - if(input.equalsIgnoreCase(values[i])) { - valid = true; - break; - } - if(!valid) - System.out.println(Ansi.colorize("Invalid selection.")); - } catch (IOException e) { - System.out.println(Ansi.colorize("Invalid selection.")); - } - } while(!valid); - - return input; - - } - - /** - * Patches a WAV file by seeking out its data and smpl chunks, - * replacing any existing smpl chunk with a new smpl chunk, - * setting loop points at the start and end of the data chunk. - * - * @param waveFile - */ - private static void patch(File waveFile) { - - try { - RandomAccessFile file = new RandomAccessFile(waveFile, "r"); - - // Also Find data location - int dataAddress = -1; - // Check if sample chunk already exists - int smplAddress = -1; - for(int i = 0; i < file.length(); i += 4) { - file.seek(i); - int scan = file.readInt(); - // match "smpl" chunk header - if(scan == 0x736D706C) - smplAddress = i; - // match "data" chunk header - if(scan == 0x64617461) - dataAddress = i; - if(smplAddress > -1 && dataAddress > -1) - break; - } - - // Determine size of data chunk - if(dataAddress > -1) { - - file.seek(dataAddress + 4); - byte[] buffer = new byte[4]; - file.read(buffer); - - int dataSize = 8 + buffer[3] - + buffer[2] * (int) Math.pow(16, 2) - + buffer[1] * (int) Math.pow(16, 4) - + buffer[0] * (int) Math.pow(16, 6); - - } else throw new IOException("Data chunk not in file."); - - // Determine size of smpl chunk - if(smplAddress > -1) { - - file.seek(smplAddress + 4); - byte[] buffer = new byte[4]; - file.read(buffer); // write size bytes to buffer - - // calculate expected chunk size - int smplSize = 8 + buffer[3] - + buffer[2] * (int) Math.pow(16, 2) - + buffer[1] * (int) Math.pow(16, 4) - + buffer[0] * (int) Math.pow(16, 6); - - } - - // Create new sample chunk - // Insert 68-byte sample chunk with loop before data chunk - - byte[] smpl = new byte[68]; - // smpl (0) - smpl[0] = 0x73; - smpl[1] = 0x6D; - smpl[2] = 0x70; - smpl[3] = 0x6C; - // size (4) - smpl[4] = 0x3C; - // manufacturer (8) - // product (12) - // sample period (16) - // MIDI unity note (20) - smpl[20] = 0x3C; - // MIDI pitch fraction (24) - // SMPTE Format (28) - // SMPTE Offset (32) - // Number of Sample Loops (36) - smpl[36] = 0x01; - // Sample Loops size (40) - // loop ID (44) - // loop type (48) - // data start - // loop start (52) - // data end - // loop end (56) - // tuning fraction (60) - // play count (64) - - - // Set loop points to start and end of data chunk - // Update the new file size in RIFF header - } catch(IOException e) { - System.out.println("Failed to patch file."); - } - - } - - /** - * Packs WAV files located at the extract directory into BIN files at the package directory. - * - * @param packDirectory - * @param extractDirectory - */ - private static void pack(File packDirectory, File extractDirectory) { - - // Identify extracted folders - File[] packages = new File(extractDirectory.toString()).listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.toLowerCase().startsWith("pack_") && !name.toLowerCase().endsWith(".bin"); - } - }); - - for(int i = 0; i < packages.length; i++) - System.out.println(packages[i].getName()); - - for(File packDir : packages) { - - // 1. Compile list of WAV files in extracted directories - File[] audioTracks = new File(packDir.toString()).listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.toLowerCase().endsWith(".wav"); - } - }); - - // 2. Check if pack files already exist in package directory - File pack = new File(packDir.getPath() + ".bin"); - if(pack.exists()); - - // 3. If pack file already exists, insert WAV files into existing file. - // 4. If pack file doesn't exist, create new file then insert WAV files. - - } - - // Hypothesis 1: Does Ridge Racer 6 use a predetermined address list for reading tracks from package? - // Test 1: Try changing the address of a track and see if the game can handle it successfully. - - // Hypothesis 2: Does Ridge Racer 6 loop tracks in their entirety, or do they use loop points defined in the file header? - // Test 1: Try editing loop information and see if it affects in-game playback. - // Test 2: Try replacing a track with a WAV file without loop data and see if it loops in in-game playback. - - } - /** * Extracts audio files to the extract directory from package files located at the package directory. * Also supports FLAC conversion for BGM files. @@ -754,6 +369,463 @@ public class AudioExtractor { } } + + + /** + * Packs WAV files located at the extract directory into BIN files at the package directory. + * + * @param packDirectory + * @param extractDirectory + */ + private static void pack(File packDirectory, File extractDirectory) { + + // Identify extracted folders + File[] packages = new File(extractDirectory.toString()).listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.toLowerCase().startsWith("pack_") && !name.toLowerCase().endsWith(".bin"); + } + }); + + for(int i = 0; i < packages.length; i++) + System.out.println(packages[i].getName()); + + for(File packDir : packages) { + + // 1. Compile list of WAV files in extracted directories + File[] audioTracks = new File(packDir.toString()).listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(".wav"); + } + }); + + // 2. Check if pack files already exist in package directory + File pack = new File(packDir.getPath() + ".bin"); + if(pack.exists()); + + // 3. If pack file already exists, insert WAV files into existing file. + // 4. If pack file doesn't exist, create new file then insert WAV files. + + } + + // Hypothesis 1: Does Ridge Racer 6 use a predetermined address list for reading tracks from package? + // Test 1: Try changing the address of a track and see if the game can handle it successfully. + + // Hypothesis 2: Does Ridge Racer 6 loop tracks in their entirety, or do they use loop points defined in the file header? + // Test 1: Try editing loop information and see if it affects in-game playback. + // Test 2: Try replacing a track with a WAV file without loop data and see if it loops in in-game playback. + + } + + /** + * Patches a WAV file by seeking out its data and smpl chunks, + * replacing any existing smpl chunk with a new smpl chunk, + * setting loop points at the start and end of the data chunk. + * + * @param waveFile + */ + private static void patch(File waveFile) { + + try { + RandomAccessFile file = new RandomAccessFile(waveFile, "r"); + + // Also Find data location + int dataAddress = -1; + // Check if sample chunk already exists + int smplAddress = -1; + + for(int i = 0; i < file.length(); i += 4) { + + file.seek(i); + int scan = file.readInt(); + + // match "data" chunk header + if(scan == ASCII_data) + dataAddress = i; + // match "smpl" chunk header + else if(scan == ASCII_smpl) + smplAddress = i; + + // if both chunks already located, stop seeking + if(smplAddress > -1 && dataAddress > -1) + break; + + } + + // Determine size of data chunk + if(dataAddress > -1) { + + file.seek(dataAddress + 4); + byte[] buffer = new byte[4]; + file.read(buffer); + + int dataSize = 8 + buffer[3] + + buffer[2] * (int) Math.pow(16, 2) + + buffer[1] * (int) Math.pow(16, 4) + + buffer[0] * (int) Math.pow(16, 6); + + } else throw new IOException("Data chunk not in file."); + + // Determine size of smpl chunk + if(smplAddress > -1) { + + file.seek(smplAddress + 4); + byte[] buffer = new byte[4]; + file.read(buffer); // write size bytes to buffer + + // calculate expected chunk size + int smplSize = 8 + buffer[3] + + buffer[2] * (int) Math.pow(16, 2) + + buffer[1] * (int) Math.pow(16, 4) + + buffer[0] * (int) Math.pow(16, 6); + + } // smpl chunk may not already exist + + // Create new sample chunk + // Insert 68-byte sample chunk with loop before data chunk + + byte[] smpl = new byte[68]; + // smpl (0) + smpl[0] = 0x73; + smpl[1] = 0x6D; + smpl[2] = 0x70; + smpl[3] = 0x6C; + // size (4) + smpl[4] = 0x3C; + // manufacturer (8) + // product (12) + // sample period (16) + // MIDI unity note (20) + smpl[20] = 0x3C; + // MIDI pitch fraction (24) + // SMPTE Format (28) + // SMPTE Offset (32) + // Number of Sample Loops (36) + smpl[36] = 0x01; + // Sample Loops size (40) + // loop ID (44) + // loop type (48) + // data start + // loop start (52) + // data end + // loop end (56) + // tuning fraction (60) + // play count (64) + + + // Set loop points to start and end of data chunk + // Update the new file size in RIFF header + + } catch(IOException e) { + System.out.println("Failed to patch file."); + } + + } + + /** + * Helper function to prompt user to select a WAV file. + * + * @param reader + * @return WAV file + */ + private static File retrieveWave(BufferedReader reader) { + + String prompt = "Please provide a WAV file: " + ANSI_BEIGE; + + File waveFile = null; + boolean valid = false; + do { + + // Retrieve an audio file + waveFile = retrieveFile(reader, prompt, ".wav"); + + try { + // validate that chosen file is a proper Wave file + RandomAccessFile file = new RandomAccessFile(waveFile, "r"); + // first 4 bytes start with RIFF + if(file.readInt() == ASCII_RIFF) { + // next 4 bytes contain the size of the file in little-endian representation + file.seek(4); + byte[] buffer = new byte[4]; + file.read(buffer); // write size bytes to buffer + + // calculate expected file size + int headerSize = 8 + buffer[3] + + buffer[2] * (int) Math.pow(16, 2) + + buffer[1] * (int) Math.pow(16, 4) + + buffer[0] * (int) Math.pow(16, 6); + + // file is valid if actual file length matches expected size + valid = headerSize == file.length(); + if(!valid) + System.out.println("File length mismatch. Expected " + headerSize + " bytes, but found " + file.length() + " bytes."); + + } else System.out.println("Invalid RIFF header."); + file.close(); + } catch (IOException e) { + System.out.println("Invalid file."); + } + + } while(!valid); + + return waveFile; + + } + + /** + * Helper function to prompt user to select a file. + * + * @param reader + * @return file + */ + private static File retrieveFile(BufferedReader reader, String prompt, String extension) { + + File file = null; + + String input = null; + boolean valid = false; + do { + System.out.print(prompt); + try { + input = reader.readLine(); + file = new File(input); + if(file.exists()) { + if(file.getName().toLowerCase().endsWith(extension)) { + valid = true; + } else System.out.println("File does not have " + extension.toUpperCase() + " file extension"); + } else System.out.println("File does not exist."); + } catch (IOException e) { + System.out.println(Ansi.colorize("Invalid file.")); + } + } while(!valid); + + return file; + + } + + /** + * Helper function to prompt user to select a color. + * + * @param reader + * @return color attribute + */ + private static Attribute retrieveColor(BufferedReader reader) { + + String prompt = "\nCommand parameters: " + Ansi.colorize("Print", Attribute.BRIGHT_BLUE_TEXT()) + + Ansi.colorize(" [color]", Attribute.BRIGHT_YELLOW_TEXT()) + Ansi.colorize(" [message]", Attribute.BRIGHT_RED_TEXT()) + + "\n" + + "\n" + Ansi.colorize("Colors", Attribute.BRIGHT_YELLOW_TEXT(), Attribute.BOLD()) + ":" + + "\n- " + Ansi.colorize("Default", Attribute.CLEAR()) + + "\n- " + Ansi.colorize("Black", Attribute.BLACK_TEXT()) + + ", " + Ansi.colorize("Gray", Attribute.BRIGHT_BLACK_TEXT()) + + ", " + Ansi.colorize("White", Attribute.BRIGHT_WHITE_TEXT()) + + "\n- " + Ansi.colorize("Indigo", Attribute.TEXT_COLOR(0x7D, 0x1A, 0xFF)) + + ", " + Ansi.colorize("Purple", Attribute.BRIGHT_MAGENTA_TEXT()) + + ", " + Ansi.colorize("Violet", Attribute.TEXT_COLOR(0xEE, 0x82, 0xEE)) + + "\n- " + Ansi.colorize("Red", Attribute.RED_TEXT()) + + ", " + Ansi.colorize("Cinnabar", Attribute.BRIGHT_RED_TEXT()) + + ", " + Ansi.colorize("Pink", Attribute.TEXT_COLOR(0xFF, 0xC0, 0xCB)) + + "\n- " + Ansi.colorize("Orange", Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F)) + + ", " + Ansi.colorize("Gold", Attribute.YELLOW_TEXT()) + + ", " + Ansi.colorize("Yellow", Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00)) + + "\n- " + Ansi.colorize("Green", Attribute.BRIGHT_GREEN_TEXT()) + + ", " + Ansi.colorize("Chartreuse", Attribute.TEXT_COLOR(0xD1, 0xEB, 0x27)) + + ", " + Ansi.colorize("Beige", Attribute.BRIGHT_YELLOW_TEXT()) + + "\n- " + Ansi.colorize("Blue", Attribute.BRIGHT_BLUE_TEXT()) + + ", " + Ansi.colorize("Teal", Attribute.TEXT_COLOR(0x00, 0x93, 0xCF)) + + ", " + Ansi.colorize("Cyan", Attribute.BRIGHT_CYAN_TEXT()) + + "\n" + + "\n" + Ansi.colorize("Please select a color:") + ANSI_BEIGE + " "; + String[] values = {"Default", "Black", "Gray", "White", "Indigo", "Purple", "Violet", "Red", "Cinnabar", "Pink", "Orange", "Gold", + "Yellow", "Green", "Chartreuse", "Beige","Blue", "Teal", "Cyan"}; + + String input = retrieveInput(reader, prompt, values); + + Attribute color = null; + switch(input.toLowerCase()) { + case "default": color = Attribute.CLEAR(); break; + case "black": color = Attribute.BLACK_TEXT(); break; + case "gray": color = Attribute.BRIGHT_BLACK_TEXT(); break; + case "white": color = Attribute.BRIGHT_WHITE_TEXT(); break; + case "indigo": color = Attribute.TEXT_COLOR(0x7D, 0x1A, 0xFF); break; + case "purple": color = Attribute.BRIGHT_MAGENTA_TEXT(); break; + case "violet": color = Attribute.TEXT_COLOR(0xEE, 0x82, 0xEE); break; + case "red": color = Attribute.RED_TEXT(); break; + case "cinnabar": color = Attribute.BRIGHT_RED_TEXT(); break; + case "pink": color = Attribute.TEXT_COLOR(0xFF, 0xC0, 0xCB); break; + case "orange": color = Attribute.TEXT_COLOR(0xFF, 0x68, 0x1F); break; + case "gold": color = Attribute.YELLOW_TEXT(); break; + case "yellow": color = Attribute.TEXT_COLOR(0xFF, 0xEA, 0x00); break; + case "green": color = Attribute.BRIGHT_GREEN_TEXT(); break; + case "chartreuse": color = Attribute.TEXT_COLOR(0xD1, 0xEB, 0x27); break; + case "beige": color = Attribute.BRIGHT_YELLOW_TEXT(); break; + case "blue": color = Attribute.BRIGHT_BLUE_TEXT(); break; + case "teal": color = Attribute.TEXT_COLOR(0x00, 0x93, 0xCF); break; + case "cyan": color = Attribute.BRIGHT_CYAN_TEXT(); break; + } + + if(CMD_MODE) + System.out.println(ANSI_BACKLINE + Ansi.colorize("Please select a color: ") + Ansi.colorize(input, color) + "\n"); + else System.out.println(Ansi.colorize("")); + + return color; + + } + + /** + * Helper function to prompt user to select a program task. + * + * @param reader + * @param input + * @return operation + */ + private static String retrieveOperation(BufferedReader reader, String input) { + + String operation = null; + String[] values = {"extract", "package", "patch", "print", "exit"}; + + if(input != null) + for(int i = 0; i < values.length; i++) + if(values[i].equalsIgnoreCase(input)) + operation = values[i]; + + if(operation == null) { + + String prompt = "\n" + Ansi.colorize("Operations", Attribute.BRIGHT_YELLOW_TEXT(), Attribute.BOLD()) + ":\n" + + "- " + Ansi.colorize("Extract", Attribute.BRIGHT_RED_TEXT()) + " audio tracks from package\n" + + "- " + Ansi.colorize("Package", Attribute.YELLOW_TEXT()) + " audio tracks into package\n" + + "- " + Ansi.colorize("Patch", Attribute.BRIGHT_GREEN_TEXT()) + " audio track to loop indefinitely\n" + + "- " + Ansi.colorize("Print", Attribute.BRIGHT_BLUE_TEXT()) + " Ridge Racer 6 style ASCII logo\n" + + "- " + Ansi.colorize("Exit", Attribute.BRIGHT_MAGENTA_TEXT()) + " program\n" + + "\nPlease select an operation: " + ANSI_BEIGE; + + operation = retrieveInput(reader, prompt, values); + + if(CMD_MODE) { + + Attribute color = null; + switch(operation.toLowerCase()) { + case "extract": + color = Attribute.BRIGHT_RED_TEXT(); + break; + case "package": + color = Attribute.YELLOW_TEXT(); + break; + case "patch": + color = Attribute.BRIGHT_GREEN_TEXT(); + break; + case "print": + color = Attribute.BRIGHT_BLUE_TEXT(); + break; + case "exit": + color = Attribute.BRIGHT_MAGENTA_TEXT(); + break; + } + System.out.println(ANSI_BACKLINE + Ansi.colorize("Please select an operation: ") + Ansi.colorize(operation, color)); + + } else System.out.print(Ansi.colorize("")); + + } + + return operation.toLowerCase(); + + } + + /** + * Prompts the user for input without strict validation. + * + * @param reader + * @param prompt + * @return input + */ + private static String retrieveInput(BufferedReader reader, String prompt) { + + String input = null; + + boolean valid = false; + do { + System.out.print(prompt); + try { + input = reader.readLine(); + valid = true; + } catch (IOException e) { + System.out.println(Ansi.colorize("Invalid input.")); + } + } while(!valid); + + return input; + + } + + /** + * Prompts the user for input and validates that the input matches one of the provided values. + * + * @param reader + * @param prompt + * @param values + * @return input + */ + private static String retrieveInput(BufferedReader reader, String prompt, String[] values) { + + String input = null; + + boolean valid = false; + do { + System.out.print(prompt); + try { + input = reader.readLine(); + for(int i = 0; i < values.length; i++) + if(input.equalsIgnoreCase(values[i])) { + valid = true; + break; + } + if(!valid) + System.out.println(Ansi.colorize("Invalid selection.")); + } catch (IOException e) { + System.out.println(Ansi.colorize("Invalid selection.")); + } + } while(!valid); + + return input; + + } + + /** + * Helper function to read the size of a file chunk. + * + * @param file + * @param chunkAddress + * @return chunk size + * @throws IOException + */ + private static int readChunkSize(File file, int chunkAddress) throws IOException { + + RandomAccessFile accessedFile = new RandomAccessFile(file, "r"); + return readChunkSize(accessedFile, chunkAddress); + + } + + /** + * Helper function to read the size of a file chunk. + * + * @param accessedFile + * @param chunkAddress + * @return chunk size + * @throws IOException + */ + private static int readChunkSize(RandomAccessFile file, int chunkAddress) throws IOException { + + byte[] buffer = new byte[4]; + + file.seek(chunkAddress + 4); + file.read(buffer); + + return 8 // size offset + + buffer[3] // 16^0 + + buffer[2] * 256 // 16^2 + + buffer[1] * 65_536 // 16^4 + + buffer[0] * 16_777_216; // 16^6 + + } /** * Creates a map of audio tracks within provided file. @@ -765,7 +837,6 @@ public class AudioExtractor { public static LinkedHashMap findAudioTracks(RandomAccessFile file) throws IOException { LinkedHashMap tracklist = new LinkedHashMap(); - int key = 0x52494646; // "RIFF" in ASCII representation // Files start on addresses divisible by 0x800 for(int a = 0; a + 0x800 < file.length(); a += 0x800) { @@ -773,7 +844,7 @@ public class AudioExtractor { file.seek(a); // File header starts with "RIFF" sequence - if(file.readInt() == key) { + if(file.readInt() == ASCII_RIFF) { // Next 4 bytes specify file length beyond first 8 bytes, in little-endian representation int length = 8; @@ -793,11 +864,6 @@ public class AudioExtractor { } - - private static String logoFormat(String string, boolean italic) { - return logoFormat(string, italic, 10); - } - /** * Creates an ASCII logo in RR6 style using the provided text. * Supports alphanumeric text with spaces only. @@ -809,6 +875,18 @@ public class AudioExtractor { public static String logoFormat(String text) { return logoFormat(text, true, 10); } + + /** + * Creates an ASCII logo in RR6 style using the provided text. + * Supports alphanumeric text with spaces only. + * + * @param string + * @param italicize logo + * @return logo formatted text + */ + private static String logoFormat(String string, boolean italic) { + return logoFormat(string, italic, 10); + } /** * Creates an ASCII logo in RR6 style using the provided text.