From 1980c5276959679ced38ef7414bd27145f761429 Mon Sep 17 00:00:00 2001 From: PostScriptReal <149461738+PostScriptReal@users.noreply.github.com> Date: Thu, 3 Jul 2025 23:50:14 +1000 Subject: [PATCH 1/4] Fix typo for GoldSRC compatibility check --- menus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menus.py b/menus.py index 9120ab9..d147c69 100644 --- a/menus.py +++ b/menus.py @@ -452,7 +452,7 @@ def __init__(self, template, master, startHidden:bool=False): self.logVal = BooleanVar(self.advOpt, value=False) self.logChk = Checkbutton(self.advOpt, text="Write log to file", variable=self.logVal, command=self.setLog) self.mVal = BooleanVar(self.advOpt, value=self.presetDat["-m"]) - self.mChk = Checkbutton(self.advOpt, text="GoldSRC compatability", variable=self.mVal) + self.mChk = Checkbutton(self.advOpt, text="GoldSRC compatibility", variable=self.mVal) self.uVal = BooleanVar(self.advOpt, value=self.presetDat["-u"]) self.uChk = Checkbutton(self.advOpt, text="Fix UV shifts", variable=self.uVal) self.vVal = BooleanVar(self.advOpt, value=self.presetDat["-V"]) From 623e16584d439eef338d288452c87ea305b6e5c9 Mon Sep 17 00:00:00 2001 From: PostScriptReal <149461738+PostScriptReal@users.noreply.github.com> Date: Fri, 4 Jul 2025 17:12:02 +1000 Subject: [PATCH 2/4] Add 0.52 Alpha (v6) .mdl support for decompiler --- README.md | 4 ++ menus.py | 110 ++++++++++++++++++++++++++++++++-------- third_party/mdl6dec.exe | Bin 0 -> 20480 bytes 3 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 third_party/mdl6dec.exe diff --git a/README.md b/README.md index d5cdf04..d4e4459 100644 --- a/README.md +++ b/README.md @@ -65,5 +65,9 @@ jsonc MIT + + GeckoN's v6 MDL Decompiler + Proprietary + diff --git a/menus.py b/menus.py index d147c69..22071c2 100644 --- a/menus.py +++ b/menus.py @@ -248,8 +248,10 @@ def __init__(self, template, master, updFunc, startHidden:bool=False): cList = open("save/compilers.txt", "r") cOptions = cList.read().split('\n') cOptions.pop(len(cOptions)-1) + cList.close() self.selComp = "GoldSRC" - self.gameSel = ttk.Combobox(master, values=cOptions) + self.comps = GamesHandler(cOptions) + self.gameSel = ttk.Combobox(master, values=self.comps.gNames) self.gameSel.current(0) self.gameSel.bind("<>", self.chComp) self.setupLabel = Label(master, text="Compiler Setup", background=thme["bg"], foreground=thme["txt"]) @@ -265,6 +267,9 @@ def __init__(self, template, master, updFunc, startHidden:bool=False): self.csPathButton = Button(self.top, text="Save Path", command=self.savePath) if not startHidden: self.show() + + self.addGame = Button(self.top, text="Add New Game", command=self.addNComp) + self.saveGame = Button(self.top, text="Save Game", command=self.saveComp) # Applying theme self.applyTheme(master) @@ -303,6 +308,45 @@ def applyTheme(self, master): except: pass + def addNComp(self): + pass + """self.gameSel.set("") + self.name.set("") + self.typeSel.current(0) + self.hrBool.set(False) + self.ucBool.set(False) + self.fbBool.set(False) + self.newGame = True""" + + def saveComp(self): + pass + """if self.newGame: + # Newgrounds Reference!?!?! + self.nG = self.name.get() + if not self.nG.lower() == "goldsrc" or not self.nG.lower() == "svengine": + oList = open("save/games.txt", "w") + self.gOptions.append(f"{self.nG}~") + nList = '\n'.join(self.gOptions) + nList = nList + '\n' + oList.write(nList) + oList.close() + uJS = { + self.nG: { + "type": self.typeSel.get(), + "capabilities": { + "fullbright": self.fbBool.get(), + "1024px": self.hrBool.get(), + "unlockedChrome": self.ucBool.get() + } + } + } + js = open(f"save/user/game{self.nG}.json", "w") + js.write(json.dumps(uJS, sort_keys=True, indent=5)) + js.close() + self.games = GamesHandler(self.gOptions) + self.gameSel["values"] = self.games.gNames + self.updFunc(self.games)""" + def inputHandler(self, e=False): self.csPath.set(self.csPathEntry.get()) self.csPaths[self.gameSel.get()] = self.csPath.get() @@ -633,6 +677,7 @@ def startDecomp(self): output = self.out.get() gotArgs = False cmdArgs = self.getArgs() + error = False if not cmdArgs == "" or not cmdArgs == " ": gotArgs = True if output == "" or output == None: @@ -654,6 +699,25 @@ def startDecomp(self): # So instead I have to use wine for Mac systems """elif sys.platform == 'darwin': tOutput = subprocess.getoutput(f'wine third_party/mdldec_win32.exe \"{mdl}\"')""" + # Checking for errors (especially the 'unknown Studio MDL format') + if tOutput.find("unknown Studio MDL format version 6") != -1: + # Telling the file moving part of the function that we are decompiling a v6 MDL file. + error = True + if sys.platform == 'linux': + shutil.copy(mdl, './') + tOutput = subprocess.getoutput(f'wine \"{os.getcwd()}/third_party/mdl6dec.exe\" \"{os.path.basename(mdl)}\" -p \"MDL6job\"') + os.remove(f"{os.path.basename(mdl)}") + # Moving the decompiler output to the output folder! + if not os.path.exists(output): + os.mkdir(output) + for f in os.listdir('MDL6job'): + print(f) + shutil.copy(f"MDL6job/{f}", os.path.join(output, f)) + shutil.rmtree('MDL6job') + else: + tOutput = subprocess.getoutput(f'\"{os.getcwd()}/third_party/mdl6dec.exe\" \"{mdl}\" -p \"{output}\"') + elif tOutput.find("ERROR:") != -1: + error = True print(tOutput) self.console.setOutput(tOutput) if self.logVal.get(): @@ -663,29 +727,31 @@ def startDecomp(self): log.write(tOutput) log.close() # Moving files to output directory (this is a workaround to a bug with Xash3D's model decompiler) - filesToMove = [] - mdlFolder = os.path.dirname(mdl) - anims = os.path.join(mdlFolder, 'anims/') - texFolder = os.path.join(mdlFolder, 'textures/') - for f in os.listdir(mdlFolder): - print(f) - if f.endswith("smd") or f.endswith("qc"): - shutil.copy(f"{mdlFolder}/{f}", os.path.join(output, f)) - os.remove(f"{mdlFolder}/{f}") - elif f.endswith("bmp") and not self.tVal.get(): - shutil.copy(f"{mdlFolder}/{f}", os.path.join(output, f)) - os.remove(f"{mdlFolder}/{f}") - shutil.copytree(anims, os.path.join(output, 'anims/')) - if self.tVal.get(): - shutil.copytree(texFolder, os.path.join(output, 'textures/')) + if not error: + if not os.path.exists(output): + os.mkdir(output) + mdlFolder = os.path.dirname(mdl) + anims = os.path.join(mdlFolder, 'anims/') + texFolder = os.path.join(mdlFolder, 'textures/') + for f in os.listdir(mdlFolder): + print(f) + if f.endswith("smd") or f.endswith("qc"): + shutil.copy(f"{mdlFolder}/{f}", os.path.join(output, f)) + os.remove(f"{mdlFolder}/{f}") + elif f.endswith("bmp") and not self.tVal.get(): + shutil.copy(f"{mdlFolder}/{f}", os.path.join(output, f)) + os.remove(f"{mdlFolder}/{f}") + shutil.copytree(anims, os.path.join(output, 'anims/')) + if self.tVal.get(): + shutil.copytree(texFolder, os.path.join(output, 'textures/')) + try: + shutil.rmtree(texFolder) + except: + pass try: - shutil.rmtree(texFolder) + shutil.rmtree(anims) except: pass - try: - shutil.rmtree(anims) - except: - pass class CompMenu(): def __init__(self, template, master, startHidden:bool=False): @@ -1354,7 +1420,7 @@ def __init__(self, template, master, startHidden:bool=False): self.ver = vnum.read().replace("(OS)", sys.platform) self.setupLabel = Label(master, text=f"Snark {self.ver} by:", background=thme["bg"], foreground=thme["txt"]) credits = ["PostScript", "\nusing:", "MDLDec by Flying With Gauss", "get_image_size by Paulo Scardine", "TkTooltip by DaedalicEntertainment", - "JSONC by John Carter" + "JSONC by John Carter", "MDL6Dec by GeckoN" ] self.nameLabel = Label(master, text="\n".join(credits), background=thme["bg"], fg=thme["txt"]) # Tooltips diff --git a/third_party/mdl6dec.exe b/third_party/mdl6dec.exe new file mode 100644 index 0000000000000000000000000000000000000000..104152083bc7e58d07f259d2e31e1a6560ac23d1 GIT binary patch literal 20480 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 NY2YUf{QsnZ{{;23g$e)w literal 0 HcmV?d00001 From 8c41c2a4952fc12ca1eac7273e1fba8aef72625b Mon Sep 17 00:00:00 2001 From: PostScriptReal <149461738+PostScriptReal@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:08:24 +1000 Subject: [PATCH 3/4] Fix check for >512 textures while game supports it --- menus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menus.py b/menus.py index 22071c2..b069f75 100644 --- a/menus.py +++ b/menus.py @@ -340,7 +340,7 @@ def saveComp(self): } } } - js = open(f"save/user/game{self.nG}.json", "w") + js = open(f"save/user/comp{self.nG}.json", "w") js.write(json.dumps(uJS, sort_keys=True, indent=5)) js.close() self.games = GamesHandler(self.gOptions) @@ -1190,7 +1190,7 @@ def compatChk(self, e=False): if not gameDat["capabilities"]["1024px"] and handler.check1024px(): warnings.append("WARNING: The selected game does not support textures higher than 512x512, please downscale the offending textures!") else: - if handler.check1024px() and gameDat["capabilities"]["unlockedChrome"]: + if handler.check1024px() and gameDat["capabilities"]["1024px"]: warnings.append("WARNING: The selected compiler does not support textures higher than 512x512, please downscale the offending textures!") elif handler.check1024px(): warnings.append("WARNING: The selected compiler and game does not support textures higher than 512x512, please downscale the offending textures!") From e0947ccce42f4960c4f5e82aab56340d2085b851 Mon Sep 17 00:00:00 2001 From: PostScriptReal <149461738+PostScriptReal@users.noreply.github.com> Date: Fri, 18 Jul 2025 15:15:15 +1000 Subject: [PATCH 4/4] Fix for QCHandler - There will now no longer be errors when compiling a model if it doesn't follow the directory structure that Crowbar and Xash3D's decompiler outputs by default. - Bumped version to 0.2.4 - Removed "open-source" from "open-source libraries" as not all third-party programs and libraries are strictly open-source. --- README.md | 2 +- helpers.py | 255 ++++++++++++++++++++++++++++------------------------ menus.py | 14 +-- version.txt | 2 +- 4 files changed, 149 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index d4e4459..09bdf67 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@

