Skip to content
isdnetworks
Go back

업데이트하니 저장이 깨졌다

새 버전을 낸 뒤 진행 상황이 사라졌다는 문의가 왔다.

Table of contents

Open Table of contents

구조체를 통째로 쓰고 있었다

저장을 구조체 통째로 fwrite 하는 방식으로 하고 있었다. 새 버전에서 필드를 하나 추가했다.

struct SaveData { int level; int gold; int stage; int gem; };
fwrite(&data, sizeof(SaveData), 1, fp);

그러면 fwrite 가 쓰는 바이트 수가 달라진다. 옛 파일은 12바이트인데 새 구조는 16바이트를 읽으려 한다. fread 로 받아 보니 앞의 셋은 맞고 마지막이 쓰레기 값이었다. 파일에 그만큼이 없기 때문이다.

이 방식은 다른 것도 걸린다. 지금은 같은 크기 필드만 있어 딱 맞지만 크기가 다른 것이 섞이면 컴파일러가 정렬을 맞추려고 사이에 빈 바이트를 넣는다. 그 규칙은 ABI 가 정한다. 에뮬레이터의 x86 과 기기의 ARM 에서 그 값이 다르면 같은 판인데 크기가 갈린다. NDK 로 두 ABI 를 다 빌드하면 그대로 드러난다.

버전 번호를 넣었다

크기로 판단하는 방법을 먼저 생각했다. 그런데 버전마다 크기를 외워야 한다. 필드 하나를 빼고 하나를 넣으면 크기가 같은데 뜻이 다른 경우도 생긴다.

파일 앞에 버전 번호를 넣고 읽을 때 그것부터 보게 했다. 크기는 결과로 달라지는 값이고 버전은 우리가 뜻을 부여한 값이다. 근거로 쓸 수 있는 것은 뒤였다.

한 단계씩 올리게 했다

버전마다 읽는 코드를 따로 두면 버전이 늘수록 코드가 쌓인다. 한 단계씩 올리는 방식으로 바꿨다.

if (version == 1) { upgradeV1toV2(); version = 2; }
if (version == 2) { upgradeV2toV3(); version = 3; }

각 단계만 알면 되고 아주 옛 파일도 여러 번 거쳐 최신이 된다. 읽고 나서 새 형식으로 다시 저장하니 다음부터는 한 번에 읽힌다.

각 버전의 파일을 하나씩 만들어 두고 전부 돌려 봤다. 1에서 3으로 가는 경로가 두 단계를 거치는지 눈으로 확인해야 했다. 새로 만든 파일만으로 시험하면 옛 경로는 한 번도 안 돈다.

이름과 값으로 바꾼 뒤

더 근본적으로는 구조체를 통째로 쓰는 것이 문제였다. 이름과 값으로 저장하게 바꾸니 항목이 늘어도 옛 파일에 없으면 기본값을 쓰고 순서가 바뀌어도 상관없다.

CCUserDefault 가 이미 그런 모양이라 그걸 쓸까 했는데 안 썼다. XML 로 평평하게만 저장해서 목록이나 중첩이 안 들어간다. 우리 저장에는 스테이지별 기록이 배열로 있었다.

이름을 같이 쓰니 파일이 커진다. 통째로 쓰는 쪽은 작고 빠른 대신 깨지기 쉽다. 이름별로 쓰는 쪽은 크고 느린 대신 안 깨진다. 저장 파일이 몇 킬로바이트라 안 깨지는 쪽을 골랐다.

정리


Share this post on:

Previous Post
더 낫다고 바꿀 이유가 되지는 않았다
Next Post
상태를 바꾸면 값 두 개가 같이 움직였다