Công khai mã nguồn bản dịch Tear Ring Saga

Thảo luận trong 'Turn Based Strategy' bắt đầu bởi SPC700, 7/1/24.

  1. SPC700

    SPC700 Legend of Zelda

    Tham gia ngày:
    1/10/20
    Bài viết:
    1,073


    Tear Ring Saga, tên ban đầu là Emblem Saga, được rất nhiều tay chơi game Việt Nam biết đến với tên gọi "Mộc đế 6" hoặc "Mộc đế PS1".
    Đơn giản là do game này có cùng cốt cách với dòng game "Mộc đế" (Fire Emblem) do hãng Ninh Tiền Đô phát hành.

    Emblem Saga là sản phẩm của Kaga Shōzō sau khi ông này rời khỏi Intelligent Systems, công ty phát triển game cho Ninh Tiền Đô, và nổi tiếng nhất qua series Fire Emblem. Kaga Shōzō cũng là người sáng tạo nên dòng game Fire Emblem, nhưng Ninh Tiền Đô lại là bên chi tiền để phát triển cả dòng game này.
    Vì thế, ngay sau khi Kaga Shōzō thành lập công ty riêng, tạo nên Emblem Saga thì giữa Ninh Tiền Đô và ông này đã diễn ra vụ kiện tụng kéo dài đến năm 2005, xoay quanh vấn đề tác quyền.
    Cũng vì lẽ đó mà Emblem Saga được đổi tên thành Tear Ring Saga.
    Bản dịch tiếng Việt này được hoàn thành (mức độ 100%) vào năm 2014. Sau 10 năm, tác giả bản dịch quyết định công khai code và script của bản dịch này để cộng đồng có thể chỉnh sửa lời thoại, xây dựng nên những câu chuyện mới.

    Link:
    https://www.mediafire.com/file/6f9wafsvd5r91jl/Emblem_Saga.rar

    Cũng giống như các phiên bản Fire Emblem khác, đây là game có khối lượng thoại đồ sộ, không thua kém bất cứ trường thiên tiểu thuyết nào.
    Ngoài ra, tác giả bản dịch này còn công khai mã nguồn + script bản dịch tiếng Việt cho nhiều game khác.
    Xem chi tiết ở link bên dưới.

    https://sites.google.com/view/sfc65816
     
    Sephir0th and namff like this.
  2. SPC700

    SPC700 Legend of Zelda

    Tham gia ngày:
    1/10/20
    Bài viết:
    1,073
    Một số bạn hỏi sao bản dịch Tear Ring Saga không chơi được trên PS Vita.
    Câu trả lời là: PS Vita không thể chơi được game PS1.
    Tuy nhiên, nó có thể chơi được game của PSP, còn máy PSP thì lại chơi được game PS1.
    Do đó, để chơi bản dịch Tear Ring Saga trên máy PS Vita thì cần phải:
    1) Convert Rom Tear Ring Saga thành dạng Eboot để máy PSP có thể đọc được.
    2) Chạy giả lập PSP trên máy PS Vita (giả lập Andreline)
    Một số hệ máy cầm tay của Ninh Tiền Đô cũng có thể chạy được bản dịch này với giả lập PS1, nhưng không khuyến khích.
    Bởi vì CPU của hệ PS nhà Sony là MIPS, trong khi CPU của các hệ máy cầm tay của nhà Ninh Tiền Đô thường là ARM, và thường yếu hơn nên độ tương thích không cao.
    Mà bây giờ nhìn lại thì thấy bản dịch này (năm 2014) chỉ mới dừng ở mức thủ thuật chứ chưa phải kỹ thuật.
    Còn link bên dưới bao gồm Rom tiếng Việt cho cả PS1 lẫn Eboot cho PSP, nhưng có thêm một số chỗ nghịch ngợm của năm 2024.

    Link:
    Super Easy Tear Ring Saga (mediafire.com)

    a) Di chuyển không tốn lượt
    b) Mua đồ không tốn tiền, nhưng bán thì tăng tiền
    c) Level up với 1 EXP
    d) Độ bền của vũ khí chỉ có tăng chứ không có giảm
    Bản vọc này thích hợp cho người chỉ muốn thưởng thức nội dung của game, hoặc người lúc nào cũng muốn tiền vào mà không thích tiền ra....

     
    Sephir0th, namff and T0977999482 like this.
  3. SPC700

    SPC700 Legend of Zelda

    Tham gia ngày:
    1/10/20
    Bài viết:
    1,073
    Hack những điều chưa ai từng làm trong TRS.

    STONE BOAT - Tear Ring Saga (google.com)

    Đánh bại Erunsth ở MAP 10

    Hầu hết người chơi kỹ Tear Ring Saga (TRS) đều có ấn tượng với kỵ sĩ Hoàng kim Erunsth ở MAP 10. Đây là một trong những nhân vật mạnh nhất game, các chỉ số cao ngất ngưởng cùng với bộ skill khủng và mấy cái khiên giảm sát thương đáng kể. May thay là trong lần đụng độ ở MAP 10, nhân vật này không chủ động tấn công người chơi, và cũng chủ động rút lui sau một số lượt đi.

    [​IMG]

    [​IMG]
    Trang bị của Erunsth: khiên giảm 20 sát thương vật lý và 2 khiên giảm sát thương ma thuật

    Tuy nhiên, nếu muốn thì người chơi cũng có thể giao chiến với Erunsth nếu đủ tự tin vào sức mạnh của mình. Chỉ có điều nhân vật này được thiết kế để không thể bị đánh bại ở MAP 10 nếu giao đấu trực tiếp. Cụ thể là trước mỗi trận đấu, CPU luôn kiểm tra xem lượng sát thương mà Erunsth sẽ nhận được trong trận ngay sau đó có vượt quá số HP còn lại của nhân vật này hay không. Nếu lượng sát thương vượt quá số lượng HP còn lại thì CPU sẽ rẻ nhánh sang hướng xử lý khác thông thường, là khiến nhân vật luôn tránh được mọi đòn đánh của người chơi. Trận đánh trong TRS và nhiều phiên bản Fire Emblem khác đều diễn ra theo kịch bản đã được tính toán sẵn từ trước. Trước mỗi trận đấu, CPU luôn tính toán tỷ lệ trúng đòn, lượng sát thương hay tỷ lệ đánh tất sát của mỗi bên, có nhân vật nào chết hay không. Mọi hình ảnh chiến đấu diễn ra sau đó chỉ là phần diễn kịch lại những kịch bản đã được định đoạn ở bước tính toán trước đó.

    Ta có thể kiểm chứng điều này bằng cách dùng phần mềm giả lập kiêm Debugger cho máy PlayStation, từ đó chỉnh sửa code để game bỏ qua bước kiểm tra này. Khi đó thì Erunsth sẽ không còn tự động né đòn khi HP xuống thấp nữa.

    Ý tưởng ở đây là đặt break point tại địa chỉ Ram quản lý giá trị HP hiện tại của nhân vật này để xem CPU làm gì với giá trị này. Đầu tiên là cần phải tìm được địa chỉ Ram quản lý HP hiện tại của Erunsth. Các Debugger thường có chức năng scan memory để tìm ra những địa chỉ nào có giá trị biến đổi theo thời gian. Chẳng hạn, nếu HP hiện tại của Erunsth đang là 49, và ta scan giá trị 49 trong memory; rồi sau một trận đánh, HP của nhân vật này giảm còn 40 thì ta tiếp tục dò tìm giá trị nào từng là 49 nhưng ngay thời điểm này là 40; và cứ tiếp tục như thế sau vài lần lọc thì sẽ tìm được địa chỉ Ram quản lý giá trị HP hiện tại của nhân vật này.

    Không chỉ các Debugger mới có chức năng này, mà một số phần mềm độc lập như Art Money hay Cheat Engine cũng có chức năng tương tự. Bằng cách này thì ta dễ dàng tìm được địa chỉ Ram này là $801925E8.

    [​IMG]

    [​IMG]
    Lưu ý là địa chỉ này chỉ quản lý HP hiện tại của nhân vật trong trận đấu, không phải là địa chỉ thể hiện giá trị HP hiện tại "thật" của nhân vật. Trước mỗi trận đấu, giá trị HP hiện tại "thật" từ địa chỉ Ram khác được copy vào $801925E8, để rồi khi trận đấu diễn ra thì CPU luôn đọc địa chỉ $801925E8 ở mỗi frame để vẽ các vạch tương ứng với số HP còn lại.

    [​IMG]

    [​IMG]
    Bằng cách thay đổi giá trị tại $801925E8 thì lượng HP được hiển thị cũng thay đổi theo.

    Nếu đặt write break point tại địa chỉ $801925E8 thì sẽ thấy giá trị HP được copy sang địa chỉ này từ Register $v0 trước khi trận đấu diễn ra.

    800f9c2c ae420020: sw $v0(00000031), 0x0020(s2)([801925e8] = 00000031)

    Đoạn log trên thể hiện giá trị 0x31 (tương đương số thập phân 49), tức giá trị HP hiện tại của Erunsth, được copy từ Register $v0 sang địa chỉ Ram $801925E8. Nếu truy ngược về trước một chút của đoạn log thì sẽ thấy có đoạn xử lý như bên dưới.

    8010d278 3042003f: andi $v0(007bc031), 0x003f

    8010d27c 0262102a: slt $v0(00000031), $s3(00000009), $v0(00000031)

    8010d280 144000d0: bne $v0(00000001), $r0(00000000), 0x8010d5c4

    8010d284 2402004a: li $v0(00000001), 0x004a

    8010d5c4 8fa30020: lw $v1(00000065), 0x0020(sp)([801ffd60] = 00000001)

    8010d5c8 00000000: nop

    Điều này có nghĩa là tại Register $v0, giá trị HP là 1 byte (0x31) nằm chung với 3 byte khác (0x00, 0x7B, 0xC0), sau đó được AND với 0x3F để giữ lại byte cuối là 0x31. Sau đó, CPU sẽ kiểm tra xem giá trị tại $s3 (đang là 0x09) có nhỏ hơn giá trị tại Register $v0 (đang là 0x31) hay không. Giá trị tại $s3 lúc này chính là lượng sát thương mà nhân vật sẽ nhận được trong trận đấu sắp tới. Nếu lượng sát thương sẽ nhận được (0x09) nhỏ hơn lượng HP hiện tại (0x31) thì $v0 sẽ được set giá trị thành 0x01.

    [​IMG]

    [​IMG]
    Lệnh SLT trong ngôn ngữ MIPS R3000A mà máy PlayStation sử dụng mang nghĩa là set giá trị 1 cho Register đích nếu giá trị của Register được so sánh nhỏ hơn giá trị của Register là đối tượng so sánh

    Sau đó, giá trị của Register $v0 được so với 0x0000 (tại Register $r0), nếu giá trị này khác 0x00 thì CPU sẽ rẻ nhánh sang địa chỉ thực thi ở $8010D5C4. Điều này có nghĩa là nếu kết quả của phép so sánh trước đó là 1 (trường hợp lượng sát thương nhỏ hơn lượng HP còn lại) thì CPU sẽ xử lý ở phần cho phép nhân vật nhận sát thương bình thường. Còn nếu kết quả của phép so sánh trước đó là 0 (trường hợp lượng sát thương lớn hơn lượng HP còn lại) thì CPU sẽ không phân nhánh mà xử lý theo hướng khiến nhân vật tự động né đòn.

    Do vậy, nếu tại địa chỉ thực hiện phép so sánh mà ta sửa lại lệnh thi hành, khiến CPU luôn phân nhánh (jump) tới địa chỉ $8010D5C4 thì nhân vật không còn né đòn được khi lượng HP xuống thấp.

    8010d278: andi $v0, 0x003f

    8010d27c: j 0x043571

    Hằng số theo sau lệnh J (jump) trong ngôn ngữ MIPS không phải là địa chỉ tuyệt đối mà CPU sẽ nhảy tới, mà là số lượng câu lệnh mà nó sẽ bỏ qua để tới địa chỉ thực thi tiếp theo. Do mỗi câu lệnh của MIPS là 32 bit (tức 4 byte) nên chỉ cần lấy địa chỉ cần nhảy tới chia cho 4 là được giá trị tham số cho lệnh J. Cụ thể ở đây là: 10D5C4/4 = 43571.

    Và kết quả sau khi chỉnh sửa câu lệnh trên thì Erunsth không còn né đòn khi HP xuống thấp nữa, cho nên nhân vật này vẫn trúng đòn, và HP xuống còn zero. Tuy nhiên, game không có phần xử lý cho nhân vật này biến mất khi HP là zero nên dẫn tới hiện tượng như bên dưới.

    [​IMG]

    [​IMG]
    Mặc dù HP là zero nhưng CPU vẫn coi nhân vật này đang tồn tại, vẫn xảy ra hội thoại của nhân vật này sau đó. Bởi mặc định CPU luôn coi nhân vật này không thể chết ở map này. Nếu muốn CPU coi nhân vật này đã chết, xóa hội thoại sau đó thì cần phải can thiệp thêm nhiều vị trí khác. Và dĩ nhiên là việc này sẽ tác động tới các flag liên quan tới event của nhân vật này ở những map sau.

    Và phần can thiệp bên trên chỉ là can thiệp vào memory mang tính nhất thời. Nếu muốn phần code này có hiệu lực vĩnh viễn (hard code) thì cần phải tìm đoạn code trên trong Rom rồi sửa như trên. Phần xử lý HP của nhân vật này nằm ở file B.bin trong CD-ROM, có địa chỉ như bên dưới.

    0001C270 ANDI v0, v0, 0x003F

    0001C274 SLT v0, v0, 0x00

    Đầu tiên là trích xuất file B.bin từ CD-ROM thông qua những phần mềm xử lý CD như CDmage, rồi chỉnh sửa đoạn code trên, compile thành mã máy rồi chèn lại B.bin đã chỉnh sửa vào CD cũng bằng CDmage.

     
    Chỉnh sửa cuối: 29/1/24
    namff thích bài này.
  4. SPC700

    SPC700 Legend of Zelda

    Tham gia ngày:
    1/10/20
    Bài viết:
    1,073
    Thử demake Tear Ring Saga.

     
  5. SPC700

    SPC700 Legend of Zelda

    Tham gia ngày:
    1/10/20
    Bài viết:
    1,073
    STONE BOAT - Tear Ring Saga

    Giải lời thề của Laquel

    Laquel là một nhân vật đặc biệt trong TRS. Nhân vật này đặc biệt không phải vì chỉ số nổi trội, kỹ năng tuyệt đỉnh mà cũng chẳng phải ở avatar trai xinh gái đẹp. Laquel đặc biệt vì sở hữu cây cung khá mạnh ở đầu game, những giữ lời thề bất sát nên khi đụng độ lính bên địch thì không bao giờ kết liễu được quân địch. Vì lời thề không phạm sát giới nên nhân vật này luôn chừa 1 HP cho quân địch, những nhát bắn sau đó luôn trật dù tỷ lệ Hit là 100%. Việc này gần giống với tướng Erunst ở Map 10, luôn tự động né đòn khi lượng HP còn lại không đủ để chịu lượng sát thương do địch gây ra. Chỉ đến giai đoạn giữa game, và chỉ khi người chơi chọn cung thủ Luca vào đội của mình thì mới xảy ra event giúp Laquel phá bỏ lời thề bất sát, có thể kết liễu quân địch trong trận đấu.

    Thử tìm các trang Cheat code khắp chốn Google nhưng tuyệt nhiên không thấy một trang nào đề cập đến Cheat code khiến Laquel bỏ lời thề, bắn chết được quân địch ngay từ những Map đầu tiên. Thế nên tôi đã tìm cách can thiệp vào mã game để sửa lại lời thề này, và đã thành công.



    Ý tưởng ở đây là tìm ra routine xử lý lượng HP còn lại của quân địch trong trận đấu, để rồi từ đó tìm ra phần xử lý đặc biệt khi Laquel là bên tấn công, và rồi vô hiệu hóa phần xử lý đặc biệt đó.

    Để tìm ra routine xử lý lượng HP còn lại của quân địch thì đầu tiên cần phải xác định được địa chỉ Ram quản lý HP của địch. Cách tìm địa chỉ này giống như phần trên đã đề cập, là dùng chức năng Memory Scan của Debugger, hoặc các phần mềm ngoại vi có chức năng dò tìm Memory như Cheat Engine hay Art Money. Chẳng hạn, nếu ban đầu quân địch có 30 HP thì ta dò tìm giá trị 30, sau đó tấn công, địch còn 27 HP thì ta tiếp tục dò tìm phần Memory nào trong số những địa chỉ Ram đã tìm được mà vừa biến đổi thành 27. Tiếp tục một vài lần như thế thì sẽ tìm được địa chỉ Ram quản lý HP của địch. Cần lưu ý là địa chỉ này không cố định, mà biến động ở từng Map, từng đơn vị lính địch nên ở đây không nên địa chỉ cụ thể.

    [​IMG]

    Chức năng Memory Scan của Duck Station. Bên trái là kết quả dò tìm những địa chỉ Ram có giá trị hiện tại là 11 (HP hiện tại của địch), và giá trị trước đó là 21

    Sau đó, đặt Write Break Point ở địa chỉ vừa tìm được thì sẽ thu được kết quả như dưới đây.

    800FB4A8: sw v0, 0x00(s0)

    Trong đó v0 là Register chứa giá trị HP còn lại của địch sau khi bị tấn công, và giá trị này được ghi vào địa chỉ nằm trong Register s2. Truy ngược về trước thì thấy giá trị của v0 là kết quả của phép tính dưới đây.

    8010D664: subu a0, a0, s1

    Trong đó a0 là giá trị HP còn lại của địch, bằng chính nó trừ đi lượng sát thương mà địch nhận được, chứa ở Register s1. Đây là lệnh quan trọng trong khâu tính toán kết quả trận đấu trong game. Nếu ta tìm ngược về trước thì sẽ thấy giá trị sát thương mà bên bị tấn công nhận được chính là giá trị tấn công của bên chủ động tấn công trừ đi giá trị phòng thủ của bên bị tấn công. Từ đây ta có thể thay đổi cơ chế tính toán, chẳng hạn như thay phép trừ bằng phép cộng, hoặc nhân chia, hoặc các phép tính toán kèm điều kiện khác. Chẳng hạn như nếu bên tấn công là một nhân vật xác định nào đó thì sát thương sẽ nhân đôi, hay chỉ còn một nửa,...

    Và nếu so sánh log ở hai trường hợp: trường hợp khi nhân vật khác không phải Laquel tấn công địch, và trường hợp Laquel tấn công địch thì ta sẽ thấy hai log này có sự khác biệt. Ở trường hợp Laquel là bên tấn công, nếu HP của địch còn lại thấp hơn giá trị sát thương mà Laquel gây ra thì CPU sẽ có thêm dòng xử lý như bên dưới.

    8010D2F8: addiu s1, v0, 0xFFFF

    Trong đó s1 chính là lượng sát thương mà bên bị tấn công nhận được, v0 là giá HP hiện tại của địch. Dòng lệnh này cho thấy lượng sát thương mà bên bị tấn công nhận được bằng HP hiện tại của địch trừ 1 HP. Đoạn xử lý này là đặc thù của riêng trường hợp khi Laquel là bên tấn công. Điều kiện để xảy ra đoạn xử lý này nằm ở phần code bên dưới.


    8010D284: addiu v0, r0, 0x4A

    8010D288: lw v1, 0x1C(fp)

    8010D28C: nop

    8010D290: andi v1, v1, 0x03FF

    8010D294: bne v1, v0, 8010D324


    Đầu tiên, giá trị ID của Laquel (0x4A) được chứa trong Register v0, sau đó CPU đọc giá trị ID của nhân vật chủ động tấn công ở địa chỉ cách framepointer một khoảng 0x1C byte. Sau đó hai giá trị này (ID của Laquel và ID của nhân vật chủ động tấn công) được so sánh với nhau, nếu không bằng nhau thì CPU sẽ nhảy đến đoạn xử lý bình thường. Còn nếu hai giá trị này bằng nhau thì CPU sẽ tiếp tục xử lý đoạn 0x8010D2F8 ở trên.

    Từ đây, nếu ta thay con số 0x4A bằng giá trị khác thì lời thề bất sát sẽ được chuyển sang nhân vật mới. Hoặc nếu muốn vô hiệu hóa đoạn xử lý bất sát này thì chỉ cần đổi thành lệnh luôn nhảy đến xử lý ở 0x8010D324, bất kể kết quả so sánh là như thế nào.

    8010D294: J 0x8010D324

    Hoặc cũng có thể tạo lời thề bất sát cho nhiều nhân vật bằng một loạt phép so sánh với từng ID.

    Phần code tính toán này nằm ở file B.bin ở thư mục gốc của CD, nên chỉ cần thay thế file này là có thể thấy kết quả như mong muốn.

    Video dưới đây là toàn bộ quá trình tìm ra routine xử lý lời thề bất sát của Laquel, và cái kết thay đổi nó.


     

Chia sẻ trang này