Skip to content

Commit

Permalink
Much better expressions in options; added manifests and folder options
Browse files Browse the repository at this point in the history
  • Loading branch information
skyjake committed Jul 8, 2005
1 parent 0fd512f commit 44f3b09
Show file tree
Hide file tree
Showing 10 changed files with 386 additions and 70 deletions.
60 changes: 56 additions & 4 deletions snowberry/addons.py
Expand Up @@ -214,7 +214,10 @@ def determineLastModified(self):
@return The last modification time.
"""
return os.path.getmtime(self.source)
try:
return os.path.getmtime(self.source)
except OSError:
return 0

def setBox(self, box):
"""Set the BoxAddon this addon is inside of.
Expand Down Expand Up @@ -323,8 +326,12 @@ def readMetaData(self):
def getCommandLine(self, profile):
"""Return the command line options to load and configure the
addon. This basic implementation assumes no configurability
and a single source file."""
return '-file ' + paths.quote(self.source)
and a single source file.
"""
if not paths.hasExtension('manifest', self.source):
return '-file ' + paths.quote(self.source)
else:
return ''

def addKeywords(self, type, identifiers):
"""Define a new keyword for the addon.
Expand Down Expand Up @@ -857,6 +864,15 @@ def getCommandLine(self, profile):
@todo Check profile for extra options?
"""
return '-deh ' + paths.quote(self.source)

def readMetaData(self):
"""Generate metadata by making guesses based on the DEH patch
name and contents."""

metadata = 'category: patches'

self.parseConfiguration(metadata)



class DEDAddon (Addon):
Expand All @@ -874,6 +890,14 @@ def getCommandLine(self, profile):
"""
return '-def ' + paths.quote(self.source)

def readMetaData(self):
"""Generate metadata by making guesses based on the DED patch
name and contents."""

metadata = 'category: definitions'

self.parseConfiguration(metadata)


def _getLatestModTime(startPath):
"""Descends recursively into the subdirectories in startPath to
Expand Down Expand Up @@ -1155,11 +1179,31 @@ def load(fileName, containingBox=None):
if extension == 'box':
addon.loadContents()

# Define the strings of this addon in the language.
# TODO: Define the strings of this addon in the language.

return identifier


def loadManifest(fileName):
"""Manifests contain metadata for other addons. A manifest may
augment or replace the metadata of an existing addon, or define an
entirely new addon.
@param fileName Path of the manifest file.
"""
identifier = paths.getBase(fileName)

if exists(identifier):
addon = get(identifier)
else:
# Create a new addon.
addon = Addon(identifier, fileName)
addons[identifier] = addon

# The manifest contains metadata configuration.
addon.parseConfiguration(file(fileName).read())


def readMetaCache():
"""The metadata cache contains the metadata extracted from all the
installed addons. The cache is stored in the user's addon
Expand Down Expand Up @@ -1362,5 +1406,13 @@ def findConflicts(roster, profile):
os.path.basename(name)):
load(name)

# Load all the manifests.
for folder in [paths.getSystemPath(paths.MANIFESTS),
paths.getUserPath(paths.MANIFESTS)]:
for name in paths.listFiles(folder):
# Case insensitive.
if paths.hasExtension('manifest', name):
loadManifest(name)

# Save the updated cache.
writeMetaCache()
184 changes: 184 additions & 0 deletions snowberry/expressions.py
@@ -0,0 +1,184 @@
# -*- coding: iso-8859-1 -*-
# $Id$
# Snowberry: Extensible Launcher for the Doomsday Engine
#
# Copyright (C) 2005
# Jaakko Keränen <jaakko.keranen@iki.fi>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not: http://www.opensource.org/

## @file expressions.py Expressions
##
## Routines for evaluating expressions.

import string
import os
import profiles as pr
import settings as st
import paths


def evaluate(expression, contextAddon):
"""
@param expression Expression to evaluate as a string.
@param contextAddon Addon in the context of which the evaluation is made.
@return The evaluated version with all functions and variables
expanded.
"""
result = ''
pos = 0

while pos < len(expression):
ch = expression[pos]

# Is this the beginning of a new expression?
if ch == '$':
# Try to scan it.
subExpression = scan(expression[pos:])

if len(subExpression):
result += execute(subExpression[1:], contextAddon)
pos += len(subExpression)
continue

# Append the character to the result.
result += ch
pos += 1

#print 'Evaluated result: ' + result
return result


def scan(src):
"""Scans a subexpression that begins with $."""

pos = 1

# Use angle brackets < and > because they can't be used on the
# command line.

# $FUNCTION
# $FUNCTION<asdas>

while pos < len(src):
ch = src[pos]
if ch not in string.uppercase + '_':
# This is no longer a part of the function name.
if ch == '<':
# Begin an argument block. Find the end.
endPos = findMatchingBracket(src, pos)
if endPos < 0:
# This is invalid!
return ''
return src[:endPos + 1]
break

pos += 1

return src[:pos]


def findMatchingBracket(src, startPos):
level = 0
pos = startPos
while pos < len(src):
if src[pos] == '<':
level += 1
elif src[pos] == '>':
level -= 1
if level == 0:
return pos
pos += 1
# Not good.
return -1


