1
1
package entralinked .network .http .dls ;
2
2
3
- import java .io .FileInputStream ;
3
+ import java .io .File ;
4
4
import java .io .IOException ;
5
+ import java .nio .file .Files ;
6
+ import java .util .Arrays ;
5
7
import java .util .List ;
6
8
7
9
import org .apache .logging .log4j .LogManager ;
10
12
import com .fasterxml .jackson .databind .ObjectMapper ;
11
13
12
14
import entralinked .Entralinked ;
13
- import entralinked .model .dlc .Dlc ;
14
- import entralinked .model .dlc .DlcList ;
15
+ import entralinked .GameVersion ;
15
16
import entralinked .model .user .ServiceSession ;
16
17
import entralinked .model .user .User ;
17
18
import entralinked .model .user .UserManager ;
18
19
import entralinked .network .http .HttpHandler ;
19
20
import entralinked .network .http .HttpRequestHandler ;
20
21
import entralinked .serialization .UrlEncodedFormFactory ;
21
- import entralinked .utility .LEOutputStream ;
22
+ import entralinked .utility .MysteryGiftUtility ;
22
23
import io .javalin .Javalin ;
23
24
import io .javalin .http .Context ;
24
25
import io .javalin .http .HttpStatus ;
@@ -30,11 +31,10 @@ public class DlsHandler implements HttpHandler {
30
31
31
32
private static final Logger logger = LogManager .getLogger ();
32
33
private final ObjectMapper mapper = new ObjectMapper (new UrlEncodedFormFactory ());
33
- private final DlcList dlcList ;
34
+ private final File rootDirectory = new File ( "dlc" ) ;
34
35
private final UserManager userManager ;
35
36
36
37
public DlsHandler (Entralinked entralinked ) {
37
- this .dlcList = entralinked .getDlcList ();
38
38
this .userManager = entralinked .getUserManager ();
39
39
}
40
40
@@ -64,6 +64,7 @@ private void handleDownloadRequest(Context ctx) throws IOException {
64
64
HttpRequestHandler <DlsRequest > handler = switch (request .action ()) {
65
65
case "list" -> this ::handleRetrieveDlcList ;
66
66
case "contents" -> this ::handleRetrieveDlcContent ;
67
+ case "count" -> this ::handleRetrieveDlcCount ;
67
68
default -> throw new IllegalArgumentException ("Invalid POST request action: " + request .action ());
68
69
};
69
70
@@ -81,15 +82,43 @@ private void handleRetrieveDlcList(DlsRequest request, Context ctx) throws IOExc
81
82
User user = ctx .attribute ("user" );
82
83
String gameCode = getDlcGameCode (request .dlcGameCode ());
83
84
String type = getRegionlessDlcType (request .dlcType ());
85
+ String attr2 = request .attr2 ();
86
+ List <File > files = user .hasDlcOverride (type ) ? Arrays .asList (user .getDlcOverride (type ))
87
+ : Arrays .asList (getDlcDirectory (gameCode , type ).listFiles ());
84
88
85
- // If an overriding DLC is present, send the data for that instead.
86
- if (user . hasDlcOverride ( type ) ) {
87
- ctx .result (dlcList . getDlcListString ( List . of ( user . getDlcOverride ( type ))) );
89
+ // Return empty string if no DLC could be found
90
+ if (files == null ) {
91
+ ctx .result ("" );
88
92
return ;
89
93
}
90
94
91
- // TODO NOTE: I assume that in a conventional implementation, certain DLC attributes may be omitted from the request.
92
- ctx .result (dlcList .getDlcListString (dlcList .getDlcList (gameCode , type , request .dlcIndex ())));
95
+ // PGL content attr2 hack
96
+ if (attr2 != null ) {
97
+ files = Arrays .asList (files .get (Integer .parseInt (attr2 ) - 1 ));
98
+ }
99
+
100
+ StringBuilder builder = new StringBuilder ();
101
+ int count = Math .min (files .size (), request .num ());
102
+
103
+ // Create DLC list string
104
+ for (int i = 0 ; i < count ; i ++) {
105
+ File file = files .get (i );
106
+
107
+ if (type == null ) {
108
+ // Generation 4 Mystery Gift
109
+ builder .append ("%s\t \t \t \t \t %s\r \n " .formatted (file .getName (), file .length ()));
110
+ } else if (type .equals ("MYSTERY" )) {
111
+ // Generation 5 Mystery Gift
112
+ String gameFlag = GameVersion .lookup (request .gameCode ()).isVersion2 () ? "F00000" : "300000" ;
113
+ builder .append ("%s\t \t %s\t %s\t \t %s\r \n " .formatted (file .getName (), type , gameFlag , 720 ));
114
+ } else {
115
+ // PGL content
116
+ builder .append ("%s\t \t %s\t %s\t \t %s\r \n " .formatted (file .getName (), type , i + 1 , file .length ()));
117
+ }
118
+ }
119
+
120
+ // Send result
121
+ ctx .result (builder .toString ());
93
122
}
94
123
95
124
/**
@@ -99,32 +128,44 @@ private void handleRetrieveDlcContent(DlsRequest request, Context ctx) throws IO
99
128
User user = ctx .attribute ("user" );
100
129
String gameCode = getDlcGameCode (request .dlcGameCode ());
101
130
String type = getRegionlessDlcType (request .dlcType ());
102
- Dlc dlc = user .hasDlcOverride (type ) ? user .getDlcOverride (type ) : dlcList .getDlc (gameCode , type , request .dlcName ());
131
+ File file = user .hasDlcOverride (type ) ? user .getDlcOverride (type ) : type != null
132
+ ? new File (rootDirectory , "%s/%s/%s" .formatted (gameCode , type , request .dlcName ()))
133
+ : new File (rootDirectory , "%s/%s" .formatted (gameCode , request .dlcName ()));
103
134
104
135
// Check if the requested DLC exists
105
- if (dlc == null ) {
136
+ if (! file . exists () ) {
106
137
ctx .status (HttpStatus .NOT_FOUND );
107
138
return ;
108
139
}
109
140
110
- // Write DLC data
111
- try (FileInputStream inputStream = new FileInputStream (dlc .path ())) {
112
- LEOutputStream outputStream = new LEOutputStream (ctx .outputStream ());
113
- inputStream .transferTo (outputStream );
114
-
115
- // If checksum is not part of the file, manually append it
116
- if (!dlc .checksumEmbedded ()) {
117
- outputStream .writeShort (dlc .checksum ());
118
- }
141
+ byte [] bytes = Files .readAllBytes (file .toPath ());
142
+
143
+ if (type == null ) {
144
+ // Generation 4 Mystery Gift
145
+ bytes = MysteryGiftUtility .createUniversalGiftData4 (bytes );
146
+ } else if (type .equals ("MYSTERY" )) {
147
+ // Generation 5 Mystery Gift
148
+ bytes = MysteryGiftUtility .createUniversalGiftData5 (bytes );
119
149
}
150
+
151
+ // Send result
152
+ ctx .result (bytes );
153
+ }
154
+
155
+ /**
156
+ * POST handler for {@code /download action=count}
157
+ */
158
+ private void handleRetrieveDlcCount (DlsRequest request , Context ctx ) throws IOException {
159
+ ctx .result ("1" ); // TODO
120
160
}
121
161
122
162
/**
123
163
* @return The game serial that should be used for downloading DLC based on the provided input.
124
164
*/
125
165
private String getDlcGameCode (String gameCode ) {
126
- return switch (gameCode ) {
127
- case "IRAJ" -> "IRAO" ;
166
+ return switch (gameCode .substring (0 , 3 )) {
167
+ case "IRA" -> "IRAO" ; // BW & B2W2
168
+ case "ADA" , "CPU" , "IPG" -> "ADAE" ; // DPPt & HGSS
128
169
default -> gameCode ;
129
170
};
130
171
}
@@ -133,12 +174,21 @@ private String getDlcGameCode(String gameCode) {
133
174
* @return The DLC type without the region identifier, or the input if it is an unknown type.
134
175
*/
135
176
private String getRegionlessDlcType (String dlcType ) {
177
+ if (dlcType == null ) {
178
+ return null ;
179
+ }
180
+
136
181
return switch (dlcType ) {
137
182
case "CGEAR_E" , "CGEAR_F" , "CGEAR_I" , "CGEAR_G" , "CGEAR_S" , "CGEAR_J" , "CGEAR_K" -> "CGEAR" ;
138
183
case "CGEAR2_E" , "CGEAR2_F" , "CGEAR2_I" , "CGEAR2_G" , "CGEAR2_S" , "CGEAR2_J" , "CGEAR2_K" -> "CGEAR2" ;
139
184
case "ZUKAN_E" , "ZUKAN_F" , "ZUKAN_I" , "ZUKAN_G" , "ZUKAN_S" , "ZUKAN_J" , "ZUKAN_K" -> "ZUKAN" ;
140
185
case "MUSICAL_E" , "MUSICAL_F" , "MUSICAL_I" , "MUSICAL_G" , "MUSICAL_S" , "MUSICAL_J" , "MUSICAL_K" -> "MUSICAL" ;
186
+ case "MYSTERY_E" , "MYSTERY_F" , "MYSTERY_I" , "MYSTERY_G" , "MYSTERY_S" , "MYSTERY_J" , "MYSTERY_K" -> "MYSTERY" ;
141
187
default -> dlcType ;
142
188
};
143
189
}
190
+
191
+ private File getDlcDirectory (String gameCode , String dlcType ) {
192
+ return dlcType == null ? new File (rootDirectory , gameCode ) : new File (rootDirectory , "%s/%s" .formatted (gameCode , dlcType ));
193
+ }
144
194
}
0 commit comments