Advertisement
ijontichy

pk3

Jul 31st, 2014
278
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.11 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. import sys, os, shutil, zipfile, tempfile
  4. import os.path
  5. from subprocess import Popen, PIPE
  6.  
  7. import which
  8. import ansicodes
  9. from pathwalker import pathwalker
  10. from decpreproc import *
  11.  
  12. WAD_DIR = os.environ["HOME"] + "/.pwads"
  13.  
  14. LINEPROCS = [defineproc.DefineProc(), includeproc.IncludeDirProc()]
  15.  
  16. NUMBERS = "0123456789"
  17. OKSTR = ansicodes.mapColors("[ ok ]", "--22--")
  18. OKLEN = len(ansicodes.stripCodes(OKSTR) )
  19.  
  20. MAKE_ACT = "make"
  21. TEST_ACT = "test"
  22. PACKAGE_ACT = "package"
  23. HELP_ACT = "help"
  24.  
  25. USAGE = "usage: {} [{}] <args>".format(os.path.basename(sys.argv[0]), "|".join([MAKE_ACT, TEST_ACT, PACKAGE_ACT, HELP_ACT]))
  26.  
  27. USAGE_FULL = """
  28. - make: Builds a PK3 from the files in the pk3/ directory, with the name being
  29. that of the current directory.
  30.  
  31. - test [port=zandronum] <args>: Runs 'make', and then runs 'port' with the PK3,
  32. along with any arguments you choose.
  33.  
  34. - package [version]: Runs 'make', with 'version' at the end of the name, copies
  35. it to a ZIP with the same name, and copies the PK3 to "{0}" if the
  36. directory exists.
  37.  
  38. - help: Prints this.
  39.  
  40. Examples:
  41. # PWD is /home/derp/derpPK3
  42. packagepk3.py make # derpPK3.pk3
  43. packagepk3.py test zandronum -file nuts
  44. packagepk3.py package 1_5 # derpPK3_1_5.pk3, derpPK3_1_5.zip,
  45. # /home/derp/.zdoom/derpPK3_1_5.pk3
  46. # PWD is /home/derp/derpWAD
  47. packagepk3.py package 1_5 # derpWAD1_5.pk3, derpWAD1_5.zip,
  48. # /home/derp/.zdoom/derpWAD1_5.pk3
  49. """.format(WAD_DIR)
  50.  
  51. DECORATE_EXTS = ("dec",)
  52. ACS_EXTS = ("c", "acs")
  53. ACS_OBJ_EXTS = ("o",)
  54. BACKUP_EXTS = ("bak", "bkp", "swp")
  55.  
  56. ACC_ERROR = "**** ERROR ****"
  57.  
  58. DEFAULT_ARGS = ["-iwad doom2", "-warp 01"]
  59.  
  60. def warn(reason):
  61. print("warning:", reason, file=sys.stderr)
  62.  
  63.  
  64. def errorExit(code = 1, reason = None):
  65. if reason:
  66. print("ERROR:", reason, file=sys.stderr)
  67.  
  68. sys.exit(code)
  69.  
  70. def usageExit(code = 1, reason = None):
  71. if reason:
  72. print("error:", reason, file=sys.stderr)
  73.  
  74. usage()
  75. sys.exit(code)
  76.  
  77. def usage():
  78. print(USAGE)
  79.  
  80. def usageFull():
  81. usage()
  82. print(USAGE_FULL)
  83.  
  84. NO_ARGS, NO_PK3, NO_ACC, NO_VERSION, ACC_FAIL, NO_BINARY, PREPROC_FAIL = range(4, 11)
  85.  
  86. PK3NAME = os.path.basename(os.path.realpath("."))
  87.  
  88. if not os.path.isdir(WAD_DIR):
  89. warn("{} does not exist - no pk3 auto-installation will be done".format(WAD_DIR))
  90. WAD_DIR = None
  91.  
  92. validProgs = ["gzdoom", "zdoom", "zandronum"]
  93. progPaths = []
  94. zdoomExe = "zdoom" + (".exe" if sys.platform[:3] == "win" else "")
  95. zandronumExe = "zandronum" + (".exe" if sys.platform[:3] == "win" else "")
  96.  
  97. progSuffix = ".exe" if sys.platform[:3] == "win" else ""
  98.  
  99. for prog in validProgs:
  100. progExe = prog + progSuffix
  101. progPath = which.which(progExe) or ""
  102.  
  103. progPaths.append(progPath)
  104.  
  105. ZDOOM = progPaths[0] or progPaths[1]
  106. ZANDRONUM = progPaths[2]
  107.  
  108. if not ZDOOM: warn("no zdoom in PATH or PWD")
  109. if not ZANDRONUM: warn("no zandronum in PATH or PWD")
  110.  
  111.  
  112. if which.is_exe("acc"):
  113. ACC = os.path.realpath("acc")
  114. else:
  115. ACC = which.which("acc")
  116.  
  117. if ACC is None:
  118. ACC_DIR = None
  119. errorExit(NO_ACC, "no acc in PATH or PWD")
  120. else:
  121. ACC_DIR = ACC.rpartition("/")[0]
  122.  
  123.  
  124.  
  125. def playPK3(pk3, binary=ZANDRONUM, *args):
  126. args = list(args)
  127.  
  128. if binary is None:
  129. errorExit(1, "no binary")
  130.  
  131. if binary[0] in "+-":
  132. args[:0] = [binary]
  133. binary = ZANDRONUM
  134.  
  135. pk3 = os.path.realpath(pk3)
  136.  
  137. if not os.path.isfile(pk3):
  138. errorExit(NO_BINARY, "somehow, {} doesn't exist".format(pk3))
  139.  
  140. binPath = which.which(binary)
  141.  
  142. if not binPath:
  143. errorExit(NO_BINARY, "no such binary {}".format(binary))
  144.  
  145. if not args:
  146. args = DEFAULT_ARGS
  147. elif isinstance(args, str):
  148. args = [i for i in args.split()] or []
  149. else:
  150. args = list(args)
  151.  
  152. pk3Game = Popen([binPath] + args + ["-file {}".format(pk3)])
  153.  
  154. try:
  155. pk3Game.wait()
  156. except KeyboardInterrupt:
  157. pk3Game.send_signal(2) # SIGINT
  158. sys.exit(2)
  159.  
  160. return 0
  161.  
  162.  
  163. def compileACS(file):
  164. file2 = file
  165. file = os.path.realpath(file)
  166.  
  167. if not os.path.isfile(file):
  168. errorExit(ACC_FAIL, "must provide name of actual file")
  169.  
  170. newFile = file.rpartition(".")[:2] + ("o",)
  171. newFile = "".join(newFile)
  172.  
  173. newFile2 = file2.rpartition(".")[:2] + ("o",)
  174. newFile2 = "".join(newFile2)
  175.  
  176. cmdline = [ACC, "-i", ACC_DIR, file, newFile]
  177. accProc = Popen(cmdline, stdout=PIPE, stderr=PIPE)
  178.  
  179. accOut, accErr = (i.decode().split("\n") for i in accProc.communicate())
  180.  
  181. if ACC_ERROR in accErr:
  182. reasonLines = accErr[accErr.index(ACC_ERROR)+1:]
  183. reason = "\n".join(reasonLines)
  184.  
  185. raise RuntimeError(reason)
  186.  
  187. return newFile2
  188.  
  189. def statPrint(statStr):
  190. statParts = statStr.split("\x1F")
  191. statParts[0] = ansicodes.mapColors(statParts[0], "A6666A")
  192. statParts[1:1] = [" "]
  193. statColor = ansicodes.mapColors(statParts, "---")
  194. print(statColor, end="")
  195.  
  196.  
  197. def makePK3(aArgs):
  198. if not os.path.isdir("pk3"):
  199. errorExit(NO_PK3, "no pk3/ directory")
  200.  
  201. parseDecorate = "parse" in (i.lower() for i in aArgs)
  202.  
  203. pk3Walk = pathwalker.PathWalker('pk3')
  204.  
  205. oldDir = os.getcwd()
  206. os.chdir("pk3")
  207.  
  208. pk3Name = "{}.pk3".format(PK3NAME)
  209. pk3Name2 = os.path.realpath("../{}.pk3".format(PK3NAME) )
  210. pk3Zip = zipfile.ZipFile(pk3Name2, "w", zipfile.ZIP_DEFLATED)
  211. pk3Files = pk3Walk.walk(abs=1, fFilter=lambda x: not x.endswith("*.o")).flattenFiles(rel=1)
  212. pk3Total = len(pk3Files)
  213. toBuild = []
  214. nLen = 0
  215. padLength = max(len(i) for i in pk3Files)
  216.  
  217. try:
  218. conWidth = int(os.environ.get("COLUMNS"))
  219. except (TypeError, ValueError):
  220. conWidth = 80
  221. builtFiles = []
  222.  
  223. try:
  224. if os.path.isfile("pk3/buildFiles"):
  225. toBuild = open("pk3/buildFiles", "r").readlines()
  226.  
  227. for i, file in enumerate(pk3Files):
  228. j = i + 1
  229.  
  230. oLen = nLen
  231. perc = "{}%".format(int( ( (j / pk3Total) * 100) ) )
  232. nLen = len(perc)
  233.  
  234. statStr = "[{:>4}]\x1F".format(perc)
  235.  
  236. fPath, fName = os.path.realpath(file).rsplit("/", 1)
  237.  
  238. try:
  239. fNameClean, fExt = fName.rsplit(".", 1)
  240. except ValueError:
  241. fNameClean, fExt = fName, ""
  242.  
  243. zipFName = file.lstrip("./")
  244.  
  245. if not os.path.isfile(file):
  246. statStr += "{} doesn't exist...".format(fName)
  247. statPrint(statStr)
  248.  
  249. elif fExt in BACKUP_EXTS:
  250. statStr += "ignore {}...".format(fName)
  251. statPrint(statStr)
  252.  
  253. elif fExt in DECORATE_EXTS and parseDecorate: # DECORATE
  254. statStr += "preprocessing {}...".format(fName)
  255. statPrint(statStr)
  256.  
  257. prevDir = os.getcwd()
  258.  
  259. os.chdir(fPath)
  260.  
  261. dTmpName = tempfile.mkstemp(prefix="dec")[1]
  262. dTmp = open(dTmpName, "w")
  263.  
  264. try:
  265. dTmp.write(fileproc.processFile(fName, LINEPROCS))
  266. except lineproc.LineProcError as exc:
  267. reason = exc.args[0]
  268.  
  269. print("\n\n !!! ERROR !!!", file=sys.stderr)
  270. print(" In file {}:".format(fPath), file=sys.stderr)
  271. print(" {}".format(reason), file=sys.stderr)
  272. sys.exit(PREPROC_FAIL)
  273.  
  274. finally:
  275. dTmp.close()
  276.  
  277. os.chdir(prevDir)
  278.  
  279. pk3Zip.write(dTmpName, zipFName)
  280.  
  281. elif fExt in ACS_OBJ_EXTS:
  282. for acsExt in ACS_EXTS:
  283. if file in builtFiles:
  284. statStr += "already added {}...".format(fName)
  285. statPrint(statStr)
  286. break
  287.  
  288. possibleSource = fPath + os.sep + fNameClean + "." + acsExt
  289.  
  290. if os.path.exists(possibleSource):
  291. statStr += "ignore {}...".format(fName)
  292. statPrint(statStr)
  293. break
  294. else:
  295. statStr += "adding {}...".format(fName)
  296. statPrint(statStr)
  297. pk3Zip.write(file, zipFName)
  298.  
  299. elif (fExt in ACS_EXTS and (not toBuild)) or (fName in toBuild): # ACS
  300. statStr += "building {}...".format(fName)
  301. statPrint(statStr)
  302. objFile = compileACS(file)
  303. pk3Zip.write(objFile, objFile.lstrip("./"))
  304. pk3Zip.write(file, zipFName)
  305.  
  306. builtFiles.append(objFile)
  307.  
  308. else:
  309. statStr += "adding {}...".format(fName)
  310. statPrint(statStr)
  311. pk3Zip.write(file, zipFName)
  312.  
  313. statClean = ansicodes.stripCodes(statStr)
  314. print(" " * (conWidth - len(statClean) - OKLEN) + OKSTR)
  315.  
  316. except KeyboardInterrupt:
  317. print()
  318. os.remove(pk3Name2)
  319. sys.exit(130)
  320.  
  321. except RuntimeError as exc:
  322. print()
  323. errorExit(1, "\n" + exc.args[0])
  324.  
  325. pk3Zip.close()
  326. os.chdir(oldDir)
  327.  
  328. return pk3Name
  329.  
  330. def testPK3(aArgs):
  331. aArgs = list(aArgs)
  332.  
  333. try:
  334. parseIndex = [i.lower() for i in aArgs].index("parse")
  335. aArgs.pop(parseIndex)
  336. pk3 = makePK3(["parse"])
  337. except ValueError:
  338. pk3 = makePK3([])
  339.  
  340. playPK3(pk3, *aArgs)
  341.  
  342.  
  343. def packagePK3(aArgs):
  344.  
  345. if len(aArgs) < 1:
  346. usageExit(NO_VERSION, "no version supplied")
  347.  
  348. try:
  349. parseIndex = [i.lower() for i in aArgs].index("parse")
  350. aArgs.pop(parseIndex)
  351. pk3 = makePK3(["parse"])
  352. except ValueError:
  353. pk3 = makePK3([])
  354.  
  355. print("renaming... ", end="")
  356. sys.stdout.flush()
  357.  
  358. pk3Name = os.path.basename(pk3)
  359. pk3Base = pk3Name.rsplit(".", 1)[0]
  360.  
  361. zipBase = [pk3Base, aArgs[0]]
  362.  
  363. if pk3Base[-1] in NUMBERS:
  364. zipBase = "_".join(zipBase)
  365. else:
  366. zipBase = "".join(zipBase)
  367.  
  368. zipName = "{}.zip".format(zipBase)
  369. pk3Name2 = "{}.pk3".format(zipBase)
  370.  
  371. shutil.move(pk3, pk3Name2)
  372. print("done.")
  373.  
  374. print("packaging... ", end="")
  375. sys.stdout.flush()
  376.  
  377.  
  378. pk3Zip = zipfile.ZipFile(zipName, "w")
  379.  
  380. for file in (pk3Name2, "README", "README.txt", "CHANGELOG", "CHANGELOG.txt"):
  381. if os.path.isfile(file):
  382. pk3Zip.write(file, file)
  383.  
  384. pk3Zip.close()
  385.  
  386. print(zipName)
  387.  
  388. if WAD_DIR is not None:
  389. pk3Name3 = WAD_DIR + "/" + pk3Name2
  390.  
  391. print("copying {} to {}... ".format(pk3Name2, pk3Name3), end="")
  392. sys.stdout.flush()
  393.  
  394. shutil.copy(pk3Name2, pk3Name3)
  395. print("done.")
  396.  
  397. def printHelp(aArgs):
  398. usageFull()
  399.  
  400.  
  401. choices = {MAKE_ACT: makePK3, TEST_ACT: testPK3, PACKAGE_ACT: packagePK3,
  402. HELP_ACT: printHelp}
  403.  
  404. #######
  405. ##
  406. # MAIN CODE
  407. ##
  408. ###
  409.  
  410. def main(*args, **kwargs):
  411. args = list(args)
  412.  
  413. try:
  414. if len(args) < 2:
  415. args.extend(input("wat do? ").split())
  416. except KeyboardInterrupt:
  417. print()
  418. sys.exit(2)
  419.  
  420. action = args[1].lower()
  421. aArgs = args[2:]
  422.  
  423. if action in choices:
  424. choices[action](aArgs)
  425. else:
  426. usageExit(127, "not a valid command")
  427.  
  428. if __name__ == "__main__":
  429. main(*sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement