사례 연구 · 동방 4:로터스 랜드 스토리

TH04의 탄막 패턴
재구성하기

원형 탄막은 짧은 각도 계산에서 시작됩니다. REA로 원본 DOS 함수를 분석하고, 명령어가 읽기 쉬운 C++로 어떻게 이어지는지 살펴보세요.

PC-98 ·16 비트 도스 · C++및 어셈블리

TH04 재구성 보기

원본 명령어에서 검증된 소스까지

  1. 01 · 원본 게임MAIN.EXEPC-98 실행 파일과 탄환 각도 계산을 위한 도우미 함수입니다.
  2. 02 · REA로 조사하기명령어 + 참조로드된 DOS 코드를 확인하고 실행 파일에서 각도 계산을 읽습니다.
  3. 03 · C++ 코드 읽기간격 배치 → 조준 → 회전각 연산을 유지 관리 중인 재구성 코드와 연결합니다.
  4. 04 · 소스 확인일치하는 바이트 2,139개기록된 컴파일러 검사는 이 도우미가 포함된 탄환 생성 코드를 다룹니다.
REA는 원본 코드와 분석 증거를 반환합니다. TH04 프로젝트는 유지보수 중인 소스와 컴파일러 검증을 제공합니다.

원형 탄막이 플레이어를 조준하는 방식

엘리의 공격은 초기 탄환 수가 16개인 조준 원형 탄막을 요청합니다. 보조 함수 하나가 각 탄환의 방향을 계산합니다. 같은 원형 탄막을 고정 방향으로 발사하거나 플레이어 쪽으로 회전할 수 있습니다.

코딩 에이전트

REA로 TH04가 원형 탄막을 만들고 플레이어를 조준하는 방식을 찾아줘. 계산을 설명하고 코드도 보여줘.

원본 실행 파일과 체크아웃한 재구성 저장소를 로컬에 준비한 상태에서 사용하는 예제 프롬프트입니다.

TH04는 시계 방향 한 바퀴를 256각도 단위로 표현합니다. 탄환이 16개면 인덱스마다 16단위씩 더합니다: 0, 16, 32, …, 240. 플레이어 방향을 더하면 모든 위치가 함께 회전합니다.

탄환 16개의 원형 탄막을 그린 두 그림입니다. 고정 탄막은 각도 0에서 시작합니다. 플레이어 방향 40을 더하면 모든 방향이 같은 만큼 회전하며, 강조한 첫 탄환은 플레이어를 향합니다. 둘 다 16각도 단위, 즉 22.5도 간격을 유지합니다.
탄환 수 16, 플레이어 방향 40, 템플릿 회전 0일 때 복구한 각도 계산을 그린 예시입니다. 그림 열기

복구된 소스의 발사 템플릿

Elly의 원형 탄막 공격 · 소스 발췌
bullet_template.angle = 0;
bullet_template.group = BG_RING_AIMED;
bullet_template.count = 16;
bullet_template_tune();

난이도와 성능 조정 전에 패턴을 선택하는 코드입니다. 공격 소스 읽기.

REA가 반환하는 내용

각 탄환의 속도와 각도를 정하는 함수 bullet_velocity_and_angle_set()을 분석합니다. REA는 명령어, 의사 코드, 참조를 반환합니다. 결과의 세 부분으로 이 패턴을 설명할 수 있습니다.

  1. 코드를 읽기 전에 DOS 이미지 검증

    에이전트 → REA
    open_binary
    {"path": "MAIN.EXE"}
    
    inspect_native_load_image
    {}
    REA → 에이전트 · 선택한 필드
    status: "verified"
    format: "dos-mz"
    load_segment: 4096
    header_bytes: 6144
    module_bytes: 150114

    로드된 코드와 재배치 기록이 제공된 실행 파일에 대응하는지 검증합니다. 위 경로는 표시를 위해 줄였습니다.

  2. 원형 탄막의 곱셈·나눗셈 읽기

    에이전트 → REA · analyze_function
    {"procedure": "0x2cfc8"}
    REA → 에이전트 · 명령어 발췌문
    0x2d0eb: MOV AX, SI
    0x2d0ed: SHL AX, 0x8
    0x2d0f0: MOV DL, byte ptr [0x53af]
    0x2d0f9: IDIV BX

    SI에는 탄환 인덱스가 있습니다. 시프트로 256을 곱한 뒤, DS:53AF에서 읽은 탄환 수로 나눕니다. 유지보수 중인 소스에서는 이 필드를 bullet_template.count라고 부릅니다. 전체 과정은 아래에 있습니다.

  3. 패턴이 플레이어 쪽으로 향하는 위치 찾기

    같은 함수 결과 · 조준 부분 발췌
    0x2d190: SUB AX, word ptr [0x53a6]
    0x2d198: SUB AX, word ptr [0x53a4]
    0x2d19d: CALLF 0x1000:1da8
    0x2d1a2: ADD word ptr [BP + -0x2], AX
    대응하는 유지보수 C++ 코드
    group_angle_offset += iatan2(
      player_pos.cur.y - bullet_template.origin.y,
      player_pos.cur.x - bullet_template.origin.x
    );

    명령어의 피연산자에서 두 좌표의 차이와 방향 보조 함수를 확인할 수 있습니다. 반환된 각도는 원형 탄막의 상대 각도에 더해집니다. 소스의 이름은 주소의 의미를 알려주고, REA는 그 뒤의 원본 연산을 확인하게 해줍니다.

어셈블리에서 읽기 쉬운 C++로

