├── .gitignore ├── AGAWide-Background.iff ├── AGAWide-Empty.iff ├── AGS2.conf ├── AGS2.e ├── AGS2Background.iff ├── AGS2Helper.e ├── AGS2Menu.e ├── Empty.iff ├── Makefile ├── Old ├── ArcadeGameSelector.e ├── agsimgloader.e └── chunkbuffer.e ├── README.markdown ├── Startup-Sequence ├── Test ├── lowlevel.e ├── test.e ├── testconf.e ├── testimgloader.e └── testnav.e ├── WB13-Background.iff ├── WB13-Empty.iff ├── agsconf.e ├── agsdefs.e ├── agsil.e ├── agsmenupos.e ├── agsnav.e ├── benchmark.e ├── ilbmloader.e ├── imgtoiff.py ├── palfade.e ├── rgbcolor.e └── sorting_table ├── make_sorting_table.py └── sorting_table /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.m 3 | *.uaem 4 | AGS.tmproj 5 | *.info 6 | *.iff 7 | !*-Background.iff 8 | !*-Empty.iff 9 | !AGS2Background.iff 10 | !Empty.iff 11 | *.ags 12 | *.run 13 | *.zip 14 | [A-Z]*.txt 15 | AGS2 16 | AGS2Menu 17 | AGS2Helper 18 | *.edbg* 19 | Test 20 | spatial_color_quant* 21 | -------------------------------------------------------------------------------- /AGAWide-Background.iff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagerValp/ArcadeGameSelector/c5517182ac6b46659aa44dadecc2b87286e14d52/AGAWide-Background.iff -------------------------------------------------------------------------------- /AGAWide-Empty.iff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagerValp/ArcadeGameSelector/c5517182ac6b46659aa44dadecc2b87286e14d52/AGAWide-Empty.iff -------------------------------------------------------------------------------- /AGS2.conf: -------------------------------------------------------------------------------- 1 | # AGS2 configuration file. 2 | 3 | # Background image and screen mode. 4 | #background = AGS:AGS2Background.iff 5 | #mode = $29000 6 | #depth = 4 7 | #text_color = 255 8 | #text_background = 254 9 | #lock_colors = 4 10 | 11 | # Menu font. 12 | #font = topaz.font 13 | #font_size = 8 14 | #font_leading = 0 15 | 16 | # Position and height of the menu. 17 | #menu_x = 24 18 | #menu_y = 8 19 | #menu_height = 30 20 | #menu_width is fixed at 26 characters 21 | 22 | # Screenshot position. 23 | #screenshot_x = 304 24 | #screenshot_y = 8 25 | # Empty placeholder screenshot. 26 | #empty_screenshot = AGS:Empty.iff 27 | 28 | # Slideshow delay in seconds 29 | # Set to zero to disable the slideshow feature 30 | #slideshow_delay_secs = 3 31 | 32 | # Any image with an index greater than or equal to this value will be displayed 33 | #slideshow_start_index = 1 34 | 35 | # Any image with an index less than or equal to this value will be displayed 36 | #slideshow_end_index = 7 37 | 38 | # Information text position and size. 39 | #text_x = 304 40 | #text_y = 144 41 | #text_width = 40 42 | #text_height = 13 43 | 44 | ### WB13 45 | #background = AGS:WB13-Background.iff 46 | #empty_screenshot = AGS:WB13-Empty.iff 47 | #menu_x = 32 48 | #menu_y = 8 49 | #menu_height = 30 50 | #screenshot_x = 288 51 | #screenshot_y = 8 52 | #text_x = 288 53 | #text_y = 152 54 | #text_width = 40 55 | #text_height = 12 56 | 57 | ### AGA Wide 58 | background = AGS:AGAWide-Background.iff 59 | empty_screenshot = AGS:AGAWide-Empty.iff 60 | lock_colors = 2 61 | menu_x = 0 62 | menu_y = 208 63 | menu_height = 6 64 | screenshot_x = 0 65 | screenshot_y = 0 66 | text_x = 232 67 | text_y = 208 68 | text_width = 49 69 | text_height = 6 70 | blue_button_action = quit 71 | -------------------------------------------------------------------------------- /AGS2.e: -------------------------------------------------------------------------------- 1 | /* AGS Launcher. */ 2 | 3 | 4 | OPT PREPROCESS 5 | 6 | 7 | ->MODULE 'dos/dos' 8 | MODULE '*agsdefs' 9 | 10 | 11 | ENUM ERR_MENU = 1, 12 | ERR_RUN, 13 | ERR_DELETE 14 | 15 | #define AGS_MENU_PATH 'AGS:AGS2Menu' 16 | #define AGS_EXECUTE_COMMAND 'Execute ' + AGS_RUN_PATH 17 | 18 | 19 | PROC main() HANDLE 20 | 21 | LOOP 22 | -> Start AGS2Menu. 23 | IF SystemTagList(AGS_MENU_PATH, [NIL]) = -1 THEN Raise(ERR_MENU) 24 | -> Check if there's a RAM:AGS.run for us, otherwise exit. 25 | IF FileLength(AGS_RUN_PATH) = -1 THEN Raise(0) 26 | -> Execute RAM:AGS.run. 27 | IF SystemTagList(AGS_EXECUTE_COMMAND, [NIL]) = -1 THEN Raise(ERR_RUN) 28 | -> Delete RAM:AGS.run. 29 | IF DeleteFile(AGS_RUN_PATH) = FALSE THEN Raise(ERR_DELETE) 30 | ENDLOOP 31 | 32 | EXCEPT DO 33 | SELECT exception 34 | CASE ERR_MENU 35 | PrintF('Couldn''t execute ' + AGS_MENU_PATH + '\n') 36 | CASE ERR_RUN 37 | PrintF('Couldn''t execute ' + AGS_RUN_PATH + '\n') 38 | CASE ERR_DELETE 39 | PrintF('Couldn''t delete ' + AGS_RUN_PATH + '\n') 40 | DEFAULT 41 | IF exception 42 | IF exception < 10000 43 | PrintF('Unknown exception \d\n', exception) 44 | ELSE 45 | PrintF('Unknown exception "\s"', [exception, 0]) 46 | ENDIF 47 | ENDIF 48 | ENDSELECT 49 | ENDPROC 50 | -------------------------------------------------------------------------------- /AGS2Background.iff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagerValp/ArcadeGameSelector/c5517182ac6b46659aa44dadecc2b87286e14d52/AGS2Background.iff -------------------------------------------------------------------------------- /AGS2Helper.e: -------------------------------------------------------------------------------- 1 | /* Background image loading handler. */ 2 | 3 | 4 | OPT OSVERSION=37 5 | OPT PREPROCESS 6 | 7 | MODULE 'amigalib/lists' 8 | MODULE 'other/ecode' 9 | MODULE 'exec/interrupts' 10 | MODULE 'exec/memory' 11 | MODULE 'exec/ports' 12 | MODULE 'exec/nodes' 13 | MODULE 'exec/lists' 14 | MODULE 'graphics/rastport' 15 | MODULE 'graphics/view' 16 | ->MODULE '*benchmark' 17 | MODULE '*agsil' 18 | MODULE '*ilbmloader' 19 | MODULE '*palfade' 20 | MODULE '*agsconf' 21 | MODULE 'devices/timer' 22 | MODULE 'exec/io' 23 | 24 | 25 | ENUM ERR_NONE, ERR_ECODE, ERR_CREATEPORT, ERR_CREATETIMER 26 | 27 | ENUM LDR_LOADING, LDR_QUITTING 28 | 29 | 30 | CONST PATH_LEN=100 31 | 32 | OBJECT loader 33 | port:PTR TO mp 34 | status:LONG 35 | rport:PTR TO rastport 36 | vport:PTR TO viewport 37 | conf:PTR TO agsconf 38 | max_colors:INT 39 | img_num:LONG 40 | img_path 41 | img_loaded:LONG 42 | ENDOBJECT 43 | 44 | DEF item_changed = 0 45 | 46 | -> This will be called every time a software interrupt is triggered. 47 | -> Does not need to be reentrant and the main task is suspended while it 48 | -> executes. 49 | PROC softint_handler(ldr:PTR TO loader) 50 | DEF msg:PTR TO agsil_msg 51 | DEF action 52 | 53 | msg := GetMsg(ldr.port) 54 | msg.reply := 0 55 | action := msg.action 56 | SELECT action 57 | CASE AGSIL_QUIT 58 | ldr.status := LDR_QUITTING 59 | CASE AGSIL_SETRPORT 60 | ldr.rport := msg.arg 61 | CASE AGSIL_SETVPORT 62 | ldr.vport := msg.arg 63 | CASE AGSIL_SETCONF 64 | ldr.conf := msg.arg 65 | CASE AGSIL_SETMAXCOLORS 66 | ldr.max_colors := msg.arg 67 | CASE AGSIL_LOAD 68 | IF (ldr.rport = NIL) OR (ldr.vport = NIL) 69 | msg.reply := -1 70 | ELSE 71 | ldr.img_num := ldr.img_num + 1 72 | item_changed := 1 73 | msg.reply := ldr.img_num 74 | StrCopy(ldr.img_path, msg.arg) 75 | ENDIF 76 | CASE AGSIL_GETIMGNUM 77 | msg.reply := ldr.img_loaded 78 | DEFAULT 79 | msg.reply := -1 80 | ENDSELECT 81 | ReplyMsg(msg) 82 | ENDPROC 83 | 84 | 85 | PROC main() HANDLE 86 | DEF ldr = NIL:PTR TO loader 87 | DEF softint = NIL:PTR TO is 88 | DEF wrapped_code 89 | DEF port = NIL:PTR TO mp 90 | 91 | ->DEF bmark = NIL:PTR TO benchmark 92 | DEF il = NIL:PTR TO ilbmloader 93 | DEF path[PATH_LEN]:STRING 94 | DEF old_path[PATH_LEN]:STRING 95 | DEF curr_img = 0 96 | 97 | -> Slideshow vars 98 | DEF tr:PTR TO timerequest 99 | DEF timer_msgport:PTR TO mp 100 | DEF timer_sig = 0 101 | DEF timer_activated = 0 102 | DEF slideshow_index = 0 103 | DEF slideshow_range_size = 0 104 | DEF i = 0 105 | DEF indexstring[2]:STRING 106 | DEF have_indexed_image = 0 107 | 108 | -> Allocate the object that we share with the IRQ handler. 109 | ldr := NewM(SIZEOF loader, MEMF_PUBLIC OR MEMF_CLEAR) 110 | ldr.img_path := String(PATH_LEN) 111 | 112 | -> Allocate and configure the soft interrupt structure. 113 | softint := NewM(SIZEOF is, MEMF_PUBLIC OR MEMF_CLEAR) 114 | IF (wrapped_code := eCodeSoftInt({softint_handler})) = NIL THEN Raise(ERR_ECODE) 115 | softint.code := wrapped_code 116 | softint.data := ldr 117 | softint.ln.pri := 0 118 | 119 | -> Create a message port and publish it. Don't use createPort() or 120 | -> CreateMsgPort() since they allocate a signal for a PA_SIGNAL type port. 121 | -> We need PA_SOFTINT. 122 | port := NewM(SIZEOF mp, MEMF_PUBLIC OR MEMF_CLEAR) 123 | ldr.port := port 124 | newList(port.msglist) 125 | port.ln.name := AGSIL_PORTNAME 126 | port.ln.pri := 1 127 | port.ln.type := NT_MSGPORT 128 | port.flags := PA_SOFTINT 129 | port.sigtask := softint 130 | AddPort(port) 131 | 132 | -> Lower priority to run in the background. 133 | SetTaskPri(FindTask(NIL), -1) 134 | 135 | -> ILBMLoader object. 136 | NEW il.init() 137 | 138 | -> Wait until we have received the config from the Menu executable before continuing 139 | REPEAT 140 | Delay(1) 141 | UNTIL ldr.conf <> NIL 142 | 143 | -> Only setup the slideshow if the feature is enabled 144 | IF ldr.conf.slideshow_delay_secs > 0 145 | slideshow_index := ldr.conf.slideshow_start_index 146 | slideshow_range_size := ldr.conf.slideshow_end_index - ldr.conf.slideshow_start_index 147 | 148 | -> Create the slideshow timer 149 | IF timer_msgport := CreateMsgPort() 150 | IF tr := CreateIORequest(timer_msgport, SIZEOF timerequest) 151 | IF OpenDevice('timer.device', UNIT_MICROHZ, tr, 0) = 0 152 | timer_sig := Shl(1, timer_msgport.sigbit) 153 | tr.io.command := TR_ADDREQUEST 154 | ELSE 155 | DeleteIORequest(tr) 156 | Raise(ERR_CREATETIMER) 157 | ENDIF 158 | ELSE 159 | DeleteMsgPort(timer_msgport) 160 | Raise(ERR_CREATETIMER) 161 | ENDIF 162 | ELSE 163 | Raise(ERR_CREATETIMER) 164 | ENDIF 165 | ENDIF 166 | 167 | ->PrintF('AGSImgLoader is waiting for requests on \s, CTRL-C to stop.\n', AGSIL_PORTNAME) 168 | REPEAT 169 | -> Ctrl-C. 170 | IF CtrlC() THEN ldr.status := LDR_QUITTING 171 | 172 | -> Check for slideshow timer event 173 | IF timer_sig 174 | WHILE GetMsg(timer_msgport) = tr 175 | -> Send a new timer request 176 | tr.time.secs := ldr.conf.slideshow_delay_secs 177 | SendIO(tr) 178 | 179 | ldr.img_num := ldr.img_num + 1 180 | ENDWHILE 181 | ENDIF 182 | 183 | IF curr_img <> ldr.img_num 184 | Disable() 185 | curr_img := ldr.img_num 186 | have_indexed_image := 0 187 | 188 | -> Only look for indexed screenshots if slideshow is enabled 189 | IF ldr.conf.slideshow_delay_secs > 0 190 | -> If the list item has changed, then we 191 | -> must reset the start index and timer 192 | IF item_changed = 1 193 | slideshow_index := ldr.conf.slideshow_start_index 194 | 195 | -> If the timer has been activated at least 196 | -> once, then cancel any pending request 197 | IF timer_activated = 1 198 | AbortIO(tr) -> end the last timer request 199 | WaitIO(tr) -> wait for it to end 200 | ENDIF 201 | 202 | -> Send a new timer request 203 | tr.time.secs := ldr.conf.slideshow_delay_secs 204 | SendIO(tr) 205 | 206 | timer_activated := 1 207 | item_changed := 0 208 | ENDIF 209 | 210 | -> Try to find an indexed screenshot 211 | FOR i := 0 TO slideshow_range_size 212 | StrCopy(path, ldr.img_path) 213 | StringF(indexstring, '-\d', slideshow_index) 214 | StrAdd(path, indexstring) 215 | StrAdd(path, '.iff') 216 | 217 | slideshow_index := slideshow_index + 1 218 | 219 | -> Make sure we honour the end index configuration 220 | IF slideshow_index = (ldr.conf.slideshow_end_index + 1) 221 | slideshow_index := ldr.conf.slideshow_start_index 222 | ENDIF 223 | 224 | -> Check if file exists 225 | IF FileLength(path) <> -1 226 | have_indexed_image := 1 227 | ENDIF 228 | 229 | -> Exit the for loop if we've found an image 230 | EXIT have_indexed_image = 1 231 | ENDFOR 232 | ENDIF 233 | 234 | -> If we haven't got an indexed image, fallback to standard file naming 235 | IF have_indexed_image = 0 236 | StrCopy(path, ldr.img_path) 237 | StrAdd(path, '.iff') 238 | 239 | IF FileLength(path) = -1 240 | -> Still didn't find an image file with standard 241 | -> naming, so just show the empty screenshot 242 | StrCopy(path, ldr.conf.empty_screenshot) 243 | ENDIF 244 | ENDIF 245 | Enable() 246 | 247 | IF StrCmp(path, old_path) 248 | ldr.img_loaded := curr_img 249 | ELSE 250 | IF il.open(path) 251 | ->NEW bmark.init(10) 252 | ->bmark.start() 253 | IF il.parse_header() = FALSE 254 | PrintF('\s failed header parsing\n', path) 255 | ELSE 256 | fade_out_vport(ldr.vport, ldr.max_colors, 5) 257 | 258 | -> Erase the area behind any last image displayed 259 | SetAPen(ldr.rport, 0) 260 | RectFill(ldr.rport, 261 | ldr.conf.screenshot_x, 262 | ldr.conf.screenshot_y, 263 | ldr.conf.screenshot_x + il.last_width - 1, 264 | ldr.conf.screenshot_y + il.last_height - 1) 265 | 266 | ->il.load_cmap(ldr.vport, ldr.max_colors) 267 | ->bmark.mark() -> 0 268 | il.load_body(ldr.rport, ldr.conf.screenshot_x, ldr.conf.screenshot_y) 269 | ->bmark.mark() -> 1 270 | fade_in_vport(il.colormap, ldr.vport, ldr.max_colors, 5) 271 | ->PrintF('\s loaded in \d ms\n', path, bmark.msecs(1)) 272 | ENDIF 273 | ldr.img_loaded := curr_img 274 | il.close() 275 | ->END bmark 276 | StrCopy(old_path, path) 277 | ELSE 278 | PrintF('\s not found\n', path) 279 | ENDIF 280 | ENDIF 281 | ELSE 282 | Delay(1) 283 | ENDIF 284 | 285 | UNTIL ldr.status = LDR_QUITTING 286 | 287 | IF ldr.conf.slideshow_delay_secs > 0 288 | -> Cancel any pending timer request 289 | IF timer_activated = 1 290 | AbortIO(tr) -> end the last timer request 291 | WaitIO(tr) -> wait for it to end 292 | ENDIF 293 | 294 | -> Now close the device and delete the IO request and message ports 295 | CloseDevice(tr) 296 | DeleteIORequest(tr) 297 | DeleteMsgPort(timer_msgport) 298 | ENDIF 299 | 300 | EXCEPT DO 301 | END il 302 | IF port 303 | IF port.ln.name THEN RemPort(port) 304 | port.sigtask := -1 305 | port.msglist.head := -1 306 | Dispose(port) 307 | ENDIF 308 | IF softint THEN Dispose(softint) 309 | IF ldr.img_path THEN DisposeLink(ldr.img_path) 310 | IF ldr THEN Dispose(ldr) 311 | SELECT exception 312 | CASE "MEM" 313 | PrintF('Out of memory\n') 314 | CASE ERR_ECODE 315 | PrintF('eCode() failed\n') 316 | CASE ERR_CREATEPORT 317 | PrintF('Couldn''t create "' + AGSIL_PORTNAME + '"\n') 318 | CASE ILBM_ERROR 319 | PrintF('\s\n', ilbm_strerror(exceptioninfo)) 320 | CASE ERR_CREATETIMER 321 | PrintF('Failed to create slideshow timer\n') 322 | DEFAULT 323 | IF exception 324 | IF exception < 10000 325 | PrintF('Unknown exception \d\n', exception) 326 | ELSE 327 | PrintF('Unknown exception "\s"', [exception, 0]) 328 | ENDIF 329 | ENDIF 330 | ENDSELECT 331 | ENDPROC 332 | -------------------------------------------------------------------------------- /AGS2Menu.e: -------------------------------------------------------------------------------- 1 | /* Arcade Game Selection version 2. */ 2 | 3 | 4 | OPT PREPROCESS 5 | 6 | 7 | MODULE 'dos/dos' 8 | MODULE 'exec/memory' 9 | MODULE 'intuition/intuition' 10 | MODULE 'intuition/screens' 11 | MODULE 'graphics/modeid' 12 | MODULE 'graphics/gfx' 13 | MODULE 'graphics/rastport' 14 | MODULE 'graphics/text' 15 | MODULE 'graphics/view' 16 | MODULE 'graphics/videocontrol' 17 | MODULE 'lowlevel' 18 | MODULE 'libraries/lowlevel' 19 | MODULE '*ilbmloader' 20 | MODULE '*agsil' 21 | MODULE '*agsnav' 22 | MODULE '*agsconf' 23 | MODULE '*agsdefs' 24 | MODULE '*agsmenupos' 25 | MODULE '*palfade' 26 | 27 | 28 | ENUM ERR_KICKSTART = 1, 29 | ERR_JOYSTICK, 30 | ERR_SCREEN, 31 | ERR_WINDOW, 32 | ERR_FONT, 33 | ERR_BACKGROUND, 34 | ERR_EMPTY, 35 | ERR_COPY_SRC, 36 | ERR_COPY_DST, 37 | ERR_COPY_WRITE 38 | 39 | 40 | OBJECT ags 41 | conf:PTR TO agsconf 42 | nav:PTR TO agsnav 43 | loader:PTR TO agsil_master 44 | rport:PTR TO rastport 45 | font:PTR TO textfont 46 | menu_rastport:PTR TO rastport 47 | menu_bitmap:PTR TO bitmap 48 | menu_bm_width:INT 49 | menu_bm_height:INT 50 | current_item:INT 51 | height:INT 52 | width:INT 53 | offset:INT 54 | char_width:INT 55 | ENDOBJECT 56 | 57 | PROC init(conf, nav, loader, rport, font) OF ags 58 | self.conf := conf 59 | self.nav := nav 60 | self.loader := loader 61 | self.rport := rport 62 | self.font := font 63 | ->self.height := 0 64 | self.width := 26 65 | ->self.offset := 0 66 | self.char_width := TextLength(rport, 'A', 1) 67 | ENDPROC 68 | 69 | PROC end() OF ags 70 | self.discard_menu_bitmap() 71 | ENDPROC 72 | 73 | CONST COPY_BUF_SIZE = 128 74 | 75 | PROC copy_file(src_path:PTR TO CHAR, dst_path:PTR TO CHAR) HANDLE 76 | DEF len 77 | DEF src_fh = NIL 78 | DEF dst_fh = NIL 79 | DEF buf[COPY_BUF_SIZE]:ARRAY OF CHAR 80 | 81 | IF (src_fh := Open(src_path, MODE_OLDFILE)) = NIL THEN Raise(ERR_COPY_SRC) 82 | IF (dst_fh := Open(dst_path, MODE_NEWFILE)) = NIL THEN Raise(ERR_COPY_DST) 83 | WHILE (len := Read(src_fh, buf, COPY_BUF_SIZE)) > 0 84 | IF Write(dst_fh, buf, len) <> len THEN Raise(ERR_COPY_WRITE) 85 | ENDWHILE 86 | EXCEPT DO 87 | IF src_fh THEN Close(src_fh) 88 | IF dst_fh THEN Close(dst_fh) 89 | IF exception 90 | PrintF('Copying \s to \s: ') 91 | ENDIF 92 | ReThrow() 93 | ENDPROC 94 | 95 | 96 | CONST REPEAT_DELAY = 8 97 | CONST RAWKEY_Q = 16 98 | CONST RAWKEY_ESC = 69 99 | CONST RAWKEY_UP = 76 100 | CONST RAWKEY_DOWN = 77 101 | CONST RAWKEY_RIGHT = 78 102 | CONST RAWKEY_LEFT = 79 103 | CONST RAWKEY_RETURN = 68 104 | 105 | PROC select(init_offset, init_pos) OF ags 106 | DEF key 107 | DEF rawkey 108 | DEF quit = FALSE 109 | DEF portstate 110 | -> Counters to delay repeat and screenshot loading. 111 | DEF up_ctr = 0 112 | DEF down_ctr = 0 113 | DEF right_ctr = 0 114 | DEF left_ctr = 0 115 | DEF screenshot_ctr = 0 116 | 117 | DEF item:PTR TO agsnav_item 118 | DEF index 119 | DEF path[255]:STRING 120 | DEF prev_item[255]:STRING 121 | DEF pos, len 122 | DEF should_redraw = 0 123 | DEF menu_pos = NIL:PTR TO agsmenupos 124 | 125 | self.reload(init_offset, init_pos) 126 | 127 | IF (portstate := ReadJoyPort(1)) = JP_TYPE_NOTAVAIL THEN Raise(ERR_JOYSTICK) 128 | 129 | REPEAT 130 | WaitTOF() 131 | 132 | portstate := ReadJoyPort(1) 133 | 134 | key := GetKey() 135 | rawkey := key AND $ffff 136 | 137 | IF ((rawkey = RAWKEY_Q) AND (key AND LLKB_RAMIGA)) OR (rawkey = RAWKEY_ESC) 138 | quit := TRUE 139 | ENDIF 140 | IF (portstate AND JP_TYPE_MASK) = JP_TYPE_GAMECTLR 141 | IF portstate AND JPF_BUTTON_BLUE 142 | IF self.conf.blue_button_action = AGSCONF_ACTION_QUIT 143 | quit := TRUE 144 | ENDIF 145 | ENDIF 146 | ENDIF 147 | 148 | -> Up / Down 149 | IF (portstate AND JPF_JOY_UP) OR (rawkey = RAWKEY_UP) 150 | IF (up_ctr = 0) OR (up_ctr > REPEAT_DELAY) 151 | IF self.current_item > 0 152 | self.current_item := self.current_item - 1 153 | self.redraw(self.current_item, self.current_item + 1) 154 | screenshot_ctr := 0 155 | ELSEIF self.offset > 0 156 | self.offset := self.offset - 1 157 | self.redraw() 158 | screenshot_ctr := 0 159 | ENDIF 160 | ENDIF 161 | INC up_ctr 162 | ELSE 163 | up_ctr := 0 164 | ENDIF 165 | 166 | IF (portstate AND JPF_JOY_DOWN) OR (rawkey = RAWKEY_DOWN) 167 | IF (down_ctr = 0) OR (down_ctr > REPEAT_DELAY) 168 | IF self.current_item < (self.height - 1) 169 | self.current_item := self.current_item + 1 170 | self.redraw(self.current_item - 1, self.current_item) 171 | screenshot_ctr := 0 172 | ELSEIF (self.current_item + self.offset) < (self.nav.num_items - 1) 173 | self.offset := self.offset + 1 174 | self.redraw() 175 | screenshot_ctr := 0 176 | ENDIF 177 | ENDIF 178 | INC down_ctr 179 | ELSE 180 | down_ctr := 0 181 | ENDIF 182 | 183 | -> Page Up / Down 184 | IF (portstate AND JPF_JOY_RIGHT) OR (rawkey = RAWKEY_RIGHT) 185 | IF (right_ctr = 0) OR (right_ctr > REPEAT_DELAY) 186 | IF (self.nav.num_items < self.height) 187 | IF (self.current_item < self.height) 188 | self.current_item := self.nav.num_items 189 | should_redraw := 1 190 | ENDIF 191 | ELSE 192 | IF (self.current_item < (self.height - 1)) 193 | self.current_item := self.height - 1 194 | should_redraw := 1 195 | ELSE 196 | IF (self.offset < (self.nav.num_items - self.height)) 197 | self.offset := Min(self.nav.num_items - self.height, self.offset + self.height - 1) 198 | should_redraw := 1 199 | ENDIF 200 | ENDIF 201 | ENDIF 202 | ENDIF 203 | INC right_ctr 204 | ELSE 205 | right_ctr := 0 206 | ENDIF 207 | 208 | IF (portstate AND JPF_JOY_LEFT) OR (rawkey = RAWKEY_LEFT) 209 | IF (left_ctr = 0) OR (left_ctr > REPEAT_DELAY) 210 | IF (self.nav.num_items < self.height) 211 | IF (self.current_item > 0) 212 | self.current_item := 0 213 | should_redraw := 1 214 | ENDIF 215 | ELSE 216 | IF (self.current_item > 0) 217 | self.current_item := 0 218 | should_redraw := 1 219 | ELSE 220 | IF (self.offset > 0) 221 | self.offset := Max(0, self.offset - self.height + 1) 222 | should_redraw := 1 223 | ENDIF 224 | ENDIF 225 | ENDIF 226 | ENDIF 227 | INC left_ctr 228 | ELSE 229 | left_ctr := 0 230 | ENDIF 231 | 232 | IF (should_redraw <> 0) 233 | self.redraw() 234 | screenshot_ctr := 0 235 | should_redraw := 0 236 | ENDIF 237 | 238 | IF (portstate AND JPF_BUTTON_RED) OR (rawkey = RAWKEY_RETURN) 239 | index := self.current_item + self.offset 240 | item := self.nav.items[index] 241 | IF item.type = AGSNAV_TYPE_DIR 242 | IF self.nav.depth AND (index = 0) -> Go to parent dir. 243 | StrCopy(path, self.nav.path) 244 | pos := EstrLen(path) - 1 245 | len := 0 246 | REPEAT 247 | DEC pos 248 | INC len 249 | UNTIL (path[pos] = "/") OR (path[pos] = ":") 250 | INC pos 251 | RightStr(prev_item, path, len) 252 | SetStr(prev_item, len - 5) 253 | path[pos] := 0 254 | SetStr(path, pos) 255 | self.nav.set_path(path) 256 | self.nav.depth := self.nav.depth - 1 257 | self.reload(0, 0, prev_item) 258 | screenshot_ctr := 0 259 | ELSEIF self.nav.depth < 6 260 | StrCopy(path, self.nav.path) 261 | StrAdd(path, item.name) 262 | StrAdd(path, '.ags/') 263 | self.nav.set_path(path) 264 | self.nav.depth := self.nav.depth + 1 265 | self.reload() 266 | screenshot_ctr := 0 267 | ENDIF 268 | ELSE 269 | -> Serialize navigation state and copy run script 270 | NEW menu_pos.init() 271 | menu_pos.write(self.nav.path, self.nav.depth, self.offset, self.current_item) 272 | END menu_pos 273 | 274 | StrCopy(path, self.nav.path) 275 | StrAdd(path, item.name) 276 | StrAdd(path, '.run') 277 | copy_file(path, AGS_RUN_PATH) 278 | screenshot_ctr := (REPEAT_DELAY + 2) -> Avoid loading now. 279 | quit := TRUE 280 | ENDIF 281 | REPEAT 282 | portstate := ReadJoyPort(1) 283 | rawkey := GetKey() AND $ffff 284 | UNTIL ((portstate AND JPF_BUTTON_RED) = 0) AND (rawkey <> RAWKEY_RETURN) 285 | ENDIF 286 | 287 | IF screenshot_ctr++ = (REPEAT_DELAY + 1) 288 | self.load_screenshot() 289 | self.load_text() 290 | ENDIF 291 | 292 | UNTIL quit 293 | 294 | ENDPROC 295 | 296 | PROC reload(offset = 0, pos = 0, select_item = NIL) OF ags 297 | DEF line 298 | DEF item:PTR TO agsnav_item 299 | 300 | IF self.nav.read_dir() = 0 THEN Raise(ERR_EMPTY) 301 | self.height := Min(self.nav.num_items, self.conf.menu_height) 302 | 303 | IF select_item 304 | FOR line := 0 TO self.nav.num_items - 1 305 | item := self.nav.items[line] 306 | IF StrCmp(select_item, item.name) THEN JUMP break 307 | ENDFOR 308 | line := 0 309 | break: 310 | WHILE line >= self.height 311 | DEC line 312 | INC offset 313 | ENDWHILE 314 | pos := line 315 | ENDIF 316 | 317 | self.offset := offset 318 | self.current_item := pos 319 | self.discard_menu_bitmap() 320 | self.create_menu_bitmap() 321 | self.redraw() 322 | ENDPROC 323 | 324 | PROC discard_menu_bitmap() OF ags 325 | DEF bm:PTR TO bitmap 326 | DEF rp:PTR TO rastport 327 | 328 | IF self.menu_bitmap <> NIL 329 | IF self.menu_bitmap.planes[0] <> NIL 330 | FreeRaster(self.menu_bitmap.planes[0], self.menu_bm_width, self.menu_bm_height) 331 | ENDIF 332 | bm := self.menu_bitmap 333 | END bm 334 | self.menu_bitmap := NIL 335 | ENDIF 336 | IF self.menu_rastport <> NIL 337 | rp := self.menu_rastport 338 | END rp 339 | self.menu_rastport := NIL 340 | ENDIF 341 | ENDPROC 342 | 343 | PROC create_menu_bitmap() OF ags HANDLE 344 | DEF bm = NIL:PTR TO bitmap 345 | DEF rp = NIL:PTR TO rastport 346 | DEF line 347 | DEF item:PTR TO agsnav_item 348 | DEF y 349 | DEF i 350 | 351 | -> Allocate a single bitplane bitmap for the menu text. 352 | self.menu_bm_width := (self.width + 2) * self.char_width 353 | self.menu_bm_height := self.nav.num_items * (self.font.ysize + self.conf.font_leading) 354 | 355 | NEW bm 356 | InitBitMap(bm, 1, self.menu_bm_width, self.menu_bm_height) 357 | bm.planes[0] := AllocRaster(self.menu_bm_width, self.menu_bm_height) 358 | IF bm.planes[0] = NIL THEN Raise("MEM") 359 | self.menu_bitmap := bm 360 | 361 | NEW rp 362 | InitRastPort(rp) 363 | rp.bitmap := bm 364 | self.menu_rastport := rp 365 | SetRast(self.menu_rastport, 0) 366 | SetFont(self.menu_rastport, self.font) 367 | 368 | ->SetABPenDrMd(self.menu_rastport, 1, 0, RP_JAM2) 369 | FOR line := 0 TO self.nav.num_items - 1 370 | item := self.nav.items[line] 371 | y := line * (self.font.ysize + self.conf.font_leading) 372 | Move(self.menu_rastport, self.menu_rastport.font.xsize, y + self.menu_rastport.font.baseline) 373 | Text(self.menu_rastport, item.name, item.length) 374 | ENDFOR 375 | EXCEPT 376 | IF bm.planes[i] <> NIL THEN FreeRaster(bm.planes[i], self.menu_bm_width, self.menu_bm_height) 377 | END bm 378 | END rp 379 | ReThrow() 380 | ENDPROC 381 | 382 | PROC redraw(start=0, end=-1) OF ags 383 | DEF src_y 384 | DEF dest_y 385 | DEF height 386 | 387 | IF end = -1 388 | IF self.nav.num_items < self.conf.menu_height 389 | SetAPen(self.rport, self.conf.text_background) 390 | RectFill(self.rport, 391 | self.conf.menu_x, 392 | self.conf.menu_y + ((self.font.ysize + self.conf.font_leading) * self.nav.num_items), 393 | self.conf.menu_x + self.menu_bm_width - 1, 394 | self.conf.menu_y + ((self.font.ysize + self.conf.font_leading) * self.conf.menu_height) - 1) 395 | ENDIF 396 | end := Min(self.conf.menu_height, self.nav.num_items) - 1 397 | ENDIF 398 | 399 | src_y := (self.offset + start) * (self.font.ysize + self.conf.font_leading) 400 | dest_y := start * (self.font.ysize + self.conf.font_leading) 401 | height := (Min(end - start, self.nav.num_items) + 1) * (self.font.ysize + self.conf.font_leading) 402 | BltBitMapRastPort(self.menu_bitmap, 403 | 0, 404 | src_y, 405 | self.rport, 406 | self.conf.menu_x, 407 | self.conf.menu_y + dest_y, 408 | self.menu_bm_width, 409 | height, 410 | $0c0) 411 | BltBitMapRastPort(self.menu_bitmap, 412 | 0, 413 | 0, 414 | self.rport, 415 | self.conf.menu_x, 416 | self.conf.menu_y + (self.current_item * (self.font.ysize + self.conf.font_leading)), 417 | self.menu_bm_width, 418 | self.font.ysize, 419 | $050) 420 | ENDPROC 421 | 422 | PROC get_item_path(path:LONG, suffix:PTR TO CHAR) OF ags 423 | DEF item:PTR TO agsnav_item 424 | 425 | item := self.nav.items[self.current_item + self.offset] 426 | StrCopy(path, self.nav.path) 427 | StrAdd(path, item.name) 428 | StrAdd(path, suffix) 429 | ENDPROC 430 | 431 | PROC load_screenshot() OF ags 432 | DEF path[100]:STRING 433 | 434 | self.get_item_path(path, '') 435 | self.loader.send_cmd(AGSIL_LOAD, path) 436 | ENDPROC 437 | 438 | PROC load_text() OF ags HANDLE 439 | DEF path[100]:STRING 440 | DEF len 441 | DEF line = NIL 442 | DEF bufsize = 0 443 | DEF adjust_read 444 | DEF fh = NIL 445 | DEF linenum = 0 446 | DEF y 447 | 448 | IF self.conf.text_height = 0 THEN Raise(0) 449 | 450 | SetAPen(self.rport, self.conf.text_background) 451 | RectFill(self.rport, 452 | self.conf.text_x, 453 | self.conf.text_y, 454 | self.conf.text_x + (self.conf.text_width * self.font.xsize) - 1, 455 | self.conf.text_y + (self.conf.text_height * (self.font.ysize + self.conf.font_leading)) - 1) 456 | 457 | self.get_item_path(path, '.txt') 458 | IF FileLength(path) = -1 THEN Raise(0) 459 | 460 | bufsize := self.conf.text_width + 2 461 | line := String(bufsize) 462 | -> Work around Fgets() bug in V36/V37. 463 | IF KickVersion(39) THEN adjust_read := 0 ELSE adjust_read := 1 464 | 465 | SetAPen(self.rport, self.conf.text_color) 466 | SetBPen(self.rport, self.conf.text_background) 467 | SetDrMd(self.rport, RP_JAM2) 468 | IF (fh := Open(path, OLDFILE)) = NIL THEN Raise(0) 469 | WHILE (linenum < self.conf.text_height) AND Fgets(fh, line, bufsize - adjust_read) 470 | len := StrLen(line) 471 | -> Trim trailing newline. 472 | IF len > 0 473 | IF (line[len - 1] = "\n") 474 | DEC len 475 | line[len] := 0 476 | ENDIF 477 | ENDIF 478 | IF len > self.conf.text_width THEN DEC len 479 | -> Fix estring length. 480 | SetStr(line, len) 481 | 482 | IF len > 0 483 | y := self.conf.text_y + 484 | self.font.baseline + 485 | ((self.font.ysize + self.conf.font_leading) * linenum) 486 | Move(self.rport, self.conf.text_x, y) 487 | Text(self.rport, line, len) 488 | ENDIF 489 | 490 | INC linenum 491 | ENDWHILE 492 | 493 | EXCEPT DO 494 | IF line THEN DisposeLink(line) 495 | IF fh THEN Close(fh) 496 | ENDPROC 497 | 498 | 499 | PROC main() HANDLE 500 | DEF conf = NIL:PTR TO agsconf 501 | DEF il = NIL:PTR TO ilbmloader 502 | DEF s = NIL:PTR TO screen 503 | DEF w = NIL:PTR TO window 504 | DEF pointer = NIL:PTR TO INT 505 | DEF s_width = 640 506 | DEF s_height = 256 507 | DEF s_depth = 4 508 | DEF s_mode 509 | DEF ta:textattr 510 | DEF font = NIL:PTR TO textfont 511 | DEF loader = NIL:PTR TO agsil_master -> Background image loader master object. 512 | DEF reply 513 | DEF nav = NIL:PTR TO agsnav -> Menu directory navigator. 514 | DEF ags = NIL:PTR TO ags -> Application controller. 515 | DEF menu_pos = NIL:PTR TO agsmenupos 516 | 517 | IF KickVersion(37) = FALSE THEN Raise(ERR_KICKSTART) 518 | 519 | IF (lowlevelbase := OpenLibrary('lowlevel.library', 0)) = NIL THEN Raise("LOWL") 520 | 521 | NEW conf.init() 522 | conf.read('AGS:AGS2.conf') 523 | 524 | IF SetJoyPortAttrsA(1, [SJA_TYPE, SJA_TYPE_JOYSTK, 0]) = FALSE 525 | Raise(ERR_JOYSTICK) 526 | ENDIF 527 | 528 | NEW loader.init() 529 | loader.start() 530 | 531 | NEW il.init() 532 | IF il.open(conf.background) = FALSE THEN Raise(ERR_BACKGROUND) 533 | IF il.parse_header() = FALSE THEN Raise(ERR_BACKGROUND) 534 | IF conf.mode = AGSCONF_AUTODETECT 535 | s_mode := IF il.mode THEN il.mode ELSE (PAL_MONITOR_ID OR HIRES_KEY) 536 | ELSE 537 | s_mode := conf.mode 538 | ENDIF 539 | s_width := il.width 540 | s_height := il.height 541 | IF conf.depth = AGSCONF_AUTODETECT 542 | s_depth := il.depth 543 | ELSE 544 | s_depth := conf.depth 545 | ENDIF 546 | 547 | IF (s := OpenScreenTagList(NIL, [ 548 | SA_WIDTH, s_width, 549 | SA_HEIGHT, s_height, 550 | SA_DEPTH, s_depth, 551 | SA_DISPLAYID, s_mode, 552 | SA_DRAGGABLE, FALSE, 553 | SA_SHOWTITLE, FALSE, 554 | SA_BEHIND, TRUE, 555 | 0])) = NIL THEN Raise(ERR_SCREEN) 556 | IF (w := OpenWindowTagList(NIL, [ 557 | WA_CUSTOMSCREEN, s, 558 | WA_WIDTH, s_width, 559 | WA_HEIGHT, s_height, 560 | WA_TITLE, 0, 561 | WA_CLOSEGADGET, FALSE, 562 | WA_BORDERLESS, TRUE, 563 | WA_RMBTRAP, TRUE, 564 | WA_ACTIVATE, TRUE, 565 | 0])) = NIL THEN Raise(ERR_WINDOW) 566 | 567 | pointer := NewM(4, MEMF_CHIP OR MEMF_CLEAR) 568 | SetPointer(w, pointer, 1, 1, 0, 0) 569 | 570 | ta.name := conf.font_name 571 | ta.ysize := conf.font_size 572 | ta.style := 0 573 | ta.flags := 0 574 | IF (font := OpenFont(ta)) = NIL THEN Raise(ERR_FONT) 575 | SetFont(w.rport, font) 576 | 577 | fade_out_vport(s.viewport, Shl(1, s_depth), 1) -> Clear palette to black. 578 | VideoControl(s.viewport.colormap, [VTAG_BORDERBLANK_SET, 0, 0]) 579 | il.load_body(w.rport, 0, 0) 580 | ScreenToFront(s) 581 | fade_in_vport(il.colormap, s.viewport, Shl(1, s_depth), 10) 582 | il.close() 583 | END il 584 | 585 | loader.wait_port() 586 | reply := loader.send_cmd(AGSIL_SETRPORT, w.rport) 587 | reply := loader.send_cmd(AGSIL_SETVPORT, s.viewport) 588 | reply := loader.send_cmd(AGSIL_SETMAXCOLORS, Shl(1, s_depth) - conf.lock_colors) 589 | reply := loader.send_cmd(AGSIL_SETCONF, conf) 590 | /* We loaded the background image directly using ilbmloader instead. 591 | reply := loader.send_cmd(AGSIL_SETXY, 0) 592 | reply := loader.send_cmd(AGSIL_LOAD, bkg_img) 593 | IF reply < 0 THEN Raise(ERR_BACKGROUND) 594 | loader.wait_load(reply) 595 | */ 596 | 597 | NEW menu_pos.init() 598 | menu_pos.read() 599 | 600 | NEW nav.init() 601 | nav.set_path(menu_pos.path) 602 | nav.depth := menu_pos.depth 603 | 604 | NEW ags.init(conf, nav, loader, w.rport, font) 605 | ags.select(menu_pos.offset, menu_pos.pos) 606 | fade_out_vport(s.viewport, Shl(1, s_depth), 10) 607 | 608 | loader.stop() 609 | 610 | EXCEPT DO 611 | END ags 612 | END nav 613 | END loader 614 | END il 615 | IF font THEN CloseFont(font) 616 | IF pointer THEN Dispose(pointer) 617 | IF w THEN CloseWindow(w) 618 | IF s THEN CloseScreen(s) 619 | SetJoyPortAttrsA(1, [SJA_REINITIALIZE, 0, 0]) 620 | END conf 621 | END menu_pos 622 | IF lowlevelbase THEN CloseLibrary(lowlevelbase) 623 | SELECT exception 624 | CASE "MEM" 625 | PrintF('Out of memory.\n') 626 | CASE AGSIL_ERROR 627 | PrintF('\s.\n', agsil_strerror(exceptioninfo)) 628 | CASE AGSNAV_ERROR 629 | PrintF('\s.\n', agsnav_strerror(exceptioninfo)) 630 | CASE AGSCONF_ERROR 631 | PrintF('\s.\n', agsconf_strerror(exceptioninfo)) 632 | CASE ILBM_ERROR 633 | PrintF('\s\n', ilbm_strerror(exceptioninfo)) 634 | CASE ERR_KICKSTART 635 | PrintF('Requires Kickstart 2.0+.\n') 636 | CASE ERR_JOYSTICK 637 | PrintF('Couldn''t read joystick.\n') 638 | CASE ERR_SCREEN 639 | PrintF('Couldn''t open screen.\n') 640 | CASE ERR_WINDOW 641 | PrintF('Couldn''t open window.\n') 642 | CASE ERR_FONT 643 | PrintF('Couldn''t open font.\n') 644 | CASE ERR_BACKGROUND 645 | PrintF('Couldn''t load background image.\n') 646 | CASE ERR_EMPTY 647 | PrintF('Menu is empty, nothing to select.\n') 648 | CASE ERR_COPY_SRC 649 | PrintF('Error opening source.\n') 650 | CASE ERR_COPY_DST 651 | PrintF('Error opening destination.\n') 652 | CASE ERR_COPY_WRITE 653 | PrintF('Error writing run script.\n') 654 | DEFAULT 655 | IF exception 656 | IF exception < 10000 657 | PrintF('Unknown exception \d\n', exception) 658 | ELSE 659 | PrintF('Unknown exception "\s"', [exception, 0]) 660 | ENDIF 661 | ENDIF 662 | ENDSELECT 663 | ENDPROC 664 | -------------------------------------------------------------------------------- /Empty.iff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagerValp/ArcadeGameSelector/c5517182ac6b46659aa44dadecc2b87286e14d52/Empty.iff -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #ECFLAGS=QUIET ERRLINE DEBUG LINEDEBUG 2 | ECFLAGS=QUIET ERRLINE 3 | EC=ec 4 | 5 | 6 | %.m : %.e 7 | @Delete $@ QUIET >NIL: 8 | $(EC) $< $(ECFLAGS) 9 | 10 | % : %.e 11 | @Delete $@ QUIET >NIL: 12 | $(EC) $< $(ECFLAGS) 13 | 14 | 15 | all: AGS2 AGS2Menu AGS2Helper 16 | 17 | 18 | AGS2: AGS2.e agsdefs.m 19 | 20 | AGS2Menu: AGS2Menu.e agsil.m agsnav.m agsconf.m rgbcolor.m ilbmloader.m palfade.m agsdefs.m agsmenupos.m 21 | 22 | AGS2Helper: AGS2Helper.e agsil.m rgbcolor.m ilbmloader.m palfade.m benchmark.m 23 | 24 | 25 | .PHONY: clean 26 | clean: 27 | Delete AGS2 QUIET >NIL: 28 | Delete AGS2Menu QUIET >NIL: 29 | Delete AGS2Helper QUIET >NIL: 30 | Delete agsdefs.m QUIET >NIL: 31 | Delete agsil.m QUIET >NIL: 32 | Delete agsnav.m QUIET >NIL: 33 | Delete agsmenupos.m QUIET >NIL: 34 | Delete agsconf.m QUIET >NIL: 35 | Delete ilbmloader.m QUIET >NIL: 36 | Delete benchmark.m QUIET >NIL: 37 | -------------------------------------------------------------------------------- /Old/ArcadeGameSelector.e: -------------------------------------------------------------------------------- 1 | OPT PREPROCESS,REG=3 2 | 3 | MODULE 'intuition/screens', 'intuition/intuition', 'graphics/modeid', 4 | 'dos/dos', 'graphics/rastport', 'graphics/text', 'lowlevel', 5 | 'libraries/lowlevel', 'tools/ilbm', 'tools/ilbmdefs', 'exec/memory', 6 | 'graphics/gfx' 7 | 8 | DEF tt[256]:ARRAY OF CHAR,ct[256]:ARRAY OF CHAR 9 | 10 | OBJECT chooser 11 | active 12 | nchoices 13 | offset 14 | height 15 | choice:PTR TO CHAR 16 | ENDOBJECT 17 | 18 | PROC clear() OF chooser 19 | DEF i 20 | 21 | self.choice:=AllocMem(65536,MEMF_CLEAR) 22 | IF self.choice=NIL THEN Raise("MEM") 23 | FOR i:=0 TO 65535 24 | self.choice[i]:=0 25 | ENDFOR 26 | self.nchoices:=0 27 | self.offset:=0 28 | self.height:=0 29 | self.active:=0 30 | ENDPROC 31 | 32 | PROC end() OF chooser 33 | IF self.choice THEN FreeMem(self.choice,65536) 34 | ENDPROC 35 | 36 | PROC sort() OF chooser 37 | DEF i,j,a:PTR TO CHAR,key[32]:ARRAY OF CHAR 38 | 39 | a:=self.choice 40 | IF self.nchoices>1 41 | FOR j:=1 TO self.nchoices-1 42 | CopyMem(a+(j*32),key,32) 43 | i:=j-1 44 | WHILE (i>=0) AND (compare(a+(i*32),key)>0) 45 | CopyMem(a+(i*32),a+((i+1)*32),32) 46 | i-- 47 | ENDWHILE 48 | CopyMem(key,a+((i+1)*32),32) 49 | ENDFOR 50 | ENDIF 51 | ENDPROC 52 | 53 | PROC compare(a:PTR TO CHAR,b:PTR TO CHAR) 54 | DEF aa[32]:ARRAY OF CHAR,bb[32]:ARRAY OF CHAR,i 55 | 56 | i:=0 ; WHILE a[i]<>0 ; aa[i]:=ct[a[i]] ; i++ ; ENDWHILE 57 | i:=0 ; WHILE b[i]<>0 ; bb[i]:=ct[b[i]] ; i++ ; ENDWHILE 58 | i:=0 59 | LOOP 60 | IF aa[i]>bb[i] THEN RETURN 1 61 | IF aa[i]NIL 90 | len:=Read(fh,buf,492) 91 | Close(fh) 92 | IF len>0 93 | l:=0 94 | p:=0 95 | i:=0 96 | WHILE i39 108 | l++ 109 | IF l=12 110 | i:=len 111 | ELSE 112 | p:=0 113 | REPEAT ; UNTIL (buf[i++]<32) OR (i>=len) 114 | ENDIF 115 | ENDIF 116 | ENDIF 117 | ENDWHILE 118 | ENDIF 119 | ENDIF 120 | 121 | SetDrMd(w.rport,RP_JAM2) 122 | Move(w.rport,304,144+w.rport.font.baseline) 123 | Text(w.rport,info,40) 124 | FOR i:=1 TO 11 125 | Move(w.rport,304,152+w.rport.font.baseline+(8*i)) 126 | Text(w.rport,info+(i*40),40) 127 | ENDFOR 128 | ENDPROC 129 | 130 | PROC main() HANDLE 131 | DEF s=NIL:PTR TO screen,w=NIL:PTR TO window,lock=NIL, 132 | menu:chooser,portstate,fb:fileinfoblock,len,i, 133 | upctr=0,downctr=0,ssctr=0,font:PTR TO textfont,ta:textattr, 134 | mouseptr[256]:ARRAY OF CHAR 135 | 136 | DeleteFile('AGS:.run') 137 | FOR i:=0 TO 255 138 | tt[i]:=i 139 | ct[i]:=i 140 | mouseptr[i]:=0 141 | ENDFOR 142 | tt[0]:=" " 143 | tt["_"]:=" " 144 | ct["_"]:=" " 145 | FOR i:=65 TO 90 146 | ct[i]:=i+32 147 | ENDFOR 148 | FOR i:=192 TO 222 149 | ct[i]:=i+32 150 | ENDFOR 151 | ct[215]:=215 152 | 153 | IF (lowlevelbase:=OpenLibrary('lowlevel.library',0))=NIL THEN Raise("LOWL") 154 | 155 | IF SetJoyPortAttrsA(1,[SJA_TYPE,SJA_TYPE_JOYSTK,0])=FALSE 156 | Raise("JATR") 157 | ENDIF 158 | 159 | NEW menu.clear() 160 | IF (lock:=Lock('AGS:',ACCESS_READ))=FALSE 161 | Raise("LOCK") 162 | ENDIF 163 | IF Examine(lock,fb)=NIL 164 | Raise("EXAM") 165 | ENDIF 166 | WHILE ExNext(lock,fb)<>FALSE 167 | len:=StrLen(fb.filename) 168 | IF (len>6) AND (len<=30) 169 | IF InStr(fb.filename,'.start')=(len-6) 170 | FOR i:=0 TO len-7 171 | menu.choice[menu.nchoices*32+i]:=fb.filename[i] 172 | ENDFOR 173 | menu.nchoices:=menu.nchoices+1 174 | ENDIF 175 | ENDIF 176 | ENDWHILE 177 | 178 | IF menu.nchoices=0 THEN Raise("NONE") 179 | 180 | menu.sort() 181 | 182 | menu.active:=0 183 | IF menu.nchoices<30 184 | menu.height:=menu.nchoices 185 | ELSE 186 | menu.height:=30 187 | ENDIF 188 | 189 | IF (s:=OpenScreenTagList(NIL,[ 190 | SA_WIDTH,640, 191 | SA_HEIGHT,256, 192 | SA_DEPTH,8, 193 | SA_DISPLAYID,HIRES_KEY, 194 | -> SA_DISPLAYID,$50041000, 195 | SA_DRAGGABLE,FALSE, 196 | SA_SHOWTITLE,FALSE, 197 | 0]))=NIL THEN Raise("SCRN") 198 | IF (w:=OpenWindowTagList(NIL,[ 199 | WA_CUSTOMSCREEN,s, 200 | WA_WIDTH,640, 201 | WA_HEIGHT,256, 202 | WA_TITLE,0, 203 | WA_CLOSEGADGET,FALSE, 204 | WA_BORDERLESS,TRUE, 205 | WA_RMBTRAP,TRUE, 206 | WA_ACTIVATE,TRUE, 207 | 0]))=NIL THEN Raise("WIND") 208 | 209 | SetPointer(w,mouseptr,1,1,0,0) 210 | 211 | ta.name:='topaz.font' 212 | ta.ysize:=8 213 | ta.style:=0 214 | ta.flags:=0 215 | IF (font:=OpenFont(ta))=NIL THEN Raise("FONT") 216 | SetFont(w.rport,font) 217 | 218 | loadbkg(w) 219 | menu.render(w) 220 | 221 | LOOP 222 | WaitTOF() 223 | IF ((portstate:=ReadJoyPort(1))=JP_TYPE_NOTAVAIL) 224 | Raise("JTYP") 225 | ENDIF 226 | 227 | IF (portstate AND JPF_BUTTON_RED) 228 | choose(menu.active+menu.offset,menu) 229 | Raise("QUIT") 230 | ELSEIF (portstate AND JPF_BUTTON_BLUE) 231 | Raise("QUIT") 232 | ENDIF 233 | 234 | IF (portstate AND JPF_JOY_UP) 235 | IF (upctr=0) OR (upctr>8) 236 | IF menu.active>0 237 | menu.active:=menu.active-1 238 | menu.render(w) 239 | ssctr:=0 240 | ELSEIF menu.offset>0 241 | menu.offset:=menu.offset-1 242 | menu.render(w) 243 | ENDIF 244 | ENDIF 245 | upctr++ 246 | ELSE 247 | upctr:=0 248 | ENDIF 249 | 250 | IF (portstate AND JPF_JOY_DOWN) 251 | IF (downctr=0) OR (downctr>8) 252 | IF menu.active<(menu.height-1) 253 | menu.active:=menu.active+1 254 | menu.render(w) 255 | ssctr:=0 256 | ELSEIF (menu.active+menu.offset)<(menu.nchoices-1) 257 | menu.offset:=menu.offset+1 258 | menu.render(w) 259 | ssctr:=0 260 | ENDIF 261 | ENDIF 262 | downctr++ 263 | ELSE 264 | downctr:=0 265 | ENDIF 266 | 267 | IF ssctr++=8 268 | menu.loadss(w) 269 | ENDIF 270 | 271 | ENDLOOP 272 | 273 | 274 | EXCEPT DO 275 | IF font THEN CloseFont(font) 276 | IF lock THEN UnLock(lock) 277 | IF w THEN CloseWindow(w) 278 | IF s THEN CloseScreen(s) 279 | SetJoyPortAttrsA(1, [SJA_REINITIALIZE,0,0]) 280 | END menu 281 | SELECT exception 282 | CASE "QUIT" 283 | CASE "FONT" ; PrintF('Couldn''t OpenFont() topaz 8...?\n') 284 | CASE "LOCK" ; PrintF('Couldn''t lock AGS:.\n') 285 | CASE "EXAM" ; PrintF('Couldn''t examine AGS:.\n') 286 | CASE "NONE" ; PrintF('No start files found.\n') 287 | CASE "MEM" ; PrintF('Out of memory.\n') 288 | CASE "SCRN" ; PrintF('Couldn''t open screen.\n') 289 | CASE "WIND" ; PrintF('Couldn''t open window.\n') 290 | CASE "INFL" ; PrintF('Couldn''t open infile.\n') 291 | CASE "OUTF" ; PrintF('Couldn''t open outfile.\n') 292 | CASE "WERR" ; PrintF('Write error.\n') 293 | CASE "LOWL" ; PrintF('Couldn''t open lowlevel.library.\n') 294 | CASE "JATR" ; PrintF('Couldn''t setup joyport.\n') 295 | CASE "JTYP" ; PrintF('Couldn''t check joystick.\n') 296 | DEFAULT 297 | PrintF('Unknown exception: \z\h[8]\n',exception) 298 | ENDSELECT 299 | ENDPROC 300 | 301 | PROC choose(i,menu:PTR TO chooser) 302 | DEF name[1024]:STRING 303 | 304 | StrCopy(name,'AGS:') 305 | StrAdd(name,menu.choice+(i*32)) 306 | StrAdd(name,'.start') 307 | copy(name,'AGS:.run') 308 | ENDPROC 309 | 310 | PROC copy(infile:PTR TO CHAR,outfile:PTR TO CHAR) HANDLE 311 | DEF len,if=NIL,of=NIL,buf[512]:ARRAY OF CHAR 312 | 313 | IF (if:=Open(infile,MODE_OLDFILE))=NIL THEN Raise("INFL") 314 | IF (of:=Open(outfile,MODE_NEWFILE))=NIL THEN Raise("OUTF") 315 | WHILE (len:=Read(if,buf,512))>0 316 | IF Write(of,buf,len)<>len THEN Raise("WERR") 317 | ENDWHILE 318 | Raise("QUIT") 319 | EXCEPT DO 320 | IF if THEN Close(if) 321 | IF of THEN Close(of) 322 | ReThrow() 323 | ENDPROC 324 | 325 | PROC loadbkg(w:PTR TO window) HANDLE 326 | DEF pic=NIL,bm=NIL:PTR TO bitmap,pi:PTR TO picinfo,r,g,b 327 | 328 | IF (pic:=ilbm_New('AGS:SelectorBKG.iff',ILBMNF_COLOURS32))=NIL THEN Raise("BKGN") 329 | IF ilbm_LoadPicture(pic,[ILBML_GETBITMAP,{bm},0])<>0 THEN Raise("BKGL") 330 | pi:=ilbm_PictureInfo(pic) 331 | IF pi.pal32<>NIL 332 | LoadRGB32(w.wscreen.viewport,pi.pal32) 333 | ELSE 334 | FOR b:=0 TO 5 335 | FOR g:=0 TO 5 336 | FOR r:=0 TO 5 337 | SetRGB4(w.wscreen.viewport,(r*36)+(g*6)+b,r*3,g*3,b*3) 338 | ENDFOR 339 | ENDFOR 340 | ENDFOR 341 | SetRGB4(w.wscreen.viewport,255,13,14,15) 342 | SetRGB4(w.wscreen.viewport,254,3,4,5) 343 | ENDIF 344 | BltBitMapRastPort(bm,0,0,w.rport,0,0,640,256,$0c0) 345 | 346 | Raise("OK") 347 | EXCEPT DO 348 | IF bm THEN ilbm_FreeBitMap(bm) 349 | IF pic THEN ilbm_Dispose(pic) 350 | SELECT exception 351 | CASE "OK" 352 | CASE "BKGN" ; PrintF('Couldn''t open background image\n') 353 | CASE "BKGL" ; PrintF('Couldn''t load background image\n') 354 | DEFAULT 355 | PrintF('Unknown loadbkg exception: \z\h[8]\n',exception) 356 | ENDSELECT 357 | ENDPROC 358 | 359 | PROC loadss(w:PTR TO window) OF chooser HANDLE 360 | DEF pic=NIL,bm=NIL:PTR TO bitmap,pi:PTR TO picinfo,picname[32]:STRING, 361 | b:PTR TO bmhd 362 | 363 | StrCopy(picname,'AGS:') 364 | StrAdd(picname,self.choice+((self.active+self.offset)*32)) 365 | StrAdd(picname,'.iff') 366 | IF (pic:=ilbm_New(picname,ILBMNF_COLOURS32))=NIL THEN Raise("NEW") 367 | IF ilbm_LoadPicture(pic,[ILBML_GETBITMAP,{bm},0])<>0 THEN Raise("LOAD") 368 | pi:=ilbm_PictureInfo(pic) 369 | b:=pi.bmhd 370 | IF (b.w<>320) OR (b.h<>128) THEN Raise("SIZE") 371 | IF pi.pal32<>NIL 372 | -> LoadRGB32(w.wscreen.viewport,pi.pal32) 373 | ENDIF 374 | BltBitMapRastPort(bm,0,0,w.rport,304,8,320,128,$0c0) 375 | 376 | Raise("OK") 377 | EXCEPT DO 378 | IF bm THEN ilbm_FreeBitMap(bm) 379 | IF pic THEN ilbm_Dispose(pic) 380 | SELECT exception 381 | CASE "OK" 382 | DEFAULT 383 | SetAPen(w.rport,254) 384 | BltPattern(w.rport,NIL,304,8,304+319,8+127,0) 385 | ENDSELECT 386 | ENDPROC 387 | -------------------------------------------------------------------------------- /Old/agsimgloader.e: -------------------------------------------------------------------------------- 1 | /* AGSImgLoader - Background loading of images for AGS. */ 2 | 3 | 4 | OPT OSVERSION=37 5 | OPT PREPROCESS 6 | 7 | MODULE 'exec/ports' 8 | MODULE 'exec/nodes' 9 | MODULE 'dos/dos' 10 | MODULE 'graphics/rastport' 11 | MODULE '*benchmark' 12 | MODULE '*agsil' 13 | MODULE '*ilbmloader' 14 | 15 | 16 | ENUM ERR_NONE, ERR_CREATEPORT 17 | 18 | 19 | PROC load_image(il:PTR TO ilbmloader, 20 | name:PTR TO CHAR, 21 | rport:PTR TO rastport, 22 | x:LONG, 23 | y:LONG) 24 | DEF bmark = NIL:PTR TO benchmark 25 | 26 | NEW bmark.init(10) 27 | PrintF('Loading \s\n', name) 28 | bmark.start() 29 | il.open(name) 30 | bmark.mark() -> 0 31 | il.parse_header() 32 | bmark.mark() -> 1 33 | il.load_body(rport, x, y) 34 | bmark.mark() -> 2 35 | il.close() 36 | PrintF('Open IFF: \d[4] ms\n', bmark.msecs(0)) 37 | PrintF('Parse IFF: \d[4] ms\n', bmark.msecs(1) - bmark.msecs(0)) 38 | PrintF('Load BODY: \d[4] ms\n', bmark.msecs(2) - bmark.msecs(1)) 39 | END bmark 40 | ENDPROC 41 | 42 | 43 | PROC main() HANDLE 44 | DEF port = NIL:PTR TO mp 45 | DEF msg:PTR TO agsil_msg 46 | DEF portsig 47 | DEF breaksig 48 | DEF signal 49 | DEF abort = FALSE 50 | DEF action 51 | DEF il = NIL:PTR TO ilbmloader 52 | DEF rport = NIL:PTR TO rastport 53 | DEF x = 0 54 | DEF y = 0 55 | DEF img_to_load:PTR TO CHAR 56 | 57 | -> ILBMLoader object. 58 | NEW il.init() 59 | 60 | -> Create a message port and publish it. 61 | IF (port := CreateMsgPort()) = NIL THEN Raise(ERR_CREATEPORT) 62 | port.ln.name := AGSIL_PORTNAME 63 | port.ln.pri := 1 64 | AddPort(port) 65 | 66 | -> Get signal masks for Wait(). 67 | portsig := Shl(1, port.sigbit) 68 | breaksig := SIGBREAKF_CTRL_C 69 | 70 | -> Lower priority to run in the background. 71 | SetTaskPri(FindTask(NIL), -1) 72 | 73 | PrintF('AGSImgLoader is waiting for requests on \s, CTRL-C to stop.\n', AGSIL_PORTNAME) 74 | REPEAT 75 | -> Wait for a message or break signal. 76 | signal := Wait(portsig OR breaksig) 77 | 78 | -> Our message port received a message. 79 | IF signal AND portsig 80 | WHILE msg := GetMsg(port) 81 | action := msg.action 82 | SELECT action 83 | CASE AGSIL_QUIT 84 | PrintF('QUIT()\n') 85 | abort := TRUE 86 | msg.reply := AGSIL_OK 87 | CASE AGSIL_SETRPORT 88 | PrintF('SETRPORT($\h[08])\n', msg.arg) 89 | rport := msg.arg 90 | msg.reply := AGSIL_OK 91 | CASE AGSIL_SETXY 92 | x := Shr(msg.arg, 16) 93 | y := msg.arg AND $ffff 94 | PrintF('SETXY(\d, \d)\n', x, y) 95 | msg.reply := AGSIL_OK 96 | CASE AGSIL_LOAD 97 | PrintF('LOAD(\s)\n', msg.arg) 98 | IF rport = NIL 99 | PrintF('No raster port set!\n') 100 | msg.reply := AGSIL_ERROR 101 | ELSE 102 | msg.reply := AGSIL_OK 103 | img_to_load := msg.arg 104 | ENDIF 105 | DEFAULT 106 | PrintF('Unknown action $\h\n', action) 107 | ENDSELECT 108 | ReplyMsg(msg) 109 | ENDWHILE 110 | ENDIF 111 | 112 | -> Ctrl-C. 113 | IF signal AND breaksig 114 | abort := TRUE 115 | ENDIF 116 | 117 | IF img_to_load 118 | load_image(il, img_to_load, rport, x, y) 119 | img_to_load := NIL 120 | ENDIF 121 | 122 | UNTIL abort 123 | 124 | EXCEPT DO 125 | END il 126 | IF port 127 | -> Empty the message queue before deleting it. 128 | PrintF('Flushing and deleting \s.\n', AGSIL_PORTNAME) 129 | WHILE msg := GetMsg(port) DO ReplyMsg(msg) 130 | IF port.ln.name THEN RemPort(port) 131 | DeleteMsgPort(port) 132 | ENDIF 133 | SELECT exception 134 | CASE ERR_CREATEPORT 135 | PrintF('Couldn''t create "\s"\n', AGSIL_PORTNAME) 136 | CASE ILBM_ERROR 137 | PrintF('ILBMLoader error \d\n', exceptioninfo) 138 | DEFAULT 139 | IF exception 140 | PrintF('Unknown exception "\s" / $\h[08]\n', 141 | [exception, 0], 142 | exception) 143 | ENDIF 144 | ENDSELECT 145 | ENDPROC 146 | -------------------------------------------------------------------------------- /Old/chunkbuffer.e: -------------------------------------------------------------------------------- 1 | OPT MODULE 2 | OPT EXPORT 3 | OPT PREPROCESS 4 | 5 | 6 | CONST CHUNKBUFSIZE = 4096 7 | 8 | OBJECT chunkbuffer 9 | iff:PTR TO iffhandle 10 | data_left:LONG 11 | buf_size:LONG 12 | buf_ctr:LONG 13 | buf[CHUNKBUFSIZE]:ARRAY OF CHAR 14 | ENDOBJECT 15 | 16 | PROC init(iff:PTR TO iffhandle) OF chunkbuffer 17 | DEF ctxnode:PTR TO contextnode 18 | self.iff := iff 19 | ctxnode := CurrentChunk(iff) 20 | ->Vprintf('"%s" / "%s", size = %ld\n', [[ctxnode.id, 0], [ctxnode.type, 0], ctxnode.size]) 21 | self.data_left := ctxnode.size 22 | self.buf_size := 0 23 | self.buf_ctr := 0 24 | ENDPROC 25 | 26 | 27 | PROC getbyte() OF chunkbuffer 28 | DEF byte 29 | DEF len 30 | 31 | IF self.buf_ctr >= self.buf_size 32 | len := IF self.data_left < CHUNKBUFSIZE THEN self.data_left ELSE CHUNKBUFSIZE 33 | ->PrintF('Reading \d bytes into buffer\n', len) 34 | self.buf_size := ReadChunkBytes(self.iff, self.buf, len) 35 | IF self.buf_size < 0 36 | ->PrintF('ReadChunkBytes() = \d\n', self.buf_size) 37 | Raise("IOER") 38 | ->ELSE 39 | -> PrintF('Read \d bytes\n', self.buf_size) 40 | ENDIF 41 | self.buf_ctr := 0 42 | self.data_left := self.data_left - len 43 | ENDIF 44 | byte := self.buf[self.buf_ctr] 45 | self.buf_ctr := self.buf_ctr + 1 46 | ->byte := self.buf_ctr AND $ff 47 | ENDPROC byte 48 | /* 49 | PROC getbytes(nbytes:LONG, dest:PTR TO CHAR) OF chunkbuffer 50 | ENDPROC 51 | */ 52 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | ArcadeGameSelector 2 2 | ==================== 3 | 4 | AGS2 is a joystick controlled menu program for the Amiga. 5 | 6 | 7 | Download 8 | -------- 9 | 10 | Downloads are available on the [releases](https://github.com/MagerValp/ArcadeGameSelector/releases) page. 11 | 12 | 13 | Discussion and Help 14 | ------------------- 15 | 16 | [EAB Forum Thread](http://eab.abime.net/showthread.php?t=68818) 17 | 18 | 19 | What's New 20 | ---------- 21 | 22 | For users of version 1 a number of things have improved: 23 | 24 | * AGA and OCS support. 25 | * Subdirectory support, up to two levels deep. 26 | * Configurable screen layout. 27 | * Better control over colors. 28 | * Screenshots are loaded in a background task, keeping the interface responsive. 29 | * Support for screenshot slideshow. 30 | 31 | 32 | System Requirements 33 | ------------------- 34 | 35 | The minimum requirements for for running AGS2 are: 36 | 37 | * 68000 CPU 38 | * 1 MB of RAM 39 | * OCS chipset 40 | * Kickstart 2.0 41 | * A hard drive or CD-ROM drive 42 | * `iffparse.library` and `lowlevel.library` 43 | 44 | However, games compatibility with the minimum setup will be low. The recommended minimum is: 45 | 46 | * 68020 CPU 47 | * 4 MB of fast RAM 48 | * 1 MB of chip RAM 49 | * [WHDLoad](http://www.whdload.de/) 50 | 51 | 52 | Installation 53 | ------------ 54 | 55 | Copy `AGS2`, `AGS2Helper`, `AGS2.conf`, `AGS2Background.iff`, and `Empty.iff` into `AGS:`. Copy `Startup-Sequence` to `S:` for AGS to start automatically. For each game that you wish to run, create a script with the commands necessary to start it and give it a `.run` extension. You can also add a screenshot with a `.iff` extension and information with a `.txt` extension. 56 | 57 | Place AGS:AGS2 in the Startup-Sequence after SetPatch, Assign AGS: and whatever customizations you need: 58 | 59 | C:SetPatch >NIL: 60 | C:NoClick NOCLICK 61 | Assign AGS: SYS:AGS 62 | 63 | AGS:AGS2 64 | 65 | ; Startup-Sequence continues here if no game is selected. 66 | 67 | 68 | Usage 69 | ----- 70 | 71 | * Joystick, gamepad, or cursor keys `Up`/`Down` to select. 72 | * `Fire` button, CD32 `Red` button, or `Return` key to start a game or enter a directory. 73 | * CD32 `Blue` button, `Escape` key, or `RAmiga + Q` to quit menu. 74 | 75 | 76 | Subdirectories 77 | -------------- 78 | 79 | Subdirectories that end with `.ags` are included at the top of the game list, and they can also have a `.iff` screenshot and a `.txt` info file. Subdirectories are currently limited to being two levels deep. 80 | 81 | 82 | Screenshots and Colors 83 | ---------------------- 84 | 85 | The menu's screenmode, depth, palette, and colors are configurable. By default the screenmode and depth are copied from the background image, and text is rendered with the last color of the palette (255) and the second to last color (254) is used for the text's background. All of these can be configured in `AGS2.conf`. When screenshots are loaded the palette is also loaded, so to keep the screenshots from changing the colors of the user interface you can use the `lock_colors` options to lock the last colors in the palette. 86 | 87 | AGS2 also implements a slideshow feature which allows you to use multiple screenshot images for each entry. The format to use for the filenames is `[ENTRY_NAME]-[X].iff`, where `[X]` is the numerical index (e.g. `Agony-1.iff`, `Agony-2.iff` etc). If the slidehow feature is enabled, AGS2 will look for indexed images within the configured numerical range. If no matching indexed images are found, or the slideshow feature is disabled, then it will attempt to find a file with the standard naming of `[ENTRY_NAME].iff` (e.g. `Agony.iff`). For optimum performance, it is recommended to use non-compressed (non-RLE) images if storage space is not a primary concern. Furthermore, although AGS2 can support mixed resolution screenshots, having the images in a standardized resolution will also produce the best visual results. 88 | 89 | 90 | Configuration 91 | ------------- 92 | 93 | `AGS2.conf` allows you configure the following variables: 94 | 95 | ### *Background image and screen mode* 96 | background = AGS:AGS2Background.iff 97 | mode = $29000 98 | depth = 4 99 | lock_colors = 4 100 | 101 | *By default depth and mode are automatically copied from the background image, only set them if you wish to override.* 102 | 103 | ### *Font selection* 104 | font = topaz.font 105 | font_size = 8 106 | font_leading = 0 107 | 108 | ### *Menu layout* 109 | menu_x = 24 110 | menu_y = 8 111 | menu_height = 30 112 | 113 | *The menu's width is fixed at 26 characters, plus two characters of padding. With Topaz/8 the menu is 224 pixels wide.* 114 | 115 | ### *Screenshots* 116 | screenshot_x = 304 117 | screenshot_y = 8 118 | empty_screenshot = AGS:Empty.iff 119 | 120 | # Slideshow delay in seconds 121 | # Set to zero to disable the slideshow feature 122 | slideshow_delay_secs = 3 123 | 124 | # Any image with an index greater than or equal to this value will be displayed 125 | slideshow_start_index = 1 126 | 127 | # Any image with an index less than or equal to this value will be displayed 128 | slideshow_end_index = 7 129 | 130 | ### *Information text display* 131 | text_x = 304 132 | text_y = 144 133 | text_width = 40 134 | text_height = 13 135 | text_color = 255 136 | text_background = 254 137 | 138 | ### *Miscellaneous* 139 | # Valid options are "quit" or "none". 140 | blue_button_action = quit 141 | -------------------------------------------------------------------------------- /Startup-Sequence: -------------------------------------------------------------------------------- 1 | C:SetPatch >NIL: 2 | Assign AGS: SYS:AGS 3 | 4 | AGS:AGS2 5 | 6 | ; Startup-Sequence continues here if no game is selected. 7 | -------------------------------------------------------------------------------- /Test/lowlevel.e: -------------------------------------------------------------------------------- 1 | OPT PREPROCESS,REG=3 2 | 3 | MODULE 'intuition/screens', 'intuition/intuition', 'graphics/modeid', 4 | 'dos/dos', 'graphics/rastport', 'graphics/text', 'lowlevel', 5 | 'libraries/lowlevel', 'tools/ilbm', 'tools/ilbmdefs', 'exec/memory', 6 | 'graphics/gfx' 7 | 8 | 9 | PROC main() HANDLE 10 | DEF s = NIL:PTR TO screen 11 | DEF w = NIL:PTR TO window 12 | DEF portstate 13 | DEF font:PTR TO textfont 14 | DEF ta:textattr 15 | DEF sx = 16 16 | DEF sy = 8 17 | DEF key 18 | DEF rawkey 19 | 20 | IF (lowlevelbase := OpenLibrary('lowlevel.library', 0)) = NIL THEN Raise("LOWL") 21 | 22 | IF SetJoyPortAttrsA(1, [SJA_TYPE, SJA_TYPE_JOYSTK, 0]) = FALSE 23 | Raise("JATR") 24 | ENDIF 25 | 26 | IF (s := OpenScreenTagList(NIL, [ 27 | SA_WIDTH, 640, 28 | SA_HEIGHT, 256, 29 | SA_DEPTH, 4, 30 | SA_DISPLAYID, HIRES_KEY, 31 | -> SA_DISPLAYID,$50041000, 32 | SA_DRAGGABLE, FALSE, 33 | SA_SHOWTITLE, FALSE, 34 | 0])) = NIL THEN Raise("SCRN") 35 | IF (w := OpenWindowTagList(NIL, [ 36 | WA_CUSTOMSCREEN, s, 37 | WA_WIDTH, 640, 38 | WA_HEIGHT, 256, 39 | WA_TITLE, 0, 40 | WA_CLOSEGADGET, FALSE, 41 | WA_BORDERLESS, TRUE, 42 | WA_RMBTRAP, TRUE, 43 | WA_ACTIVATE, TRUE, 44 | 0])) = NIL THEN Raise("WIND") 45 | 46 | ->SetPointer(w, mouseptr, 1, 1, 0, 0) 47 | 48 | ta.name := 'topaz.font' 49 | ta.ysize := 8 50 | ta.style := 0 51 | ta.flags := 0 52 | IF (font := OpenFont(ta)) = NIL THEN Raise("FONT") 53 | SetFont(w.rport, font) 54 | 55 | SetAPen(w.rport, 255) 56 | SetBPen(w.rport, 254) 57 | SetDrMd(w.rport, RP_JAM2) 58 | 59 | LOOP 60 | WaitTOF() 61 | 62 | key := GetKey() 63 | rawkey := key AND $ffff 64 | 65 | IF (rawkey = 16) AND (key AND LLKB_RAMIGA) -> RAmiga-Q 66 | Raise("QUIT") 67 | ELSEIF rawkey = 69 -> Esc 68 | Raise("QUIT") 69 | ENDIF 70 | 71 | IF ((portstate := ReadJoyPort(1)) = JP_TYPE_NOTAVAIL) 72 | Raise("JTYP") 73 | ENDIF 74 | 75 | -> IF (portstate AND JPF_BUTTON_RED) 76 | -> Raise("QUIT") 77 | -> ELSEIF (portstate AND JPF_BUTTON_BLUE) 78 | -> Raise("QUIT") 79 | -> ENDIF 80 | 81 | Move(w.rport, 6 * sx, (3 * sy) + w.rport.font.baseline) 82 | IF (portstate AND JPF_BUTTON_RED) 83 | Text(w.rport, 'Rd', 2) 84 | ELSE 85 | Text(w.rport, ' ', 2) 86 | ENDIF 87 | 88 | Move(w.rport, 6 * sx, (5 * sy) + w.rport.font.baseline) 89 | IF (portstate AND JPF_BUTTON_BLUE) 90 | Text(w.rport, 'Bl', 2) 91 | ELSE 92 | Text(w.rport, ' ', 2) 93 | ENDIF 94 | 95 | Move(w.rport, 3 * sx, (3 * sy) + w.rport.font.baseline) 96 | IF (portstate AND JPF_JOY_UP) 97 | Text(w.rport, '/\\', 2) 98 | ELSE 99 | Text(w.rport, ' ', 2) 100 | ENDIF 101 | 102 | Move(w.rport, 3 * sx, (5 * sy) + w.rport.font.baseline) 103 | IF (portstate AND JPF_JOY_DOWN) 104 | Text(w.rport, '\\/', 2) 105 | ELSE 106 | Text(w.rport, ' ', 2) 107 | ENDIF 108 | 109 | Move(w.rport, 2 * sx, (4 * sy) + w.rport.font.baseline) 110 | IF (portstate AND JPF_JOY_LEFT) 111 | Text(w.rport, '< ', 2) 112 | ELSE 113 | Text(w.rport, ' ', 2) 114 | ENDIF 115 | 116 | Move(w.rport, 4 * sx, (4 * sy) + w.rport.font.baseline) 117 | IF (portstate AND JPF_JOY_RIGHT) 118 | Text(w.rport, ' >', 2) 119 | ELSE 120 | Text(w.rport, ' ', 2) 121 | ENDIF 122 | 123 | ENDLOOP 124 | 125 | 126 | EXCEPT DO 127 | IF font THEN CloseFont(font) 128 | IF w THEN CloseWindow(w) 129 | IF s THEN CloseScreen(s) 130 | SetJoyPortAttrsA(1, [SJA_REINITIALIZE, 0, 0]) 131 | SELECT exception 132 | CASE "QUIT" 133 | CASE "FONT" ; PrintF('Couldn''t OpenFont() topaz 8...?\n') 134 | CASE "LOCK" ; PrintF('Couldn''t lock AGS:.\n') 135 | CASE "EXAM" ; PrintF('Couldn''t examine AGS:.\n') 136 | CASE "NONE" ; PrintF('No start files found.\n') 137 | CASE "MEM" ; PrintF('Out of memory.\n') 138 | CASE "SCRN" ; PrintF('Couldn''t open screen.\n') 139 | CASE "WIND" ; PrintF('Couldn''t open window.\n') 140 | CASE "INFL" ; PrintF('Couldn''t open infile.\n') 141 | CASE "OUTF" ; PrintF('Couldn''t open outfile.\n') 142 | CASE "WERR" ; PrintF('Write error.\n') 143 | CASE "LOWL" ; PrintF('Couldn''t open lowlevel.library.\n') 144 | CASE "JATR" ; PrintF('Couldn''t setup joyport.\n') 145 | CASE "JTYP" ; PrintF('Couldn''t check joystick.\n') 146 | DEFAULT 147 | PrintF('Unknown exception: \z\h[8]\n',exception) 148 | ENDSELECT 149 | ENDPROC 150 | -------------------------------------------------------------------------------- /Test/test.e: -------------------------------------------------------------------------------- 1 | /* Test AGSImgLoader by sending messages. */ 2 | 3 | 4 | OPT OSVERSION=37 5 | OPT PREPROCESS 6 | 7 | OBJECT testobj 8 | cbool:CHAR 9 | ibool:INT 10 | ENDOBJECT 11 | 12 | PROC main() HANDLE 13 | ->DEF to:PTR TO testobj 14 | PrintFault(205, 'hejsan') 15 | EXCEPT DO 16 | ENDPROC 17 | -------------------------------------------------------------------------------- /Test/testconf.e: -------------------------------------------------------------------------------- 1 | /* Test AGSConf. */ 2 | 3 | 4 | OPT OSVERSION=37 5 | OPT PREPROCESS 6 | 7 | MODULE '*agsconf' 8 | 9 | 10 | PROC main() HANDLE 11 | DEF conf = NIL:PTR TO agsconf 12 | 13 | NEW conf.init() 14 | conf.read('AGS:AGS2.conf') 15 | 16 | EXCEPT DO 17 | END conf 18 | ENDPROC 19 | -------------------------------------------------------------------------------- /Test/testimgloader.e: -------------------------------------------------------------------------------- 1 | /* Test AGSImgLoader by sending messages. */ 2 | 3 | 4 | OPT OSVERSION=37 5 | OPT PREPROCESS 6 | 7 | MODULE 'intuition/intuition' 8 | MODULE 'intuition/screens' 9 | MODULE 'graphics/text' 10 | MODULE '*agsil' 11 | 12 | 13 | ENUM ERR_NONE, ERR_SCREEN, ERR_WINDOW 14 | 15 | PROC main() HANDLE 16 | DEF am:PTR TO agsil_master 17 | DEF reply 18 | 19 | DEF w = NIL:PTR TO window 20 | DEF class 21 | DEF scr:PTR TO screen 22 | DEF top_border 23 | DEF left_border 24 | DEF right_border 25 | DEF bottom_border 26 | DEF width 27 | DEF height 28 | 29 | -> Calculate border sizes for Workbench windows. 30 | IF (scr := LockPubScreen(NIL)) = NIL THEN Throw(AGSIL_ERROR, ERR_SCREEN) 31 | top_border := scr.wbortop + scr.font.ysize + 1 32 | left_border := scr.wborleft 33 | right_border := scr.wborright 34 | bottom_border := scr.wborbottom 35 | UnlockPubScreen(NIL, scr) 36 | 37 | -> Open window for testing. 38 | width := 320 + left_border + right_border 39 | height := 128 + top_border + bottom_border 40 | IF (w := OpenWindowTagList(NIL,[ 41 | ->WA_CUSTOMSCREEN, s, 42 | WA_WIDTH, width, 43 | WA_HEIGHT, height, 44 | ->WA_TOP, 256, 45 | WA_TITLE, 'agsimgloader', 46 | WA_CLOSEGADGET, TRUE, 47 | ->WA_BORDERLESS, TRUE, 48 | ->WA_RMBTRAP, TRUE, 49 | WA_ACTIVATE, FALSE, 50 | WA_IDCMP, IDCMP_CLOSEWINDOW, ->$268, 51 | 0])) = NIL THEN Raise("WIND") 52 | 53 | NEW am.init() 54 | 55 | PrintF('SETRPORT($\h[08]): ', w.rport) 56 | reply := am.send_cmd(AGSIL_SETRPORT, w.rport) 57 | PrintF('\d\n', reply) 58 | 59 | PrintF('SETXY(\d, \d): ', left_border, top_border) 60 | reply := am.send_cmd(AGSIL_SETXY, Shl(left_border, 16) OR top_border) 61 | PrintF('\d\n', reply) 62 | 63 | PrintF('LOAD(\s): ', 'test128.iff') 64 | reply := am.send_cmd(AGSIL_LOAD, 'test128.iff') 65 | PrintF('\d\n', reply) 66 | 67 | WHILE (class := WaitIMessage(w)) <> IDCMP_CLOSEWINDOW 68 | ENDWHILE 69 | 70 | PrintF('QUIT(): ') 71 | reply := am.send_cmd(AGSIL_QUIT, NIL) 72 | PrintF('\d\n', reply) 73 | 74 | EXCEPT DO 75 | END am 76 | IF w THEN CloseWindow(w) 77 | SELECT exception 78 | CASE ERR_WINDOW 79 | PrintF('Couldn''t open window\n') 80 | CASE ERR_SCREEN 81 | PrintF('Couldn''t lock screen\n') 82 | CASE AGSIL_ERROR 83 | PrintF('AGSIL error \d\n', exceptioninfo) 84 | CASE "MEM" 85 | PrintF('Out of memory\n') 86 | DEFAULT 87 | IF exception 88 | PrintF('Unknown exception "\s" / $\h[08]\n', 89 | [exception, 0], 90 | exception) 91 | ENDIF 92 | ENDSELECT 93 | ENDPROC 94 | -------------------------------------------------------------------------------- /Test/testnav.e: -------------------------------------------------------------------------------- 1 | /* Test AGSMenu. */ 2 | 3 | 4 | OPT OSVERSION=37 5 | OPT PREPROCESS 6 | 7 | MODULE '*agsnav' 8 | 9 | 10 | PROC main() HANDLE 11 | DEF am = NIL:PTR TO agsnav 12 | DEF i 13 | DEF item:PTR TO agsnav_item 14 | 15 | NEW am.init() 16 | am.read_dir() 17 | 18 | FOR i := 0 TO am.num_items - 1 19 | item := am.items[i] 20 | PrintF('\s\n', item.name) 21 | ENDFOR 22 | 23 | EXCEPT DO 24 | END am 25 | ENDPROC 26 | -------------------------------------------------------------------------------- /WB13-Background.iff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagerValp/ArcadeGameSelector/c5517182ac6b46659aa44dadecc2b87286e14d52/WB13-Background.iff -------------------------------------------------------------------------------- /WB13-Empty.iff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagerValp/ArcadeGameSelector/c5517182ac6b46659aa44dadecc2b87286e14d52/WB13-Empty.iff -------------------------------------------------------------------------------- /agsconf.e: -------------------------------------------------------------------------------- 1 | /* AGSConf reads and stores application configuration. */ 2 | 3 | OPT MODULE 4 | 5 | 6 | MODULE 'dos/dos' 7 | ->MODULE 'dos/dosextens' 8 | ->MODULE 'dos/exall' 9 | 10 | 11 | EXPORT CONST AGSCONF_ERROR = "AGSC" 12 | EXPORT ENUM AGSCONF_ERR_READ = 1, AGSCONF_ERR_VALUE, AGSCONF_ERR_UNKNOWN 13 | EXPORT ENUM AGSCONF_ACTION_NONE = 0, AGSCONF_ACTION_QUIT = 1 14 | 15 | 16 | EXPORT PROC agsconf_strerror(num:LONG) IS ListItem([ 17 | 'Error reading config file', 18 | 'Illegal value', 19 | 'Unknown option' 20 | ], num - 1) 21 | 22 | 23 | EXPORT CONST AGSCONF_AUTODETECT = -1 24 | 25 | EXPORT OBJECT agsconf 26 | background:LONG -> PTR TO STRING -> AGS:AGS2Background.iff 27 | mode:LONG -> = $29000 28 | depth:INT -> = 4 29 | lock_colors:INT -> = 4 30 | 31 | font_name:LONG -> PTR TO STRING -> topaz.font 32 | font_size:INT -> = 8 33 | font_leading:INT -> = 0 34 | 35 | menu_x:INT -> = 24 36 | menu_y:INT -> = 8 37 | menu_height:INT -> = 30 38 | 39 | screenshot_x:INT -> = 304 40 | screenshot_y:INT -> = 8 41 | empty_screenshot:LONG -> PTR TO STRING -> AGS:Empty.iff 42 | 43 | text_x:INT 44 | text_y:INT 45 | text_width:INT 46 | text_height:INT 47 | text_color:INT -> = 255 48 | text_background:INT -> = 254 49 | 50 | blue_button_action:LONG -> AGSCONF_ACTION_QUIT 51 | 52 | slideshow_delay_secs:INT -> = 0 (disabled) 53 | slideshow_start_index:INT -> = 1 54 | slideshow_end_index:INT -> = 7 55 | ENDOBJECT 56 | 57 | -> Initialize with default configuration. 58 | PROC init() OF agsconf 59 | self.background := String(128) 60 | self.font_name := String(32) 61 | self.empty_screenshot := String(128) 62 | 63 | StrCopy(self.background, 'AGS:AGS2Background.iff') 64 | self.mode := AGSCONF_AUTODETECT 65 | self.depth := AGSCONF_AUTODETECT 66 | self.text_color := 255 67 | self.text_background := 254 68 | self.lock_colors := 4 69 | 70 | StrCopy(self.font_name, 'topaz.font') 71 | self.font_size := 8 72 | self.font_leading := 0 73 | 74 | self.menu_x := 24 75 | self.menu_y := 8 76 | self.menu_height := 30 77 | 78 | self.screenshot_x := 304 79 | self.screenshot_y := 8 80 | StrCopy(self.empty_screenshot, 'AGS:Empty.iff') 81 | 82 | self.text_x := 304 83 | self.text_y := 144 84 | self.text_width := 40 85 | self.text_height := (248 - self.text_y) / self.font_size 86 | 87 | self.blue_button_action := AGSCONF_ACTION_QUIT 88 | 89 | self.slideshow_delay_secs := 0 90 | self.slideshow_start_index := 1 91 | self.slideshow_end_index := 7 92 | ENDPROC 93 | 94 | PROC end() OF agsconf 95 | DisposeLink(self.background) 96 | DisposeLink(self.font_name) 97 | DisposeLink(self.empty_screenshot) 98 | ENDPROC 99 | 100 | PROC set_value(key:PTR TO CHAR, value:PTR TO CHAR) OF agsconf 101 | DEF num, read 102 | 103 | IF StrCmp(key, 'background') 104 | StrCopy(self.background, value) 105 | ELSEIF StrCmp(key, 'font') 106 | StrCopy(self.font_name, value) 107 | ELSEIF StrCmp(key, 'empty_screenshot') 108 | StrCopy(self.empty_screenshot, value) 109 | ELSEIF StrCmp(key, 'blue_button_action') 110 | IF StrCmp(value, 'quit') 111 | self.blue_button_action := AGSCONF_ACTION_QUIT 112 | ELSEIF StrCmp(value, 'none') 113 | self.blue_button_action := AGSCONF_ACTION_NONE 114 | ELSE 115 | Throw(AGSCONF_ERROR, AGSCONF_ERR_UNKNOWN) 116 | ENDIF 117 | ELSE 118 | num, read := Val(value) 119 | IF read = 0 THEN Throw(AGSCONF_ERROR, AGSCONF_ERR_VALUE) 120 | IF StrCmp(key, 'mode') 121 | self.mode := num 122 | ELSEIF StrCmp(key, 'depth') 123 | self.depth := num 124 | ELSEIF StrCmp(key, 'text_color') 125 | self.text_color := num 126 | ELSEIF StrCmp(key, 'text_background') 127 | self.text_background := num 128 | ELSEIF StrCmp(key, 'lock_colors') 129 | self.lock_colors := num 130 | ELSEIF StrCmp(key, 'font_size') 131 | self.font_size := num 132 | ELSEIF StrCmp(key, 'font_leading') 133 | self.font_leading := num 134 | ELSEIF StrCmp(key, 'menu_x') 135 | self.menu_x := num 136 | ELSEIF StrCmp(key, 'menu_y') 137 | self.menu_y := num 138 | ELSEIF StrCmp(key, 'menu_height') 139 | self.menu_height := num 140 | ELSEIF StrCmp(key, 'screenshot_x') 141 | self.screenshot_x := num 142 | ELSEIF StrCmp(key, 'screenshot_y') 143 | self.screenshot_y := num 144 | ELSEIF StrCmp(key, 'text_x') 145 | self.text_x := num 146 | ELSEIF StrCmp(key, 'text_y') 147 | self.text_y := num 148 | ELSEIF StrCmp(key, 'text_width') 149 | self.text_width := num 150 | ELSEIF StrCmp(key, 'text_height') 151 | self.text_height := num 152 | ELSEIF StrCmp(key, 'slideshow_delay_secs') 153 | self.slideshow_delay_secs := num 154 | ELSEIF StrCmp(key, 'slideshow_start_index') 155 | self.slideshow_start_index := num 156 | ELSEIF StrCmp(key, 'slideshow_end_index') 157 | self.slideshow_end_index := num 158 | ELSE 159 | Throw(AGSCONF_ERROR, AGSCONF_ERR_UNKNOWN) 160 | ENDIF 161 | ENDIF 162 | ENDPROC 163 | 164 | CONST BUFSIZE=128 165 | 166 | PROC read(filename:PTR TO CHAR) OF agsconf HANDLE 167 | DEF fh = NIL 168 | DEF line[BUFSIZE]:STRING 169 | DEF bufsize 170 | DEF linenum = 0 171 | DEF len 172 | DEF pos 173 | DEF key[32]:STRING 174 | DEF value[96]:STRING 175 | 176 | IF (fh := Open(filename, OLDFILE)) = NIL 177 | IF IoErr() = 205 178 | Raise(0) 179 | ELSE 180 | PrintFault(IoErr(), filename) 181 | Throw(AGSCONF_ERROR, AGSCONF_ERR_READ) 182 | ENDIF 183 | ENDIF 184 | 185 | IF KickVersion(39) 186 | bufsize := BUFSIZE 187 | ELSE 188 | -> Workaround for bug in V36/V37. 189 | bufsize := BUFSIZE - 1 190 | ENDIF 191 | -> Read a line at a time, max BUFSIZE - 1 characters per line. 192 | WHILE Fgets(fh, line, bufsize) 193 | INC linenum 194 | 195 | len := StrLen(line) 196 | -> Trim trailing newline. 197 | IF len > 0 198 | IF (line[len - 1] = "\n") 199 | DEC len 200 | line[len] := 0 201 | ENDIF 202 | ENDIF 203 | -> Fix estring length. 204 | SetStr(line, len) 205 | ->PrintF('Config line \d: "\s"\n', linenum, line) 206 | 207 | IF len = 0 208 | -> Skip empty lines. 209 | ELSEIF line[0] = "#" 210 | -> Skip comment lines. 211 | ELSE 212 | -> Split string on first occurence of =. 213 | pos := InStr(line, '=') 214 | IF pos < 1 215 | Throw(AGSCONF_ERROR, AGSCONF_ERR_READ) 216 | ENDIF 217 | -> Copy left hand side as the config key name. 218 | MidStr(key, line, 0, pos) 219 | -> Trim trailing spaces. 220 | len := EstrLen(key) 221 | WHILE (len > 0) AND (key[len - 1] = " ") DO DEC len 222 | SetStr(key, len) 223 | 224 | -> Copy right hand side as the config value, skipping leading 225 | -> spaces. 226 | INC pos 227 | WHILE (pos < EstrLen(line)) AND (line[pos] = " ") DO INC pos 228 | IF pos = EstrLen(line) 229 | Throw(AGSCONF_ERROR, AGSCONF_ERR_READ) 230 | ENDIF 231 | MidStr(value, line, pos, EstrLen(line) - pos) 232 | -> Trim trailing spaces. 233 | len := EstrLen(value) 234 | WHILE (len > 0) AND (value[len - 1] = " ") DO DEC len 235 | SetStr(value, len) 236 | 237 | self.set_value(key, value) 238 | ENDIF 239 | 240 | ENDWHILE 241 | 242 | EXCEPT DO 243 | IF fh THEN Close(fh) 244 | IF exception 245 | IF linenum THEN PrintF('\s error on line \d\n', filename, linenum) 246 | ReThrow() 247 | ENDIF 248 | ENDPROC 249 | -------------------------------------------------------------------------------- /agsdefs.e: -------------------------------------------------------------------------------- 1 | /* Common defines for AGS. */ 2 | 3 | 4 | OPT MODULE 5 | OPT EXPORT 6 | OPT PREPROCESS 7 | 8 | 9 | #define AGS_RUN_PATH 'RAM:AGS.run' 10 | -------------------------------------------------------------------------------- /agsil.e: -------------------------------------------------------------------------------- 1 | /* AGSIL contains the control classes for background image loading. */ 2 | 3 | 4 | OPT MODULE 5 | OPT EXPORT 6 | OPT PREPROCESS 7 | 8 | 9 | MODULE 'exec/memory' 10 | MODULE 'exec/ports' 11 | MODULE 'exec/nodes' 12 | MODULE 'dos/dostags' 13 | 14 | 15 | CONST AGSIL_ERROR = "AGSI" 16 | ENUM AGSIL_ERR_FINDPORT = 1, AGSIL_ERR_CREATEPORT, AGSIL_ERR_START 17 | 18 | EXPORT PROC agsil_strerror(num:LONG) IS ListItem([ 19 | 'FindPort failed', 20 | 'CreatePort failed', 21 | 'Couldn''t start image loader' 22 | ], num - 1) 23 | 24 | 25 | #define AGSIL_PORTNAME 'agsilport' 26 | 27 | ENUM AGSIL_QUIT, 28 | AGSIL_SETRPORT, 29 | AGSIL_SETVPORT, 30 | AGSIL_SETCONF, 31 | AGSIL_SETMAXCOLORS, 32 | AGSIL_LOAD, 33 | AGSIL_GETIMGNUM 34 | 35 | OBJECT agsil_msg 36 | msg:mn 37 | action:LONG 38 | arg:LONG 39 | reply:LONG 40 | ENDOBJECT 41 | 42 | 43 | OBJECT agsil_master 44 | replyport:PTR TO mp 45 | msg:PTR TO agsil_msg 46 | send_quit -> Set to TRUE if we should attempt to send an AGSIL_QUIT 47 | ENDOBJECT -> message in end(). 48 | 49 | PROC init() OF agsil_master 50 | self.replyport := CreateMsgPort() 51 | IF self.replyport = NIL THEN Throw(AGSIL_ERROR, AGSIL_ERR_CREATEPORT) 52 | self.msg := NewM(SIZEOF agsil_msg, MEMF_PUBLIC OR MEMF_CLEAR) 53 | self.msg.msg.ln.type := NT_MESSAGE 54 | self.msg.msg.length := SIZEOF agsil_msg 55 | self.msg.msg.replyport := self.replyport 56 | self.send_quit := TRUE 57 | ENDPROC 58 | 59 | PROC end() OF agsil_master 60 | DEF port:PTR TO mp 61 | 62 | IF self.send_quit 63 | self.msg.action := AGSIL_QUIT 64 | self.msg.arg := NIL 65 | Forbid() 66 | port := FindPort(AGSIL_PORTNAME) 67 | IF port THEN PutMsg(port, self.msg) 68 | Permit() 69 | IF port 70 | WaitPort(self.replyport) 71 | GetMsg(self.replyport) 72 | ENDIF 73 | ENDIF 74 | IF self.msg THEN Dispose(self.msg) 75 | IF self.replyport THEN DeleteMsgPort(self.replyport) 76 | ENDPROC 77 | 78 | PROC start() OF agsil_master 79 | DEF result 80 | DEF console 81 | 82 | console := Open('CON:0/40/640/150/irqimgloader/auto/close/wait', OLDFILE) 83 | result := SystemTagList('AGS:AGS2Helper', [ 84 | SYS_ASYNCH, TRUE, 85 | SYS_INPUT, console, 86 | SYS_OUTPUT, NIL, 87 | 0]) 88 | IF result = -1 89 | Close(console) 90 | Throw(AGSIL_ERROR, AGSIL_ERR_START) 91 | ENDIF 92 | ENDPROC 93 | 94 | PROC wait_port() OF agsil_master 95 | DEF count = 0 96 | 97 | REPEAT 98 | IF FindPort(AGSIL_PORTNAME) 99 | RETURN 100 | ENDIF 101 | Delay(1) 102 | UNTIL count++ = 250 103 | -> Throw an error if it takes more than 5 seconds for the agsil port to 104 | -> appear. 105 | Throw(AGSIL_ERROR, AGSIL_ERR_START) 106 | ENDPROC 107 | 108 | PROC send_cmd(action:LONG, arg:LONG) OF agsil_master 109 | DEF port:PTR TO mp 110 | DEF reply:PTR TO agsil_msg 111 | 112 | self.msg.action := action 113 | IF action = AGSIL_QUIT 114 | self.send_quit := FALSE -> QUIT has already been sent, no need for 115 | ENDIF -> end() to do it. 116 | self.msg.arg := arg 117 | Forbid() 118 | port := FindPort(AGSIL_PORTNAME) 119 | IF port THEN PutMsg(port, self.msg) 120 | Permit() 121 | IF port = NIL THEN Throw(AGSIL_ERROR, AGSIL_ERR_FINDPORT) 122 | WaitPort(self.replyport) 123 | IF (reply := GetMsg(self.replyport)) = NIL 124 | self.msg.reply := NIL 125 | ENDIF 126 | ENDPROC self.msg.reply 127 | 128 | PROC stop() OF agsil_master IS self.send_cmd(AGSIL_QUIT, NIL) 129 | 130 | PROC wait_load(img_num:LONG) OF agsil_master 131 | DEF num 132 | 133 | WHILE (num := self.send_cmd(AGSIL_GETIMGNUM, NIL)) <> img_num 134 | Delay(1) 135 | ENDWHILE 136 | ENDPROC 137 | -------------------------------------------------------------------------------- /agsmenupos.e: -------------------------------------------------------------------------------- 1 | /* De/serialize navigation and menu position */ 2 | 3 | OPT PREPROCESS 4 | OPT MODULE 5 | 6 | MODULE 'dos/dos' 7 | 8 | #define AGS_MENUPOS_PATH 'RAM:AGS.pos' 9 | 10 | EXPORT OBJECT agsmenupos 11 | path -> STRING 12 | depth:LONG 13 | offset:LONG 14 | pos:LONG 15 | ENDOBJECT 16 | 17 | CONST BUFSIZE=256 18 | 19 | PROC init() OF agsmenupos 20 | self.path := String(BUFSIZE) 21 | ENDPROC 22 | 23 | PROC end() OF agsmenupos 24 | DisposeLink(self.path) 25 | ENDPROC 26 | 27 | PROC read() OF agsmenupos 28 | DEF fh = NIL 29 | DEF line[BUFSIZE]:STRING 30 | DEF linenum = 0 31 | DEF bufsize, len, num, read 32 | 33 | IF KickVersion(39) THEN bufsize := BUFSIZE ELSE bufsize := BUFSIZE - 1 34 | 35 | IF fh := Open(AGS_MENUPOS_PATH, MODE_OLDFILE) 36 | WHILE Fgets(fh, line, bufsize) 37 | INC linenum 38 | SELECT linenum 39 | CASE 1 40 | len := StrLen(line) 41 | IF len > 0 42 | IF (line[len - 1] = "\n") 43 | DEC len 44 | line[len] := 0 45 | ENDIF 46 | ENDIF 47 | SetStr(line, len) 48 | StrCopy(self.path, line) 49 | CASE 2 50 | num, read := Val(line) 51 | IF read <> 0 THEN self.depth := num 52 | CASE 3 53 | num, read := Val(line) 54 | IF read <> 0 THEN self.offset := num 55 | CASE 4 56 | num, read := Val(line) 57 | IF read <> 0 THEN self.pos := num 58 | CASE 5 59 | JUMP break 60 | ENDSELECT 61 | ENDWHILE 62 | break: 63 | Close(fh) 64 | DeleteFile(AGS_MENUPOS_PATH) 65 | ELSE 66 | StrCopy(self.path, 'AGS:') 67 | ENDIF 68 | ENDPROC 69 | 70 | PROC write(path:PTR TO CHAR, depth, offset, pos) OF agsmenupos 71 | DEF fh = NIL 72 | 73 | StrCopy(self.path, path) 74 | self.depth := depth 75 | self.offset := offset 76 | self.pos := pos 77 | 78 | IF fh := Open(AGS_MENUPOS_PATH, MODE_NEWFILE) 79 | VfPrintf(fh, '\s\n\d\n\d\n\d', [self.path, self.depth, self.offset, self.pos]:LONG) 80 | Close(fh) 81 | ENDIF 82 | ENDPROC 83 | -------------------------------------------------------------------------------- /agsnav.e: -------------------------------------------------------------------------------- 1 | /* AGSNav navigates and parses the menu directory structure. */ 2 | 3 | OPT MODULE 4 | 5 | 6 | MODULE 'dos/dos' 7 | MODULE 'dos/dosextens' 8 | MODULE 'dos/exall' 9 | 10 | 11 | EXPORT CONST AGSNAV_ERROR = "AGSM" 12 | EXPORT ENUM AGSNAV_ERR_LOCK = 1, AGSNAV_ERR_EXALL 13 | 14 | EXPORT PROC agsnav_strerror(num:LONG) IS ListItem([ 15 | 'Couldn''t lock directory', 16 | 'Error reading directory' 17 | ], num - 1) 18 | 19 | 20 | EXPORT ENUM AGSNAV_TYPE_DIR = 1, AGSNAV_TYPE_FILE = 2 21 | 22 | EXPORT OBJECT agsnav_item 23 | name -> STRING 24 | length:LONG 25 | type:LONG 26 | ENDOBJECT 27 | 28 | 29 | EXPORT OBJECT agsnav 30 | path -> STRING 31 | depth:LONG 32 | num_items:LONG 33 | items:PTR TO LONG -> PTR TO ARRAY OF agsnav_item 34 | reserved:LONG 35 | ENDOBJECT 36 | 37 | 38 | PROC init() OF agsnav 39 | self.set_path('AGS:') 40 | ENDPROC 41 | 42 | PROC set_path(path:PTR TO CHAR) OF agsnav 43 | IF self.path THEN DisposeLink(self.path) 44 | self.path := String(StrLen(path) + 1) 45 | StrCopy(self.path, path) 46 | ENDPROC 47 | 48 | PROC end() OF agsnav 49 | self.clear(0) 50 | IF self.path THEN DisposeLink(self.path) 51 | ENDPROC 52 | 53 | -> Clear the current navigator and allocate enough space for num_reserve items. 54 | PROC clear(num_reserve:LONG) OF agsnav 55 | DEF i 56 | DEF item:PTR TO agsnav_item 57 | ->DEF name:PTR TO CHAR 58 | 59 | IF self.items 60 | FOR i := 0 TO self.num_items - 1 61 | item := self.items[i] 62 | DisposeLink(item.name) 63 | END item 64 | ENDFOR 65 | END self.items[self.reserved] 66 | ENDIF 67 | self.num_items := 0 68 | 69 | IF num_reserve 70 | IF self.depth THEN INC num_reserve 71 | NEW self.items[num_reserve] 72 | self.reserved := num_reserve 73 | IF self.depth THEN self.add_item('..', AGSNAV_TYPE_DIR) 74 | ENDIF 75 | ENDPROC 76 | 77 | PROC compare_items(item1:PTR TO agsnav_item, item2:PTR TO agsnav_item) 78 | IF item2.type > item1.type THEN RETURN 1 79 | IF item2.type < item1.type THEN RETURN -1 80 | ENDPROC str_compare(item1.name, item2.name) 81 | 82 | -> Insert the item into a sorted list, with directories before files. 83 | PROC add_item(name:PTR TO CHAR, type:LONG) OF agsnav 84 | DEF item:PTR TO agsnav_item 85 | DEF pos, found 86 | DEF i 87 | 88 | NEW item 89 | item.type := type 90 | item.name := String(StrLen(name) + 1) 91 | StrCopy(item.name, name) 92 | item.length := EstrLen(item.name) 93 | IF self.num_items = 0 94 | self.items[0] := item 95 | ELSE 96 | pos := 0 97 | found := FALSE 98 | WHILE found = FALSE 99 | IF pos >= self.num_items 100 | found := TRUE 101 | ELSEIF compare_items(self.items[pos], item) <= 0 102 | found := TRUE 103 | ELSE 104 | INC pos 105 | ENDIF 106 | ENDWHILE 107 | IF pos < self.num_items 108 | FOR i := self.num_items TO pos STEP -1 109 | self.items[i] := self.items[i - 1] 110 | ENDFOR 111 | ENDIF 112 | self.items[pos] := item 113 | ENDIF 114 | self.num_items := self.num_items + 1 115 | ENDPROC 116 | 117 | PROC str_ends_with(str, suffix) 118 | DEF str_len 119 | DEF suffix_len 120 | 121 | str_len := StrLen(str) 122 | suffix_len := StrLen(suffix) 123 | IF str_len < suffix_len THEN RETURN FALSE 124 | ENDPROC InStr(str, suffix, str_len - suffix_len) <> -1 125 | 126 | -> Compare strings (case insensitive, custom symbol order) 127 | PROC str_compare(a, b) 128 | DEF lut:PTR TO CHAR 129 | WHILE a[] 130 | IF a[] <> b[] 131 | lut := {latin1_sorting_lut} 132 | IF lut[a[]] < lut[b[]] THEN RETURN 1 133 | RETURN -1 134 | ENDIF 135 | INC a 136 | INC b 137 | ENDWHILE 138 | IF b[] > 0 THEN RETURN 1 139 | RETURN 0 140 | ENDPROC 141 | 142 | -> Sorting table for str_compare() 143 | latin1_sorting_lut: 144 | CHAR $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 145 | CHAR $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 146 | CHAR $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0a,$0b,$0c,$0d,$0e,$0f 147 | CHAR $10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$88,$89,$8a,$8b,$8c,$8d 148 | CHAR $8e,$1a,$24,$26,$2a,$2c,$36,$38,$3a,$3c,$46,$48,$4a,$4c,$4e,$52 149 | CHAR $5c,$5e,$60,$62,$65,$67,$71,$73,$75,$77,$7c,$8f,$90,$91,$92,$93 150 | CHAR $94,$1b,$25,$27,$2b,$2d,$37,$39,$3b,$3d,$47,$49,$4b,$4d,$4f,$53 151 | CHAR $5d,$5f,$61,$63,$66,$68,$72,$74,$76,$78,$7d,$95,$96,$97,$98,$00 152 | CHAR $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 153 | CHAR $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 154 | CHAR $00,$99,$9a,$9b,$9c,$9d,$9e,$9f,$a0,$a1,$a2,$a3,$a4,$00,$a5,$a6 155 | CHAR $a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af,$b0,$b1,$b2,$b3,$b4,$b5,$b6 156 | CHAR $1c,$1e,$20,$22,$80,$7e,$82,$28,$2e,$30,$32,$34,$3e,$40,$42,$44 157 | CHAR $b7,$50,$54,$56,$58,$5a,$84,$b8,$86,$69,$6b,$6d,$6f,$79,$b9,$64 158 | CHAR $1d,$1f,$21,$23,$81,$7f,$83,$29,$2f,$31,$33,$35,$3f,$41,$43,$45 159 | CHAR $ba,$51,$55,$57,$59,$5b,$85,$bb,$87,$6a,$6c,$6e,$70,$7a,$bc,$7b 160 | 161 | 162 | -> Clear the current menu, read the current directory, and add all menu items. 163 | CONST BUFSIZE = 512 164 | PROC read_dir() OF agsnav HANDLE 165 | DEF lock = NIL:PTR TO filelock 166 | DEF eac = NIL:PTR TO exallcontrol 167 | DEF ead:PTR TO exalldata 168 | DEF buffer[BUFSIZE]:ARRAY OF CHAR 169 | DEF continue 170 | DEF error 171 | 172 | DEF first = NIL -> :PTR TO STRING 173 | DEF current = NIL -> :PTR TO STRING 174 | DEF num_items = 0 175 | DEF next = NIL -> :PTR TO STRING 176 | DEF should_add 177 | 178 | DEF name[30]:STRING 179 | DEF type 180 | 181 | -> Read the current directory with ExAll(). 182 | IF (lock := Lock(self.path, ACCESS_READ)) = FALSE 183 | Throw(AGSNAV_ERROR, AGSNAV_ERR_LOCK) 184 | ENDIF 185 | eac := AllocDosObject(DOS_EXALLCONTROL, NIL) 186 | eac.lastkey := 0 187 | eac.matchstring := NIL 188 | eac.matchfunc := NIL 189 | REPEAT 190 | continue := ExAll(lock, buffer, BUFSIZE, ED_TYPE, eac) 191 | error := IoErr() 192 | IF (continue = 0) AND (error <> ERROR_NO_MORE_ENTRIES) 193 | Throw(AGSNAV_ERROR, AGSNAV_ERR_EXALL) 194 | ENDIF 195 | IF eac.entries 196 | ead := buffer 197 | WHILE ead <> NIL 198 | -> Only add directories and files ending with .run. 199 | IF (ead.type > 0) AND str_ends_with(ead.name, '.ags') 200 | should_add := TRUE 201 | ELSEIF str_ends_with(ead.name, '.run') 202 | should_add := TRUE 203 | ELSE 204 | should_add := FALSE 205 | ENDIF 206 | IF should_add 207 | -> For each entry allocate a string for the name and set the 208 | -> first character to D for directories and F for files. 209 | next := String(StrLen(ead.name) + 1) 210 | IF ead.type < 0 211 | StrCopy(next, 'F') 212 | ELSE 213 | StrCopy(next, 'D') 214 | ENDIF 215 | StrAdd(next, ead.name) 216 | IF current = NIL 217 | current := next 218 | first := next 219 | ELSE 220 | Link(current, next) 221 | current := next 222 | ENDIF 223 | INC num_items 224 | ENDIF 225 | ead := ead.next 226 | ENDWHILE 227 | ENDIF 228 | UNTIL continue = FALSE 229 | 230 | self.clear(num_items) 231 | 232 | current := first 233 | WHILE current 234 | IF current[0] = "F" 235 | type := AGSNAV_TYPE_FILE 236 | StrCopy(name, current + 1, EstrLen(current) - 5) 237 | ELSE 238 | type := AGSNAV_TYPE_DIR 239 | StrCopy(name, current + 1, EstrLen(current) - 5) 240 | ENDIF 241 | self.add_item(name, type) 242 | next := Next(current) 243 | current := next 244 | ENDWHILE 245 | DisposeLink(current) 246 | 247 | EXCEPT DO 248 | IF eac THEN FreeDosObject(DOS_EXALLCONTROL, eac) 249 | IF lock THEN UnLock(lock) 250 | ReThrow() 251 | ENDPROC self.num_items 252 | -------------------------------------------------------------------------------- /benchmark.e: -------------------------------------------------------------------------------- 1 | OPT MODULE 2 | OPT EXPORT 3 | 4 | 5 | MODULE 'timer' 6 | MODULE 'devices/timer' 7 | MODULE 'exec/io' 8 | 9 | 10 | OBJECT benchmark 11 | tr:PTR TO timerequest 12 | timer_device_open:LONG 13 | num_marks:LONG 14 | mark_ctr:LONG 15 | start_time:PTR TO timeval 16 | end_time:PTR TO timeval 17 | ENDOBJECT 18 | 19 | PROC init(num_marks:LONG) OF benchmark 20 | IF OpenDevice('timer.device', UNIT_MICROHZ, self.tr, 0) = 0 21 | self.timer_device_open := TRUE 22 | timerbase := self.tr.io.device 23 | ELSE 24 | Raise("TIME") 25 | ENDIF 26 | self.num_marks := num_marks 27 | self.mark_ctr := 0 28 | NEW self.start_time 29 | NEW self.end_time[num_marks] 30 | ENDPROC 31 | 32 | PROC end() OF benchmark 33 | IF self.timer_device_open THEN CloseDevice(self.tr) 34 | END self.start_time 35 | END self.end_time[self.num_marks] 36 | ENDPROC 37 | 38 | -> Start the benchmark timer. 39 | PROC start() OF benchmark 40 | GetSysTime(self.start_time) 41 | ENDPROC 42 | 43 | -> Stop the benchmark timer. 44 | PROC mark() OF benchmark 45 | GetSysTime(self.end_time[self.mark_ctr]) 46 | self.mark_ctr := self.mark_ctr + 1 47 | ENDPROC 48 | 49 | -> Return the time delta as milliseconds. Overflows after around 24 days. 50 | PROC msecs(mark:LONG) OF benchmark 51 | DEF ms_start 52 | DEF ms_end 53 | 54 | ms_start := Mul(self.start_time.secs, 1000) + Div(self.start_time.micro, 1000) 55 | ms_end := Mul(self.end_time[mark].secs, 1000) + Div(self.end_time[mark].micro, 1000) 56 | ENDPROC ms_end - ms_start 57 | 58 | -> Return the time delta as microseconds. Overflows after 2147 seconds. 59 | PROC usecs(mark:LONG) OF benchmark 60 | DEF us_start 61 | DEF us_end 62 | 63 | us_start := Mul(self.start_time.secs, 1000000) + self.start_time.micro 64 | us_end := Mul(self.end_time[mark].secs, 1000000) + self.end_time[mark].micro 65 | ENDPROC us_end - us_start 66 | -------------------------------------------------------------------------------- /ilbmloader.e: -------------------------------------------------------------------------------- 1 | /* Class for loading ILBM files. */ 2 | 3 | 4 | OPT MODULE 5 | OPT PREPROCESS 6 | 7 | 8 | MODULE 'graphics/gfx' 9 | MODULE 'graphics/rastport' 10 | MODULE 'graphics/view' 11 | MODULE 'iffparse' 12 | MODULE 'libraries/iffparse' 13 | MODULE '*rgbcolor' 14 | 15 | 16 | EXPORT CONST ILBM_ERROR = "ILBM" 17 | EXPORT ENUM ERR_IFF_LIBRARY = 1, 18 | ERR_IFF_ALLOCIFF, 19 | ERR_IFF_ALREADYOPEN, 20 | ERR_IFF_OPENFILE, 21 | ERR_IFF_OPENIFF, 22 | ERR_IFF_ALLOCRASTER 23 | 24 | EXPORT PROC ilbm_strerror(num:LONG) IS ListItem([ 25 | 'ERR_IFF_LIBRARY', 26 | 'ERR_IFF_ALLOCIFF', 27 | 'ERR_IFF_ALREADYOPEN', 28 | 'ERR_IFF_OPENFILE', 29 | 'ERR_IFF_OPENIFF', 30 | 'ERR_IFF_ALLOCRASTER' 31 | ], num - 1) 32 | 33 | ENUM COMPRESSION_NONE, COMPRESSION_BYTERUN1 34 | ENUM MASK_NONE, MASK_HASMASK, MASK_HASTRANSPARENTCOLOR, MASK_LASSO 35 | 36 | OBJECT ilbm_bmhd 37 | w:INT 38 | h:INT 39 | x:INT 40 | y:INT 41 | nplanes:CHAR 42 | masking:CHAR 43 | compression:CHAR 44 | ->pad:CHAR 45 | transparentcolor:INT 46 | xaspect:CHAR 47 | yaspect:CHAR 48 | pagewidth:INT 49 | pageheight:INT 50 | ENDOBJECT 51 | 52 | EXPORT OBJECT ilbmloader 53 | iff:PTR TO iffhandle 54 | 55 | width:INT 56 | last_width:INT 57 | height:INT 58 | last_height:INT 59 | depth:CHAR 60 | last_depth:CHAR 61 | masking:CHAR 62 | compression:CHAR 63 | 64 | ncolors:LONG 65 | colormap:PTR TO rgbcolor 66 | 67 | mode:LONG 68 | 69 | is_open:INT 70 | 71 | bm:PTR TO bitmap 72 | planeptr:PTR TO CHAR 73 | ENDOBJECT 74 | 75 | EXPORT PROC init() OF ilbmloader 76 | DEF iff:PTR TO iffhandle 77 | 78 | -> Read ILBM image using iffparse.library. 79 | IF (iffparsebase := OpenLibrary('iffparse.library', 0)) = NIL THEN Throw(ILBM_ERROR, ERR_IFF_LIBRARY) 80 | -> Allocate IFF handle and prepare it for DOS streams. 81 | IF (iff := AllocIFF()) = NIL THEN Throw(ILBM_ERROR, ERR_IFF_ALLOCIFF) 82 | self.iff := iff 83 | InitIFFasDOS(self.iff) 84 | ENDPROC 85 | 86 | PROC end() OF ilbmloader 87 | IF self.is_open THEN self.close() 88 | IF self.iff THEN FreeIFF(self.iff) 89 | IF iffparsebase THEN CloseLibrary(iffparsebase) 90 | IF self.bm THEN self.free_in_memory_bitmap() 91 | ENDPROC 92 | 93 | PROC free_in_memory_bitmap() OF ilbmloader 94 | IF KickVersion(39) 95 | FreeBitMap(self.bm) 96 | ELSE 97 | FreeRaster(self.planeptr, self.last_width * self.last_depth, self.last_height) 98 | END self.bm 99 | ENDIF 100 | 101 | self.bm := NIL 102 | ENDPROC 103 | 104 | -> Open an IFF image for reading. 105 | EXPORT PROC open(name:PTR TO CHAR) OF ilbmloader 106 | DEF fh 107 | 108 | IF self.is_open THEN Throw(ILBM_ERROR, ERR_IFF_ALREADYOPEN) 109 | 110 | IF (fh := Open(name, OLDFILE)) = NIL 111 | -> THEN Throw(ILBM_ERROR, ERR_IFF_OPENFILE) 112 | RETURN FALSE 113 | ENDIF 114 | self.iff.stream := fh 115 | 116 | IF OpenIFF(self.iff, IFFF_READ) 117 | Close(fh) 118 | self.iff.stream := NIL 119 | Throw(ILBM_ERROR, ERR_IFF_OPENIFF) 120 | ENDIF 121 | 122 | self.is_open := TRUE 123 | ENDPROC TRUE 124 | 125 | EXPORT PROC close() OF ilbmloader 126 | IF self.is_open 127 | IF self.colormap THEN END self.colormap[self.ncolors] 128 | Close(self.iff.stream) 129 | CloseIFF(self.iff) 130 | self.is_open := FALSE 131 | ENDIF 132 | ENDPROC 133 | 134 | -> Parse image header. 135 | EXPORT PROC parse_header() OF ilbmloader 136 | DEF bmhd:PTR TO ilbm_bmhd 137 | DEF sp:PTR TO storedproperty 138 | DEF cmap:PTR TO CHAR 139 | DEF ret 140 | DEF i 141 | 142 | PropChunk(self.iff, "ILBM", "BMHD") 143 | PropChunk(self.iff, "ILBM", "CMAP") 144 | PropChunk(self.iff, "ILBM", "CAMG") 145 | StopChunk(self.iff, "ILBM", "BODY") 146 | ret := ParseIFF(self.iff, IFFPARSE_SCAN) 147 | ->PrintF('ParseIFF returned \d\n', ret) 148 | 149 | IF (sp := FindProp(self.iff, "ILBM", "BMHD")) = NIL THEN RETURN FALSE 150 | -> Throw(ILBM_ERROR, ERR_IFF_NOBMHD) 151 | bmhd := sp.data 152 | ->PrintF('w = \d, h = \d\n', bmhd.w, bmhd.h) 153 | ->PrintF('x = \d, y = \d\n', bmhd.x, bmhd.y) 154 | ->PrintF('nplanes = \d\n', bmhd.nplanes) 155 | ->PrintF('masking = \d\n', bmhd.masking) 156 | ->PrintF('compression = \d\n', bmhd.compression) 157 | ->PrintF('transparentcolor = \d\n', bmhd.transparentcolor) 158 | ->PrintF('xaspect = \d, yaspect = \d\n', bmhd.xaspect, bmhd.yaspect) 159 | ->PrintF('pagewidth = \d, pageheight = \d\n', bmhd.pagewidth, bmhd.pageheight) 160 | self.width := bmhd.w 161 | self.height := bmhd.h 162 | self.depth := bmhd.nplanes 163 | self.masking := bmhd.masking 164 | self.compression := bmhd.compression 165 | ->self.transparentcolor := bmhd.transparentcolor 166 | 167 | -> If this is the first image we've loaded, then just initialise the 168 | -> last width and height properties with the values from the first image. 169 | IF self.bm = NIL 170 | self.last_width := bmhd.w 171 | self.last_height := bmhd.h 172 | self.last_depth := bmhd.nplanes 173 | ENDIF 174 | 175 | IF (sp := FindProp(self.iff, "ILBM", "CMAP")) <> NIL 176 | self.ncolors := Min(sp.size / 3, Shl(1, self.depth)) 177 | NEW self.colormap[self.ncolors] 178 | cmap := sp.data 179 | FOR i := 0 TO self.ncolors - 1 180 | self.colormap[i].r := cmap[]++ 181 | self.colormap[i].g := cmap[]++ 182 | self.colormap[i].b := cmap[]++ 183 | ENDFOR 184 | ENDIF 185 | 186 | IF (sp := FindProp(self.iff, "ILBM", "CAMG")) <> NIL 187 | self.mode := Long(sp.data) 188 | ENDIF 189 | ENDPROC TRUE 190 | 191 | -> Load colormap into viewport. 192 | EXPORT PROC load_cmap(vport:PTR TO viewport, max_colors:LONG) OF ilbmloader 193 | DEF colors32:PTR TO LONG 194 | DEF colors4:PTR TO INT 195 | DEF i 196 | DEF offset 197 | DEF ncolors 198 | 199 | ncolors := Min(self.ncolors, max_colors) 200 | IF KickVersion(39) 201 | NEW colors32[(ncolors * 3) + 2] 202 | colors32[0] := Shl(ncolors, 16) 203 | colors32[(ncolors * 3) + 1] := 0 204 | FOR i := 0 TO ncolors - 1 205 | offset := (i * 3) + 1 206 | colors32[offset] := Shl(self.colormap[i].r, 24) 207 | colors32[offset + 1] := Shl(self.colormap[i].g, 24) 208 | colors32[offset + 2] := Shl(self.colormap[i].b, 24) 209 | ENDFOR 210 | LoadRGB32(vport, colors32) 211 | END colors32[(ncolors * 3) + 2] 212 | ELSE 213 | NEW colors4[ncolors] 214 | FOR i := 0 TO ncolors - 1 215 | colors4[i] := Shl(self.colormap[i].r AND $f0, 4) OR (self.colormap[i].g AND $f0) OR Shr(self.colormap[i].b, 4) 216 | ENDFOR 217 | LoadRGB4(vport, colors4, ncolors) 218 | END colors4[ncolors] 219 | ENDIF 220 | ENDPROC 221 | 222 | -> Load image body into raster port at x, y. 223 | EXPORT PROC load_body(rport:PTR TO rastport, x:LONG, y:LONG) OF ilbmloader 224 | DEF ctxnode:PTR TO contextnode 225 | DEF cdata:PTR TO CHAR 226 | DEF i 227 | 228 | IF self.bm AND (self.width <> self.last_width OR self.height <> self.last_height OR self.depth <> self.last_depth) 229 | -> We have a preallocated in-memory bitmap, but it's not the right size or 230 | -> depth for this new image, so free it so that it'll be reallocated below 231 | self.free_in_memory_bitmap() 232 | ENDIF 233 | 234 | IF self.bm = NIL 235 | -> Allocate an in-memory bitmap to be used for blitting. 236 | IF KickVersion(39) 237 | self.bm := AllocBitMap(self.width, self.height, self.depth, BMF_INTERLEAVED, rport) 238 | ELSE 239 | -> For Kickstart 2.0 we have to allocate and initialize the bitmap manually. 240 | NEW self.bm 241 | -> Width has to be multiplied by depth to match AllocBitMap. 242 | InitBitMap(self.bm, self.depth, self.width * self.depth, self.height) 243 | -> Width also has to be multiplied by depth here too. 244 | self.planeptr := AllocRaster(self.width * self.depth, self.height) 245 | IF self.planeptr = NIL 246 | END self.bm 247 | Throw(ILBM_ERROR, ERR_IFF_ALLOCRASTER) 248 | ENDIF 249 | -> Assign each line to the bm.planes. This emulates BMF_INTERLEAVED. 250 | FOR i := 0 TO self.bm.depth - 1 251 | self.bm.planes[i] := self.planeptr + ((self.bm.bytesperrow / self.bm.depth) * i) 252 | ENDFOR 253 | ENDIF 254 | ENDIF 255 | 256 | -> Keep a copy of the width, height and depth of this image 257 | -> which will be checked and used if we load another image 258 | self.last_width := self.width 259 | self.last_height := self.height 260 | self.last_depth := self.depth 261 | 262 | /* 263 | PrintF('bm = $\h[08]\n', bm) 264 | PrintF('bm.bytesperrow = \d\n', bm.bytesperrow) 265 | PrintF('bm.rows = \d\n', bm.rows) 266 | PrintF('bm.flags = \d\n', bm.flags) 267 | PrintF('bm.depth = \d\n', bm.depth) 268 | FOR i := 0 TO bm.depth - 1 269 | PrintF('bm.planes[\d] = $\h[08]\n', i, bm.planes[i]) 270 | ENDFOR 271 | */ 272 | -> Load the chunk data. 273 | ctxnode := CurrentChunk(self.iff) 274 | 275 | -> Check if image is RLE compressed 276 | IF self.compression = COMPRESSION_BYTERUN1 277 | NEW cdata[ctxnode.size] 278 | ReadChunkBytes(self.iff, cdata, ctxnode.size) 279 | -> Unpack and blit into raster port. 280 | self.ilbm_body_unpack(cdata, self.bm, rport, x, y) 281 | -> Free chunk data. 282 | END cdata[ctxnode.size] 283 | ELSE 284 | -> Read the data directly into the bitmap (image must not be compressed) 285 | ReadChunkBytes(self.iff, self.bm.planes[0], ctxnode.size) 286 | -> Blit into raster port. 287 | BltBitMapRastPort(self.bm, 0, 0, rport, x, y, self.width, self.height, $0c0) 288 | ENDIF 289 | ENDPROC 290 | 291 | -> Optimized loop for decoding ilbm body. 292 | PROC ilbm_body_unpack(cdata:PTR TO CHAR, 293 | bm:PTR TO bitmap, 294 | rport:PTR TO rastport, 295 | x:LONG, 296 | y:LONG) OF ilbmloader 297 | DEF i:REG 298 | DEF buf_ptr:REG PTR TO CHAR 299 | DEF cmd:REG 300 | DEF byte:REG 301 | DEF line_buffer = NIL:PTR TO CHAR 302 | DEF line 303 | DEF bytes_per_line 304 | DEF bytes_left 305 | DEF file_depth 306 | 307 | -> If there is a bitmap mask there's an extra bitplane for each line. 308 | file_depth := self.depth + (IF self.masking = MASK_HASMASK THEN 1 ELSE 0) 309 | bytes_per_line := Shl(Shr(self.width + 15, 4), 1) * file_depth 310 | -> Allocate a buffer to hold one unpacked line. 311 | NEW line_buffer[bytes_per_line] 312 | -> Unpack image line by line. 313 | FOR line := 0 TO self.height - 1 314 | buf_ptr := line_buffer 315 | bytes_left := bytes_per_line 316 | REPEAT 317 | cmd := cdata[]++ 318 | SELECT 256 OF cmd 319 | CASE $80 320 | -> $80 bytes are ignored. 321 | CASE $81 TO $ff 322 | -> -1 to -127 => repeat next byte -n + 1 times 323 | byte := cdata[]++ 324 | cmd := 256 - cmd 325 | FOR i := 0 TO cmd 326 | buf_ptr[]++ := byte 327 | ENDFOR 328 | bytes_left := bytes_left - (cmd + 1) 329 | DEFAULT 330 | FOR i := 0 TO cmd 331 | buf_ptr[]++ := cdata[]++ 332 | ENDFOR 333 | bytes_left := bytes_left - (cmd + 1) 334 | ENDSELECT 335 | UNTIL bytes_left < 1 336 | 337 | CopyMem(line_buffer, bm.planes[0] + (bm.bytesperrow * line), bm.bytesperrow) 338 | ENDFOR 339 | 340 | -> Now that the bitmap is completely unpacked into memory, blit it to the raster port 341 | BltBitMapRastPort(bm, 0, 0, rport, x, y, self.width, self.height, $0c0) 342 | 343 | END line_buffer 344 | ENDPROC 345 | -------------------------------------------------------------------------------- /imgtoiff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import optparse 6 | try: 7 | from PIL import Image 8 | except ImportError: 9 | print >>sys.stderr, "Pillow is required, install with sudo pip install pillow" 10 | sys.exit(1) 11 | import array 12 | import math 13 | import struct 14 | import cStringIO as StringIO 15 | import tempfile 16 | import shutil 17 | import os.path 18 | import subprocess 19 | 20 | 21 | def packbits(data): 22 | # https://github.com/kmike/packbits 23 | # 24 | # Copyright (c) 2013 Mikhail Korobov 25 | # 26 | # Permission is hereby granted, free of charge, to any person obtaining a copy 27 | # of this software and associated documentation files (the "Software"), to deal 28 | # in the Software without restriction, including without limitation the rights 29 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 30 | # copies of the Software, and to permit persons to whom the Software is 31 | # furnished to do so, subject to the following conditions: 32 | # 33 | # The above copyright notice and this permission notice shall be included in 34 | # all copies or substantial portions of the Software. 35 | # 36 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 37 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 38 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 39 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 40 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 41 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 42 | # THE SOFTWARE. 43 | 44 | if len(data) == 0: 45 | return data 46 | 47 | if len(data) == 1: 48 | return b'\x00' + data 49 | 50 | data = bytearray(data) 51 | 52 | result = bytearray() 53 | buf = bytearray() 54 | pos = 0 55 | repeat_count = 0 56 | MAX_LENGTH = 127 57 | 58 | # we can safely start with RAW as empty RAW sequences 59 | # are handled by finish_raw() 60 | state = 'RAW' 61 | 62 | def finish_raw(): 63 | if len(buf) == 0: 64 | return 65 | result.append(len(buf)-1) 66 | result.extend(buf) 67 | buf[:] = bytearray() 68 | 69 | def finish_rle(): 70 | result.append(256-(repeat_count - 1)) 71 | result.append(data[pos]) 72 | 73 | while pos < len(data)-1: 74 | current_byte = data[pos] 75 | 76 | if data[pos] == data[pos+1]: 77 | if state == 'RAW': 78 | # end of RAW data 79 | finish_raw() 80 | state = 'RLE' 81 | repeat_count = 1 82 | elif state == 'RLE': 83 | if repeat_count == MAX_LENGTH: 84 | # restart the encoding 85 | finish_rle() 86 | repeat_count = 0 87 | # move to next byte 88 | repeat_count += 1 89 | 90 | else: 91 | if state == 'RLE': 92 | repeat_count += 1 93 | finish_rle() 94 | state = 'RAW' 95 | repeat_count = 0 96 | elif state == 'RAW': 97 | if len(buf) == MAX_LENGTH: 98 | # restart the encoding 99 | finish_raw() 100 | 101 | buf.append(current_byte) 102 | 103 | pos += 1 104 | 105 | if state == 'RAW': 106 | buf.append(data[pos]) 107 | finish_raw() 108 | else: 109 | repeat_count += 1 110 | finish_rle() 111 | 112 | return bytes(result) 113 | 114 | def unpackbits(data): 115 | data = bytearray(data) # <- python 2/3 compatibility fix 116 | result = bytearray() 117 | pos = 0 118 | while pos < len(data): 119 | header_byte = data[pos] 120 | if header_byte > 127: 121 | header_byte -= 256 122 | pos += 1 123 | 124 | if 0 <= header_byte <= 127: 125 | result.extend(data[pos:pos+header_byte+1]) 126 | pos += header_byte+1 127 | elif header_byte == -128: 128 | pass 129 | else: 130 | result.extend([data[pos]] * (1 - header_byte)) 131 | pos += 1 132 | 133 | return bytes(result) 134 | 135 | 136 | # Converted from lz78.c by Ray/tSCc. 137 | LZ78_BITS = 12 138 | LZ78_HASHING_SHIFT = LZ78_BITS - 8 139 | LZ78_LALIGN = 32 - LZ78_BITS 140 | LZ78_MAX_VALUE = (1 << LZ78_BITS) - 1 141 | LZ78_MAX_CODE = LZ78_MAX_VALUE - 1 142 | if LZ78_BITS == 14: 143 | LZ78_TABLE_SIZE = 18041 144 | elif LZ78_BITS == 13: 145 | LZ78_TABLE_SIZE = 9029 146 | elif LZ78_BITS == 12: 147 | LZ78_TABLE_SIZE = 5021 148 | 149 | code_value = array.array("i", (0 for i in xrange(LZ78_TABLE_SIZE))) 150 | prefix_code = array.array("i", (0 for i in xrange(LZ78_TABLE_SIZE))) 151 | append_character = array.array("B", (0 for i in xrange(LZ78_TABLE_SIZE))) 152 | 153 | output_bit_count = 0 154 | output_bit_buffer = 0 155 | 156 | def output_code(outp, code): 157 | global output_bit_count 158 | global output_bit_buffer 159 | 160 | output_bit_buffer |= code << (LZ78_LALIGN - output_bit_count) 161 | output_bit_count += LZ78_BITS 162 | while output_bit_count >= 8: 163 | outp.write(chr((output_bit_buffer >> 24) & 0xff)) 164 | output_bit_buffer <<= 8 165 | output_bit_count -= 8 166 | 167 | def find_match(hash_prefix, hash_character): 168 | global code_value 169 | global prefix_code 170 | global append_character 171 | 172 | index = (hash_character << LZ78_HASHING_SHIFT) ^ hash_prefix 173 | if index == 0: 174 | offset = 1 175 | else: 176 | offset = LZ78_TABLE_SIZE - index 177 | while True: 178 | if code_value[index] == -1: 179 | return index 180 | if (prefix_code[index] == hash_prefix) and (append_character[index] == hash_character): 181 | return index 182 | index -= offset 183 | if index < 0: 184 | index += LZ78_TABLE_SIZE 185 | 186 | def lz78pack(data): 187 | global code_value 188 | global prefix_code 189 | global append_character 190 | global output_bit_count 191 | global output_bit_buffer 192 | 193 | output_bit_count = 0 194 | output_bit_buffer = 0 195 | for i in xrange(LZ78_TABLE_SIZE): 196 | code_value[i] = -1 197 | prefix_code[i] = 0 198 | append_character[i] = 0 199 | 200 | inp = StringIO.StringIO(data) 201 | outp = StringIO.StringIO() 202 | 203 | outp.write(struct.pack(">L", len(data))) 204 | 205 | next_code = 256 206 | 207 | string_code = ord(inp.read(1)) 208 | while True: 209 | c = inp.read(1) 210 | if c == "": 211 | break 212 | character = ord(c) 213 | index = find_match(string_code, character) 214 | if code_value[index] != -1: 215 | string_code = code_value[index] 216 | else: 217 | if next_code <= LZ78_MAX_CODE: 218 | code_value[index] = next_code 219 | next_code += 1 220 | prefix_code[index] = string_code 221 | append_character[index] = character 222 | output_code(outp, string_code) 223 | string_code = character 224 | 225 | output_code(outp, string_code) 226 | output_code(outp, LZ78_MAX_VALUE) 227 | output_code(outp, 0) 228 | 229 | return outp.getvalue() 230 | 231 | 232 | def iff_chunk(id, *args): 233 | chunk_len = 0 234 | data = list() 235 | for d in args: 236 | if len(d) & 1: 237 | data.append(d + "\x00") 238 | chunk_len += len(d) + 1 239 | else: 240 | data.append(d) 241 | chunk_len += len(d) 242 | return id + struct.pack(">L", chunk_len) + "".join(data) 243 | 244 | def zeros(len): 245 | for i in xrange(len): 246 | yield 0 247 | 248 | def create_header(width, height, palette, mode, pack): 249 | depth = int(math.ceil(math.log(len(palette), 2))) 250 | # Create IFF chunks for header. 251 | bmhd = iff_chunk("BMHD", struct.pack(">HHHHBBBxHBBHH", 252 | width, # w:INT 253 | height, # h:INT 254 | 0, # x:INT 255 | 0, # y:INT 256 | depth, # nplanes:CHAR 257 | 0, # masking:CHAR 258 | pack, # compression:CHAR 259 | # pad:CHAR 260 | 0, # transparentcolor:INT 261 | 60, # xaspect:CHAR 262 | 60, # yaspect:CHAR 263 | width, # pagewidth:INT 264 | height, # pageheight:INT 265 | )) 266 | 267 | cmap = iff_chunk("CMAP", "".join(struct.pack("BBB", r, g, b) for (r, g, b) in palette)) 268 | 269 | if mode is not None: 270 | camg = iff_chunk("CAMG", struct.pack(">L", mode)) 271 | else: 272 | camg = "" 273 | 274 | return depth, bmhd, cmap, camg 275 | 276 | def convert_planar(width, height, depth, pixels): 277 | # Calculate dimensions. 278 | plane_width = ((width + 15) / 16) * 16 279 | bpr = plane_width / 8 280 | plane_size = bpr * height 281 | 282 | # Convert image to planar bitmap. 283 | planes = tuple(array.array("B", (0 for i in xrange(plane_size))) for j in xrange(depth)) 284 | for y in xrange(height): 285 | rowoffset = y * bpr 286 | for x in xrange(width): 287 | offset = rowoffset + x / 8 288 | xmod = 7 - (x & 7) 289 | p = pixels[x, y] 290 | for plane in xrange(depth): 291 | planes[plane][offset] |= ((p >> plane) & 1) << xmod 292 | 293 | return bpr, planes 294 | 295 | def create_ilbm(width, height, pixels, palette, mode, pack): 296 | # Get header. 297 | depth, bmhd, cmap, camg = create_header(width, height, palette, mode, pack) 298 | # Get planar bitmap. 299 | bpr, planes = convert_planar(width, height, depth, pixels) 300 | # Create interleaved bitmap. 301 | rows = list() 302 | for y in xrange(height): 303 | for row in (planes[plane][y * bpr:y * bpr + bpr].tostring() for plane in xrange(depth)): 304 | rows.append(row) 305 | 306 | if pack == 0: # No compression. 307 | body = iff_chunk("BODY", "".join(r for r in rows)) 308 | elif pack == 1: # Packbits. 309 | body = iff_chunk("BODY", "".join(packbits(r) for r in rows)) 310 | 311 | form = iff_chunk("FORM", "ILBM", bmhd, cmap, camg, body) 312 | return form 313 | 314 | def create_acbm(width, height, pixels, palette, mode, pack): 315 | # Get header. 316 | depth, bmhd, cmap, camg = create_header(width, height, palette, mode, pack) 317 | # Get planar bitmap. 318 | bpr, planes = convert_planar(width, height, depth, pixels) 319 | 320 | if pack == 0: # No compression. 321 | abit = iff_chunk("ABIT", "".join(p.tostring() for p in planes)) 322 | elif pack == 1: # Packbits. 323 | abit = iff_chunk("ABIT", "".join(packbits(p.tostring()) for p in planes)) 324 | elif pack == 78: # LZ78 by Ray of tSCc. 325 | abit = iff_chunk("ABIT", lz78pack("".join(p.tostring() for p in planes))) 326 | 327 | form = iff_chunk("FORM", "ACBM", bmhd, cmap, camg, abit) 328 | return form 329 | 330 | 331 | # Spatial Color Quantization by Derrick Coetzee. 332 | def scolorq(image, max_colors, mode, dither): 333 | height = image.size[1] 334 | width = image.size[0] 335 | 336 | tempdir = tempfile.mkdtemp() 337 | try: 338 | if image.format != "RGB": 339 | image = image.convert("RGB") 340 | 341 | rgb_path = os.path.join(tempdir, "original.rgb") 342 | with open(rgb_path, "wb") as f: 343 | f.write(image.tostring()) 344 | 345 | quant_path = os.path.join(tempdir, "quantized.rgb") 346 | 347 | scq_path = os.path.join(os.path.dirname(__file__), "spatial_color_quant") 348 | try: 349 | subprocess.check_call((scq_path, 350 | rgb_path, 351 | "%d" % width, 352 | "%d" % height, 353 | "%d" % max_colors, 354 | quant_path, 355 | "0.7" if dither else "0.0000001", 356 | "3")) 357 | except OSError: 358 | sys.exit("Requires spatial_color_quant:\n\n" 359 | " http://www.cs.berkeley.edu/~dcoetzee/downloads/scolorq/") 360 | 361 | with open(quant_path) as f: 362 | qimage = Image.fromstring("RGB", image.size, f.read()) 363 | finally: 364 | shutil.rmtree(tempdir) 365 | return qimage.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE, colors=max_colors) 366 | 367 | AGA = 1 368 | OCS = 2 369 | 370 | def main(argv): 371 | p = optparse.OptionParser() 372 | p.set_usage("""Usage: %prog [options] infile outfile.iff""") 373 | p.add_option("-v", "--verbose", action="store_true", help="Verbose output.") 374 | p.add_option("-o", "--ocs", action="store_true", help="OCS (4 bits per channel).") 375 | p.add_option("-a", "--aga", action="store_true", help="AGA (8 bits per channel).") 376 | p.add_option("-s", "--scale", action="store", nargs=2, type="int", help="Scale to width.") 377 | p.add_option("-c", "--colors", action="store", type="int", help="Max number of colors.") 378 | p.add_option("-d", "--dither", action="store_true", help="Dither when resampling.") 379 | p.add_option("-m", "--mode", action="store", type="int", help="Amiga display mode ID.") 380 | p.add_option("-p", "--pack", action="store", type="int", default=None, help="Select compression algorithm.") 381 | p.add_option("-f", "--format", action="store", default="ILBM", help="ILBM or ACBM.") 382 | p.add_option("-q", "--quant", action="store", default="adaptive", help="'adaptive' (fast) or 'spatial' (best).") 383 | options, argv = p.parse_args(argv) 384 | if len(argv) != 3: 385 | print >>sys.stderr, p.get_usage() 386 | return 1 387 | 388 | # Argument parsing. 389 | 390 | infile = argv[1] 391 | outfile = argv[2] 392 | 393 | if options.ocs: 394 | mode = OCS 395 | max_colors = 32 396 | else: 397 | mode = AGA 398 | max_colors = 256 399 | 400 | if options.scale: 401 | new_size = options.scale 402 | else: 403 | new_size = None 404 | 405 | if options.colors: 406 | if options.colors < 2 or options.colors > max_colors: 407 | print >>sys.stderr, "Colors should be between 2 and %d" % max_colors 408 | return 1 409 | max_colors = options.colors 410 | 411 | if options.dither: 412 | dither_method = Image.FLOYDSTEINBERG 413 | else: 414 | dither_method = Image.NONE 415 | 416 | if options.pack is None: 417 | if options.format.upper() == "ILBM": 418 | pack = 1 419 | else: 420 | pack = 0 421 | else: 422 | pack = options.pack 423 | 424 | # Image conversion. 425 | 426 | im = Image.open(infile) 427 | #print im.format, im.size, im.mode 428 | if new_size is None: 429 | new_size = im.size 430 | if (new_size != im.size) or (im.format not in ("P", "L")): 431 | # Convert to RGB. 432 | rgb = im.convert("RGB") 433 | # Resize if needed. 434 | if new_size != im.size: 435 | rgb = rgb.resize(new_size, Image.ANTIALIAS) 436 | # Simulate 12bpp for OCS, this reduces the input load for the 437 | # quantizer. 438 | if mode == OCS: 439 | pixels = rgb.load() 440 | for y in xrange(rgb.size[1]): 441 | for x in xrange(rgb.size[0]): 442 | r, g, b = pixels[x, y] 443 | r = (r & 0xf0) | (r >> 4) 444 | g = (g & 0xf0) | (g >> 4) 445 | b = (b & 0xf0) | (b >> 4) 446 | pixels[x, y] = (r, g, b) 447 | # Convert back to palette mode. 448 | if options.quant == "spatial": 449 | new_image = scolorq(rgb, max_colors, mode, options.dither) 450 | else: 451 | new_image = rgb.convert("P", 452 | dither=dither_method, 453 | palette=Image.ADAPTIVE, 454 | colors=max_colors) 455 | elif len(im.getcolors()) > max_colors: 456 | if options.quant == "spatial": 457 | new_image = scolorq(rgb, max_colors, mode, options.dither) 458 | else: 459 | new_image = im.convert("P", 460 | dither=dither_method, 461 | palette=Image.ADAPTIVE, 462 | colors=max_colors) 463 | else: 464 | new_image = im.copy() 465 | 466 | palette = list() 467 | p = new_image.im.getpalette("RGB") 468 | for i in xrange(min(max_colors, len(new_image.getcolors()))): 469 | r = ord(p[i * 3]) 470 | g = ord(p[i * 3 + 1]) 471 | b = ord(p[i * 3 + 2]) 472 | if mode == OCS: 473 | palette.append(((r & 0xf0) | (r >> 4), 474 | (g & 0xf0) | (g >> 4), 475 | (b & 0xf0) | (b >> 4))) 476 | else: 477 | palette.append((r, g, b)) 478 | 479 | pixels = new_image.load() 480 | width, height = new_image.size 481 | with open(outfile, "wb") as f: 482 | if options.format.upper() == "ILBM": 483 | f.write(create_ilbm(width, height, pixels, palette, options.mode, pack)) 484 | elif options.format.upper() == "ACBM": 485 | f.write(create_acbm(width, height, pixels, palette, options.mode, pack)) 486 | else: 487 | print >>sys.stderr, "Unsupported format" 488 | return 1 489 | 490 | return 0 491 | 492 | 493 | if __name__ == '__main__': 494 | sys.exit(main(sys.argv)) 495 | 496 | -------------------------------------------------------------------------------- /palfade.e: -------------------------------------------------------------------------------- 1 | /* Module for fading palettes. */ 2 | 3 | 4 | OPT MODULE 5 | OPT PREPROCESS 6 | 7 | 8 | ->MODULE 'graphics/gfx' 9 | ->MODULE 'graphics/rastport' 10 | MODULE 'graphics/view' 11 | MODULE '*rgbcolor' 12 | 13 | 14 | -> Fade viewport palette to black. 15 | EXPORT PROC fade_out_vport(vport:PTR TO viewport, max_colors:LONG, steps:LONG) 16 | DEF ncolors 17 | DEF ncolors32 18 | DEF color32 = NIL:PTR TO LONG 19 | DEF fade32 = NIL:PTR TO LONG 20 | DEF color4 = NIL:PTR TO INT 21 | DEF fade4 = NIL:PTR TO INT 22 | DEF i 23 | DEF step 24 | DEF v 25 | DEF r, g, b 26 | 27 | ncolors := Min(vport.colormap.count, max_colors) 28 | IF KickVersion(39) 29 | ncolors32 := (ncolors * 3) + 2 30 | NEW color32[ncolors32] 31 | NEW fade32[ncolors32] 32 | GetRGB32(vport.colormap, 0, ncolors, color32 + 4) 33 | color32[0] := Shl(ncolors, 16) 34 | fade32[0] := color32[0] 35 | FOR step := steps - 1 TO 0 STEP -1 36 | FOR i := 0 TO (ncolors * 3) - 1 37 | v := Shr(color32[i + 1], 24) AND $ff 38 | fade32[i + 1] := Mul(((v * step) / steps), $01010101) 39 | ENDFOR 40 | WaitTOF() 41 | LoadRGB32(vport, fade32) 42 | ENDFOR 43 | ELSE 44 | NEW color4[ncolors] 45 | NEW fade4[ncolors] 46 | FOR i := 0 TO ncolors - 1 47 | color4[i] := GetRGB4(vport.colormap, i) 48 | ENDFOR 49 | FOR step := steps - 1 TO 0 STEP -1 50 | FOR i := 0 TO ncolors - 1 51 | r := ((Shr(color4[i], 8) AND $0f) * step) / steps 52 | g := ((Shr(color4[i], 4) AND $0f) * step) / steps 53 | b := ((color4[i] AND $0f) * step) / steps 54 | fade4[i] := Shl(r, 8) OR Shl(g, 4) OR b 55 | ENDFOR 56 | WaitTOF() 57 | LoadRGB4(vport, fade4, ncolors) 58 | ENDFOR 59 | ENDIF 60 | 61 | IF color32 THEN END color32[ncolors32] 62 | IF fade32 THEN END fade32[ncolors32] 63 | IF color4 THEN END color4[ncolors] 64 | IF fade4 THEN END fade4[ncolors] 65 | ENDPROC 66 | 67 | -> Fade in new palette. 68 | EXPORT PROC fade_in_vport(colors:PTR TO rgbcolor, vport:PTR TO viewport, max_colors:LONG, steps:LONG) 69 | DEF ncolors 70 | DEF ncolors32 71 | DEF fade32 = NIL:PTR TO LONG 72 | DEF fade4 = NIL:PTR TO INT 73 | DEF i 74 | DEF step 75 | DEF r, g, b 76 | 77 | ncolors := Min(vport.colormap.count, max_colors) 78 | IF KickVersion(39) 79 | ncolors32 := (ncolors * 3) + 2 80 | NEW fade32[ncolors32] 81 | fade32[0] := Shl(ncolors, 16) 82 | FOR step := 1 TO steps 83 | FOR i := 0 TO ncolors - 1 84 | fade32[(i * 3) + 1] := Mul((colors[i].r * step) / steps, $01010101) 85 | fade32[(i * 3) + 2] := Mul((colors[i].g * step) / steps, $01010101) 86 | fade32[(i * 3) + 3] := Mul((colors[i].b * step) / steps, $01010101) 87 | ENDFOR 88 | WaitTOF() 89 | LoadRGB32(vport, fade32) 90 | ENDFOR 91 | ELSE 92 | NEW fade4[ncolors] 93 | FOR step := 1 TO steps 94 | FOR i := 0 TO ncolors - 1 95 | r := Shr((colors[i].r * step) / steps, 4) 96 | g := Shr((colors[i].g * step) / steps, 4) 97 | b := Shr((colors[i].b * step) / steps, 4) 98 | fade4[i] := Shl(r, 8) OR Shl(g, 4) OR b 99 | ENDFOR 100 | WaitTOF() 101 | LoadRGB4(vport, fade4, ncolors) 102 | ENDFOR 103 | ENDIF 104 | 105 | IF fade32 THEN END fade32[ncolors32] 106 | IF fade4 THEN END fade4[ncolors] 107 | ENDPROC 108 | 109 | -------------------------------------------------------------------------------- /rgbcolor.e: -------------------------------------------------------------------------------- 1 | /* Module for RGB color type. */ 2 | 3 | 4 | OPT MODULE 5 | 6 | 7 | EXPORT OBJECT rgbcolor 8 | r:CHAR 9 | g:CHAR 10 | b:CHAR 11 | ENDOBJECT 12 | -------------------------------------------------------------------------------- /sorting_table/make_sorting_table.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | 6 | def main(): 7 | sort_lut = [] 8 | sort_order = [' '] 9 | 10 | with open('sorting_table', 'r') as f: 11 | for l in f.readlines(): 12 | if len(l) > 0 and not l[0].isspace(): 13 | sort_order.append(l[0]) 14 | 15 | for i in range(256): 16 | if chr(i) in sort_order: 17 | sort_lut.append(sort_order.index(chr(i))) 18 | else: 19 | sort_lut.append(0) 20 | 21 | for i in range(16): 22 | items = [] 23 | for j in range(16): 24 | items.append('${0:02x}'.format(sort_lut[(i*16)+j])) 25 | print(" CHAR {}".format(','.join(items))) 26 | 27 | if __name__ == "__main__": 28 | sys.exit(main()) 29 | -------------------------------------------------------------------------------- /sorting_table/sorting_table: -------------------------------------------------------------------------------- 1 | ! 2 | " 3 | # 4 | $ 5 | % 6 | & 7 | ' 8 | ( 9 | ) 10 | * 11 | + 12 | , 13 | - 14 | . 15 | / 16 | 17 | 0 18 | 1 19 | 2 20 | 3 21 | 4 22 | 5 23 | 6 24 | 7 25 | 8 26 | 9 27 | A 28 | a 29 | À 30 | à 31 | Á 32 | á 33 | Â 34 | â 35 | Ã 36 | ã 37 | B 38 | b 39 | C 40 | c 41 | Ç 42 | ç 43 | D 44 | d 45 | E 46 | e 47 | È 48 | è 49 | É 50 | é 51 | Ê 52 | ê 53 | Ë 54 | ë 55 | F 56 | f 57 | G 58 | g 59 | H 60 | h 61 | I 62 | i 63 | Ì 64 | ì 65 | Í 66 | í 67 | Î 68 | î 69 | Ï 70 | ï 71 | J 72 | j 73 | K 74 | k 75 | L 76 | l 77 | M 78 | m 79 | N 80 | n 81 | Ñ 82 | ñ 83 | O 84 | o 85 | Ò 86 | ò 87 | Ó 88 | ó 89 | Ô 90 | ô 91 | Õ 92 | õ 93 | P 94 | p 95 | Q 96 | q 97 | R 98 | r 99 | S 100 | s 101 | ß 102 | T 103 | t 104 | U 105 | u 106 | Ù 107 | ù 108 | Ú 109 | ú 110 | Û 111 | û 112 | Ü 113 | ü 114 | V 115 | v 116 | W 117 | w 118 | X 119 | x 120 | Y 121 | y 122 | Ý 123 | ý 124 | ÿ 125 | Z 126 | z 127 | Å 128 | å 129 | Ä 130 | ä 131 | Æ 132 | æ 133 | Ö 134 | ö 135 | Ø 136 | ø 137 | 138 | : 139 | ; 140 | < 141 | = 142 | > 143 | ? 144 | @ 145 | 146 | [ 147 | \ 148 | ] 149 | ^ 150 | _ 151 | ` 152 | { 153 | | 154 | } 155 | ~ 156 | 157 | ¡ 158 | ¢ 159 | £ 160 | ¤ 161 | ¥ 162 | ¦ 163 | § 164 | ¨ 165 | © 166 | ª 167 | « 168 | ¬ 169 | ® 170 | ¯ 171 | ° 172 | ± 173 | ² 174 | ³ 175 | ´ 176 | µ 177 | ¶ 178 | · 179 | ¸ 180 | ¹ 181 | º 182 | » 183 | ¼ 184 | ½ 185 | ¾ 186 | ¿ 187 | Ð 188 | × 189 | Þ 190 | ð 191 | ÷ 192 | þ 193 | --------------------------------------------------------------------------------