Module:ScenarioArchiveToWiki

From Fire Emblem Heroes Wiki
Jump to: navigation, search

This module has two functions.

convert[edit source]

This function converts raw decompressed message archives processed by FEAT (this expects and only works with FEAT's specific brand of formatting, it may not work if you use any other tool or process the archives manually) into wikitext for story pages.

Edit the module page and paste this in the console:

p.convert({ args = { [==[PasteEnglishHere]==], [==[PasteJapaneseHere]==]} })

convertJSON[edit source]

This function converts HertzDevil's JSON-formatted dumps into wikitext.

Paste this in the console:
p.convertJSON({args={[==[
EnglishJSONHere
]==], 
[==[
JapaneseJSONHere
]==]}})

Adding to the template[edit source]

There are a couple of parts to dialogue:

 $WmMEID_ブルーノ,ch04_23_Marth_F_Mask,Face|$w0|…………$k$p世界は、絶望に\n覆われようとしている…$k$p行かなければ。\n取り戻さなければ、未来を…$k$p$Sbs150|
  • Red is the internal ID of the nameplate the game uses. Here it is ブルーノ, which turns into ??? when displayed in-game. Technically the full ID also includes the MEID_ but that is dropped for the purposes of this template (It's highly unlikely it would matter, and if it does, just convert to wikitext manually).
  • Blue is the internal ID for the image that is used. It does not always match the nameplate, here Marth: Enigmatic Blade is shown as ??? despite the Hero's name being "Marth".
  • Green is the expression.
  • Purple is the dialogue.
  • Gray is the other stuff the template removes.


These templates store the conversion for nameplates and heroes, if the output is wrong it is most likely because these pages have not been updated:

These are ordered from most important to least important. The module does not check Template:NpidToNameplateEN for example if the Japanese nameplate matches the Japanese name.

Misc[edit source]

  • $k: Tap to advance
    • Almost always succeeded by $p.
  • $p: Scroll up text
    • Always preceded by $k.
  • $Nu: [Summoner]
  • $Nf: [Friend]
  • $Sbs{number}|: Unknown. Possibly delay in ms.
  • $E{expression}|: Expression
  • $Sbv{unknown},{unknown2}|: Unknown. Only appears in XX002. Appears to have no visible effect.
  • $Sbp{BGM ID}|: BGM
  • $Ssp{SE ID}|: Sound effect
  • $Fo{number1},{R},{G},{B},{Unknown2}|: Fade out the screen to a specified background color. Unknown1 is possibly the time in ms to complete this fade. Unknown2 is probably alpha. R,G,B,A go from 0 to 255.
  • $Fi{number}|: Unknown. Always seen paired after $Fo, so most likely time in ms to fade back in the normal screen.
  • $Sbs{unknown}|: Unknown
  • $w{unknown}|: Unknown



local p = {}
local cargo = mw.ext.cargo
--[======[
	convert JSON use:
p.convertJSON({args={[==[
EnglishTextHere
]==], 
[==[
JapaneseTextHere
]==]}})
	
	]======]
function p.convert(frame) -- Console use: p.convert({ args = { [==[EnglishTextHere]==], [==[JapaneseTextHere]==]} })
	local jsonTable1 = {}
	for key,val in mw.ustring.gmatch(frame.args[1] or "", "%s*(.-): ([^\n]*)" ) do
		jsonTable1[#jsonTable1 + 1] = {["key"] = key, ["value"] = val}
	end
	local jsonTable2 = {}
	for key,val in mw.ustring.gmatch(frame.args[2] or "", "%s*(.-): ([^\n]*)" ) do
		jsonTable2[#jsonTable2 + 1] = {["key"] = key, ["value"] = val}
	end
	frame.args[1] = mw.text.jsonEncode(jsonTable1)
	frame.args[2] = mw.text.jsonEncode(jsonTable2)
	return p.convertJSON(frame)
end

local function parseScenario(s, lang)
	local frame = mw.getCurrentFrame()
	local wikitext = ""
	local parsed = ""
	local sections = {""}
	local mwgsub = mw.ustring.gsub
	local mwtrim = mw.text.trim
	local mwsplit = mw.text.split
	local mwsub = mw.ustring.sub
	local i = 1
	s = mwgsub( s, "$k" , "" )
	s = mwgsub( s, "$p" , "\t" )
	s = mwgsub( s, "$Nu", "{{Summoner}}" )
	s = mwgsub( s, "$Nf", "{{Friend}}" )
	for section in mw.ustring.gmatch(s, "$[^$]*") do
		local parts = mwsplit(section, "|")
		for _,v in ipairs(parts) do
			if v ~= "" then
				sections[i] = mwgsub( mwgsub( mwtrim(v), "%s*\t%s*" , "<br />" ), "\n" , "<br />" )
				i = i + 1
			end
		end
	end
	local npid
	local name
	local nameJPJA
	local heroMatchesName
	local img
	local expression
	for k,section in ipairs(sections) do
		if mwsub(section, 1, 1) ~= "$" then
			local STT = "{{StoryTextTable"
			local dialogue = section
			if dialogue ~= "" then
				if not heroMatchesName then
					STT = STT .. "|nameplate=" .. name
				end
				STT = STT .. "|" .. img
				if expression ~= "Face" then
					STT = STT .. "|expression=" .. (mw.ustring.match( expression, "Face_(.*)" ) or "")
				end
				STT = STT .. "|" .. dialogue
				if lang == "ja" then
					STT = STT .. "|ja}}\n"
				else
					STT = STT .. "}}\n"
				end
				wikitext = wikitext .. STT
			end
		elseif mwsub(section, 2, 3) == "Wm" then
			local speakerInfo = mwsplit(mwsub(section, 4), ",")
			npid = mw.ustring.match( speakerInfo[1], "M[EP]ID_(.*)" )
			nameJPJA = frame:expandTemplate{ title = "npidToNameplateJP", args = { npid } }
			img = frame:expandTemplate{ title = 'imgToHero', args = { speakerInfo[2] } }
			heroMatchesName = nameJPJA == frame:expandTemplate{ title = "GetHeroJPName", args = { img } }
			if lang == "ja" then
				name = nameJPJA
			else
				name = frame:expandTemplate{ title = "npidToNameplateEN", args = { npid } }
			end
			expression = speakerInfo[3]
		elseif mwsub(section, 2, 2) == "E" then
			expression = mwsub(section, 3)
		elseif mwsub(section, 2, 4) == "Sbp" then
			wikitext = wikitext .. "{{StoryBGM|" .. mwsub(mw.text.split(section, ",")[1], 5) .. "}}\n"
		elseif mwsub(section, 2, 4) == "Ssp" then
			wikitext = wikitext .. "{{StorySE|" .. mwsub(mw.text.split(section, ",")[1], 5) .. "}}\n"
		elseif mwsub(section, 2, 3) == "Fo" then
			local params = mwsplit(mwsub(section, 4), ",")
			wikitext = wikitext .. "{{StoryFo|" .. table.concat(params,"|") .. "}}\n"
		end
	end
	return wikitext
end

local function parseStructure(obj, lang)
	local wikitext = ""
	local opening
	local mapBegin
	local mapEnd
	local ending
	local sectionCount = 0
	for _,t in ipairs(obj) do
		if (t["key"] == "MID_SCENARIO_OPENING_BGM") then
			if not opening then	opening = "===Opening===\n{{StoryTextTableHeader}}\n"	end
			opening = opening .. "{{StoryBGM|" .. t["value"] .. "}}\n"
		elseif (t["key"] == "MID_SCENARIO_OPENING_IMAGE") then
			if not opening then	opening = "===Opening===\n{{StoryTextTableHeader}}\n"	end
			opening = opening .. "{{StoryImage|" .. t["value"] .. "}}\n"
		elseif (t["key"] == "MID_SCENARIO_OPENING") then
			if not opening then	opening = "===Opening===\n{{StoryTextTableHeader}}\n"	end
			opening = opening .. parseScenario(t["value"], lang)
		elseif (t["key"] == "MID_SCENARIO_MAP_BEGIN_BGM") then
			if not mapBegin then	mapBegin = "===Beginning of the battle===\n{{StoryTextTableHeader}}\n"	end
			mapBegin = mapBegin .. "{{StoryBGM|" .. t["value"] .. "}}\n"
		elseif (t["key"] == "MID_SCENARIO_MAP_BEGIN_IMAGE") then
			if not mapBegin then	mapBegin = "===Beginning of the battle===\n{{StoryTextTableHeader}}\n"	end
			mapBegin = mapBegin .. "{{StoryImage|" .. t["value"] .. "}}\n"
		elseif (t["key"] == "MID_SCENARIO_MAP_BEGIN") then
			if not mapBegin then	mapBegin = "===Beginning of the battle===\n{{StoryTextTableHeader}}\n"	end
			mapBegin = mapBegin .. parseScenario(t["value"], lang)
		elseif (t["key"] == "MID_SCENARIO_MAP_END_BGM") then
			if not mapEnd then	mapEnd = "====Stage Clear====\n{{StoryTextTableHeader}}\n"	end
			mapEnd = mapEnd .. "{{StoryBGM|" .. t["value"] .. "}}\n"
		elseif (t["key"] == "MID_SCENARIO_MAP_END_IMAGE") then
			if not mapEnd then	mapEnd = "====Stage Clear====\n{{StoryTextTableHeader}}\n"	end
			mapEnd = mapEnd .. "{{StoryImage|" .. t["value"] .. "}}\n"
		elseif (t["key"] == "MID_SCENARIO_MAP_END") then
			if not mapEnd then	mapEnd = "====Stage Clear====\n{{StoryTextTableHeader}}\n"	end
			mapEnd = mapEnd .. parseScenario(t["value"], lang)
		elseif (t["key"] == "MID_SCENARIO_ENDING_BGM") then
			if not ending then	ending = "===Ending===\n{{StoryTextTableHeader}}\n"	end
			ending = ending .. "{{StoryBGM|" .. t["value"] .. "}}\n"
		elseif (t["key"] == "MID_SCENARIO_ENDING_IMAGE") then
			if not ending then	ending = "===Ending===\n{{StoryTextTableHeader}}\n"	end
			ending = ending .. "{{StoryImage|" .. t["value"] .. "}}\n"
		elseif (t["key"] == "MID_SCENARIO_ENDING") then
			if not ending then	ending = "===Ending===\n{{StoryTextTableHeader}}\n"	end
			ending = ending .. parseScenario(t["value"], lang)
		end
	end
	if opening then
		sectionCount = sectionCount + 1
		wikitext = wikitext .. opening .. "{{StoryTextTableEnd}}\n"
	end
	if mapBegin then
		sectionCount = sectionCount + 1
		wikitext = wikitext .. mapBegin .. "{{StoryTextTableEnd}}\n"
	end
	if mapEnd then
		sectionCount = sectionCount + 1
		wikitext = wikitext .. mapEnd .. "{{StoryTextTableEnd}}\n"
	end
	if ending then
		sectionCount = sectionCount + 1
		wikitext = wikitext .. ending .. "{{StoryTextTableEnd}}\n"
	end
	return wikitext, sectionCount
end

function p.convertJSON(frame)
	local wikiTextUSEN,sectionCountUSEN = parseStructure(mw.text.jsonDecode(frame.args[1] or "[]"), "en")
	local wikiTextJPJA,sectionCountJPJA = parseStructure(mw.text.jsonDecode(frame.args[2] or "[]"), "ja")
	
	if sectionCountUSEN == sectionCountJPJA and sectionCountUSEN == 1 then
		local sectionHeader = mw.ustring.match( wikiTextUSEN, "=+[^=]+=+\n" )
		
		outputWikitext = "==Story==\n"..
		sectionHeader..
		"<tabber>English=\n"..
		mw.ustring.gsub( wikiTextUSEN, "=+[^=]+=+\n", "", 1 )..
		"|-|Japanese=\n"..
		mw.ustring.gsub( wikiTextJPJA, "=+[^=]+=+\n", "", 1 )..
		"</tabber>\n{{StoryNav||}}\n"
	else
		outputWikitext = "==Story==\n<tabber>English=\n"..wikiTextUSEN.."|-|Japanese=\n"..wikiTextJPJA.."</tabber>\n{{StoryNav||}}\n"
	end
	mw.log(mw.text.trim(outputWikitext))
	return mw.getCurrentFrame():callParserFunction{ name = '#tag', args = { 'pre', outputWikitext } }
end

return p