diff --git a/pre-edited-cs/data/Stage/Hell3.tsc b/pre-edited-cs/data/Stage/Hell3.tsc
index 74358e1..f25d55e 100644
--- a/pre-edited-cs/data/Stage/Hell3.tsc
+++ b/pre-edited-cs/data/Stage/Hell3.tsc
@@ -1 +1 @@
-0-FSS\S0-_pqd_idlSSSS_hqg0-FSS\T0-_pqd_idlSSST_hqg0-FSS\U0-_pqd_idlSSSU_hqg0-FSS\V0-_pqd_idlSSSV_hqg0-FSS\W0-_pqd_idlSSSW_hqg0-FSS\X0-_nh|0-_iomTXVU]SUSS_idlSSSV0-_iomTXXS]SUST_hqg0-FSS\Y0-_nh|_p|eSSSU_zdlSSSU_sul_idlSSST_hqg0-FSTSS0-_sul_idrSSST_wudSS[T]SS\X]STXT]SSTV0-FSTTS0-_sul_irpSSTY_idrSSSV0-_zdlSSVS0-_iomTXVW]STTT0-_wudSS[W]SS\X]SSTS]SSSU0-FSTTT0-_wudSS[X]SS\X]SSTS]SSSU0-FSUSS0-_fqsSUSS]SVUS]SSSS0-_ioPTXVU_ioNTXVV0-_zdlSSSU_idlSSSV_iomTXXS]SUST_hqg0-FSUST0-_gqsSUST0-_fpsSSSZ]SSTW]SSST0-_fpsSSS[]SSTW]SSST0-_fpsSSS\]SSTW]SSST0-_fpsSSTS]SSTW]SSST0-_fpsSSTT]SSTW]SSST0-_fpsSSTU]SSTW]SSST0-_fpsSSTV]SSTW]SSST_hqg0-FSUWS0-FSUWT0-_gqsSUWT_dqsSUWS]SSTS]SSSV_hqg0-FSUWU0-FSUWV0-_gqsSUWV_dqsSUWU]SSTS]SSSV_hqg0-FSUWW0-FSUWX0-_gqsSUWX_dqsSUWW]SSTS]SSSV_hqg0-FSUXS0-FSUXT0-_gqsSUXT_dqsSUXS]SSTS]SSST_hqg0-FSUXU0-#SUXV0-_gqsSUXV_dqsSUXU]SSTS]SSSV_hqg0-FSUXW0-FSUXX0-_gqsSUXX_dqsSUXW]SSTS]SSST_hqg0-FSUXY0-FSUXZ0-_gqsSUXZ_dqsSUXY]SSTS]SSSV_hqg0-FSU\S0-_gqsSU\S_vrxSSTU0-_fpsSTTZ]SSS\]SSU[0-_fpsSTTZ]SSTS]SSU[0-_fpsSTTZ]SSTT]SSU[0-_hqg0-FSU\T0-_gqsSU\T_vrxSSTU0-_fpsSS\X]SSSV]SSU[0-_fpsSS\X]SSSW]SSU[0-_fpsSS\X]SSSX]SSU[0-_fpsSS\X]SSSY]SSU[0-_hqg0-FSU\U0-_gqsSU\U_vrxSSTU0-_fpsSSSS]SSSS]SSU[0-_fpsSSSS]SSSS]SSU[0-_fpsSSSS]SSSS]SSU[0-_hqg0-FSVSS0-_gqsSVSS_gqdSVS\_gqdSVTT_gqdSVTZ0-_irqSVST]SSTY0-_fpxSSSZ_evoSSSS0-_fpsSSUS]SSTT]SSST0-_fpsSSUS]SSTU]SSST0-_erdSTSS0-_fqsTSST]SVVS]SSSS0-_fqsTSSU]SVVS]SSSU0-_vrxSSTU0-_hqg0-FSVST0-FSVSU0-_prySST[]SSTS_hqg0-FSV\S0-_sul_iomTXXT]SSST_ioNTXXT0-_vrxSSUU_fqsSV\S]SSUT]SSSS_hyhSWSS0-FSWSS0-_hyhSSVX0-FS\SS0-_gqsS\SS_hqg0-FS\ST0-_gqsS\ST_hqg0-FTSSS0-_erdSXSS_fpxSSSS_ioPTXXS_hqg0-FTSST0-FTSSU
\ No newline at end of file
+>;Taaja>;m~rmt~�aadgmwrzaaaamvu>;Taajb>;m~rmt~�aadgmwrzaaabmvu>;Taajc>;m~rmt~�aadgmwrzaaacmvu>;Taajd>;m~rmt~�aadgmwrzaaadmvu>;Taaje>;m~rmt~�aadgmwrzaaaemvu>;Taajf>;m|v�>;mw}{bfdckacaamwrzaaad>;mw}{bffakacabmvu>;Taajg>;m|v�m~�saaacm�rzaaacm��zmwrzaaabmvu>;Tabaa>;m��zmwr�aaabm��raaibkaajfkabfbkaabd>;Tabba>;m��zmw�~aabgmwr�aaad>;m�rzaada>;mw}{bfdekabbb>;m��raaiekaajfkaabakaaac>;Tabbb>;m��raaifkaajfkaabakaaac>;Tacaa>;mt�acaakadcakaaaa>;mw}^bfdcmw}\bfdd>;m�rzaaacmwrzaaadmw}{bffakacabmvu>;Tacab>;mu�acab>;mt~�aaahkaabekaaab>;mt~�aaaikaabekaaab>;mt~�aaajkaabekaaab>;mt~�aabakaabekaaab>;mt~�aabbkaabekaaab>;mt~�aabckaabekaaab>;mt~�aabdkaabekaaabmvu>;Tacea>;Taceb>;mu�acebmr�aceakaabakaaadmvu>;Tacec>;Taced>;mu�acedmr�aceckaabakaaadmvu>;Tacee>;Tacef>;mu�acefmr�aceekaabakaaadmvu>;Tacfa>;Tacfb>;mu�acfbmr�acfakaa1akaaabmvu>;Tacfc>;Tacfd>;mu�acfdmr�acfckaabakaaadmvu>;Tacfe>;Tacff>;mu�acffmr�acfekaabakaaabmvu>;Tacfg>;Tacfh>;mu�acfhmr�acfgkaabakaaadmvu>;Tacja>;mu�acjam���aabc>;mt~�abbhkaaajkaaci>;mt~�abbhkaabakaaci>;mt~�abbhkaabbkaaci>;mvu>;Tacjb>;mu�acjbm���aabc>;mt~�aajfkaaadkaaci>;mt~�aajfkaaaekaaci>;mt~�aajfkaaafkaaci>;mt~�aajfkaaagkaaci>;mvu>;Tacjc>;mu�acjcm���aabc>;mt~�aaaakaaaakaaci>;mt~�aaaakaaaakaaci>;mt~�aaaakaaaakaaci>;mvu>;Tadaa>;mu�adaamuradajmuradbbmuradbh>;mw�adabkaabg>;mt~�aaahms�}aaaa>;mt~�aacakaabbkaaab>;mt~�aacakaabckaaab>;ms�rabaa>;mt�baabkaddakaaaa>;mt�baackaddakaaac>;m���aabc>;mvu>;Tadab>;Tadac>;m~��aabikaabamvu>;Tadja>;m��zmw}{bffbkaaabmw}\bffb>;m���aaccmt�adjakaacbkaaaamv�vaeaa>;Taeaa>;mv�vaadf>;Tajaa>;mu�ajaamvu>;Tajab>;mu�ajabmvu>;Tbaaa>;ms�rafaamt~�aaaamw}^bffamvu>;Tbaab>;Tbaac
\ No newline at end of file
diff --git a/pre-edited-cs/data/Stage/ScriptSource/Hell3.txt b/pre-edited-cs/data/Stage/ScriptSource/Hell3.txt
index 6535f85..8d21f51 100644
--- a/pre-edited-cs/data/Stage/ScriptSource/Hell3.txt
+++ b/pre-edited-cs/data/Stage/ScriptSource/Hell3.txt
@@ -1,14 +1,14 @@
 
 #0090
