Skip to content

Commit

Permalink
Created Excel build
Browse files Browse the repository at this point in the history
  • Loading branch information
Ylimegirl committed Oct 14, 2021
1 parent 0c6f640 commit 87c491c
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 57 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ Inspired by [050644zf's ArknightsStoryTextReader](https://github.com/050644zf/Ar

To use, place the .JSON files you want parsed inside an "inputs" directory, and then run in the command line:

`python reader.py`
`python xlsxreader.py`

**If a file is *not* properly formatted as a JSON file, this code will not work!**

## Known issues
- May miss certain types of flavor text
- Output could use some retooling in formatting
- xlsx outputs may miss content the txt outputs include
114 changes: 64 additions & 50 deletions reader.py → xlsxreader.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os
import json
from replacer import nameTrans
from replacer import formatDialogue
from replacer import replaceNewLine
import openpyxl
from openpyxl.styles import Alignment, Font, Border, Side
from xlsxreplacer import nameTrans, formatDialogue
from pretty import prettyJSON

if not os.path.exists("inputs"):
Expand All @@ -27,134 +27,135 @@


dict = json.loads(parsed)#converts json to dict format
newname = "outputs/" + item[0:item.find(".")] + "_parsed.txt"
newname = "outputs/" + item[0:item.find(".")] + "_parsed.xlsx"
if os.path.exists(newname):#deletes _parsed file if it already exists
os.remove(newname)
new_file = open(newname, "a", encoding="utf-8")

new_file = openpyxl.Workbook()
sheet=new_file.active
sheet.title = item[0:item.find(".")]

for group in dict:
if group.startswith("voice"):
new_file.write(group + "\t" + replaceNewLine(dict[group]["serif"].replace("@", "\n")) + "\n")#voice line jasons are so simple. bless
sheet.append([group, dict[group]["serif"].replace("@", "\n")])#voice line jasons are so simple. bless


elif group.startswith("group"):
new_file.write("---------------------------------------------\n[GROUP]\t" + group + "\n")
sheet.append(["GROUP", group])
for x in dict[group]:
for y, z in x.items(): #i don't know if there's a way to do this without three goddamn levels of nesting no. twst's file formatting sucks
if y == "serif" and "text" in z.keys() and "speaker" in z.keys():# handles main dialogue (serifs)
if "visible" in z["speaker"].keys() and not z["speaker"]["visible"]: # handles serifs w/o a speaker
new_file.write("SFX\t" + formatDialogue(z["text"]) + "\n")
sheet.append(["SFX", formatDialogue(z["text"])])
elif "callNext" in z.keys() and not z["callNext"]: # handles bits of dialogue separated by other actions of some sort
new_file.write(nameTrans(z["speaker"]["text"]) + "\t" + formatDialogue(z["text"]))
sheet.append([nameTrans(z["speaker"]["text"]), formatDialogue(z["text"])])
elif "callClear" in z.keys() and not z["callClear"]: # other half of the callNext code
new_file.write(formatDialogue(z["text"]) + "\n")
sheet["B"+str(sheet._current_row)].value=sheet["B"+str(sheet._current_row)].value + formatDialogue(z["text"])
else:
new_file.write(nameTrans(z["speaker"]["text"]) + "\t" + formatDialogue(z["text"]) + "\n")
sheet.append([nameTrans(z["speaker"]["text"]), formatDialogue(z["text"])])
elif y == "serif" and not "text" in z.keys(): # skips serifs without text
pass

elif y == "choice": # handles choices
count = 1
for obj in z:
new_file.write("[CHOICE]\t" + str(count) + ": " + formatDialogue(obj["text"]) + " (GOTO: " + obj["goTo"] + ")\n")
sheet.append(["[CHOICE]", str(count) + ": " + formatDialogue(obj["text"]), "GOTO: " + obj["goTo"]])
count+=1

elif y == "goTo":
new_file.write("[GOTO]\t" + z["goTo"] + "\n") # handles pathing
sheet.append(["[GOTO]", z["goTo"]]) # handles pathing

elif y == "title" and "textWhite" in z.keys() and "textGold" in z.keys(): # handles titles
if "episode" in z.keys(): # handles titles for episodes
new_file.write("[TITLE]\tEpisode " + z["episode"]["storyNum"] + ": " + z["textWhite"] + "\n\t" + z["textGold"] + "\n")
sheet.append(["[TITLE]", "Episode " + z["episode"]["storyNum"] + ": " + z["textWhite"] + "\n" + z["textGold"]])
elif "prologue" in z.keys(): # handles titles for the prologue
new_file.write("[TITLE]\tPrologue " + z["prologue"]["storyNum"] + ": " + z["textWhite"] + "\n\t" + z["textGold"] + "\n")
sheet.append(["[TITLE]", "Prologue " + z["prologue"]["storyNum"] + ": " + z["textWhite"] + "\n" + z["textGold"]])
elif "eventEpisode" in z.keys(): # handles titles for events
new_file.write("[TITLE]\tEvent Episode " + z["eventEpisode"]["storyNum"] + ": " + z["textWhite"] + "\n\t" + z["textGold"] + "\n")
sheet.append(["[TITLE]", "Event Episode " + z["eventEpisode"]["storyNum"] + ": " + z["textWhite"] + "\n" + z["textGold"]])
elif "eventTitle" in z.keys(): # ...also handles titles for events
new_file.write("[TITLE]\tEvent Episode " + z["eventTitle"]["storyNum"] + ": " + z["textWhite"] + "\n\t" + z["textGold"] + "\n")
sheet.append(["[TITLE]", "Event Episode " + z["eventTitle"]["storyNum"] + ": " + z["textWhite"] + "\n" + z["textGold"]])
elif "personal" in z.keys(): # handles titles for personal stories
new_file.write("[TITLE]\t" + z["textGold"] + " " + z["personal"]["rarity"] + ": " + z["personal"]["anotherName"] + "\n\t" + z["textWhite"] + "\n")
sheet.append(["[TITLE]", z["textGold"] + " " + z["personal"]["rarity"] + ": " + z["personal"]["anotherName"] + "\n" + z["textWhite"]])
elif "talk" in z.keys(): # handles titles for chats
new_file.write("[TITLE]\t" + z["textGold"] + " Chat: " + z["talk"]["text"] + "\n\t" + z["textWhite"] + "\n")
sheet.append(["[TITLE]", z["textGold"] + " Chat: " + z["talk"]["text"] + "\n" + z["textWhite"]])
else: # handles other titles
new_file.write("[TITLE]\t" + z["textWhite"] + "\n\t" + z["textGold"] + "\n")
sheet.append(["[TITLE]", z["textWhite"] + "\n" + z["textGold"]])
elif y == "title" and not "textWhite" in z.keys(): # skips titles without text
pass

elif y == "text" and "text" in z.keys(): # handles generic "text" content
new_file.write("[TEXT]\t" + formatDialogue(z["text"]) + "\n")
sheet.append(["[TEXT]", formatDialogue(z["text"])])

elif y == "place":
if "jp" in z.keys() and "en" in z.keys(): # handles place titles
new_file.write("[PLACE]\t" + z["jp"] + "\n\t" + z["en"] + "\n")
sheet.append(["[PLACE]", z["jp"] + "\n" + z["en"]])
elif not "jp" in z.keys(): # skips places without text
pass

elif y == "narration":
if "text" in z.keys(): # handles narration boxes
new_file.write("[NARRATION]\t" + z["text"] + "\n")
sheet.append(["[NARRATION]", z["text"]])
elif "fadeOut" in z.keys():
pass
elif "delete" in z.keys() and z["delete"]:
pass

elif y == "telop":
if "text" in z.keys(): #handles "telops"
new_file.write("[TELOP]\t" + z["text"] + "\n")
sheet.append(["[TELOP]", z["text"]])
elif not "text" in z.keys(): # skips telops without text
pass

elif y == "balloon":
if "text" in z.keys() and "speaker" in z.keys(): # handles speech balloons (in rhythmics and battle cutscenes)
new_file.write(nameTrans(z["speaker"]) + "\t" + formatDialogue(z["text"]) + "\n")
sheet.append([nameTrans(z["speaker"]), formatDialogue(z["text"])])
elif not "text" in z.keys(): # skips balloons without text
pass

elif y == "cardSprite":
if "cardId" in z.keys(): # handles card graphics
if "delete" in z.keys() and z["delete"]:
new_file.write("[CARD]\tHide Card " + str(z["cardId"]) + "\n")
sheet.append(["[CARD]", "Hide Card " + str(z["cardId"])])
elif "isGroovy" in z.keys() and z["isGroovy"]:
new_file.write("[CARD]\tShow Card " + str(z["cardId"]) + " Groovy\n")
sheet.append(["[CARD]", "Show Card " + str(z["cardId"]) + " Groovy"])
else:
new_file.write("[CARD]\tShow Card " + str(z["cardId"]) + "\n")
sheet.append(["[CARD]", "Show Card " + str(z["cardId"])])
elif "delete" in z.keys() and z["delete"] == True:
new_file.write("[CARD]\tHide Card\n")
sheet.append(["[CARD]", "Hide Card"])

elif y == "sprite":
if "path" in z.keys(): # handles item sprites
new_file.write("[SPRITE]\tShow " + z["id"] + " (" + z["path"][z["path"].rfind("/") + 1:] + ")\n")
sheet.append(["[SPRITE]", "Show " + z["id"] + " (" + z["path"][z["path"].rfind("/") + 1:] + ")"])
elif "fadeOut" in z.keys():
new_file.write("[SPRITE]\tFade out " + z["id"] + "\n")
sheet.append(["[SPRITE]", "Fade out " + z["id"]])
elif "visible" in z.keys() and z["visible"] == True:
new_file.write("[SPRITE]\tShow " + z["id"] + "\n")
sheet.append(["[SPRITE]", "Show " + z["id"]])
elif "visible" in z.keys() and z["visible"] == False:
new_file.write("[SPRITE]\tHide " + z["id"] + "\n")
sheet.append(["[SPRITE]", "Hide " + z["id"]])
elif "delete" in z.keys() and z["delete"]:
new_file.write("[SPRITE]\tHide " + z["id"] + "\n")
sheet.append(["[SPRITE]", "Hide " + z["id"]])

elif y == "movie" and "path" in z.keys(): # handles movies
new_file.write("[MOVIE]\t" + z["path"] + "\n")
sheet.append(["[MOVIE]", z["path"]])
elif y == "mirrorMovie":
if "delete" in z.keys() and z["delete"] == True: # handles movies but for the mirrors
pass
elif "mirrorId" in z.keys() and not "animation" in z.keys():
new_file.write("[MIRROR]\tMovie " + str(z["mirrorId"]) + "\n")
sheet.append(["[MIRROR]", "Movie " + str(z["mirrorId"])])
else:
pass

elif y == "blot":
if "dormitoryId" in z.keys() and "animation" in z.keys() and "phase" in z["animation"].keys(): # handles (over)blot animations
new_file.write("[BLOT]\tDorm " + str(z["dormitoryId"]) + " Phase " + str(z["animation"]["phase"]) + "\n")
sheet.append(["[BLOT]", "Dorm " + str(z["dormitoryId"]) + " Phase " + str(z["animation"]["phase"])])
elif "animation" in z.keys() and "phase" in z["animation"].keys():
new_file.write("[BLOT]\tPhase " + str(z["animation"]["phase"]) + "\n")
sheet.append(["[BLOT]", "Phase " + str(z["animation"]["phase"])])
elif "dormitoryId" in z.keys() and not "animation" in z.keys():
pass
elif "delete" in z.keys() and z["delete"]:
pass
elif y == "overBlot":
if "dormitoryId" in z.keys() and "animation" in z.keys():
new_file.write("[BLOT]\tDorm " + str(z["dormitoryId"]) + " Overblot\n")
sheet.append(["[BLOT]", "Dorm " + str(z["dormitoryId"]) + " Overblot"])
elif "dormitoryId" in z.keys() and not "animation" in z.keys():
pass
elif "delete" in z.keys() and z["delete"]:
Expand All @@ -164,44 +165,57 @@
pass

else: # for debugging mostly and to catch types i missed
new_file.write(y + "\t(no code to handle this type of object yet, sorry! --Ylime)\n")
sheet.append([y, "(no code to handle this type of object yet, sorry! --Ylime)"])


elif group.startswith("word") or group.startswith("w") or group.startswith("cut") or group.startswith("c") or group.startswith("motion"): # this applies to both the intro text and also rhythmics. yes <3
groupPrinted = False;
for x in dict[group]:
if(groupPrinted == False and ("balloon" in x.keys() or "narration" in x.keys())):
new_file.write("[" + group + "]\n");
sheet.append(["[" + group + "]"]);
groupPrinted = True;
for y, z in x.items():
if y == "balloon":
if "text" in z.keys() and "targetId" in z.keys():
new_file.write(z["targetId"] + "\t" + formatDialogue(z["text"]) + "\n")
sheet.append([z["targetId"], formatDialogue(z["text"])])
elif "delete" in z.keys() and z["delete"]:
pass

elif y == "narration":
if "text" in z.keys():
new_file.write("[NARRATION]\t" + replaceNewLine(z["text"]) + "\n")
sheet.append(["[NARRATION]", z["text"]])
elif not "text" in z.keys():
pass

elif y == "voice" or y == "voiceWait" or y == "wait" or y == "spineCharacter" or y == "moveCamera" or y == "zoomCamera" or y == "emotion" or y == "spfxTriggerKicker" or y == "spfxTargetPointFollower" or y == "spine":
pass

else:
new_file.write(y + "\t" + str(z) + "(no code to handle this type of object yet, sorry! --Ylime)\n")
sheet.append([y, str(z) + " (no code to handle this type of object yet, sorry! --Ylime)"])

elif group.startswith("initialize"): # skips past rhythmic animations
new_file.write("(no dialogue in this type of JSON object!)\n")
sheet.append(["(no dialogue in this type of JSON object!)"])
break

else:
new_file.write("(no code to handle this type of JSON file yet, sorry! --Ylime)\n")

sheet.append(["(no code to handle this type of JSON file yet, sorry! --Ylime)"])

new_file.write("---------------------------------------------\nTwstStoryReader v" + verNum + " by Ylimegirl\nhttps://github.com/Ylimegirl/TwstStoryReader")
new_file.close()
sheet.append(["TwstStoryReader v" + verNum + " (Excel Build) by Ylimegirl\nhttps://github.com/Ylimegirl/TwstStoryReader"])

sheet.column_dimensions["A"].width=15
sheet.column_dimensions["B"].width=75
for cell in sheet["A"]:
cell.alignment = Alignment(wrap_text=True, vertical="center", horizontal="center")
for cell in sheet["B"]:
cell.alignment = Alignment(wrap_text=True, vertical="center")
if(sheet.max_column >= 3):
sheet.column_dimensions["C"].width=15
for cell in sheet["C"]:
cell.alignment = Alignment(wrap_text=True, vertical="center", horizontal="center")
sheet.merge_cells(start_row=sheet.max_row, start_column=1, end_row=sheet.max_row, end_column=sheet.max_column)
sheet["A"+str(sheet.max_row)].alignment = Alignment(wrap_text=True, horizontal="center", vertical="center")
sheet.row_dimensions[sheet.max_row].height=30
new_file.save(filename=newname)

print("> Parsed " + item)

Expand Down
6 changes: 1 addition & 5 deletions replacer.py → xlsxreplacer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,9 @@ def nameTrans(string):
string = unicodedata.normalize("NFKC", string) # normalizes fullwidth alphanumeric etc. characters to "half-width"
return string

def replaceNewLine(string): # formats newlines in dialogue strings to be indented
return string.replace("\n", "\n\t")

def formatDialogue(string):
string = string.replace("[HERO_NAME]", "ユウ")
string = removeCR(string)
return replaceNewLine(string)
return removeCR(string)

def removeCR(string): # removes stray carriage returns?? why are they here?? hello??
return re.sub("\r(\n|$)", "\g<1>", string)

0 comments on commit 87c491c

Please sign in to comment.