├── LUA
├── config.sun
├── modules
│ ├── calc.lua
│ ├── ftp.lua
│ ├── record.lua
│ ├── info.lua
│ ├── mail.lua
│ ├── camera.lua
│ ├── themes.lua
│ ├── gallery.lua
│ ├── game.lua
│ ├── music.lua
│ ├── clock.lua
│ ├── extdata.lua
│ ├── video.lua
│ ├── cia.lua
│ └── fb.lua
├── scripts
│ ├── title_list.lua
│ ├── music_api.lua
│ └── funcs.lua
└── sun_index.lua
├── themes
└── Default
│ ├── images
│ ├── 0.jpg
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ ├── 4.jpg
│ ├── 5.jpg
│ ├── bg.jpg
│ ├── cia.jpg
│ ├── fb.jpg
│ ├── ftp.jpg
│ ├── calc.jpg
│ ├── camera.jpg
│ ├── charge.jpg
│ ├── clock.jpg
│ ├── game.jpg
│ ├── info.jpg
│ ├── keys.png
│ ├── keys_c.png
│ ├── keys_t.png
│ ├── mail.jpg
│ ├── music.jpg
│ ├── nums.png
│ ├── nums_c.png
│ ├── nums_t.png
│ ├── record.jpg
│ ├── themes.jpg
│ ├── video.jpg
│ ├── extdata.jpg
│ ├── ftp_icon.jpg
│ ├── gallery.jpg
│ ├── keys_c_t.png
│ ├── nums_c_t.png
│ ├── clock_icon.jpg
│ └── music_icon.jpg
│ ├── fonts
│ └── main.ttf
│ ├── sounds
│ └── alarm.ogg
│ └── colors.lua
├── README.md
├── .gitattributes
├── .gitignore
└── LICENSE.txt
/LUA/config.sun:
--------------------------------------------------------------------------------
1 | main_dir = "/3ds/Sunshell"
--------------------------------------------------------------------------------
/LUA/modules/calc.lua:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/LUA/modules/calc.lua
--------------------------------------------------------------------------------
/LUA/scripts/title_list.lua:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/LUA/scripts/title_list.lua
--------------------------------------------------------------------------------
/themes/Default/images/0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/0.jpg
--------------------------------------------------------------------------------
/themes/Default/images/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/1.jpg
--------------------------------------------------------------------------------
/themes/Default/images/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/2.jpg
--------------------------------------------------------------------------------
/themes/Default/images/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/3.jpg
--------------------------------------------------------------------------------
/themes/Default/images/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/4.jpg
--------------------------------------------------------------------------------
/themes/Default/images/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/5.jpg
--------------------------------------------------------------------------------
/themes/Default/fonts/main.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/fonts/main.ttf
--------------------------------------------------------------------------------
/themes/Default/images/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/bg.jpg
--------------------------------------------------------------------------------
/themes/Default/images/cia.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/cia.jpg
--------------------------------------------------------------------------------
/themes/Default/images/fb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/fb.jpg
--------------------------------------------------------------------------------
/themes/Default/images/ftp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/ftp.jpg
--------------------------------------------------------------------------------
/themes/Default/images/calc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/calc.jpg
--------------------------------------------------------------------------------
/themes/Default/images/camera.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/camera.jpg
--------------------------------------------------------------------------------
/themes/Default/images/charge.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/charge.jpg
--------------------------------------------------------------------------------
/themes/Default/images/clock.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/clock.jpg
--------------------------------------------------------------------------------
/themes/Default/images/game.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/game.jpg
--------------------------------------------------------------------------------
/themes/Default/images/info.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/info.jpg
--------------------------------------------------------------------------------
/themes/Default/images/keys.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/keys.png
--------------------------------------------------------------------------------
/themes/Default/images/keys_c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/keys_c.png
--------------------------------------------------------------------------------
/themes/Default/images/keys_t.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/keys_t.png
--------------------------------------------------------------------------------
/themes/Default/images/mail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/mail.jpg
--------------------------------------------------------------------------------
/themes/Default/images/music.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/music.jpg
--------------------------------------------------------------------------------
/themes/Default/images/nums.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/nums.png
--------------------------------------------------------------------------------
/themes/Default/images/nums_c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/nums_c.png
--------------------------------------------------------------------------------
/themes/Default/images/nums_t.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/nums_t.png
--------------------------------------------------------------------------------
/themes/Default/images/record.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/record.jpg
--------------------------------------------------------------------------------
/themes/Default/images/themes.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/themes.jpg
--------------------------------------------------------------------------------
/themes/Default/images/video.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/video.jpg
--------------------------------------------------------------------------------
/themes/Default/sounds/alarm.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/sounds/alarm.ogg
--------------------------------------------------------------------------------
/themes/Default/images/extdata.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/extdata.jpg
--------------------------------------------------------------------------------
/themes/Default/images/ftp_icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/ftp_icon.jpg
--------------------------------------------------------------------------------
/themes/Default/images/gallery.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/gallery.jpg
--------------------------------------------------------------------------------
/themes/Default/images/keys_c_t.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/keys_c_t.png
--------------------------------------------------------------------------------
/themes/Default/images/nums_c_t.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/nums_c_t.png
--------------------------------------------------------------------------------
/themes/Default/images/clock_icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/clock_icon.jpg
--------------------------------------------------------------------------------
/themes/Default/images/music_icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rinnegatamante/Sunshell/HEAD/themes/Default/images/music_icon.jpg
--------------------------------------------------------------------------------
/themes/Default/colors.lua:
--------------------------------------------------------------------------------
1 | black = Color.new(0,0,0)
2 | white = Color.new(255,255,255)
3 | green_wifi = Color.new(0,166,81)
4 | red_wifi = Color.new(200,0,0)
5 | selected = Color.new(255,0,0)
6 | selected_item = Color.new(0,0,200,50)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is the official repository of Sunshell, first multifunction shell for 3DS.
2 | NOTE: Here you'll find only LUA scripts. Scripts run under lpp-3ds so if you want to use latest commits, please grab latest commit of lpp-3ds from main repository ( https://github.com/Rinnegatamante/lpp-3ds ).
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear on external disk
35 | .Spotlight-V100
36 | .Trashes
37 |
38 | # Directories potentially created on remote AFP share
39 | .AppleDB
40 | .AppleDesktop
41 | Network Trash Folder
42 | Temporary Items
43 | .apdisk
44 |
--------------------------------------------------------------------------------
/LUA/modules/ftp.lua:
--------------------------------------------------------------------------------
1 | -- Set private "FTP" mode
2 | mode = "FTP"
3 |
4 | -- Module background code
5 | function BackgroundFTP()
6 | last_shared = Network.updateFTP()
7 | end
8 |
9 | function FTPGC()
10 | Socket.term()
11 | last_shared = nil
12 | end
13 |
14 | -- Internal module settings
15 | FreeIconTopbar("FTP")
16 | if Network.isWifiEnabled() then
17 | if build == "Ninjhax 2" then
18 | ShowError("Network features currently unavailable on Ninjhax 2.")
19 | CallMainMenu()
20 | else
21 | if last_shared == nil then
22 | last_shared = "Waiting for connection..."
23 | Socket.init()
24 | table.insert(bg_apps,{BackgroundFTP,FTPGC,"FTP Server"}) -- Adding FTP module to background apps
25 | end
26 | end
27 | else
28 | ShowError("You need to be connected to an Hotspot to use FTP server.")
29 | CallMainMenu()
30 | end
31 |
32 | -- Rendering functions
33 | function AppTopScreenRender()
34 | Graphics.fillRect(5,395,40,220,black)
35 | Graphics.fillRect(6,394,41,219,white)
36 | end
37 |
38 | function AppBottomScreenRender()
39 | Graphics.fillRect(5,315,40,92,black)
40 | Graphics.fillRect(6,314,41,91,white)
41 | end
42 |
43 | -- Module main cycle
44 | function AppMainCycle()
45 |
46 | -- Draw FTP info
47 | Font.print(ttf,9,45,"IP: "..Network.getIPAddress(),black,TOP_SCREEN)
48 | Font.print(ttf,9,60,"Port: 5000",black,TOP_SCREEN)
49 | ftp_cmd = LinesGenerator(last_shared,90)
50 | for i,line in pairs(ftp_cmd) do
51 | Font.print(ttf,9,line[2],line[1],black,TOP_SCREEN)
52 | end
53 |
54 | -- Draw bottom screen commands info
55 | Font.print(ttf,9,45,"A = Restart FTP server",black,BOTTOM_SCREEN)
56 | Font.print(ttf,9,60,"SELECT = Return Main Menu",black,BOTTOM_SCREEN)
57 | Font.print(ttf,9,75,"B = Term FTP server",black,BOTTOM_SCREEN)
58 |
59 | -- Sets controls triggering
60 | if Controls.check(pad,KEY_A) and not Controls.check(oldpad,KEY_A) then
61 | CloseBGApp("FTP Server")
62 | dofile(main_dir.."/modules/ftp.lua")
63 | elseif Controls.check(pad,KEY_SELECT) and not Controls.check(oldpad,KEY_SELECT) then
64 | AddIconTopbar(theme_dir.."/images/ftp_icon.jpg","FTP")
65 | CallMainMenu()
66 | elseif Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
67 | CloseBGApp("FTP Server")
68 | CallMainMenu()
69 | end
70 | end
--------------------------------------------------------------------------------
/LUA/scripts/music_api.lua:
--------------------------------------------------------------------------------
1 | -- Music module API used to trigger Music module with headset changing status --
2 |
3 | -- Module background code
4 | function BackgroundMusic()
5 | Sound.updateStream()
6 |
7 | -- Cycle mode
8 | if cycle_index > 1 then
9 | if Sound.getTime(current_song) >= Sound.getTotalTime(current_song) then
10 | Sound.pause(current_song)
11 | Sound.close(current_song)
12 | if cycle_index == 2 then
13 | song_idx = song_idx + 1
14 | if song_idx > #my_songs then
15 | song_idx = 1
16 | end
17 | elseif cycle_index == 3 then
18 | tmp_idx = song_idx + 1
19 | found = false
20 | while tmp_idx < #my_songs do
21 | if my_songs[tmp_idx][3] == current_subfolder then
22 | song_idx = tmp_idx
23 | found = true
24 | break
25 | end
26 | tmp_idx = tmp_idx + 1
27 | end
28 | if not found then
29 | tmp_idx = 1
30 | while tmp_idx < #my_songs do
31 | if my_songs[tmp_idx][3] == current_subfolder then
32 | song_idx = tmp_idx
33 | found = true
34 | break
35 | end
36 | tmp_idx = tmp_idx + 1
37 | end
38 | end
39 | end
40 | if my_songs[song_idx][2] == "WAV" then
41 | current_song = Sound.openWav(my_songs[song_idx][4].."/"..my_songs[song_idx][1],true)
42 | elseif my_songs[song_idx][2] == "AIFF" then
43 | current_song = Sound.openAiff(my_songs[song_idx][4].."/"..my_songs[song_idx][1],true)
44 | end
45 | Sound.play(current_song,NO_LOOP,0x08,0x09)
46 | current_subfolder = my_songs[song_idx][3]
47 | end
48 | end
49 | end
50 |
51 | not_started = false
52 | cycle_index = 2
53 | my_songs = {}
54 | function AddSongsFromDir(dir,album)
55 | tmp = System.listDirectory(dir)
56 | for i,file in pairs(tmp) do
57 | if not file.directory then
58 | tmp_file = io.open(dir.."/"..file.name,FREAD)
59 | magic = io.read(tmp_file,0,4)
60 | if magic == "RIFF" then
61 | table.insert(my_songs,{file.name,"WAV",album,dir})
62 | elseif magic == "FORM" then
63 | table.insert(my_songs,{file.name,"AIFF",album,dir})
64 | end
65 | io.close(tmp_file)
66 | else
67 | AddSongsFromDir(dir.."/"..file.name,file.name)
68 | end
69 | end
70 | end
71 | AddSongsFromDir("/MUSIC",nil)
72 | if my_songs[1][2] == "WAV" then
73 | current_song = Sound.openWav(my_songs[1][4].."/"..my_songs[1][1],true)
74 | elseif my_songs[1][2] == "AIFF" then
75 | current_song = Sound.openAiff(my_songs[1][4].."/"..my_songs[1][1],true)
76 | end
77 | Sound.play(current_song,NO_LOOP,0x08,0x09)
78 | current_subfolder = my_songs[1][3]
79 | song_idx = 1
80 | AddIconTopbar(theme_dir.."/images/music_icon.jpg","Music")
81 | table.insert(bg_apps,{BackgroundMusic,MusicGC,"Music"}) -- Starting background Music module
--------------------------------------------------------------------------------
/LUA/modules/record.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Record" mode
2 | mode = "Record"
3 |
4 | -- Internal module settings
5 | local does_reg_exist = false
6 | local is_recording = false
7 | local state = "Stopped"
8 |
9 | -- Rendering functions
10 | function AppTopScreenRender()
11 | Graphics.fillRect(5,395,40,220,black)
12 | Graphics.fillRect(6,394,41,219,white)
13 | end
14 |
15 | function AppBottomScreenRender()
16 | end
17 |
18 | -- Module main cycle
19 | function AppMainCycle()
20 |
21 | -- Checking if recording finished
22 | if is_recording then
23 | if state == "Recording" and not Mic.isRecording() then
24 | does_reg_exist = true
25 | state = "Stopped"
26 | cur_sound = Mic.stop()
27 | is_recording = false
28 | end
29 | end
30 |
31 | -- Showing Controls and Info
32 | if not is_recording then
33 | TopCropPrint(9,45,"Press A to start recording",black,TOP_SCREEN)
34 | else
35 | TopCropPrint(9,45,"Press A to stop recording",black,TOP_SCREEN)
36 | TopCropPrint(9,60,"Press R to pause/resume recording",black,TOP_SCREEN)
37 | end
38 | if does_reg_exist then
39 | TopCropPrint(9,75,"Press X to listen recorded sound",black,TOP_SCREEN)
40 | TopCropPrint(9,90,"Press Y to save recorded sound",black,TOP_SCREEN)
41 | end
42 | TopCropPrint(9,200,"State: "..state,black,TOP_SCREEN)
43 |
44 | -- Sets controls triggering
45 | if Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
46 | if is_recording then
47 | tmp = Mic.stop()
48 | Sound.close(tmp)
49 | elseif does_reg_exist then
50 | if Sound.isPlaying(cur_sound) then
51 | Sound.pause(cur_sound)
52 | end
53 | Sound.close(cur_sound)
54 | end
55 | CallMainMenu()
56 | elseif Controls.check(pad,KEY_R) or Controls.check(pad,KEY_R) then
57 | if is_recording then
58 | if Mic.isRecording() then
59 | Mic.pause()
60 | state = "Paused"
61 | else
62 | Mic.resume()
63 | state = "Recording"
64 | end
65 | end
66 | elseif Controls.check(pad,KEY_X) or Controls.check(pad,KEY_X) then
67 | if does_reg_exist then
68 | Sound.play(cur_sound, NO_LOOP)
69 | end
70 | elseif Controls.check(pad,KEY_Y) or Controls.check(pad,KEY_Y) then
71 | if does_reg_exist then
72 | h,m,s = System.getTime()
73 | Sound.saveWav(cur_sound,"/MUSIC/"..h.."-"..m.."-"..s..".wav")
74 | ShowWarning("Sound saved successfully!")
75 | end
76 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
77 | if is_recording then
78 | cur_sound = Mic.stop()
79 | is_recording = false
80 | does_reg_exist = true
81 | state = "Stopped"
82 | else
83 | if does_reg_exist then
84 | Sound.close(cur_sound)
85 | does_reg_exist = false
86 | end
87 | is_recording = true
88 | Mic.start(10, 32730)
89 | state = "Recording"
90 | end
91 | end
92 | end
--------------------------------------------------------------------------------
/LUA/modules/info.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Info" mode
2 | mode = "Info"
3 |
4 | -- Internal module settings
5 | mac_addr = Network.getMacAddress()
6 | if Network.isWifiEnabled() then
7 | oldn = true
8 | ip_addr = Network.getIPAddress()
9 | else
10 | oldn = false
11 | ip_addr = "0.0.0.0"
12 | end
13 | usr = System.getUsername()
14 | day, mnth = System.getBirthday()
15 | model = System.getModel()
16 | region = System.getRegion()
17 | fw1,fw2,fw3 = System.getFirmware()
18 | fw = fw1 .. "." .. fw2 .. "-" .. fw3
19 | k1,k2,k3 = System.getKernel()
20 | kernel = k1 .. "." .. k2 .. "-" .. k3
21 | free_space = System.getFreeSpace()
22 | sorting = "Bytes"
23 | if free_space > 1024 then
24 | sorting = "KBs"
25 | free_space = free_space / 1024
26 | if free_space > 1024 then
27 | sorting = "MBs"
28 | free_space = free_space / 1024
29 | end
30 | end
31 | free_space = string.format("%8.2f", free_space)
32 |
33 | -- Rendering functions
34 | function AppTopScreenRender()
35 | Graphics.fillRect(5,395,40,220,black)
36 | Graphics.fillRect(6,394,41,219,white)
37 | end
38 |
39 | function AppBottomScreenRender()
40 | end
41 |
42 | -- Module main cycle
43 | function AppMainCycle()
44 |
45 | -- Draw console info
46 | if model == 1 then
47 | Font.print(ttf,9,45,"Model: 3DS XL",black,TOP_SCREEN)
48 | elseif model == 2 then
49 | Font.print(ttf,9,45,"Model: New 3DS",black,TOP_SCREEN)
50 | elseif model == 3 then
51 | Font.print(ttf,9,45,"Model: 2DS",black,TOP_SCREEN)
52 | elseif model == 4 then
53 | Font.print(ttf,9,45,"Model: New 3DS XL",black,TOP_SCREEN)
54 | else
55 | Font.print(ttf,9,45,"Model: 3DS",black,TOP_SCREEN)
56 | end
57 | if region == 1 then
58 | Font.print(ttf,9,60,"Region: USA",black,TOP_SCREEN)
59 | elseif region == 2 then
60 | Font.print(ttf,9,60,"Region: EUR",black,TOP_SCREEN)
61 | else
62 | Font.print(ttf,9,60,"Region: JPN",black,TOP_SCREEN)
63 | end
64 |
65 | -- Update IP Address
66 | if oldn and not Network.isWifiEnabled() then
67 | ip_addr = "0.0.0.0"
68 | oldn = false
69 | elseif Network.isWifiEnabled() and not oldn then
70 | oldn = true
71 | if build ~= "Ninjhax 2" then
72 | Socket.init()
73 | ip_addr = Network.getIPAddress()
74 | Socket.term()
75 | else
76 | ip_addr = "0.0.0.0"
77 | end
78 | end
79 |
80 | -- Draw info
81 | Font.print(ttf,9,75,"Username: " .. usr,black,TOP_SCREEN)
82 | Font.print(ttf,9,90,"Birthday: " .. day .. " " .. months[mnth],black,TOP_SCREEN)
83 | Font.print(ttf,9,105,"Firmware Build: " .. fw,black,TOP_SCREEN)
84 | Font.print(ttf,9,120,"Kernel Build: " .. kernel,black,TOP_SCREEN)
85 | Font.print(ttf,9,135,"Free Space: "..free_space.." "..sorting,black,TOP_SCREEN)
86 | Font.print(ttf,9,150,"MAC Address: "..mac_addr,black,TOP_SCREEN)
87 | Font.print(ttf,9,165,"IP Address: "..ip_addr,black,TOP_SCREEN)
88 | Font.print(ttf,9,180,"Build: "..build,black,TOP_SCREEN)
89 |
90 | -- Sets controls triggering
91 | if Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
92 | CallMainMenu()
93 | end
94 |
95 | end
--------------------------------------------------------------------------------
/LUA/modules/mail.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Mail" mode
2 | mode = "Mail"
3 |
4 | -- Internal module settings
5 | if Network.isWifiEnabled() then
6 | object = "Mail Object"
7 | to = "sample@gmail.com"
8 | body = "This is my e-mail body."
9 | else
10 | ShowError("You need to be connected on Internet to send mails.")
11 | CallMainMenu()
12 | end
13 | CallKeyboard(70,20)
14 | state = "Main"
15 | screenshots = false
16 |
17 | -- Rendering functions
18 | function AppTopScreenRender()
19 | Graphics.fillRect(5,395,40,220,black)
20 | Graphics.fillRect(6,394,41,219,white)
21 | end
22 |
23 | function AppBottomScreenRender()
24 | ShowKeyboard()
25 | Graphics.fillRect(5,315,190,230,black)
26 | Graphics.fillRect(6,314,191,229,white)
27 | end
28 |
29 | -- Module main cycle
30 | function AppMainCycle()
31 |
32 | -- Draw mail elements
33 | TopCropPrint(9,45,"Obj: "..object,black,TOP_SCREEN)
34 | TopCropPrint(9,60,"To: "..to,black,TOP_SCREEN)
35 | Font.print(ttf,9,75,"Body:",black,TOP_SCREEN)
36 | CropPrint(9,195,"State: "..state,black,BOTTOM_SCREEN)
37 | text = LinesGenerator(body,90)
38 | for i,line in pairs(text) do
39 | Font.print(ttf,9,line[2],line[1],black,TOP_SCREEN)
40 | end
41 |
42 | -- Keyboard input
43 | if state ~= "Main" then
44 | in_game = true -- Disable START button function
45 | CropPrint(9,213,"Press L to confirm",black,BOTTOM_SCREEN)
46 | if Controls.check(pad,KEY_L) and not Controls.check(oldpad,KEY_L) then
47 | state = "Main"
48 | end
49 | input = KeyboardInput()
50 | if input > 0 then
51 | if state == "Body" then
52 | tmp = body
53 | elseif state == "To" then
54 | tmp = to
55 | else
56 | tmp = object
57 | end
58 | if input == 0x09 then
59 | if string.len(tmp) > 1 then
60 | tmp = string.sub(tmp,1,string.len(tmp)-1)
61 | else
62 | tmp = ""
63 | end
64 | else
65 | tmp = tmp .. string.char(input)
66 | end
67 | if state == "Body" then
68 | body = tmp
69 | elseif state == "To" then
70 | to = tmp
71 | else
72 | object = tmp
73 | end
74 | end
75 | end
76 |
77 | -- Sets controls triggering
78 | if state == "Main" then
79 | if Controls.check(pad,KEY_A) and not Controls.check(oldpad,KEY_A) then
80 | state = "Body"
81 | elseif Controls.check(pad,KEY_X) then
82 | state = "To"
83 | elseif Controls.check(pad,KEY_Y) then
84 | state = "Obj"
85 | elseif Controls.check(pad,KEY_R) and not Controls.check(oldpad,KEY_R) then
86 | if Network.isWifiEnabled() then
87 | if Network.sendMail(to,object,body) then
88 | ShowWarning("Mail sent successfully!")
89 | else
90 | ShowError("An error has occurred while sending mail.")
91 | end
92 | else
93 | ShowError("You need to be connected on Internet to send mails.")
94 | end
95 | elseif Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
96 | CloseKeyboard()
97 | CallMainMenu()
98 | end
99 | end
100 | end
--------------------------------------------------------------------------------
/LUA/modules/camera.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Camera" mode
2 | mode = "Camera"
3 |
4 | -- Internal module settings
5 | update_bottom_screen = true
6 | ui_enabled = false
7 | screenshots = false
8 | SetBottomRefresh(false)
9 | SetTopRefresh(false)
10 | Camera.init(TOP_SCREEN, OUTER_CAM, PHOTO_MODE_NORMAL, false)
11 | local scene = OUTER_CAM
12 | local photo_mode = PHOTO_MODE_NORMAL
13 | local resolution = VGA_RES
14 | local function GetPhotoMode(pm)
15 | if pm == PHOTO_MODE_NORMAL then
16 | return "Normal"
17 | elseif pm == PHOTO_MODE_PORTRAIT then
18 | return "Portrait"
19 | elseif pm == PHOTO_MODE_LANDSCAPE then
20 | return "Landscape"
21 | elseif pm == PHOTO_MODE_NIGHTVIEW then
22 | return "Night Mode"
23 | elseif pm == PHOTO_MODE_LETTER then
24 | return "Letter"
25 | end
26 | end
27 | local function GetResolution(pm)
28 | if pm == VGA_RES then
29 | return "VGA (640x480)"
30 | elseif pm == QVGA_RES then
31 | return "QVGA (320x240)"
32 | elseif pm == QQVGA_RES then
33 | return "QQVGA (160x120)"
34 | elseif pm == CIF_RES then
35 | return "CIF (352x288)"
36 | elseif pm == QCIF_RES then
37 | return "QCIF (176x144)"
38 | elseif pm == DS_RES then
39 | return "NDS (256x192)"
40 | elseif pm == HDS_RES then
41 | return "HDS (512x384)"
42 | elseif pm == CTR_RES then
43 | return "3DS (400x240)"
44 | end
45 | end
46 |
47 | function UpdateBottomScreen()
48 |
49 | -- Clear bottom screen
50 | Screen.clear(BOTTOM_SCREEN)
51 |
52 | -- Show controls info
53 | Screen.debugPrint(0,0, "Controls:", selected, BOTTOM_SCREEN)
54 | Screen.debugPrint(0,25, "L = Take Photo", white, BOTTOM_SCREEN)
55 | Screen.debugPrint(0,40, "X = Swap camera scene", white, BOTTOM_SCREEN)
56 | Screen.debugPrint(0,55, "Y = Change Photo Mode", white, BOTTOM_SCREEN)
57 | Screen.debugPrint(0,70, "A = Change Photo Resolution", white, BOTTOM_SCREEN)
58 |
59 | -- Show Photo settings
60 | Screen.debugPrint(0,100, "Settings: ", selected, BOTTOM_SCREEN)
61 | Screen.debugPrint(0,125, "Photo Mode: " .. GetPhotoMode(photo_mode), white, BOTTOM_SCREEN)
62 | Screen.debugPrint(0,140, "Resolution: " .. GetResolution(resolution), white, BOTTOM_SCREEN)
63 |
64 | end
65 |
66 | -- Rendering functions
67 | function AppTopScreenRender()
68 | end
69 |
70 | function AppBottomScreenRender()
71 | end
72 |
73 | -- Module main cycle
74 | function AppMainCycle()
75 |
76 | if update_bottom_screen then
77 | OneshotPrint(UpdateBottomScreen)
78 | update_bottom_screen = false
79 | end
80 |
81 | -- Show camera scene
82 | Camera.getOutput()
83 |
84 | -- Sets controls triggering
85 | if Controls.check(pad, KEY_L) then
86 | h,m,s = System.getTime()
87 | Camera.takePhoto("/DCIM/"..h.."-"..m.."-"..s..".jpg", resolution, true)
88 | elseif Controls.check(pad, KEY_Y) and not Controls.check(oldpad, KEY_Y) then
89 | photo_mode = photo_mode + 1
90 | if photo_mode > PHOTO_MODE_LETTER then
91 | photo_mode = PHOTO_MODE_NORMAL
92 | end
93 | Camera.init(TOP_SCREEN, scene, photo_mode, false)
94 | update_bottom_screen = true
95 | elseif Controls.check(pad, KEY_A) and not Controls.check(oldpad, KEY_A) then
96 | resolution = resolution + 1
97 | if resolution > CTR_RES then
98 | resolution = VGA_RES
99 | end
100 | update_bottom_screen = true
101 | elseif Controls.check(pad, KEY_X) and not Controls.check(oldpad, KEY_X) then
102 | Camera.term()
103 | if scene == OUTER_CAM then
104 | scene = INNER_CAM
105 | else
106 | scene = OUTER_CAM
107 | end
108 | Camera.init(TOP_SCREEN, scene, photo_mode, false)
109 | elseif Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
110 | Camera.term()
111 | CallMainMenu()
112 | end
113 |
114 | end
--------------------------------------------------------------------------------
/LUA/modules/themes.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Themes" mode
2 | mode = "Themes"
3 |
4 | -- Internal module settings
5 | master_index_t = 0
6 | p_t = 1
7 | my_themes = {}
8 | tmp = System.listDirectory(main_dir.."/themes")
9 | for i,file in pairs(tmp) do
10 | if file.directory then
11 | table.insert(my_themes,file.name)
12 | end
13 | end
14 |
15 | function UpdateBottomScreen()
16 |
17 | -- Showing files list
18 | base_y = 0
19 | for l, file in pairs(my_themes) do
20 | if (base_y > 226) then
21 | break
22 | end
23 | if (l >= master_index_t) then
24 | if (l==p_t) then
25 | Screen.fillRect(0,319,base_y,base_y+15,selected_item,BOTTOM_SCREEN)
26 | color = selected
27 | else
28 | color = black
29 | end
30 | CropPrint(0,base_y,file,color,BOTTOM_SCREEN)
31 | base_y = base_y + 15
32 | end
33 | end
34 |
35 | end
36 |
37 | -- Rendering functions
38 | function AppTopScreenRender()
39 | end
40 |
41 | function AppBottomScreenRender()
42 | end
43 |
44 | -- Module main cycle
45 | function AppMainCycle()
46 |
47 | UpdateBottomScreen()
48 |
49 | -- Sets controls triggering
50 | if (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
51 | GarbageCollection()
52 | Font.unload(ttf)
53 | System.deleteFile(start_dir.."/config.sun")
54 | config_file = io.open(start_dir.."/config.sun",FCREATE)
55 | io.write(config_file,0,"main_dir = \""..main_dir.."\"\ntheme = \""..my_themes[p_t].."\"",string.len(main_dir)+string.len(my_themes[p_t])+24)
56 | io.close(config_file)
57 | theme_dir = main_dir.."/themes/"..my_themes[p_t]
58 | bg = Screen.loadImage(theme_dir.."/images/bg.jpg")
59 | if System.doesFileExist(theme_dir.."/images/music.jpg") then
60 | ext = ".jpg"
61 | else
62 | ext = ".png"
63 | end
64 | music = Screen.loadImage(theme_dir.."/images/music"..ext)
65 | video = Screen.loadImage(theme_dir.."/images/video"..ext)
66 | info = Screen.loadImage(theme_dir.."/images/info"..ext)
67 | fb = Screen.loadImage(theme_dir.."/images/fb"..ext)
68 | game = Screen.loadImage(theme_dir.."/images/game"..ext)
69 | photo = Screen.loadImage(theme_dir.."/images/photo"..ext)
70 | cia = Screen.loadImage(theme_dir.."/images/cia"..ext)
71 | extdata = Screen.loadImage(theme_dir.."/images/extdata"..ext)
72 | calc = Screen.loadImage(theme_dir.."/images/calc"..ext)
73 | mail = Screen.loadImage(theme_dir.."/images/mail"..ext)
74 | themes = Screen.loadImage(theme_dir.."/images/themes"..ext)
75 | clock = Screen.loadImage(theme_dir.."/images/clock"..ext)
76 | ftp = Screen.loadImage(theme_dir.."/images/ftp"..ext)
77 | charge = Screen.loadImage(theme_dir.."/images/charge"..ext)
78 | b0 = Screen.loadImage(theme_dir.."/images/0"..ext)
79 | b1 = Screen.loadImage(theme_dir.."/images/1"..ext)
80 | b2 = Screen.loadImage(theme_dir.."/images/2"..ext)
81 | b3 = Screen.loadImage(theme_dir.."/images/3"..ext)
82 | b4 = Screen.loadImage(theme_dir.."/images/4"..ext)
83 | b5 = Screen.loadImage(theme_dir.."/images/5"..ext)
84 | tools = {}
85 | table.insert(tools,{game,"/modules/game.lua","Applications"})
86 | table.insert(tools,{info,"/modules/info.lua","Console Info"})
87 | table.insert(tools,{photo,"/modules/photo.lua","Photos"})
88 | table.insert(tools,{music,"/modules/music.lua","Musics"})
89 | table.insert(tools,{video,"/modules/video.lua","Videos"})
90 | table.insert(tools,{fb,"/modules/fb.lua","Filebrowser"})
91 | table.insert(tools,{cia,"/modules/cia.lua","CIA Manager"})
92 | table.insert(tools,{extdata,"/modules/extdata.lua","Extdata Manager"})
93 | table.insert(tools,{calc,"/modules/calc.lua","Calc"})
94 | table.insert(tools,{clock,"/modules/clock.lua","Clock"})
95 | table.insert(tools,{ftp,"/modules/ftp.lua","FTP Server"})
96 | table.insert(tools,{mail,"/modules/mail.lua","Mail"})
97 | table.insert(tools,{themes,"/modules/themes.lua","Theme Manager"})
98 | ttf = Font.load(theme_dir.."/fonts/main.ttf")
99 | dofile(theme_dir.."/colors.lua")
100 | Font.setPixelSizes(ttf,18)
101 | elseif Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
102 | CallMainMenu()
103 | elseif (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
104 | p_t = p_t - 1
105 | if (p_t >= 16) then
106 | master_index_t = p_t - 15
107 | end
108 | update_bottom_screen = true
109 | elseif (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
110 | p_t = p_t + 1
111 | if (p_t >= 17) then
112 | master_index_t = p_t - 15
113 | end
114 | update_bottom_screen = true
115 | end
116 | if (p_t < 1) then
117 | p_t = #my_themes
118 | if (p_t >= 17) then
119 | master_index_t = p_t - 15
120 | end
121 | elseif (p_t > #my_themes) then
122 | master_index_t = 0
123 | p_t = 1
124 | end
125 | end
--------------------------------------------------------------------------------
/LUA/modules/gallery.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Gallery" mode
2 | mode = "Gallery"
3 |
4 | -- Internal module settings
5 | master_index_p = 0
6 | p_p = 1
7 | update_frame = false
8 | update_bottom_screen = true
9 | ui_enabled = false
10 | not_started_p = true
11 | SetBottomRefresh(false)
12 | my_photos = {}
13 | function AddDirPhoto(dir)
14 | tmp = System.listDirectory(dir)
15 | for i,file in pairs(tmp) do
16 | if not file.directory then
17 | if string.upper(string.sub(file.name,-4)) == ".PNG" or string.upper(string.sub(file.name,-4)) == ".BMP" or string.upper(string.sub(file.name,-4)) == ".JPG" then
18 | table.insert(my_photos,{dir.."/"..file.name,file.name})
19 | end
20 | else
21 | AddDirPhoto(dir.."/"..file.name)
22 | end
23 | end
24 | end
25 | AddDirPhoto("/DCIM")
26 | if #my_photos > 0 then
27 | x_print = 0
28 | y_print = 0
29 | big_image = false
30 | current_photo = Graphics.loadImage(my_photos[1][1])
31 | width = Graphics.getImageWidth(current_photo)
32 | height = Graphics.getImageHeight(current_photo)
33 | if width > 400 then
34 | width = 400
35 | big_image = true
36 | end
37 | if height > 240 then
38 | height = 240
39 | big_image = true
40 | end
41 | else
42 | ShowError("DCIM folder is empty.")
43 | CallMainMenu()
44 | end
45 |
46 | function UpdateBottomScreen()
47 |
48 | -- Clear bottom screen
49 | Screen.clear(BOTTOM_SCREEN)
50 |
51 | -- Showing files list
52 | base_y = 0
53 | for l, file in pairs(my_photos) do
54 | if (base_y > 226) then
55 | break
56 | end
57 | if (l >= master_index_p) then
58 | if (l==p_p) then
59 | Screen.fillRect(0,319,base_y,base_y+15,selected_item,BOTTOM_SCREEN)
60 | color = selected
61 | else
62 | color = white
63 | end
64 | CropPrint(0,base_y,file[2],color,BOTTOM_SCREEN)
65 | base_y = base_y + 15
66 | end
67 | end
68 |
69 | end
70 |
71 | -- Rendering functions
72 | function AppTopScreenRender()
73 |
74 | -- Showing current image
75 | if big_image then
76 | Graphics.drawPartialImage(0,0,x_print,y_print,width,height,current_photo)
77 | x,y = Controls.readCirclePad()
78 | if (x < - 100) and (x_print > 0) then
79 | x_print = x_print - 5
80 | if x_print < 0 then
81 | x_print = 0
82 | end
83 | end
84 | if (y > 100) and (y_print > 0) then
85 | y_print = y_print - 5
86 | if y_print < 0 then
87 | y_print = 0
88 | end
89 | end
90 | if (x > 100) and (x_print + width < Graphics.getImageWidth(current_photo)) then
91 | x_print = x_print + 5
92 | end
93 | if (y < - 100) and (y_print + height < Graphics.getImageHeight(current_photo)) then
94 | y_print = y_print + 5
95 | end
96 | if x_print + width > Graphics.getImageWidth(current_photo) then
97 | x_print = Graphics.getImageWidth(current_photo) - width
98 | end
99 | if y_print + height > Graphics.getImageHeight(current_photo) then
100 | y_print = Graphics.getImageHeight(current_photo) - height
101 | end
102 | else
103 | Graphics.drawImage(0,0,current_photo)
104 | end
105 |
106 | end
107 |
108 | function AppBottomScreenRender()
109 | end
110 |
111 | -- Module main cycle
112 | function AppMainCycle()
113 |
114 | if update_bottom_screen then
115 | OneshotPrint(UpdateBottomScreen)
116 | update_bottom_screen = false
117 | end
118 |
119 | -- Update current image
120 | if update_frame then
121 | update_frame = false
122 | Graphics.freeImage(current_photo)
123 | x_print = 0
124 | y_print = 0
125 | big_image = false
126 | current_photo = Graphics.loadImage(my_photos[p_p][1])
127 | width = Graphics.getImageWidth(current_photo)
128 | height = Graphics.getImageHeight(current_photo)
129 | if width > 400 then
130 | width = 400
131 | big_image = true
132 | end
133 | if height > 240 then
134 | height = 240
135 | big_image = true
136 | end
137 | end
138 |
139 | -- Sets controls triggering
140 | if Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
141 | Graphics.freeImage(current_photo)
142 | CallMainMenu()
143 | elseif (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) and not_started_p then
144 | p_p = p_p - 1
145 | if (p_p >= 16) then
146 | master_index_p = p_p - 15
147 | end
148 | update_frame = true
149 | update_bottom_screen = true
150 | elseif (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) and not_started_p then
151 | p_p = p_p + 1
152 | if (p_p >= 17) then
153 | master_index_p = p_p - 15
154 | end
155 | update_frame = true
156 | update_bottom_screen = true
157 | end
158 | if (p_p < 1) then
159 | p_p = #my_photos
160 | if (p_p >= 17) then
161 | master_index_p = p_p - 15
162 | end
163 | elseif (p_p > #my_photos) then
164 | master_index_p = 0
165 | p_p = 1
166 | end
167 | end
--------------------------------------------------------------------------------
/LUA/modules/game.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Game" mode
2 | mode = "Game"
3 |
4 | -- Internal module settings
5 | master_index_g = 0
6 | p_g = 1
7 | function CheckDirectory(src,key)
8 | tmp = System.listDirectory(src)
9 | for i,file in pairs(tmp) do
10 | if file.directory then
11 | if file.name == key then
12 | return true
13 | end
14 | end
15 | end
16 | return false
17 | end
18 | my_apps = {}
19 | function ExtCallMainMenu()
20 | System.exit = my_exit
21 | Sound.init()
22 | CallMainMenu()
23 | end
24 |
25 | -- Adding preinstalled LUA homebrews
26 | for i,app in pairs(System.listDirectory(main_dir.."/apps")) do
27 | if app.directory then
28 | dofile(main_dir.."/apps/"..app.name.."/data.lua")
29 | table.insert(my_apps,{true,app.name,app.name,app_desc,app_author,nil,true})
30 | end
31 | end
32 |
33 | if build == "Ninjhax 1" then
34 | dir = System.listDirectory("/3ds/")
35 | for i,file in pairs(dir) do
36 | if file.directory then
37 | if System.doesFileExist("/3ds/"..(file.name).."/"..(file.name)..".3dsx") then
38 | if System.doesFileExist("/3ds/"..(file.name).."/"..(file.name)..".smdh") then
39 | app = System.extractSMDH("/3ds/"..(file.name).."/"..(file.name)..".smdh")
40 | table.insert(my_apps,{true,file.name,app.title,app.desc,app.author,app.icon,false})
41 | else
42 | table.insert(my_apps,{true,file.name,file.name,"","",nil,false})
43 | end
44 | end
45 | end
46 | end
47 | end
48 | if build ~= "Ninjhax 2" then
49 | table.insert(my_apps,{false,nil,"Game Cartridge","","0x0",nil})-- Insert Gamecard voice
50 | dir = System.listCIA()
51 | for i,file in pairs(dir) do
52 | if file.mediatype == SDMC then
53 | table.insert(my_apps,{false,file.unique_id,file.product_id,"","0x"..string.sub(string.format('%02X',file.unique_id),1,-3),nil})
54 | end
55 | end
56 |
57 | -- Game titles parsing
58 | dofile(main_dir.."/scripts/title_list.lua")
59 | assigned = 0
60 | for i,title in pairs(title_list) do
61 | if assigned >= (#my_apps - 1) then
62 | break
63 | end
64 | for z,app in pairs(my_apps) do
65 | if app[2] == title[3] then
66 | app[3] = title[1]
67 | assigned = assigned + 1
68 | end
69 | end
70 | end
71 |
72 | end
73 |
74 | if #my_apps <= 0 then
75 | ShowError("Cannot access any launchable application.")
76 | CallMainMenu()
77 | end
78 |
79 | -- Rendering functions
80 | function AppTopScreenRender()
81 | Graphics.fillRect(5,395,40,220,black)
82 | Graphics.fillRect(6,394,41,219,white)
83 | end
84 |
85 | function AppBottomScreenRender()
86 | end
87 |
88 | -- Module main cycle
89 | function AppMainCycle()
90 |
91 | -- Draw bottom screen listmenu and top screen info
92 | base_y = 0
93 | for l, file in pairs(my_apps) do
94 | if (base_y > 226) then
95 | break
96 | end
97 | if (l >= master_index_g) then
98 | if (l==p_g) then
99 | Screen.fillRect(0,319,base_y,base_y+15,selected_item,BOTTOM_SCREEN)
100 | TopCropPrint(9,45,file[3],selected,TOP_SCREEN)
101 | TopCropPrint(9,60,file[5],black,TOP_SCREEN)
102 | gw_rom = System.getGWRomID()
103 | if file[2] == nil and build == "CIA" and gw_rom ~= "" then -- GW roms support
104 | desc = LinesGenerator("This is a 3DS rom probably loaded with a Gateway card. Product-ID: "..gw_rom,90)
105 | else
106 | desc = LinesGenerator(file[4],90)
107 | end
108 | for i,line in pairs(desc) do
109 | Font.print(ttf,9,line[2],line[1],black,TOP_SCREEN)
110 | end
111 | if file[6] ~= nil then
112 | Screen.fillEmptyRect(341,390,43,92,black,TOP_SCREEN)
113 | Screen.drawImage(342,44,file[6],TOP_SCREEN)
114 | end
115 | color = selected
116 | else
117 | color = black
118 | end
119 | if file[1] then
120 | CropPrint(0,base_y,file[2],color,BOTTOM_SCREEN)
121 | else
122 | CropPrint(0,base_y,file[3],color,BOTTOM_SCREEN)
123 | end
124 | base_y = base_y + 15
125 | end
126 | end
127 |
128 | -- Sets controls triggering
129 | if Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
130 | CallMainMenu()
131 | for l, file in pairs(my_apps) do
132 | if file[6] ~= nil then
133 | Screen.freeImage(file[6])
134 | end
135 | end
136 | elseif Controls.check(pad,KEY_A) then
137 | for i,bg_apps_code in pairs(bg_apps) do
138 | bg_apps_code[2]()
139 | end
140 | for l, file in pairs(my_apps) do
141 | if file[6] ~= nil then
142 | Screen.freeImage(file[6])
143 | end
144 | end
145 | Sound.term()
146 | if my_apps[p_g][1] then
147 | if my_apps[p_g][7] then
148 | ui_enabled = false
149 | screenshots = false
150 | my_exit = System.exit
151 | in_game = true
152 | System.exit = ExtCallMainMenu
153 | dofile(main_dir.."/apps/"..my_apps[p_g][2].."/index.lua")
154 | else
155 | Font.unload(ttf)
156 | GarbageCollection()
157 | System.launch3DSX("/3ds/"..my_apps[p_g][2].."/"..my_apps[p_g][2]..".3dsx")
158 | end
159 | else
160 | Font.unload(ttf)
161 | GarbageCollection()
162 | if my_apps[p_g][2] == nil then
163 | ShowWarning("You will be redirected to sysNand and your gamecard will be launched.")
164 | System.launchGamecard()
165 | else
166 | System.launchCIA(my_apps[p_g][2],SDMC)
167 | end
168 | end
169 | elseif (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
170 | p_g = p_g - 1
171 | if (p_g >= 16) then
172 | master_index_g = p_g - 15
173 | end
174 | elseif (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
175 | p_g = p_g + 1
176 | if (p_g >= 17) then
177 | master_index_g = p_g - 15
178 | end
179 | end
180 | if (p_g < 1) then
181 | p_g = #my_apps
182 | if (p_g >= 17) then
183 | master_index_g = p_g - 15
184 | end
185 | elseif (p_g > #my_apps) then
186 | master_index_g = 0
187 | p_g = 1
188 | end
189 | end
--------------------------------------------------------------------------------
/LUA/modules/music.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Music" mode
2 | mode = "Music"
3 |
4 | -- Internal module settings
5 | FreeIconTopbar("Music")
6 | hide = false
7 | master_index_m = 0
8 | text_for_top_screen = ""
9 | p_m = 1
10 | if not_started == nil then
11 | not_started = true
12 | end
13 | my_songs = {}
14 | cycle_mode = {"No Cycle","All","Subfolder","Song"}
15 | if cycle_index == nil then
16 | cycle_index = 1
17 | end
18 | function AddSongsFromDir(dir,album)
19 | tmp = System.listDirectory(dir)
20 | for i,file in pairs(tmp) do
21 | if not file.directory then
22 | tmp_file = io.open(dir.."/"..file.name,FREAD)
23 | magic = io.read(tmp_file,0,4)
24 | if magic == "RIFF" then
25 | table.insert(my_songs,{file.name,"WAV",album,dir})
26 | elseif magic == "FORM" then
27 | table.insert(my_songs,{file.name,"AIFF",album,dir})
28 | elseif magic == "OggS" then
29 | table.insert(my_songs,{file.name,"OGG",album,dir})
30 | end
31 | io.close(tmp_file)
32 | else
33 | AddSongsFromDir(dir.."/"..file.name,file.name)
34 | end
35 | end
36 | end
37 | AddSongsFromDir("/MUSIC",nil)
38 | blue = Color.new(0,0,255)
39 |
40 | if #my_songs <= 0 then
41 | ShowError("MUSIC folder is empty.")
42 | CallMainMenu()
43 | end
44 |
45 | -- Module background code
46 | function BackgroundMusic()
47 | Sound.updateStream()
48 |
49 | -- Cycle mode
50 | if cycle_index > 1 then
51 | if Sound.getTime(current_song) >= Sound.getTotalTime(current_song) then
52 | update_list = true
53 | Sound.pause(current_song)
54 | Sound.close(current_song)
55 | if cycle_index == 2 then
56 | song_idx = song_idx + 1
57 | if song_idx > #my_songs then
58 | song_idx = 1
59 | end
60 | elseif cycle_index == 3 then
61 | tmp_idx = song_idx + 1
62 | found = false
63 | while tmp_idx < #my_songs do
64 | if my_songs[tmp_idx][3] == current_subfolder then
65 | song_idx = tmp_idx
66 | found = true
67 | break
68 | end
69 | tmp_idx = tmp_idx + 1
70 | end
71 | if not found then
72 | tmp_idx = 1
73 | while tmp_idx < #my_songs do
74 | if my_songs[tmp_idx][3] == current_subfolder then
75 | song_idx = tmp_idx
76 | found = true
77 | break
78 | end
79 | tmp_idx = tmp_idx + 1
80 | end
81 | end
82 | end
83 | if my_songs[song_idx][2] == "WAV" then
84 | current_song = Sound.openWav(my_songs[song_idx][4].."/"..my_songs[song_idx][1],true)
85 | elseif my_songs[song_idx][2] == "AIFF" then
86 | current_song = Sound.openAiff(my_songs[song_idx][4].."/"..my_songs[song_idx][1],true)
87 | elseif my_songs[song_idx][2] == "OGG" then
88 | current_song = Sound.openOgg(my_songs[song_idx][4].."/"..my_songs[song_idx][1],true)
89 | end
90 | Sound.play(current_song,NO_LOOP)
91 | current_subfolder = my_songs[song_idx][3]
92 | end
93 | end
94 | end
95 |
96 | function ShowFileList()
97 | base_y = 0
98 | for l, file in pairs(my_songs) do
99 | if (base_y > 226) then
100 | break
101 | end
102 | if (l >= master_index_m) then
103 | if (l==p_m) then
104 | if file[3] ~= nil then
105 | text_for_top_screen = "Subfolder: "..file[3]
106 | else
107 | text_for_top_screen = "Subfolder: None"
108 | end
109 | Screen.fillRect(0,319,base_y,base_y+15,selected_item,BOTTOM_SCREEN)
110 | color = selected
111 | else
112 | if (l==song_idx) then
113 | color = blue
114 | else
115 | color = black
116 | end
117 | end
118 | CropPrint(0,base_y,file[1],color,BOTTOM_SCREEN)
119 | base_y = base_y + 15
120 | end
121 | end
122 | end
123 |
124 | -- Internal Module GarbageCollection
125 | function MusicGC()
126 | if current_song ~= nil then
127 | Sound.pause(current_song)
128 | Sound.close(current_song)
129 | current_song = nil
130 | end
131 | end
132 |
133 | -- Rendering functions
134 | function AppTopScreenRender()
135 | Graphics.fillRect(5,395,40,220,black)
136 | Graphics.fillRect(6,394,41,219,white)
137 | end
138 |
139 | function AppBottomScreenRender()
140 | end
141 |
142 | -- Module main cycle
143 | function AppMainCycle()
144 |
145 | -- Draw Cycle Mode info
146 | Font.print(ttf,9,200,"Cycle mode: "..cycle_mode[cycle_index],black,TOP_SCREEN)
147 |
148 | -- Showing files list
149 | ShowFileList()
150 |
151 | -- Showing Song info
152 | if not not_started then
153 | TopCropPrint(9,45,"Title: "..Sound.getTitle(current_song),black,TOP_SCREEN)
154 | TopCropPrint(9,60,"Author: "..Sound.getAuthor(current_song),black,TOP_SCREEN)
155 | if my_songs[song_idx][3] ~= nil then
156 | Font.print(ttf,9,75,"Subfolder: "..my_songs[song_idx][3],black,TOP_SCREEN)
157 | else
158 | Font.print(ttf,9,75,"Subfolder: None",black,TOP_SCREEN)
159 | end
160 | if Sound.getType(current_song) == 1 then
161 | TopCropPrint(9,90,"Audiotype: Mono",black,TOP_SCREEN)
162 | else
163 | TopCropPrint(9,90,"Audiotype: Stereo",black,TOP_SCREEN)
164 | end
165 | TopCropPrint(9,105,"Time: "..FormatTime(Sound.getTime(current_song)).." / "..FormatTime(Sound.getTotalTime(current_song)),black,TOP_SCREEN)
166 | TopCropPrint(9,120,"Samplerate: "..Sound.getSrate(current_song),black,TOP_SCREEN)
167 | else
168 | Font.print(ttf,9,45,text_for_top_screen,black,TOP_SCREEN)
169 | end
170 |
171 | -- Power-on screens triggering
172 | if hide then
173 | if pad ~= 0x00 then
174 | hide = false
175 | Controls.enableScreen(TOP_SCREEN)
176 | Controls.enableScreen(BOTTOM_SCREEN)
177 | end
178 | end
179 |
180 | -- Sets controls triggering
181 | if (Controls.check(pad,KEY_SELECT)) and not (Controls.check(oldpad,KEY_SELECT)) and not (not_started) then
182 | CallMainMenu()
183 | AddIconTopbar(theme_dir.."/images/music_icon.jpg","Music")
184 | elseif (Controls.check(pad,KEY_R)) and not (Controls.check(oldpad,KEY_R)) and not (not_started) then
185 | if not hide then
186 | Controls.disableScreen(TOP_SCREEN)
187 | Controls.disableScreen(BOTTOM_SCREEN)
188 | hide = true
189 | end
190 | elseif (Controls.check(pad,KEY_X)) and not (Controls.check(oldpad,KEY_X)) and not (not_started) then
191 | if Sound.isPlaying(current_song) then
192 | Sound.pause(current_song)
193 | else
194 | Sound.resume(current_song)
195 | end
196 | elseif (Controls.check(pad,KEY_Y)) and not (Controls.check(oldpad,KEY_Y)) and not (not_started) then
197 | not_started = true
198 | CloseBGApp("Music")
199 | current_song = nil
200 | elseif (Controls.check(pad,KEY_DLEFT)) and not (Controls.check(oldpad,KEY_DLEFT)) then
201 | cycle_index = cycle_index - 1
202 | if cycle_index < 1 then
203 | cycle_index = 4
204 | end
205 | elseif (Controls.check(pad,KEY_DRIGHT)) and not (Controls.check(oldpad,KEY_DRIGHT)) then
206 | cycle_index = cycle_index + 1
207 | if cycle_index > 4 then
208 | cycle_index = 1
209 | end
210 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
211 | if current_song ~= nil then
212 | MusicGC()
213 | end
214 | not_started = false
215 | if my_songs[p_m][2] == "WAV" then
216 | current_song = Sound.openWav(my_songs[p_m][4].."/"..my_songs[p_m][1],true)
217 | elseif my_songs[p_m][2] == "AIFF" then
218 | current_song = Sound.openAiff(my_songs[p_m][4].."/"..my_songs[p_m][1],true)
219 | elseif my_songs[p_m][2] == "OGG" then
220 | current_song = Sound.openOgg(my_songs[p_m][4].."/"..my_songs[p_m][1],true)
221 | end
222 | Sound.play(current_song,NO_LOOP)
223 | current_subfolder = my_songs[p_m][3]
224 | song_idx = p_m
225 | table.insert(bg_apps,{BackgroundMusic,MusicGC,"Music"}) -- Starting background Music module
226 | elseif Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
227 | CallMainMenu()
228 | not_started = true
229 | if current_song ~= nil then
230 | CloseBGApp("Music")
231 | end
232 | elseif (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
233 | p_m = p_m - 1
234 | if (p_m >= 16) then
235 | master_index_m = p_m - 15
236 | end
237 | update_list = true
238 | elseif (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
239 | p_m = p_m + 1
240 | if (p_m >= 17) then
241 | master_index_m = p_m - 15
242 | end
243 | update_list = true
244 | end
245 | if (p_m < 1) then
246 | p_m = #my_songs
247 | if (p_m >= 17) then
248 | master_index_m = p_m - 15
249 | end
250 | elseif (p_m > #my_songs) then
251 | master_index_m = 0
252 | p_m = 1
253 | end
254 | end
--------------------------------------------------------------------------------
/LUA/modules/clock.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Clock" mode
2 | mode = "Clock"
3 |
4 | -- Internal module settings
5 | FreeIconTopbar("Clock")
6 | screenshots = false
7 | function Cronometer(millisecs)
8 | secs = millisecs / 1000
9 | mins = secs / 60
10 | hours = math.floor(mins / 60)
11 | mins = math.floor(mins % 60)
12 | secs = math.floor(secs % 60)
13 | millisecs = math.floor(millisecs % 1000)
14 | if mins < 10 then
15 | mins = "0"..mins
16 | end
17 | if secs < 10 then
18 | secs = "0"..secs
19 | end
20 | while string.len(tostring(millisecs)) < 3 do
21 | millisecs = "0"..millisecs
22 | end
23 | return hours..":"..mins..":"..secs..":"..millisecs
24 | end
25 | cronometer = false
26 | crono_time = math.tointeger(0)
27 |
28 | -- Module background code
29 | function BackgroundClock()
30 | if my_alarm_time[1] == nil then -- Countdown
31 | if Timer.getTime(count_crono) / 1000 > my_alarm_time[3] then
32 | Sound.play(alarm_sound,LOOP,0x08,0x09)
33 | ShowWarning("Countdown ended.")
34 | CloseBGApp("Clock")
35 | end
36 | else -- Alarm
37 |
38 | -- Blit Alarm alert on Main Menu
39 | if module == "Main Menu" then
40 | Screen.fillEmptyRect(5,200,210,230,black,TOP_SCREEN)
41 | Screen.fillRect(6,199,211,229,white,TOP_SCREEN)
42 | Font.print(ttf,8,214,"Alarm: "..my_alarm_time[1]..":"..my_alarm_time[2]..":"..my_alarm_time[3],black,TOP_SCREEN)
43 | end
44 |
45 | h,m,s = System.getTime()
46 | if h == tonumber(my_alarm_time[1]) and m == tonumber(my_alarm_time[2]) and s == tonumber(my_alarm_time[3]) then
47 | Sound.play(alarm_sound,LOOP,0x08,0x09)
48 | ShowWarning("Alarm clock!")
49 | CloseBGApp("Clock")
50 | end
51 | end
52 | end
53 |
54 | function ClockGC()
55 | FreeIconTopbar("Clock")
56 | if Sound.isPlaying(alarm_sound) then
57 | Sound.pause(alarm_sound)
58 | end
59 | Sound.close(alarm_sound)
60 | if my_alarm_time[1] == nil then
61 | Timer.destroy(count_crono)
62 | end
63 | set_alarm = nil
64 | alarm_start = nil
65 | alarm_sound = nil
66 | end
67 |
68 | -- Rendering functions
69 | function AppTopScreenRender()
70 | Graphics.fillRect(5,395,40,220,black)
71 | Graphics.fillRect(6,394,41,219,white)
72 | end
73 |
74 | function AppBottomScreenRender()
75 |
76 | if set_alarm ~= nil then
77 | Graphics.fillRect(5,100,75,95,black)
78 | Graphics.fillRect(6,99,76,94,white)
79 | end
80 |
81 | end
82 |
83 | -- Module main cycle
84 | function AppMainCycle()
85 |
86 | -- Draw controls info
87 | h,m,s = System.getTime()
88 | if m < 10 then
89 | m = "0"..m
90 | end
91 | if s < 10 then
92 | s = "0"..s
93 | end
94 | Font.print(ttf,9,45,"Current time: "..h..":"..m..":"..s,black,TOP_SCREEN)
95 | if cronometer then
96 | crono_time = Timer.getTime(crono)
97 | Font.print(ttf,9,70,"A = Resume/Pause Chronometer",black,TOP_SCREEN)
98 | else
99 | Font.print(ttf,9,70,"A = Start Chronometer",black,TOP_SCREEN)
100 | end
101 | Font.print(ttf,9,85,"X = Set Countdown",black,TOP_SCREEN)
102 | Font.print(ttf,9,100,"Y = Set Alarm",black,TOP_SCREEN)
103 | Font.print(ttf,9,115,"L = Start Countdown/Alarm",black,TOP_SCREEN)
104 | Font.print(ttf,9,130,"R = Stop Countdown/Alarm",black,TOP_SCREEN)
105 | Font.print(ttf,9,145,"SELECT = Reset Chronometer",black,TOP_SCREEN)
106 | Font.print(ttf,9,160,"B = Return Main Menu",black,TOP_SCREEN)
107 |
108 | -- Reset x,y coordinates
109 | y = 50
110 | x = 15
111 |
112 | -- Sets keyboard triggering
113 | i = 1
114 | z = 1
115 | x_c = x+195
116 | y_c = y-5
117 | while (i <= 12) do
118 | exec = false
119 | Screen.fillEmptyRect(x_c,x_c+30,y_c,y_c+25,black,BOTTOM_SCREEN)
120 | Screen.fillRect(x_c+1,x_c+29,y_c+1,y_c+24,white,BOTTOM_SCREEN)
121 | if Controls.check(pad,KEY_TOUCH) and not Controls.check(oldpad,KEY_TOUCH) then
122 | c1,c2 = Controls.readTouch()
123 | if c1 >= x_c and c2 >= y_c then
124 | if c1 < x_c + 25 and c2 < y_c + 25 then
125 | exec = true
126 | end
127 | end
128 | end
129 | if exec then
130 | if set_alarm ~= nil then
131 | if set_alarm and j < 7 and i < 11 then
132 | if i == 10 then
133 | alarm_table[j] = 0
134 | else
135 | alarm_table[j] = i
136 | end
137 | j=j+1
138 | if alarm_table[1] >= 2 then
139 | alarm_table[1] = 2
140 | if alarm_table[2] > 3 then
141 | alarm_table[2] = 3
142 | end
143 | end
144 | if alarm_table[3] > 5 then
145 | alarm_table[3] = 5
146 | end
147 | if alarm_table[5] > 5 then
148 | alarm_table[5] = 5
149 | end
150 | elseif set_alarm and i > 10 then
151 | if i == 11 then
152 | if j > 1 then
153 | alarm_table[j-1] = 0
154 | j = j - 1
155 | end
156 | else
157 | j = 1
158 | alarm_table = {0,0,0,0,0,0}
159 | end
160 | else
161 | if i < 10 then
162 | if countdown_time == 0 then
163 | countdown_time = i
164 | else
165 | countdown_time = tonumber(tostring(countdown_time)..i)
166 | end
167 | else
168 | if i == 11 then
169 | countdown_time = math.floor(countdown_time / 10)
170 | elseif i == 12 then
171 | countdown_time = 0
172 | else
173 | countdown_time = tonumber(tostring(countdown_time)..0)
174 | end
175 | end
176 | end
177 | end
178 | end
179 | i=i+1
180 | z=z+1
181 | x_c = x_c + 30
182 | if z > 3 then
183 | x_c = x+195
184 | y_c = y_c + 25
185 | z = 1
186 | end
187 | end
188 |
189 | -- Draw alarm config
190 | if set_alarm ~= nil then
191 | if set_alarm then
192 | k = 1
193 | my_alarm = ""
194 | while k < 7 do
195 | my_alarm = my_alarm .. alarm_table[k]
196 | if (k == 2 or k == 4) then
197 | my_alarm = my_alarm .. ":"
198 | end
199 | k = k + 1
200 | end
201 | Font.print(ttf,9,80,my_alarm,black,BOTTOM_SCREEN)
202 | end
203 | end
204 |
205 | -- Draw numeric keyboard
206 | Font.print(ttf,x+200,y,"1",black,BOTTOM_SCREEN)
207 | Font.print(ttf,x+230,y,"2",black,BOTTOM_SCREEN)
208 | Font.print(ttf,x+260,y,"3",black,BOTTOM_SCREEN)
209 | Font.print(ttf,x+200,y+25,"4",black,BOTTOM_SCREEN)
210 | Font.print(ttf,x+230,y+25,"5",black,BOTTOM_SCREEN)
211 | Font.print(ttf,x+260,y+25,"6",black,BOTTOM_SCREEN)
212 | Font.print(ttf,x+200,y+50,"7",black,BOTTOM_SCREEN)
213 | Font.print(ttf,x+230,y+50,"8",black,BOTTOM_SCREEN)
214 | Font.print(ttf,x+260,y+50,"9",black,BOTTOM_SCREEN)
215 | Font.print(ttf,x+200,y+75,"0",black,BOTTOM_SCREEN)
216 | Font.print(ttf,x+230,y+75,"D",black,BOTTOM_SCREEN)
217 | Font.print(ttf,x+260,y+75,"C",black,BOTTOM_SCREEN)
218 |
219 | -- Draw Cronometer/Alarm stats
220 | Font.print(ttf,9,185,"Chronometer: "..Cronometer(crono_time),black,TOP_SCREEN)
221 | if alarm_start ~= nil then
222 | if my_alarm_time[1] == nil then
223 | Font.print(ttf,9,198,"Countdown set for: "..my_alarm_time[3].." seconds.",black,TOP_SCREEN)
224 | else
225 | Font.print(ttf,9,198,"Alarm set for: "..my_alarm_time[1]..":"..my_alarm_time[2]..":"..my_alarm_time[3],black,TOP_SCREEN)
226 | end
227 | end
228 |
229 | -- Sets controls triggering
230 | if Controls.check(pad,KEY_R) and not Controls.check(oldpad,KEY_R) and alarm_start ~= nil then
231 | CloseBGApp("Clock")
232 | elseif Controls.check(pad,KEY_L) and not Controls.check(oldpad,KEY_L) and set_alarm ~= nil then
233 | alarm_sound = Sound.openOgg(theme_dir.."/sounds/alarm.ogg")
234 | if set_alarm then
235 | alarm_start = false
236 | hours = alarm_table[1]..alarm_table[2]
237 | minutes = alarm_table[3]..alarm_table[4]
238 | secs = alarm_table[5]..alarm_table[6]
239 | my_alarm_time = {hours,minutes,secs}
240 | else
241 | my_alarm_time = {nil,nil,countdown_time}
242 | count_crono = Timer.new()
243 | alarm_start = false
244 | end
245 | set_alarm = nil
246 | table.insert(bg_apps,{BackgroundClock,ClockGC,"Clock"}) -- Adding Clock module to background apps
247 | elseif Controls.check(pad,KEY_A) and not Controls.check(oldpad,KEY_A) then
248 | if not cronometer then
249 | crono = Timer.new()
250 | cronometer = true
251 | else
252 | if Timer.isPlaying(crono) then
253 | Timer.pause(crono)
254 | else
255 | Timer.resume(crono)
256 | end
257 | end
258 | elseif Controls.check(pad,KEY_SELECT) and not Controls.check(oldpad,KEY_SELECT) then
259 | if cronometer then
260 | Timer.reset(crono)
261 | end
262 | elseif Controls.check(pad,KEY_Y) and not Controls.check(oldpad,KEY_Y) then
263 | set_alarm = true
264 | alarm_table = {0,0,0,0,0,0}
265 | j=1
266 | elseif Controls.check(pad,KEY_X) and not Controls.check(oldpad,KEY_X) then
267 | set_alarm = false
268 | countdown_time = 0
269 | elseif Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
270 | if cronometer then
271 | Timer.destroy(crono)
272 | end
273 | if alarm_sound ~= nil then
274 | AddIconTopbar(theme_dir.."/images/clock_icon.jpg","Clock")
275 | end
276 | CallMainMenu()
277 | end
278 | end
--------------------------------------------------------------------------------
/LUA/modules/extdata.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Extdata" mode
2 | mode = "Extdata"
3 |
4 | -- Internal module settings
5 | if extdata_backup == nil then
6 | ShowWarning("Extdata listing will take time to be generated. Press OK to start list generation.")
7 | files_table = System.scanExtdata()
8 | extdata_backup = files_table
9 | end
10 | DisableRenderer()
11 | update_bottom_screen = true
12 | update_top_screen = true
13 | ui_enabled = false
14 | extdata_directory = "/"
15 | white = Color.new(255,255,255)
16 | black = Color.new(0,0,0)
17 | red = Color.new(255,0,0)
18 | green = Color.new(0,255,0)
19 | menu_color = Color.new(255,255,255)
20 | selected_color = Color.new(0,255,0)
21 | selected_item = Color.new(0,0,200,50)
22 | hex_values = {}
23 | hex_text = {}
24 | updateTXT = false
25 | select_mode = false
26 | update_main_extdata = true
27 | action_check = false
28 | move_base = nil
29 | copy_base = nil
30 | x_print = 0
31 | y_print = 0
32 | MAX_RAM_ALLOCATION = 10485760
33 | copy_type = 0
34 | move_type = 0
35 | txt_index = 0
36 | txt_words = 0
37 | txt_i = 0
38 | old_indexes = {}
39 | p = 1
40 | master_index = 0
41 | function DumpFile(input,archive)
42 | inp = io.open(extdata_directory..input,FREAD,archive)
43 | if System.doesFileExist("/"..input) then
44 | System.deleteFile("/"..input)
45 | end
46 | out = io.open("/"..string.format('%02X',archive).."_"..input,FCREATE)
47 | size = io.size(inp)
48 | index = 0
49 | while (index+(MAX_RAM_ALLOCATION/2) < size) do
50 | io.write(out,index,io.read(inp,index,MAX_RAM_ALLOCATION/2),(MAX_RAM_ALLOCATION/2))
51 | index = index + (MAX_RAM_ALLOCATION/2)
52 | end
53 | if index < size then
54 | io.write(out,index,io.read(inp,index,size-index),(size-index))
55 | end
56 | io.close(inp)
57 | io.close(out)
58 | end
59 | function RestoreFile(input,archive)
60 | inp = io.open("/"..string.format('%02X',archive).."_"..input,FREAD)
61 | out = io.open(extdata_directory..input,FWRITE,archive)
62 | if io.size(inp) <= io.size(out) then
63 | size = io.size(inp)
64 | index = 0
65 | while (index+(MAX_RAM_ALLOCATION/2) < size) do
66 | io.write(out,index,io.read(inp,index,MAX_RAM_ALLOCATION/2),(MAX_RAM_ALLOCATION/2))
67 | index = index + (MAX_RAM_ALLOCATION/2)
68 | end
69 | if index < size then
70 | io.write(out,index,io.read(inp,index,size-index),(size-index))
71 | end
72 | end
73 | io.close(inp)
74 | io.close(out)
75 | end
76 | function RestoreFolder(input,archive)
77 | files = System.listDirectory("/"..string.format('%02X',archive).."_"..input)
78 | for z, file in pairs(files) do
79 | if (file.directory) then
80 | RestoreFolder(input.."/"..file.name,archive)
81 | else
82 | RestoreFile(input.."/"..file.name,archive)
83 | end
84 | end
85 | end
86 | function OpenExtdataFile(text, archive)
87 | ExtGC()
88 | current_file = io.open(extdata_directory..text,FREAD,archive)
89 | txt_index = 0
90 | updateTXT = true
91 | end
92 | function OpenDirectory(text,archive_id)
93 | i=0
94 | if text == ".." then
95 | j=-2
96 | while string.sub(extdata_directory,j,j) ~= "/" do
97 | j=j-1
98 | end
99 | extdata_directory = string.sub(extdata_directory,1,j)
100 | else
101 | extdata_directory = extdata_directory..text.."/"
102 | end
103 | if extdata_directory == "/" then
104 | files_table = extdata_backup
105 | else
106 | files_table = System.listExtdataDir(extdata_directory,archive_id)
107 | local extra = {}
108 | extra.name = ".."
109 | extra.size = 0
110 | extra.directory = true
111 | extra.archive = archive_id
112 | table.insert(files_table,extra)
113 | end
114 | end
115 | function ExtGC()
116 | if current_file ~= nil then
117 | update_top_screen = true
118 | io.close(current_file)
119 | old_indexes = {}
120 | txt_i = 0
121 | end
122 | current_file = nil
123 | end
124 | function DumpFolder(input,archive)
125 | files = System.listExtdataDir(extdata_directory..input,archive)
126 | System.createDirectory("/"..string.format('%02X',archive).."_"..input)
127 | for z, file in pairs(files) do
128 | if (file.directory) then
129 | DumpFolder(input.."/"..file.name,archive)
130 | else
131 | DumpFile(input.."/"..file.name,archive)
132 | end
133 | end
134 | end
135 | function BuildHex(file, index)
136 | MAX_LENGTH = 120
137 | SIZE = io.size(file)
138 | if ((index + MAX_LENGTH) < SIZE) then
139 | READ_LENGTH = MAX_LENGTH
140 | else
141 | READ_LENGTH = SIZE - index
142 | end
143 | if (index < SIZE) then
144 | hex_text = {}
145 | hex_values = {}
146 | text = io.read(file,index,READ_LENGTH)
147 | t = 1
148 | while (t <= 15) do
149 | if ((t*8) > string.len(text)) then
150 | temp = string.sub(text,1+(t-1)*8,-1)
151 | else
152 | temp = string.sub(text,1+(t-1)*8,t*8)
153 | end
154 | t2 = 1
155 | while t2 <= string.len(temp) do
156 | table.insert(hex_values,string.byte(temp,t2))
157 | t2 = t2 + 1
158 | end
159 | table.insert(hex_text,temp)
160 | t = t + 1
161 | end
162 | table.insert(old_indexes, index)
163 | index = index + READ_LENGTH
164 | txt_i = txt_i + 1
165 | end
166 | return index
167 | end
168 | function PrintTop()
169 | Screen.clear(TOP_SCREEN)
170 | if (current_file == nil) then
171 | Font.print(ttf,0,0,"Basic Controls:",white,TOP_SCREEN)
172 | Font.print(ttf,0,15,"A = Open file/folder",white,TOP_SCREEN)
173 | Font.print(ttf,0,30,"X = Restore file/folder from SD card",white,TOP_SCREEN)
174 | Font.print(ttf,0,45,"Y = Dump file/folder to SD card",white,TOP_SCREEN)
175 | Font.print(ttf,0,60,"B = Return Main Menu",white,TOP_SCREEN)
176 | Font.print(ttf,0,75,"Left/Right = Scroll file",white,TOP_SCREEN)
177 | else
178 | for l, line in pairs(hex_text) do
179 | Font.print(ttf,280,(l-1)*15,string.gsub(line,"\0"," "),white,TOP_SCREEN)
180 | temp = 1
181 | while (temp <= string.len(line)) do
182 | if (temp % 2 == 0) then
183 | Font.print(ttf,0+(temp-1)*30,(l-1)*15,string.format('%02X', hex_values[(l-1)*8+temp]),white,TOP_SCREEN)
184 | else
185 | Font.print(ttf,0+(temp-1)*30,(l-1)*15,string.format('%02X', hex_values[(l-1)*8+temp]),red,TOP_SCREEN)
186 | end
187 | temp = temp + 1
188 | end
189 | end
190 | Font.print(ttf,0,225,"Offset: 0x" .. string.format('%X', old_indexes[#old_indexes]) .. " (" .. (old_indexes[#old_indexes]) .. ")",white,TOP_SCREEN)
191 | end
192 | end
193 | function PrintBottom()
194 | base_y = 0
195 | Screen.clear(BOTTOM_SCREEN)
196 | for l, file in pairs(files_table) do
197 | if (base_y > 226) then
198 | break
199 | end
200 | if (l >= master_index) then
201 | if (l==p) then
202 | Screen.fillRect(0,319,base_y,base_y+15,selected_item,BOTTOM_SCREEN)
203 | color = selected_color
204 | else
205 | color = menu_color
206 | end
207 | if file.name == ".." then
208 | CropPrint(0,base_y,file.name,color,BOTTOM_SCREEN)
209 | else
210 | CropPrint(0,base_y,file.name.." ["..string.format('%02X',file.archive).."]",color,BOTTOM_SCREEN)
211 | end
212 | base_y = base_y + 15
213 | end
214 | end
215 | end
216 |
217 | -- Module main cycle
218 | function AppMainCycle()
219 | if update_top_screen then
220 | if (updateTXT) then
221 | txt_index = BuildHex(current_file,txt_index)
222 | updateTXT = false
223 | end
224 | OneshotPrint(PrintTop)
225 | update_top_screen = false
226 | end
227 | if update_bottom_screen then
228 | OneshotPrint(PrintBottom)
229 | update_bottom_screen = false
230 | end
231 |
232 | -- Base Controls Functions
233 | if (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
234 | p = p - 1
235 | if (p >= 16) then
236 | master_index = p - 15
237 | end
238 | update_bottom_screen = true
239 | elseif (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
240 | p = p + 1
241 | if (p >= 17) then
242 | master_index = p - 15
243 | end
244 | update_bottom_screen = true
245 | end
246 | if (p < 1) then
247 | p = #files_table
248 | if (p >= 17) then
249 | master_index = p - 15
250 | end
251 | elseif (p > #files_table) then
252 | master_index = 0
253 | p = 1
254 | end
255 | if (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
256 | if (files_table[p].directory) then
257 | OpenDirectory(files_table[p].name,files_table[p].archive)
258 | p=1
259 | master_index=0
260 | update_bottom_screen = true
261 | else
262 | OpenExtdataFile(files_table[p].name,files_table[p].archive)
263 | end
264 | elseif (Controls.check(pad,KEY_X)) and not (Controls.check(oldpad,KEY_X)) then
265 | if (files_table[p].directory) then
266 | RestoreFolder(files_table[p].name,files_table[p].archive)
267 | else
268 | RestoreFile(files_table[p].name,files_table[p].archive)
269 | end
270 | elseif (Controls.check(pad,KEY_Y)) and not (Controls.check(oldpad,KEY_Y)) then
271 | if (files_table[p].directory) then
272 | DumpFolder(files_table[p].name,files_table[p].archive)
273 | else
274 | DumpFile(files_table[p].name,files_table[p].archive)
275 | end
276 | elseif (Controls.check(pad,KEY_B)) and not (Controls.check(oldpad,KEY_B)) then
277 | ExtGC()
278 | CallMainMenu()
279 | elseif (Controls.check(pad,KEY_DLEFT)) and not (Controls.check(oldpad,KEY_DLEFT)) then
280 | if current_file ~= nil then
281 | update_top_screen = true
282 | if (txt_i > 1) then
283 | updateTXT = true
284 | table.remove(old_indexes)
285 | txt_index = table.remove(old_indexes)
286 | txt_i = txt_i - 2
287 | end
288 | end
289 | elseif (Controls.check(pad,KEY_DRIGHT)) and not (Controls.check(oldpad,KEY_DRIGHT)) then
290 | if current_file ~= nil then
291 | update_top_screen = true
292 | updateTXT = true
293 | end
294 | elseif (Controls.check(pad,KEY_TOUCH)) then
295 | x,y = Controls.readTouch()
296 | new_index = math.ceil(y/15)
297 | if (new_index <= #files_table) then
298 | if master_index > 0 then
299 | p = new_index + master_index - 1
300 | else
301 | p = new_index
302 | end
303 | end
304 | end
305 | if not (Controls.check(pad,KEY_TOUCH)) then
306 | master_index = p - 15
307 | end
308 | end
309 |
--------------------------------------------------------------------------------
/LUA/modules/video.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Video" mode
2 | mode = "Video"
3 |
4 | -- Internal module settings
5 | master_index_v = 0
6 | if Screen.get3DLevel() > 0 then
7 | Screen.enable3D()
8 | end
9 | old_3d = Screen.get3DLevel()
10 | p_v = 1
11 | update_frame = false
12 | ui_enabled = false
13 | DisableRenderer()
14 | not_started_v = true
15 | tmp = System.listDirectory("/VIDEO")
16 | my_videos = {}
17 | hide = false
18 | for i,file in pairs(tmp) do
19 | if not file.directory then
20 | tmp_file = io.open("/VIDEO/"..file.name,FREAD)
21 | magic = io.read(tmp_file,0,4)
22 | if magic == "BMPV" then
23 | table.insert(my_videos,{file.name,"BMPV"})
24 | elseif magic == "JPGV" then
25 | table.insert(my_videos,{file.name,"JPGV"})
26 | end
27 | io.close(tmp_file)
28 | end
29 | end
30 | if #my_videos > 0 then
31 | current_type = my_videos[1][2]
32 | if current_type == "BMPV" then
33 | current_file = BMPV.load("/VIDEO/"..my_videos[1][1])
34 | current_size = BMPV.getSize(current_file)
35 | elseif current_type == "JPGV" then
36 | current_file = JPGV.load("/VIDEO/"..my_videos[1][1])
37 | current_size = JPGV.getSize(current_file)
38 | end
39 | else
40 | ShowError("VIDEO folder is empty.")
41 | CallMainMenu()
42 | end
43 |
44 | frame_succession = Timer.new()
45 | current_frame = 60
46 |
47 | -- Rendering functions
48 | function AppTopScreenRender()
49 | Graphics.fillRect(5,395,40,220,black)
50 | Graphics.fillRect(6,394,41,219,white)
51 | end
52 |
53 | function AppBottomScreenRender()
54 | end
55 |
56 | -- Module main cycle
57 | function AppMainCycle()
58 | slide_status = Screen.get3DLevel()
59 |
60 | -- Clear bottom screen
61 | Screen.clear(BOTTOM_SCREEN)
62 | if not_started_v then
63 |
64 | -- Update preview info
65 | if update_frame then
66 | Timer.reset(frame_succession)
67 | update_frame = false
68 | current_type = my_videos[p_v][2]
69 | if current_type == "BMPV" then
70 | current_file = BMPV.load("/VIDEO/"..my_videos[p_v][1])
71 | current_size = BMPV.getSize(current_file)
72 | elseif current_type == "JPGV" then
73 | current_file = JPGV.load("/VIDEO/"..my_videos[p_v][1])
74 | current_size = JPGV.getSize(current_file)
75 | end
76 | current_frame = 60
77 | Timer.reset(frame_succession)
78 | end
79 |
80 | -- Showing Preview frames
81 | if current_type == "BMPV" then
82 | BMPV.showFrame(0,0,current_file,current_frame,TOP_SCREEN)
83 | elseif current_type == "JPGV" then
84 | if (slide_status == 0) then
85 | JPGV.showFrame(0,0,current_file,current_frame,TOP_SCREEN,false)
86 | else
87 | JPGV.showFrame(0,0,current_file,current_frame,TOP_SCREEN,true)
88 | end
89 | end
90 | if Timer.getTime(frame_succession) > 5000 then
91 | current_frame = math.ceil(current_frame + (current_size / 10))
92 | if current_frame > current_size then
93 | current_frame = 1
94 | end
95 | Timer.reset(frame_succession)
96 | end
97 |
98 | -- Showing files list
99 | base_y = 0
100 | for l, file in pairs(my_videos) do
101 | if (base_y > 226) then
102 | break
103 | end
104 | if (l >= master_index_v) then
105 | if (l==p_v) then
106 | base_y2 = base_y
107 | if (base_y) == 0 then
108 | base_y = 2
109 | end
110 | Screen.fillRect(0,319,base_y-2,base_y2+12,selected_item,BOTTOM_SCREEN)
111 | color = selected
112 | if (base_y) == 2 then
113 | base_y = 0
114 | end
115 | else
116 | color = white
117 | end
118 | DebugCropPrint(0,base_y,file[1],color,BOTTOM_SCREEN)
119 | base_y = base_y + 15
120 | end
121 | end
122 |
123 | else
124 |
125 | -- Video playback
126 | if current_type == "JPGV" then
127 | if (slide_status == 0) then
128 | JPGV.draw(0,0,current_file,TOP_SCREEN,false)
129 | else
130 | JPGV.draw(0,0,current_file,TOP_SCREEN,true)
131 | end
132 | else
133 | BMPV.draw(0,0,current_file,TOP_SCREEN)
134 | end
135 |
136 | if not hide then
137 | -- Video playback info (JPGV side)
138 | if current_type == "JPGV" then
139 | Screen.debugPrint(0,10,"A = Pause/Resume",white,BOTTOM_SCREEN)
140 | Screen.debugPrint(0,25,"Y = Close video",white,BOTTOM_SCREEN)
141 | Screen.debugPrint(0,40,"B = Return Main Menu",white,BOTTOM_SCREEN)
142 | Screen.debugPrint(0,55,"X = Hide Bottom Screen",white,BOTTOM_SCREEN)
143 | Screen.debugPrint(0,100,"Infos:",white,BOTTOM_SCREEN)
144 | Screen.debugPrint(0,114,"FPS: "..JPGV.getFPS(current_file),white,BOTTOM_SCREEN)
145 | cur_time_sec = math.ceil(JPGV.getFrame(current_file) / JPGV.getFPS(current_file))
146 | cur_time_min = 0
147 | while (cur_time_sec >= 60) do
148 | cur_time_sec = cur_time_sec - 60
149 | cur_time_min = cur_time_min + 1
150 | end
151 | if (cur_time_sec < 10) then
152 | Screen.debugPrint(0,128,"Time: " .. cur_time_min .. ":0" .. cur_time_sec .. " / " .. tot_time_min .. ":" .. tot_time_sec,white,BOTTOM_SCREEN)
153 | else
154 | Screen.debugPrint(0,128,"Time: " .. cur_time_min .. ":" .. cur_time_sec .. " / " .. tot_time_min .. ":" .. tot_time_sec,white,BOTTOM_SCREEN)
155 | end
156 | Screen.debugPrint(0,142,"Samplerate: "..JPGV.getSrate(current_file),white,BOTTOM_SCREEN)
157 | percentage = ((JPGV.getFrame(current_file) * 100) / JPGV.getSize(current_file))
158 | Screen.debugPrint(0,200,"Percentage: " ..math.ceil(percentage) .. "%",white,BOTTOM_SCREEN)
159 | Screen.fillEmptyRect(2,318,214,234,white,BOTTOM_SCREEN)
160 | move = ((314 * percentage) / 100)
161 | Screen.fillRect(3,3 + math.ceil(move),215,233,white,BOTTOM_SCREEN)
162 |
163 | -- Video playback info (BMPV side)
164 | elseif current_type == "BMPV" then
165 | Screen.debugPrint(0,10,"A = Pause/Resume",white,BOTTOM_SCREEN)
166 | Screen.debugPrint(0,25,"Y = Close video",white,BOTTOM_SCREEN)
167 | Screen.debugPrint(0,40,"B = Return Main Menu",white,BOTTOM_SCREEN)
168 | Screen.debugPrint(0,55,"X = Power off Bottom Screen",white,BOTTOM_SCREEN)
169 | Screen.debugPrint(0,100,"Infos:",white,BOTTOM_SCREEN)
170 | Screen.debugPrint(0,114,"FPS: "..BMPV.getFPS(current_file),white,BOTTOM_SCREEN)
171 | cur_time_sec = math.ceil(BMPV.getFrame(current_file) / BMPV.getFPS(current_file))
172 | cur_time_min = 0
173 | while (cur_time_sec >= 60) do
174 | cur_time_sec = cur_time_sec - 60
175 | cur_time_min = cur_time_min + 1
176 | end
177 | if (cur_time_sec < 10) then
178 | Screen.debugPrint(0,128,"Time: " .. cur_time_min .. ":0" .. cur_time_sec .. " / " .. tot_time_min .. ":" .. tot_time_sec,white,BOTTOM_SCREEN)
179 | else
180 | Screen.debugPrint(0,128,"Time: " .. cur_time_min .. ":" .. cur_time_sec .. " / " .. tot_time_min .. ":" .. tot_time_sec,white,BOTTOM_SCREEN)
181 | end
182 | Screen.debugPrint(0,142,"Samplerate: "..BMPV.getSrate(current_file),white,BOTTOM_SCREEN)
183 | percentage = math.ceil((BMPV.getFrame(current_file) * 100) / BMPV.getSize(current_file))
184 | Screen.debugPrint(0,200,"Percentage: " ..percentage .. "%",white,BOTTOM_SCREEN)
185 | Screen.fillEmptyRect(2,318,214,234,white,BOTTOM_SCREEN)
186 | move = ((314 * percentage) / 100)
187 | Screen.fillRect(3,3 + math.ceil(move),215,233,white,BOTTOM_SCREEN)
188 | end
189 | end
190 |
191 | end
192 |
193 | -- 3D effect support
194 | if slide_status == 0 and old_3d ~= 0 then
195 | Screen.disable3D()
196 | elseif slide_status > 0 and old_3d == 0 then
197 | Screen.enable3D()
198 | end
199 |
200 | -- Sets controls triggering
201 | if (Controls.check(pad,KEY_Y)) and not (Controls.check(oldpad,KEY_Y)) and not (not_started_v) then
202 | Timer.reset(frame_succession)
203 | not_started_v = true
204 | if hide then
205 | Controls.enableScreen(BOTTOM_SCREEN)
206 | hide = false
207 | end
208 | if current_type == "JPGV" then
209 | JPGV.stop(current_file)
210 | else
211 | BMPV.stop(current_file)
212 | end
213 | elseif (Controls.check(pad,KEY_X)) and not (Controls.check(oldpad,KEY_X)) and not (not_started_v) then
214 | if hide then
215 | Controls.enableScreen(BOTTOM_SCREEN)
216 | else
217 | Controls.disableScreen(BOTTOM_SCREEN)
218 | end
219 | hide = not hide
220 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
221 | if not_started_v then
222 | not_started_v = false
223 | if current_type == "JPGV" then
224 | JPGV.start(current_file,NO_LOOP,0x08,0x09)
225 | tot_time_sec = math.ceil(JPGV.getSize(current_file) / JPGV.getFPS(current_file))
226 | tot_time_min = 0
227 | while (tot_time_sec >= 60) do
228 | tot_time_sec = tot_time_sec - 60
229 | tot_time_min = tot_time_min + 1
230 | end
231 | elseif current_type == "BMPV" then
232 | BMPV.start(current_file,NO_LOOP,0x08,0x09)
233 | tot_time_sec = math.ceil(BMPV.getSize(current_file) / BMPV.getFPS(current_file))
234 | tot_time_min = 0
235 | while (tot_time_sec >= 60) do
236 | tot_time_sec = tot_time_sec - 60
237 | tot_time_min = tot_time_min + 1
238 | end
239 | end
240 | else
241 | if current_type == "JPGV" then
242 | if JPGV.isPlaying(current_file) then
243 | JPGV.pause(current_file)
244 | else
245 | JPGV.resume(current_file)
246 | end
247 | else
248 | if BMPV.isPlaying(current_file) then
249 | BMPV.pause(current_file)
250 | else
251 | BMPV.resume(current_file)
252 | end
253 | end
254 | end
255 | elseif Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
256 | if (slide_status > 0) then
257 | Screen.disable3D()
258 | end
259 | if hide then
260 | Controls.enableScreen(BOTTOM_SCREEN)
261 | end
262 | CallMainMenu()
263 | Timer.destroy(frame_succession)
264 | if current_type == "JPGV" then
265 | JPGV.stop(current_file)
266 | JPGV.unload(current_file)
267 | elseif current_type == "BMPV" then
268 | BMPV.stop(current_file)
269 | BMPV.unload(current_file)
270 | end
271 | elseif (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) and not_started_v then
272 | if current_type == "JPGV" then
273 | JPGV.unload(current_file)
274 | elseif current_type == "BMPV" then
275 | BMPV.unload(current_file)
276 | end
277 | p_v = p_v - 1
278 | if (p_v >= 16) then
279 | master_index_v = p_v - 15
280 | end
281 | update_frame = true
282 | elseif (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) and not_started_v then
283 | if current_type == "JPGV" then
284 | JPGV.unload(current_file)
285 | elseif current_type == "BMPV" then
286 | BMPV.unload(current_file)
287 | end
288 | p_v = p_v + 1
289 | if (p_v >= 17) then
290 | master_index_v = p_v - 15
291 | end
292 | update_frame = true
293 | end
294 | if (p_v < 1) then
295 | p_v = #my_videos
296 | if (p_v >= 17) then
297 | master_index_v = p_v - 15
298 | end
299 | elseif (p_v > #my_videos) then
300 | master_index_v = 0
301 | p_v = 1
302 | end
303 | old_3d = slide_status
304 | end
--------------------------------------------------------------------------------
/LUA/sun_index.lua:
--------------------------------------------------------------------------------
1 | -- Create system folders if doesn't exist
2 | System.createDirectory("/VIDEO")
3 | System.createDirectory("/MUSIC")
4 | System.createDirectory("/DCIM")
5 |
6 | -- Open config file and system files
7 | dofile("/config.sun")
8 | theme_dir = main_dir.."/themes/"..theme
9 | Graphics.init()
10 | bg = Graphics.loadImage(theme_dir.."/images/bg.jpg")
11 | if System.doesFileExist(theme_dir.."/images/music.jpg") then
12 | ext = ".jpg"
13 | else
14 | ext = ".png"
15 | end
16 | music = Graphics.loadImage(theme_dir.."/images/music"..ext)
17 | video = Graphics.loadImage(theme_dir.."/images/video"..ext)
18 | info = Graphics.loadImage(theme_dir.."/images/info"..ext)
19 | fb = Graphics.loadImage(theme_dir.."/images/fb"..ext)
20 | game = Graphics.loadImage(theme_dir.."/images/game"..ext)
21 | camera = Graphics.loadImage(theme_dir.."/images/camera"..ext)
22 | gallery = Graphics.loadImage(theme_dir.."/images/gallery"..ext)
23 | cia = Graphics.loadImage(theme_dir.."/images/cia"..ext)
24 | extdata = Graphics.loadImage(theme_dir.."/images/extdata"..ext)
25 | calc = Graphics.loadImage(theme_dir.."/images/calc"..ext)
26 | mail = Graphics.loadImage(theme_dir.."/images/mail"..ext)
27 | themes = Graphics.loadImage(theme_dir.."/images/themes"..ext)
28 | clock = Graphics.loadImage(theme_dir.."/images/clock"..ext)
29 | ftp = Graphics.loadImage(theme_dir.."/images/ftp"..ext)
30 | charge = Graphics.loadImage(theme_dir.."/images/charge"..ext)
31 | record = Graphics.loadImage(theme_dir.."/images/record"..ext)
32 | b0 = Graphics.loadImage(theme_dir.."/images/0"..ext)
33 | b1 = Graphics.loadImage(theme_dir.."/images/1"..ext)
34 | b2 = Graphics.loadImage(theme_dir.."/images/2"..ext)
35 | b3 = Graphics.loadImage(theme_dir.."/images/3"..ext)
36 | b4 = Graphics.loadImage(theme_dir.."/images/4"..ext)
37 | b5 = Graphics.loadImage(theme_dir.."/images/5"..ext)
38 | ttf = Font.load(theme_dir.."/fonts/main.ttf")
39 | dofile(theme_dir.."/colors.lua")
40 | Font.setPixelSizes(ttf,18)
41 |
42 | -- Setting some system vars, funcs, etc...
43 | bg_apps = {}
44 | topbar_icons = {}
45 | old_headset = Controls.headsetStatus()
46 | in_game = false
47 | renderer = true
48 | refresh_screen = true
49 | refresh_screen2 = true
50 | Sound.init()
51 | app_index = 1
52 | version = "0.5 PRO"
53 | ui_enabled = true
54 | screenshots = true
55 | oldpad = KEY_A
56 | module = "Main Menu"
57 | months = {"January", "February","March","April","May","June","July", "August", "September", "October", "November", "December"}
58 | days_table = {}
59 | dv,d,m,y = System.getDate()
60 | if (y % 400 == 0) or (y % 100 ~= 0 and y % 4 == 0) then
61 | month_days = {31,29,31,30,31,30,31,31,30,31,30,31}
62 | else
63 | month_days = {31,28,31,30,31,30,31,31,30,31,30,31}
64 | end
65 | i = 1
66 | if i == d then
67 | if dv == 7 then
68 | dv = 0
69 | end
70 | table.insert(days_table,dv)
71 | else
72 | tmp = ((d - i) % 7)
73 | if tmp > dv then
74 | my_dv = 7 + dv - tmp
75 | else
76 | my_dv = dv - tmp
77 | end
78 | if my_dv > 6 then
79 | my_dv = 0
80 | end
81 | table.insert(days_table,my_dv)
82 | end
83 | i = 2
84 | while i <= month_days[m] do
85 | my_dv = days_table[i-1] + 1
86 | if my_dv > 6 then
87 | my_dv = 0
88 | end
89 | table.insert(days_table,my_dv)
90 | i=i+1
91 | end
92 | -- Loading internal extra Sunshell functions
93 | dofile(main_dir.."/scripts/funcs.lua")
94 |
95 | -- Set detected build in use
96 | build_idx = System.checkBuild()
97 | if build_idx == 0 then
98 | build = "Ninjhax 1"
99 | elseif build_idx == 1 then
100 | build = "Custom Firmware"
101 | else
102 | build = "Ninjhax 2"
103 | end
104 |
105 | -- Setting modules as apps
106 | tools = {}
107 | table.insert(tools,{game,"/modules/game.lua","Applications"})
108 | table.insert(tools,{info,"/modules/info.lua","Console Info"})
109 | table.insert(tools,{camera,"/modules/camera.lua","Camera"})
110 | table.insert(tools,{gallery,"/modules/gallery.lua","Gallery"})
111 | table.insert(tools,{music,"/modules/music.lua","Musics"})
112 | table.insert(tools,{video,"/modules/video.lua","Videos"})
113 | table.insert(tools,{fb,"/modules/fb.lua","Filebrowser"})
114 | table.insert(tools,{cia,"/modules/cia.lua","CIA Manager"})
115 | table.insert(tools,{extdata,"/modules/extdata.lua","Extdata Manager"})
116 | table.insert(tools,{calc,"/modules/calc.lua","Calc"})
117 | table.insert(tools,{record,"/modules/record.lua","Record"})
118 | table.insert(tools,{clock,"/modules/clock.lua","Clock"})
119 | table.insert(tools,{ftp,"/modules/ftp.lua","FTP Server"})
120 | table.insert(tools,{mail,"/modules/mail.lua","Mail"})
121 | table.insert(tools,{themes,"/modules/themes.lua","Theme Manager"})
122 |
123 | -- Top screen rendering
124 | function drawTopScreenUI()
125 |
126 | -- Background
127 | if ui_enabled then
128 | Graphics.drawPartialImage(0,0,0,0,400,240,bg)
129 | end
130 |
131 | -- Calendar
132 | if mode == nil then
133 | Graphics.fillRect(75,320,40,200,black)
134 | Graphics.fillRect(76,319,41,199,white)
135 | end
136 |
137 | if ui_enabled then
138 |
139 | -- Topbar icons
140 | for i,icon in pairs(topbar_icons) do
141 | Graphics.drawImage(350-i*21,2,icon[1],TOP_SCREEN)
142 | end
143 |
144 | -- Battery and Wifi alert
145 | if System.isBatteryCharging() then
146 | Graphics.drawImage(350,2,charge)
147 | else
148 | battery_lv = System.getBatteryLife()
149 | if battery_lv == 0 then
150 | Graphics.drawImage(350,2,b0)
151 | elseif battery_lv == 1 then
152 | Graphics.drawImage(350,2,b1)
153 | elseif battery_lv == 2 then
154 | Graphics.drawImage(350,2,b2)
155 | elseif battery_lv == 3 then
156 | Graphics.drawImage(350,2,b3)
157 | elseif battery_lv == 4 then
158 | Graphics.drawImage(350,2,b4)
159 | else
160 | Graphics.drawImage(350,2,b5)
161 | end
162 | end
163 | if Network.isWifiEnabled() then
164 | net_lv = Network.getWifiLevel()
165 | if net_lv > 0 then
166 | Graphics.fillRect(326-#topbar_icons*21,331-#topbar_icons*21,14,18,green_wifi)
167 | if net_lv > 1 then
168 | Graphics.fillRect(333-#topbar_icons*21,338-#topbar_icons*21,8,18,green_wifi)
169 | if net_lv > 2 then
170 | Graphics.fillRect(340-#topbar_icons*21,345-#topbar_icons*21,2,18,green_wifi)
171 | end
172 | end
173 | end
174 | else
175 | Graphics.fillRect(340-#topbar_icons*21,345-#topbar_icons*21,16,18,red_wifi)
176 | Graphics.fillRect(333-#topbar_icons*21,338-#topbar_icons*21,16,18,red_wifi)
177 | Graphics.fillRect(326-#topbar_icons*21,331-#topbar_icons*21,16,18,red_wifi)
178 | end
179 | end
180 |
181 | end
182 |
183 | function drawBottomScreenUI()
184 | if ui_enabled then
185 | if refresh_screen then
186 | Graphics.drawPartialImage(0,0,40,240,320,240,bg)
187 | end
188 | end
189 | if mode == nil then
190 | x = 4
191 | y = 10
192 | for i,tool in pairs(tools) do
193 | if x > 300 then
194 | x = 4
195 | y = y + 58
196 | end
197 | if app_index == i then
198 | Graphics.fillRect(x-3,x+50,y-3,y+50,selected)
199 | end
200 | Graphics.drawImage(x,y,tool[1])
201 | if Controls.check(pad,KEY_TOUCH) and not Controls.check(oldpad,KEY_TOUCH) then
202 | tx,ty = Controls.readTouch()
203 | if tx >= x and tx <= x+48 and ty >= y and ty <= y+48 then
204 | module = tool[3]
205 | dofile(main_dir..tool[2])
206 | end
207 | end
208 | x = x + 53
209 | end
210 | end
211 | end
212 |
213 | -- Main cycle
214 | while true do
215 | Screen.refresh()
216 | pad = Controls.read()
217 |
218 | -- Headset tracking for Music module auto-start
219 | if old_headset and not Controls.headsetStatus() then -- Removing headset
220 | for i,app in pairs(bg_apps) do
221 | if app[3] == "Music" then
222 | if Sound.isPlaying(current_song) then
223 | Sound.pause(current_song)
224 | end
225 | break
226 | end
227 | end
228 | old_headset = false
229 | elseif not old_headset and Controls.headsetStatus() then -- Inserting headset
230 | done = false
231 | for i,app in pairs(bg_apps) do
232 | if app[3] == "Music" then
233 | if not Sound.isPlaying(current_song) then
234 | Sound.resume(current_song)
235 | end
236 | done = true
237 | break
238 | end
239 | end
240 | if not done then
241 | dofile(main_dir.."/scripts/music_api.lua")
242 | end
243 | old_headset = true
244 | end
245 |
246 | if renderer then
247 |
248 | -- Top screen rendering
249 | if refresh_screen2 then
250 | Graphics.initBlend(TOP_SCREEN)
251 | drawTopScreenUI()
252 | if mode ~= nil then
253 | AppTopScreenRender()
254 | end
255 | Graphics.termBlend()
256 | end
257 |
258 | -- Bottom screen rendering
259 | if refresh_screen then
260 | Graphics.initBlend(BOTTOM_SCREEN)
261 | drawBottomScreenUI()
262 | if mode ~= nil then
263 | AppBottomScreenRender()
264 | end
265 | Graphics.termBlend()
266 | end
267 |
268 | end
269 |
270 | -- Executing background apps
271 | for i,bg_app_code in pairs(bg_apps) do
272 | bg_app_code[1]()
273 | end
274 |
275 | -- Main menu
276 | if mode == nil then
277 | if widget == nil then
278 |
279 | -- Blit calendar
280 | dv,d,m,ye = System.getDate()
281 | i = 1
282 | x = 80
283 | y = 85
284 | Font.print(ttf,x+85,y-40,months[m].." "..ye,black,TOP_SCREEN)
285 | Font.print(ttf,x,y-20,"S",selected,TOP_SCREEN)
286 | Font.print(ttf,x+35,y-20,"M",selected,TOP_SCREEN)
287 | Font.print(ttf,x+70,y-20,"T",selected,TOP_SCREEN)
288 | Font.print(ttf,x+105,y-20,"W",selected,TOP_SCREEN)
289 | Font.print(ttf,x+140,y-20,"T",selected,TOP_SCREEN)
290 | Font.print(ttf,x+175,y-20,"F",selected,TOP_SCREEN)
291 | Font.print(ttf,x+210,y-20,"S",selected,TOP_SCREEN)
292 | while i <= month_days[m] do
293 | if i == d then
294 | Font.print(ttf,x + (days_table[i]) * 35,y,i,selected,TOP_SCREEN)
295 | else
296 | Font.print(ttf,x + (days_table[i]) * 35,y,i,black,TOP_SCREEN)
297 | end
298 | if days_table[i] == 6 then
299 | y = y + 20
300 | end
301 | i=i+1
302 | end
303 |
304 | end
305 |
306 | -- Setting digital pad controls triggering
307 | if Controls.check(pad,KEY_DUP) and not Controls.check(oldpad,KEY_DUP) then
308 | app_index = app_index - 6
309 | if app_index < 1 then
310 | while (app_index + 6) <= #tools do
311 | app_index = app_index + 6
312 | end
313 | end
314 | elseif Controls.check(pad,KEY_DDOWN) and not Controls.check(oldpad,KEY_DDOWN) then
315 | app_index = app_index + 6
316 | if app_index > #tools then
317 | app_index = app_index % 6
318 | if app_index == 0 then
319 | app_index = 6
320 | end
321 | end
322 | elseif Controls.check(pad,KEY_DLEFT) and not Controls.check(oldpad,KEY_DLEFT) then
323 | app_index = app_index - 1
324 | if app_index % 6 == 0 then
325 | app_index = app_index + 6
326 | if app_index > #tools then
327 | app_index = #tools
328 | end
329 | end
330 | elseif Controls.check(pad,KEY_DRIGHT) and not Controls.check(oldpad,KEY_DRIGHT) then
331 | app_index = app_index + 1
332 | if app_index % 6 == 1 then
333 | app_index = app_index - 6
334 | end
335 | if app_index > #tools then
336 | app_index = math.floor(app_index / 6) * 6 + 1
337 | end
338 | end
339 |
340 | -- Setting app starting by pressing A button
341 | if Controls.check(pad,KEY_A) and not Controls.check(oldpad,KEY_A) then
342 | module = tools[app_index][3]
343 | dofile(main_dir..tools[app_index][2])
344 | end
345 | else
346 |
347 | -- App cycles
348 | AppMainCycle()
349 |
350 | end
351 |
352 | -- Blit topbar info
353 | if ui_enabled then
354 |
355 | -- Blit clock
356 | hours,minutes,seconds = System.getTime()
357 | if minutes < 10 then
358 | minutes = "0"..minutes
359 | end
360 | if seconds < 10 then
361 | seconds = "0"..seconds
362 | end
363 | formatted_time = hours..":"..minutes..":"..seconds
364 | Font.print(ttf,276-#topbar_icons*21,3,formatted_time,white,TOP_SCREEN)
365 | Font.print(ttf,4,3,"Sunshell v."..version.." - "..module,white,TOP_SCREEN)
366 |
367 | end
368 |
369 | -- Sets up universal controls
370 | if Controls.check(pad,KEY_START) and not in_game and not Controls.check(oldpad,KEY_START) then
371 | GarbageCollection()
372 | for i,bg_apps_code in pairs(bg_apps) do
373 | bg_apps_code[2]()
374 | end
375 | for i,icon in pairs(topbar_icons) do
376 | Graphics.freeImage(icon[1])
377 | end
378 | Sound.term()
379 | Font.unload(ttf)
380 | Graphics.term()
381 | if start_dir == "/" and build ~= "Custom Firmware" then -- boot.3dsx patch
382 | System.reboot()
383 | else
384 | System.exit()
385 | end
386 | elseif Controls.check(pad,KEY_L) and not Controls.check(oldpad,KEY_L) and screenshots then
387 | screen_index = 0
388 | while System.doesFileExist("/DCIM/Sunshell_"..screen_index..".jpg") do
389 | screen_index = screen_index + 1
390 | end
391 | System.takeScreenshot("/DCIM/Sunshell_"..screen_index..".jpg",true)
392 | end
393 |
394 | Screen.waitVblankStart()
395 | Screen.flip()
396 | oldpad = pad
397 | if in_game then
398 | in_game = false
399 | end
400 | end
--------------------------------------------------------------------------------
/LUA/scripts/funcs.lua:
--------------------------------------------------------------------------------
1 | -- Internal SunShell variables
2 | -- ui_enabled = true/false -- Sets Sunshell UI state (CallMainMenu force automatically ui_enabled to true value)
3 | -- screenshots = true/false -- Sets Sunshell screenshot function through L button state (CallMainMenu force automatically screenshots to true value)
4 |
5 | -- Internal SunShell extra functions
6 |
7 | start_dir = System.currentDirectory()
8 |
9 | -- * explode
10 | -- PHP explode porting for LUA developing
11 | function explode(div,str)
12 | pos = 0
13 | arr = {}
14 | for st,sp in function() return string.find(str,div,pos,true) end do
15 | table.insert(arr,string.sub(str,pos,st-1))
16 | pos = sp + 1
17 | end
18 | table.insert(arr,string.sub(str,pos))
19 | return arr
20 | end
21 |
22 | -- * CallMainMenu
23 | -- Sets SunShell to Main Menu mode, usefull to exit from a module
24 | function CallMainMenu()
25 | mode = nil
26 | module = "Main Menu"
27 | ui_enabled = true
28 | renderer = true
29 | screenshots = true
30 | refresh_screen = true
31 | refresh_screen2 = true
32 | System.currentDirectory(start_dir)
33 | end
34 |
35 | -- * SetBottomRefresh
36 | -- Sets Refreshing Screen state for Bottom Screen
37 | function SetBottomRefresh(value)
38 | refresh_screen = value
39 | end
40 |
41 | -- * SetTopRefresh
42 | -- Sets Refreshing Screen state for Top Screen
43 | function SetTopRefresh(value)
44 | refresh_screen2 = value
45 | end
46 |
47 | -- * DisableRenderer
48 | -- Disable GPU renderer for both screens
49 | function DisableRenderer()
50 | renderer = false
51 | end
52 |
53 | -- * CustomRenderBottom
54 | -- Sets up a custom GPU rendering scene for bottom screen
55 | function CustomRenderBottom(func)
56 | Graphics.initBlend(BOTTOM_SCREEN)
57 | func()
58 | Graphics.termBlend()
59 | end
60 |
61 | -- * CustomRenderTop
62 | -- Sets up a custom GPU rendering scene for top screen
63 | function CustomRenderTop(func)
64 | Graphics.initBlend(TOP_SCREEN)
65 | func()
66 | Graphics.termBlend()
67 | end
68 |
69 | -- * CloseBGApp
70 | -- Close a selected BG App
71 | function CloseBGApp(my_app)
72 | for i, apps in pairs(bg_apps) do
73 | if apps[3] == my_app then
74 | apps[2]()
75 | table.remove(bg_apps,i)
76 | break
77 | end
78 | end
79 | end
80 |
81 | -- * FormatTime
82 | -- Format a number of seconds in a time-like string (Example: 123 seconds = 02:03)
83 | function FormatTime(seconds)
84 | minute = math.floor(seconds/60)
85 | seconds = seconds%60
86 | hours = math.floor(minute/60)
87 | minute = minute%60
88 | if minute < 10 then
89 | minute = "0"..minute
90 | end
91 | if seconds < 10 then
92 | seconds = "0"..seconds
93 | end
94 | if hours == 0 then
95 | return minute..":"..seconds
96 | else
97 | return hours..":"..minute..":"..seconds
98 | end
99 | end
100 |
101 | -- * GarbageCollection
102 | -- Free all allocated SunShell elements
103 | function GarbageCollection()
104 | Graphics.freeImage(bg)
105 | Graphics.freeImage(b0)
106 | Graphics.freeImage(b1)
107 | Graphics.freeImage(b2)
108 | Graphics.freeImage(b3)
109 | Graphics.freeImage(b4)
110 | Graphics.freeImage(b5)
111 | Graphics.freeImage(charge)
112 | for i,tool in pairs(tools) do
113 | Graphics.freeImage(tool[1])
114 | end
115 | end
116 |
117 | -- * CropPrint
118 | -- Used to print long strings on BOTTOM_SCREEN, it automatically crop too long strings
119 | function CropPrint(x, y, text, color, screen)
120 | if string.len(text) > 50 then
121 | Font.print(ttf,x+2, y, string.sub(text,1,50) .. "...", color, screen)
122 | else
123 | Font.print(ttf,x+2, y, text, color, screen)
124 | end
125 | end
126 |
127 | -- * TopCropPrint
128 | -- Used to print long strings on TOP_SCREEN, it automatically crop too long strings
129 | function TopCropPrint(x, y, text, color, screen)
130 | if string.len(text) > 100 then
131 | Font.print(ttf,x+2, y, string.sub(text,1,100) .. "...", color, screen)
132 | else
133 | Font.print(ttf,x+2, y, text, color, screen)
134 | end
135 | end
136 |
137 | -- * DebugCropPrint
138 | -- Used to print long strings on BOTTOM_SCREEN, it automatically crop too long strings
139 | function DebugCropPrint(x, y, text, color, screen)
140 | if string.len(text) > 25 then
141 | Screen.debugPrint(x, y, string.sub(text,1,25) .. "...", color, screen)
142 | else
143 | Screen.debugPrint(x, y, text, color, screen)
144 | end
145 | end
146 |
147 | -- * DebugTopCropPrint
148 | -- Used to print long strings on TOP_SCREEN, it automatically crop too long strings
149 | function DebugTopCropPrint(x, y, text, color, screen)
150 | if string.len(text) > 42 then
151 | Screen.debugPrint(x, y, string.sub(text,1,42) .. "...", color, screen)
152 | else
153 | Screen.debugPrint(x, y, text, color, screen)
154 | end
155 | end
156 |
157 | -- * LastSpace
158 | -- Return index of last space for text argument
159 | function LastSpace(text)
160 | found = false
161 | start = -1
162 | while string.sub(text,start,start) ~= " " do
163 | start = start - 1
164 | end
165 | return start
166 | end
167 |
168 | -- * ErrorGenerator
169 | -- PRIVATE FUNCTION: DO NOT USE
170 | function ErrorGenerator(text)
171 | y = 68
172 | error_lines = {}
173 | while string.len(text) > 50 do
174 | endl = 51 + LastSpace(string.sub(text,1,50))
175 | table.insert(error_lines,{string.sub(text,1,endl), y})
176 | text = string.sub(text,endl+1,-1)
177 | y = y + 15
178 | end
179 | if string.len(text) > 0 then
180 | table.insert(error_lines,{text, y})
181 | end
182 | end
183 |
184 | -- * ShowError
185 | -- Shows a SunShell error with a customizable text
186 | function ShowError(text)
187 | confirm = false
188 | ErrorGenerator(text)
189 | max_y = error_lines[#error_lines][2] + 40
190 | Screen.fillEmptyRect(5,315,50,max_y,black,BOTTOM_SCREEN)
191 | Screen.fillRect(6,314,51,max_y-1,white,BOTTOM_SCREEN)
192 | Font.print(ttf,8,53,"Error",selected,BOTTOM_SCREEN)
193 | for i,line in pairs(error_lines) do
194 | Font.print(ttf,8,line[2],line[1],black,BOTTOM_SCREEN)
195 | end
196 | Screen.fillEmptyRect(147,176,max_y - 23, max_y - 8,black,BOTTOM_SCREEN)
197 | Font.print(ttf,155,max_y - 23,"OK",black,BOTTOM_SCREEN)
198 | Screen.flip()
199 | Screen.refresh()
200 | Screen.fillEmptyRect(5,315,50,max_y,black,BOTTOM_SCREEN)
201 | Screen.fillRect(6,314,51,max_y-1,white,BOTTOM_SCREEN)
202 | Font.print(ttf,8,53,"Error",selected,BOTTOM_SCREEN)
203 | for i,line in pairs(error_lines) do
204 | Font.print(ttf,8,line[2],line[1],black,BOTTOM_SCREEN)
205 | end
206 | Screen.fillEmptyRect(147,176,max_y - 23, max_y - 8,black,BOTTOM_SCREEN)
207 | Font.print(ttf,155,max_y - 23,"OK",black,BOTTOM_SCREEN)
208 | while not confirm do
209 | if (Controls.check(Controls.read(),KEY_TOUCH)) then
210 | x,y = Controls.readTouch()
211 | if x >= 147 and x <= 176 and y >= max_y - 23 and y <= max_y - 8 then
212 | confirm = true
213 | end
214 | end
215 | end
216 | end
217 |
218 | -- * ShowWarning
219 | -- Shows a SunShell warning with a customizable text
220 | function ShowWarning(text)
221 | confirm = false
222 | ErrorGenerator(text)
223 | max_y = error_lines[#error_lines][2] + 40
224 | Screen.fillEmptyRect(5,315,50,max_y,black,BOTTOM_SCREEN)
225 | Screen.fillRect(6,314,51,max_y-1,white,BOTTOM_SCREEN)
226 | Font.print(ttf,8,53,"Warning",selected,BOTTOM_SCREEN)
227 | for i,line in pairs(error_lines) do
228 | Font.print(ttf,8,line[2],line[1],black,BOTTOM_SCREEN)
229 | end
230 | Screen.fillEmptyRect(147,176,max_y - 23, max_y - 8,black,BOTTOM_SCREEN)
231 | Font.print(ttf,155,max_y - 23,"OK",black,BOTTOM_SCREEN)
232 | Screen.flip()
233 | Screen.refresh()
234 | Screen.fillEmptyRect(5,315,50,max_y,black,BOTTOM_SCREEN)
235 | Screen.fillRect(6,314,51,max_y-1,white,BOTTOM_SCREEN)
236 | Font.print(ttf,8,53,"Warning",selected,BOTTOM_SCREEN)
237 | for i,line in pairs(error_lines) do
238 | Font.print(ttf,8,line[2],line[1],black,BOTTOM_SCREEN)
239 | end
240 | Screen.fillEmptyRect(147,176,max_y - 23, max_y - 8,black,BOTTOM_SCREEN)
241 | Font.print(ttf,155,max_y - 23,"OK",black,BOTTOM_SCREEN)
242 | while not confirm do
243 | if (Controls.check(Controls.read(),KEY_TOUCH)) then
244 | x,y = Controls.readTouch()
245 | if x >= 147 and x <= 176 and y >= max_y - 23 and y <= max_y - 8 then
246 | confirm = true
247 | end
248 | end
249 | end
250 | end
251 |
252 | -- * LinesGenerator
253 | -- Similar to CropPrint but for TOP_SCREEN, you can see Applications src to know how to use it
254 | function LinesGenerator(text,y)
255 | error_lines = {}
256 | while string.len(text) > 60 do
257 | endl = 61 + LastSpace(string.sub(text,1,60))
258 | table.insert(error_lines,{string.sub(text,1,endl), y})
259 | text = string.sub(text,endl+1,-1)
260 | y = y + 15
261 | end
262 | if string.len(text) > 0 then
263 | table.insert(error_lines,{text, y})
264 | end
265 | return error_lines
266 | end
267 |
268 | -- * DebugLinesGenerator
269 | -- Similar to CropPrint but for TOP_SCREEN, you can see Mail src to know how to use it
270 | function DebugLinesGenerator(text,y)
271 | error_lines = {}
272 | while string.len(text) > 40 do
273 | endl = 41 + LastSpace(string.sub(text,1,40))
274 | table.insert(error_lines,{string.sub(text,1,endl), y})
275 | text = string.sub(text,endl+1,-1)
276 | y = y + 15
277 | end
278 | if string.len(text) > 0 then
279 | table.insert(error_lines,{text, y})
280 | end
281 | return error_lines
282 | end
283 |
284 | -- * AddIconTopbar
285 | -- Add an Icon to Topbar
286 | function AddIconTopbar(filename,id)
287 | table.insert(topbar_icons, {Graphics.loadImage(filename),id})
288 | end
289 |
290 | -- * FreeIconTopbar
291 | -- Delete an Icon from Topbar
292 | function FreeIconTopbar(my_app)
293 | for i, icon in pairs(topbar_icons) do
294 | if icon[2] == my_app then
295 | Graphics.freeImage(icon[1])
296 | table.remove(topbar_icons,i)
297 | break
298 | end
299 | end
300 | end
301 |
302 | -- * OneshotPrint
303 | -- Optimized generic print function for code which needs to be executed only one time
304 | function OneshotPrint(my_func)
305 | my_func()
306 | Screen.flip()
307 | Screen.refresh()
308 | my_func()
309 | end
310 |
311 | -- * CallKeyboard
312 | -- Calls a new instance for a Danzeff Keyboard
313 | function CallKeyboard(x, y)
314 | danzeff_mode = 1
315 | blockx = 2
316 | blocky = 2
317 | danzeff_x = x
318 | danzeff_y = y
319 | danzeff_map = {
320 | ",abc.def!ghi-jkl\009m n?opq(rst:uvw)xyz",
321 | "\0\0\0001\0\0\0002\0\0\0003\0\0\0004\009\0 5\0\0\0006\0\0\0007\0\0\0008\0\00009",
322 | "^ABC@DEF*GHI_JKL\009M N\"OPQ=RST;UVW/XYZ",
323 | "'(.)\"<'>-[_]!{?}\009\0 \0+\\=/:@;#~$`%*^|&"
324 | }
325 | pic1 = Graphics.loadImage(theme_dir.."/images/keys.png")
326 | pic2 = Graphics.loadImage(theme_dir.."/images/nums.png")
327 | pic3 = Graphics.loadImage(theme_dir.."/images/keys_c.png")
328 | pic4 = Graphics.loadImage(theme_dir.."/images/nums_c.png")
329 | pic1m = Graphics.loadImage(theme_dir.."/images/keys_t.png")
330 | pic2m = Graphics.loadImage(theme_dir.."/images/nums_t.png")
331 | pic3m = Graphics.loadImage(theme_dir.."/images/keys_c_t.png")
332 | pic4m = Graphics.loadImage(theme_dir.."/images/nums_c_t.png")
333 | olddanzpad = KEY_A
334 | end
335 |
336 | -- * ShowKeyboard
337 | -- Shows current keyboard instance
338 | function ShowKeyboard()
339 | if danzeff_mode == 1 then
340 | h = pic1
341 | Graphics.drawImage(danzeff_x, danzeff_y, pic1m)
342 | elseif danzeff_mode == 2 then
343 | h = pic2
344 | Graphics.drawImage(danzeff_x, danzeff_y, pic2m)
345 | elseif danzeff_mode == 3 then
346 | h = pic3
347 | Graphics.drawImage(danzeff_x, danzeff_y, pic3m)
348 | else
349 | h = pic4
350 | Graphics.drawImage(danzeff_x, danzeff_y, pic4m)
351 | end
352 | danx = (blockx - 1) * 50
353 | dany = (blocky - 1) * 50
354 | Graphics.drawPartialImage(danzeff_x + danx, danzeff_y + dany, danx, dany, 50, 50, h)
355 | end
356 |
357 | -- * KeyboardInput()
358 | -- Gets current keyboard input
359 | function KeyboardInput()
360 | cx, cy = Controls.readCirclePad()
361 | danzpad = Controls.read()
362 | posx = 2
363 | posy = 2
364 | if cx < -50 then
365 | posx = 1
366 | end
367 | if cx > 50 then
368 | posx = 3
369 | end
370 | if cy > 50 then
371 | posy = 1
372 | end
373 | if cy < -50 then
374 | posy = 3
375 | end
376 | if blocky ~= posy or blockx ~= posx then
377 | blocky = posy
378 | blockx = posx
379 | end
380 | if danzeff_mode > 2 then
381 | danzeff_mode = danzeff_mode - 2
382 | end
383 | if Controls.check(danzpad, KEY_R) and not Controls.check(olddanzpad, KEY_R) then
384 | if danzeff_mode == 1 then
385 | danzeff_mode = 2
386 | else
387 | danzeff_mode = 1
388 | end
389 | end
390 | if Controls.check(danzpad, KEY_R) then
391 | danzeff_mode = danzeff_mode + 2
392 | end
393 | charpos = (blocky - 1) * 12 + (blockx - 1) * 4
394 | if Controls.check(danzpad, KEY_Y) and not Controls.check(olddanzpad, KEY_Y) then
395 | res = string.byte(danzeff_map[danzeff_mode], charpos + 2)
396 | elseif Controls.check(danzpad, KEY_A) and not Controls.check(olddanzpad, KEY_A) then
397 | res = string.byte(danzeff_map[danzeff_mode], charpos + 4)
398 | elseif Controls.check(danzpad, KEY_B) and not Controls.check(olddanzpad, KEY_B) then
399 | res = string.byte(danzeff_map[danzeff_mode], charpos + 3)
400 | elseif Controls.check(danzpad, KEY_X) and not Controls.check(olddanzpad, KEY_X) then
401 | res = string.byte(danzeff_map[danzeff_mode], charpos + 1)
402 | else
403 | res = 0
404 | end
405 | olddanzpad = danzpad
406 | return res
407 | end
408 |
409 | -- * CloseKeyboard()
410 | -- Close current keyboard instance
411 | function CloseKeyboard()
412 | Graphics.freeImage(pic1)
413 | Graphics.freeImage(pic2)
414 | Graphics.freeImage(pic3)
415 | Graphics.freeImage(pic4)
416 | Graphics.freeImage(pic1m)
417 | Graphics.freeImage(pic2m)
418 | Graphics.freeImage(pic3m)
419 | Graphics.freeImage(pic4m)
420 | end
--------------------------------------------------------------------------------
/LUA/modules/cia.lua:
--------------------------------------------------------------------------------
1 | -- Set private "Cia" mode
2 | mode = "Cia"
3 |
4 | -- Internal module settings
5 | master_index_cia = 0
6 | p_cia = 1
7 | if build == "Ninjhax 2" then
8 | ShowError("CIA manager unavailable on Ninjhax 2.")
9 | CallMainMenu()
10 | else
11 | cia_table = System.listCIA()
12 | update_list = true
13 |
14 | -- Game titles parsing
15 | dofile(main_dir.."/scripts/title_list.lua")
16 | assigned = 0
17 | for i,title in pairs(title_list) do
18 | if assigned >= (#cia_table - 1) then
19 | break
20 | end
21 | for z,app in pairs(cia_table) do
22 | if app.unique_id == title[3] then
23 | app["title_name"] = title[1]
24 | assigned = assigned + 1
25 | end
26 | end
27 | end
28 | end
29 |
30 | not_extracted = true
31 | function TableConcat(t1,t2)
32 | for i=1,#t2 do
33 | t1[#t1+1] = t2[i]
34 | end
35 | return t1
36 | end
37 | function listDirectory(dir)
38 | dir = System.listDirectory(dir)
39 | folders_table = {}
40 | files_table = {}
41 | for i,file in pairs(dir) do
42 | if file.directory then
43 | table.insert(folders_table,file)
44 | elseif (string.sub(file.name,-4) == ".cia") or (string.sub(file.name,-4) == ".CIA") then
45 | table.insert(files_table,file)
46 | end
47 | end
48 | table.sort(files_table, function (a, b) return (a.name:lower() < b.name:lower() ) end)
49 | table.sort(folders_table, function (a, b) return (a.name:lower() < b.name:lower() ) end)
50 | return_table = TableConcat(folders_table,files_table)
51 | return return_table
52 | end
53 | function UpdateSpace()
54 | free_space = System.getFreeSpace()
55 | f = "Bytes"
56 | f_free_space = free_space
57 | if free_space > 1024 then
58 | f_free_space = free_space / 1024
59 | f = "KBs"
60 | if f_free_space > 1024 then
61 | f_free_space = f_free_space / 1024
62 | f = "MBs"
63 | end
64 | end
65 | end
66 | UpdateSpace()
67 | files_table = listDirectory("/")
68 | int_mode = "CIA"
69 | function GetCategory(c)
70 | if c==0 then
71 | return "Application"
72 | elseif c==1 then
73 | return "System"
74 | elseif c==2 then
75 | return "Demo"
76 | elseif c==3 then
77 | return "Patch"
78 | elseif c==4 then
79 | return "TWL"
80 | end
81 | end
82 | function OpenDirectory(text,archive_id)
83 | i=0
84 | if text == ".." then
85 | j=-2
86 | while string.sub(System.currentDirectory(),j,j) ~= "/" do
87 | j=j-1
88 | end
89 | System.currentDirectory(string.sub(System.currentDirectory(),1,j))
90 | else
91 | System.currentDirectory(System.currentDirectory()..text.."/")
92 | end
93 | files_table = listDirectory(System.currentDirectory())
94 | if System.currentDirectory() ~= "/" then
95 | local extra = {}
96 | extra.name = ".."
97 | extra.size = 0
98 | extra.directory = true
99 | table.insert(files_table,extra)
100 | end
101 | end
102 |
103 | function ShowSDMCList()
104 | base_y = 0
105 | for l, file in pairs(files_table) do
106 | if (base_y > 226) then
107 | break
108 | end
109 | if (l >= master_index_cia) then
110 | if (l==p_cia) then
111 | Screen.fillRect(0,319,base_y,base_y+15,selected_item,BOTTOM_SCREEN)
112 | color = selected
113 | else
114 | color = black
115 | end
116 | CropPrint(0,base_y,file.name,color,BOTTOM_SCREEN)
117 | base_y = base_y + 15
118 | end
119 | end
120 | end
121 |
122 | function ShowCIAList()
123 | base_y = 0
124 | for l, file in pairs(cia_table) do
125 | if (base_y > 226) then
126 | break
127 | end
128 | if (l >= master_index_cia) then
129 | if (l==p_cia) then
130 | base_y2 = base_y
131 | Screen.fillRect(0,319,base_y,base_y+15,selected_item,BOTTOM_SCREEN)
132 | color = selected
133 | else
134 | color = black
135 | end
136 | if file.title_name == nil then
137 | CropPrint(0,base_y,file.product_id,color,BOTTOM_SCREEN)
138 | else
139 | CropPrint(0,base_y,file.title_name,color,BOTTOM_SCREEN)
140 | end
141 | base_y = base_y + 15
142 | end
143 | end
144 | end
145 |
146 | -- Rendering functions
147 | function AppTopScreenRender()
148 | Graphics.fillRect(5,395,40,220,black)
149 | Graphics.fillRect(6,394,41,219,white)
150 | end
151 |
152 | function AppBottomScreenRender()
153 | end
154 |
155 | -- Module main cycle
156 | function AppMainCycle()
157 |
158 | -- Reset internal variables
159 | sm_index = 1
160 | base_y = 0
161 |
162 | -- Draw bottom screen listmenu and top screen info
163 | Screen.debugPrint(9,175,"Free Space: "..f_free_space.." "..f,black,TOP_SCREEN)
164 | if int_mode == "SDMC" then
165 |
166 | -- Showing files list
167 | --if update_list then
168 | --BottomBGRefresh()
169 | ShowSDMCList()
170 | --update_list = false
171 | --end
172 |
173 | Screen.debugPrint(9,190,"SDMC listing",black,TOP_SCREEN)
174 | if not files_table[p_cia].directory then
175 | if not_extracted == true then
176 | cia_data = System.extractCIA(System.currentDirectory()..files_table[p_cia].name)
177 | end
178 | Screen.debugPrint(9,45,"Title: "..cia_data.title,black,TOP_SCREEN)
179 | Screen.debugPrint(9,60,"Unique ID: 0x"..string.sub(string.format('%02X',cia_data.unique_id),1,-3),black,TOP_SCREEN)
180 | tmp = io.open(System.currentDirectory()..files_table[p_cia].name,FREAD)
181 | size = io.size(tmp)
182 | f_size = size
183 | f2 = "Bytes"
184 | if size > 1024 then
185 | f_size = size / 1024
186 | f2 = "KBs"
187 | if f_size > 1024 then
188 | f_size = f_size / 1024
189 | f2 = "MBs"
190 | end
191 | end
192 | Screen.debugPrint(9,75,"Filesize: "..f_size.." "..f2,black,TOP_SCREEN)
193 | io.close(tmp)
194 | end
195 | else
196 | --if update_list then
197 | --BottomBGRefresh()
198 | ShowCIAList()
199 | --update_list = false
200 | --end
201 | Screen.debugPrint(9,190,"Imported CIA listing",black,TOP_SCREEN)
202 | Screen.debugPrint(9,45,"Unique ID: 0x"..string.sub(string.format('%02X',cia_table[p_cia].unique_id),1,-3),black,TOP_SCREEN)
203 | Screen.debugPrint(9,60,"Product ID: "..cia_table[p_cia].product_id,black,TOP_SCREEN)
204 | Screen.debugPrint(9,75,"Category: "..GetCategory(cia_table[p_cia].category),black,TOP_SCREEN)
205 | if (cia_table[p_cia].platform == 3) then
206 | Screen.debugPrint(9,90,"Platform: DSi",black,TOP_SCREEN)
207 | else
208 | Screen.debugPrint(9,90,"Platform: 3DS",black,TOP_SCREEN)
209 | end
210 | if cia_table[p_cia].mediatype == 1 then
211 | Screen.debugPrint(9,105,"Location: SDMC",Color.new(0,0,255),TOP_SCREEN)
212 | else
213 | Screen.debugPrint(9,105,"Location: NAND",Color.new(255,0,0),TOP_SCREEN)
214 | end
215 | end
216 |
217 | -- Sets controls triggering
218 | if Controls.check(pad,KEY_B) or Controls.check(pad,KEY_START) then
219 | CallMainMenu()
220 | elseif (Controls.check(pad,KEY_A) and not Controls.check(oldpad,KEY_A)) then
221 | oldpad = KEY_A
222 | if (int_mode == "SDMC") then
223 | sm_index = 1
224 | if (files_table[p_cia].directory) then
225 | OpenDirectory(files_table[p_cia].name)
226 | p_cia = 1
227 | master_index_cia = 0
228 | else
229 | if free_space - size > 0 then
230 | while true do
231 | Screen.waitVblankStart()
232 | Screen.refresh()
233 | pad = Controls.read()
234 | Screen.fillEmptyRect(60,260,50,82,black,BOTTOM_SCREEN)
235 | Screen.fillRect(61,259,51,81,white,BOTTOM_SCREEN)
236 | if (sm_index == 1) then
237 | Screen.fillRect(61,259,51,66,selected_item,BOTTOM_SCREEN)
238 | Screen.debugPrint(63,53,"Confirm",selected,BOTTOM_SCREEN)
239 | Screen.debugPrint(63,68,"Cancel",black,BOTTOM_SCREEN)
240 | if (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
241 | sm_index = 2
242 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
243 | TopCropPrint(9,100,"Importing " .. files_table[p_cia].name.."...",black,TOP_SCREEN)
244 | Screen.debugPrint(9,115,"Please wait...",black,TOP_SCREEN)
245 | Screen.flip()
246 | Screen.waitVblankStart()
247 | System.installCIA(System.currentDirectory()..files_table[p_cia].name)
248 | UpdateSpace()
249 | break
250 | end
251 | else
252 | Screen.fillRect(61,259,66,81,selected_item,BOTTOM_SCREEN)
253 | Screen.debugPrint(63,53,"Confirm",black,BOTTOM_SCREEN)
254 | Screen.debugPrint(63,68,"Cancel",selected,BOTTOM_SCREEN)
255 | if (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
256 | sm_index = 1
257 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
258 | break
259 | end
260 | end
261 | oldpad = pad
262 | Screen.flip()
263 | end
264 | update_list = true
265 | end
266 | end
267 | else
268 | while true do
269 | Screen.waitVblankStart()
270 | Screen.refresh()
271 | pad = Controls.read()
272 | Screen.fillEmptyRect(60,260,50,82,black,BOTTOM_SCREEN)
273 | Screen.fillRect(61,259,51,81,white,BOTTOM_SCREEN)
274 | if (sm_index == 1) then
275 | Screen.fillRect(61,259,51,66,selected_item,BOTTOM_SCREEN)
276 | Screen.debugPrint(63,53,"Confirm",selected,BOTTOM_SCREEN)
277 | Screen.debugPrint(63,68,"Cancel",black,BOTTOM_SCREEN)
278 | if (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
279 | sm_index = 2
280 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
281 | System.uninstallCIA(cia_table[p_cia].access_id,cia_table[p_cia].mediatype)
282 | UpdateSpace()
283 | break
284 | end
285 | else
286 | Screen.fillRect(61,259,66,81,selected_item,BOTTOM_SCREEN)
287 | Screen.debugPrint(63,53,"Confirm",black,BOTTOM_SCREEN)
288 | Screen.debugPrint(63,68,"Cancel",selected,BOTTOM_SCREEN)
289 | if (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
290 | sm_index = 1
291 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
292 | break
293 | end
294 | end
295 | Screen.flip()
296 | oldpad = pad
297 | end
298 | update_list = true
299 | cia_table = System.listCIA()
300 | if p_cia > #cia_table then
301 | p_cia = p_cia - 1
302 | end
303 |
304 | -- Game titles parsing
305 | assigned = 0
306 | for i,title in pairs(title_list) do
307 | if assigned >= (#cia_table - 1) then
308 | break
309 | end
310 | for z,app in pairs(cia_table) do
311 | if app.unique_id == title[3] then
312 | app["title_name"] = title[1]
313 | assigned = assigned + 1
314 | end
315 | end
316 | end
317 |
318 | end
319 | elseif (Controls.check(pad,KEY_Y) and not Controls.check(oldpad,KEY_Y)) then
320 | oldpad = KEY_A
321 | if (int_mode == "SDMC") then
322 | sm_index = 1
323 | if not (files_table[p_cia].directory) and (free_space - size > 0) then
324 | while true do
325 | Screen.waitVblankStart()
326 | Screen.refresh()
327 | pad = Controls.read()
328 | Screen.fillEmptyRect(60,260,50,82,black,BOTTOM_SCREEN)
329 | Screen.fillRect(61,259,51,81,white,BOTTOM_SCREEN)
330 | if (sm_index == 1) then
331 | Screen.fillRect(61,259,51,66,selected_item,BOTTOM_SCREEN)
332 | Screen.debugPrint(63,53,"Confirm",selected,BOTTOM_SCREEN)
333 | Screen.debugPrint(63,68,"Cancel",black,BOTTOM_SCREEN)
334 | if (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
335 | sm_index = 2
336 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
337 | TopCropPrint(9,100,"Importing " .. files_table[p_cia].name.."...",black,TOP_SCREEN)
338 | Screen.debugPrint(9,115,"Please wait...",black,TOP_SCREEN)
339 | Screen.flip()
340 | Screen.waitVblankStart()
341 | System.installCIA(System.currentDirectory()..files_table[p_cia].name,0)
342 | System.deleteFile(System.currentDirectory()..files_table[p_cia].name)
343 | UpdateSpace()
344 | files_table = listDirectory(System.currentDirectory())
345 | if System.currentDirectory() ~= "/" then
346 | local extra = {}
347 | extra.name = ".."
348 | extra.size = 0
349 | extra.directory = true
350 | table.insert(files_table,extra)
351 | end
352 | if (p_cia > #files_table) then
353 | p_cia = p_cia - 1
354 | end
355 | break
356 | end
357 | else
358 | Screen.fillRect(61,259,66,81,selected_item,BOTTOM_SCREEN)
359 | Screen.debugPrint(63,53,"Confirm",selected,BOTTOM_SCREEN)
360 | Screen.debugPrint(63,68,"Cancel",black,BOTTOM_SCREEN)
361 | if (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
362 | sm_index = 1
363 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
364 | break
365 | end
366 | end
367 | oldpad = pad
368 | Screen.flip()
369 | end
370 | update_list = true
371 | end
372 | else
373 | System.launchCIA(cia_table[p_cia].unique_id,cia_table[p_cia].mediatype)
374 | end
375 | elseif (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
376 | not_extracted = true
377 | p_cia = p_cia - 1
378 | if (p_cia >= 16) then
379 | master_index_cia = p_cia - 15
380 | end
381 | update_list = true
382 | elseif (Controls.check(pad,KEY_DLEFT)) and not (Controls.check(oldpad,KEY_DLEFT)) then
383 | not_extracted = true
384 | p_cia = p_cia - 16
385 | if p_cia < 1 then
386 | p_cia = 1
387 | end
388 | if (p_cia >= 16) then
389 | master_index_cia = p_cia - 15
390 | else
391 | master_index_cia = 0
392 | end
393 | update_list = true
394 | elseif (Controls.check(pad,KEY_DRIGHT)) and not (Controls.check(oldpad,KEY_DRIGHT)) then
395 | not_extracted = true
396 | p_cia = p_cia + 16
397 | if ((p_cia > #files_table) and (int_mode == "SDMC")) then
398 | p_cia = #files_table
399 | elseif ((p_cia > #cia_table) and (int_mode == "CIA")) then
400 | p_cia = #cia_table
401 | end
402 | if (p_cia >= 17) then
403 | master_index_cia = p_cia - 15
404 | end
405 | update_list = true
406 | elseif (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
407 | not_extracted = true
408 | p_cia = p_cia + 1
409 | if (p_cia >= 17) then
410 | master_index_cia = p_cia - 15
411 | end
412 | update_list = true
413 | end
414 | if (p_cia < 1) then
415 | if (int_mode == "SDMC") then
416 | p_cia = #files_table
417 | else
418 | p_cia = #cia_table
419 | end
420 | if (p_cia >= 17) then
421 | master_index_cia = p_cia - 15
422 | end
423 | elseif ((p_cia > #files_table) and (int_mode == "SDMC")) or ((p_cia > #cia_table) and (int_mode == "CIA")) then
424 | master_index_cia = 0
425 | p_cia = 1
426 | end
427 | if (Controls.check(pad,KEY_SELECT)) and not (Controls.check(oldpad,KEY_SELECT)) then
428 | update_list = true
429 | if int_mode=="CIA" then
430 | int_mode = "SDMC"
431 | else
432 | int_mode = "CIA"
433 | cia_table = System.listCIA()
434 |
435 | -- Game titles parsing
436 | assigned = 0
437 | for i,title in pairs(title_list) do
438 | if assigned >= (#cia_table - 1) then
439 | break
440 | end
441 | for z,app in pairs(cia_table) do
442 | if app.unique_id == title[3] then
443 | app["title_name"] = title[1]
444 | assigned = assigned + 1
445 | end
446 | end
447 | end
448 |
449 | end
450 | p_cia = 1
451 | master_index_cia = 0
452 | end
453 | end
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/LUA/modules/fb.lua:
--------------------------------------------------------------------------------
1 | -- Set private "FB" mode
2 | mode = "FB"
3 |
4 | -- Internal module settings
5 | update_bottom_screen = true
6 | update_top_screen = true
7 | DisableRenderer()
8 | ui_enabled = false
9 | red = Color.new(255,0,0)
10 | green = Color.new(0,255,0)
11 | menu_color = Color.new(255,255,255)
12 | selected_color = Color.new(0,255,0)
13 | selected_item = Color.new(0,0,200,50)
14 | copy_or_move = false
15 | function PrintControls()
16 | Screen.clear(TOP_SCREEN)
17 | Font.print(ttf,0,0,"Basic Controls:",white,TOP_SCREEN)
18 | Font.print(ttf,0,15,"A = Open file/folder",white,TOP_SCREEN)
19 | Font.print(ttf,0,30,"SELECT = Open file with...",white,TOP_SCREEN)
20 | Font.print(ttf,0,45,"R = Create new folder",white,TOP_SCREEN)
21 | Font.print(ttf,0,60,"X = File operations",white,TOP_SCREEN)
22 | Font.print(ttf,0,75,"Y = Transfer file between SD cards",white,TOP_SCREEN)
23 | Font.print(ttf,0,90,"B = Return Main Menu",white,TOP_SCREEN)
24 | Font.print(ttf,0,105,"---------------------------------",white,TOP_SCREEN)
25 | Font.print(ttf,0,120,"Opened file Controls:",white,TOP_SCREEN)
26 | Font.print(ttf,0,135,"X = Cancel file copy/move",white,TOP_SCREEN)
27 | Font.print(ttf,0,150,"Y = Confirm file copy/move",white,TOP_SCREEN)
28 | Font.print(ttf,0,165,"Left/Right = Pause/Resume",white,TOP_SCREEN)
29 | Font.print(ttf,0,180,"Left/Right = Extract icon (SMDH)",white,TOP_SCREEN)
30 | Font.print(ttf,0,195,"Left/Right = Scroll file",white,TOP_SCREEN)
31 | end
32 | function TableConcat(t1,t2)
33 | for i=1,#t2 do
34 | t1[#t1+1] = t2[i]
35 | end
36 | return t1
37 | end
38 | function SortDirectory(dir)
39 | folders_table = {}
40 | files_table = {}
41 | for i,file in pairs(dir) do
42 | if file.directory then
43 | table.insert(folders_table,file)
44 | else
45 | table.insert(files_table,file)
46 | end
47 | end
48 | table.sort(files_table, function (a, b) return (a.name:lower() < b.name:lower() ) end)
49 | table.sort(folders_table, function (a, b) return (a.name:lower() < b.name:lower() ) end)
50 | return_table = TableConcat(folders_table,files_table)
51 | return return_table
52 | end
53 | files_table = SortDirectory(System.listDirectory("/"))
54 | System.currentDirectory("/")
55 | p = 1
56 | current_file = nil
57 | current_type = nil
58 | big_image = false
59 | master_index = 0
60 | sm_index = 1
61 | sm_voices = {"Video Player","Music Player","Image Viewer","Text Reader","LUA Interpreter","HEX Viewer","3DSX Launcher","CIA Installer","SMDH Decoder","ZIP Extractor","Info Viewer","TTF Viewer","Cancel"}
62 | hex_values = {}
63 | hex_text = {}
64 | updateTXT = false
65 | select_mode = false
66 | update_main_extdata = true
67 | action_check = false
68 | move_base = nil
69 | copy_base = nil
70 | x_print = 0
71 | y_print = 0
72 | copy_type = 0
73 | move_type = 0
74 | txt_index = 0
75 | txt_words = 0
76 | txt_i = 0
77 | old_indexes = {}
78 | MAX_RAM_ALLOCATION = 10485760
79 | function DumpFile(input,archive)
80 | inp = io.open(extdata_directory..input,FREAD,archive)
81 | if System.doesFileExist("/"..input) then
82 | System.deleteFile("/"..input)
83 | end
84 | out = io.open("/"..string.format('%02X',archive).."_"..input,FCREATE)
85 | size = io.size(inp)
86 | index = 0
87 | while (index+(MAX_RAM_ALLOCATION/2) < size) do
88 | io.write(out,index,io.read(inp,index,MAX_RAM_ALLOCATION/2),(MAX_RAM_ALLOCATION/2))
89 | index = index + (MAX_RAM_ALLOCATION/2)
90 | end
91 | if index < size then
92 | io.write(out,index,io.read(inp,index,size-index),(size-index))
93 | end
94 | io.close(inp)
95 | io.close(out)
96 | end
97 | function CopyFile(input,output)
98 | inp = io.open(input,FREAD)
99 | if System.doesFileExist(output) then
100 | System.deleteFile(output)
101 | end
102 | out = io.open(output,FCREATE)
103 | size = io.size(inp)
104 | index = 0
105 | while (index+(MAX_RAM_ALLOCATION/2) < size) do
106 | io.write(out,index,io.read(inp,index,MAX_RAM_ALLOCATION/2),(MAX_RAM_ALLOCATION/2))
107 | index = index + (MAX_RAM_ALLOCATION/2)
108 | end
109 | if index < size then
110 | io.write(out,index,io.read(inp,index,size-index),(size-index))
111 | end
112 | io.close(inp)
113 | io.close(out)
114 | end
115 | function CopyDir(input,output)
116 | files = System.listDirectory(input)
117 | System.createDirectory(output)
118 | for z, file in pairs(files) do
119 | if (file.directory) then
120 | CopyDir(input.."/"..file.name,output.."/"..file.name)
121 | else
122 | CopyFile(input.."/"..file.name,output.."/"..file.name)
123 | end
124 | end
125 | end
126 | function ForceOpenFile(text, size, mode)
127 | update_top_screen = true
128 | if mode == "SMDH" then
129 | FBGC()
130 | current_type = "SMDH"
131 | current_file = System.extractSMDH(System.currentDirectory()..text)
132 | smdh_show = Console.new(TOP_SCREEN)
133 | Console.append(smdh_show,"Title: "..current_file.title.."\n\n")
134 | Console.append(smdh_show,"Description: "..current_file.desc.."\n\n")
135 | Console.append(smdh_show,"Author: "..current_file.author)
136 | elseif mode == "3DSX" and build == "3DSX" then
137 | FBGC()
138 | Screen.freeImage(bg)
139 | Sound.term()
140 | System.launch3DSX(System.currentDirectory()..text)
141 | elseif mode == "BMPV" then
142 | FBGC()
143 | current_file = io.open(System.currentDirectory()..text,FREAD)
144 | magic = io.read(current_file,0,4)
145 | io.close(current_file)
146 | if magic == "BMPV" then
147 | current_file = BMPV.load(System.currentDirectory()..text)
148 | current_type = "BMPV"
149 | BMPV.start(current_file,NO_LOOP,0x08,0x09)
150 | else
151 | current_file = JPGV.load(System.currentDirectory()..text)
152 | current_type = "JPGV"
153 | JPGV.start(current_file,NO_LOOP,0x08,0x09)
154 | end
155 | elseif mode == "WAV" then
156 | FBGC()
157 | current_file = io.open(System.currentDirectory()..text,FREAD)
158 | magic = io.read(current_file,8,4)
159 | io.close(current_file)
160 | if magic == "AIFF" then
161 | current_file = Sound.openAiff(System.currentDirectory()..text,true)
162 | current_type = "WAV"
163 | Sound.play(current_file,NO_LOOP,0x08,0x09)
164 | elseif magic == "OggS" then
165 | current_type = ""
166 | current_file = Sound.openOgg(System.currentDirectory()..text,true)
167 | Sound.play(current_file,NO_LOOP,0x08,0x09)
168 | else
169 | current_file = Sound.openWav(System.currentDirectory()..text,true)
170 | current_type = "WAV"
171 | Sound.play(current_file,NO_LOOP,0x08,0x09)
172 | end
173 | elseif mode == "IMG" then
174 | FBGC()
175 | current_type = "IMG"
176 | current_file = Graphics.loadImage(System.currentDirectory()..text)
177 | if Graphics.getImageWidth(current_file) > 400 then
178 | width = 400
179 | big_image = true
180 | end
181 | if Graphics.getImageHeight(current_file) > 240 then
182 | height = 240
183 | big_image = true
184 | end
185 | elseif mode == "LUA" then
186 | FBGC()
187 | GarbageCollection()
188 | for i,bg_apps_code in pairs(bg_apps) do
189 | bg_apps_code[2]()
190 | end
191 | for i,icon in pairs(topbar_icons) do
192 | Graphics.freeImage(icon[1])
193 | end
194 | Sound.term()
195 | Font.unload(ttf)
196 | Graphics.term()
197 | reset_dir = System.currentDirectory()
198 | System.currentDirectory(string.sub(System.currentDirectory(),1,-2))
199 | dofile(System.currentDirectory().."/"..text)
200 | System.currentDirectory(reset_dir)
201 | current_type = "LUA"
202 | Graphics.init()
203 | Sound.init()
204 | ttf = Font.load(theme_dir.."/fonts/main.ttf")
205 | elseif mode == "TXT" then
206 | FBGC()
207 | current_file = io.open(System.currentDirectory()..text,FREAD)
208 | text_console = Console.new(TOP_SCREEN)
209 | current_type = "TXT"
210 | txt_index = 0
211 | txt_words = 0
212 | updateTXT = true
213 | elseif mode == "HEX" then
214 | FBGC()
215 | current_file = io.open(System.currentDirectory()..text,FREAD)
216 | current_type = "HEX"
217 | txt_index = 0
218 | updateTXT = true
219 | elseif mode == "INFO" then
220 | FBGC()
221 | current_file = io.open(System.currentDirectory()..text,FREAD)
222 | current_type = "INFO"
223 | f_size = io.size(current_file)
224 | f = "Bytes"
225 | if (f_size > 1024) then
226 | f_size = f_size / 1024
227 | f = "KBs"
228 | end
229 | if (f_size > 1024) then
230 | f_size = f_size / 1024
231 | f = "MBs"
232 | end
233 | io.close(current_file)
234 | text_console = Console.new(TOP_SCREEN)
235 | Console.append(text_console,"Filename: "..text.."\n")
236 | i = -1
237 | while string.sub(text,i,i) ~= "." do
238 | i = i - 1
239 | end
240 | i = i + 1
241 | Console.append(text_console,"Format: "..string.upper(string.sub(text,i)).."\n")
242 | Console.append(text_console,"Size: "..f_size.." "..f.."\n")
243 | elseif mode == "ZIP" then
244 | FBGC()
245 | pass = System.startKeyboard("")
246 | System.extractZIP(System.currentDirectory()..text,System.currentDirectory()..string.sub(text,1,-5),pass)
247 | files_table = System.listDirectory(System.currentDirectory())
248 | if System.currentDirectory() ~= "/" then
249 | local extra = {}
250 | extra.name = ".."
251 | extra.size = 0
252 | extra.directory = true
253 | table.insert(files_table,extra)
254 | end
255 | files_table = SortDirectory(files_table)
256 | elseif mode == "TTF" then
257 | FBGC()
258 | current_file = Font.load(System.currentDirectory()..text)
259 | current_type = "TTF"
260 | elseif mode == "CIA" then
261 | FBGC()
262 | sm_index = 1
263 | cia_data = System.extractCIA(System.currentDirectory()..text)
264 | oldpad = KEY_A
265 | while true do
266 | Screen.refresh()
267 | Screen.waitVblankStart()
268 | Screen.clear(TOP_SCREEN)
269 | Font.print(ttf,0,0,"Title: "..cia_data.title,white,TOP_SCREEN)
270 | Font.print(ttf,0,15,"Unique ID: 0x"..string.sub(string.format('%02X',cia_data.unique_id),1,-3),white,TOP_SCREEN)
271 | Controls.init()
272 | pad = Controls.read()
273 | Screen.fillEmptyRect(60,260,50,82,black,BOTTOM_SCREEN)
274 | Screen.fillRect(61,259,51,81,white,BOTTOM_SCREEN)
275 | if (sm_index == 1) then
276 | Screen.fillRect(61,259,51,66,green,BOTTOM_SCREEN)
277 | Font.print(ttf,63,53,"Confirm",red,BOTTOM_SCREEN)
278 | Font.print(ttf,63,68,"Cancel",black,BOTTOM_SCREEN)
279 | if (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
280 | sm_index = 2
281 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
282 | System.installCIA(System.currentDirectory()..text)
283 | break
284 | end
285 | else
286 | Screen.fillRect(61,259,66,81,green,BOTTOM_SCREEN)
287 | Font.print(ttf,63,53,"Confirm",black,BOTTOM_SCREEN)
288 | Font.print(ttf,63,68,"Cancel",red,BOTTOM_SCREEN)
289 | if (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
290 | sm_index = 1
291 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
292 | break
293 | end
294 | end
295 | oldpad = pad
296 | Screen.flip()
297 | end
298 | end
299 | end
300 | function DeleteDir(dir)
301 | files = System.listDirectory(dir)
302 | for z, file in pairs(files) do
303 | if (file.directory) then
304 | DeleteDir(dir.."/"..file.name)
305 | else
306 | System.deleteFile(dir.."/"..file.name)
307 | end
308 | end
309 | System.deleteDirectory(dir)
310 | end
311 | function OpenFile(text, size)
312 | update_top_screen = true
313 | if string.upper(string.sub(text,-5)) == ".SMDH" then
314 | FBGC()
315 | current_type = "SMDH"
316 | current_file = System.extractSMDH(System.currentDirectory()..text)
317 | smdh_show = Console.new(TOP_SCREEN)
318 | Console.append(smdh_show,"Title: "..current_file.title.."\n\n")
319 | Console.append(smdh_show,"Description: "..current_file.desc.."\n\n")
320 | Console.append(smdh_show,"Author: "..current_file.author)
321 | elseif string.upper(string.sub(text,-4)) == ".ZIP" then
322 | FBGC()
323 | pass = System.startKeyboard("")
324 | System.extractZIP(System.currentDirectory()..text,System.currentDirectory()..string.sub(text,1,-5),pass)
325 | files_table = System.listDirectory(System.currentDirectory())
326 | if System.currentDirectory() ~= "/" then
327 | local extra = {}
328 | extra.name = ".."
329 | extra.size = 0
330 | extra.directory = true
331 | table.insert(files_table,extra)
332 | end
333 | files_table = SortDirectory(files_table)
334 | elseif (string.upper(string.sub(text,-5)) == ".3DSX") and build == "Ninjhax 1" then
335 | FBGC()
336 | GarbageCollection()
337 | for i,bg_apps_code in pairs(bg_apps) do
338 | bg_apps_code[2]()
339 | end
340 | for i,icon in pairs(topbar_icons) do
341 | Graphics.freeImage(icon[1])
342 | end
343 | Sound.term()
344 | Font.unload(ttf)
345 | Graphics.term()
346 | System.launch3DSX(System.currentDirectory()..text)
347 | elseif string.upper(string.sub(text,-5)) == ".BMPV" then
348 | FBGC()
349 | current_file = BMPV.load(System.currentDirectory()..text)
350 | current_type = "BMPV"
351 | BMPV.start(current_file,NO_LOOP,0x08,0x09)
352 | elseif string.upper(string.sub(text,-5)) == ".JPGV" then
353 | FBGC()
354 | current_file = JPGV.load(System.currentDirectory()..text)
355 | current_type = "JPGV"
356 | JPGV.start(current_file,NO_LOOP,0x08,0x09)
357 | elseif string.upper(string.sub(text,-4)) == ".TTF" then
358 | FBGC()
359 | current_file = Font.load(System.currentDirectory()..text)
360 | current_type = "TTF"
361 | elseif string.upper(string.sub(text,-4)) == ".WAV" then
362 | FBGC()
363 | current_file = Sound.openWav(System.currentDirectory()..text,true)
364 | current_type = "WAV"
365 | Sound.play(current_file,NO_LOOP,0x08,0x09)
366 | elseif string.upper(string.sub(text,-4)) == ".OGG" then
367 | FBGC()
368 | current_file = Sound.openOgg(System.currentDirectory()..text,true)
369 | current_type = "WAV"
370 | Sound.play(current_file,NO_LOOP,0x08,0x09)
371 | elseif string.upper(string.sub(text,-4)) == ".AIF" or string.upper(string.sub(text,-5)) == ".AIFF" then
372 | FBGC()
373 | current_file = Sound.openAiff(System.currentDirectory()..text,true)
374 | current_type = "WAV"
375 | Sound.play(current_file,NO_LOOP,0x08,0x09)
376 | elseif string.upper(string.sub(text,-4)) == ".PNG" or string.upper(string.sub(text,-4)) == ".BMP" or string.upper(string.sub(text,-4)) == ".JPG" then
377 | FBGC()
378 | current_type = "IMG"
379 | current_file = Graphics.loadImage(System.currentDirectory()..text)
380 | width = Graphics.getImageWidth(current_file)
381 | height = Graphics.getImageHeight(current_file)
382 | if width > 400 then
383 | width = 400
384 | big_image = true
385 | end
386 | if height > 240 then
387 | height = 240
388 | big_image = true
389 | end
390 | elseif string.upper(string.sub(text,-4)) == ".LUA" then
391 | FBGC()
392 | GarbageCollection()
393 | for i,bg_apps_code in pairs(bg_apps) do
394 | bg_apps_code[2]()
395 | end
396 | for i,icon in pairs(topbar_icons) do
397 | Graphics.freeImage(icon[1])
398 | end
399 | Sound.term()
400 | Font.unload(ttf)
401 | Graphics.term()
402 | reset_dir = System.currentDirectory()
403 | System.currentDirectory(string.sub(System.currentDirectory(),1,-2))
404 | dofile(System.currentDirectory().."/"..text)
405 | System.currentDirectory(reset_dir)
406 | current_type = "LUA"
407 | Sound.init()
408 | Graphics.init()
409 | ttf = Font.load(theme_dir.."/fonts/main.ttf")
410 | elseif string.upper(string.sub(text,-4)) == ".TXT" then
411 | FBGC()
412 | current_file = io.open(System.currentDirectory()..text,FREAD)
413 | text_console = Console.new(TOP_SCREEN)
414 | current_type = "TXT"
415 | txt_index = 0
416 | txt_words = 0
417 | updateTXT = true
418 | elseif string.upper(string.sub(text,-4)) == ".CIA" and build ~= "Ninjhax 2" then
419 | FBGC()
420 | sm_index = 1
421 | cia_data = System.extractCIA(System.currentDirectory()..text)
422 | oldpad = KEY_A
423 | while true do
424 | Screen.refresh()
425 | Screen.waitVblankStart()
426 | Screen.clear(TOP_SCREEN)
427 | Font.print(ttf,0,0,"Title: "..cia_data.title,white,TOP_SCREEN)
428 | Font.print(ttf,0,15,"Unique ID: 0x"..string.sub(string.format('%02X',cia_data.unique_id),1,-3),white,TOP_SCREEN)
429 | Controls.init()
430 | pad = Controls.read()
431 | Screen.fillEmptyRect(60,260,50,82,black,BOTTOM_SCREEN)
432 | Screen.fillRect(61,259,51,81,white,BOTTOM_SCREEN)
433 | if (sm_index == 1) then
434 | Screen.fillRect(61,259,51,66,green,BOTTOM_SCREEN)
435 | Font.print(ttf,63,53,"Confirm",red,BOTTOM_SCREEN)
436 | Font.print(ttf,63,68,"Cancel",black,BOTTOM_SCREEN)
437 | if (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
438 | sm_index = 2
439 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
440 | System.installCIA(System.currentDirectory()..text)
441 | break
442 | end
443 | else
444 | Screen.fillRect(61,259,66,81,green,BOTTOM_SCREEN)
445 | Font.print(ttf,63,53,"Confirm",black,BOTTOM_SCREEN)
446 | Font.print(ttf,63,68,"Cancel",red,BOTTOM_SCREEN)
447 | if (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
448 | sm_index = 1
449 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
450 | break
451 | end
452 | end
453 | oldpad = pad
454 | Screen.flip()
455 | end
456 | end
457 | end
458 | function OpenDirectory(text,archive_id)
459 | update_bottom_screen = true
460 | i=0
461 | if text == ".." then
462 | j=-2
463 | while string.sub(System.currentDirectory(),j,j) ~= "/" do
464 | j=j-1
465 | end
466 | System.currentDirectory(string.sub(System.currentDirectory(),1,j))
467 | else
468 | System.currentDirectory(System.currentDirectory()..text.."/")
469 | end
470 | files_table = System.listDirectory(System.currentDirectory())
471 | if System.currentDirectory() ~= "/" then
472 | local extra = {}
473 | extra.name = ".."
474 | extra.size = 0
475 | extra.directory = true
476 | table.insert(files_table,extra)
477 | end
478 | files_table = SortDirectory(files_table)
479 | end
480 | function FBGC()
481 | if current_type == "SMDH" then
482 | Console.destroy(smdh_show)
483 | Screen.freeImage(current_file.icon)
484 | elseif current_type == "TTF" then
485 | Font.unload(current_file)
486 | elseif current_type == "BMPV" then
487 | if BMPV.isPlaying(current_file) then
488 | BMPV.stop(current_file)
489 | end
490 | BMPV.unload(current_file)
491 | elseif current_type == "JPGV" then
492 | if JPGV.isPlaying(current_file) then
493 | JPGV.stop(current_file)
494 | end
495 | JPGV.unload(current_file)
496 | elseif current_type == "WAV" then
497 | if Sound.isPlaying(current_file) then
498 | Sound.pause(current_file)
499 | end
500 | Sound.close(current_file)
501 | elseif current_type == "IMG" then
502 | Graphics.freeImage(current_file)
503 | big_image = false
504 | y_print = 0
505 | x_print = 0
506 | elseif current_type == "TXT" then
507 | io.close(current_file)
508 | Console.destroy(text_console)
509 | old_indexes = {}
510 | txt_i = 0
511 | elseif current_type == "INFO" then
512 | Console.destroy(text_console)
513 | elseif current_type == "HEX" then
514 | io.close(current_file)
515 | old_indexes = {}
516 | txt_i = 0
517 | end
518 | current_type = nil
519 | end
520 | function ThemePrint(x, y, text, color, screen)
521 | if string.len(text) > 40 then
522 | Font.print(ttf,x, y, string.sub(text,1,40) .. "...", color, screen)
523 | else
524 | Font.print(ttf,x, y, text, color, screen)
525 | end
526 | end
527 | function BuildLines(file, index)
528 | MAX_LENGTH = 1200
529 | SIZE = io.size(file)
530 | if ((index + MAX_LENGTH) < SIZE) then
531 | READ_LENGTH = MAX_LENGTH
532 | else
533 | READ_LENGTH = SIZE - index
534 | end
535 | if (index < SIZE) then
536 | Console.clear(text_console)
537 | Console.append(text_console,io.read(file,index,READ_LENGTH))
538 | txt_words = Console.show(text_console)
539 | else
540 | txt_words = 0
541 | end
542 | if (txt_words > 0) then
543 | table.insert(old_indexes, index)
544 | index = index + txt_words
545 | txt_i = txt_i + 1
546 | end
547 | return index
548 | end
549 | function BuildHex(file, index)
550 | MAX_LENGTH = 120
551 | SIZE = io.size(file)
552 | if ((index + MAX_LENGTH) < SIZE) then
553 | READ_LENGTH = MAX_LENGTH
554 | else
555 | READ_LENGTH = SIZE - index
556 | end
557 | if (index < SIZE) then
558 | hex_text = {}
559 | hex_values = {}
560 | text = io.read(file,index,READ_LENGTH)
561 | t = 1
562 | while (t <= 15) do
563 | if ((t*8) > string.len(text)) then
564 | temp = string.sub(text,1+(t-1)*8,-1)
565 | else
566 | temp = string.sub(text,1+(t-1)*8,t*8)
567 | end
568 | t2 = 1
569 | while t2 <= string.len(temp) do
570 | table.insert(hex_values,string.byte(temp,t2))
571 | t2 = t2 + 1
572 | end
573 | table.insert(hex_text,temp)
574 | t = t + 1
575 | end
576 | table.insert(old_indexes, index)
577 | index = index + READ_LENGTH
578 | txt_i = txt_i + 1
579 | end
580 | return index
581 | end
582 | function PrintSMDHInfo()
583 | Screen.clear(TOP_SCREEN)
584 | Console.show(smdh_show)
585 | Font.print(ttf,0,170,"Icon:",white,TOP_SCREEN)
586 | Screen.drawImage(0,185,current_file.icon,TOP_SCREEN)
587 | end
588 | function PrintText()
589 | Screen.clear(TOP_SCREEN)
590 | Console.show(text_console)
591 | end
592 | function PrintHex()
593 | Screen.clear(TOP_SCREEN)
594 | for l, line in pairs(hex_text) do
595 | Font.print(ttf,280,(l-1)*15,string.gsub(line,"\0"," "),white,TOP_SCREEN)
596 | temp = 1
597 | while (temp <= string.len(line)) do
598 | if (temp % 2 == 0) then
599 | Font.print(ttf,0+(temp-1)*30,(l-1)*15,string.format('%02X', hex_values[(l-1)*8+temp]),white,TOP_SCREEN)
600 | else
601 | Font.print(ttf,0+(temp-1)*30,(l-1)*15,string.format('%02X', hex_values[(l-1)*8+temp]),red,TOP_SCREEN)
602 | end
603 | temp = temp + 1
604 | end
605 | end
606 | Font.print(ttf,0,225,"Offset: 0x" .. string.format('%X', old_indexes[#old_indexes]) .. " (" .. (old_indexes[#old_indexes]) .. ")",white,TOP_SCREEN)
607 | end
608 | function TestFont()
609 | Screen.clear(TOP_SCREEN)
610 | Font.setPixelSizes(current_file,8)
611 | Font.print(current_file,0,5,"8: The quick brown fox",white,TOP_SCREEN)
612 | Font.print(current_file,10,13,"jumps over the lazy dog",white,TOP_SCREEN)
613 | Font.setPixelSizes(current_file,12)
614 | Font.print(current_file,0,25,"12: The quick brown fox",white,TOP_SCREEN)
615 | Font.print(current_file,10,37,"jumps over the lazy dog",white,TOP_SCREEN)
616 | Font.setPixelSizes(current_file,18)
617 | Font.print(current_file,0,54,"18: The quick brown fox",white,TOP_SCREEN)
618 | Font.print(current_file,10,72,"jumps over the lazy dog",white,TOP_SCREEN)
619 | Font.setPixelSizes(current_file,24)
620 | Font.print(current_file,0,95,"24: The quick brown fox",white,TOP_SCREEN)
621 | Font.print(current_file,10,119,"jumps over the lazy dog",white,TOP_SCREEN)
622 | Font.setPixelSizes(current_file,30)
623 | Font.print(current_file,0,149,"30: The quick brown fox",white,TOP_SCREEN)
624 | Font.print(current_file,10,179,"jumps over the lazy dog",white,TOP_SCREEN)
625 | end
626 | function ListMenu()
627 | base_y = 0
628 | Screen.clear(BOTTOM_SCREEN)
629 | for l, file in pairs(files_table) do
630 | if (base_y > 226) then
631 | break
632 | end
633 | if (l >= master_index) then
634 | if (l==p) then
635 | Screen.fillRect(0,319,base_y,base_y+15,selected_item,BOTTOM_SCREEN)
636 | color = selected_color
637 | else
638 | color = menu_color
639 | end
640 | CropPrint(0,base_y,file.name,color,BOTTOM_SCREEN)
641 | base_y = base_y + 15
642 | end
643 | end
644 | if move_base ~= nil then
645 | Font.print(ttf,300,0,"M",selected_color,BOTTOM_SCREEN)
646 | elseif copy_base ~= nil then
647 | Font.print(ttf,300,0,"C",selected_color,BOTTOM_SCREEN)
648 | end
649 | end
650 |
651 | -- Rendering functions
652 | function AppTopScreenRender()
653 | end
654 |
655 | function AppBottomScreenRender()
656 | end
657 |
658 | function BlitImage()
659 | if big_image then
660 | Graphics.drawPartialImage(0,0,x_print,y_print,width,height,current_file)
661 | x,y = Controls.readCirclePad()
662 | if (x < - 100) and (x_print > 0) then
663 | x_print = x_print - 5
664 | if x_print < 0 then
665 | x_print = 0
666 | end
667 | end
668 | if (y > 100) and (y_print > 0) then
669 | y_print = y_print - 5
670 | if y_print < 0 then
671 | y_print = 0
672 | end
673 | end
674 | if (x > 100) and (x_print + width < Graphics.getImageWidth(current_file)) then
675 | x_print = x_print + 5
676 | end
677 | if (y < - 100) and (y_print + height < Graphics.getImageHeight(current_file)) then
678 | y_print = y_print + 5
679 | end
680 | if x_print + width > Graphics.getImageWidth(current_file) then
681 | x_print = Graphics.getImageWidth(current_file) - width
682 | end
683 | if y_print + height > Graphics.getImageHeight(current_file) then
684 | y_print = Graphics.getImageHeight(current_file) - height
685 | end
686 | else
687 | Graphics.drawImage(0,0,current_file)
688 | end
689 | end
690 |
691 | -- Module main cycle
692 | function AppMainCycle()
693 | i = 1
694 | if update_top_screen then
695 | if (current_type == "SMDH") then
696 | OneshotPrint(PrintSMDHInfo)
697 | update_top_screen = false
698 | elseif (current_type == "TTF") then
699 | OneshotPrint(TestFont)
700 | update_top_screen = false
701 | elseif (current_type == "BMPV") then
702 | BMPV.draw(0,0,current_file,TOP_SCREEN)
703 | elseif (current_type == "JPGV") then
704 | JPGV.draw(0,0,current_file,TOP_SCREEN)
705 | elseif (current_type == "WAV") then
706 | Screen.clear(TOP_SCREEN)
707 | Sound.updateStream()
708 | Font.print(ttf,0,0,"Title: ",white,TOP_SCREEN)
709 | ThemePrint(0,15,Sound.getTitle(current_file),white,TOP_SCREEN)
710 | Font.print(ttf,0,40,"Author: ",white,TOP_SCREEN)
711 | ThemePrint(0,55,Sound.getAuthor(current_file),white,TOP_SCREEN)
712 | Font.print(ttf,0,80,"Time: "..FormatTime(Sound.getTime(current_file)).." / "..FormatTime(Sound.getTotalTime(current_file)),white,TOP_SCREEN)
713 | Font.print(ttf,0,95,"Samplerate: "..Sound.getSrate(current_file),white,TOP_SCREEN)
714 | if Sound.getType(current_file) == 1 then
715 | stype = "Mono"
716 | else
717 | stype = "Stereo"
718 | end
719 | Font.print(ttf,0,110,"Audiotype: "..stype,white,TOP_SCREEN)
720 | elseif (current_type == "INFO") then
721 | OneshotPrint(PrintText)
722 | update_top_screen = false
723 | elseif (current_type == "TXT") then
724 | if (updateTXT) then
725 | txt_index = BuildLines(current_file,txt_index)
726 | updateTXT = false
727 | end
728 | OneshotPrint(PrintText)
729 | update_top_screen = false
730 | elseif (current_type == "HEX") then
731 | if (updateTXT) then
732 | txt_index = BuildHex(current_file,txt_index)
733 | updateTXT = false
734 | end
735 | OneshotPrint(PrintHex)
736 | update_top_screen = false
737 | else
738 | OneshotPrint(PrintControls)
739 | update_top_screen = false
740 | end
741 | end
742 | if current_type == "IMG" then
743 | CustomRenderTop(BlitImage)
744 | end
745 | if update_bottom_screen then
746 | OneshotPrint(ListMenu)
747 | update_bottom_screen = false
748 | end
749 |
750 | -- Select Mode Controls Functions
751 | if (select_mode) then
752 | Screen.fillEmptyRect(60,260,35,232,black,BOTTOM_SCREEN)
753 | Screen.fillRect(61,259,36,231,white,BOTTOM_SCREEN)
754 | for l, voice in pairs(sm_voices) do
755 | if (l == sm_index) then
756 | Screen.fillRect(61,259,36+(l-1)*15,36+l*15,green,BOTTOM_SCREEN)
757 | Font.print(ttf,63,36+(l-1)*15,voice,red,BOTTOM_SCREEN)
758 | else
759 | Font.print(ttf,63,36+(l-1)*15,voice,black,BOTTOM_SCREEN)
760 | end
761 | end
762 | if (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
763 | sm_index = sm_index - 1
764 | elseif (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
765 | sm_index = sm_index + 1
766 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
767 | if (sm_index == 1) then
768 | ForceOpenFile(files_table[p].name,files_table[p].size,"BMPV")
769 | elseif (sm_index == 2) then
770 | ForceOpenFile(files_table[p].name,files_table[p].size,"WAV")
771 | elseif (sm_index == 3) then
772 | ForceOpenFile(files_table[p].name,files_table[p].size,"IMG")
773 | elseif (sm_index == 4) then
774 | ForceOpenFile(files_table[p].name,files_table[p].size,"TXT")
775 | elseif (sm_index == 5) then
776 | ForceOpenFile(files_table[p].name,files_table[p].size,"LUA")
777 | elseif (sm_index == 6) then
778 | ForceOpenFile(files_table[p].name,files_table[p].size,"HEX")
779 | elseif (sm_index == 7) then
780 | if build == "Custom Firmware" or build == "Ninjhax 2" then
781 | ShowError("3DSX Launcher not available on Custom Firmwares and Ninjhax 2.")
782 | else
783 | ForceOpenFile(files_table[p].name,files_table[p].size,"3DSX")
784 | end
785 | elseif (sm_index == 8) then
786 | if build == "Ninjhax 2" then
787 | ShowError("CIA Installer not available on Ninjhax 2.")
788 | else
789 | ForceOpenFile(files_table[p].name,files_table[p].size,"CIA")
790 | end
791 | elseif (sm_index == 9) then
792 | ForceOpenFile(files_table[p].name,files_table[p].size,"SMDH")
793 | elseif (sm_index == 10) then
794 | ForceOpenFile(files_table[p].name,files_table[p].size,"ZIP")
795 | elseif (sm_index == 11) then
796 | ForceOpenFile(files_table[p].name,files_table[p].size,"INFO")
797 | elseif (sm_index == 12) then
798 | ForceOpenFile(files_table[p].name,files_table[p].size,"TTF")
799 | end
800 | sm_index = 1
801 | select_mode = false
802 | update_bottom_screen = true
803 | elseif (Controls.check(pad,KEY_START)) then
804 | FBGC()
805 | GarbageCollection()
806 | for i,bg_apps_code in pairs(bg_apps) do
807 | bg_apps_code[2]()
808 | end
809 | for i,icon in pairs(topbar_icons) do
810 | Graphics.freeImage(icon[1])
811 | end
812 | Sound.term()
813 | Font.unload(ttf)
814 | Graphics.term()
815 | if start_dir == "/" and build ~= "Custom Firmware" then -- boot.3dsx patch
816 | System.reboot()
817 | else
818 | System.exit()
819 | end
820 | end
821 | if (sm_index < 1) then
822 | sm_index = #sm_voices
823 | elseif (sm_index > #sm_voices) then
824 | sm_index = 1
825 | end
826 | -- Action Check
827 | elseif (action_check) then
828 | Screen.fillEmptyRect(60,260,50,127,black,BOTTOM_SCREEN)
829 | Screen.fillRect(61,259,51,126,white,BOTTOM_SCREEN)
830 | if sm_index > 5 then
831 | sm_index = 1
832 | elseif sm_index < 1 then
833 | sm_index = 5
834 | end
835 | colors = {black,black,black,black,black}
836 | colors[sm_index] = red
837 | Screen.fillRect(61,259,36+sm_index*15,51+sm_index*15,green,BOTTOM_SCREEN)
838 | Font.print(ttf,63,51,"Delete",colors[1],BOTTOM_SCREEN)
839 | Font.print(ttf,63,66,"Rename",colors[2],BOTTOM_SCREEN)
840 | Font.print(ttf,63,81,"Copy",colors[3],BOTTOM_SCREEN)
841 | Font.print(ttf,63,96,"Move",colors[4],BOTTOM_SCREEN)
842 | Font.print(ttf,63,111,"Cancel",colors[5],BOTTOM_SCREEN)
843 | if (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
844 | sm_index = sm_index + 1
845 | elseif (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
846 | sm_index = sm_index - 1
847 | elseif (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
848 | if sm_index == 1 then
849 | update_bottom_screen = true
850 | if (files_table[p].directory) then
851 | if (files_table[p].name ~= "..") then
852 | DeleteDir(System.currentDirectory()..files_table[p].name)
853 | end
854 | else
855 | System.deleteFile(System.currentDirectory()..files_table[p].name)
856 | end
857 | while (#files_table > 0) do
858 | table.remove(files_table)
859 | end
860 | files_table = System.listDirectory(System.currentDirectory())
861 | if System.currentDirectory() ~= "/" then
862 | local extra = {}
863 | extra.name = ".."
864 | extra.size = 0
865 | extra.directory = true
866 | table.insert(files_table,extra)
867 | end
868 | files_table = SortDirectory(files_table)
869 | if (p > #files_table) then
870 | p = p - 1
871 | end
872 | action_check = false
873 | elseif sm_index == 2 then
874 | update_bottom_screen = true
875 | if (files_table[p].name ~= "..") then
876 | new_name = System.startKeyboard(files_table[p].name)
877 | pad = KEY_A
878 | oldpad = KEY_A
879 | if (files_table[p].directory) then
880 | System.renameDirectory(System.currentDirectory() .. files_table[p].name,System.currentDirectory() .. new_name)
881 | else
882 | System.renameFile(System.currentDirectory() .. files_table[p].name,System.currentDirectory() .. new_name)
883 | end
884 | files_table = System.listDirectory(System.currentDirectory())
885 | if System.currentDirectory() ~= "/" then
886 | local extra = {}
887 | extra.name = ".."
888 | extra.size = 0
889 | extra.directory = true
890 | table.insert(files_table,extra)
891 | end
892 | files_table = SortDirectory(files_table)
893 | end
894 | action_check = false
895 | sm_index = 1
896 | elseif sm_index == 3 then
897 | if (files_table[p].directory) then
898 | if (files_table[p].name ~= "..") then
899 | copy_name = files_table[p].name
900 | copy_base = System.currentDirectory() .. files_table[p].name
901 | copy_type = 0
902 | end
903 | else
904 | copy_type = 1
905 | copy_name = files_table[p].name
906 | copy_base = System.currentDirectory() .. files_table[p].name
907 | end
908 | action_check = false
909 | sm_index = 1
910 | elseif sm_index == 4 then
911 | if (files_table[p].name ~= "..") then
912 | update_bottom_screen = true
913 | move_base = System.currentDirectory() .. files_table[p].name
914 | move_name = files_table[p].name
915 | if (files_table[p].directory) then
916 | move_type = 0
917 | else
918 | move_type = 1
919 | end
920 | end
921 | action_check = false
922 | sm_index = 1
923 | elseif sm_index == 5 then
924 | update_bottom_screen = true
925 | sm_index = 1
926 | action_check = false
927 | end
928 | end
929 | else
930 | -- Base Controls Functions
931 | if (Controls.check(pad,KEY_DUP)) and not (Controls.check(oldpad,KEY_DUP)) then
932 | update_bottom_screen = true
933 | p = p - 1
934 | if (p >= 16) then
935 | master_index = p - 15
936 | end
937 | elseif (Controls.check(pad,KEY_DDOWN)) and not (Controls.check(oldpad,KEY_DDOWN)) then
938 | update_bottom_screen = true
939 | p = p + 1
940 | if (p >= 17) then
941 | master_index = p - 15
942 | end
943 | end
944 | if (p < 1) then
945 | p = #files_table
946 | if (p >= 17) then
947 | master_index = p - 15
948 | end
949 | elseif (p > #files_table) then
950 | master_index = 0
951 | p = 1
952 | end
953 | if (Controls.check(pad,KEY_A)) and not (Controls.check(oldpad,KEY_A)) then
954 | if (files_table[p].directory) then
955 | OpenDirectory(files_table[p].name,0)
956 | p=1
957 | master_index=0
958 | else
959 | OpenFile(files_table[p].name,files_table[p].size)
960 | end
961 | elseif (Controls.check(pad,KEY_X)) and not (Controls.check(oldpad,KEY_X)) then
962 | if (move_base == nil) and (copy_base == nil) then
963 | action_check = true
964 | else
965 | update_bottom_screen = true
966 | move_base = nil
967 | move_name = nil
968 | copy_base = nil
969 | copy_name = nil
970 | end
971 | elseif (Controls.check(pad,KEY_Y)) and not (Controls.check(oldpad,KEY_Y)) then
972 | if (move_base ~= nil) then
973 | update_bottom_screen = true
974 | if (move_type == 0) then
975 | System.renameDirectory(move_base,System.currentDirectory() .. move_name)
976 | else
977 | System.renameFile(move_base,System.currentDirectory() .. move_name)
978 | end
979 | move_base = nil
980 | files_table = System.listDirectory(System.currentDirectory())
981 | if System.currentDirectory() ~= "/" then
982 | local extra = {}
983 | extra.name = ".."
984 | extra.size = 0
985 | extra.directory = true
986 | table.insert(files_table,extra)
987 | end
988 | files_table = SortDirectory(files_table)
989 | elseif (copy_base ~= nil) then
990 | copy_end = System.currentDirectory() .. copy_name
991 | if copy_end == copy_base then
992 | temp_copy = "Copy_" .. copy_name
993 | copy_end = System.currentDirectory() .. temp_copy
994 | if (copy_type == 1) then
995 | while System.doesFileExist(copy_end) do
996 | temp_copy = "Copy_" .. temp_copy
997 | copy_end = System.currentDirectory() .. temp_copy
998 | end
999 | end
1000 | end
1001 | if (copy_type == 0) then
1002 | CopyDir(copy_base,copy_end)
1003 | else
1004 | CopyFile(copy_base,copy_end)
1005 | end
1006 | files_table = System.listDirectory(System.currentDirectory())
1007 | if System.currentDirectory() ~= "/" then
1008 | local extra = {}
1009 | extra.name = ".."
1010 | extra.size = 0
1011 | extra.directory = true
1012 | table.insert(files_table,extra)
1013 | end
1014 | files_table = SortDirectory(files_table)
1015 | copy_name = nil
1016 | copy_base = nil
1017 | else
1018 | update_top_screen = true
1019 | FBGC()
1020 | if (files_table[p].directory == false) then
1021 | inp = io.open(System.currentDirectory()..files_table[p].name,FREAD)
1022 | filesize = io.size(inp)
1023 | if filesize > MAX_RAM_ALLOCATION * 2 then
1024 | ShowError("This file is too large for this transfer mode.")
1025 | else
1026 | file_ptr = io.read(inp,0,filesize)
1027 | io.close(inp)
1028 | ShowWarning("Insert the SD card where the file needs to be sent and then press OK.")
1029 | --while not System.checkSDMC() do TODO: Add this feature in public lpp-3ds
1030 | -- ShowError("Please insert an SD card in your console to transfer a file.")
1031 | --end
1032 | out = io.open("/"..files_table[p].name,FCREATE)
1033 | io.write(out,0,file_ptr,filesize)
1034 | io.close(out)
1035 | System.currentDirectory("/")
1036 | update_bottom_screen = true
1037 | files_table = System.listDirectory(System.currentDirectory())
1038 | if System.currentDirectory() ~= "/" then
1039 | local extra = {}
1040 | extra.name = ".."
1041 | extra.size = 0
1042 | extra.directory = true
1043 | table.insert(files_table,extra)
1044 | end
1045 | file_ptr = nil
1046 | files_table = SortDirectory(files_table)
1047 | end
1048 | oldpad = KEY_A
1049 | end
1050 | end
1051 | elseif (Controls.check(pad,KEY_B)) and not (Controls.check(oldpad,KEY_B)) then
1052 | FBGC()
1053 | CallMainMenu()
1054 | elseif (Controls.check(pad,KEY_DLEFT)) and not (Controls.check(oldpad,KEY_DLEFT)) then
1055 | if (current_type == "SMDH") then
1056 | update_bottom_screen = true
1057 | name = System.startKeyboard("icon.bmp")
1058 | Screen.saveBitmap(current_file.icon,System.currentDirectory()..name)
1059 | oldpad = KEY_A
1060 | files_table = System.listDirectory(System.currentDirectory())
1061 | if System.currentDirectory() ~= "/" then
1062 | local extra = {}
1063 | extra.name = ".."
1064 | extra.size = 0
1065 | extra.directory = true
1066 | table.insert(files_table,extra)
1067 | end
1068 | files_table = SortDirectory(files_table)
1069 | elseif (current_type == "TXT") or (current_type == "HEX") then
1070 | if (txt_i > 1) then
1071 | update_top_screen = true
1072 | updateTXT = true
1073 | table.remove(old_indexes)
1074 | txt_index = table.remove(old_indexes)
1075 | txt_i = txt_i - 2
1076 | end
1077 | elseif (current_type == "WAV") then
1078 | if (Sound.isPlaying(current_file)) then
1079 | Sound.pause(current_file)
1080 | else
1081 | Sound.resume(current_file)
1082 | end
1083 | elseif (current_type == "BMPV") then
1084 | if (BMPV.isPlaying(current_file)) then
1085 | BMPV.pause(current_file)
1086 | else
1087 | BMPV.resume(current_file)
1088 | end
1089 | elseif (current_type == "JPGV") then
1090 | if (JPGV.isPlaying(current_file)) then
1091 | JPGV.pause(current_file)
1092 | else
1093 | JPGV.resume(current_file)
1094 | end
1095 | end
1096 | elseif (Controls.check(pad,KEY_DRIGHT)) and not (Controls.check(oldpad,KEY_DRIGHT)) then
1097 | if (current_type == "SMDH") then
1098 | update_bottom_screen = true
1099 | name = System.startKeyboard("icon.bmp")
1100 | Screen.saveBitmap(current_file.icon,System.currentDirectory()..name)
1101 | oldpad = KEY_A
1102 | files_table = System.listDirectory(System.currentDirectory())
1103 | if System.currentDirectory() ~= "/" then
1104 | local extra = {}
1105 | extra.name = ".."
1106 | extra.size = 0
1107 | extra.directory = true
1108 | table.insert(files_table,extra)
1109 | end
1110 | files_table = SortDirectory(files_table)
1111 | elseif (current_type == "TXT") or (current_type == "HEX") then
1112 | update_top_screen = true
1113 | updateTXT = true
1114 | elseif (current_type == "WAV") then
1115 | if (Sound.isPlaying(current_file)) then
1116 | Sound.pause(current_file)
1117 | else
1118 | Sound.resume(current_file)
1119 | end
1120 | elseif (current_type == "BMPV") then
1121 | if (BMPV.isPlaying(current_file)) then
1122 | BMPV.pause(current_file)
1123 | else
1124 | BMPV.resume(current_file)
1125 | end
1126 | elseif (current_type == "JPGV") then
1127 | if (JPGV.isPlaying(current_file)) then
1128 | JPGV.pause(current_file)
1129 | else
1130 | JPGV.resume(current_file)
1131 | end
1132 | end
1133 | elseif (Controls.check(pad,KEY_SELECT)) and not (Controls.check(oldpad,KEY_SELECT)) then
1134 | if (files_table[p].directory == false) then
1135 | select_mode = true
1136 | end
1137 | elseif (Controls.check(pad,KEY_R)) and not (Controls.check(oldpad,KEY_R)) then
1138 | update_bottom_screen = true
1139 | name = System.startKeyboard("New Folder")
1140 | System.createDirectory(System.currentDirectory()..name)
1141 | pad = KEY_A
1142 | files_table = System.listDirectory(System.currentDirectory())
1143 | if System.currentDirectory() ~= "/" then
1144 | local extra = {}
1145 | extra.name = ".."
1146 | extra.size = 0
1147 | extra.directory = true
1148 | table.insert(files_table,extra)
1149 | end
1150 | files_table = SortDirectory(files_table)
1151 | elseif (Controls.check(pad,KEY_TOUCH)) then
1152 | update_bottom_screen = true
1153 | x,y = Controls.readTouch()
1154 | new_index = math.ceil(y/15)
1155 | if (new_index <= #files_table) then
1156 | if master_index > 0 then
1157 | p = new_index + master_index - 1
1158 | else
1159 | p = new_index
1160 | end
1161 | end
1162 | end
1163 | end
1164 | if (Controls.check(oldpad,KEY_TOUCH)) and not (Controls.check(pad,KEY_TOUCH)) then
1165 | update_bottom_screen = true
1166 | master_index = p - 15
1167 | end
1168 | end
--------------------------------------------------------------------------------