If you want a bug to be fixed, open up an issue on the Github Repo and describe the issue, with steps to reproduce, operating system information, and/or terminal logs if applicable. If the program doesn't automatically open up with a terminal by default, then open up a terminal either by right-clicking and clicking 'Open in Terminal' in the context menu that pops up on Linux systems or on Windows systems, click the bar that shows the path to Snark and type 'cmd', then type in the name of the executable and hit enter (on Linux systems you need to type in './' before the executable name, otherwise the system will think it's a terminal command and not an executable). If it is a feature request, tag it as a feature request and describe what you want to have implemented into Snark. You can check the Official Snark Development Tracker to see your bug report or feature request while it's in progress.


External Libraries/Programs Used

-

Snark uses some open-source libraries for proper usage, these libraries along with their licenses are shown in the table below:

+

Snark uses some libraries for proper usage, these libraries along with their licenses are shown in the table below:

diff --git a/helpers.py b/helpers.py index ba8d3d7..c8b0c73 100644 --- a/helpers.py +++ b/helpers.py @@ -150,50 +150,85 @@ def __init__(self, qc): self.qcLoc = os.path.dirname(qc) self.cbarFrmt = False - def crowbarFormatCheck(self): + def relPathCheck(self): checks = 0 count = -1 - cd = False - cdTex = 0 + cd = "" + cdTex = "" newCD = "" - cdRef = 0 + cdLoc = 0 newCDtex = "" - cdTexR = 0 + cdTexLoc = 0 self.newQC = self.qcf self.newQCPath = "" while checks < 2: count += 1 - qcL = self.qcf[count] - if qcL.startswith("$cdtex"): - if qcL.find('\"./textures/\"') != -1: - cdTex = 1 - cdTexR = count + try: + qcL = self.qcf[count] + if qcL.startswith("$cdtex"): + cdTexLoc = count + # Getting the string inside the $cdtex command, y'know, the thing inside the quotes? Yeah, that thing. + start = 0 + end = 0 + if qcL.find('\"') != -1: + start = qcL.find('\"') + else: + start = qcL.find("\'") + + if qcL.find('\"', start+1) != -1: + end = qcL.find('\"', start+1) + else: + end = qcL.find("\'", start+1) + cdTex = qcL[start+1:end] + print(f"cdtex directory: {cdTex}") checks += 1 - elif qcL.find('\".\"') != -1: - cdTex = 2 - cdTexR = count + elif qcL.startswith("$cd"): + cdLoc = count + # Getting the string inside the $cdtex command, y'know, the thing inside the quotes? Yeah, that thing. + start = 0 + end = 0 + if qcL.find('\"') != -1: + start = qcL.find('\"') + else: + start = qcL.find("\'") + + if qcL.find('\"', start+1) != -1: + end = qcL.find('\"', start+1) + else: + end = qcL.find("\'", start+1) + cd = qcL[start+1:end] + print(f"cdtex directory: {cdTex}") checks += 1 - elif qcL.startswith("$cd") and qcL.find('\".\"') != -1: - cd = True - cdRef = count - checks += 1 - if cd or cdTex != 0: + except: + print("Something went wrong during cd checks! The model may not compile properly!") + break + if cd or cdTex != "": self.cbarFrmt = True print(cd) print(cdTex) - if cd: - newCD = self.qcf[cdRef] - newCD = newCD.replace('\".\"', f'\"{self.qcLoc}\"') - self.newQC[cdRef] = newCD + if cd != "": + newCD = self.qcf[cdLoc] + if cd == "." or cd == "./": + newCD = newCD.replace(cd, f'{self.qcLoc}') + elif cd.startswith("./"): + newCD = newCD.replace(cd, f'{self.qcLoc}/{cd[2:]}') + else: + newCD = newCD.replace(cd, f'{self.qcLoc}/{cd}') + self.newQC[cdLoc] = newCD print(newCD) - if cdTex == 1: - newCDtex = self.qcf[cdTexR] - newCDtex = newCDtex.replace('\"./textures/\"', f'\"{self.qcLoc}/textures/\"') - self.newQC[cdTexR] = newCDtex + if cdTex != "": + newCDtex = self.qcf[cdTexLoc] + if cdTex == "." or cdTex == "./": + newCDtex = newCDtex.replace(cdTex, f'{self.qcLoc}') + elif cdTex.startswith("./"): + newCDtex = newCDtex.replace(cdTex, f'{self.qcLoc}/{cdTex[2:]}') + else: + newCDtex = newCDtex.replace(cdTex, f'{self.qcLoc}/{cdTex}') + self.newQC[cdTexLoc] = newCDtex elif cdTex == 2: - newCDtex = self.qcf[cdTexR] + newCDtex = self.qcf[cdTexLoc] newCDtex = newCDtex.replace('\".\"', f'\"{self.qcLoc}\"') - self.newQC[cdTexR] = newCDtex + self.newQC[cdTexLoc] = newCDtex self.newQCPath = os.path.join(self.qcLoc, "temp.qc") f = open(self.newQCPath, "w") f.write("".join(self.newQC)) @@ -221,6 +256,7 @@ def getMDLname(self): def check1024px(self): checks = 0 count = -1 + cdTex = "" newCDtex = "" self.newQC = self.qcf self.newQCPath = "" @@ -229,53 +265,48 @@ def check1024px(self): count += 1 qcL = self.qcf[count] if qcL.startswith("$cdtex"): - if qcL.find('\"./textures/\"') != -1: - cdTex = 1 - checks += 1 - elif qcL.find('\".\"') != -1: - cdTex = 2 - checks += 1 - if cdTex != 0: - if cdTex == 1: - count = -1 - texPath = os.path.join(self.qcLoc, "textures/") - textures = os.listdir(texPath) - while count < len(textures)-1: - count += 1 - tex = textures[count] - fTex = os.path.join(self.qcLoc,tex) - if os.path.isfile(fTex): - try: - width, height = get_image_size.get_image_size(os.path.join(self.qcLoc,tex)) - except get_image_size.UnknownImageFormat: - width, height = -1, -1 - if width > 512 or height > 512: - self.found1024 = True - elif cdTex == 2: - count = -1 - files = os.listdir(self.qcLoc) - textures = [] - while count < len(files)-1: - count += 1 - if files[count].endswith('.bmp'): - textures.append(files[count]) - count = -1 - while count < len(textures)-1: - count += 1 - tex = textures[count] - fTex = os.path.join(self.qcLoc,tex) - if os.path.isfile(fTex): - try: - width, height = get_image_size.get_image_size(os.path.join(self.qcLoc,tex)) - except get_image_size.UnknownImageFormat: - width, height = -1, -1 - if width > 512 or height > 512: - self.found1024 = True + # Getting the string inside the $cdtex command, y'know, the thing inside the quotes? Yeah, that thing. + start = 0 + end = 0 + if qcL.find('\"') != -1: + start = qcL.find('\"') + else: + start = qcL.find("\'") + + if qcL.find('\"', start+1) != -1: + end = qcL.find('\"', start+1) + else: + end = qcL.find("\'", start+1) + cdTex = qcL[start+1:end] + print(f"cdtex directory: {cdTex}") + checks += 1 + if cdTex != "": + # if cdTex == 1: + count = -1 + if cdTex == "." or cdTex == "./": + texPath = self.qcLoc + elif cdTex.startswith("./"): + texPath = os.path.join(self.qcLoc, cdTex[2:]) + else: + texPath = os.path.join(self.qcLoc, cdTex) + textures = os.listdir(texPath) + while count < len(textures)-1: + count += 1 + tex = textures[count] + fTex = os.path.join(self.qcLoc,tex) + if os.path.isfile(fTex) and tex.endswith(".bmp"): + try: + width, height = get_image_size.get_image_size(fTex) + except get_image_size.UnknownImageFormat: + width, height = -1, -1 + if width > 512 or height > 512: + self.found1024 = True return self.found1024 def checkCHROME(self): checks = 0 count = -1 + cdTex = "" newCDtex = "" texmodes = [] self.newQC = self.qcf @@ -285,52 +316,44 @@ def checkCHROME(self): count += 1 qcL = self.qcf[count] if qcL.startswith("$cdtex"): - if qcL.find('\"./textures/\"') != -1: - cdTex = 1 - checks += 1 - elif qcL.find('\".\"') != -1: - cdTex = 2 - checks += 1 - if cdTex != 0: - if cdTex == 1: - count = -1 - texPath = os.path.join(self.qcLoc, "textures/") - textures = os.listdir(texPath) - while count < len(textures)-1: - count += 1 - tex = textures[count] - fTex = os.path.join(self.qcLoc,tex) - texL = tex.lower() - print(tex) - if texL.find("chrome") != -1 and os.path.isfile(fTex): - try: - width, height = get_image_size.get_image_size(os.path.join(texPath,tex)) - except get_image_size.UnknownImageFormat: - width, height = -1, -1 - if not width == 64 or not height == 64: - self.fndUnlChr = True - elif cdTex == 2: - count = -1 - files = os.listdir(self.qcLoc) - textures = [] - while count < len(files)-1: - count += 1 - if files[count].endswith('.bmp'): - textures.append(files[count]) - count = -1 - while count < len(textures)-1: - count += 1 - tex = textures[count] - fTex = os.path.join(self.qcLoc,tex) - texL = tex.lower() - print(tex) - if texL.find("chrome") != -1 and os.path.isfile(fTex): - try: - width, height = get_image_size.get_image_size(fTex) - except get_image_size.UnknownImageFormat: - width, height = -1, -1 - if not width == 64 or not height == 64: - self.fndUnlChr = True + # Getting the string inside the $cdtex command, y'know, the thing inside the quotes? Yeah, that thing. + start = 0 + end = 0 + if qcL.find('\"') != -1: + start = qcL.find('\"') + else: + start = qcL.find("\'") + + if qcL.find('\"', start+1) != -1: + end = qcL.find('\"', start+1) + else: + end = qcL.find("\'", start+1) + cdTex = qcL[start+1:end] + print(f"cdtex directory: {cdTex}") + checks += 1 + if cdTex != "": + # if cdTex == 1: + count = -1 + if cdTex == "." or cdTex == "./": + texPath = self.qcLoc + elif cdTex.startswith("./"): + texPath = os.path.join(self.qcLoc, cdTex[2:]) + else: + texPath = os.path.join(self.qcLoc, cdTex) + textures = os.listdir(texPath) + while count < len(textures)-1: + count += 1 + tex = textures[count] + fTex = os.path.join(self.qcLoc,tex) + texL = tex.lower() + print(tex) + if texL.find("chrome") != -1 and os.path.isfile(fTex) and tex.endswith('.bmp'): + try: + width, height = get_image_size.get_image_size(fTex) + except get_image_size.UnknownImageFormat: + width, height = -1, -1 + if not width == 64 or not height == 64: + self.fndUnlChr = True return self.fndUnlChr def checkTRM(self, renderM:int): count = -1 diff --git a/menus.py b/menus.py index b069f75..624b69d 100644 --- a/menus.py +++ b/menus.py @@ -256,7 +256,9 @@ def __init__(self, template, master, updFunc, startHidden:bool=False): self.gameSel.bind("<>", self.chComp) self.setupLabel = Label(master, text="Compiler Setup", background=thme["bg"], foreground=thme["txt"]) self.nameLabel = Label(self.top, text="Name: ", background=thme["bg"], foreground=thme["txt"]) - self.pathLabel = Label(self.top, text="Custom path: ", background=thme["bg"], foreground=thme["txt"]) + self.pathLabel = Label(self.top, text="Path: ", background=thme["bg"], foreground=thme["txt"]) + self.typeLabel = Label(self.top, text="Engine Type: ") + self.cPathLabel = Label(self.top, text="Custom path: ", background=thme["bg"], foreground=thme["txt"]) self.name = StringVar() self.name.set(cOptions[0]) self.nameEntry = Entry(self.top, textvariable=self.name, width=50) @@ -385,7 +387,7 @@ def chComp(self, e): self.hiddenEdit = False self.nameLabel.grid(column=1, row=4, sticky=(W)) self.nameEntry.grid(column=2, row=4, sticky=(W)) - self.pathLabel.grid(column=1, row=5, sticky="w") + self.cPathLabel.grid(column=1, row=5, sticky="w") self.csPathEntry.grid(column=2, row=5, sticky="w") self.csPathButton.grid(column=3,row=5,sticky="w", padx=(5,0)) # If editing options were available and the compiler has editing disabled @@ -393,7 +395,7 @@ def chComp(self, e): self.hiddenEdit = True self.nameLabel.grid_remove() self.nameEntry.grid_remove() - self.pathLabel.grid(column=1, row=4, sticky="w") + self.cPathLabel.grid(column=1, row=4, sticky="w") self.csPathEntry.grid(column=2, row=4, sticky="w") self.csPathButton.grid(column=3,row=4,sticky="w", padx=(5,0)) @@ -410,13 +412,13 @@ def show(self): self.top.grid(column=1, row=4, sticky="nsew") self.nameLabel.grid(column=1, row=0, sticky=(W)) self.nameEntry.grid(column=2, row=0, sticky=(W)) - self.pathLabel.grid(column=1, row=1, sticky="w") + self.cPathLabel.grid(column=1, row=1, sticky="w") self.csPathEntry.grid(column=2, row=1, sticky="w", padx=(15,0)) self.csPathButton.grid(column=3,row=1,sticky="w", padx=(5,0)) else: self.hiddenEdit = True self.top.grid(column=1, row=4, sticky="nsew") - self.pathLabel.grid(column=1, row=0, sticky="w") + self.cPathLabel.grid(column=1, row=0, sticky="w") self.csPathEntry.grid(column=2, row=0, sticky="w", padx=(15,0)) self.csPathButton.grid(column=3,row=0,sticky="w", padx=(5,0)) @@ -1330,7 +1332,7 @@ def startCompile(self): cOpts = self.getCompilerOptions() # Checking if the QC file supplied uses relative pathing for $cd and $cdtexture as the compiler cannot find the files otherwise qcRelChk = QCHandler(mdl) - qcRelChk.crowbarFormatCheck() + qcRelChk.relPathCheck() if qcRelChk.cbarFrmt: mdl = qcRelChk.newQCPath diff --git a/version.txt b/version.txt index 824a641..f35b749 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v0.2.3-(OS)-alpha +v0.2.4-(OS)-alpha
Library/Program