mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-05 00:02:29 -04:00
Refactor cardset config.txt parser to a new module
This commit is contained in:
parent
dfff276fc1
commit
bf9ce90a5b
4 changed files with 222 additions and 120 deletions
129
pysollib/app.py
129
pysollib/app.py
|
@ -33,6 +33,7 @@ from pysollib.actions import PysolMenubar
|
|||
from pysollib.actions import PysolToolbar
|
||||
from pysollib.app_stat_result import GameStatResult
|
||||
from pysollib.app_statistics import Statistics
|
||||
from pysollib.cardsetparser import read_cardset_config
|
||||
from pysollib.gamedb import GAME_DB, GI, loadGame
|
||||
from pysollib.help import destroy_help_html, help_about
|
||||
from pysollib.images import Images, SubsampledImages
|
||||
|
@ -53,7 +54,7 @@ from pysollib.pysoltk import SelectCardsetDialogWithPreview
|
|||
from pysollib.pysoltk import SelectDialogTreeData
|
||||
from pysollib.pysoltk import destroy_find_card_dialog
|
||||
from pysollib.pysoltk import loadImage, wm_withdraw
|
||||
from pysollib.resource import CSI, Cardset, CardsetConfig, CardsetManager
|
||||
from pysollib.resource import CSI, CardsetManager
|
||||
from pysollib.resource import Music, MusicManager
|
||||
from pysollib.resource import Sample, SampleManager
|
||||
from pysollib.resource import Tile, TileManager
|
||||
|
@ -1070,125 +1071,14 @@ Please select a %(correct_type)s type cardset.
|
|||
|
||||
# read & parse a cardset config.txt file - see class Cardset in resource.py
|
||||
def _readCardsetConfig(self, dirname, filename):
|
||||
with open(filename, "r") as f:
|
||||
lines = f.readlines()
|
||||
lines = [line.strip() for line in lines]
|
||||
if not lines[0].startswith("PySol"):
|
||||
return None
|
||||
config = CardsetConfig()
|
||||
if not self._parseCardsetConfig(config, lines):
|
||||
# print filename, 'invalid config'
|
||||
return None
|
||||
if config.CARDD > self.top.winfo_screendepth():
|
||||
return None
|
||||
cs = Cardset()
|
||||
cs.dir = dirname
|
||||
cs.update(config.__dict__)
|
||||
return cs
|
||||
|
||||
def _parseCardsetConfig(self, cs, line):
|
||||
def perr(line, field=None, msg=''):
|
||||
if not DEBUG:
|
||||
return
|
||||
if field:
|
||||
print_err('_parseCardsetConfig error: line #%d, field #%d %s'
|
||||
% (line, field, msg))
|
||||
else:
|
||||
print_err('_parseCardsetConfig error: line #%d: %s'
|
||||
% (line, msg))
|
||||
if len(line) < 6:
|
||||
perr(1, msg='number of lines')
|
||||
return 0
|
||||
# line[0]: magic identifier, possible version information
|
||||
fields = [f.strip() for f in line[0].split(';')]
|
||||
if len(fields) >= 2:
|
||||
m = re.search(r"^(\d+)$", fields[1])
|
||||
if m:
|
||||
cs.version = int(m.group(1))
|
||||
if cs.version >= 3:
|
||||
if len(fields) < 5:
|
||||
perr(1, msg='number of fields')
|
||||
return 0
|
||||
cs.ext = fields[2]
|
||||
m = re.search(r"^(\d+)$", fields[3])
|
||||
if not m:
|
||||
perr(1, 3, 'not integer')
|
||||
return 0
|
||||
cs.type = int(m.group(1))
|
||||
m = re.search(r"^(\d+)$", fields[4])
|
||||
if not m:
|
||||
perr(1, 4, 'not integer')
|
||||
return 0
|
||||
cs.ncards = int(m.group(1))
|
||||
if cs.version >= 4:
|
||||
if len(fields) < 6:
|
||||
perr(1, msg='number of fields')
|
||||
return 0
|
||||
styles = fields[5].split(",")
|
||||
for s in styles:
|
||||
m = re.search(r"^\s*(\d+)\s*$", s)
|
||||
if not m:
|
||||
perr(1, 5, 'not integer')
|
||||
return 0
|
||||
s = int(m.group(1))
|
||||
if s not in cs.styles:
|
||||
cs.styles.append(s)
|
||||
if cs.version >= 5:
|
||||
if len(fields) < 7:
|
||||
perr(1, msg='number of fields')
|
||||
return 0
|
||||
m = re.search(r"^(\d+)$", fields[6])
|
||||
if not m:
|
||||
perr(1, 6, 'not integer')
|
||||
return 0
|
||||
cs.year = int(m.group(1))
|
||||
if len(cs.ext) < 2 or cs.ext[0] != ".":
|
||||
perr(1, msg='invalid extention')
|
||||
return 0
|
||||
# line[1]: identifier/name
|
||||
if not line[1]:
|
||||
perr(2, msg='empty line')
|
||||
return 0
|
||||
cs.ident = line[1]
|
||||
m = re.search(r"^(.*;)?([^;]+)$", cs.ident)
|
||||
if not m:
|
||||
perr(2, msg='invalid format')
|
||||
return 0
|
||||
cs.name = m.group(2).strip()
|
||||
# line[2]: CARDW, CARDH, CARDD
|
||||
m = re.search(r"^(\d+)\s+(\d+)\s+(\d+)", line[2])
|
||||
if not m:
|
||||
perr(3, msg='invalid format')
|
||||
return 0
|
||||
cs.CARDW, cs.CARDH, cs.CARDD = \
|
||||
int(m.group(1)), int(m.group(2)), int(m.group(3))
|
||||
# line[3]: CARD_UP_YOFFSET, CARD_DOWN_YOFFSET,
|
||||
# SHADOW_XOFFSET, SHADOW_YOFFSET
|
||||
m = re.search(r"^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", line[3])
|
||||
if not m:
|
||||
perr(4, msg='invalid format')
|
||||
return 0
|
||||
cs.CARD_XOFFSET = int(m.group(1))
|
||||
cs.CARD_YOFFSET = int(m.group(2))
|
||||
cs.SHADOW_XOFFSET = int(m.group(3))
|
||||
cs.SHADOW_YOFFSET = int(m.group(4))
|
||||
# line[4]: default background
|
||||
back = line[4]
|
||||
if not back:
|
||||
perr(5, msg='empty line')
|
||||
return 0
|
||||
# line[5]: all available backgrounds
|
||||
cs.backnames = [f.strip() for f in line[5].split(';')]
|
||||
if back in cs.backnames:
|
||||
cs.backindex = cs.backnames.index(back)
|
||||
else:
|
||||
cs.backnames.insert(0, back)
|
||||
cs.backindex = 0
|
||||
cs = read_cardset_config(dirname, filename)
|
||||
# set offsets from options.cfg
|
||||
if cs.ident in self.opt.offsets:
|
||||
cs.CARD_XOFFSET, cs.CARD_YOFFSET = self.opt.offsets[cs.ident]
|
||||
# if cs.type != 1: print cs.type, cs.name
|
||||
return 1
|
||||
|
||||
if cs.CARDD > self.top.winfo_screendepth():
|
||||
return None
|
||||
return cs
|
||||
|
||||
def initCardsets(self):
|
||||
manager = self.cardset_manager
|
||||
|
@ -1232,9 +1122,8 @@ Please select a %(correct_type)s type cardset.
|
|||
# print '+', cs.name
|
||||
fnames[cs.name] = 1
|
||||
else:
|
||||
print_err('fail _readCardsetConfig: %s %s'
|
||||
% (d, f))
|
||||
pass
|
||||
print_err('failed to parse cardset file: %s'
|
||||
% f)
|
||||
except Exception:
|
||||
# traceback.print_exc()
|
||||
pass
|
||||
|
|
150
pysollib/cardsetparser.py
Normal file
150
pysollib/cardsetparser.py
Normal file
|
@ -0,0 +1,150 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- mode: python; coding: utf-8; -*-
|
||||
# ---------------------------------------------------------------------------##
|
||||
#
|
||||
# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
|
||||
# Copyright (C) 2003 Mt. Hood Playing Card Co.
|
||||
# Copyright (C) 2005-2009 Skomoroh
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ---------------------------------------------------------------------------##
|
||||
|
||||
import re
|
||||
|
||||
from pysollib.mfxutil import print_err
|
||||
from pysollib.resource import Cardset, CardsetConfig
|
||||
|
||||
|
||||
def read_cardset_config(dirname, filename):
|
||||
"""Parse a cardset config file and produce a Cardset object.
|
||||
|
||||
This function returns None if any errors occurred.
|
||||
"""
|
||||
with open(filename, "r") as f:
|
||||
lines = f.readlines()
|
||||
lines = [line.strip() for line in lines]
|
||||
if not lines[0].startswith("PySol"):
|
||||
return None
|
||||
config = parse_cardset_config(lines)
|
||||
if not config:
|
||||
print_err('invalid cardset: %s' % filename)
|
||||
return None
|
||||
cs = Cardset()
|
||||
cs.dir = dirname
|
||||
cs.update(config.__dict__)
|
||||
return cs
|
||||
|
||||
|
||||
def parse_cardset_config(line):
|
||||
def perr(line_no, field=None, msg=''):
|
||||
if field:
|
||||
print_err('cannot parse cardset config: line #%d, field #%d: %s'
|
||||
% (line_no, field, msg))
|
||||
else:
|
||||
print_err('cannot parse cardset config: line #%d: %s'
|
||||
% (line_no, msg))
|
||||
|
||||
cs = CardsetConfig()
|
||||
if len(line) < 6:
|
||||
perr(1, msg='not enough lines in file')
|
||||
return None
|
||||
# line[0]: magic identifier, possible version information
|
||||
fields = [f.strip() for f in line[0].split(';')]
|
||||
if len(fields) >= 2:
|
||||
m = re.search(r"^(\d+)$", fields[1])
|
||||
if m:
|
||||
cs.version = int(m.group(1))
|
||||
if cs.version >= 3:
|
||||
if len(fields) < 5:
|
||||
perr(1, msg='not enough fields')
|
||||
return None
|
||||
cs.ext = fields[2]
|
||||
m = re.search(r"^(\d+)$", fields[3])
|
||||
if not m:
|
||||
perr(1, 3, 'not integer')
|
||||
return None
|
||||
cs.type = int(m.group(1))
|
||||
m = re.search(r"^(\d+)$", fields[4])
|
||||
if not m:
|
||||
perr(1, 4, 'not integer')
|
||||
return None
|
||||
cs.ncards = int(m.group(1))
|
||||
if cs.version >= 4:
|
||||
if len(fields) < 6:
|
||||
perr(1, msg='not enough fields')
|
||||
return None
|
||||
styles = fields[5].split(",")
|
||||
for s in styles:
|
||||
m = re.search(r"^\s*(\d+)\s*$", s)
|
||||
if not m:
|
||||
perr(1, 5, 'not integer')
|
||||
return None
|
||||
s = int(m.group(1))
|
||||
if s not in cs.styles:
|
||||
cs.styles.append(s)
|
||||
if cs.version >= 5:
|
||||
if len(fields) < 7:
|
||||
perr(1, msg='not enough fields')
|
||||
return None
|
||||
m = re.search(r"^(\d+)$", fields[6])
|
||||
if not m:
|
||||
perr(1, 6, 'not integer')
|
||||
return None
|
||||
cs.year = int(m.group(1))
|
||||
if len(cs.ext) < 2 or cs.ext[0] != ".":
|
||||
perr(1, msg='specifies an invalid file extension')
|
||||
return None
|
||||
# line[1]: identifier/name
|
||||
if not line[1]:
|
||||
perr(2, msg='unexpected empty line')
|
||||
return None
|
||||
cs.ident = line[1]
|
||||
m = re.search(r"^(.*;)?([^;]+)$", cs.ident)
|
||||
if not m:
|
||||
perr(2, msg='invalid format')
|
||||
return None
|
||||
cs.name = m.group(2).strip()
|
||||
# line[2]: CARDW, CARDH, CARDD
|
||||
m = re.search(r"^(\d+)\s+(\d+)\s+(\d+)", line[2])
|
||||
if not m:
|
||||
perr(3, msg='invalid format')
|
||||
return None
|
||||
cs.CARDW, cs.CARDH, cs.CARDD = \
|
||||
int(m.group(1)), int(m.group(2)), int(m.group(3))
|
||||
# line[3]: CARD_UP_YOFFSET, CARD_DOWN_YOFFSET,
|
||||
# SHADOW_XOFFSET, SHADOW_YOFFSET
|
||||
m = re.search(r"^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", line[3])
|
||||
if not m:
|
||||
perr(4, msg='invalid format')
|
||||
return None
|
||||
cs.CARD_XOFFSET = int(m.group(1))
|
||||
cs.CARD_YOFFSET = int(m.group(2))
|
||||
cs.SHADOW_XOFFSET = int(m.group(3))
|
||||
cs.SHADOW_YOFFSET = int(m.group(4))
|
||||
# line[4]: default background
|
||||
back = line[4]
|
||||
if not back:
|
||||
perr(5, msg='unexpected empty line')
|
||||
return None
|
||||
# line[5]: all available backgrounds
|
||||
cs.backnames = [f.strip() for f in line[5].split(';')]
|
||||
if back in cs.backnames:
|
||||
cs.backindex = cs.backnames.index(back)
|
||||
else:
|
||||
cs.backnames.insert(0, back)
|
||||
cs.backindex = 0
|
||||
|
||||
# if cs.type != 1: print cs.type, cs.name
|
||||
return cs
|
|
@ -44,6 +44,7 @@ print('ok 1 - imported')
|
|||
for ver in PY_VERS:
|
||||
for mod in [
|
||||
'pysol_tests.acard_unit',
|
||||
'pysol_tests.cardsetparser',
|
||||
'pysol_tests.game_drag',
|
||||
'pysol_tests.hint',
|
||||
'pysol_tests.import_file1',
|
||||
|
|
62
tests/lib/pysol_tests/cardsetparser.py
Normal file
62
tests/lib/pysol_tests/cardsetparser.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
# Written by Juhani Numminen, under the MIT Expat License.
|
||||
|
||||
import unittest
|
||||
|
||||
from pysollib.cardsetparser import parse_cardset_config
|
||||
from pysollib.resource import CSI, CardsetConfig
|
||||
|
||||
|
||||
class MyTests(unittest.TestCase):
|
||||
|
||||
def _assertCcEqual(self, a, b, msg=None):
|
||||
"""Assert that CardsetConfig objects a and b have equal attributes."""
|
||||
return self.assertDictEqual(a.__dict__, b.__dict__, msg)
|
||||
|
||||
def test_good_cardset(self):
|
||||
config_txt = """\
|
||||
PySolFC solitaire cardset;4;.gif;1;52;7
|
||||
123-dondorf;Dondorf
|
||||
79 123 8
|
||||
16 25 7 7
|
||||
back01.gif
|
||||
back01.gif
|
||||
"""
|
||||
|
||||
reference = CardsetConfig()
|
||||
reference.update(dict(
|
||||
version=4,
|
||||
ext='.gif',
|
||||
type=CSI.TYPE_FRENCH,
|
||||
ncards=52,
|
||||
styles=[7],
|
||||
ident='123-dondorf;Dondorf',
|
||||
name='Dondorf',
|
||||
CARDW=79,
|
||||
CARDH=123,
|
||||
CARDD=8,
|
||||
CARD_XOFFSET=16,
|
||||
CARD_YOFFSET=25,
|
||||
SHADOW_XOFFSET=7,
|
||||
SHADOW_YOFFSET=7,
|
||||
backindex=0,
|
||||
backnames=['back01.gif'],
|
||||
))
|
||||
self._assertCcEqual(
|
||||
parse_cardset_config(config_txt.split('\n')),
|
||||
reference,
|
||||
'parse_cardset_config should parse well-formed v4 config.txt ' +
|
||||
'correctly')
|
||||
|
||||
def test_reject_too_few_fields(self):
|
||||
config_txt = """\
|
||||
PySolFC solitaire cardset;4;.gif;1;52
|
||||
123-dondorf;Dondorf
|
||||
79 123 8
|
||||
16 25 7 7
|
||||
back01.gif
|
||||
back01.gif
|
||||
"""
|
||||
self.assertIsNone(
|
||||
parse_cardset_config(config_txt.split('\n')),
|
||||
'parse_cardset_config should reject v4 config.txt with ' +
|
||||
'a missing field on the first line')
|
Loading…
Add table
Reference in a new issue