PS2SDK
PS2 Homebrew Libraries
igreeting.c
1 /*
2 # _____ ___ ____ ___ ____
3 # ____| | ____| | | |____|
4 # | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5 #-----------------------------------------------------------------------
6 # Copyright ps2dev - http://www.ps2dev.org
7 # Licenced under Academic Free License version 2.0
8 # Review ps2sdk README & LICENSE files for further details.
9 */
10 
11 #include "irx_imports.h"
12 
13 #include <iop_mmio_hwport.h>
14 #include <tamtypes.h>
15 #include <mipscopaccess.h>
16 
17 struct RomImgData
18 {
19  const void *ImageStart;
20  const void *RomdirStart;
21  const void *RomdirEnd;
22 };
23 
24 struct RomDirEntry
25 {
26  char name[10];
27  u16 ExtInfoEntrySize;
28  unsigned int size;
29 };
30 
31 struct RomdirFileStat
32 {
33  const struct RomDirEntry *romdirent;
34  const void *data;
35  const struct ExtInfoFieldEntry *extinfo;
36 };
37 
38 struct ExtInfoFieldEntry
39 {
40  // cppcheck-suppress unusedStructMember
41  u16 value;
42  u8 ExtLength;
43  u8 type;
44  u8 payload[];
45 };
46 
47 #ifdef IGREETING_DTL_T
48 struct rominfo_item
49 {
50  // cppcheck-suppress unusedStructMember
51  const char *m_device_name;
52  void *m_delay_register;
53  int m_delay_register_value;
54  void *m_base_address;
55  int m_chunk_size;
56  int m_config_offset_1;
57  int m_config_offset_2;
58  int m_flash_size;
59  int m_manufacturer;
60  int m_device_id;
61  int m_write_width;
62  int m_address_register;
63  // cppcheck-suppress unusedStructMember
64  int m_flash_offset;
65  int m_flash_blocksize;
66 };
67 
68 struct romflash_sigcheck_res
69 {
70  void *m_config_addr_1;
71  void *m_config_addr_2;
72  void *m_base;
73  void *m_base_end;
74  int m_manufacturer_res;
75  int m_device_id_res;
76 };
77 #endif
78 
79 #ifdef IGREETING_DTL_T
80 static void do_get_dip_switch_values_chr(char *str, int val, int count);
81 #endif
82 static struct RomImgData *GetIOPRPStat(const u32 *start_addr, const u32 *end_addr, struct RomImgData *rid);
83 static struct RomdirFileStat *
84 GetFileStatFromImage(const struct RomImgData *rid, const char *filename, struct RomdirFileStat *rdfs);
85 static const struct ExtInfoFieldEntry *
86 do_find_extinfo_entry(const struct RomdirFileStat *rdfs, unsigned int extinfo_type);
87 #ifdef IGREETING_DTL_T
88 static const struct rominfo_item *flash_probe(const struct rominfo_item *rii);
89 #endif
90 
91 #ifdef IGREETING_DTL_T
92 static const char *boardinfo_uma_dsw202 =
93  "\r\nUMA board DSW202\r\n --- PS kernel --\r\n Sw8 bit0 0^ use H1500, 1_ no use H1500 (CD-BOOT "
94  "only)\r\n Sw7 bit1 0^ display color bar 1_ no color bar\r\n Sw6 bit2 0^ IOP Kernel 1_ PS Kernel "
95  "(when PS mode)\r\n Sw5 bit3 0^ check cd-rom 1_ ignore cdrom always\r\n Sw4 bit4 0^ Dram 16M "
96  "1_ Dram 2M\r\n Sw3 bit5 0^ disable 1_ enable EE ssbus access\r\n --- IOP kernel --\r\n Sw5 bit3 "
97  "0^ Extr Wide DMA 1_ Extr Wide DMA disable\r\n Sw4 bit4 0^ Dram 16M 1_ Dram 2M\r\n Sw3 bit5 "
98  "0^ disable 1_ enable EE ssbus access\r\n --- EE --\r\n Sw2 bit6 0^ -- 1_ --\r\n "
99  "Sw1 bit7 0^ EE normal boot 1_ EE/GS self test\r\n\r\n";
100 static const char *boardinfo_common =
101  " --- PS kernel --\r\n D0 0^ use H1500, 1_ no use H1500 (CD-BOOT only)\r\n D1 0^ display color bar "
102  "1_ no color bar\r\n D2 0^ IOP Kernel 1_ PS Kernel (when PS mode)\r\n D3 0^ check cd-rom 1_ "
103  "ignore cdrom always\r\n D4 0^ Dram 8M 1_ Dram 2M\r\n D5 0^ disable 1_ enable EE ssbus "
104  "access\r\n --- IOP kernel --\r\n D0 0^ NTSC mode 1_ PAL mode\r\n D3 0^ Extr Wide DMA 1_ Extr "
105  "Wide DMA disable\r\n D4 0^ Dram 8M 1_ Dram 2M\r\n D5 0^ disable 1_ enable EE ssbus "
106  "access\r\n --- EE --\r\n D6 0^ -- 1_ --\r\n D7 0^ EE normal boot 1_ EE/GS self "
107  "test\r\n\r\n";
108 static const char *boardinfo_unknown = "\r\nUnknown board\r\n";
109 #endif
110 static const char *romgen_eq_str = " ROMGEN=";
111 #ifdef IGREETING_DTL_T
112 static const char *cpuid_eq_str = " CPUID=";
113 #else
114 static const char *cpuid_eq_str = ", IOP info (CPUID=";
115 #endif
116 static const char *cach_config_eq_str = ", CACH_CONFIG=";
117 #ifdef IGREETING_DTL_T
118 static struct rominfo_item default_list[] = {
119  {"Gmain1-3, flash mode, Fujitsu 512K flash",
120  (void *)0xBF801010,
121  1256783,
122  (void *)0xBFC00000,
123  1,
124  1365,
125  682,
126  524288,
127  4,
128  164,
129  8,
130  0,
131  0,
132  0},
133  {"Gmain4..., flash mode, Fujitsu 2M flash",
134  (void *)0xBF801010,
135  1387855,
136  (void *)0xBFC00000,
137  2,
138  2730,
139  1365,
140  2097152,
141  4,
142  196,
143  8,
144  0,
145  0,
146  0},
147  {"Gmain4..., flash mode, AMD 2M flash",
148  (void *)0xBF801010,
149  1387855,
150  (void *)0xBFC00000,
151  2,
152  2730,
153  1365,
154  2097152,
155  1,
156  196,
157  8,
158  0,
159  0,
160  0},
161  {"shimakawa sp, flash mode, Fujitsu 4M flash",
162  (void *)0xBF801010,
163  1453391,
164  (void *)0xBFC00000,
165  2,
166  2730,
167  1365,
168  4194304,
169  4,
170  95,
171  8,
172  0,
173  0,
174  0},
175  {NULL, NULL, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
176 #endif
177 
178 int _start(int ac, char **av)
179 {
180  int *boot_mode_4;
181  int cop0_processor_mode;
182  const char *iop_or_ps_mode_str;
183  const struct ExtInfoFieldEntry *extinfo_entry;
184  const char *extinfo_id_str;
185  const char *comma_index;
186  struct RomImgData rid;
187  struct RomdirFileStat rdfs;
188 #ifdef IGREETING_DTL_T
189  int *boot_mode_2;
190  int *boot_mode_3;
191  const char *board_type_str;
192  const char *board_info_str;
193  int boardtype_int;
194  int board_has_wide_dma;
195  const char *rom_or_flashrom_boot_str;
196  u8 tmp_dma_wide_ch;
197  char switch_values_str[16];
198 #endif
199  USE_IOP_MMIO_HWPORT();
200 
201  (void)ac;
202  (void)av;
203 
204  boot_mode_4 = QueryBootMode(4);
205 #ifndef IGREETING_DTL_T
206  printf("\nPlayStation 2 ======== ");
207 #endif
208  if ( boot_mode_4 )
209  {
210  switch ( *(u16 *)boot_mode_4 )
211  {
212 #ifndef IGREETING_DTL_T
213  case 0:
214  printf("Hard reset boot");
215  break;
216 #endif
217  case 1:
218  printf("Soft reboot");
219  break;
220  case 2:
221  printf("Update rebooting..");
222 #ifdef IGREETING_DTL_T
223  boot_mode_3 = QueryBootMode(3);
224  if ( boot_mode_3 )
225  printf(" reset parameter for IOP=%08x_%08x", boot_mode_3[2], boot_mode_3[1]);
226 #endif
227  break;
228  case 3:
229  printf("Update reboot complete");
230  break;
231  default:
232  break;
233  }
234  printf("\n");
235  }
236  if ( !boot_mode_4 || !*(u16 *)boot_mode_4 )
237  {
238  cop0_processor_mode = get_mips_cop_reg(0, COP0_REG_PRId);
239 #ifdef IGREETING_DTL_T
240  board_type_str = "";
241  boardtype_int = iop_mmio_hwport->exp2_r2[4612];
242  board_info_str = boardinfo_common;
243  board_has_wide_dma = 1;
244  switch ( boardtype_int & 0xF8 )
245  {
246  case 16:
247  board_type_str = "\r\nGmain-1.0 board DSW602\r\n";
248  break;
249  case 32:
250  board_type_str = "\r\nGmain-2.0 board DSW602\r\n";
251  break;
252  case 48:
253  board_type_str = "\r\nGmain-3.0 board DSW602\r\n";
254  break;
255  case 64:
256  board_type_str = "\r\nB3system-1.0 front dipsw\r\n";
257  board_has_wide_dma = 0;
258  break;
259  case 80:
260  board_type_str = "\r\nGmain-4.0 board DSW602\r\n";
261  break;
262  case 88:
263  board_type_str = "\r\nGmain-5.0 board DSW602\r\n";
264  break;
265  case 96:
266  board_type_str = "\r\nMPU-4.0 board DSW602\r\n";
267  board_has_wide_dma = 0;
268  break;
269  case 112:
270  board_type_str = "\r\nGmain-10.0/11.0 board DSW602\r\n";
271  break;
272  case 120:
273  board_type_str = "\r\nGmain-12.0 board DSW602\r\n";
274  break;
275  case 124:
276  board_type_str = "\r\nGmain-13.0 board DSW602\r\n";
277  break;
278  default:
279  board_info_str = boardinfo_unknown;
280  board_has_wide_dma = 0;
281  break;
282  }
283  if ( boardtype_int < 2 )
284  {
285  board_info_str = boardinfo_uma_dsw202;
286  board_has_wide_dma = (boardtype_int & 0xFE) == 0;
287  }
288  write(1, (char *)board_type_str, strlen(board_type_str));
289  // Unofficial: make board info string not go through a structure
290  write(1, (char *)board_info_str, strlen(board_info_str));
291  write(1, (char *)cpuid_eq_str, strlen(cpuid_eq_str));
292  printf("%x", cop0_processor_mode);
293  write(1, (char *)romgen_eq_str, strlen(romgen_eq_str));
294  printf("%04x-%04x", *(vu16 *)(0xBFC00102), *(vu16 *)(0xBFC00100));
295 #else
296  write(1, (char *)romgen_eq_str, strlen(romgen_eq_str));
297  printf("%04x-%04x", *(vu16 *)(0xBFC00102), *(vu16 *)(0xBFC00100));
298  write(1, (char *)cpuid_eq_str, strlen(cpuid_eq_str));
299  printf("%x", cop0_processor_mode);
300 #endif
301  write(1, (char *)cach_config_eq_str, strlen(cach_config_eq_str));
302  printf("%lx, %ldMB", (unsigned long)*(vu32 *)(0xFFFE0130), (long)((u32)QueryMemSize() + 256) >> 20);
303  if ( cop0_processor_mode >= 16 )
304  {
305 #ifdef IGREETING_DTL_T
306  iop_or_ps_mode_str = ((iop_mmio_hwport->iop_sbus_ctrl[0] & 8) != 0) ? ", PS mode" : ", IOP mode";
307 #else
308  iop_or_ps_mode_str = ((iop_mmio_hwport->iop_sbus_ctrl[0] & 8) != 0) ? ", PS mode)\r\n" : ", IOP mode)\r\n";
309 #endif
310  write(1, (char *)iop_or_ps_mode_str, strlen(iop_or_ps_mode_str));
311  }
312 #ifdef IGREETING_DTL_T
313  CpuDisableIntr();
314  rom_or_flashrom_boot_str = (flash_probe(default_list)) ? ", FlashROM boot\r\n" : ", ROM boot\r\n";
315  CpuEnableIntr();
316  write(1, (char *)rom_or_flashrom_boot_str, strlen(rom_or_flashrom_boot_str));
317 #endif
318  // Unofficial: pad filename
319  if (
320  GetIOPRPStat((u32 *)0xBFC00000, (u32 *)0xBFC10000, &rid)
321  && GetFileStatFromImage(&rid, "ROMDIR\x00\x00\x00\x00\x00", &rdfs) )
322  {
323  extinfo_entry = do_find_extinfo_entry(&rdfs, 3);
324  if ( extinfo_entry )
325  {
326  extinfo_id_str = (const char *)extinfo_entry->payload;
327  comma_index = rindex(extinfo_id_str, ',');
328  write(1, " <", 2);
329  // Unofficial: if comma not found, just write the whole thing
330  write(1, (char *)extinfo_id_str, comma_index ? (comma_index - extinfo_id_str) : (int)strlen(extinfo_id_str));
331  printf(":%ld>\n", (long)(u16)(uiptr)extinfo_id_str);
332  }
333  }
334 #ifdef IGREETING_DTL_T
335  write(1, " SW=b7:", 7);
336  do_get_dip_switch_values_chr(switch_values_str, (u8)iop_mmio_hwport->exp2_r2[4352], 8);
337  write(1, switch_values_str, 8);
338  write(1, ":b0", 3);
339  boot_mode_2 = QueryBootMode(2);
340  if ( boot_mode_2 )
341  {
342  printf(", RESET parameter EE=%08x_%08x", boot_mode_2[2], boot_mode_2[1]);
343  boot_mode_3 = QueryBootMode(3);
344  if ( boot_mode_3 )
345  printf(", IOP=%08x_%08x", boot_mode_3[2], boot_mode_3[1]);
346  }
347  write(1, "\r\n", 2);
348  if ( board_has_wide_dma )
349  {
350  tmp_dma_wide_ch = iop_mmio_hwport->exp2_r2[4608];
351  iop_mmio_hwport->exp2_r2[4608] = -1;
352  printf(" DMA_WIDE_CH=%x\n", (u8)iop_mmio_hwport->exp2_r2[4608]);
353  iop_mmio_hwport->exp2_r2[4608] = tmp_dma_wide_ch;
354  }
355  if ( (iop_mmio_hwport->exp2_r2[4352] & (1 << 5)) != 0 )
356  {
357  iop_mmio_hwport->iop_sbus_ctrl[0] |= 1;
358  }
359 #endif
360  }
361  return 1;
362 }
363 
364 #ifdef IGREETING_DTL_T
365 static void do_get_dip_switch_values_chr(char *str, int val, int count)
366 {
367  int i;
368 
369  for ( i = 0; i < count; i += 1 )
370  {
371  str[i] = (((val >> ((count - i) - 1)) & 1) != 0) ? '_' : '^';
372  }
373 }
374 #endif
375 
376 static struct RomImgData *GetIOPRPStat(const u32 *start_addr, const u32 *end_addr, struct RomImgData *rid)
377 {
378  unsigned int cur_offset;
379 
380  for ( cur_offset = 0; &start_addr[cur_offset >> 2] < end_addr; cur_offset += 16 )
381  {
382  if (
383  start_addr[cur_offset >> 2] == 0x45534552 && start_addr[(cur_offset >> 2) + 1] == 0x54
384  && !(start_addr[(cur_offset >> 2) + 2] & 0xFFFF)
385  && ((start_addr[(cur_offset >> 2) + 3] + 15) & 0xFFFFFFF0) == cur_offset )
386  {
387  rid->ImageStart = start_addr;
388  rid->RomdirStart = &start_addr[cur_offset >> 2];
389  rid->RomdirEnd = (char *)&start_addr[cur_offset >> 2] + *(&start_addr[cur_offset >> 2] + 7);
390  return rid;
391  }
392  }
393  rid->ImageStart = 0;
394  return 0;
395 }
396 
397 static struct RomdirFileStat *
398 GetFileStatFromImage(const struct RomImgData *rid, const char *filename, struct RomdirFileStat *rdfs)
399 {
400  int cur_addr_aligned;
401  int total_extinfo_size;
402  const struct RomDirEntry *RomdirStart;
403  int i;
404  char cur_romdir_name[12];
405 
406  cur_addr_aligned = 0;
407  total_extinfo_size = 0;
408  memset(cur_romdir_name, 0, sizeof(cur_romdir_name));
409  for ( i = 0; i < 12 && filename[i] >= ' '; i += 1 )
410  {
411  cur_romdir_name[i] = filename[i];
412  }
413  for ( RomdirStart = (const struct RomDirEntry *)rid->RomdirStart;
414  (u32 *)(RomdirStart->name) != (u32 *)(cur_romdir_name)
415  && (u32 *)(RomdirStart->name + 4) != (u32 *)(cur_romdir_name + 4)
416  && (u16 *)(RomdirStart->name + 8) != (u16 *)(cur_romdir_name + 8);
417  RomdirStart += 1 )
418  {
419  cur_addr_aligned += ((RomdirStart->size) + 15) & 0xFFFFFFF0;
420  total_extinfo_size += (s16)RomdirStart->ExtInfoEntrySize;
421  }
422  if ( !*(u32 *)RomdirStart->name )
423  return 0;
424  rdfs->romdirent = RomdirStart;
425  rdfs->extinfo = RomdirStart->ExtInfoEntrySize ?
426  (const struct ExtInfoFieldEntry *)((const char *)rid->RomdirEnd + total_extinfo_size) :
427  0;
428  rdfs->data = &((char *)rid->ImageStart)[cur_addr_aligned];
429  return rdfs;
430 }
431 
432 const struct ExtInfoFieldEntry *do_find_extinfo_entry(const struct RomdirFileStat *rdfs, unsigned int extinfo_type)
433 {
434  const struct ExtInfoFieldEntry *extinfo;
435  const struct ExtInfoFieldEntry *extinfo_end;
436 
437  extinfo = rdfs->extinfo;
438  extinfo_end = &extinfo[(unsigned int)((s16)rdfs->romdirent->ExtInfoEntrySize) >> 2];
439  for ( ; extinfo < extinfo_end;
440  extinfo = (const struct ExtInfoFieldEntry *)((char *)extinfo + ((extinfo->ExtLength) & 0xFC) + 4) )
441  {
442  if ( extinfo->type == extinfo_type )
443  {
444  return extinfo;
445  }
446  }
447  return 0;
448 }
449 
450 #ifdef IGREETING_DTL_T
451 static void flash_reset(const struct rominfo_item *rii)
452 {
453  CpuDisableIntr();
454  switch ( rii->m_write_width )
455  {
456  case 8:
457  *((vu8 *)rii->m_base_address + rii->m_config_offset_1) = 0xF0;
458  break;
459  case 16:
460  *((vu16 *)rii->m_base_address + rii->m_config_offset_1) = 0xF0;
461  break;
462  default:
463  break;
464  }
465  CpuEnableIntr();
466 }
467 
468 static int checksig(const struct rominfo_item *rii, struct romflash_sigcheck_res *rsr, int offset)
469 {
470  u8 *effective_address_1;
471  u16 *effective_address_2;
472  int tmp_manufacturer;
473  USE_IOP_MMIO_HWPORT();
474 
475  // Unofficial: don't write to m_manufacturer
476  tmp_manufacturer = rii->m_manufacturer;
477  switch ( rii->m_write_width )
478  {
479  case 8:
480  effective_address_1 = (u8 *)((u8 *)rii->m_base_address + offset);
481  rsr->m_config_addr_1 = &effective_address_1[rii->m_config_offset_1];
482  rsr->m_config_addr_2 = &effective_address_1[rii->m_config_offset_2];
483  rsr->m_base = effective_address_1;
484  rsr->m_base_end = &effective_address_1[rii->m_chunk_size];
485  *(vu8 *)rsr->m_config_addr_1 = 0xAA;
486  *(vu8 *)rsr->m_config_addr_2 = 0x55;
487  *(vu8 *)rsr->m_config_addr_1 = 0x90;
488  *(vu32 *)&iop_mmio_hwport->exp2_r2[4352] = 0xFFFFFF6F;
489  rsr->m_manufacturer_res = *((vu8 *)rsr->m_base);
490  rsr->m_device_id_res = *((vu8 *)rsr->m_base_end);
491  break;
492  case 16:
493  effective_address_2 = (u16 *)((u8 *)rii->m_base_address + offset);
494  rsr->m_config_addr_1 = &effective_address_2[rii->m_config_offset_1];
495  rsr->m_config_addr_2 = &effective_address_2[rii->m_config_offset_2];
496  rsr->m_base = effective_address_2;
497  rsr->m_base_end = &effective_address_2[rii->m_chunk_size];
498  *(vu16 *)rsr->m_config_addr_1 = 0xAA;
499  *(vu16 *)rsr->m_config_addr_2 = 0x55;
500  *(vu16 *)rsr->m_config_addr_1 = 0x90;
501  *(vu32 *)&iop_mmio_hwport->exp2_r2[4352] = 0xFFFFFF6F;
502  rsr->m_manufacturer_res = *((vu16 *)rsr->m_base);
503  rsr->m_device_id_res = *((vu16 *)rsr->m_base_end);
504  break;
505  default:
506  break;
507  }
508  flash_reset(rii);
509  if ( !tmp_manufacturer && (rsr->m_manufacturer_res == 1 || rsr->m_manufacturer_res == 4) )
510  tmp_manufacturer = rsr->m_manufacturer_res;
511  return (rsr->m_manufacturer_res == tmp_manufacturer) && (rsr->m_device_id_res == rii->m_device_id);
512 }
513 
514 static int flash_checksig(const struct rominfo_item *rii)
515 {
516  int old_address_reg;
517  int old_delay_reg;
518  int condtmp1;
519  int flash_chunks;
520  int cur_flash_chunk;
521  struct romflash_sigcheck_res rsr;
522  USE_IOP_MMIO_HWPORT();
523 
524  memset(&rsr, 0, sizeof(rsr));
525  old_address_reg = 0;
526  old_delay_reg = 0;
527  if ( rii->m_address_register )
528  {
529  old_address_reg = iop_mmio_hwport->ssbus2.ind_1_address;
530  iop_mmio_hwport->ssbus2.ind_1_address = rii->m_address_register;
531  }
532  if ( rii->m_delay_register )
533  {
534  old_delay_reg = *((vu32 *)rii->m_delay_register);
535  *((vu32 *)rii->m_delay_register) = rii->m_delay_register_value;
536  *(vu32 *)&iop_mmio_hwport->exp2_r2[4352] = *(vu32 *)rii->m_delay_register;
537  }
538  flash_chunks = rii->m_flash_size / rii->m_flash_blocksize;
539  for ( cur_flash_chunk = 0;
540  (condtmp1 = checksig(rii, &rsr, flash_chunks * cur_flash_chunk)) && (cur_flash_chunk < rii->m_flash_blocksize);
541  cur_flash_chunk += 1 )
542  {
543  }
544  if ( !condtmp1 )
545  {
546  if ( rii->m_delay_register )
547  {
548  *((vu32 *)rii->m_delay_register) = old_delay_reg;
549  *(vu32 *)&iop_mmio_hwport->exp2_r2[4352] = *(vu32 *)rii->m_delay_register;
550  }
551  if ( rii->m_address_register )
552  iop_mmio_hwport->ssbus2.ind_1_address = old_address_reg;
553  }
554  return condtmp1;
555 }
556 
557 static const struct rominfo_item *flash_probe(const struct rominfo_item *rii)
558 {
559  CpuDisableIntr();
560  for ( ; rii->m_write_width; rii += 1 )
561  {
562  if ( flash_checksig(rii) )
563  {
564  CpuEnableIntr();
565  return rii;
566  }
567  }
568  CpuEnableIntr();
569  return 0;
570 }
571 #endif
RomDirEntry
Definition: romdrv.h:39
CpuDisableIntr
int CpuDisableIntr()
Definition: intrman.c:228
CpuEnableIntr
int CpuEnableIntr()
Definition: intrman.c:240
iop_mmio_hwport.h
ExtInfoFieldEntry
Definition: romdrv.h:54
mipscopaccess.h
count
u32 count
start sector of fragmented bd/file
Definition: usbhdfsd-common.h:3
tamtypes.h
COP0_REG_PRId
@ COP0_REG_PRId
Definition: mipscopaccess.h:42
RomImgData
Definition: igreeting.c:17
RomdirFileStat
Definition: romdrv.c:26