data/claims/parappa-the-rapper-2.yamlPaRappa the Rapper 2 (the parappadev/parappa2 decompilation)
Game: PaRappa the Rapper 2.
In the July 12th NTSC-J prototype of PaRappa the Rapper 2, a press is judged by which slot of a fixed grid it lands in, not by its distance from the teacher's note. The line clock converts a vsync count to ticks as count × 96 × tempo / 3600, so with 60 vsyncs a second and tempo in beats per minute a beat is 96 ticks (computed). Each press is stored with its time less the line's start time (ofs_frame); on_th_make adds that start modulo 96, plus 96, and divides by 4, which puts the press in the 4-tick slot of its own time modulo 96 (computed). thnum_get reads the slot in a 24-bit pattern that repeats every beat and numbers its runs of equal bits, so a press in a run of 1s gets an even th_num and a press in a run of 0s an odd one. For the main grid (CK_TH_NORMAL, 0x00e79e79) the 1s cover ticks 92 to 11, 20 to 35, 44 to 59, and 68 to 83 of each beat, that is from 4 ticks before to 11 ticks after each multiple of 24 (a quarter of the beat), and the 0s cover the 8 ticks from 12 to 19 after it (computed). In the first of a line's three checks, exh_normal_add counts each press in a 1-run whose key the teacher's line uses (otehon_all; while the rank is Cool, any key, and in one-button play, triangle) and exh_normal_sub takes one off for each such press in a 0-run, each weighted 48/16, that is ±3 (computed); a press anywhere in a run scores the same. The same check's imitation test (exh_mane, weight 8/16) runs over every pair of a teacher's note and a press and counts each pair with the same th_num and, outside one-button play, the same key, so one press can count once for each note in its run; it scores 2 × that count − presses; it runs again on a late grid (CK_TH_LATE, 0x00f3cf3c, with the teacher's notes moved 2 slots later) and keeps the better result. The third check uses a swing grid (CK_TH_HANE, 0x00c1cc1c). A line's score (now_score) is the sum of the three checks' totals, and in the game play step (PSTEP_GAME) it is then halved as (now_score + 1) / 2 in one-button play (PLAY_TYPE_ONE) and halved that way again in the easy table mode (PLAY_TABLE_EASY), so twice when both apply; a check's total is 0 when its all-keys test fails and −100 when the presses reach the line's length in 24-tick steps, rounded up, plus 3 (exh_renda_out). In the hook play step (PSTEP_HOOK) a line is scored by an imitation test alone (manemane_check), on the main grid. parappa-the-rapper-2/press-grid
About: PaRappa the Rapper 2 Timing window
Value: 16 ticks of every 24 in which a press scores, from 4 before to 11 after each step (computed from 0x00e79e79; 96 ticks a beat)
Codeparappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) ·src/main/scrctrl.c#L3990-L3991if (scr_main_pp->scr_ctrl_pp[i].gtime_type == GTIME_VSYNC) {
scr_main_pp->scr_ctrl_pp[i].lineTime = ((TimeCallbackTimeGetChan(i) * 96.0f * GetLineTempo(i) + 1800.0f) / 3600.0f);parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2141-L2145SCR_TAP_MEMORY *mkey_pp = &sindv_pp->scr_tap_memory[sindv_pp->scr_tap_memory_cnt];
…
mkey_pp->ofs_frame = onKeyTime - tapset_pp->taptimeStart - sindv_pp->current_time;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2765-L2787exam_check[i].tapset_pp = IndvGetTapSetAdrs(sindv_pp);
…
exam_check[i].ofs_tick = exam_check[i].tapset_pp->taptimeStart + sindv_pp->current_time;
…
exam_check[i].otehon_all = otehon_all_make(&exam_check[i]);
…
if (global_data.play_typeL == PLAY_TYPE_ONE) {
exam_check[i].otehon_all = KcTR;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L136static u_int thnum_tbl[] = { 0x00e79e79, 0x00f3cf3c, 0x00c1cc1c, 0x00000000 };parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2222-L2247static int thnum_get(int p96_num, CK_TH_ENUM ckth) {
…
thnum_data = thnum_tbl[ckth];
for (i = 0; i <= p96_num; i++) {
ck_dat = (thnum_data >> (23 - (i % 24))) & 1;
if (ck_bit < 0) {
if (ck_dat == 0) {
ret_cnt = 1;
}
} else if (ck_dat != ck_bit) {
ret_cnt++;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2253-L2312static void on_th_make(EXAM_CHECK *ec_pp, CK_TH_ENUM ckth) {
…
ofsT = ec_pp->ofs_tick % 96;
ofsT += 96;
…
frame = ec_pp->stm_pp[i].ofs_frame;
p96_num = (frame + ofsT) / 4;
…
ec_pp->ted[i].th_num = thnum_get(p96_num, ckth);
…
if (ckth == CK_TH_LATE) {
p96_num += 2;
…parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2317-L2343static int exh_normal_add(EXAM_CHECK *ec_pp) {
…
if (!(ec_pp->ted[i].th_num & 1)) {
if (GetIndex2KeyCode(ec_pp->ted[i].key) & ec_pp->otehon_all) {
ret++;
…
static int exh_normal_sub(EXAM_CHECK *ec_pp) {
…
if (GetIndex2KeyCode(ec_pp->ted[i].key) & ec_pp->otehon_all) {
ret -= ec_pp->ted[i].th_num & 1;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L99-L110static EXH_STR exh_str_normal[] = {
{ .score_prg = exh_normal_add, .save_p = EXH_NORMAL_ADD, .bairitu = 48 },
{ .score_prg = exh_normal_sub, .save_p = EXH_NORMAL_SUB, .bairitu = 48 },
…
{ .score_prg = exh_mane, .save_p = EXH_MANE, .bairitu = 8 },
{ .score_prg = exh_all_add, .save_p = EXH_TOTAL, .bairitu = 16 },parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2583-L2684static int exh_renda_out(EXAM_CHECK *ec_pp) {
…
renda_ck = ec_pp->tapset_pp->taptimeEnd - ec_pp->tapset_pp->taptimeStart;
renda_ck = ((renda_ck + 23) / 24);
renda_ck += 3;
if (ec_pp->ted_num >= renda_ck) {
return -1;
…
static int manemane_check_sub(EXAM_CHECK *ec_pp) {
…
if (global_data.play_typeL == PLAY_TYPE_ONE) {
if (ec_pp->oth[i].th_num == ec_pp->ted[j].th_num) {
…parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2790-L2856if (global_data.play_step == PSTEP_HOOK) {
…
scrprgstr_pp = scrprgstr_hook;
…
CK_TH_ENUM ck_th_enum_tbl[3] = {
CK_TH_NORMAL,
CK_TH_NORMAL,
CK_TH_HANE
};
…
on_th_make(exam_check_pp, ck_th_enum_tbl[i]);
…
…parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L3077-L3087if (scex_pp->exam_do == EXAM_DO_NON) {
ExamScoreCheck(sindv_pp);
if (global_data.play_step == PSTEP_GAME) {
if (global_data.play_typeL == PLAY_TYPE_ONE) {
sindv_pp->global_ply->now_score = (sindv_pp->global_ply->now_score + 1) / 2;
}
if (global_data.play_table_modeL == PLAY_TABLE_EASY) {
sindv_pp->global_ply->now_score = (sindv_pp->global_ply->now_score + 1) / 2;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L131-L134static EXH_STR exh_str_hook[] = {
{ .score_prg = manemane_check, .save_p = EXH_MANE, .bairitu = 16 },
{ .score_prg = exh_all_add, .save_p = EXH_TOTAL, .bairitu = 16 },Checked by claude, 2026-10-22 — read ScrExamSetCheck's halving at PSTEP_GAME (L3080-L3087), after ExamScoreCheck; read thnum_get, on_th_make, treateTimeChange, and every exh_ check whole; the exh_str tables and scrprgstr; ExamScoreCheck whole (the three checks, their grids, the bairitu/16 weighting, the sum); the press store in tapEventCheck (L2134-L2150); and ScrTimeRenew's vsync branch. The grid runs were decoded by script from the three thnum_tbl words, with thnum_get's run count replayed across the beat boundary (slot 23 joins slots 0-2 of the next beat). That vsyncs run at 60 a second and that tempo is in beats per minute are not shown in these lines; the statement makes the 96-tick beat conditional on both. exh_yaku (the original and swing checks' pattern test) was not traced, and the statement says nothing of itCited by: PaRappa the Rapper 2, Timing window
In the July 12th NTSC-J prototype of PaRappa the Rapper 2, the rank is one of 14 levels (RANK_LEVEL, RLVL_COOL 0 to RLVL_END2 13), and RANK_LEVEL2DISP_LEVEL shows levels 0 and 1 as Cool, 2 to 4 as Good, 5 to 7 as Bad, 8 to 10 as Awful, and 11 to 13 as none (DLVL_MAX). The stage's score data sets each exam's type (SCRRJ_EXAM). At an exam other than hook, VS, cancel, and bonus, the player's line score (now_score) is set against a target (exam_coolP): the line's coolup from the stage data plus what the teacher's own presses score when run through the same checks (ExamScoreCheckSame, which scores a copy and returns its unhalved sum, so in one-button play or the easy table mode a halved line score meets a target whose teacher part isn't halved), a part that is a flat 150 while the rank is at level 0 or 1, and 11111 when there is no line. Under a Cool exam a score below the target steps the rank down (levelDownRank) and any other steps it up (levelUpRank). Under a Good exam a score at or above the target steps up, a score of 0 or less steps down, and a score between them moves level 2 (RLVL_GOOD_COOL) down to Good, leaves Good alone, and steps any other level up. Under a Bad or Awful exam a score above 0 steps up and any other steps down. A step moves to the next level along the tables, which put an in-between level beside each rank: up from Good (3) is level 2, still shown Good, then Cool (0); down from Good is level 4, then Bad (6); down from Awful (9) is level 10 and then RLVL_END1 (12). So a shown rank changes after two steps the same way, and a step back from an in-between level returns to the plain rank (computed from the tables). In one-button play (PLAY_TYPE_ONE) or the easy table mode (PLAY_TABLE_EASY), a Good exam that would leave the rank at level 0, 1, or 2 sets it to Good (3), so the rank can't reach Cool there. parappa-the-rapper-2/rank-meter
About: PaRappa the Rapper 2 Rank meter
Value: 2 steps the same way, one per exam, that change the shown rank (computed from levelUpRank and levelDownRank)
Codeparappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) ·include/main/etc.h#L246-L259RLVL_COOL = 0,
RLVL_COOL_GOOD = 1,
RLVL_GOOD_COOL = 2,
RLVL_GOOD = 3,
RLVL_GOOD_BAD = 4,
RLVL_BAD_GOOD = 5,
RLVL_BAD = 6,
RLVL_BAD_AWFUL = 7,
RLVL_AWFUL_BAD = 8,
RLVL_AWFUL = 9,
RLVL_AWFUL_END = 10,
RLVL_END0 = 11,
…parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L453-L464DISP_LEVEL RANK_LEVEL2DISP_LEVEL(RANK_LEVEL lvl) {
DISP_LEVEL lvl_tbl[17] = {
DLVL_COOL, DLVL_COOL,
DLVL_GOOD, DLVL_GOOD, DLVL_GOOD,
DLVL_BAD, DLVL_BAD, DLVL_BAD,
DLVL_AWFUL, DLVL_AWFUL, DLVL_AWFUL,
DLVL_MAX, DLVL_MAX, DLVL_MAX,parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L1352-L1353case SCRRJ_EXAM:
sindv_pp->scr_exam_str.exam_enum = Rsub;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L3067-L3087if (scex_pp->exam_enum == EXAM_CANCEL) {
…
if (scex_pp->exam_enum == EXAM_BONUS) {
…
if (scex_pp->exam_do == EXAM_DO_NON) {
ExamScoreCheck(sindv_pp);
if (global_data.play_step == PSTEP_GAME) {
if (global_data.play_typeL == PLAY_TYPE_ONE) {
sindv_pp->global_ply->now_score = (sindv_pp->global_ply->now_score + 1) / 2;
}
if (global_data.play_table_modeL == PLAY_TABLE_EASY) {
sindv_pp->global_ply->now_score = (sindv_pp->global_ply->now_score + 1) / 2;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2873-L2908global_ply_tmp = *sindv_pp->global_ply;
sindv_tmp = *sindv_pp;
sindv_tmp.global_ply = &global_ply_tmp;
…
ExamScoreCheck(&sindv_tmp);
return global_ply_tmp.now_score;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2859-L2871static int ExamScoreCheckSame(SCORE_INDV_STR *sindv_pp) {
…
if (sindv_pp->global_ply->rank_level == RLVL_COOL ||
sindv_pp->global_ply->rank_level == RLVL_COOL_GOOD) {
return 150;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L3201-L3259scex_pp->exam_point = sindv_pp->global_ply->now_score;
if (tapset_pp == NULL) {
scex_pp->exam_coolP = 11111;
} else {
exp = ExamScoreCheckSame(sindv_pp);
if (global_data.play_step != PSTEP_VS) {
scex_pp->exam_coolP = tapset_pp->coolup + exp;
…
case EXAM_COOL:
if (scex_pp->exam_point < scex_pp->exam_coolP) {
rank_saki = levelDownRank(rank_moto);
} else {
…parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2922-L2968static int levelUpRank(RANK_LEVEL lvl) {
RANK_LEVEL up_tbl[17] = {
/* COOL -> COOL */ RLVL_COOL,
/* COOL/GOOD -> COOL */ RLVL_COOL,
/* GOOD/COOL -> COOL */ RLVL_COOL,
/* GOOD -> GOOD/COOL */ RLVL_GOOD_COOL,
/* GOOD/BAD -> GOOD */ RLVL_GOOD,
/* BAD/GOOD -> GOOD */ RLVL_GOOD,
/* BAD -> BAD/GOOD */ RLVL_BAD_GOOD,
/* BAD/AWFUL -> BAD */ RLVL_BAD,
/* AWFUL/BAD -> BAD */ RLVL_BAD,
/* AWFUL -> AWFUL/BAD */ RLVL_AWFUL_BAD,
…parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L3487-L3491if (scex_pp->exam_enum != EXAM_HOOK && scex_pp->exam_enum != EXAM_VS) {
if (rank_saki != rank_moto) {
int line_change;
sindv_pp->global_ply->rank_level = rank_saki;Checked by claude, 2026-10-22 — read the RANK_LEVEL enum, RANK_LEVEL2DISP_LEVEL, levelUpRank and levelDownRank whole (the arrays' values, not their comments, which are the decompilers'), ExamScoreCheckSame whole (it scores a copy with ExamScoreCheck and returns that sum, with no halving), the halving at L3080-L3087, the SCRRJ_EXAM case of the score reader, and ScrExamSetCheck from the exam-type gotos (L3067-L3078) through the target, the Cool, Good, Bad, and Awful cases, and the write of the new level (L3487-L3491). The two-step reading was worked through every level from 0 to 10 by hand. Where the rank sits when a given exam type comes up is set by the stage's score data, which the atlas doesn't fetch; the statement says only what each exam type does. What levels 11 to 13 lead to (game over) is not tracedCited by: PaRappa the Rapper 2, Rank meter
In the July 12th NTSC-J prototype of PaRappa the Rapper 2, while the rank is at level 0 or 1 (RLVL_COOL, RLVL_COOL_GOOD, both shown as Cool), a line is scored three ways differently. Every key the game maps (KcAll: triangle, circle, cross, square, L1, and R1 by GetIndex2KeyCode) counts as one the teacher's line uses (otehon_all), where otherwise only the keys in the teacher's line count, so on-grid presses of any of them score and exh_nombar_sub, which takes points for keys outside the teacher's, finds none (computed); in one-button play (PLAY_TYPE_ONE) the set is triangle alone either way. Five checks score 0: the all-keys test (EXH_ALLKEY_OUT), the imitation test (EXH_MANE), and three that compare the presses with the teacher's notes (EXH_MBAR_KEY_OUT and EXH_MBAR_TIME_OUT, the first press's key and slot run against the first note's, and EXH_MBAR_NUM_OUT, the count of presses against the count of notes). And the teacher's part of the target is a flat 150 (ExamScoreCheckSame), not the teacher's notes scored. The atlas reads this as Cool's freestyle: following the teacher is no longer checked, and off-grid presses and mashing (exh_renda_out) still cost. Under a Good exam the rank gets to Cool after two lines in a row that each score at least the target, the line's coolup plus the teacher's notes scored (computed: Good to level 2, then 0), except in one-button play or the easy table mode, which hold it at Good; under a Cool exam a line below coolup + 150 steps it down, and two in a row put it back at Good (computed). parappa-the-rapper-2/cool-mode
About: PaRappa the Rapper 2 Rank meter
Value: 150 the teacher's part of a Cool line's target, added to the line's coolup
Codeparappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) ·src/main/scrctrl.c#L2773-L2787if (sindv_pp->global_ply->rank_level == RLVL_COOL || sindv_pp->global_ply->rank_level == RLVL_COOL_GOOD) {
exam_check[i].otehon_all = KcAll;
} else {
exam_check[i].otehon_all = otehon_all_make(&exam_check[i]);
}
…
if (global_data.play_typeL == PLAY_TYPE_ONE) {
exam_check[i].otehon_all = KcTR;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · include/main/scrctrl.h#L81-L88typedef enum {
KcTR = 16,
KcCI = 32,
KcXX = 64,
KcSQ = 128,
KcL1 = 4,
KcR1 = 8,
KcAll = 252,parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/etc.c#L500-L503int GetIndex2KeyCode(int index) {
static int KeyCode[7] = {
0, KcTR, KcCI, KcXX, KcSQ, KcL1, KcR1,parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2345-L2357static int exh_nombar_sub(EXAM_CHECK *ec_pp) {
…
int keycode = GetIndex2KeyCode(ec_pp->ted[i].key);
if ((keycode & ec_pp->otehon_all) == 0) {
ret -= 1;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2383-L2420static int exh_mbar_key_out(EXAM_CHECK *ec_pp) {
…
} else if (ec_pp->oth[0].key == ec_pp->ted[0].key) {
…
static int exh_mbar_time_out(EXAM_CHECK *ec_pp) {
…
} else if (ec_pp->oth[0].th_num == ec_pp->ted[0].th_num) {
…
static int exh_mbar_num_out(EXAM_CHECK *ec_pp) {
int ret = ec_pp->ted_num - ec_pp->oth_num;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2831-L2844ret = scrprgstr_pp->exh_str_pp[j].score_prg(exam_check_pp);
if (sindv_pp->global_ply->rank_level == RLVL_COOL || sindv_pp->global_ply->rank_level == RLVL_COOL_GOOD) {
if (scrprgstr_pp->exh_str_pp[j].save_p == EXH_ALLKEY_OUT ||
scrprgstr_pp->exh_str_pp[j].save_p == EXH_MBAR_NUM_OUT ||
scrprgstr_pp->exh_str_pp[j].save_p == EXH_MBAR_KEY_OUT ||
scrprgstr_pp->exh_str_pp[j].save_p == EXH_MANE ||
scrprgstr_pp->exh_str_pp[j].save_p == EXH_MBAR_TIME_OUT) {
ret = 0;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2868-L2871if (sindv_pp->global_ply->rank_level == RLVL_COOL ||
sindv_pp->global_ply->rank_level == RLVL_COOL_GOOD) {
return 150;parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L3209-L3247exp = ExamScoreCheckSame(sindv_pp);
if (global_data.play_step != PSTEP_VS) {
scex_pp->exam_coolP = tapset_pp->coolup + exp;
…
case EXAM_COOL:
if (scex_pp->exam_point < scex_pp->exam_coolP) {
rank_saki = levelDownRank(rank_moto);
…
case EXAM_GOOD:
if (scex_pp->exam_point >= scex_pp->exam_coolP) {
rank_saki = levelUpRank(rank_moto);
…
…parappa2: a decompilation of PaRappa the Rapper 2 (parappadev/parappa2) · src/main/scrctrl.c#L2922-L2951RANK_LEVEL up_tbl[17] = {
/* COOL -> COOL */ RLVL_COOL,
/* COOL/GOOD -> COOL */ RLVL_COOL,
/* GOOD/COOL -> COOL */ RLVL_COOL,
/* GOOD -> GOOD/COOL */ RLVL_GOOD_COOL,
…
RANK_LEVEL down_tbl[17] = {
/* COOL -> COOL/GOOD */ RLVL_COOL_GOOD,
/* COOL/GOOD -> GOOD */ RLVL_GOOD,Checked by claude, 2026-10-22 — read ExamScoreCheck's otehon_all setup and its Cool zeroing in the non-hook loop (the hook loop, L2803-L2811, zeroes the same five under the hook Cool levels), ExamScoreCheckSame's early return, exh_nombar_sub, the three mbar checks, GetIndex2KeyCode, KEY_CODE_ENUM, and the Cool and Good exam cases with levelUpRank and levelDownRank. That nombar finds no penalty in Cool follows from KcAll holding every code GetIndex2KeyCode returns but 0, and a stored press always has a key (tapEventCheck stores one only when KeyIndex isn't KiNO, L2140). "Freestyle" is the atlas's word; the code names no such modeCited by: PaRappa the Rapper 2, Rank meter