1
0
Fork 0
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:
Juhani Numminen 2020-10-06 14:35:28 +03:00
parent dfff276fc1
commit bf9ce90a5b
4 changed files with 222 additions and 120 deletions

View file

@ -33,6 +33,7 @@ from pysollib.actions import PysolMenubar
from pysollib.actions import PysolToolbar from pysollib.actions import PysolToolbar
from pysollib.app_stat_result import GameStatResult from pysollib.app_stat_result import GameStatResult
from pysollib.app_statistics import Statistics from pysollib.app_statistics import Statistics
from pysollib.cardsetparser import read_cardset_config
from pysollib.gamedb import GAME_DB, GI, loadGame from pysollib.gamedb import GAME_DB, GI, loadGame
from pysollib.help import destroy_help_html, help_about from pysollib.help import destroy_help_html, help_about
from pysollib.images import Images, SubsampledImages from pysollib.images import Images, SubsampledImages
@ -53,7 +54,7 @@ from pysollib.pysoltk import SelectCardsetDialogWithPreview
from pysollib.pysoltk import SelectDialogTreeData from pysollib.pysoltk import SelectDialogTreeData
from pysollib.pysoltk import destroy_find_card_dialog from pysollib.pysoltk import destroy_find_card_dialog
from pysollib.pysoltk import loadImage, wm_withdraw 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 Music, MusicManager
from pysollib.resource import Sample, SampleManager from pysollib.resource import Sample, SampleManager
from pysollib.resource import Tile, TileManager 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 # read & parse a cardset config.txt file - see class Cardset in resource.py
def _readCardsetConfig(self, dirname, filename): def _readCardsetConfig(self, dirname, filename):
with open(filename, "r") as f: cs = read_cardset_config(dirname, filename)
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
# set offsets from options.cfg # set offsets from options.cfg
if cs.ident in self.opt.offsets: if cs.ident in self.opt.offsets:
cs.CARD_XOFFSET, cs.CARD_YOFFSET = self.opt.offsets[cs.ident] 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): def initCardsets(self):
manager = self.cardset_manager manager = self.cardset_manager
@ -1232,9 +1122,8 @@ Please select a %(correct_type)s type cardset.
# print '+', cs.name # print '+', cs.name
fnames[cs.name] = 1 fnames[cs.name] = 1
else: else:
print_err('fail _readCardsetConfig: %s %s' print_err('failed to parse cardset file: %s'
% (d, f)) % f)
pass
except Exception: except Exception:
# traceback.print_exc() # traceback.print_exc()
pass pass

150
pysollib/cardsetparser.py Normal file
View 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

View file

@ -44,6 +44,7 @@ print('ok 1 - imported')
for ver in PY_VERS: for ver in PY_VERS:
for mod in [ for mod in [
'pysol_tests.acard_unit', 'pysol_tests.acard_unit',
'pysol_tests.cardsetparser',
'pysol_tests.game_drag', 'pysol_tests.game_drag',
'pysol_tests.hint', 'pysol_tests.hint',
'pysol_tests.import_file1', 'pysol_tests.import_file1',

View 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')