단계를 선택해 원본 명령어와 각도 계산을 연결해 보세요. 이 요약은 조준 원형 탄막 경로를 따릅니다. 전체 소스는 확산형, 중첩형, 무작위 패턴도 처리합니다.

REA · 원본 16비트 명령어
0x2cfcf: MOV SI, word ptr [BP + 0x4]
0x2d0eb: MOV AX, SI
0x2d0ed: SHL AX, 0x8
0x2d0f0: MOV DL, byte ptr [0x53af]
0x2d0f4: MOV DH, 0x0
0x2d0f6: PUSH DX
0x2d0f7: CWD
0x2d0f8: POP BX
0x2d0f9: IDIV BX
0x2d0fb: MOV word ptr [BP + -0x2], AX
0x2d18d: MOV AX, [0x4650]
0x2d190: SUB AX, word ptr [0x53a6]
0x2d194: PUSH AX
0x2d195: MOV AX, [0x464e]
0x2d198: SUB AX, word ptr [0x53a4]
0x2d19c: PUSH AX
0x2d19d: CALLF 0x1000:1da8
0x2d1a2: ADD word ptr [BP + -0x2], AX
0x2d1b9: MOV AL, byte ptr [BP + -0x2]
0x2d1bc: ADD AL, byte ptr [0x53ad]
0x2d1c0: MOV [0xbcc8], AL
읽기 쉬운 C++ · 조준 원형 탄막 요약
unsigned char aimed_ring_angle(
    int index, int count,
    int player_direction,
    unsigned char rotation)
{
    int angle = index;
    angle = (angle * 256) / count;
    angle += player_direction;
    angle += rotation;
    return (unsigned char)angle;
}

01 · 탄환 인덱스를 읽습니다. 스택 인수를 SI에 로드합니다. 탄환 16발로 이루어진 원형 탄막에서 이 인덱스는 0부터 15까지입니다.

어셈블리는 조준 원형 탄막 분기, 조준 계산, 최종 저장을 발췌한 것입니다. 위 C++는 읽기 쉬운 요약입니다. 아래 컴파일러 검증은 연결된 프로젝트의 실제 유지보수 소스에 적용됩니다.

원형 탄막과 조준의 유지보수 소스 읽기
원형 탄막 매크로 · TH04 분기 발췌
angle_offset = (i * BULLET_ANGLE_FULL_TURN) / count;
if (i >= (count - 1)) {
    group_complete = true;
}
goto aim_or_no_aim;
각도와 속도 벡터 · 소스 발췌
aim:
    group_angle_offset += iatan2(
        (player_pos.cur.y - bullet_template.origin.y),
        (player_pos.cur.x - bullet_template.origin.x)
    );

no_aim:
    vector2_near(
        bullet_template.velocity,
        (group_angle_offset + bullet_template.angle),
        speed
    );
    bullet_spawn_angle = (group_angle_offset + bullet_template.angle);

원형 탄막 매크로 · 조준·속도 소스 · 각도 표현

유지보수 소스 검증

TH04 프로젝트에 기록된 검사에서는 버전을 고정한 Turbo C++ 4.0J 도구 모음으로 이 도우미 함수를 포함한 탄환 생성 코드를 컴파일합니다.

2,139바이트 일치

컴파일된 탄환 생성 코드는 원본 실행 파일의 2,139바이트 구간과 일치합니다.

독립적인 클린 빌드 두 번

별도로 빌드한 두 결과가 같은 바이트로 일치합니다. 기록된 입력 해시는 여기서 보여주는 유지보수 소스와 일치합니다.

2026년 10월 3일 기록. 컴파일러 검증 읽기.

대상 식별 정보와 분석 범위
원본 대상 MAIN.EXE, 156,258바이트, DOS MZ
보조 함수 진입점 REA가 로드한 이미지의 0x2cfc8
원본 파일 오프셋 address_to_file_offset이 반환한 0x1e7c8
기록된 코드 분석 REA 4.1.0 · 2026년 10월 7일

컴파일러 검사는 이 도우미 함수를 포함한 전체 탄환 추가 코드 섹션을 다룹니다. MAP 기여분과 겹치는 재배치도 비교합니다.

REA는 로드 세그먼트 0x1000에서 정적 DOS 로드 이미지를 검증했습니다. 주소는 이 분석 이미지를 기준으로 합니다. 함수 결과는 전체 517바이트 범위 안의 두 구간에 본문 503바이트가 있다고 보고합니다. 위 발췌 명령어는 이 구간에 속합니다.

소스의 이름과 컴파일러 검증은 TH04 체크포인트 0d72e980에서 가져왔습니다. 이전 재구성 관찰에는 프로젝트 자체 분석 스크립트를 사용했으며, 이 페이지는 같은 원본 대상을 REA로 새로 분석한 결과를 추가합니다. 게임의 식별 정보는 저장소 매니페스트로 고정돼 있지만, 독립적인 무변경 덤프의 출처 확인은 아직 남아 있습니다.

그림은 각도 계산을 설명합니다. 전체 게임의 조작, 타이밍, PC-98 하드웨어 동작은 재구성 프로젝트에서 별도로 런타임 검증합니다.

전체 TH04 재구성

프로젝트는 유지보수 중인 C++과 어셈블리로 게임의 DOS 프로그램 네 개를 빌드합니다. 소스는 탄환 생성 외에도 스테이지, 보스, 렌더링, 입력, 메뉴, 엔딩, 점수 처리를 다룹니다.

TH04 저장소 살펴보기

상단