def getCommand(src):
"""Extract the command from the beginning of an expression.
@param src Expression. Must start at <code>$</code>.
@return Command as a string.
"""
command = ''
pos = 0
while pos < len(src):
if src[pos] in string.uppercase + '_':
command += src[pos]
else:
break
pos += 1

return command


def execute(src, contextAddon):
"""Execute a command.
@param src Command to execute. Must start at <code>$</code>.
@return The command's evaluated result.
"""

#print "executing: " + src

command = getCommand(src)
if len(src) > len(command):
argSrc = src[len(command) + 1 : -1]
else:
argSrc = ''

#print 'command: ' + command + ';'
#print 'argSrc: ' + argSrc + ';'

if command == 'BUNDLE':
if contextAddon:
return contextAddon.getContentPath()
else:
return ''

if command == 'DENGBASE':
arg = evaluate(argSrc, contextAddon)
#if st.isDefined('doomsday-base'):
# return os.path.join(st.getSystemString('doomsday-base'), arg)
#else:
return '}' + arg

if command == 'PATH':
arg = os.path.normpath(evaluate(argSrc, contextAddon))
return paths.quote(arg)

if command == 'VALUE':
arg = evaluate(argSrc, contextAddon)
activeProfile = pr.getActive()

# Try local value identifier first.
if contextAddon:
localized = contextAddon.makeLocalId(arg)
v = activeProfile.getValue(localized)
if v:
return v.getValue()

v = activeProfile.getValue(arg)
if v:
return v.getValue()

return ''

if command == 'QUOTE':
return paths.quote( evaluate(argSrc, contextAddon) )

return ''
3 changes: 3 additions & 0 deletions snowberry/lang/english.lang
Expand Up @@ -118,6 +118,7 @@ load-order-move-top: First
load-order-move-bottom: Last

addon-inspector-dialog: Addon Inspector
addon-type-generic: Generic Addon
addon-type-box: Snowberry Box
addon-type-bundle: Snowberry Bundle
addon-type-pk3: PK3 Package
Expand Down Expand Up @@ -272,6 +273,8 @@ category-gamedata-maps-hm: H-M
category-gamedata-maps-ns: N-S
category-gamedata-maps-tz: T-Z
category-gamedata-maps-09: 0-9
category-patches: DeHackEd Patches
category-definitions: Doomsday Engine Definitions

#
# File Types
Expand Down
13 changes: 11 additions & 2 deletions snowberry/paths.py
Expand Up @@ -53,6 +53,7 @@ def isHomeless():
# Directory Type constants to be used with getSystemPath() and
# getUserPath().
ADDONS = 'addons'
MANIFESTS = 'manifests'
UNINSTALLED = 'uninstalled'
CONF = 'conf'
LANG = 'lang'
Expand Down Expand Up @@ -140,8 +141,8 @@ def _checkSnowberryHome():
_createDir(homeDir)

# Create the rest of the user directories if they don't exist yet.
for p in [ADDONS, UNINSTALLED, CONF, LANG, PLUGINS, PROFILES, GRAPHICS,
RUNTIME]:
for p in [ADDONS, MANIFESTS, UNINSTALLED, CONF, LANG, PLUGINS, PROFILES,
GRAPHICS, RUNTIME]:
_createDir(getUserPath(p))


Expand Down Expand Up @@ -256,6 +257,7 @@ def findBitmap(name):

def getBase(path):
"""Returns the file name sans path and extension."""

base = os.path.basename(path)
if base.find('.') < 0:
# There is no extension.
Expand Down Expand Up @@ -284,6 +286,13 @@ def quote(fileName):
return '"' + fileName.replace('"', '""') + '"'


def fixSlashes(path):
"""Replace all forward and backward slashes with the correct
slashes of the host OS."""




def addAddonPath(path):
"""Define a new custom directory for addons. Addons can be loaded
from all the custom directories. Installed addons always go to
Expand Down
7 changes: 6 additions & 1 deletion snowberry/plugins/launcher.py
Expand Up @@ -222,9 +222,14 @@ def generateOptions(profile):
for setting in effectiveSettings:
# If the setting's required addons are not being loaded, skip
# this setting.
skipThis = False
for req in setting.getRequiredAddons():
if req not in usedAddonIds:
continue
skipThis = True
break

if skipThis:
continue

# All settings can't be used at all times. In
# case of a conflict, some of the settings are ignored.
Expand Down
3 changes: 2 additions & 1 deletion snowberry/plugins/profilelist.py
Expand Up @@ -80,13 +80,14 @@ def init():
# Create the profile list and the profile control buttons.
area = ui.getArea(ui.Area.PROFILES)
area.setWeight(1)
area.setBorder(3)
area.setBorder(0) #3)

global profileList
profileList = area.createFormattedList("profile-list")

# This should be a small button.
area.setWeight(0)
area.setBorder(3)
controls = area.createArea(alignment=ui.Area.ALIGN_HORIZONTAL, border=2)
controls.setExpanding(False)
#area.setExpanding(False)
Expand Down

0 comments on commit 44f3b09

Please sign in to comment.