Files
Aislo/tools/stmate-test/conv_win8_ini.py
T

166 lines
8.0 KiB
Python

# -*- coding: utf-8 -*-
"""구버전 → Win8 설정 — 판 가리기(Chk_STDir) · 7.x Start.Ini → Start8x.Ini · DOS Start.MEM 읽기.
근거(지식DB REA 분석/37 §2 · ST_U_Archive Chk_Data 0x459280 · Chk_STDir 0x45a78c · ST_U_Convert 0x48e07c · 0x48fea8):
판 = 파일 이름 묶음 규칙(위에서 처음 맞는 것) · 120·130·140 은 그 설정 [PROJECT] PMODE(기본 1)를 더함 · 110 은 111 고정
7.x 설정 키 대응은 0x48e07c ReadString/Write 차례 · DOS MEM = Clipper 변수 머리 32바이트(이름 11 · 형 +11 · 길이 +16·+17)
"""
from __future__ import annotations
import re
import struct
from dbf_memo import ini_items
RULES = [
(140, ("STINI.MEM", "RATE.DBF", "QCODE.DBF")),
(130, ("START8X.INI", "RATE.DBF", "QCODE.DBF")),
(120, ("START.INI", "FORM.DBF", "KANJUB2.DBF", "IMG.DB")),
(110, ("START.MEM", "FORM.DBF", "KANJUB2.DBF", "COSTN1.DBF")),
(150, ("START8X", "COSTA.DBF", "COSTN.SMT", "PRJLST.SMT", "SYSINFO.DBF")),
]
JODAL = ("GLOBAL.INI", "M.TXT", "L.TXT", "E.TXT", "W.TXT", "UNIT.TXT", "UNITPART.TXT", "J.TXT", "JPART.TXT")
JODAL += ("JUNIT.TXT", "DANGA.TXT", "DANGA.DBF", "DANGA.DBT", "GONGJONG.TXT", "MAIN.TXT", "MAINPART.TXT")
RULES.append((200, JODAL))
CORE = ("COSTN.DBF", "BDQTY.DBF", "PRJLST.DBF", "GRASS.DBF")
def ini_dict(text: str) -> dict[str, dict[str, str]]:
out: dict[str, dict[str, str]] = {}
for sec, k, v in ini_items(text):
out.setdefault(sec.upper(), {}).setdefault(k.upper(), v)
return out
def _pmode(files: dict, name: str) -> int:
raw = files.get(name)
if raw is None:
return 1
v = ini_dict(raw.decode("cp949", "replace")).get("PROJECT", {}).get("PMODE", "1")
try:
return int(v.strip() or "1")
except ValueError:
return 0
def detect(files: dict) -> int:
"""폴더 파일 이름(대문자) → 판 번호 — 111 DOS · 120+PMODE 7.x · 130/140+PMODE 이미 Win8 · 200 조달청 TEXT."""
names = {n.upper() for n in files}
code = 0
if len(":".join(names)) >= 5:
code = next((c for c, need in RULES if all(n in names for n in need)), 0)
if code == 0:
return 100 if names & {"COSTN.DBF", "BDQTY.DBF", "PRJLST.DBF"} else 0
if code not in (150, 200) and not all(n in names for n in CORE):
return 100
if code == 110:
return 111
ini = {120: "START.INI", 130: "START8X.INI", 140: "STINI.MEM"}.get(code)
return code + _pmode(files, ini) if ini else code
# 7.x [START] 키 — (대상 키, 원본 키, 기본값)
START_7X = [("BMKNM", "BMKNM", "MLST"), ("ACDNM", "ACDNM", "1"), ("BPNNM", "ACDNM", "1")]
START_7X += [("KCDNM", "KCDNM", "1"), ("ZCDNM", "ZCDNM", "2"), ("CHGNM", "CHGNM", "1"), ("CHBNM", "CHGNM", "1")]
START_7X += [("CHNNM", "CHNNM", "1"), ("BCDNM", "BCDNM", "0"), ("DCDNM", "DCDNM", "0"), ("ICDNM", "ICDNM", "0")]
START_7X += [("LCDNM", "LCDNM", "1"), ("MCDNM", "MCDNM", "1"), ("PCDNM", "PCDNM", "10")]
START_7X += [("SCDNM", "SCDNM", "0"), ("WCDNM", "WCDNM", "0"), ("SUBNM", "SUBNM", ""), ("LMGNM", "LMGNM", "4")]
START_7X += [("MKRNM", "MKRNM", "5"), ("TMGNM", "TMGNM", "6"), ("JEMNM", "JEMNM", "123456")]
JMOK = ["가격 정보", "물가 자료", "유통 물가", "견적단가1", "견적단가2", "적용 단가"]
START_7X += [(f"JMOK{i}", f"JMOK{i}", v) for i, v in enumerate(JMOK, 1)]
START_7X += [(f"JMK{i}0", f"JMK{i}0", v) for i, v in enumerate(["가.정", "물.자", "유.물", "견적 1", "견적 2"], 1)]
START_7X += [("JUKNM", "JUKNM", "0")]
EQUIP_7X = [("RXXNM", "START", "RXXNM", "1/8 * 16/12 * 25/20"), ("RXTNM", "START", "RXTNM", "")]
EQUIP_7X += [("EXLNM1", "START", "EX1NM", "1000"), ("EXHNM1", "START", "EX2NM", "1000")]
EQUIP_7X += [("EXLNM2", "START", "EXCNM", "1000"), ("EXHNM2", "START", "EX0NM", "1000")]
EQUIP_7X += [(k, "START", "EX1NM", "1000") for k in ("EXLNM3", "EXHNM3", "EXLNM4", "EXHNM4")]
def write_ini(sections: list[tuple[str, list[tuple[str, str]]]]) -> bytes:
out = []
for sec, items in sections:
if items:
out.append(f"[{sec}]")
out += [f"{k}={v}" for k, v in items]
return ("\r\n".join(out) + "\r\n").encode("cp949", "replace")
def ini_from_7x(start_ini: bytes, conld: str, contd: str) -> bytes:
"""7.x Start.Ini → Start8x.Ini(0x48e07c) — 원본에 없는 키는 기본값."""
src = ini_dict(start_ini.decode("cp949", "replace"))
g = lambda sec, k, d: src.get(sec, {}).get(k, d) # noqa: E731
start = [("ARENM", "1" if g("START", "ARENM", "A->") == "A->" else "2")]
start += [(t, g("START", s, d)) for t, s, d in START_7X]
prt = [("ORDNM", g("PRINT", "ORDNM", "")), ("FONT", g("PRINT", "CBFONT", "굴림체")), ("F_COL0", "clBlack")]
prt += [("F_COL1", g("PRINT", "COLDAN", "clRed")), ("F_COL2", g("PRINT", "COLBYN", "clBlack"))]
prt += [("SH_TYP0", g("PRINT", "FOMNM", "0")), ("SAN_TYP", g("PRINT", "LANNM", "0"))]
prt += [("SAN_AMT", "1"), ("FLT_JAB", "3")]
eq = [(t, g(s, k, d)) for t, s, k, d in EQUIP_7X]
eq += [(k, v) for k, v in (("CONLD", conld), ("CONTD", contd)) if v]
prj = [("PCODE", "XXXX"), ("PMODE", g("PROJECT", "PMODE", "1")), ("PNAME", g("PROJECT", "PNAME", "프로젝트명"))]
prj.append(("P_DIR", ""))
nag = [("NAGNM", src["START"]["NAGNM"])] if "NAGNM" in src.get("START", {}) else []
for t, k in (("ST_MHAL", "PMHAL"), ("EQ_LHAL", "EQ_L"), ("EQ_MHAL", "EQ_M"), ("EQ_SHAL", "EQ_S")):
if k in src.get("PROJECT", {}):
nag.append((t, src["PROJECT"][k]))
secs = [("START", start), ("PRINT", prt), ("EQUIPMENT", eq), ("PROJECT", prj), ("NAG_HAL", nag)]
return write_ini(secs)
def read_mem(b: bytes) -> dict[str, object]:
"""Clipper .MEM 변수 — C 글(TrimRight) · N 실수(%-20.6F 뒤 Trim) · L 0/1 · D 율리우스 일수(조건부)."""
out: dict[str, object] = {}
o = 0
while o + 32 <= len(b) and b[o] not in (0x1A, 0):
name = b[o : o + 11].split(b"\x00", 1)[0].decode("ascii", "replace").upper()
typ = chr(b[o + 11] & 0x7F)
ln = b[o + 16] | (b[o + 17] << 8)
o += 32
if typ == "C":
out[name] = b[o : o + ln].split(b"\x00", 1)[0].decode("cp949", "replace").rstrip()
o += ln
elif typ in ("N", "D"):
v = struct.unpack_from("<d", b, o)[0]
out[name] = (f"{v:<20.6f}").strip() if typ == "N" else v
o += 8
elif typ == "L":
out[name] = "1" if b[o] else "0"
o += 1
else:
break
return out
DOS_PRINT = ("ILWNM", "TOGNM", "LANNM")
DOS_EQUIP = ("RXTNM", "RXXNM", "EX1NM", "EX2NM", "EXCNM", "EX0NM")
def ini_from_dos(mem: bytes, conld: str, contd: str) -> tuple[bytes, list[str]]:
"""DOS Start.MEM → Start8x.Ini(0x48fea8 → 0x44d370) — [START] 34키 표는 실행 중 채워져 이름 미확인 →
[PRINT]·[EQUIPMENT] 로 가는 키 밖의 MEM 변수는 [START] 로(조건부)."""
v = read_mem(mem)
start = [(k, str(x)) for k, x in v.items() if k not in DOS_PRINT + DOS_EQUIP]
start = [kv for kv in start if kv[0] not in ("SUBNM", "QCDNM", "XCDNM", "JUKNM")]
start += [("SUBNM", ""), ("QCDNM", "0"), ("XCDNM", "0"), ("JUKNM", "0")]
prt = [(k, str(v[k])) for k in DOS_PRINT if k in v]
prt += [("ORDNM", " "), ("FONT", "굴림체"), ("F_COL0", "clBlack"), ("F_COL1", "clRed"), ("F_COL2", "clBlack")]
eq = [(k, str(v[k])) for k in DOS_EQUIP if k in v]
eq += [(k, x) for k, x in (("CONLD", conld), ("CONTD", contd)) if x]
note = ["DOS [START] 키 이름 표(0x588294)는 실행 중 채워짐 — MEM 변수 이름 그대로 씀(조건부)"]
return write_ini([("START", start), ("PRINT", prt), ("EQUIPMENT", eq), ("PROJECT", [("PMODE", "1")])]), note
def b_list(rows: list[dict]) -> str:
"""CONLD/CONTD 의 BDCODE 중 B 로 시작하는 6자 코드를 겹치지 않게 이음(0x477e4c)."""
out = []
for r in rows:
c = str(r.get("BDCODE", "")).strip()
if c.startswith("B") and c not in out:
out.append(c)
return "".join(out)
def swap_codes(text: str, mapping: dict[str, str]) -> str:
return re.sub("|".join(map(re.escape, mapping)), lambda m: mapping[m.group(0)], text) if mapping else text