-<MNA<FAI0000<END
+<MNA<CMU0036<FAI0000<END
 #0091
-<MNA<FAI0001<END
+<MNA<CMU0036<FAI0001<END
 #0092
-<MNA<FAI0002<END
+<MNA<CMU0036<FAI0002<END
 #0093
-<MNA<FAI0003<END
+<MNA<CMU0036<FAI0003<END
 #0094
-<MNA<FAI0004<END
+<MNA<CMU0036<FAI0004<END
 
 #0095
 <KEY
diff --git a/src/database/music.lua b/src/database/music.lua
new file mode 100644
index 0000000..1448b0a
--- /dev/null
+++ b/src/database/music.lua
@@ -0,0 +1,227 @@
+local song = Class:extend()
+function song:new(name, id, jingle, beta)
+  self.name = name
+  self.id = id
+  self.jingle = jingle or false
+  self.beta = beta or false
+end
+
+local songs = {
+  xxxx = song("XXXX", "0000", true),
+  mischievousRobot = song("Mischievous Robot", "0001"),
+  safety = song("Safety", "0002"),
+  gameOver = song("Game Over", "0003", true),
+  gravity = song("Gravity", "0004"),
+  onToGrasstown = song("On to Grasstown", "0005"),
+  meltdown = song("Meltdown 2", "0006"),
+  eyesOfFlame = song("Eyes of Flame", "0007"),
+  gestation = song("Gestation", "0008"),
+  mimigaTown = song("Mimiga Town", "0009"),
+  getItem = song("Get Item!", "0010", true),
+  balrogsTheme = song("Balrog's Theme", "0011"),
+  cemetary = song("Cemetary", "0012"),
+  plant = song("Plant", "0013"),
+  pulse = song("Pulse", "0014"),
+  victory = song("Victory!", "0015", true),
+  getLifeCapsule = song("Get Life Capsule!", "0016", true),
+  tyrant = song("Tyrant", "0017"),
+  run = song("Run!", "0018"),
+  jenka1 = song("Jenka 1", "0019"),
+  labyrinthFight = song("Labyrinth Fight", "0020"),
+  access = song("Access", "0021"),
+  oppression = song("Oppression", "0022"),
+  geothermal = song("Geothermal", "0023"),
+  caveStory = song("Cave Story", "0024"),
+  moonsong = song("Moonsong", "0025"),
+  herosEnd = song("Hero's End", "0026"),
+  scorchingBack = song("Scorching Back", "0027"),
+  quiet = song("Quiet", "0028"),
+  finalCave = song("Final Cave", "0029"),
+  balcony = song("Balcony", "0030"),
+  charge = song("Charge", "0031"),
+  lastBattle = song("Last Battle", "0032"),
+  theWayBackHome = song("The Way Back Home", "0033"),
+  zombie = song("Zombie", "0034"),
+  breakDown = song("Break Down", "0035"),
+  runningHell = song("Running Hell", "0036"),
+  jenka2 = song("Jenka 2", "0037"),
+  livingWaterway = song("Living Waterway", "0038"),
+  sealChamber = song("Seal Chamber", "0039"),
+  torokosTheme = song("Toroko's Theme", "0040"),
+  white = song('"White"', "0041"),
+  windFortress = song("Wind Fortress", "0042", false, true),
+  halloween2 = song("Halloween 2", "0043", false, true),
+  peopleOfTheRoot = song("People of the Root", "0044", false, true),
+  pierWalk = song("Pier Walk", "0045", false, true),
+  snoopyCake = song("Snoopy Cake", "0046", false, true)
+}
+
+local cue = Class:extend()
+function cue:new(map, events, default)
+  self.map = map
+  self.events = events
+  self.songid = default
+  self.default = default
+end
+
+local cues = {
+  cue("Pens1", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0002"),
+  cue("Pens1", {"0095", "0098"}, "0014"),
+  cue("Eggs", {"0090", "0091", "0092", "0093", "0094", "0095", "0099", "0503"}, "0001"),
+  cue("Eggs", {"0600"}, "0004"),
+  cue("EggX", {"0095"}, "0014"),
+  cue("EggR", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
+  cue("Weed", {"0090", "0091", "0092", "0093", "0094", "0098", "0099"}, "0005"),
+  cue("Santa", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
+  cue("Santa", {"0099"}, "0028"),
+  cue("Chako", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
+  cue("Chako", {"0099"}, "0028"),
+  cue("MazeI", {"0090", "0091", "0092", "0093", "0094", "0400", "0601"}, "0019"),
+  cue("Sand", {"0090", "0091", "0092", "0093", "0094", "0099", "0210", "0601"}, "0006"),
+  cue("Sand", {"0202"}, "0007"),
+  cue("Mimi", {"0090", "0091", "0092", "0093", "0094", "0302"}, "0009"),
+  cue("Mimi", {"0095", "0096", "0097", "0098", "0099"}, "0028"),
+  cue("Cave", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
+  cue("Start", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
+  cue("Barr", {"0090", "0091", "0092", "0093", "0094", "0402", "1001"}, "0008"),
+  cue("Barr", {"1000"}, "0011"),
+  cue("Barr", {"1000"}, "0004"),
+  cue("Pool", {"0090", "0091", "0092", "0093", "0094"}, "0009"),
+  cue("Pool", {"0095", "0096", "0097", "0098", "0099", "0410"}, "0028"),
+  cue("Cemet", {"0090", "0091", "0092", "0093", "0094"}, "0012"),
+  cue("Plant", {"0090", "0091", "0092", "0093", "0094"}, "0013"),
+  cue("Plant", {"0095", "0096", "0097", "0098", "0099"}, "0028"),
+  cue("Shelt", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0008"),
+  cue("Comu", {"0090", "0091", "0092", "0093", "0094"}, "0009"),
+  cue("Comu", {"0095", "0096", "0097", "0098", "0099"}, "0028"),
+  cue("Cthu", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
+  cue("Malco", {"0090", "0091", "0092", "0093", "0094", "0203"}, "0008"),
+  cue("Malco", {"0200", "0204"}, "0004"),
+  cue("Malco", {"0200"}, "0011"),
+  cue("Frog", {"0090", "0091", "0092", "0093", "0094", "1000"}, "0008"),
+  cue("Frog", {"0202"}, "0011"),
+  cue("Frog", {"0202"}, "0007"),
+  cue("Curly", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
+  cue("Curly", {"0300"}, "0004"),
+  cue("WeedB", {"0302"}, "0004"),
+  cue("Stream", {"0090", "0091", "0092", "0093", "0094"}, "0018"),
+  cue("Jenka1", {"0090", "0091", "0092", "0093", "0094"}, "0019"),
+  cue("Gard", {"0502"}, "0017"),
+  cue("Gard", {"0502"}, "0018"),
+  cue("Gard", {"0502"}, "0004"),
+  cue("Jenka2", {"0090", "0091", "0092", "0093", "0095", "0200"}, "0019"),
+  cue("Jenka2", {"0094"}, "0011"),
+  cue("SandE", {"0090", "0091", "0092", "0093", "0094", "0600"}, "0006"),
+  cue("SandE", {"0600"}, "0011"),
+  cue("MazeH", {"0090", "0091", "0092", "0093", "0094"}, "0019"),
+  cue("MazeW", {"0090", "0091", "0092", "0093", "0094", "1000"}, "0037"),
+  cue("MazeW", {"0302"}, "0007"),
+  cue("MazeO", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
+  cue("MazeD", {"0400"}, "0004"),
+  cue("MazeA", {"0090", "0091", "0092", "0093", "0094", "0301"}, "0008"),
+  cue("MazeB", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0008"),
+  cue("MazeS", {"0090", "0091", "0092", "0093", "0094", "0310", "0600"}, "0008"),
+  cue("MazeS", {"0321"}, "0011"),
+  cue("MazeS", {"0321"}, "0004"),
+  cue("MazeM", {"0090", "0091", "0092", "0093", "0094", "0301"}, "0020"),
+  cue("Drain", {"0090", "0091", "0092", "0093", "0094", "0150"}, "0008"),
+  cue("Drain", {"0095", "0096", "0097"}, "0023"),
+  cue("Almond", {"0090", "0091", "0092", "0093", "0094", "0361"}, "0023"),
+  cue("Almond", {"0452", "0500"}, "0022"),
+  cue("River", {"0090", "0091", "0092", "0093", "0094", "0095"}, "0038"),
+  cue("Eggs2", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0027"),
+  cue("Cthu2", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
+  cue("EggR2", {"0090", "0091", "0092", "0093", "0094", "1000"}, "0008"),
+  cue("EggR2", {"0304"}, "0004"),
+  cue("EggX2", {"0090", "0091", "0092", "0093", "0095"}, "0014"),
+  cue("Oside", {"0090", "0091", "0092", "0093", "0094"}, "0025"),
+  cue("Oside", {"0402"}, "0026"),
+  cue("Itoh", {"0090", "0091", "0092", "0093", "0094", "0095"}, "0008"),
+  cue("Cent", {"0090", "0091", "0092", "0093", "0094"}, "0024"),
+  cue("Jail1", {"0090", "0091", "0092", "0093", "0094", "0220"}, "0008"),
+  cue("Momo", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
+  cue("lounge", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
+  cue("Jail2", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0008"),
+  cue("Blcny1", {"0090", "0091", "0092", "0093", "0094"}, "0030"),
+  cue("Priso1", {"0090", "0091", "0092", "0093", "0094"}, "0029"),
+  cue("Ring1", {"0090", "0091", "0092", "0093", "0094", "0097", "0098", "0402"}, "0030"),
+  cue("Ring1", {"0502", "0503"}, "0007"),
+  cue("Ring1", {"0099"}, "0018"),
+  cue("Ring2", {"0090", "0091", "0092", "0093", "0094", "0098", "0420"}, "0030"),
+  cue("Ring2", {"0502"}, "0007"),
+  cue("Ring2", {"0410"}, "0031"),
+  cue("Prefa1", {"0090", "0091", "0092", "0093", "0094"}, "0030"),
+  cue("Priso2", {"0090", "0091", "0092", "0093", "0094", "0250"}, "0029"),
+  cue("Priso2", {"0241"}, "0004"),
+  cue("Ring3", {"0502"}, "0034"),
+  cue("Ring3", {"0502"}, "0032"),
+  cue("Ring3", {"0600"}, "0018"),
+  cue("Little", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
+  cue("Blcny2", {"0090", "0091", "0092", "0093", "0094"}, "0018"),
+  cue("Blcny2", {"0400"}, "0035"),
+  cue("Pixel", {"0090", "0091", "0092", "0093", "0094", "0253"}, "0014"),
+  cue("Hell1", {"0090", "0091", "0092", "0094", "0096"}, "0036"),
+  cue("Hell2", {"0090", "0091", "0092", "0093", "0094"}, "0036"),
+  cue("Hell3", {"0090", "0091", "0092", "0093", "0094"}, "0036"),
+  cue("Hell3", {"0300"}, "0007"),
+  cue("Mapi", {"0420"}, "0004"),
+  cue("Ballo1", {"0095"}, "0039"),
+  cue("Ballo1", {"0500"}, "0004"),
+  cue("Ballo1", {"0900"}, "0007"),
+  cue("Ballo1", {"1000"}, "0032"),
+  cue("Pole", {"0090", "0091", "0092", "0093", "0094", "0095"}, "0008"),
+  cue("Ballo2", {"0500"}, "0034")
+}
+
+local music = Class:extend()
+function music:new()
+  self.betaEnabled = false
+  self.songs = songs
+  self.cues = cues
+end
+
+local _isValid =  function(key, song, self)
+  return not song.jingle and (not song.beta or self.betaEnabled)
+end
+
+function music:getShuffledSongs()
+ return _.shuffle(_.filter(self.songs, _isValid, self))
+end
+
+function music:getCues()
+  return self.cues
+end
+
+-- SHUFFLE songs: every cue with a given song becomes the same, new song
+function music:shuffle(tscFiles)
+  local shuffled = self:getShuffledSongs()
+
+  local idmap = _.map(self.songs, function (k,v)
+    -- don't remap any invalid songs
+    if not _isValid(k,v,self) then return v.id, v.id end
+    return v.id, _.pop(shuffled).id
+  end)
+
+  for k,cue in pairs(self.cues) do
+    cue.songid = idmap[cue.songid]
+  end
+  self:writeCues(tscFiles)
+end
+
+-- RANDOMIZE songs: any cue can play any song
+function music:randomize(tscFiles)
+  for k,cue in pairs(self.cues) do
+    cue.songid = self:getShuffledSongs()[1].id
+  end
+  self:writeCues(tscFiles)
+end
+
+function music:writeCues(tscFiles)
+  for k,cue in pairs(self.cues) do
+    for k,event in ipairs(cue.events) do
+      tscFiles[cue.map]:placeSongAtCue(cue.songid, event, cue.map, cue.default)
+    end
+  end
+end
+
+return music
\ No newline at end of file
diff --git a/src/randomizer.lua b/src/randomizer.lua
index cca714c..ab093b7 100644
--- a/src/randomizer.lua
+++ b/src/randomizer.lua
@@ -1,6 +1,7 @@
 local Items = require 'database.items'
 local TscFile  = require 'tsc_file'
 local WorldGraph = require 'database.world_graph'
+local Music = require 'database.music'
 
 local C = Class:extend()
 
@@ -14,6 +15,12 @@ do
       end
     end
   end
+  for key, cue in pairs(Music():getCues()) do
+    local filename = cue.map
+    if not _.contains(TSC_FILES, filename) then
+      table.insert(TSC_FILES, filename)
+    end
+  end
 end
 
 local csdirectory
@@ -32,6 +39,8 @@ function C:new()
   self._isCaveStoryPlus = false
   self.itemDeck = Items()
   self.worldGraph = WorldGraph(self.itemDeck)
+  self.music = Music()
+
   self.customseed = nil
   self.puppy = false
   self.obj = ""
@@ -61,8 +70,10 @@ function C:randomize()
   self:_updateSharecode(seed)
 
   local tscFiles = self:_createTscFiles(dirStage)
-  -- self:_writePlaintext(tscFiles)
+  
   self:_shuffleItems(tscFiles)
+  self.music:randomize(tscFiles)
+
   self:_writeModifiedData(tscFiles)
   self:_writePlaintext(tscFiles)
   self:_writeLog()
diff --git a/src/tsc_file.lua b/src/tsc_file.lua
index 6a56b89..2213f51 100644
--- a/src/tsc_file.lua
+++ b/src/tsc_file.lua
@@ -19,20 +19,6 @@ function C:new(path)
 
   assert(file:close())
   assert(file:release())
-
-  -- Determine set of items which can be replaced later.
-  --[[
-  self._unreplaced = {}
-  self._mapName = path:match("^.+/(.+)$")
-  for k, v in pairs(ITEM_DATA) do repeat
-    if (v.map .. '.tsc') ~= self._mapName then
-      break -- continue
-    end
-    local item = _.clone(v)
-    table.insert(self._unreplaced, item)
-  until true end
-  self._unreplaced = _.shuffle(self._unreplaced)
-  ]]
 end
 
 function C:hasUnreplacedItems()
@@ -48,103 +34,51 @@ function C:placeItemAtLocation(item, location)
   end
 end
 
-function C:replaceItem(replacement)
-  assert(self:hasUnreplacedItems())
-  local key = self._unreplaced[#self._unreplaced].key
-  self:replaceSpecificItem(key, replacement)
+function C:placeSongAtCue(songid, event, map, originalid)
+  local wasChanged
+  self._text, wasChanged = self:_stringReplace(self._text, "<CMU" .. originalid, "<CMU" .. songid, event, {"<CMU0015", "<CMU0000"})
+  if not wasChanged then
+    local template = "Unable to replace [%s] event #%s's music cue with %q."
+    logWarning(template:format(map, event, songid))
+  end
 end
 
-function C:replaceSpecificItem(originalKey, replacement)
-  -- Fetch item with key matching originalKey.
-  local original
-  for index, item in ipairs(self._unreplaced) do
-    if item.key == originalKey then
-      original = item
-      table.remove(self._unreplaced, index)
-      break
+function C:_stringReplace(text, needle, replacement, label, overrides)
+  overrides = overrides or {}
+  local pStart, pEnd = self:_getLabelPositionRange(label)
+
+  local i, o = -1, -1
+  while(o <= i) do
+    o = nil
+    i = text:find(needle, pStart)
+
+    if i == nil then
+      logDebug(('Unable to replace "%s" with "%s"'):format(needle, replacement))
+      return text, false
+    elseif i > pEnd then
+      -- This is totally normal and can be ignored.
+      logDebug(('Found "%s", but was outside of label.'):format(needle, replacement))
+      return text, false
     end
-  end
-  assert(original, 'No unreplaced item with key: ' .. originalKey)
 
-  -- Log change
-  local template = "[%s] %s -> %s"
-  logNotice(template:format(self._mapName, original.name, replacement.name))
-
-  -- Replace before.
-  if original.replaceBefore then
-    for needle, replacement in pairs(original.replaceBefore) do
-      local wasChanged
-      self._text, wasChanged = self:_stringReplace(self._text, needle, replacement, original.label)
-
-      -- Log error if replace was not optional.
-      if wasChanged == false then
-        local wasOptional = false
-        for _, pattern in ipairs(OPTIONAL_REPLACES) do
-          if needle:find(pattern, 1, true) then
-            wasOptional = true
-            break
-          end
-        end
-        if wasOptional == false then
-          local template = 'Unable to replace [%s] "%s" with "%s".'
-          logError(template:format(original.map, needle, replacement))
+    -- find the earliest occurence of an override
+    for k,v in ipairs(overrides) do
+      local over = text:find(v, pStart)
+      if over ~= nil then
+        if o ~= nil then
+          o = math.min(o, over)
+        else
+          o = over
         end
       end
     end
+
+    -- no overrides found
+    if o == nil then break end
+
+    pStart = o+1
   end
 
-  -- Replace attributes.
-  self:_replaceAttribute(original, replacement, 'command')
-  self:_replaceAttribute(original, replacement, 'getText')
-  self:_replaceAttribute(original, replacement, 'displayCmd')
-  self:_replaceAttribute(original, replacement, 'music')
-end
-
-function C:_replaceAttribute(original, replacement, attribute)
-  local originalTexts = original[attribute]
-  if originalTexts == nil or originalTexts == '' then
-    return
-  elseif type(originalTexts) == 'string' then
-    originalTexts = {originalTexts}
-  end
-
-  local replaceText = replacement[attribute] or ''
-  if type(replaceText) == 'table' then
-    replaceText = replaceText[1]
-  end
-  -- Fix: After collecting Curly's Panties or Chako's Rouge, music would go silent.
-  if attribute == 'music' and replaceText == '' then
-    replaceText = "<CMU0010"
-  end
-
-  -- Loop through each possible original value until we successfully replace one.
-  for _, originalText in ipairs(originalTexts) do repeat
-    if originalText == "" then
-      break -- continue
-    end
-    local changed
-    self._text, changed = self:_stringReplace(self._text, originalText, replaceText, original.label)
-    if changed then
-      return
-    end
-  until true end
-
-  local logMethod = (attribute == 'command') and logError or logWarning
-  local template = 'Unable to replace original "%s" for [%s] %s.'
-  logMethod(template:format(attribute, original.map, original.name))
-end
-
-function C:_stringReplace(text, needle, replacement, label)
-  local pStart, pEnd = self:_getLabelPositionRange(label)
-  local i = text:find(needle, pStart)
-  if i == nil then
-    logNotice(('Unable to replace "%s" with "%s"'):format(needle, replacement))
-    return text, false
-  elseif i > pEnd then
-    -- This is totally normal and can be ignored.
-    logNotice(('Found "%s", but was outside of label.'):format(needle, replacement))
-    return text, false
-  end
   local len = needle:len()
   local j = i + len - 1
   assert((i % 1 == 0) and (i > 0) and (i <= j), tostring(i))
@@ -170,7 +104,7 @@ function C:_getLabelPositionRange(label)
 
   assert(type(label) == 'string')
   assert(#label == 4)
-  assert(tonumber(label) >= 1)
+  assert(tonumber(label) >= 0)
   assert(tonumber(label) <= 9999)
 
   local i = 1